What happens
On PostgreSQL, a login holding only USAGE on the schema and SELECT on the tables sees an
empty primary key on every table it can read. The symptoms cascade from there:
Table.primary_key returns []
parents(primary=True) returns nothing, so key_source raises
"A table must have dependencies from its primary key for auto-populate to work"
- a row count builds invalid SQL —
SELECT count(DISTINCT ) ... — because the distinct-column
list is empty
The same login on MySQL works correctly, and the same tables read correctly on PostgreSQL from
a login that owns them or holds a write privilege.
Why
PostgresAdapter.load_primary_keys_sql (src/datajoint/adapters/postgres.py:828-840) reads
primary keys from information_schema:
FROM information_schema.key_column_usage kcu
JOIN information_schema.table_constraints tc
ON kcu.constraint_name = tc.constraint_name
AND kcu.table_schema = tc.table_schema
WHERE ... AND tc.constraint_type = 'PRIMARY KEY'
PostgreSQL's information_schema.table_constraints shows only constraints on tables the current
user owns or holds some privilege other than SELECT on. A SELECT-only login therefore
matches no rows, and the join yields nothing — silently, since an empty result is
indistinguishable from a table with no primary key.
Foreign keys do not have this problem. load_foreign_keys_sql
(src/datajoint/adapters/postgres.py:842-864) already reads pg_constraint, which is readable
by any login. So a read-only session currently knows how tables link but not what keys them —
the two halves of the dependency graph come from sources with different visibility rules.
Suggested fix
Read primary keys from the system catalogs the way foreign keys already are — pg_index joined
to pg_attribute for the indexed columns, filtered on indisprimary, or pg_constraint with
contype = 'p'. Either is readable by any login and returns column order directly, which
information_schema does not guarantee without ordinal_position.
MySQL is unaffected: its information_schema.key_column_usage is filtered by privilege too, but
SELECT is enough to see it there.
Why it matters now
Any deployment that hands out read-only PostgreSQL credentials hits this on the first
auto-populated table. It also blocks the branch-resolution work
(#1552), where a branch session
holds exactly this shape of credential — read on the pipeline's schema, write only inside its
own draft — so on PostgreSQL that session cannot resolve a key source at all. That makes this a
precondition for branching on PostgreSQL rather than a consequence of it.
Verifying
Reproduces on PostgreSQL 16 with DataJoint 2.3.3:
CREATE ROLE reader LOGIN PASSWORD '...';
GRANT USAGE ON SCHEMA myschema TO reader;
GRANT SELECT ON ALL TABLES IN SCHEMA myschema TO reader;
Connect as reader, then SomeTable.primary_key returns [] where the owner sees the real key.
What happens
On PostgreSQL, a login holding only
USAGEon the schema andSELECTon the tables sees anempty primary key on every table it can read. The symptoms cascade from there:
Table.primary_keyreturns[]parents(primary=True)returns nothing, sokey_sourceraises"A table must have dependencies from its primary key for auto-populate to work"
SELECT count(DISTINCT ) ...— because the distinct-columnlist is empty
The same login on MySQL works correctly, and the same tables read correctly on PostgreSQL from
a login that owns them or holds a write privilege.
Why
PostgresAdapter.load_primary_keys_sql(src/datajoint/adapters/postgres.py:828-840) readsprimary keys from
information_schema:PostgreSQL's
information_schema.table_constraintsshows only constraints on tables the currentuser owns or holds some privilege other than
SELECTon. ASELECT-only login thereforematches no rows, and the join yields nothing — silently, since an empty result is
indistinguishable from a table with no primary key.
Foreign keys do not have this problem.
load_foreign_keys_sql(
src/datajoint/adapters/postgres.py:842-864) already readspg_constraint, which is readableby any login. So a read-only session currently knows how tables link but not what keys them —
the two halves of the dependency graph come from sources with different visibility rules.
Suggested fix
Read primary keys from the system catalogs the way foreign keys already are —
pg_indexjoinedto
pg_attributefor the indexed columns, filtered onindisprimary, orpg_constraintwithcontype = 'p'. Either is readable by any login and returns column order directly, whichinformation_schemadoes not guarantee withoutordinal_position.MySQL is unaffected: its
information_schema.key_column_usageis filtered by privilege too, butSELECTis enough to see it there.Why it matters now
Any deployment that hands out read-only PostgreSQL credentials hits this on the first
auto-populated table. It also blocks the branch-resolution work
(#1552), where a branch session
holds exactly this shape of credential — read on the pipeline's schema, write only inside its
own draft — so on PostgreSQL that session cannot resolve a key source at all. That makes this a
precondition for branching on PostgreSQL rather than a consequence of it.
Verifying
Reproduces on PostgreSQL 16 with DataJoint 2.3.3:
Connect as
reader, thenSomeTable.primary_keyreturns[]where the owner sees the real key.