Repository navigation
Declaration accepts auto-populated tables that declare their own primary-key attributes #1576
Description
Summary
Computed and Imported tables may declare primary-key attributes of their own, beyond their foreign keys. DataJoint accepts them at declaration without a warning; the only signal comes later, and only when job reservation is used: the jobs table logs Ignoring non-FK primary key attribute '<attr>' in jobs table for <table>. Job granularity will be degraded. The documentation says such a table should not exist: a new dimension in an auto-populated table belongs in a part table (explanation/entity-integrity.md, "Autopopulated tables introduce dimensions only through part tables"). #1523 states the same rule in passing: an auto-populated table introduces no new dimension, only its parts may. Declaration does not check it, and the resulting table behaves badly in a way that is easy to miss.
Reproduction
DataJoint 2.3.5.dev2 (master), MySQL 8.0:
import datajoint as dj
schema = dj.Schema("scratch_dimrule")
calls = []
@schema
class Image(dj.Manual):
definition = """
image_id : int32
---
n_blobs : int32 # stands in for what detection would find
"""
@schema
class Blob(dj.Computed):
definition = """
-> Image
blob_idx : int32 # declared by the auto-populated table itself
---
x : float32
"""
def make(self, key):
calls.append(key["image_id"])
n = (Image & key).fetch1("n_blobs")
self.insert(dict(key, blob_idx=i, x=float(i)) for i in range(n))
Image.insert([dict(image_id=1, n_blobs=2), dict(image_id=2, n_blobs=0)])
Blob.populate()
Blob.populate()
Blob.populate()
print("rows:", len(Blob()))
print("make() calls by image_id:", calls)
print("remaining in key_source - Blob:", len(Blob.key_source - Blob))Output:
rows: 2
make() calls by image_id: [1, 2, 2, 2]
remaining in key_source - Blob: 1
The declaration raises no error and emits no warning, and neither does populate() with the default reserve_jobs=False. Blob.primary_key is ['image_id', 'blob_idx'].
With job reservation the table does draw a warning, from the jobs table rather than from declaration. Blob.populate(reserve_jobs=True), Blob.jobs, and Blob.jobs.refresh() each log:
Ignoring non-FK primary key attribute 'blob_idx' in jobs table for `scratch_dimrule`.`__blob`. Job granularity will be degraded.
That message (jobs.py, _get_fk_derived_pk_attrs) describes a consequence for job bookkeeping, not the modeling problem, and a pipeline that never reserves jobs never sees it.
Why it matters
The table has no row that stands for the computation as a whole, only rows for its pieces. A key whose computation legitimately yields zero pieces (image 2 above: no blobs found) leaves nothing behind. It stays in key_source - Blob and is recomputed on every populate(), indefinitely; with reserve_jobs=True it is re-queued each time. progress() reports it as remaining forever. The same holds for any downstream table that depends on Blob: it cannot tell "no blobs" from "not computed yet".
The documented pattern has no such problem. The master records the computation (num_blobs = 0), the part holds the pieces, and both are inserted in one transaction by the master's make():
@schema
class Detection(dj.Computed):
definition = """
-> Image
---
num_blobs : int32
"""
class Blob(dj.Part):
definition = """
-> master
blob_idx : int32
---
x : float32
"""Proposal
At declaration, when a Computed or Imported table's primary key contains attributes that do not come from its foreign keys (the same condition the jobs table already detects):
- warn, naming the attributes and pointing to the part-table pattern; and
- optionally, raise behind a config setting (e.g.
config.declaration.strict_dimensions = True), for teams that want the rule enforced.
The check is the same one dj.Diagram already makes to underline a table that introduces a dimension (introduces_dimension in diagram.py), so the two would agree: an auto-populated table would never be underlined, and a Part table could be.
Existing tables declared this way keep working. The warning is emitted when the class is declared or activated, not on every query. The jobs table's message could then point to the same explanation, or be folded into it.
Activity
dimitri-yatsenko commentedon Oct 9, 2026
Related documentation issue: datajoint/datajoint-docs#299 asks the master-part spec to state this rule (an auto-populated table introduces new dimensions only through its part tables).
dimitri-yatsenko commentedon Oct 9, 2026
Correction to the original description: there is an existing warning, though not at declaration. Re-tested on 2.3.5.dev2 against MySQL 8.0, step by step:
Computedtable with its own primary-key attributepopulate()(defaultreserve_jobs=False)populate(reserve_jobs=True),.jobs,.jobs.refresh()Ignoring non-FK primary key attribute 'idx' in jobs table for ... Job granularity will be degraded.(jobs.py,_get_fk_derived_pk_attrs)The description above is updated to say so. The proposal stands: the condition is already detected for the jobs table, and moving the check to declaration (worded as the modeling rule, with the part-table pattern) would reach pipelines that never reserve jobs.