Found while working #11101 (which repaired the ordering of the PG/MySQL key). This is a membership defect in the same method — a different defect class, so it was deliberately not folded in. Measured on a live PostgreSQL 16.13.
What
packages/drivers/driver-sql/src/sql-driver.ts, SqlDriver.introspectPrimaryKeys, Postgres arm. The query reads all of pg_index.indkey. For a covering primary key, indkey holds the key columns and the INCLUDEd payload columns; indnkeyatts is the count of the leading entries that are actually key columns, and it is not consulted.
Postgres reaches this via CREATE UNIQUE INDEX … INCLUDE (…) promoted with ALTER TABLE … ADD CONSTRAINT … PRIMARY KEY USING INDEX.
Measured (PostgreSQL 16.13)
CREATETABLEcovered (k1 textNOT NULL, k2 textNOT NULL, payload text);
CREATEUNIQUE INDEXcovered_pkON covered (k2, k1) INCLUDE (payload);
ALTERTABLE covered ADD CONSTRAINT covered_pkey PRIMARY KEY USING INDEX covered_pk;
indkey | indnatts | indnkeyatts
--------+----------+-------------
2 1 3 | 3 | 2
The declared key is (k2, k1). payload (attnum 3) is a payload column, not a key column — indnkeyatts = 2 says so.
Both the pre-#11101 query and the post-#11101 query report it as a key member:
--- OLD query (membership test, a.attnum = ANY(i.indkey)) ---
k1
k2
payload
--- NEW query (#11101, joins the ordinality of indkey) ---
k2
k1
payload
#11101 neither introduced nor worsened this — the two arms agree on membership and differ only in order, which is why it was left alone there. The payload entry is wrong in both.
Why it matters
primaryKeys is consumed as an addressing / upsert-conflict-target key (federated-object codegen, the persisted external_catalog under ADR-0015, schema-drift comparison). A key with an extra member is a different key: an upsert conflict target naming a non-key column does not match the constraint, and drift comparison against a correctly-declared (k2, k1) reports a phantom unexpected_key_member:payload.
Likely shape of the fix
Bound the join to the key attributes: WHERE k.ord <= i.indnkeyatts (or unnest(i.indkey[0:i.indnkeyatts-1]) — note int2vector is 0-based). indnkeyatts exists in PG 11+; below that it equals indnatts and the bound is a no-op.
Worth checking the MySQL and SQLite arms for the analogous question before fixing, so all three dialects keep the single answer #11101 just established. MySQL has no covering-index concept for a PRIMARY KEY, so it is likely PG-only.
Not measured here
Whether any in-tree consumer currently introspects a covering primary key. The repo's own schema sync does not create them; this reaches a remote table under federation (ADR-0015), where the DDL is not ours.
Found while working #11101 (which repaired the ordering of the PG/MySQL key). This is a membership defect in the same method — a different defect class, so it was deliberately not folded in. Measured on a live PostgreSQL 16.13.
What
packages/drivers/driver-sql/src/sql-driver.ts,SqlDriver.introspectPrimaryKeys, Postgres arm. The query reads all ofpg_index.indkey. For a covering primary key,indkeyholds the key columns and theINCLUDEd payload columns;indnkeyattsis the count of the leading entries that are actually key columns, and it is not consulted.Postgres reaches this via
CREATE UNIQUE INDEX … INCLUDE (…)promoted withALTER TABLE … ADD CONSTRAINT … PRIMARY KEY USING INDEX.Measured (PostgreSQL 16.13)
The declared key is
(k2, k1).payload(attnum 3) is a payload column, not a key column —indnkeyatts = 2says so.Both the pre-#11101 query and the post-#11101 query report it as a key member:
#11101 neither introduced nor worsened this — the two arms agree on membership and differ only in order, which is why it was left alone there. The
payloadentry is wrong in both.Why it matters
primaryKeysis consumed as an addressing / upsert-conflict-target key (federated-object codegen, the persistedexternal_catalogunder ADR-0015, schema-drift comparison). A key with an extra member is a different key: an upsert conflict target naming a non-key column does not match the constraint, and drift comparison against a correctly-declared(k2, k1)reports a phantomunexpected_key_member:payload.Likely shape of the fix
Bound the join to the key attributes:
WHERE k.ord <= i.indnkeyatts(orunnest(i.indkey[0:i.indnkeyatts-1])— noteint2vectoris 0-based).indnkeyattsexists in PG 11+; below that it equalsindnattsand the bound is a no-op.Worth checking the MySQL and SQLite arms for the analogous question before fixing, so all three dialects keep the single answer #11101 just established. MySQL has no covering-index concept for a PRIMARY KEY, so it is likely PG-only.
Not measured here
Whether any in-tree consumer currently introspects a covering primary key. The repo's own schema sync does not create them; this reaches a remote table under federation (ADR-0015), where the DDL is not ours.