Skip to content

Commit

Permalink
Handle DEFERRABLE and NOT DEFERRABLE constraints in `CREATE TABLE…
Browse files Browse the repository at this point in the history
…` statements (#556)

Handle `DEFERRABLE` and `NOT DEFERRABLE` constraints in `CREATE TABLE`
statements

`NOT DEFERRABLE` constraints are the default, so add testcases to ensure
that constraints with this modifier are converted to `OpCreateTable`
operations.

`DEFERRABLE` constraints and the `INITIALLY IMMEDIATE` and `INITIALLY
DEFERRED` modifiers are not supported, so fall back to raw SQL
operations when these options are present.
  • Loading branch information
andrew-farries authored Dec 19, 2024
1 parent 627b64e commit c64edc6
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 1 deletion.
16 changes: 16 additions & 0 deletions pkg/sql2pgroll/create_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,28 @@ func convertColumnDef(tableName string, col *pgq.ColumnDef) (*migrations.Column,
if foreignKey == nil {
return nil, nil
}
case pgq.ConstrType_CONSTR_ATTR_NOT_DEFERRABLE:
// NOT DEFERRABLE constraints are the default and are supported, but no
// extra annotation is needed
continue
case pgq.ConstrType_CONSTR_GENERATED:
// Generated columns are not supported
return nil, nil
case pgq.ConstrType_CONSTR_IDENTITY:
// Identity columns are not supported
return nil, nil
case pgq.ConstrType_CONSTR_ATTR_DEFERRABLE:
// Deferrable constraints are not supported
return nil, nil
case pgq.ConstrType_CONSTR_ATTR_IMMEDIATE:
// Initially immediate deferred constraints are not supported
return nil, nil
case pgq.ConstrType_CONSTR_ATTR_DEFERRED:
// Initially deferred deferred constraints are not supported
return nil, nil
default:
// Any other type of constraint is not supported
return nil, nil
}
}

Expand Down
45 changes: 44 additions & 1 deletion pkg/sql2pgroll/create_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,18 @@ func TestConvertCreateTableStatements(t *testing.T) {
sql: "CREATE TABLE foo(a int UNIQUE)",
expectedOp: expect.CreateTableOp5,
},
{
sql: "CREATE TABLE foo(a int UNIQUE NOT DEFERRABLE)",
expectedOp: expect.CreateTableOp5,
},
{
sql: "CREATE TABLE foo(a int PRIMARY KEY)",
expectedOp: expect.CreateTableOp6,
},
{
sql: "CREATE TABLE foo(a int PRIMARY KEY NOT DEFERRABLE)",
expectedOp: expect.CreateTableOp6,
},
{
sql: "CREATE TABLE foo(a int CHECK (a > 0))",
expectedOp: expect.CreateTableOp10,
Expand All @@ -49,33 +57,57 @@ func TestConvertCreateTableStatements(t *testing.T) {
expectedOp: expect.CreateTableOp11,
},
{
sql: "CREATE TABLE foo(a int REFERENCES bar(b))",
sql: "CREATE TABLE foo(a int REFERENCES bar(b) NOT DEFERRABLE)",
expectedOp: expect.CreateTableOp12,
},
{
sql: "CREATE TABLE foo(a int REFERENCES bar(b) ON UPDATE NO ACTION)",
expectedOp: expect.CreateTableOp12,
},
{
sql: "CREATE TABLE foo(a int REFERENCES bar(b) ON UPDATE NO ACTION NOT DEFERRABLE)",
expectedOp: expect.CreateTableOp12,
},
{
sql: "CREATE TABLE foo(a int REFERENCES bar(b) ON DELETE NO ACTION)",
expectedOp: expect.CreateTableOp12,
},
{
sql: "CREATE TABLE foo(a int REFERENCES bar(b) ON DELETE NO ACTION NOT DEFERRABLE)",
expectedOp: expect.CreateTableOp12,
},
{
sql: "CREATE TABLE foo(a int REFERENCES bar(b) ON DELETE RESTRICT)",
expectedOp: expect.CreateTableOp13,
},
{
sql: "CREATE TABLE foo(a int REFERENCES bar(b) ON DELETE RESTRICT NOT DEFERRABLE)",
expectedOp: expect.CreateTableOp13,
},
{
sql: "CREATE TABLE foo(a int REFERENCES bar(b) ON DELETE SET NULL)",
expectedOp: expect.CreateTableOp14,
},
{
sql: "CREATE TABLE foo(a int REFERENCES bar(b) ON DELETE SET NULL NOT DEFERRABLE)",
expectedOp: expect.CreateTableOp14,
},
{
sql: "CREATE TABLE foo(a int REFERENCES bar(b) ON DELETE SET DEFAULT)",
expectedOp: expect.CreateTableOp15,
},
{
sql: "CREATE TABLE foo(a int REFERENCES bar(b) ON DELETE SET DEFAULT NOT DEFERRABLE)",
expectedOp: expect.CreateTableOp15,
},
{
sql: "CREATE TABLE foo(a int REFERENCES bar(b) ON DELETE CASCADE)",
expectedOp: expect.CreateTableOp16,
},
{
sql: "CREATE TABLE foo(a int REFERENCES bar(b) ON DELETE CASCADE NOT DEFERRABLE)",
expectedOp: expect.CreateTableOp16,
},
{
sql: "CREATE TABLE foo(a varchar(255))",
expectedOp: expect.CreateTableOp3,
Expand Down Expand Up @@ -203,6 +235,17 @@ func TestUnconvertableCreateTableStatements(t *testing.T) {
// Generated columns are not supported
"CREATE TABLE foo(a int GENERATED ALWAYS AS (1) STORED)",
"CREATE TABLE foo(a int GENERATED ALWAYS AS IDENTITY)",

// Deferrable constraints are not supported
"CREATE TABLE foo(a int UNIQUE DEFERRABLE)",
"CREATE TABLE foo(a int PRIMARY KEY DEFERRABLE)",
"CREATE TABLE foo(a int REFERENCES bar(b) DEFERRABLE)",
"CREATE TABLE foo(a int UNIQUE DEFERRABLE INITIALLY IMMEDIATE)",
"CREATE TABLE foo(a int PRIMARY KEY DEFERRABLE INITIALLY IMMEDIATE)",
"CREATE TABLE foo(a int REFERENCES bar(b) DEFERRABLE INITIALLY IMMEDIATE)",
"CREATE TABLE foo(a int UNIQUE DEFERRABLE INITIALLY DEFERRED)",
"CREATE TABLE foo(a int PRIMARY KEY DEFERRABLE INITIALLY DEFERRED)",
"CREATE TABLE foo(a int REFERENCES bar(b) DEFERRABLE INITIALLY DEFERRED)",
}

for _, sql := range tests {
Expand Down

0 comments on commit c64edc6

Please sign in to comment.