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
{{ message }}
Repository navigation
drop() leaves the jobs table behind, and a table re-declared under the same name inherits it #1601
drop() removes an auto-populated table but not its jobs table, ~~<table_name>. drop_quick() (table.py:1224) drops only full_table_name. The jobs table is created lazily by AutoPopulate.jobs (autopopulate.py:182-188), and nothing else removes it, so it stays, with its records and its primary key.
When a table is later declared under the same name, its jobs property picks up the old jobs table. If the new table's primary key differs, populate(reserve_jobs=True) computes nothing and raises no error. refresh() adds key_source - target - jobs, and the old records, keyed on part of the new primary key, cover every new key.
Reproduction
DataJoint 2.3.5.dev8 (master, a00b89f), MySQL 8.0.46, Python 3.11:
importdatajointasdjschema=dj.Schema("scratch_drop_jobs")
@schemaclassItem(dj.Manual):
definition=""" item : int32 """@schemaclassParam(dj.Lookup):
definition=""" param : int32 """contents= [(1,), (2,)]
@schemaclassResult(dj.Computed):
definition=""" -> Item --- value : int32 """defmake(self, key):
raiseRuntimeError("not ready")
Item.insert({"item": i} foriinrange(3))
Result.populate(reserve_jobs=True, suppress_errors=True)
Result.drop(prompt=False)
print("tables after drop:", [r[0] forrinschema.connection.query("SHOW TABLES FROM `scratch_drop_jobs`")])
@schemaclassResult(dj.Computed): # same name, primary key now includes Paramdefinition=""" -> Item -> Param --- value : int32 """defmake(self, key):
self.insert1(dict(key, value=1))
print("jobs primary key:", Result.jobs.heading.primary_key, "records:", [j["status"] forjinResult.jobs.to_dicts()])
Result.populate(reserve_jobs=True)
print("rows after populate(reserve_jobs=True):", len(Result()), "of", len(Result.key_source))
Output:
tables after drop: ['#param', 'item', '~lineage', '~~result']
jobs primary key: ['item'] records: ['error', 'error', 'error']
rows after populate(reserve_jobs=True): 0 of 6
Expected behavior
drop() drops each table's jobs table along with the table, as it already removes the table's ~lineage entries.
AutoPopulate.jobs checks that an existing jobs table's primary key matches the table's, and raises if it does not. That also catches jobs tables left behind by earlier releases.
For the fix: the jobs table is created lazily, by AutoPopulate.jobs in whichever process populates, so drop() cannot count on the jobs property of the class it is called on. A virtual module's classes, and the descendants the cascade reaches, never touch it. Dropping ~~<table_name> by name, when it exists, for each table the cascade drops, covers both cases.
Activity
dimitri-yatsenko commentedon Oct 19, 2026
Two more cases, on
masterat a00b89f (2.3.5.dev8), MySQL 8.0.21:drop()was called on..jobs, leaves them too.For the fix: the jobs table is created lazily, by
AutoPopulate.jobsin whichever process populates, sodrop()cannot count on thejobsproperty of the class it is called on. A virtual module's classes, and the descendants the cascade reaches, never touch it. Dropping~~<table_name>by name, when it exists, for each table the cascade drops, covers both cases.