Skip to content

Commit 9c1aaeb

Browse files
committed
fix: correct cross-schema foreign key detection in get_related_tables (uses r_owner + fallback owner resolution)
1 parent 5811da4 commit 9c1aaeb

File tree

2 files changed

+292
-222
lines changed

2 files changed

+292
-222
lines changed

db_context/database.py

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -646,44 +646,55 @@ async def get_related_tables(self, table_name: str) -> Dict[str, List[str]]:
646646
conn = await self.get_connection()
647647
try:
648648
cursor = conn.cursor()
649+
requested_table = table_name.upper()
649650
schema = await self._get_effective_schema(conn)
650-
651-
# Get tables referenced by this table
652-
referenced_tables_result = await self._execute_cursor_fetch(cursor, """
653-
SELECT /*+ RESULT_CACHE LEADING(ac acc) USE_NL(acc) */
654-
DISTINCT acc.table_name AS referenced_table
655-
FROM all_constraints ac
656-
JOIN all_cons_columns acc ON acc.constraint_name = ac.r_constraint_name
657-
AND acc.owner = ac.owner
658-
WHERE ac.constraint_type = 'R'
659-
AND ac.table_name = :table_name
660-
AND ac.owner = :owner
661-
""", table_name=table_name.upper(), owner=schema)
662-
663-
referenced_tables = [row[0] for row in referenced_tables_result]
664-
665-
# Get tables that reference this table
666-
referencing_tables_result = await self._execute_cursor_fetch(cursor, """
667-
WITH pk_constraints AS (
668-
SELECT /*+ MATERIALIZE */ constraint_name
669-
FROM all_constraints
670-
WHERE table_name = :table_name
671-
AND constraint_type IN ('P', 'U')
672-
AND owner = :owner
673-
)
674-
SELECT /*+ RESULT_CACHE LEADING(ac pk) USE_NL(pk) */ DISTINCT ac.table_name AS referencing_table
675-
FROM all_constraints ac
676-
JOIN pk_constraints pk ON ac.r_constraint_name = pk.constraint_name
677-
WHERE ac.constraint_type = 'R'
678-
AND ac.owner = :owner
679-
""", table_name=table_name.upper(), owner=schema)
680-
681-
referencing_tables = [row[0] for row in referencing_tables_result]
682-
683-
return {
684-
'referenced_tables': referenced_tables,
685-
'referencing_tables': referencing_tables
686-
}
651+
652+
# Helper to run the two directional queries for a given owner
653+
async def _run_for_owner(owner: str):
654+
# Tables this table references (parent tables)
655+
ref_rows = await self._execute_cursor_fetch(cursor, """
656+
SELECT /*+ RESULT_CACHE */ DISTINCT parent_cols.table_name
657+
FROM all_constraints fk
658+
JOIN all_constraints pk
659+
ON pk.constraint_name = fk.r_constraint_name
660+
AND pk.owner = fk.r_owner
661+
JOIN all_cons_columns parent_cols
662+
ON parent_cols.constraint_name = pk.constraint_name
663+
AND parent_cols.owner = pk.owner
664+
WHERE fk.constraint_type = 'R'
665+
AND fk.table_name = :table_name
666+
AND fk.owner = :owner
667+
""", table_name=requested_table, owner=owner)
668+
669+
# Tables that reference this table (child tables)
670+
referencing_rows = await self._execute_cursor_fetch(cursor, """
671+
SELECT /*+ RESULT_CACHE */ DISTINCT fk.table_name
672+
FROM all_constraints pk
673+
JOIN all_constraints fk
674+
ON fk.r_constraint_name = pk.constraint_name
675+
AND fk.r_owner = pk.owner
676+
WHERE pk.constraint_type IN ('P','U')
677+
AND pk.table_name = :table_name
678+
AND pk.owner = :owner
679+
AND fk.constraint_type = 'R'
680+
""", table_name=requested_table, owner=owner)
681+
682+
return [r[0] for r in ref_rows], [r[0] for r in referencing_rows]
683+
684+
# First attempt with the effective schema (target_schema or connection user)
685+
referenced_tables, referencing_tables = await _run_for_owner(schema)
686+
687+
# If nothing found at all, attempt to discover actual owner of the table and retry once
688+
if not referenced_tables and not referencing_tables:
689+
owner_rows = await self._execute_cursor_fetch(cursor, """
690+
SELECT DISTINCT owner FROM all_tables WHERE table_name = :table_name
691+
""", table_name=requested_table)
692+
if owner_rows:
693+
actual_owner = owner_rows[0][0]
694+
if actual_owner and actual_owner.upper() != schema:
695+
referenced_tables, referencing_tables = await _run_for_owner(actual_owner.upper())
696+
697+
return { 'referenced_tables': referenced_tables, 'referencing_tables': referencing_tables }
687698

688699
finally:
689700
await self._close_connection(conn)

0 commit comments

Comments
 (0)