dialect/sql/schema: allow adding DEFAULT to columns on migration #1758 (#2199)

* add feature to SET DEFAULT value when add new default value to a existing column

* refactoring unit tests

* correct misspells

* code review refactors

* refactors

* Update dialect/sql/schema/postgres.go

* Update dialect/sql/schema/postgres.go

* Update dialect/sql/schema/postgres.go

* Update dialect/sql/schema/postgres.go

* Update dialect/sql/schema/postgres.go

Co-authored-by: Ariel Mashraki <7413593+a8m@users.noreply.github.com>
This commit is contained in:
Mehmet Yılmaz
2021-12-20 12:29:07 +03:00
committed by GitHub
parent 71af8263d7
commit 4d01a56b8d
4 changed files with 50 additions and 7 deletions

View File

@@ -375,6 +375,9 @@ func (m *Migrate) changeSet(curr, new *Table) (*changes, error) {
// Change nullability of a column.
case c1.Nullable != c2.Nullable:
change.column.modify = append(change.column.modify, c1)
// Change default value.
case c1.Default != nil && c2.Default == nil:
change.column.modify = append(change.column.modify, c1)
}
}

View File

@@ -301,7 +301,7 @@ func TestMySQL_Create(t *testing.T) {
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectExec(escape("ALTER TABLE `users` ADD COLUMN `age` bigint NOT NULL, ADD COLUMN `ts` timestamp NOT NULL")).
mock.ExpectExec(escape("ALTER TABLE `users` ADD COLUMN `age` bigint NOT NULL, ADD COLUMN `ts` timestamp NOT NULL, MODIFY COLUMN `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
},

View File

@@ -380,7 +380,7 @@ func (d *Postgres) addColumn(c *Column) *sql.ColumnBuilder {
b.Attr("GENERATED BY DEFAULT AS IDENTITY")
}
c.nullable(b)
d.writeDefault(b, c)
d.writeDefault(b, c, "DEFAULT")
if c.Collation != "" {
b.Attr("COLLATE " + strconv.Quote(c.Collation))
}
@@ -389,7 +389,7 @@ func (d *Postgres) addColumn(c *Column) *sql.ColumnBuilder {
// writeDefault writes the `DEFAULT` clause to column builder
// if exists and supported by the driver.
func (d *Postgres) writeDefault(b *sql.ColumnBuilder, c *Column) {
func (d *Postgres) writeDefault(b *sql.ColumnBuilder, c *Column, clause string) {
if c.Default == nil || !c.supportDefault() {
return
}
@@ -403,7 +403,7 @@ func (d *Postgres) writeDefault(b *sql.ColumnBuilder, c *Column) {
attr = fmt.Sprintf("'%s'", strings.ReplaceAll(v, "'", "''"))
}
}
b.Attr("DEFAULT " + attr)
b.Attr(clause + " " + attr)
}
// alterColumn returns list of ColumnBuilder for applying in order to alter a column.
@@ -415,9 +415,17 @@ func (d *Postgres) alterColumn(c *Column) (ops []*sql.ColumnBuilder) {
} else {
ops = append(ops, b.Column(c.Name).Attr("SET NOT NULL"))
}
if c.Default != nil && c.supportDefault() {
ops = append(ops, d.writeSetDefault(b.Column(c.Name), c))
}
return ops
}
func (d *Postgres) writeSetDefault(b *sql.ColumnBuilder, c *Column) *sql.ColumnBuilder {
d.writeDefault(b, c, "SET DEFAULT")
return b
}
// hasUniqueName reports if the index has a unique name in the schema.
func hasUniqueName(i *Index) bool {
// Trim the "_key" suffix if it was added by Postgres for implicit indexes.

View File

@@ -211,6 +211,8 @@ func TestPostgres_Create(t *testing.T) {
mock.ExpectQuery(escape(fmt.Sprintf(indexesQuery, "CURRENT_SCHEMA()", "users"))).
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "primary", "unique", "seq_in_index"}).
AddRow("users_pkey", "id", "t", "t", 0))
mock.ExpectExec(escape(`ALTER TABLE "users" ALTER COLUMN "block_size" TYPE bigint, ALTER COLUMN "block_size" SET NOT NULL, ALTER COLUMN "block_size" SET DEFAULT current_setting('block_size')::bigint`)).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
},
},
@@ -288,7 +290,7 @@ func TestPostgres_Create(t *testing.T) {
mock.ExpectQuery(escape(fmt.Sprintf(indexesQuery, "CURRENT_SCHEMA()", "users"))).
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "primary", "unique", "seq_in_index"}).
AddRow("users_pkey", "id", "t", "t", 0))
mock.ExpectExec(escape(`ALTER TABLE "users" ADD COLUMN "age" bigint NOT NULL, ALTER COLUMN "updated_at" TYPE timestamp with time zone, ALTER COLUMN "updated_at" DROP NOT NULL, ALTER COLUMN "deleted_at" TYPE timestamp with time zone, ALTER COLUMN "deleted_at" DROP NOT NULL`)).
mock.ExpectExec(escape(`ALTER TABLE "users" ADD COLUMN "age" bigint NOT NULL, ALTER COLUMN "created_at" TYPE date, ALTER COLUMN "created_at" SET NOT NULL, ALTER COLUMN "created_at" SET DEFAULT CURRENT_DATE, ALTER COLUMN "updated_at" TYPE timestamp with time zone, ALTER COLUMN "updated_at" DROP NOT NULL, ALTER COLUMN "deleted_at" TYPE timestamp with time zone, ALTER COLUMN "deleted_at" DROP NOT NULL`)).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
},
@@ -513,6 +515,36 @@ func TestPostgres_Create(t *testing.T) {
mock.ExpectCommit()
},
},
{
name: "modify column default value",
tables: []*Table{
{
Name: "users",
Columns: []*Column{
{Name: "id", Type: field.TypeInt, Increment: true},
{Name: "name", Type: field.TypeString, Default: "unknown"},
},
PrimaryKey: []*Column{
{Name: "id", Type: field.TypeInt, Increment: true},
},
},
},
before: func(mock pgMock) {
mock.start("120000")
mock.tableExists("users", true)
mock.ExpectQuery(escape(`SELECT "column_name", "data_type", "is_nullable", "column_default", "udt_name", "numeric_precision", "numeric_scale", "character_maximum_length" FROM "information_schema"."columns" WHERE "table_schema" = CURRENT_SCHEMA() AND "table_name" = $1`)).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"column_name", "data_type", "is_nullable", "column_default", "udt_name", "numeric_precision", "numeric_scale", "character_maximum_length"}).
AddRow("id", "bigint", "NO", "NULL", "int8", nil, nil, nil).
AddRow("name", "character", "NO", "NULL", "bpchar", nil, nil, nil))
mock.ExpectQuery(escape(fmt.Sprintf(indexesQuery, "CURRENT_SCHEMA()", "users"))).
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "primary", "unique", "seq_in_index"}).
AddRow("users_pkey", "id", "t", "t", 0))
mock.ExpectExec(escape(`ALTER TABLE "users" ALTER COLUMN "name" TYPE varchar, ALTER COLUMN "name" SET NOT NULL, ALTER COLUMN "name" SET DEFAULT 'unknown'`)).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
},
},
{
name: "apply uniqueness on column",
tables: []*Table{
@@ -923,7 +955,7 @@ func TestPostgres_Create(t *testing.T) {
Name: "users",
Columns: []*Column{
{Name: "id", Type: field.TypeInt, Increment: true},
{Name: "name", Type: field.TypeString, Nullable: false, SchemaType: map[string]string{dialect.Postgres: "varchar(20)"}},
{Name: "name", Type: field.TypeString, SchemaType: map[string]string{dialect.Postgres: "varchar(20)"}, Default: "unknown"},
},
PrimaryKey: []*Column{
{Name: "id", Type: field.TypeInt, Increment: true},
@@ -941,7 +973,7 @@ func TestPostgres_Create(t *testing.T) {
mock.ExpectQuery(escape(fmt.Sprintf(indexesQuery, "CURRENT_SCHEMA()", "users"))).
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "primary", "unique", "seq_in_index"}).
AddRow("users_pkey", "id", "t", "t", 0))
mock.ExpectExec(escape(`ALTER TABLE "users" ALTER COLUMN "name" TYPE varchar(20), ALTER COLUMN "name" SET NOT NULL`)).
mock.ExpectExec(escape(`ALTER TABLE "users" ALTER COLUMN "name" TYPE varchar(20), ALTER COLUMN "name" SET NOT NULL, ALTER COLUMN "name" SET DEFAULT 'unknown'`)).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
},