From 5650198fd9d8ffafa6a6738cb3ce87195ef4a716 Mon Sep 17 00:00:00 2001 From: Eric Fritz Date: Thu, 19 Sep 2024 18:51:53 -0500 Subject: [PATCH] WIP. --- cmd/migrate/describe.go | 40 ------- cmd/migrate/drift.go | 49 -------- cmd/migrate/{ => internal/commands}/create.go | 35 ++++-- cmd/migrate/internal/commands/describe.go | 50 ++++++++ cmd/migrate/internal/commands/drift.go | 67 +++++++++++ cmd/migrate/{ => internal/commands}/state.go | 35 ++++-- cmd/migrate/internal/commands/undo.go | 46 +++++++ cmd/migrate/internal/commands/up.go | 54 +++++++++ cmd/migrate/internal/database/dial.go | 10 ++ cmd/migrate/internal/database/runner.go | 25 ++++ cmd/migrate/internal/flags/database_url.go | 48 ++++++++ cmd/migrate/internal/flags/migrations_dir.go | 12 ++ cmd/migrate/internal/flags/no_color.go | 26 ++++ cmd/migrate/internal/flags/util.go | 17 +++ cmd/migrate/internal/logging/logger.go | 20 ++++ cmd/migrate/main.go | 113 ++---------------- cmd/migrate/undo.go | 34 ------ cmd/migrate/up.go | 38 ------ drift.go | 2 +- rows_slice_scanner.go | 3 +- 20 files changed, 436 insertions(+), 288 deletions(-) delete mode 100644 cmd/migrate/describe.go delete mode 100644 cmd/migrate/drift.go rename cmd/migrate/{ => internal/commands}/create.go (60%) create mode 100644 cmd/migrate/internal/commands/describe.go create mode 100644 cmd/migrate/internal/commands/drift.go rename cmd/migrate/{ => internal/commands}/state.go (72%) create mode 100644 cmd/migrate/internal/commands/undo.go create mode 100644 cmd/migrate/internal/commands/up.go create mode 100644 cmd/migrate/internal/database/dial.go create mode 100644 cmd/migrate/internal/database/runner.go create mode 100644 cmd/migrate/internal/flags/database_url.go create mode 100644 cmd/migrate/internal/flags/migrations_dir.go create mode 100644 cmd/migrate/internal/flags/no_color.go create mode 100644 cmd/migrate/internal/flags/util.go create mode 100644 cmd/migrate/internal/logging/logger.go delete mode 100644 cmd/migrate/undo.go delete mode 100644 cmd/migrate/up.go diff --git a/cmd/migrate/describe.go b/cmd/migrate/describe.go deleted file mode 100644 index da8384c..0000000 --- a/cmd/migrate/describe.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/go-nacelle/pgutil" - "github.com/spf13/cobra" -) - -var describeCmd = &cobra.Command{ - Use: "describe", - Short: "Describe the current database schema", - RunE: describe, -} - -func init() { - rootCmd.AddCommand(describeCmd) -} - -func describe(cmd *cobra.Command, args []string) error { - db, err := dial() - if err != nil { - return err - } - - description, err := pgutil.DescribeSchema(context.Background(), db) - if err != nil { - return err - } - - serialized, err := json.Marshal(description) - if err != nil { - return err - } - - fmt.Printf("%s\n", serialized) - return nil -} diff --git a/cmd/migrate/drift.go b/cmd/migrate/drift.go deleted file mode 100644 index b6f6021..0000000 --- a/cmd/migrate/drift.go +++ /dev/null @@ -1,49 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "fmt" - "os" - - "github.com/go-nacelle/pgutil" - "github.com/spf13/cobra" -) - -var driftCmd = &cobra.Command{ - Use: "drift", - Short: "Compare the current database schema against the expected schema", - RunE: drift, -} - -func init() { - rootCmd.AddCommand(driftCmd) -} - -func drift(cmd *cobra.Command, args []string) error { - db, err := dial() - if err != nil { - return err - } - - description, err := pgutil.DescribeSchema(context.Background(), db) - if err != nil { - return err - } - - b, err := os.ReadFile("description.json") - if err != nil { - return err - } - - var expected pgutil.SchemaDescription - if err := json.Unmarshal(b, &expected); err != nil { - return err - } - - for _, d := range pgutil.Compare(expected, description) { - fmt.Printf("%s\n\n", d) - } - - return nil -} diff --git a/cmd/migrate/create.go b/cmd/migrate/internal/commands/create.go similarity index 60% rename from cmd/migrate/create.go rename to cmd/migrate/internal/commands/create.go index ac43fc2..e2c4794 100644 --- a/cmd/migrate/create.go +++ b/cmd/migrate/internal/commands/create.go @@ -1,4 +1,4 @@ -package main +package commands import ( "fmt" @@ -7,27 +7,36 @@ import ( "regexp" "strings" + "github.com/go-nacelle/log/v2" "github.com/go-nacelle/pgutil" + "github.com/go-nacelle/pgutil/cmd/migrate/internal/flags" "github.com/spf13/cobra" ) -var createCmd = &cobra.Command{ - Use: "create [flags] 'migration name'", - Short: "Create a new schema migration", - Args: cobra.ExactArgs(1), - RunE: create, -} +func CreateCommand(logger log.Logger) *cobra.Command { + var ( + migrationsDirectory string + ) + + createCmd := &cobra.Command{ + Use: "create [flags] 'migration name'", + Short: "Create a new schema migration", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return create(migrationsDirectory, args[0]) + }, + } -func init() { - rootCmd.AddCommand(createCmd) + flags.RegisterMigrationsDirectoryFlag(createCmd, &migrationsDirectory) + return createCmd } -func create(cmd *cobra.Command, args []string) error { - if err := ensureMigrationDirectoryExists(migrationDirectory); err != nil { +func create(migrationsDirectory string, name string) error { + if err := ensureMigrationDirectoryExists(migrationsDirectory); err != nil { return err } - definitions, err := pgutil.ReadMigrations(pgutil.NewFilesystemMigrationReader(migrationDirectory)) + definitions, err := pgutil.ReadMigrations(pgutil.NewFilesystemMigrationReader(migrationsDirectory)) if err != nil { return err } @@ -37,7 +46,7 @@ func create(cmd *cobra.Command, args []string) error { lastID = definitions[len(definitions)-1].ID } - dirPath := filepath.Join(migrationDirectory, fmt.Sprintf("%d_%s", lastID+1, canonicalize(args[0]))) + dirPath := filepath.Join(migrationsDirectory, fmt.Sprintf("%d_%s", lastID+1, canonicalize(name))) upPath := filepath.Join(dirPath, "up.sql") downPath := filepath.Join(dirPath, "down.sql") diff --git a/cmd/migrate/internal/commands/describe.go b/cmd/migrate/internal/commands/describe.go new file mode 100644 index 0000000..7d97120 --- /dev/null +++ b/cmd/migrate/internal/commands/describe.go @@ -0,0 +1,50 @@ +package commands + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/go-nacelle/log/v2" + "github.com/go-nacelle/pgutil" + "github.com/go-nacelle/pgutil/cmd/migrate/internal/database" + "github.com/go-nacelle/pgutil/cmd/migrate/internal/flags" + "github.com/spf13/cobra" +) + +func DescribeCommand(logger log.Logger) *cobra.Command { + var ( + databaseURL string + ) + + describeCmd := &cobra.Command{ + Use: "describe", + Short: "Describe the current database schema", + RunE: func(cmd *cobra.Command, args []string) error { + return describe(databaseURL, logger) + }, + } + + flags.RegisterDatabaseURLFlag(describeCmd, &databaseURL) + return describeCmd +} + +func describe(databaseURL string, logger log.Logger) error { + db, err := database.Dial(databaseURL, logger) + if err != nil { + return err + } + + description, err := pgutil.DescribeSchema(context.Background(), db) + if err != nil { + return err + } + + serialized, err := json.Marshal(description) + if err != nil { + return err + } + + fmt.Printf("%s\n", serialized) + return nil +} diff --git a/cmd/migrate/internal/commands/drift.go b/cmd/migrate/internal/commands/drift.go new file mode 100644 index 0000000..f55b849 --- /dev/null +++ b/cmd/migrate/internal/commands/drift.go @@ -0,0 +1,67 @@ +package commands + +import ( + "context" + "encoding/json" + "fmt" + "os" + + "github.com/go-nacelle/log/v2" + "github.com/go-nacelle/pgutil" + "github.com/go-nacelle/pgutil/cmd/migrate/internal/database" + "github.com/go-nacelle/pgutil/cmd/migrate/internal/flags" + "github.com/spf13/cobra" +) + +func DriftCommand(logger log.Logger) *cobra.Command { + var ( + databaseURL string + ) + + driftCmd := &cobra.Command{ + Use: "drift", + Short: "Compare the current database schema against the expected schema", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return drift(databaseURL, logger, args[0]) + }, + } + + flags.RegisterDatabaseURLFlag(driftCmd, &databaseURL) + return driftCmd +} + +func drift(databaseURL string, logger log.Logger, filename string) error { + db, err := database.Dial(databaseURL, logger) + if err != nil { + return err + } + + description, err := pgutil.DescribeSchema(context.Background(), db) + if err != nil { + return err + } + + b, err := os.ReadFile(filename) + if err != nil { + return err + } + + var expected pgutil.SchemaDescription + if err := json.Unmarshal(b, &expected); err != nil { + return err + } + + statements := pgutil.Compare(expected, description) + + if len(statements) == 0 { + fmt.Printf("No drift detected\n") + return nil + } + + for _, d := range statements { + fmt.Printf("%s\n\n", d) + } + + return nil +} diff --git a/cmd/migrate/state.go b/cmd/migrate/internal/commands/state.go similarity index 72% rename from cmd/migrate/state.go rename to cmd/migrate/internal/commands/state.go index cf3af76..f4570c6 100644 --- a/cmd/migrate/state.go +++ b/cmd/migrate/internal/commands/state.go @@ -1,4 +1,4 @@ -package main +package commands import ( "context" @@ -6,22 +6,35 @@ import ( "strings" "github.com/fatih/color" + "github.com/go-nacelle/log/v2" "github.com/go-nacelle/pgutil" + "github.com/go-nacelle/pgutil/cmd/migrate/internal/database" + "github.com/go-nacelle/pgutil/cmd/migrate/internal/flags" "github.com/spf13/cobra" ) -var stateCmd = &cobra.Command{ - Use: "state", - Short: "Display the current state of the database schema", - RunE: state, -} +func StatCommand(logger log.Logger) *cobra.Command { + var ( + databaseURL string + migrationsDirectory string + ) + + stateCmd := &cobra.Command{ + Use: "state", + Short: "Display the current state of the database schema", + RunE: func(cmd *cobra.Command, args []string) error { + return state(databaseURL, migrationsDirectory, logger) + }, + } -func init() { - rootCmd.AddCommand(stateCmd) + flags.RegisterDatabaseURLFlag(stateCmd, &databaseURL) + flags.RegisterMigrationsDirectoryFlag(stateCmd, &migrationsDirectory) + flags.RegisterNoColorFlag(stateCmd) + return stateCmd } -func state(cmd *cobra.Command, args []string) error { - runner, err := runner() +func state(databaseURL, migrationsDirectory string, logger log.Logger) error { + runner, err := database.CreateRunner(databaseURL, migrationsDirectory, logger) if err != nil { return err } @@ -55,7 +68,7 @@ func state(cmd *cobra.Command, args []string) error { color, statusEmoji, statusText := definitionStatus(log, exists) color.Printf( - "%s %04d: %s\t%s\n", + "%s %04d: %s\t\t%s\n", statusEmoji, definition.ID, definition.Name+strings.Repeat(" ", maxDefinitionLen-len(definition.Name)), diff --git a/cmd/migrate/internal/commands/undo.go b/cmd/migrate/internal/commands/undo.go new file mode 100644 index 0000000..ee1741e --- /dev/null +++ b/cmd/migrate/internal/commands/undo.go @@ -0,0 +1,46 @@ +package commands + +import ( + "context" + "fmt" + "strconv" + + "github.com/go-nacelle/log/v2" + "github.com/go-nacelle/pgutil/cmd/migrate/internal/database" + "github.com/go-nacelle/pgutil/cmd/migrate/internal/flags" + "github.com/spf13/cobra" +) + +func UndoCommand(logger log.Logger) *cobra.Command { + var ( + databaseURL string + migrationsDirectory string + ) + + undoCmd := &cobra.Command{ + Use: "undo [migration_id]", + Short: "Undo migrations up to and including the specified migration ID", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + migrationID, err := strconv.Atoi(args[0]) + if err != nil { + return fmt.Errorf("invalid migration ID: %v", err) + } + + return undo(databaseURL, migrationsDirectory, logger, migrationID) + }, + } + + flags.RegisterDatabaseURLFlag(undoCmd, &databaseURL) + flags.RegisterMigrationsDirectoryFlag(undoCmd, &migrationsDirectory) + return undoCmd +} + +func undo(databaseURL string, migrationsDirectory string, logger log.Logger, migrationID int) error { + runner, err := database.CreateRunner(databaseURL, migrationsDirectory, logger) + if err != nil { + return err + } + + return runner.Undo(context.Background(), migrationID) +} diff --git a/cmd/migrate/internal/commands/up.go b/cmd/migrate/internal/commands/up.go new file mode 100644 index 0000000..536d9ac --- /dev/null +++ b/cmd/migrate/internal/commands/up.go @@ -0,0 +1,54 @@ +package commands + +import ( + "context" + "fmt" + "strconv" + + "github.com/go-nacelle/log/v2" + "github.com/go-nacelle/pgutil/cmd/migrate/internal/database" + "github.com/go-nacelle/pgutil/cmd/migrate/internal/flags" + "github.com/spf13/cobra" +) + +func UpCommand(logger log.Logger) *cobra.Command { + var ( + databaseURL string + migrationsDirectory string + ) + + upCmd := &cobra.Command{ + Use: "up [migration_id]?", + Short: "Run migrations up to and including the specified migration ID", + Args: cobra.MaximumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + var migrationID *int + if len(args) != 0 { + val, err := strconv.Atoi(args[0]) + if err != nil { + return fmt.Errorf("invalid migration ID: %v", err) + } + migrationID = &val + } + + return up(databaseURL, migrationsDirectory, logger, migrationID) + }, + } + + flags.RegisterDatabaseURLFlag(upCmd, &databaseURL) + flags.RegisterMigrationsDirectoryFlag(upCmd, &migrationsDirectory) + return upCmd +} + +func up(databaseURL, migrationsDirectory string, logger log.Logger, migrationID *int) error { + runner, err := database.CreateRunner(databaseURL, migrationsDirectory, logger) + if err != nil { + return err + } + + if migrationID == nil { + return runner.ApplyAll(context.Background()) + } + + return runner.Apply(context.Background(), *migrationID) +} diff --git a/cmd/migrate/internal/database/dial.go b/cmd/migrate/internal/database/dial.go new file mode 100644 index 0000000..e5f6e98 --- /dev/null +++ b/cmd/migrate/internal/database/dial.go @@ -0,0 +1,10 @@ +package database + +import ( + "github.com/go-nacelle/log/v2" + "github.com/go-nacelle/pgutil" +) + +func Dial(databaseURL string, logger log.Logger) (pgutil.DB, error) { + return pgutil.Dial(databaseURL, logger) +} diff --git a/cmd/migrate/internal/database/runner.go b/cmd/migrate/internal/database/runner.go new file mode 100644 index 0000000..0639d23 --- /dev/null +++ b/cmd/migrate/internal/database/runner.go @@ -0,0 +1,25 @@ +package database + +import ( + "github.com/go-nacelle/log/v2" + "github.com/go-nacelle/pgutil" +) + +func CreateRunner(databaseURL string, migrationDirectory string, logger log.Logger) (*pgutil.Runner, error) { + if migrationDirectory == "" { + panic("migration directory is not set by called command") + } + + db, err := Dial(databaseURL, logger) + if err != nil { + return nil, err + } + + reader := pgutil.NewFilesystemMigrationReader(migrationDirectory) + runner, err := pgutil.NewMigrationRunner(db, reader, logger) + if err != nil { + return nil, err + } + + return runner, nil +} diff --git a/cmd/migrate/internal/flags/database_url.go b/cmd/migrate/internal/flags/database_url.go new file mode 100644 index 0000000..bae21d7 --- /dev/null +++ b/cmd/migrate/internal/flags/database_url.go @@ -0,0 +1,48 @@ +package flags + +import ( + "fmt" + "net/url" + + "github.com/go-nacelle/pgutil" + "github.com/spf13/cobra" +) + +func RegisterDatabaseURLFlag(cmd *cobra.Command, databaseURL *string) { + defaultURL := pgutil.BuildDatabaseURL() + + masked, err := maskDatabasePassword(defaultURL) + if err != nil { + panic(err) + } + + cmd.PersistentFlags().StringVarP( + databaseURL, + "url", "u", + "", + fmt.Sprintf("The database connection URL (default %s)", masked), + ) + + registerPreRun(cmd, func(cmd *cobra.Command, args []string) error { + if *databaseURL == "" { + *databaseURL = defaultURL + } + + return nil + }) +} + +func maskDatabasePassword(databaseURL string) (string, error) { + parsedURL, err := url.Parse(databaseURL) + if err != nil { + return "", fmt.Errorf("failed to parse database URL: %w", err) + } + + if parsedURL.User != nil { + if _, ok := parsedURL.User.Password(); ok { + parsedURL.User = url.UserPassword(parsedURL.User.Username(), "xxxxx") + } + } + + return parsedURL.String(), nil +} diff --git a/cmd/migrate/internal/flags/migrations_dir.go b/cmd/migrate/internal/flags/migrations_dir.go new file mode 100644 index 0000000..632c952 --- /dev/null +++ b/cmd/migrate/internal/flags/migrations_dir.go @@ -0,0 +1,12 @@ +package flags + +import "github.com/spf13/cobra" + +func RegisterMigrationsDirectoryFlag(cmd *cobra.Command, migrationDirectory *string) { + cmd.PersistentFlags().StringVarP( + migrationDirectory, + "dir", "d", + "migrations", + "The directory where schema migrations are defined", + ) +} diff --git a/cmd/migrate/internal/flags/no_color.go b/cmd/migrate/internal/flags/no_color.go new file mode 100644 index 0000000..419a4bc --- /dev/null +++ b/cmd/migrate/internal/flags/no_color.go @@ -0,0 +1,26 @@ +package flags + +import ( + "github.com/fatih/color" + "github.com/spf13/cobra" +) + +func RegisterNoColorFlag(cmd *cobra.Command) { + var noColor bool + + cmd.PersistentFlags().BoolVarP( + &noColor, + "no-color", + "", + false, + "Disable color output", + ) + + registerPreRun(cmd, func(cmd *cobra.Command, args []string) error { + if noColor { + color.NoColor = true + } + + return nil + }) +} diff --git a/cmd/migrate/internal/flags/util.go b/cmd/migrate/internal/flags/util.go new file mode 100644 index 0000000..4ac5b6a --- /dev/null +++ b/cmd/migrate/internal/flags/util.go @@ -0,0 +1,17 @@ +package flags + +import "github.com/spf13/cobra" + +func registerPreRun(cmd *cobra.Command, f func(cmd *cobra.Command, args []string) error) { + previous := cmd.PersistentPreRunE + + cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { + if previous != nil { + if err := previous(cmd, args); err != nil { + return err + } + } + + return f(cmd, args) + } +} diff --git a/cmd/migrate/internal/logging/logger.go b/cmd/migrate/internal/logging/logger.go new file mode 100644 index 0000000..c08b990 --- /dev/null +++ b/cmd/migrate/internal/logging/logger.go @@ -0,0 +1,20 @@ +package logging + +import ( + "github.com/go-nacelle/config/v3" + "github.com/go-nacelle/log/v2" +) + +func CreateLogger() (log.Logger, error) { + cfg := config.NewConfig(config.NewEnvSourcer("PGUTIL")) + if err := cfg.Init(); err != nil { + return nil, err + } + + c := &log.Config{} + if err := cfg.Load(c); err != nil { + return nil, err + } + + return log.InitLogger(c) +} diff --git a/cmd/migrate/main.go b/cmd/migrate/main.go index a34aa0a..86f4c11 100644 --- a/cmd/migrate/main.go +++ b/cmd/migrate/main.go @@ -1,123 +1,34 @@ package main import ( - "fmt" - "net/url" "os" - "github.com/fatih/color" - "github.com/go-nacelle/config/v3" - "github.com/go-nacelle/log/v2" - "github.com/go-nacelle/pgutil" + "github.com/go-nacelle/pgutil/cmd/migrate/internal/commands" + "github.com/go-nacelle/pgutil/cmd/migrate/internal/logging" "github.com/spf13/cobra" ) -func main() { - if err := rootCmd.Execute(); err != nil { - os.Exit(1) - } -} - var rootCmd = &cobra.Command{ Use: "migrate", Short: "Manage and execute Postgres schema migrations", } -var ( - migrationDirectory string - databaseURL string - defaultDatabaseURL = pgutil.BuildDatabaseURL() - noColor bool - logger log.Logger -) - func init() { - if err := initLogger(); err != nil { - panic(err) - } - - masked, err := maskDatabasePassword(defaultDatabaseURL) + logger, err := logging.CreateLogger() if err != nil { panic(err) } - rootCmd.PersistentFlags().StringVarP( - &migrationDirectory, - "dir", "d", - "migrations", - "The directory where schema migrations are defined", - ) - - rootCmd.PersistentFlags().StringVarP( - &databaseURL, - "url", "u", - "", - fmt.Sprintf("The database connection URL (default %s)", masked), - ) - - rootCmd.PersistentFlags().BoolVarP( - &noColor, - "no-color", - "", - false, - "Disable color output", - ) - - rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) { - if noColor { - color.NoColor = true - } - } + rootCmd.AddCommand(commands.CreateCommand(logger)) + rootCmd.AddCommand(commands.UpCommand(logger)) + rootCmd.AddCommand(commands.UndoCommand(logger)) + rootCmd.AddCommand(commands.StatCommand(logger)) + rootCmd.AddCommand(commands.DescribeCommand(logger)) + rootCmd.AddCommand(commands.DriftCommand(logger)) } -func initLogger() (err error) { - cfg := config.NewConfig(config.NewEnvSourcer("PGUTIL")) - if err := cfg.Init(); err != nil { - return err - } - - c := &log.Config{} - if err := cfg.Load(c); err != nil { - return err - } - - logger, err = log.InitLogger(c) - return err -} - -func maskDatabasePassword(databaseURL string) (string, error) { - parsedURL, err := url.Parse(databaseURL) - if err != nil { - return "", fmt.Errorf("failed to parse database URL: %w", err) - } - - if parsedURL.User != nil { - if _, ok := parsedURL.User.Password(); ok { - parsedURL.User = url.UserPassword(parsedURL.User.Username(), "xxxxx") - } - } - - return parsedURL.String(), nil -} - -func dial() (pgutil.DB, error) { - if databaseURL == "" { - databaseURL = defaultDatabaseURL - } - return pgutil.Dial(databaseURL, logger) -} - -func runner() (*pgutil.Runner, error) { - db, err := dial() - if err != nil { - return nil, err - } - - reader := pgutil.NewFilesystemMigrationReader(migrationDirectory) - runner, err := pgutil.NewMigrationRunner(db, reader, logger) - if err != nil { - return nil, err +func main() { + if err := rootCmd.Execute(); err != nil { + os.Exit(1) } - - return runner, nil } diff --git a/cmd/migrate/undo.go b/cmd/migrate/undo.go deleted file mode 100644 index 1be0aa5..0000000 --- a/cmd/migrate/undo.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "context" - "fmt" - "strconv" - - "github.com/spf13/cobra" -) - -var undoCmd = &cobra.Command{ - Use: "undo [migration_id]", - Short: "Undo migrations up to and including the specified migration ID", - Args: cobra.ExactArgs(1), - RunE: undo, -} - -func init() { - rootCmd.AddCommand(undoCmd) -} - -func undo(cmd *cobra.Command, args []string) error { - migrationID, err := strconv.Atoi(args[0]) - if err != nil { - return fmt.Errorf("invalid migration ID: %v", err) - } - - runner, err := runner() - if err != nil { - return err - } - - return runner.Undo(context.Background(), migrationID) -} diff --git a/cmd/migrate/up.go b/cmd/migrate/up.go deleted file mode 100644 index d8b4dba..0000000 --- a/cmd/migrate/up.go +++ /dev/null @@ -1,38 +0,0 @@ -package main - -import ( - "context" - "fmt" - "strconv" - - "github.com/spf13/cobra" -) - -var upCmd = &cobra.Command{ - Use: "up [migration_id]?", - Short: "Run migrations up to and including the specified migration ID", - Args: cobra.MaximumNArgs(1), - RunE: up, -} - -func init() { - rootCmd.AddCommand(upCmd) -} - -func up(cmd *cobra.Command, args []string) error { - runner, err := runner() - if err != nil { - return err - } - - if len(args) == 0 { - return runner.ApplyAll(context.Background()) - } - - migrationID, err := strconv.Atoi(args[0]) - if err != nil { - return fmt.Errorf("invalid migration ID: %v", err) - } - - return runner.Apply(context.Background(), migrationID) -} diff --git a/drift.go b/drift.go index e47cd20..2f17c42 100644 --- a/drift.go +++ b/drift.go @@ -146,7 +146,7 @@ func Compare(a, b SchemaDescription) (statements []string) { {"drop", "trigger", sortByKey}, {"drop", "view", sortDropViews}, {"drop", "index", sortByKey}, - {"drop", "constraint", sortByKey}, + {"drop", "constraint", sortByKey}, // TODO - needs to be primary key/unique, fkey, others {"drop", "column", sortByKey}, {"drop", "sequence", sortByKey}, {"drop", "table", sortByKey}, diff --git a/rows_slice_scanner.go b/rows_slice_scanner.go index 6dafa1b..7dd3160 100644 --- a/rows_slice_scanner.go +++ b/rows_slice_scanner.go @@ -10,7 +10,8 @@ func NewSliceScanner[T any](f ScanValueFunc[T]) SliceScannerFunc[T] { } func NewMaybeSliceScanner[T any](f MaybeScanValueFunc[T]) SliceScannerFunc[T] { - return func(rows Rows, queryErr error) (values []T, _ error) { + return func(rows Rows, queryErr error) ([]T, error) { + values := make([]T, 0) scan := func(s Scanner) (bool, error) { value, ok, err := f(s) if err != nil {