Skip to content

feat: add get_table_foreign_key_info method for MySQL and Oracle database#351

Merged
AnshumanX304 merged 2 commits into
datachecks:mainfrom
AnshumanX304:data-discovery
Nov 28, 2025
Merged

feat: add get_table_foreign_key_info method for MySQL and Oracle database#351
AnshumanX304 merged 2 commits into
datachecks:mainfrom
AnshumanX304:data-discovery

Conversation

@AnshumanX304

@AnshumanX304 AnshumanX304 commented Nov 28, 2025

Copy link
Copy Markdown
Contributor

… sources

Fixes/Implements

Description

Summary Goes here.

Type of change

Delete irrelevant options.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

  • Locally Tested
  • Needs Testing From Production

Summary by CodeRabbit

Release Notes

  • New Features
    • Added functionality to retrieve and display foreign key relationship information for database tables across MySQL and Oracle database sources. Returns constraint names, column mappings, and referenced table details with error handling for failed queries.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Nov 28, 2025

Copy link
Copy Markdown

Walkthrough

Two database datasource classes receive new methods to retrieve foreign key relationship information. The MysqlDataSource and OracleDataSource each gain a get_table_foreign_key_info() method that queries their respective system catalogs, applies error handling with logging, and returns structured dictionaries containing foreign key constraint details.

Changes

Cohort / File(s) Summary
Foreign Key Info Methods
dcs_core/integrations/databases/mysql.py, dcs_core/integrations/databases/oracle.py
Added new public method get_table_foreign_key_info(table_name: str, schema: str | None = None) to both MysqlDataSource and OracleDataSource classes. Each method queries the respective database's information schema/system views to retrieve foreign key constraint details, returns list of dictionaries with keys: constraint_name, table_name, fk_column, referenced_table, referenced_column. Includes error handling that logs failures and returns empty list.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Verify SQL query correctness for each database system (MySQL information_schema vs. Oracle ALL_CONSTRAINTS views)
  • Confirm error handling and logging behavior consistency
  • Check field mapping accuracy between raw query results and returned dictionary structure

Possibly related PRs

Suggested labels

Review effort 2/5

Suggested reviewers

  • rishav2803
  • Ryuk-me

Poem

🐰 A rabbit hops through schemas grand,
Foreign keys now join hand in hand!
MySQL, Oracle aligned in grace,
Constraints revealed in every place. 🔑✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is incomplete and contains only placeholder template text without meaningful content about the changes, objectives, or testing performed. Fill in the description with actual content including the issue number, a summary of changes, the type of change checkbox, and testing status. Replace placeholder text with substantive information.
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and clearly describes the main change: adding a new method get_table_foreign_key_info to both MySQL and Oracle database sources.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
dcs_core/integrations/databases/oracle.py (1)

706-706: Add docstring for public method.

This public method should include a docstring describing its purpose, parameters, return value, and usage.

 def get_table_foreign_key_info(self, table_name: str, schema: str | None = None):
+    """
+    Get foreign key constraint information for a table.
+    
+    :param table_name: Name of the table
+    :param schema: Optional schema name (defaults to instance schema_name)
+    :return: List of dictionaries containing foreign key details with keys:
+             constraint_name, table_name, fk_column, referenced_table, referenced_column
+    """
     schema = schema or self.schema_name
dcs_core/integrations/databases/mysql.py (1)

402-402: Add docstring for public method.

This public method should include a docstring describing its purpose, parameters, return value, and usage for consistency with other documented methods in the codebase.

 def get_table_foreign_key_info(self, table_name: str, schema: str | None = None):
+    """
+    Get foreign key constraint information for a table.
+    
+    :param table_name: Name of the table
+    :param schema: Optional schema name (defaults to instance schema_name)
+    :return: List of dictionaries containing foreign key details with keys:
+             constraint_name, table_name, fk_column, referenced_table, referenced_column
+    """
     schema = schema or self.schema_name
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0cd368 and a96617b.

📒 Files selected for processing (2)
  • dcs_core/integrations/databases/mysql.py (2 hunks)
  • dcs_core/integrations/databases/oracle.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
dcs_core/integrations/databases/oracle.py (4)
dcs_core/integrations/databases/mysql.py (1)
  • get_table_foreign_key_info (402-437)
dcs_core/integrations/databases/postgres.py (1)
  • get_table_foreign_key_info (420-447)
dcs_core/integrations/databases/mssql.py (2)
  • get_table_foreign_key_info (824-872)
  • fetchall (107-108)
