You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On the PostgreSQL backend, DataJoint does not create a secondary index on a child table's foreign-key columns except in the unique case. MySQL/InnoDB creates one implicitly as part of enforcing every foreign key; PostgreSQL does not, and there is no server setting to make it. This leaves an unindexed-FK performance gap on Postgres for a subset of foreign keys.
Background
MySQL/InnoDB requires and auto-creates an index on the referencing (child) columns of every FK.
PostgreSQL auto-indexes only the referenced (parent) side (the PK/unique constraint, already indexed). It never indexes the referencing (child) columns, and this is not configurable via postgresql.conf — it is a deliberate design choice, left to the DBA.
Current DataJoint behavior
compile_foreign_key() appends the constraint but emits an index only when the FK is unique:
# src/datajoint/declare.py (~288-307)foreign_key_sql.append(
f"FOREIGN KEY ({fk_cols}) REFERENCES {ref_table_name} ({pk_cols}) ON UPDATE CASCADE ON DELETE RESTRICT"
)
# declare unique indexifis_unique:
index_cols=", ".join(adapter.quote_identifier(attr) forattrinref.primary_key)
index_sql.append(f"UNIQUE INDEX ({index_cols})")
So on Postgres a non-unique FK gets no supporting index from DataJoint or from the engine.
When this actually bites (and when it doesn't)
Because a DataJoint FK inherits the parent's primary key into the child, the FK columns are often a left-prefix of the child's primary key — in which case the child's PK index already covers FK lookups on both backends, and no extra index is needed.
The gap is real only when the FK columns are not a usable left-prefix of an existing index, i.e.:
secondary (non-primary) foreign keys — FK attributes not in the child PK; and
FK columns that sit in a non-leading position of a composite PK (e.g. PK (a, b) where b comes from the FK), so lookups/cascades filtering on the FK alone can't use the PK index.
In those cases, on Postgres, cascade deletes (ON DELETE), joins on the FK, and the parent-existence check scan the child without index support — a performance cliff on large child tables, and an asymmetry vs. MySQL.
Proposal
On the Postgres DDL path, have declare.py emit an explicit CREATE INDEX on the FK columns when they are not already covered by a left-prefix of the PK or an existing index. MySQL can continue to rely on InnoDB's implicit index (avoid duplicating it). Points to work through:
Skip when FK columns are a left-prefix of the child PK (or an existing declared index) to avoid redundant indexes.
Idempotency with alter() (don't churn/duplicate indexes on redeclare).
Index naming.
The unique FK case already emits a UNIQUE INDEX; keep that.
References
src/datajoint/declare.py — compile_foreign_key() (~L288-307); PostgreSQL index handling (~L494-510).
Docs note added in datajoint-docs Added set_password and show_definition #227 (explanation/referential-integrity.md §5) currently advises users to declare an explicit index(...) on Postgres; if this is fixed in the framework, that guidance flips back to "the database indexes them."
Summary
On the PostgreSQL backend, DataJoint does not create a secondary index on a child table's foreign-key columns except in the
uniquecase. MySQL/InnoDB creates one implicitly as part of enforcing every foreign key; PostgreSQL does not, and there is no server setting to make it. This leaves an unindexed-FK performance gap on Postgres for a subset of foreign keys.Background
postgresql.conf— it is a deliberate design choice, left to the DBA.Current DataJoint behavior
compile_foreign_key()appends the constraint but emits an index only when the FK is unique:So on Postgres a non-unique FK gets no supporting index from DataJoint or from the engine.
When this actually bites (and when it doesn't)
Because a DataJoint FK inherits the parent's primary key into the child, the FK columns are often a left-prefix of the child's primary key — in which case the child's PK index already covers FK lookups on both backends, and no extra index is needed.
The gap is real only when the FK columns are not a usable left-prefix of an existing index, i.e.:
(a, b)wherebcomes from the FK), so lookups/cascades filtering on the FK alone can't use the PK index.In those cases, on Postgres, cascade deletes (
ON DELETE), joins on the FK, and the parent-existence check scan the child without index support — a performance cliff on large child tables, and an asymmetry vs. MySQL.Proposal
On the Postgres DDL path, have
declare.pyemit an explicitCREATE INDEXon the FK columns when they are not already covered by a left-prefix of the PK or an existing index. MySQL can continue to rely on InnoDB's implicit index (avoid duplicating it). Points to work through:alter()(don't churn/duplicate indexes on redeclare).uniqueFK case already emits aUNIQUE INDEX; keep that.References
src/datajoint/declare.py—compile_foreign_key()(~L288-307); PostgreSQL index handling (~L494-510).adapters/mysql.py(InnoDB implicit index),adapters/postgres.py.set_passwordandshow_definition#227 (explanation/referential-integrity.md§5) currently advises users to declare an explicitindex(...)on Postgres; if this is fixed in the framework, that guidance flips back to "the database indexes them."