diff --git a/ir/inspector.go b/ir/inspector.go index 4d39426a..1cdfbc0b 100644 --- a/ir/inspector.go +++ b/ir/inspector.go @@ -550,15 +550,15 @@ func (i *Inspector) buildConstraints(ctx context.Context, schema *IR, targetSche // Add referenced column only if it doesn't exist if !refColumnExists { - // Get the foreign ordinal position for proper ordering - refPosition := position // Default fallback to source position - if constraint.ForeignOrdinalPosition.Valid { - refPosition = int(constraint.ForeignOrdinalPosition.Int32) - } - + // Use the local column's constraint position for the referenced column. + // The local and referenced columns are paired together in the FK definition, + // so they must have the same position to maintain correct ordering. + // Note: ForeignOrdinalPosition from the query is fa.attnum (column position + // in the foreign table's definition), which is wrong - we need the constraint + // array position, which is the same as the local column's position. refConstraintCol := &ConstraintColumn{ Name: refColumnName, - Position: refPosition, // Use foreign ordinal position for referenced column + Position: position, // Use local column's constraint position } c.ReferencedColumns = append(c.ReferencedColumns, refConstraintCol) } diff --git a/testdata/diff/create_table/composite_fk_column_order/diff.sql b/testdata/diff/create_table/composite_fk_column_order/diff.sql new file mode 100644 index 00000000..98f68775 --- /dev/null +++ b/testdata/diff/create_table/composite_fk_column_order/diff.sql @@ -0,0 +1,2 @@ +ALTER TABLE order_items +ADD CONSTRAINT fk_order_items_order FOREIGN KEY (customer_id, order_id) REFERENCES orders (customer_id, order_id); diff --git a/testdata/diff/create_table/composite_fk_column_order/new.sql b/testdata/diff/create_table/composite_fk_column_order/new.sql new file mode 100644 index 00000000..bbee912c --- /dev/null +++ b/testdata/diff/create_table/composite_fk_column_order/new.sql @@ -0,0 +1,22 @@ +-- Test case: Composite FK where FK column order differs from table column order +-- This tests that constraint column ordering is preserved independently of table column order + +-- Referenced table with composite primary key +CREATE TABLE public.orders ( + order_id integer NOT NULL, + customer_id integer NOT NULL, + name varchar(255) NOT NULL, + CONSTRAINT orders_pkey PRIMARY KEY (customer_id, order_id) +); + +-- Referencing table where columns are defined in OPPOSITE order from how FK references them +CREATE TABLE public.order_items ( + id serial PRIMARY KEY, + -- Table defines columns in this order: order_id first (lower attnum), then customer_id (higher attnum) + order_id integer NOT NULL, + customer_id integer NOT NULL, + quantity integer NOT NULL, + -- FK references columns in DIFFERENT order than table definition + -- customer_id is listed first in FK, but has higher attnum in table + CONSTRAINT fk_order_items_order FOREIGN KEY (customer_id, order_id) REFERENCES public.orders(customer_id, order_id) +); diff --git a/testdata/diff/create_table/composite_fk_column_order/old.sql b/testdata/diff/create_table/composite_fk_column_order/old.sql new file mode 100644 index 00000000..5afc72b6 --- /dev/null +++ b/testdata/diff/create_table/composite_fk_column_order/old.sql @@ -0,0 +1,19 @@ +-- Test case: Composite FK where FK column order differs from table column order +-- This tests that constraint column ordering is preserved independently of table column order + +-- Referenced table with composite primary key +CREATE TABLE public.orders ( + order_id integer NOT NULL, + customer_id integer NOT NULL, + name varchar(255) NOT NULL, + CONSTRAINT orders_pkey PRIMARY KEY (customer_id, order_id) +); + +-- Referencing table where columns are defined in OPPOSITE order from how FK references them +CREATE TABLE public.order_items ( + id serial PRIMARY KEY, + -- Table defines columns in this order: order_id first (lower attnum), then customer_id (higher attnum) + order_id integer NOT NULL, + customer_id integer NOT NULL, + quantity integer NOT NULL +); diff --git a/testdata/diff/create_table/composite_fk_column_order/plan.json b/testdata/diff/create_table/composite_fk_column_order/plan.json new file mode 100644 index 00000000..4b4004bc --- /dev/null +++ b/testdata/diff/create_table/composite_fk_column_order/plan.json @@ -0,0 +1,26 @@ +{ + "version": "1.0.0", + "pgschema_version": "1.6.1", + "created_at": "1970-01-01T00:00:00Z", + "source_fingerprint": { + "hash": "1948e0ca8413d527d01353e9a8b4f4581198c99a5b994959bda5e05beb462153" + }, + "groups": [ + { + "steps": [ + { + "sql": "ALTER TABLE order_items\nADD CONSTRAINT fk_order_items_order FOREIGN KEY (customer_id, order_id) REFERENCES orders (customer_id, order_id) NOT VALID;", + "type": "table.constraint", + "operation": "create", + "path": "public.order_items.fk_order_items_order" + }, + { + "sql": "ALTER TABLE order_items VALIDATE CONSTRAINT fk_order_items_order;", + "type": "table.constraint", + "operation": "create", + "path": "public.order_items.fk_order_items_order" + } + ] + } + ] +} diff --git a/testdata/diff/create_table/composite_fk_column_order/plan.sql b/testdata/diff/create_table/composite_fk_column_order/plan.sql new file mode 100644 index 00000000..13a6be43 --- /dev/null +++ b/testdata/diff/create_table/composite_fk_column_order/plan.sql @@ -0,0 +1,4 @@ +ALTER TABLE order_items +ADD CONSTRAINT fk_order_items_order FOREIGN KEY (customer_id, order_id) REFERENCES orders (customer_id, order_id) NOT VALID; + +ALTER TABLE order_items VALIDATE CONSTRAINT fk_order_items_order; diff --git a/testdata/diff/create_table/composite_fk_column_order/plan.txt b/testdata/diff/create_table/composite_fk_column_order/plan.txt new file mode 100644 index 00000000..54da9e0b --- /dev/null +++ b/testdata/diff/create_table/composite_fk_column_order/plan.txt @@ -0,0 +1,16 @@ +Plan: 1 to modify. + +Summary by type: + tables: 1 to modify + +Tables: + ~ order_items + + fk_order_items_order (constraint) + +DDL to be executed: +-------------------------------------------------- + +ALTER TABLE order_items +ADD CONSTRAINT fk_order_items_order FOREIGN KEY (customer_id, order_id) REFERENCES orders (customer_id, order_id) NOT VALID; + +ALTER TABLE order_items VALIDATE CONSTRAINT fk_order_items_order;