dcs_core/integrations/databases/sybase.py (1)
  • fetchall (194-195)
dcs_core/integrations/databases/mysql.py (5)
dcs_core/integrations/databases/oracle.py (1)
  • get_table_foreign_key_info (706-748)
dcs_core/integrations/databases/postgres.py (1)
  • get_table_foreign_key_info (420-447)
dcs_core/integrations/databases/mssql.py (2)
  • get_table_foreign_key_info (824-872)
  • fetchall (107-108)
dcs_core/core/datasource/sql_datasource.py (1)
  • fetchall (120-123)
dcs_core/integrations/databases/sybase.py (1)
  • fetchall (194-195)
🪛 Ruff (0.14.6)
dcs_core/integrations/databases/oracle.py

709-730: Possible SQL injection vector through string-based query construction

(S608)


734-734: Do not catch blind exception: Exception

(BLE001)

dcs_core/integrations/databases/mysql.py

405-419: Possible SQL injection vector through string-based query construction

(S608)


423-423: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (1)
dcs_core/integrations/databases/mysql.py (1)

18-18: LGTM!

The logger import is appropriate for the new error handling in get_table_foreign_key_info.

Comment on lines +402 to +437
def get_table_foreign_key_info(self, table_name: str, schema: str | None = None):
schema = schema or self.schema_name

query = f"""
SELECT
kcu.CONSTRAINT_NAME AS constraint_name,
kcu.TABLE_NAME AS table_name,
kcu.COLUMN_NAME AS fk_column,
kcu.REFERENCED_TABLE_NAME AS referenced_table,
kcu.REFERENCED_COLUMN_NAME AS referenced_column
FROM information_schema.TABLE_CONSTRAINTS tc
JOIN information_schema.KEY_COLUMN_USAGE kcu
ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
AND tc.TABLE_SCHEMA = kcu.TABLE_SCHEMA
WHERE tc.CONSTRAINT_TYPE = 'FOREIGN KEY'
AND tc.TABLE_NAME = '{table_name}'
AND tc.TABLE_SCHEMA = '{schema}';
"""

try:
rows = self.fetchall(query)
except Exception as e:
logger.error(f"Failed to fetch fk info for dataset: {table_name} ({e})")
return []

data = [
{
"constraint_name": row[0],
"table_name": row[1],
"fk_column": row[2],
"referenced_table": row[3],
"referenced_column": row[4],
}
for row in rows
]
return data

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

SQL injection vulnerability: use parameterized queries.

The table_name and schema parameters are directly interpolated into the SQL query using f-strings, creating a SQL injection vulnerability. A malicious input like table_name="users' OR '1'='1" could manipulate the query logic or extract unauthorized data.

Apply this diff to use parameterized queries with SQLAlchemy:

 def get_table_foreign_key_info(self, table_name: str, schema: str | None = None):
     schema = schema or self.schema_name
 
     query = f"""
         SELECT
             kcu.CONSTRAINT_NAME AS constraint_name,
             kcu.TABLE_NAME AS table_name,
             kcu.COLUMN_NAME AS fk_column,
             kcu.REFERENCED_TABLE_NAME AS referenced_table,
             kcu.REFERENCED_COLUMN_NAME AS referenced_column
         FROM information_schema.TABLE_CONSTRAINTS tc
         JOIN information_schema.KEY_COLUMN_USAGE kcu
             ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
             AND tc.TABLE_SCHEMA = kcu.TABLE_SCHEMA
         WHERE tc.CONSTRAINT_TYPE = 'FOREIGN KEY'
-        AND tc.TABLE_NAME = '{table_name}'
-        AND tc.TABLE_SCHEMA = '{schema}';
+        AND tc.TABLE_NAME = :table_name
+        AND tc.TABLE_SCHEMA = :schema
     """
 
     try:
-        rows = self.fetchall(query)
+        result = self.connection.execute(
+            text(query), 
+            {"table_name": table_name, "schema": schema}
+        )
+        rows = result.fetchall()
     except Exception as e:
         logger.error(f"Failed to fetch fk info for dataset: {table_name} ({e})")
         return []
 
     data = [
         {
             "constraint_name": row[0],
             "table_name": row[1],
             "fk_column": row[2],
             "referenced_table": row[3],
             "referenced_column": row[4],
         }
         for row in rows
     ]
     return data

Note: This SQL injection pattern appears throughout the codebase in similar query methods. Consider addressing this systematically across all database datasource classes.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.14.6)

405-419: Possible SQL injection vector through string-based query construction

(S608)


423-423: Do not catch blind exception: Exception

