Skip to content

Commit 57f376d

Browse files
fix: Fix cascade delete for multi-column FKs and renamed attributes
Two critical fixes for PostgreSQL cascade delete: 1. Fix PostgreSQL constraint info query to properly match FK columns - Use referential_constraints to join FK and PK columns by position - Previous query returned cross product of all columns - Now returns correct matched pairs: (fk_col, parent_table, pk_col) 2. Fix Heading.select() to preserve table_info (adapter context) - Projections with renamed attributes need adapter for quoting - New heading now inherits table_info from parent heading - Prevents fallback to backticks on PostgreSQL All cascade delete tests now passing: - test_simple_cascade_delete[postgresql] ✅ - test_multi_level_cascade_delete[postgresql] ✅ - test_cascade_delete_with_renamed_attrs[postgresql] ✅ All unit tests passing (212/212). All multi-backend tests passing (4/4).
1 parent 338e7ea commit 57f376d

File tree

2 files changed

+17
-5
lines changed

2 files changed

+17
-5
lines changed

src/datajoint/adapters/postgres.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -668,16 +668,25 @@ def get_foreign_keys_sql(self, schema_name: str, table_name: str) -> str:
668668
)
669669

670670
def get_constraint_info_sql(self, constraint_name: str, schema_name: str, table_name: str) -> str:
671-
"""Query to get FK constraint details from information_schema."""
671+
"""
672+
Query to get FK constraint details from information_schema.
673+
674+
Returns matched pairs of (fk_column, parent_table, pk_column) for each
675+
column in the foreign key constraint, ordered by position.
676+
"""
672677
return (
673678
"SELECT "
674679
" kcu.column_name as fk_attrs, "
675680
" '\"' || ccu.table_schema || '\".\"' || ccu.table_name || '\"' as parent, "
676681
" ccu.column_name as pk_attrs "
677682
"FROM information_schema.key_column_usage AS kcu "
678-
"JOIN information_schema.constraint_column_usage AS ccu "
679-
" ON kcu.constraint_name = ccu.constraint_name "
680-
" AND kcu.constraint_schema = ccu.constraint_schema "
683+
"JOIN information_schema.referential_constraints AS rc "
684+
" ON kcu.constraint_name = rc.constraint_name "
685+
" AND kcu.constraint_schema = rc.constraint_schema "
686+
"JOIN information_schema.key_column_usage AS ccu "
687+
" ON rc.unique_constraint_name = ccu.constraint_name "
688+
" AND rc.unique_constraint_schema = ccu.constraint_schema "
689+
" AND kcu.ordinal_position = ccu.ordinal_position "
681690
"WHERE kcu.constraint_name = %s "
682691
" AND kcu.table_schema = %s "
683692
" AND kcu.table_name = %s "

src/datajoint/heading.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,10 @@ def select(self, select_list, rename_map=None, compute_map=None):
604604
dict(default_attribute_properties, name=new_name, attribute_expression=expr)
605605
for new_name, expr in compute_map.items()
606606
)
607-
return Heading(chain(copy_attrs, compute_attrs), lineage_available=self._lineage_available)
607+
# Inherit table_info so the new heading has access to the adapter
608+
new_heading = Heading(chain(copy_attrs, compute_attrs), lineage_available=self._lineage_available)
609+
new_heading.table_info = self.table_info
610+
return new_heading
608611

609612
def _join_dependent(self, dependent):
610613
"""Build attribute list when self → dependent: PK = PK(self), self's attrs first."""

0 commit comments

Comments
 (0)