Skip to content

fix(pg-meta): pair composite FK columns positionally to avoid cartesi…#317

Open
anp0429 wants to merge 1 commit into
supabase:mainfrom
anp0429:fix/composite-fk-cartesian-product
Open

fix(pg-meta): pair composite FK columns positionally to avoid cartesi…#317
anp0429 wants to merge 1 commit into
supabase:mainfrom
anp0429:fix/composite-fk-cartesian-product

Conversation

@anp0429

@anp0429 anp0429 commented Jul 1, 2026

Copy link
Copy Markdown

What kind of change does this PR introduce?

Bug fix : data-integrity issue in list_tables (verbose) foreign-key output.

What is the current behavior?

For any composite (multi-column) foreign key, list_tables (verbose) returns the cartesian product of the source and target columns. An N-column FK produces N² foreign_key_constraints entries instead of N - reporting column pairings that do not exist in the schema.

Repro:

create table public.parent (a int not null, b int not null, primary key (a, b));
create table public.child (
  a int not null, b int not null,
  constraint child_parent_fk foreign key (a, b) references public.parent (a, b)
);

list_tables with verbose: true returns 4 constraints for child_parent_fk:

public.child.a => public.parent.a
public.child.a => public.parent.b   ← does not exist
public.child.b => public.parent.a   ← does not exist
public.child.b => public.parent.b

Only a=>a and b=>b are real. Because this tool exists so an AI agent can reason about database structure, the fabricated relationships are silently trusted - an agent could infer key relationships that don't exist.

Root cause is in pg-meta/tables.sql. The FK subquery joins source and target columns independently:

... on sa.attrelid = c.conrelid  and sa.attnum = any (c.conkey)
... on ta.attrelid = c.confrelid and ta.attnum = any (c.confkey)

any(conkey) against any(confkey) cross-joins every source column with every target column, instead of pairing them positionally.

What is the new behavior?

The two arrays are walked in lockstep with unnest(conkey, confkey) with ordinality, so column i pairs only with column i. The example above now returns exactly the two real pairs. Adds a regression test asserting the exact set of column pairs for a composite FK.

Additional context

The same pg-meta query backs the schema-docs work in #278, so this fix helps that path as well.

…an product

The foreign-key subquery in tables.sql joined source and target columns
independently (attnum = any(conkey) with attnum = any(confkey)), producing the
cartesian product of a composite key's columns. An N-column foreign key yielded
N^2 relationship rows, reporting column pairings that do not exist in the schema.
This surfaces in list_tables (verbose) foreign_key_constraints.

Pair the columns positionally with unnest(conkey, confkey) with ordinality so
column i maps only to column i. Adds a regression test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants