Skip to content

Commit

Permalink
Allow INITIALLY IMMEDIATE constraints
Browse files Browse the repository at this point in the history
Convert `INITIALLY IMMEDIATE` constraints appearing in `CREATE TABLE`
column constraints to `OpCreateTable` operations rather than raw SQL.

The documentation is a little misleading, as it makes it sound like
`INITIALLY IMMEDIATE` implies that the constraint is `DEFERRABLE`, but
in practice it looks like `INITIALLY IMMEDIATE` without `DEFERRABLE` is
a no-op.

Documentation:

https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-INITIALLY

In practice:

```sql
CREATE TABLE foo(a int PRIMARY KEY GENERATED ALWAYS AS IDENTITY)

-- Specify an INITIALLY IMMEDIATE constraint without DEFERRABLE
ALTER TABLE foo ADD COLUMN bar int UNIQUE INITIALLY IMMEDIATE

-- Query the catalog to see the constraint
SELECT conname AS constraint_name,
        condeferrable AS is_deferrable,
        condeferred AS is_deferred
FROM pg_constraint
WHERE conrelid = 'foo'::regclass;
```

Result:
```
+-----------------+---------------+-------------+
| constraint_name | is_deferrable | is_deferred |
|-----------------+---------------+-------------|
| foo_pkey        | False         | False       |
| foo_bar_key     | False         | False       |
+-----------------+---------------+-------------+
```

ie, the `UNIQUE` constraint added to `bar` is not `DEFERRABLE` or
`DEFERRED`.

In contrast, if the column `bar` is defined:

```sql
ALTER TABLE foo ADD COLUMN bar int UNIQUE DEFERRABLE INITIALLY IMMEDIATE
```

Then the same catalog query shows:

```
+-----------------+---------------+-------------+
| constraint_name | is_deferrable | is_deferred |
|-----------------+---------------+-------------|
| foo_pkey        | False         | False       |
| foo_bar_key     | True          | False       |
+-----------------+---------------+-------------+
```
ie, the constraint is `DEFERRABLE`.

So specifying `INITIALLY IMMEDIATE` without `DEFERRABLE` is a no-op.
  • Loading branch information
andrew-farries committed Dec 20, 2024
1 parent e1ba0af commit 6155486
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 6 deletions.
11 changes: 5 additions & 6 deletions pkg/sql2pgroll/create_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,11 @@ 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
case
pgq.ConstrType_CONSTR_ATTR_NOT_DEFERRABLE,
pgq.ConstrType_CONSTR_ATTR_IMMEDIATE:
// NOT DEFERRABLE and INITIALLY IMMEDIATE constraints are the default and
// are supported, but no extra annotation is needed
continue
case pgq.ConstrType_CONSTR_GENERATED:
// Generated columns are not supported
Expand All @@ -152,9 +154,6 @@ func convertColumnDef(tableName string, col *pgq.ColumnDef) (*migrations.Column,
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
Expand Down
12 changes: 12 additions & 0 deletions pkg/sql2pgroll/create_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ func TestConvertCreateTableStatements(t *testing.T) {
sql: "CREATE TABLE foo(a int UNIQUE NOT DEFERRABLE)",
expectedOp: expect.CreateTableOp5,
},
{
sql: "CREATE TABLE foo(a int UNIQUE INITIALLY IMMEDIATE)",
expectedOp: expect.CreateTableOp5,
},
{
sql: "CREATE TABLE foo(a int PRIMARY KEY)",
expectedOp: expect.CreateTableOp6,
Expand All @@ -52,6 +56,10 @@ func TestConvertCreateTableStatements(t *testing.T) {
sql: "CREATE TABLE foo(a int PRIMARY KEY NOT DEFERRABLE)",
expectedOp: expect.CreateTableOp6,
},
{
sql: "CREATE TABLE foo(a int PRIMARY KEY INITIALLY IMMEDIATE)",
expectedOp: expect.CreateTableOp6,
},
{
sql: "CREATE TABLE foo(a int CHECK (a > 0))",
expectedOp: expect.CreateTableOp10,
Expand All @@ -64,6 +72,10 @@ func TestConvertCreateTableStatements(t *testing.T) {
sql: "CREATE TABLE foo(a int REFERENCES bar(b) NOT DEFERRABLE)",
expectedOp: expect.CreateTableOp12,
},
{
sql: "CREATE TABLE foo(a int REFERENCES bar(b) INITIALLY IMMEDIATE)",
expectedOp: expect.CreateTableOp12,
},
{
sql: "CREATE TABLE foo(a int REFERENCES bar(b) ON UPDATE NO ACTION)",
expectedOp: expect.CreateTableOp12,
Expand Down

0 comments on commit 6155486

Please sign in to comment.