Skip to content

driver-sql (PG): a cross-schema foreign key is reported with an UNQUALIFIED referencedTable, and the bare name does not resolve on the session's search_path #11377

Description

@os-zhuang

Found while implementing #11324, which repairs the join correlations in SqlDriver.introspectForeignKeys' Postgres arm. Deliberately not fixed there: this is a shape question about IntrospectedForeignKey, a different class from the join-correlation defects that card owns, and its resolution changes an interface every consumer reads. #11324 is a separate subject and is not addressed by this issue.

What

packages/drivers/driver-sql/src/sql-driver.tsIntrospectedForeignKey carries

referencedTable: string;

with no schema qualification, and every arm fills it from a bare relation name (ccu.table_name before #11324, parent.relname after).

Until #11324 that key was never observably wrong on Postgres, because a foreign key whose target lived in another schema contributed zero rows and never reached a consumer at all. Repairing that defect is what makes this one reachable: the cross-schema constraint is now returned, and it is returned naming its parent by a bare name that the session's search_path does not resolve.

Measured

On live PostgreSQL 16.13, search_path = os11324_probe:

createschemaos11324_far;
createtableos11324_far.remote_parent (id varchar(64) primary key);
createtablecross_child (
id varchar(64) primary key, p varchar(64),
constraint fk_cross foreign key (p) referencesos11324_far.remote_parent(id));

introspectForeignKeys('cross_child') now answers

{ columnName: 'p', referencedTable: 'remote_parent', referencedColumn: 'id', constraintName: 'fk_cross' }

remote_parent is not reachable by that name from os11324_probe. The answer is true of the constraint and unusable as an address.

Why it is not merely cosmetic

packages/objectql/src/util.ts:209 turns each foreign key straight into a lookup field:

reference: foreignKey.referencedTable,

So the bare name becomes an object reference. Two outcomes, both silent: it points at nothing, or — the worse one, and the same collision family #11201 documented — it points at a same-named table in the current schema, i.e. a lookup wired to the wrong object with no diagnostic anywhere.

Options, not a recommendation

Recording the shape rather than choosing it, because the choice reaches every consumer of IntrospectedTable:

  1. Add an optional referencedSchema and leave referencedTable bare — additive, but every consumer must learn to read it or nothing changes.
  2. Qualify referencedTable when (and only when) the parent is outside the session's search_path — no interface change, but the key's spelling becomes conditional, which is its own trap.
  3. Decide the driver should not report a cross-schema target at all and say so loudly rather than by omission.

Option 1 is the shape introspectSchema's other keys would suggest; option 2 is the one that needs no consumer change and is therefore the one most likely to be chosen for the wrong reason. This wants a decision, not a patch.

Not measured here

Whether any in-tree deployment currently declares a cross-schema foreign key. The reachability above was reproduced directly against the driver on a live server; it does not depend on a consumer to be wrong.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions