test(operator): write more unit tests

This commit is contained in:
grandwizard28 2025-07-21 16:05:28 +05:30
parent e96eb187e4
commit 6edcaeaebc
No known key found for this signature in database
GPG Key ID: C7A26EDC5B7054B7
2 changed files with 278 additions and 146 deletions

View File

@ -42,11 +42,42 @@ func (operator *Operator) RenameTable(table *Table, newName TableName) [][]byte
}
func (operator *Operator) AlterTable(oldTable *Table, oldTableUniqueConstraints []*UniqueConstraint, newTable *Table) [][]byte {
// The following has to be done in order:
// - Drop constraints
// - Drop columns (some columns might be part of constraints therefore this depends on Step 1)
// - Add columns, then modify columns
// - Rename table
// - Add constraints (some constraints might be part of columns therefore this depends on Step 3, constraint names also depend on table name which is changed in Step 4)
// - Create unique indices from unique constraints for the new table
sql := [][]byte{}
// Check if the name has changed
if oldTable.Name != newTable.Name {
sql = append(sql, operator.RenameTable(oldTable, newTable.Name)...)
// Drop primary key constraint if it is in the old table but not in the new table.
if oldTable.PrimaryKeyConstraint != nil && newTable.PrimaryKeyConstraint == nil {
sql = append(sql, operator.DropConstraint(oldTable, oldTableUniqueConstraints, oldTable.PrimaryKeyConstraint)...)
}
// Drop primary key constraint if it is in the old table and the new table but they are different.
if oldTable.PrimaryKeyConstraint != nil && newTable.PrimaryKeyConstraint != nil && !oldTable.PrimaryKeyConstraint.Equals(newTable.PrimaryKeyConstraint) {
sql = append(sql, operator.DropConstraint(oldTable, oldTableUniqueConstraints, newTable.PrimaryKeyConstraint)...)
}
// Drop foreign key constraints that are in the old table but not in the new table.
for _, fkConstraint := range oldTable.ForeignKeyConstraints {
if index := operator.findForeignKeyConstraint(newTable, fkConstraint); index == -1 {
sql = append(sql, operator.DropConstraint(oldTable, oldTableUniqueConstraints, fkConstraint)...)
}
}
// Drop all unique constraints.
for _, uniqueConstraint := range oldTableUniqueConstraints {
sql = append(sql, operator.DropConstraint(oldTable, oldTableUniqueConstraints, uniqueConstraint)...)
}
// Reduce number of drops for engines with no SCreateAndDropConstraint.
if !operator.support.SCreateAndDropConstraint && len(sql) > 0 {
// Do not send the unique constraints to recreate table. We will change them to indexes at the end.
sql = operator.RecreateTable(oldTable, nil)
}
// Drop columns that are in the old table but not in the new table.
@ -64,8 +95,24 @@ func (operator *Operator) AlterTable(oldTable *Table, oldTableUniqueConstraints
}
// Modify columns that are in the new table and in the old table
alterColumnSQLs := [][]byte{}
for _, column := range newTable.Columns {
sql = append(sql, operator.AlterColumn(oldTable, oldTableUniqueConstraints, column)...)
alterColumnSQLs = append(alterColumnSQLs, operator.AlterColumn(oldTable, oldTableUniqueConstraints, column)...)
}
// Reduce number of drops for engines with no SAlterTableAlterColumnSetAndDrop.
if !operator.support.SAlterTableAlterColumnSetAndDrop && len(alterColumnSQLs) > 0 {
// Do not send the unique constraints to recreate table. We will change them to indexes at the end.
sql = append(sql, operator.RecreateTable(oldTable, nil)...)
}
if operator.support.SAlterTableAlterColumnSetAndDrop && len(alterColumnSQLs) > 0 {
sql = append(sql, alterColumnSQLs...)
}
// Check if the name has changed
if oldTable.Name != newTable.Name {
sql = append(sql, operator.RenameTable(oldTable, newTable.Name)...)
}
// If the old table does not have a primary key constraint and the new table does, we need to create it.
@ -76,19 +123,8 @@ func (operator *Operator) AlterTable(oldTable *Table, oldTableUniqueConstraints
}
if oldTable.PrimaryKeyConstraint != nil {
if newTable.PrimaryKeyConstraint == nil {
sql = append(sql, operator.DropConstraint(oldTable, oldTableUniqueConstraints, oldTable.PrimaryKeyConstraint)...)
} else {
if !oldTable.PrimaryKeyConstraint.Equals(newTable.PrimaryKeyConstraint) {
sql = append(sql, operator.CreateConstraint(oldTable, oldTableUniqueConstraints, newTable.PrimaryKeyConstraint)...)
}
}
}
// Drop foreign key constraints that are in the old table but not in the new table.
for _, fkConstraint := range oldTable.ForeignKeyConstraints {
if index := operator.findForeignKeyConstraint(newTable, fkConstraint); index == -1 {
sql = append(sql, operator.DropConstraint(oldTable, oldTableUniqueConstraints, fkConstraint)...)
if !oldTable.PrimaryKeyConstraint.Equals(newTable.PrimaryKeyConstraint) {
sql = append(sql, operator.CreateConstraint(oldTable, oldTableUniqueConstraints, newTable.PrimaryKeyConstraint)...)
}
}
@ -99,12 +135,12 @@ func (operator *Operator) AlterTable(oldTable *Table, oldTableUniqueConstraints
}
}
// Drop all unique constraints and create indices for the new table.
// Create indices for the new table.
for _, uniqueConstraint := range oldTableUniqueConstraints {
sql = append(sql, operator.DropConstraint(oldTable, oldTableUniqueConstraints, uniqueConstraint)...)
sql = append(sql, uniqueConstraint.ToIndex(oldTable.Name).ToCreateSQL(operator.fmter))
}
// Remove duplicate SQLs.
return slices.CompactFunc(sql, func(a, b []byte) bool {
return string(a) == string(b)
})
@ -286,7 +322,11 @@ func (operator *Operator) DropConstraint(table *Table, uniqueConstraints []*Uniq
return [][]byte{uniqueConstraints[uniqueConstraintIndex].ToDropSQL(operator.fmter, table.Name)}
}
return operator.RecreateTable(table, append(uniqueConstraints[:uniqueConstraintIndex], uniqueConstraints[uniqueConstraintIndex+1:]...))
var copyOfUniqueConstraints []*UniqueConstraint
copyOfUniqueConstraints = append(copyOfUniqueConstraints, uniqueConstraints[:uniqueConstraintIndex]...)
copyOfUniqueConstraints = append(copyOfUniqueConstraints, uniqueConstraints[uniqueConstraintIndex+1:]...)
return operator.RecreateTable(table, copyOfUniqueConstraints)
}
if operator.support.SCreateAndDropConstraint {

View File

@ -1,6 +1,7 @@
package sqlschema
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
@ -707,8 +708,7 @@ func TestOperatorAlterTable(t *testing.T) {
table *Table
uniqueConstraints []*UniqueConstraint
newTable *Table
support OperatorSupport
expectedSQLs [][]byte
expected map[OperatorSupport][][]byte
}{
{
name: "NoOperation",
@ -725,9 +725,12 @@ func TestOperatorAlterTable(t *testing.T) {
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "name", DataType: DataTypeText, Nullable: false, Default: ""},
},
ForeignKeyConstraints: []*ForeignKeyConstraint{},
},
expected: map[OperatorSupport][][]byte{
{SCreateAndDropConstraint: false, SAlterTableAddAndDropColumnIfNotExistsAndExists: false, SAlterTableAlterColumnSetAndDrop: false}: {},
{SCreateAndDropConstraint: true, SAlterTableAddAndDropColumnIfNotExistsAndExists: true, SAlterTableAlterColumnSetAndDrop: true}: {},
},
support: OperatorSupport{},
expectedSQLs: [][]byte{},
},
{
name: "RenameTable",
@ -742,10 +745,15 @@ func TestOperatorAlterTable(t *testing.T) {
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
},
ForeignKeyConstraints: []*ForeignKeyConstraint{},
},
support: OperatorSupport{},
expectedSQLs: [][]byte{
[]byte(`ALTER TABLE "users" RENAME TO "users_new"`),
expected: map[OperatorSupport][][]byte{
{SCreateAndDropConstraint: false, SAlterTableAddAndDropColumnIfNotExistsAndExists: false, SAlterTableAlterColumnSetAndDrop: false}: {
[]byte(`ALTER TABLE "users" RENAME TO "users_new"`),
},
{SCreateAndDropConstraint: true, SAlterTableAddAndDropColumnIfNotExistsAndExists: true, SAlterTableAlterColumnSetAndDrop: true}: {
[]byte(`ALTER TABLE "users" RENAME TO "users_new"`),
},
},
},
{
@ -764,149 +772,227 @@ func TestOperatorAlterTable(t *testing.T) {
{Name: "name", DataType: DataTypeText, Nullable: false, Default: ""},
{Name: "age", DataType: DataTypeInteger, Nullable: true, Default: ""},
},
ForeignKeyConstraints: []*ForeignKeyConstraint{},
},
support: OperatorSupport{
SAlterTableAddAndDropColumnIfNotExistsAndExists: true,
},
expectedSQLs: [][]byte{
[]byte(`ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "age" INTEGER`),
expected: map[OperatorSupport][][]byte{
{SCreateAndDropConstraint: false, SAlterTableAddAndDropColumnIfNotExistsAndExists: false, SAlterTableAlterColumnSetAndDrop: false}: {
[]byte(`ALTER TABLE "users" ADD COLUMN "age" INTEGER`),
},
{SCreateAndDropConstraint: true, SAlterTableAddAndDropColumnIfNotExistsAndExists: true, SAlterTableAlterColumnSetAndDrop: true}: {
[]byte(`ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "age" INTEGER`),
},
},
},
{
name: "AddColumn_NullableNoDefault_SAlterTableAddAndDropColumnIfNotExistsAndExistsFalse",
name: "CreatePrimaryKeyConstraint",
table: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "name", DataType: DataTypeText, Nullable: false, Default: ""},
},
},
newTable: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
},
PrimaryKeyConstraint: &PrimaryKeyConstraint{
ColumnNames: []ColumnName{"id"},
},
ForeignKeyConstraints: []*ForeignKeyConstraint{},
},
expected: map[OperatorSupport][][]byte{
{SCreateAndDropConstraint: false, SAlterTableAddAndDropColumnIfNotExistsAndExists: false, SAlterTableAlterColumnSetAndDrop: false}: {
[]byte(`CREATE TABLE IF NOT EXISTS "users__temp" ("id" INTEGER NOT NULL, CONSTRAINT "pk_users" PRIMARY KEY ("id"))`),
[]byte(`INSERT INTO "users__temp" ("id") SELECT "id" FROM "users"`),
[]byte(`DROP TABLE IF EXISTS "users"`),
[]byte(`ALTER TABLE "users__temp" RENAME TO "users"`),
},
{SCreateAndDropConstraint: true, SAlterTableAddAndDropColumnIfNotExistsAndExists: true, SAlterTableAlterColumnSetAndDrop: true}: {
[]byte(`ALTER TABLE "users" ADD CONSTRAINT "pk_users" PRIMARY KEY ("id")`),
},
},
},
{
name: "DropPrimaryKeyConstraint_AlterColumnNullable",
table: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "name", DataType: DataTypeText, Nullable: true, Default: ""},
},
PrimaryKeyConstraint: &PrimaryKeyConstraint{ColumnNames: []ColumnName{"id"}},
},
newTable: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "name", DataType: DataTypeText, Nullable: false, Default: ""},
},
ForeignKeyConstraints: []*ForeignKeyConstraint{},
},
expected: map[OperatorSupport][][]byte{
{SCreateAndDropConstraint: false, SAlterTableAddAndDropColumnIfNotExistsAndExists: false, SAlterTableAlterColumnSetAndDrop: false}: {
// first drop to remove the primary key constraint
[]byte(`CREATE TABLE IF NOT EXISTS "users__temp" ("id" INTEGER NOT NULL, "name" TEXT)`),
[]byte(`INSERT INTO "users__temp" ("id", "name") SELECT "id", "name" FROM "users"`),
[]byte(`DROP TABLE IF EXISTS "users"`),
[]byte(`ALTER TABLE "users__temp" RENAME TO "users"`),
// second drop to make the column nullable
[]byte(`CREATE TABLE IF NOT EXISTS "users__temp" ("id" INTEGER NOT NULL, "name" TEXT NOT NULL)`),
[]byte(`INSERT INTO "users__temp" ("id", "name") SELECT "id", "name" FROM "users"`),
[]byte(`DROP TABLE IF EXISTS "users"`),
[]byte(`ALTER TABLE "users__temp" RENAME TO "users"`),
},
{SCreateAndDropConstraint: true, SAlterTableAddAndDropColumnIfNotExistsAndExists: true, SAlterTableAlterColumnSetAndDrop: true}: {
[]byte(`ALTER TABLE "users" DROP CONSTRAINT IF EXISTS "pk_users"`),
[]byte(`ALTER TABLE "users" ALTER COLUMN "name" SET NOT NULL`),
},
},
},
{
name: "DropForeignKeyConstraint_DropColumn",
table: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "org_id", DataType: DataTypeInteger, Nullable: false, Default: ""},
},
ForeignKeyConstraints: []*ForeignKeyConstraint{
{ReferencingColumnName: "org_id", ReferencedTableName: "orgs", ReferencedColumnName: "id"},
},
},
newTable: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
},
ForeignKeyConstraints: []*ForeignKeyConstraint{},
},
expected: map[OperatorSupport][][]byte{
{SCreateAndDropConstraint: false, SAlterTableAddAndDropColumnIfNotExistsAndExists: false, SAlterTableAlterColumnSetAndDrop: false}: {
// first drop to remove the foreign key constraint
[]byte(`CREATE TABLE IF NOT EXISTS "users__temp" ("id" INTEGER NOT NULL, "org_id" INTEGER NOT NULL)`),
[]byte(`INSERT INTO "users__temp" ("id", "org_id") SELECT "id", "org_id" FROM "users"`),
[]byte(`DROP TABLE IF EXISTS "users"`),
[]byte(`ALTER TABLE "users__temp" RENAME TO "users"`),
// second drop to remove the column
[]byte(`ALTER TABLE "users" DROP COLUMN "org_id"`),
},
{SCreateAndDropConstraint: true, SAlterTableAddAndDropColumnIfNotExistsAndExists: true, SAlterTableAlterColumnSetAndDrop: true}: {
// first drop to remove the foreign key constraint
[]byte(`ALTER TABLE "users" DROP CONSTRAINT IF EXISTS "fk_users_org_id"`),
// second drop to remove the column
[]byte(`ALTER TABLE "users" DROP COLUMN IF EXISTS "org_id"`),
},
},
},
{
name: "DropMultipleConstraints",
table: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "name", DataType: DataTypeText, Nullable: false, Default: ""},
{Name: "age", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "org_id", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "team_id", DataType: DataTypeInteger, Nullable: false, Default: ""},
},
PrimaryKeyConstraint: &PrimaryKeyConstraint{ColumnNames: []ColumnName{"id"}},
ForeignKeyConstraints: []*ForeignKeyConstraint{
{ReferencingColumnName: "org_id", ReferencedTableName: "orgs", ReferencedColumnName: "id"},
{ReferencingColumnName: "team_id", ReferencedTableName: "teams", ReferencedColumnName: "id"},
},
},
uniqueConstraints: []*UniqueConstraint{
{ColumnNames: []ColumnName{"name", "age"}},
},
newTable: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "name", DataType: DataTypeText, Nullable: false, Default: ""},
{Name: "age", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "org_id", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "team_id", DataType: DataTypeInteger, Nullable: false, Default: ""},
},
ForeignKeyConstraints: []*ForeignKeyConstraint{
{ReferencingColumnName: "team_id", ReferencedTableName: "teams", ReferencedColumnName: "id"},
},
},
expected: map[OperatorSupport][][]byte{
{SCreateAndDropConstraint: false, SAlterTableAddAndDropColumnIfNotExistsAndExists: false, SAlterTableAlterColumnSetAndDrop: false}: {
[]byte(`CREATE TABLE IF NOT EXISTS "users__temp" ("id" INTEGER NOT NULL, "name" TEXT NOT NULL, "age" INTEGER NOT NULL, "org_id" INTEGER NOT NULL, "team_id" INTEGER NOT NULL, CONSTRAINT "fk_users_team_id" FOREIGN KEY ("team_id") REFERENCES "teams" ("id"))`),
[]byte(`INSERT INTO "users__temp" ("id", "name", "age", "org_id", "team_id") SELECT "id", "name", "age", "org_id", "team_id" FROM "users"`),
[]byte(`DROP TABLE IF EXISTS "users"`),
[]byte(`ALTER TABLE "users__temp" RENAME TO "users"`),
[]byte(`CREATE UNIQUE INDEX IF NOT EXISTS "uq_users_name_age" ON "users" ("name", "age")`),
},
{SCreateAndDropConstraint: true, SAlterTableAddAndDropColumnIfNotExistsAndExists: true, SAlterTableAlterColumnSetAndDrop: true}: {
[]byte(`ALTER TABLE "users" DROP CONSTRAINT IF EXISTS "pk_users"`),
[]byte(`ALTER TABLE "users" DROP CONSTRAINT IF EXISTS "fk_users_org_id"`),
[]byte(`ALTER TABLE "users" DROP CONSTRAINT IF EXISTS "uq_users_name_age"`),
[]byte(`CREATE UNIQUE INDEX IF NOT EXISTS "uq_users_name_age" ON "users" ("name", "age")`),
},
},
},
{
name: "DropUniqueConstraints_AlterMultipleColumns",
table: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "name", DataType: DataTypeText, Nullable: true, Default: ""},
{Name: "age", DataType: DataTypeInteger, Nullable: true, Default: ""},
},
},
support: OperatorSupport{
SAlterTableAddAndDropColumnIfNotExistsAndExists: false,
},
expectedSQLs: [][]byte{
[]byte(`ALTER TABLE "users" ADD COLUMN "age" INTEGER`),
},
},
{
name: "CreatePrimaryKeyConstraint_SCreateAndDropConstraintTrue",
table: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
},
},
newTable: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
},
PrimaryKeyConstraint: &PrimaryKeyConstraint{
ColumnNames: []ColumnName{"id"},
},
},
support: OperatorSupport{
SCreateAndDropConstraint: true,
},
expectedSQLs: [][]byte{
[]byte(`ALTER TABLE "users" ADD CONSTRAINT "pk_users" PRIMARY KEY ("id")`),
},
},
{
name: "CreatePrimaryKeyConstraint_SCreateAndDropConstrainttFalse",
table: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
},
},
newTable: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
},
PrimaryKeyConstraint: &PrimaryKeyConstraint{
ColumnNames: []ColumnName{"id"},
},
},
support: OperatorSupport{
SCreateAndDropConstraint: false,
},
expectedSQLs: [][]byte{
[]byte(`CREATE TABLE IF NOT EXISTS "users__temp" ("id" INTEGER NOT NULL, CONSTRAINT "pk_users" PRIMARY KEY ("id"))`),
[]byte(`INSERT INTO "users__temp" ("id") SELECT "id" FROM "users"`),
[]byte(`DROP TABLE IF EXISTS "users"`),
[]byte(`ALTER TABLE "users__temp" RENAME TO "users"`),
},
},
{
name: "DropPrimaryKeyConstraint_AlterColumnNullable_SAlterTableAddAndDropColumnIfNotExistsAndExistsFalse_SAlterTableAlterColumnSetAndDropFalse_SCreateAndDropConstraintFalse",
table: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "name", DataType: DataTypeText, Nullable: true, Default: ""},
{Name: "email", DataType: DataTypeInteger, Nullable: false, Default: ""},
},
PrimaryKeyConstraint: &PrimaryKeyConstraint{ColumnNames: []ColumnName{"id"}},
ForeignKeyConstraints: []*ForeignKeyConstraint{
{ReferencingColumnName: "org_id", ReferencedTableName: "orgs", ReferencedColumnName: "id"},
},
},
uniqueConstraints: []*UniqueConstraint{
{ColumnNames: []ColumnName{"email"}},
{name: "my_name_constraint", ColumnNames: []ColumnName{"name"}},
},
newTable: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "name", DataType: DataTypeText, Nullable: false, Default: ""},
},
},
support: OperatorSupport{
SAlterTableAddAndDropColumnIfNotExistsAndExists: false,
SAlterTableAlterColumnSetAndDrop: false,
SCreateAndDropConstraint: false,
},
expectedSQLs: [][]byte{
// first drop to make the column nullable
[]byte(`CREATE TABLE IF NOT EXISTS "users__temp" ("id" INTEGER NOT NULL, "name" TEXT NOT NULL, CONSTRAINT "pk_users" PRIMARY KEY ("id"))`),
[]byte(`INSERT INTO "users__temp" ("id", "name") SELECT "id", "name" FROM "users"`),
[]byte(`DROP TABLE IF EXISTS "users"`),
[]byte(`ALTER TABLE "users__temp" RENAME TO "users"`),
// second drop to remove the primary key constraint
[]byte(`CREATE TABLE IF NOT EXISTS "users__temp" ("id" INTEGER NOT NULL, "name" TEXT NOT NULL)`),
[]byte(`INSERT INTO "users__temp" ("id", "name") SELECT "id", "name" FROM "users"`),
[]byte(`DROP TABLE IF EXISTS "users"`),
[]byte(`ALTER TABLE "users__temp" RENAME TO "users"`),
},
},
{
name: "DropPrimaryKeyConstraint_AlterColumnNullable_SAlterTableAddAndDropColumnIfNotExistsAndExistsTrue_SAlterTableAlterColumnSetAndDropTrue_SCreateAndDropConstraintTrue",
table: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "name", DataType: DataTypeText, Nullable: true, Default: ""},
{Name: "age", DataType: DataTypeInteger, Nullable: false, Default: "0"},
{Name: "email", DataType: DataTypeInteger, Nullable: false, Default: ""},
},
PrimaryKeyConstraint: &PrimaryKeyConstraint{ColumnNames: []ColumnName{"id"}},
},
newTable: &Table{
Name: "users",
Columns: []*Column{
{Name: "id", DataType: DataTypeInteger, Nullable: false, Default: ""},
{Name: "name", DataType: DataTypeText, Nullable: false, Default: ""},
ForeignKeyConstraints: []*ForeignKeyConstraint{
{ReferencingColumnName: "org_id", ReferencedTableName: "orgs", ReferencedColumnName: "id"},
},
},
support: OperatorSupport{
SAlterTableAddAndDropColumnIfNotExistsAndExists: true,
SAlterTableAlterColumnSetAndDrop: true,
SCreateAndDropConstraint: true,
},
expectedSQLs: [][]byte{
[]byte(`ALTER TABLE "users" ALTER COLUMN "name" SET NOT NULL`),
[]byte(`ALTER TABLE "users" DROP CONSTRAINT IF EXISTS "pk_users"`),
expected: map[OperatorSupport][][]byte{
{SCreateAndDropConstraint: false, SAlterTableAddAndDropColumnIfNotExistsAndExists: false, SAlterTableAlterColumnSetAndDrop: false}: {
// first drop to remove unique constraint
[]byte(`CREATE TABLE IF NOT EXISTS "users__temp" ("id" INTEGER NOT NULL, "name" TEXT, "age" INTEGER, "email" INTEGER NOT NULL, CONSTRAINT "pk_users" PRIMARY KEY ("id"), CONSTRAINT "fk_users_org_id" FOREIGN KEY ("org_id") REFERENCES "orgs" ("id"))`),
[]byte(`INSERT INTO "users__temp" ("id", "name", "age", "email") SELECT "id", "name", "age", "email" FROM "users"`),
[]byte(`DROP TABLE IF EXISTS "users"`),
[]byte(`ALTER TABLE "users__temp" RENAME TO "users"`),
// second drop to change all columns
[]byte(`CREATE TABLE IF NOT EXISTS "users__temp" ("id" INTEGER NOT NULL, "name" TEXT NOT NULL, "age" INTEGER NOT NULL DEFAULT 0, "email" INTEGER NOT NULL, CONSTRAINT "pk_users" PRIMARY KEY ("id"), CONSTRAINT "fk_users_org_id" FOREIGN KEY ("org_id") REFERENCES "orgs" ("id"))`),
[]byte(`INSERT INTO "users__temp" ("id", "name", "age", "email") SELECT "id", "name", "age", "email" FROM "users"`),
[]byte(`DROP TABLE IF EXISTS "users"`),
[]byte(`ALTER TABLE "users__temp" RENAME TO "users"`),
// create unique index for the constraint
[]byte(`CREATE UNIQUE INDEX IF NOT EXISTS "uq_users_email" ON "users" ("email")`),
[]byte(`CREATE UNIQUE INDEX IF NOT EXISTS "uq_users_name" ON "users" ("name")`),
},
{SCreateAndDropConstraint: true, SAlterTableAddAndDropColumnIfNotExistsAndExists: true, SAlterTableAlterColumnSetAndDrop: true}: {
[]byte(`ALTER TABLE "users" DROP CONSTRAINT IF EXISTS "uq_users_email"`),
[]byte(`ALTER TABLE "users" DROP CONSTRAINT IF EXISTS "my_name_constraint"`),
[]byte(`ALTER TABLE "users" ALTER COLUMN "name" SET NOT NULL`),
[]byte(`ALTER TABLE "users" ALTER COLUMN "age" SET NOT NULL`),
[]byte(`ALTER TABLE "users" ALTER COLUMN "age" SET DEFAULT 0`),
[]byte(`CREATE UNIQUE INDEX IF NOT EXISTS "uq_users_email" ON "users" ("email")`),
[]byte(`CREATE UNIQUE INDEX IF NOT EXISTS "uq_users_name" ON "users" ("name")`),
},
},
},
}
@ -914,11 +1000,17 @@ func TestOperatorAlterTable(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
fmter := NewFormatter(schema.NewNopFormatter().Dialect())
operator := NewOperator(fmter, testCase.support)
for support, sqls := range testCase.expected {
operator := NewOperator(fmter, support)
clonedTable := testCase.table.Clone()
actuals := operator.AlterTable(testCase.table, testCase.uniqueConstraints, testCase.newTable)
assert.Equal(t, testCase.expectedSQLs, actuals)
assert.Equal(t, testCase.newTable, testCase.table)
actuals := operator.AlterTable(clonedTable, testCase.uniqueConstraints, testCase.newTable)
for _, sql := range actuals {
fmt.Println(string(sql))
}
assert.Equal(t, sqls, actuals)
assert.EqualValues(t, testCase.newTable, clonedTable)
}
})
}
}