(BLE001)

Comment on lines +706 to +748
def get_table_foreign_key_info(self, table_name: str, schema: str | None = None):
schema = schema or self.schema_name

query = f"""
SELECT
ac.CONSTRAINT_NAME AS constraint_name,
ac.TABLE_NAME AS table_name,
acc.COLUMN_NAME AS fk_column,
r_ac.TABLE_NAME AS referenced_table,
r_acc.COLUMN_NAME AS referenced_column
FROM ALL_CONSTRAINTS ac
JOIN ALL_CONS_COLUMNS acc
ON ac.CONSTRAINT_NAME = acc.CONSTRAINT_NAME
AND ac.OWNER = acc.OWNER
JOIN ALL_CONSTRAINTS r_ac
ON ac.R_CONSTRAINT_NAME = r_ac.CONSTRAINT_NAME
AND ac.R_OWNER = r_ac.OWNER
JOIN ALL_CONS_COLUMNS r_acc
ON r_ac.CONSTRAINT_NAME = r_acc.CONSTRAINT_NAME
AND r_ac.OWNER = r_acc.OWNER
AND acc.POSITION = r_acc.POSITION
WHERE ac.CONSTRAINT_TYPE = 'R'
AND ac.TABLE_NAME = '{table_name.upper()}'
AND ac.OWNER = '{schema.upper()}';
"""

try:
rows = self.fetchall(query)
except Exception as e:
logger.error(f"Failed to fetch fk info for dataset: {table_name} ({e})")
return []

data = [
{
"constraint_name": row[0],
"table_name": row[1],
"fk_column": row[2],
"referenced_table": row[3],
"referenced_column": row[4],
}
for row in rows
]
return data

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

SQL injection vulnerability: use parameterized queries.

The table_name and schema parameters are directly interpolated into the SQL query using f-strings. Even though .upper() is called, this does not prevent SQL injection attacks. A malicious input like table_name="users' OR '1'='1" could manipulate the query logic.

Apply this diff to use parameterized queries with SQLAlchemy:

 def get_table_foreign_key_info(self, table_name: str, schema: str | None = None):
     schema = schema or self.schema_name
 
     query = f"""
         SELECT
             ac.CONSTRAINT_NAME AS constraint_name,
             ac.TABLE_NAME AS table_name,
             acc.COLUMN_NAME AS fk_column,
             r_ac.TABLE_NAME AS referenced_table,
             r_acc.COLUMN_NAME AS referenced_column
         FROM ALL_CONSTRAINTS ac
         JOIN ALL_CONS_COLUMNS acc
             ON ac.CONSTRAINT_NAME = acc.CONSTRAINT_NAME
             AND ac.OWNER = acc.OWNER
         JOIN ALL_CONSTRAINTS r_ac
             ON ac.R_CONSTRAINT_NAME = r_ac.CONSTRAINT_NAME
             AND ac.R_OWNER = r_ac.OWNER
         JOIN ALL_CONS_COLUMNS r_acc
             ON r_ac.CONSTRAINT_NAME = r_acc.CONSTRAINT_NAME
             AND r_ac.OWNER = r_acc.OWNER
             AND acc.POSITION = r_acc.POSITION
         WHERE ac.CONSTRAINT_TYPE = 'R'
-        AND ac.TABLE_NAME = '{table_name.upper()}'
-        AND ac.OWNER = '{schema.upper()}';
+        AND ac.TABLE_NAME = :table_name
+        AND ac.OWNER = :schema
     """
 
     try:
-        rows = self.fetchall(query)
+        from sqlalchemy import text
+        result = self.connection.execute(
+            text(query), 
+            {"table_name": table_name.upper(), "schema": schema.upper()}
+        )
+        rows = result.fetchall()
     except Exception as e:
         logger.error(f"Failed to fetch fk info for dataset: {table_name} ({e})")
         return []
 
     data = [
         {
             "constraint_name": row[0],
             "table_name": row[1],
             "fk_column": row[2],
             "referenced_table": row[3],
             "referenced_column": row[4],
         }
         for row in rows
     ]
     return data

Note: Similar SQL injection vulnerabilities exist in other methods throughout the codebase (e.g., query_get_table_indexes, query_get_table_columns). Consider a broader refactoring effort to use parameterized queries consistently.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.14.6)

709-730: Possible SQL injection vector through string-based query construction

(S608)


734-734: Do not catch blind exception: Exception

(BLE001)

@AnshumanX304 AnshumanX304 merged commit 5f26f7b into datachecks:main Nov 28, 2025
5 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants