Track foreign key ON DELETE setting in the internal schema representation (#311)

Add the `ON DELETE` setting of a foreign key to the information stored
about the key in the internal schema representation.

The schema representation for a foreign key now looks like:

```json
{
  "some_table": {
    ...
    "foreignKeys": {
      "fk_users_id": {
        "name": "fk_users_id",
        "columns": [
          "user_id"
        ],
        "onDelete": "NO ACTION",
        "referencedTable": "users",
        "referencedColumns": [
          "id"
        ]
      }
    }
  }
}
```

Fixes https://github.com/xataio/pgroll/issues/309
This commit is contained in:
Andrew Farries 2024-03-07 08:59:45 +00:00 committed by GitHub
parent 099a443074
commit c88c0602c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 69 additions and 3 deletions

View File

@ -95,6 +95,9 @@ type ForeignKey struct {
// The columns in the referenced table that the foreign key references
ReferencedColumns []string `json:"referencedColumns"`
// The ON DELETE behavior of the foreign key
OnDelete string `json:"onDelete"`
}
type CheckConstraint struct {

View File

@ -223,21 +223,29 @@ BEGIN
'name', fk_details.conname,
'columns', fk_details.columns,
'referencedTable', fk_details.referencedTable,
'referencedColumns', fk_details.referencedColumns
'referencedColumns', fk_details.referencedColumns,
'onDelete', fk_details.onDelete
)), '{}'::json)
FROM (
SELECT
fk_constraint.conname,
array_agg(fk_attr.attname ORDER BY fk_constraint.conkey::int[]) AS columns,
fk_cl.relname AS referencedTable,
array_agg(ref_attr.attname ORDER BY fk_constraint.confkey::int[]) AS referencedColumns
array_agg(ref_attr.attname ORDER BY fk_constraint.confkey::int[]) AS referencedColumns,
CASE
WHEN fk_constraint.confdeltype = 'a' THEN 'NO ACTION'
WHEN fk_constraint.confdeltype = 'r' THEN 'RESTRICT'
WHEN fk_constraint.confdeltype = 'c' THEN 'CASCADE'
WHEN fk_constraint.confdeltype = 'd' THEN 'SET DEFAULT'
WHEN fk_constraint.confdeltype = 'n' THEN 'SET NULL'
END as onDelete
FROM pg_constraint AS fk_constraint
INNER JOIN pg_class fk_cl ON fk_constraint.confrelid = fk_cl.oid
INNER JOIN pg_attribute fk_attr ON fk_attr.attrelid = fk_constraint.conrelid AND fk_attr.attnum = ANY(fk_constraint.conkey)
INNER JOIN pg_attribute ref_attr ON ref_attr.attrelid = fk_constraint.confrelid AND ref_attr.attnum = ANY(fk_constraint.confkey)
WHERE fk_constraint.conrelid = t.oid
AND fk_constraint.contype = 'f'
GROUP BY fk_constraint.conname, fk_cl.relname
GROUP BY fk_constraint.conname, fk_cl.relname, fk_constraint.confdeltype
) AS fk_details
)
)), '{}'::json) FROM pg_class AS t

View File

@ -471,6 +471,61 @@ func TestReadSchema(t *testing.T) {
Columns: []string{"fk"},
ReferencedTable: "table1",
ReferencedColumns: []string{"id"},
OnDelete: "NO ACTION",
},
},
CheckConstraints: map[string]schema.CheckConstraint{},
UniqueConstraints: map[string]schema.UniqueConstraint{},
},
},
},
},
{
name: "foreign key with ON DELETE CASCADE",
createStmt: "CREATE TABLE public.table1 (id int PRIMARY KEY); CREATE TABLE public.table2 (fk int NOT NULL, CONSTRAINT fk_fkey FOREIGN KEY (fk) REFERENCES public.table1 (id) ON DELETE CASCADE)",
wantSchema: &schema.Schema{
Name: "public",
Tables: map[string]schema.Table{
"table1": {
Name: "table1",
Columns: map[string]schema.Column{
"id": {
Name: "id",
Type: "integer",
Nullable: false,
Unique: true,
},
},
PrimaryKey: []string{"id"},
Indexes: map[string]schema.Index{
"table1_pkey": {
Name: "table1_pkey",
Unique: true,
Columns: []string{"id"},
},
},
CheckConstraints: map[string]schema.CheckConstraint{},
UniqueConstraints: map[string]schema.UniqueConstraint{},
ForeignKeys: map[string]schema.ForeignKey{},
},
"table2": {
Name: "table2",
Columns: map[string]schema.Column{
"fk": {
Name: "fk",
Type: "integer",
Nullable: false,
},
},
PrimaryKey: []string{},
Indexes: map[string]schema.Index{},
ForeignKeys: map[string]schema.ForeignKey{
"fk_fkey": {
Name: "fk_fkey",
Columns: []string{"fk"},
ReferencedTable: "table1",
ReferencedColumns: []string{"id"},
OnDelete: "CASCADE",
},
},
CheckConstraints: map[string]schema.CheckConstraint{},