diff --git a/migrator.go b/migrator.go index ae15d4a..fccf0ff 100644 --- a/migrator.go +++ b/migrator.go @@ -78,6 +78,9 @@ func (m Migrator) HasColumn(value interface{}, name string) bool { func (m Migrator) AlterColumn(value interface{}, name string) error { return m.RunWithoutForeignKey(func() error { return m.recreateTable(value, nil, func(ddl *ddl, stmt *gorm.Statement) (*ddl, []interface{}, error) { + if stmt.Schema == nil { + return nil, nil, fmt.Errorf("failed to alter field with name %v: model with schema is required", name) + } if field := stmt.Schema.LookUpField(name); field != nil { var sqlArgs []interface{} for i, f := range ddl.fields { @@ -157,8 +160,10 @@ func (m Migrator) ColumnTypes(value interface{}) ([]gorm.ColumnType, error) { func (m Migrator) DropColumn(value interface{}, name string) error { return m.RunWithoutForeignKey(func() error { return m.recreateTable(value, nil, func(ddl *ddl, stmt *gorm.Statement) (*ddl, []interface{}, error) { - if field := stmt.Schema.LookUpField(name); field != nil { - name = field.DBName + if stmt.Schema != nil { + if field := stmt.Schema.LookUpField(name); field != nil { + name = field.DBName + } } ddl.removeColumn(name) diff --git a/migrator_test.go b/migrator_test.go index 301d3bc..4e61045 100644 --- a/migrator_test.go +++ b/migrator_test.go @@ -163,6 +163,39 @@ func TestRecreateTableWithView(t *testing.T) { } } +// DropColumn and AlterColumn must accept a table-name string like the base +// GORM migrator and the other dialects do; stmt.Schema is nil in that case +// and used to cause a nil pointer dereference. +func TestMigratorStringTableName(t *testing.T) { + db, err := gorm.Open(Open("file:migrator_string_value?mode=memory&cache=shared"), &gorm.Config{}) + if err != nil { + t.Fatalf("gorm.Open: %v", err) + } + // close the pool so the shared in-memory database is torn down and + // repeated runs (go test -count=N) start from a clean state + t.Cleanup(func() { + if sqlDB, err := db.DB(); err == nil { + _ = sqlDB.Close() + } + }) + if err := db.Exec("CREATE TABLE `string_value_table` (`id` integer, `b` text)").Error; err != nil { + t.Fatal(err) + } + + if err := db.Migrator().DropColumn("string_value_table", "b"); err != nil { + t.Errorf("DropColumn with a table-name string: %v", err) + } + if db.Migrator().HasColumn("string_value_table", "b") { + t.Error("column b still present after DropColumn") + } + + // AlterColumn needs the model schema to build the new column type; a + // table-name string must produce a regular error, not a panic. + if err := db.Migrator().AlterColumn("string_value_table", "id"); err == nil { + t.Error("AlterColumn with a table-name string: expected an error, got nil") + } +} + func openRecreateTestDB(t *testing.T, name string) *gorm.DB { t.Helper() db, err := gorm.Open(Open("file:"+name+"?mode=memory&cache=shared"), &gorm.Config{})