Skip to content

fix(oracle): restore catalog-aware constraint reflection - #31809

Merged
IceS2 merged 1 commit into
1.13from
backport-31676-1.13
Aug 20, 2026
Merged

fix(oracle): restore catalog-aware constraint reflection#31809
IceS2 merged 1 commit into
1.13from
backport-31676-1.13

Conversation

@IceS2

@IceS2 IceS2 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Backports #31676 to 1.13.

This applies catalog-aware Oracle primary-key, unique-constraint, and foreign-key reflection and preserves backing-index metadata.

Focused verification: 4 Oracle constraint-reflection tests passed.

Greptile Summary

The PR restores catalog-aware Oracle constraint reflection by overriding primary-key, unique-constraint, and foreign-key reflection while preserving backing-index metadata.

  • Uses the configured DBA_* or ALL_* Oracle catalog views for constraint queries.
  • Resolves synonyms and database links before reflecting constraints.
  • Adds focused tests for catalog selection, empty results, synonym resolution, foreign keys, and composite unique constraints.

Confidence Score: 5/5

The PR appears safe to merge; no concrete changed-code defect or security issue was identified.

The new reflection methods preserve the expected constraint shapes, select from the configured Oracle catalog, handle synonym targets before querying, and are covered by focused regression tests.

Important Files Changed

Filename Overview
ingestion/src/metadata/ingestion/source/database/oracle/metadata.py Rebinds the Oracle dialect’s public constraint-reflection methods to catalog-aware connector implementations.
ingestion/src/metadata/ingestion/source/database/oracle/queries.py Extends the constraint query with the backing index name required for unique-constraint metadata.
ingestion/src/metadata/ingestion/source/database/oracle/utils.py Adds cached primary-key, unique-constraint, and foreign-key reflection with shared synonym, schema, and database-link preparation.
ingestion/tests/unit/topology/database/test_oracle_constraint_reflection.py Covers both supported catalog prefixes, reflected constraint shapes, empty defaults, and synonym resolution.

Reviews (1): Last reviewed commit: "fix(oracle): restore catalog-aware const..." | Re-trigger Greptile

Context used:

