datajoint-python icon indicating copy to clipboard operation
datajoint-python copied to clipboard

Master rows not deleted when corresponding Parts are deleted via dependency

Open chrisroat opened this issue 4 years ago • 0 comments
trafficstars

Bug Report

Description

When Part table rows are deleted because an upstream dependency is deleted, the master rows with key prefix for those rows should also be deleted. They are not, and this causes data integrity violation:

  • Downstream tables depending on those part table rows transitively through the master table will not be deleted.
  • Repopulating the master table will not recalculate the deleted rows.

Reproducibility

Include:

  • OS: Linux (Ubuntu 18.04)
  • Python Version 3.8.10
  • MySQL Version: 5.7
  • MySQL Deployment Strategy: local docker and Google Cloud Sql
  • DataJoint Version: 0.13.2
  • Minimum number of steps to reliably reproduce the issue: See below
  • Complete error stack as a result of evaluating the above steps: See below

Reproducer: The SegmentRound row is properly deleted when corresponding AcquisitionRound row is deleted. However, the corresponding Segment row is NOT deleted, but should be.

import datajoint as dj
schema = dj.schema("bad_dep")

@schema
class Acquisition(dj.Manual):
    definition = """
    id: int
    """
    
@schema
class AcquisitionRound(dj.Manual):
    definition = """
    -> Acquisition
    round: int
    """
    
@schema
class Segment(dj.Computed):
    definition = """
    -> Acquisition
    """
    
    class SegmentRound(dj.Part):  # Only depends on one round per image
        definition = """
        -> Segment
        -> AcquisitionRound
        """
        
    def make(self, key):
        segment_round = 1  # may depend on acquisition
        self.insert1(key)
        self.SegmentRound.insert1({**key, "round": segment_round})

Acquisition.insert1({'id': 1})
AcquisitionRound.insert([{'id': 1, 'round': r} for r in range(2)])
Segment.populate()

assert len(Segment.SegmentRound()) == 1
assert len(Segment()) == 1

AcquisitionRound.delete(safemode=False)

assert len(Segment.SegmentRound()) == 0
assert len(Segment()) == 0  # FAILS
Deleting 1 rows from `bad_dep`.`__segment__segment_round`
Deleting 2 rows from `bad_dep`.`acquisition_round`
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-2-ab560ec702b8> in <module>
     42 
     43 assert len(Segment.SegmentRound()) == 0
---> 44 assert len(Segment()) == 0  # FAILS

AssertionError: 

Expected Behavior

The final assertion should not fail.

chrisroat avatar Jul 04 '21 15:07 chrisroat