Skip to content

Commit

Permalink
feature: raw sql possible
Browse files Browse the repository at this point in the history
  • Loading branch information
alexisvisco committed Oct 31, 2024
1 parent 900377f commit 926cead
Show file tree
Hide file tree
Showing 18 changed files with 237 additions and 41 deletions.
18 changes: 13 additions & 5 deletions cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ package cmd

import (
"fmt"
"path"
"path/filepath"
"time"

"github.com/alexisvisco/amigo/pkg/amigo"
"github.com/alexisvisco/amigo/pkg/types"
"github.com/alexisvisco/amigo/pkg/utils"
"github.com/alexisvisco/amigo/pkg/utils/events"
"github.com/alexisvisco/amigo/pkg/utils/logger"
"github.com/gobuffalo/flect"
"github.com/spf13/cobra"
"path"
"path/filepath"
"time"
)

// createCmd represents the create command
Expand Down Expand Up @@ -48,7 +49,11 @@ var createCmd = &cobra.Command{
version := now.UTC().Format(utils.FormatTime)
cmdCtx.Create.Version = version

migrationFileName := fmt.Sprintf("%s_%s.go", version, flect.Underscore(args[0]))
ext := "go"
if cmdCtx.Create.Type == "sql" {
ext = "sql"
}
migrationFileName := fmt.Sprintf("%s_%s.%s", version, flect.Underscore(args[0]), ext)
file, err := utils.CreateOrOpenFile(filepath.Join(cmdCtx.MigrationFolder, migrationFileName))
if err != nil {
return fmt.Errorf("unable to open/create file: %w", err)
Expand Down Expand Up @@ -97,14 +102,17 @@ var createCmd = &cobra.Command{
func init() {
rootCmd.AddCommand(createCmd)
createCmd.Flags().StringVar(&cmdCtx.Create.Type, "type", "change",
"The type of migration to create, possible values are [classic, change]")
"The type of migration to create, possible values are [classic, change, sql]")

createCmd.Flags().BoolVarP(&cmdCtx.Create.Dump, "dump", "d", false,
"dump with pg_dump the current schema and add it to the current migration")

createCmd.Flags().StringVarP(&cmdCtx.Create.DumpSchema, "dump-schema", "s", "public",
"the schema to dump if --dump is set")

createCmd.Flags().StringVar(&cmdCtx.Create.SQLSeparator, "sql-separator", "-- migrate:down",
"the separator to split the up and down part of the migration")

createCmd.Flags().BoolVar(&cmdCtx.Create.Skip, "skip", false,
"skip will set the migration as applied without executing it")
}
13 changes: 12 additions & 1 deletion docs/docs/03-cli/03-create.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (m Migration20240502155033SchemaVersion) Date() time.Time {
- `--skip` will insert the current version of the schema into the `mig_schema_versions` table without running the migration (because the schema already exists).
- `--dump-schema` to specify the schema to dump. (default is `public`)
- `--pg-dump-path` to specify the path to the `pg_dump` command.
- `--type` to specify the type of migration to create, possible values are [classic, change] (default is `change`)
- `--type` to specify the type of migration to create, possible values are [classic, change, sql] (default is `change`)

## Difference between classic and change migration

Expand Down Expand Up @@ -109,7 +109,18 @@ func (m Migration20240502155033SchemaVersion) Change(s *pg.Schema) {
}
```

### Raw sql migrations

You can also create a raw SQL migration by using the `--type sql` flag.

This will produce a migration file like this:

```sql
-- todo: write up migrations here
-- migrate:down
-- todo: write down migrations here

```



8 changes: 8 additions & 0 deletions example/sqlite/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@ package migrations

import (
"github.com/alexisvisco/amigo/pkg/schema"
"github.com/alexisvisco/amigo/pkg/schema/pg"

"embed"
)

//go:embed *.sql
var sqlMigrationsFS embed.FS

var Migrations = []schema.Migration{
&Migration20240602080728CreateTableSchemaVersion{},
&Migration20240602081304AddIndex{},
&Migration20240602081806DropIndex{},
schema.NewSQLMigration[*pg.Schema](sqlMigrationsFS, "20240602081806_drop_index.sql", "2024-06-02T10:18:06+02:00",
"-- migrate:down"),
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ require (
github.com/simukti/sqldb-logger v0.0.0-20230108155151-646c1a075551

// user for the cli (only imported in cmd)
github.com/spf13/cobra v1.8.0
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
)

Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4E
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
Expand Down Expand Up @@ -70,6 +71,8 @@ github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
Expand Down
1 change: 1 addition & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
Expand Down
60 changes: 45 additions & 15 deletions pkg/amigo/amigo.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,6 @@ package amigo
import (
"database/sql"
"fmt"
"github.com/alexisvisco/amigo/pkg/amigoctx"
"github.com/alexisvisco/amigo/pkg/schema"
"github.com/alexisvisco/amigo/pkg/templates"
"github.com/alexisvisco/amigo/pkg/types"
"github.com/alexisvisco/amigo/pkg/utils"
"github.com/alexisvisco/amigo/pkg/utils/cmdexec"
"github.com/gobuffalo/flect"
"io"
"os"
"path"
Expand All @@ -18,6 +11,14 @@ import (
"sort"
"strings"
"time"

"github.com/alexisvisco/amigo/pkg/amigoctx"
"github.com/alexisvisco/amigo/pkg/schema"
"github.com/alexisvisco/amigo/pkg/templates"
"github.com/alexisvisco/amigo/pkg/types"
"github.com/alexisvisco/amigo/pkg/utils"
"github.com/alexisvisco/amigo/pkg/utils/cmdexec"
"github.com/gobuffalo/flect"
)

type Amigo struct {
Expand Down Expand Up @@ -128,6 +129,7 @@ type GenerateMigrationFileParams struct {

// GenerateMigrationFile generate a migration file in the migrations folder
func (a Amigo) GenerateMigrationFile(params *GenerateMigrationFileParams) error {

structName := utils.MigrationStructName(params.Now, params.Name)

orDefault := func(s string) string {
Expand All @@ -138,6 +140,7 @@ func (a Amigo) GenerateMigrationFile(params *GenerateMigrationFileParams) error
}

fileContent, err := templates.GetMigrationTemplate(templates.MigrationData{
IsSQL: params.Type == types.MigrationFileTypeSQL,
Package: a.ctx.PackagePath,
StructName: structName,
Name: flect.Underscore(params.Name),
Expand Down Expand Up @@ -173,13 +176,33 @@ func (a Amigo) GenerateMigrationsFiles(writer io.Writer) error {
}

var migrations []string
var mustImportSchemaPackage *string
for _, k := range keys {
migrations = append(migrations, utils.MigrationStructName(k, migrationFiles[k]))
if migrationFiles[k].IsSQL {
// schema.NewSQLMigration[*pg.Schema](sqlMigrationsFS, "20240602081806_drop_index.sql", "2024-06-02T10:18:06+02:00", "---- down:"),
line := fmt.Sprintf("schema.NewSQLMigration[%s](sqlMigrationsFS, \"%s\", \"%s\", \"%s\")",
a.Driver.StructName(),
migrationFiles[k].FulName,
k.Format(time.RFC3339),
a.ctx.Create.SQLSeparator,
)

migrations = append(migrations, line)

if mustImportSchemaPackage == nil {
v := a.Driver.PackageSchemaPath()
mustImportSchemaPackage = &v
}
} else {
migrations = append(migrations, fmt.Sprintf("&%s{}", utils.MigrationStructName(k, migrationFiles[k].Name)))

}
}

content, err := templates.GetMigrationsTemplate(templates.MigrationsData{
Package: a.ctx.PackagePath,
Migrations: migrations,
Package: a.ctx.PackagePath,
Migrations: migrations,
ImportSchemaPackage: mustImportSchemaPackage,
})

if err != nil {
Expand Down Expand Up @@ -214,8 +237,14 @@ func (a Amigo) GetStatus(db *sql.DB) ([]string, error) {
return state, nil
}

func (a Amigo) GetMigrationFiles(ascending bool) (map[time.Time]string, []time.Time, error) {
migrationFiles := make(map[time.Time]string)
type MigrationFile struct {
Name string
FulName string
IsSQL bool
}

func (a Amigo) GetMigrationFiles(ascending bool) (map[time.Time]MigrationFile, []time.Time, error) {
migrationFiles := make(map[time.Time]MigrationFile)

// get the list of structs by the file name
err := filepath.Walk(a.ctx.MigrationFolder, func(path string, info os.FileInfo, err error) error {
Expand All @@ -224,13 +253,14 @@ func (a Amigo) GetMigrationFiles(ascending bool) (map[time.Time]string, []time.T
}

if !info.IsDir() {
if utils.FileRegexp.MatchString(info.Name()) {
matches := utils.FileRegexp.FindStringSubmatch(info.Name())
if utils.MigrationFileRegexp.MatchString(info.Name()) {
matches := utils.MigrationFileRegexp.FindStringSubmatch(info.Name())
fileTime := matches[1]
migrationName := matches[2]
ext := matches[3]

t, _ := time.Parse(utils.FormatTime, fileTime)
migrationFiles[t] = migrationName
migrationFiles[t] = MigrationFile{Name: migrationName, IsSQL: ext == "sql", FulName: info.Name()}
}
}

Expand Down
15 changes: 11 additions & 4 deletions pkg/amigo/run_migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import (
"context"
"database/sql"
"errors"
"io"
"log/slog"
"time"

"github.com/alexisvisco/amigo/pkg/amigoctx"
"github.com/alexisvisco/amigo/pkg/schema"
"github.com/alexisvisco/amigo/pkg/schema/base"
Expand All @@ -13,9 +17,6 @@ import (
"github.com/alexisvisco/amigo/pkg/utils"
"github.com/alexisvisco/amigo/pkg/utils/dblog"
sqldblogger "github.com/simukti/sqldb-logger"
"io"
"log/slog"
"time"
)

var (
Expand All @@ -32,6 +33,7 @@ type RunMigrationParams struct {
Direction types.MigrationDirection
Migrations []schema.Migration
LogOutput io.Writer
Context context.Context
}

// RunMigrations migrates the database, it is launched via the generated main file or manually in a codebase.
Expand All @@ -41,7 +43,12 @@ func (a Amigo) RunMigrations(params RunMigrationParams) error {
return err
}

ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(a.ctx.Migration.Timeout))
originCtx := context.Background()
if params.Context != nil {
originCtx = params.Context
}

ctx, cancel := context.WithDeadline(originCtx, time.Now().Add(a.ctx.Migration.Timeout))
defer cancel()

oldLogger := slog.Default()
Expand Down
39 changes: 33 additions & 6 deletions pkg/amigoctx/ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package amigoctx
import (
"errors"
"fmt"
"github.com/alexisvisco/amigo/pkg/types"
"regexp"
"strings"
"time"

"github.com/alexisvisco/amigo/pkg/types"
)

var (
Expand Down Expand Up @@ -71,6 +72,26 @@ func (r *Context) GetRealDSN() string {
return r.Root.DSN
}

func (a *Context) WithAmigoFolder(folder string) *Context {
a.Root.AmigoFolderPath = folder
return a
}

func (a *Context) WithMigrationFolder(folder string) *Context {
a.Root.MigrationFolder = folder
return a
}

func (a *Context) WithPackagePath(packagePath string) *Context {
a.Root.PackagePath = packagePath
return a
}

func (a *Context) WithSchemaVersionTable(table string) *Context {
a.Root.SchemaVersionTable = table
return a
}

func (a *Context) WithDSN(dsn string) *Context {
a.Root.DSN = dsn
return a
Expand All @@ -86,6 +107,11 @@ func (a *Context) WithSteps(steps int) *Context {
return a
}

func (a *Context) WithShowSQL(showSQL bool) *Context {
a.Root.ShowSQL = showSQL
return a
}

func (r *Root) ValidateDSN() error {
if r.DSN == "" {
return ErrDSNEmpty
Expand Down Expand Up @@ -116,17 +142,18 @@ func (m *Migration) ValidateVersion() error {
}

type Create struct {
Type string
Dump bool
DumpSchema string
Skip bool
Type string
Dump bool
DumpSchema string
SQLSeparator string

Skip bool
// Version is post setted after the name have been generated from the arg and time
Version string
}

func (c *Create) ValidateType() error {
allowedTypes := []string{string(types.MigrationFileTypeClassic), string(types.MigrationFileTypeChange)}
allowedTypes := []string{string(types.MigrationFileTypeClassic), string(types.MigrationFileTypeChange), string(types.MigrationFileTypeSQL)}

for _, t := range allowedTypes {
if c.Type == t {
Expand Down
10 changes: 9 additions & 1 deletion pkg/schema/base/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package base

import (
"fmt"
"strings"

"github.com/alexisvisco/amigo/pkg/schema"
"github.com/alexisvisco/amigo/pkg/types"
"github.com/alexisvisco/amigo/pkg/utils"
"strings"
)

// Schema is the base schema. It is used to support unknown database types and provide a default implementation.
Expand Down Expand Up @@ -117,6 +118,13 @@ func (p *Schema) FindAppliedVersions() []string {
return versions
}

func (p *Schema) Exec(query string, args ...interface{}) {
_, err := p.TX.ExecContext(p.Context.Context, query, args...)
if err != nil {
p.Context.RaiseError(fmt.Errorf("error while executing query: %w", err))
}
}

func ColumnType(ctx *schema.MigratorContext, options schema.ColumnData) func() string {
return func() string {
strBuilder := strings.Builder{}
Expand Down
Loading

0 comments on commit 926cead

Please sign in to comment.