@IceS2
IceS2 requested a review from a team as a code owner August 20, 2026 06:27
@github-actions

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

  • No GitHub issue is linked. Link an issue in the Development section of the PR (or add Fixes #12345 to the description). For a same-org cross-repo issue, add Fixes open-metadata/<repo>#123 to the description.

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

@github-actions github-actions Bot added Ingestion safe to test Add this label to run secure Github workflows on PRs labels Aug 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

The Python checkstyle failed.

Please run make py_format and py_format_check in the root of your repository and commit the changes to this PR.
You can also use pre-commit to automate the Python code formatting.

You can install the pre-commit hooks with make install_test precommit_install.

Comment on lines +456 to +470
if kw.get("oracle_resolve_synonyms", False):
rows = list(
self._get_synonyms(
connection,
schema,
[table_name],
dblink,
info_cache=kw.get("info_cache"),
)
)
if rows:
row = rows[0]
table_name = self.denormalize_name(row.table_name)
schema = self.denormalize_name(row.table_owner)
if row.db_link:

@gitar-bot gitar-bot Bot Aug 20, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Bug: Synonym path skips name denormalization when no synonym found

When oracle_resolve_synonyms=True but _get_synonyms returns no rows (i.e. the object is a regular table, not a synonym), _prepare_constraint_args leaves table_name/schema as the normalized (lowercase) values received from the inspector and never calls denormalize_name. The constraint query then matches ac.table_name = :table_name/ac.owner = :owner against Oracle's uppercase data dictionary and returns nothing, so PK/unique/FK constraints silently disappear. SQLAlchemy's original _prepare_reflection_args always falls back to denormalize_name(table_name) and denormalize_name(schema or default_schema_name) when no synonym is resolved. Add the same fallback in the if rows: branch's else path.

Denormalize table/schema even when synonym resolution yields no rows.:

if kw.get("oracle_resolve_synonyms", False):
    rows = list(
        self._get_synonyms(
            connection, schema, [table_name], dblink,
            info_cache=kw.get("info_cache"),
        )
    )
    if rows:
        row = rows[0]
        table_name = self.denormalize_name(row.table_name)
        schema = self.denormalize_name(row.table_owner)
        if row.db_link:
            dblink = row.db_link if row.db_link.startswith("@") else f"@{row.db_link}"
    else:
        table_name = self.denormalize_name(table_name)
        schema = self.denormalize_name(schema or self.default_schema_name)
else:

Was this helpful? React with 👍 / 👎

@gitar-bot

gitar-bot Bot commented Aug 20, 2026

Copy link
Copy Markdown
Code Review ⚠️ Changes requested 0 resolved / 1 findings

Backports catalog-aware constraint reflection for Oracle databases. Synonym path skips name denormalization when no synonym found, which needs to be addressed.

⚠️ Bug: Synonym path skips name denormalization when no synonym found

📄 ingestion/src/metadata/ingestion/source/database/oracle/utils.py:456-470

When oracle_resolve_synonyms=True but _get_synonyms returns no rows (i.e. the object is a regular table, not a synonym), _prepare_constraint_args leaves table_name/schema as the normalized (lowercase) values received from the inspector and never calls denormalize_name. The constraint query then matches ac.table_name = :table_name/ac.owner = :owner against Oracle's uppercase data dictionary and returns nothing, so PK/unique/FK constraints silently disappear. SQLAlchemy's original _prepare_reflection_args always falls back to denormalize_name(table_name) and denormalize_name(schema or default_schema_name) when no synonym is resolved. Add the same fallback in the if rows: branch's else path.

Denormalize table/schema even when synonym resolution yields no rows.
if kw.get("oracle_resolve_synonyms", False):
    rows = list(
        self._get_synonyms(
            connection, schema, [table_name], dblink,
            info_cache=kw.get("info_cache"),
        )
    )
    if rows:
        row = rows[0]
        table_name = self.denormalize_name(row.table_name)
        schema = self.denormalize_name(row.table_owner)
        if row.db_link:
            dblink = row.db_link if row.db_link.startswith("@") else f"@{row.db_link}"
    else:
        table_name = self.denormalize_name(table_name)
        schema = self.denormalize_name(schema or self.default_schema_name)
else:
🤖 Prompt for agents
Code Review: Backports catalog-aware constraint reflection for Oracle databases. Synonym path skips name denormalization when no synonym found, which needs to be addressed.

1. ⚠️ Bug: Synonym path skips name denormalization when no synonym found
   Files: ingestion/src/metadata/ingestion/source/database/oracle/utils.py:456-470

   When `oracle_resolve_synonyms=True` but `_get_synonyms` returns no rows (i.e. the object is a regular table, not a synonym), `_prepare_constraint_args` leaves `table_name`/`schema` as the normalized (lowercase) values received from the inspector and never calls `denormalize_name`. The constraint query then matches `ac.table_name = :table_name`/`ac.owner = :owner` against Oracle's uppercase data dictionary and returns nothing, so PK/unique/FK constraints silently disappear. SQLAlchemy's original `_prepare_reflection_args` always falls back to `denormalize_name(table_name)` and `denormalize_name(schema or default_schema_name)` when no synonym is resolved. Add the same fallback in the `if rows:` branch's else path.

   Fix (Denormalize table/schema even when synonym resolution yields no rows.):
   if kw.get("oracle_resolve_synonyms", False):
       rows = list(
           self._get_synonyms(
               connection, schema, [table_name], dblink,
               info_cache=kw.get("info_cache"),
           )
       )
       if rows:
           row = rows[0]
           table_name = self.denormalize_name(row.table_name)
           schema = self.denormalize_name(row.table_owner)
           if row.db_link:
               dblink = row.db_link if row.db_link.startswith("@") else f"@{row.db_link}"
       else:
           table_name = self.denormalize_name(table_name)
           schema = self.denormalize_name(schema or self.default_schema_name)
   else:

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

@IceS2
IceS2 merged commit 2668762 into 1.13 Aug 20, 2026
63 of 88 checks passed
@IceS2
IceS2 deleted the backport-31676-1.13 branch August 20, 2026 06:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ingestion safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant