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.ts — IntrospectedForeignKey carries
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:
- Add an optional
referencedSchema and leave referencedTable bare — additive, but every consumer must learn to read it or nothing changes. - 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. - 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.
Found while implementing #11324, which repairs the join correlations in
SqlDriver.introspectForeignKeys' Postgres arm. Deliberately not fixed there: this is a shape question aboutIntrospectedForeignKey, 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.ts—IntrospectedForeignKeycarrieswith no schema qualification, and every arm fills it from a bare relation name (
ccu.table_namebefore #11324,parent.relnameafter).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_pathdoes not resolve.Measured
On live PostgreSQL 16.13,
search_path = os11324_probe:introspectForeignKeys('cross_child')now answersremote_parentis not reachable by that name fromos11324_probe. The answer is true of the constraint and unusable as an address.Why it is not merely cosmetic
packages/objectql/src/util.ts:209turns each foreign key straight into a lookup field: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:referencedSchemaand leavereferencedTablebare — additive, but every consumer must learn to read it or nothing changes.referencedTablewhen (and only when) the parent is outside the session'ssearch_path— no interface change, but the key's spelling becomes conditional, which is its own trap.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.