Skip to content

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

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

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

Conversation

@IceS2

@IceS2 IceS2 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Backports #31676 to 2.0.

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

Restores catalog-aware Oracle constraint reflection by overriding primary-key, unique-constraint, and foreign-key inspection while retaining backing-index metadata.

  • Uses the configured Oracle catalog prefix for constraint queries.
  • Handles synonym and database-link reflection arguments.
  • Adds focused tests for DBA/ALL catalogs, empty results, and synonym resolution.

Confidence Score: 5/5

The pull request appears safe to merge, with no concrete changed-code failure established.

The new reflection methods preserve the expected constraint shapes, route queries through the configured Oracle catalog, and include focused coverage for the principal catalog and synonym paths.

Important Files Changed

Filename Overview
ingestion/src/metadata/ingestion/source/database/oracle/metadata.py Rebinds the three public Oracle constraint-reflection methods to catalog-aware implementations.
ingestion/src/metadata/ingestion/source/database/oracle/queries.py Extends the constraint query with the backing index name needed for unique-constraint metadata.
ingestion/src/metadata/ingestion/source/database/oracle/utils.py Implements catalog-aware PK, unique, and foreign-key reflection with identifier, synonym, and database-link handling.
ingestion/tests/unit/topology/database/test_oracle_constraint_reflection.py Verifies both catalog prefixes, empty reflection results, backing-index metadata, and synonym resolution.

Sequence Diagram

sequenceDiagram
    participant Ingestion as Oracle ingestion
    participant Inspector as SQLAlchemy Inspector
    participant Dialect as Patched OracleDialect
    participant Catalog as Oracle catalog
    Ingestion->>Inspector: Reflect table constraints
    Inspector->>Dialect: get_pk_constraint / get_unique_constraints / get_foreign_keys
    Dialect->>Dialect: Resolve synonym and normalize identifiers
    Dialect->>Catalog: Query configured DBA_ or ALL_ catalog views
    Catalog-->>Dialect: Constraint rows and backing index names
    Dialect-->>Inspector: SQLAlchemy reflection metadata
    Inspector-->>Ingestion: PK, unique, and foreign-key definitions
Loading

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

@IceS2
IceS2 requested a review from a team as a code owner August 20, 2026 06:17
@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 +422 to +436
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 resolution skips denormalization when no synonym found

In _prepare_constraint_args, when oracle_resolve_synonyms=True but self._get_synonyms(...) returns no rows (the table is a real table, not a synonym), table_name/schema are never denormalized and are passed to the query as the raw (typically lowercase) input. Since Oracle stores identifiers uppercase, ac.table_name = :table_name won't match and PK/UQ/FK reflection silently returns empty. Upstream SQLAlchemy always falls back to denormalize_name(...) in this case. Fix by denormalizing on the no-synonym fallback path.

Always denormalize on the no-synonym fallback, matching upstream behavior.:

def _prepare_constraint_args(self, connection, table_name, schema, **kw):
    dblink = kw.get("dblink", "")
    if dblink and not dblink.startswith("@"):
        dblink = f"@{dblink}"

    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}"
            return table_name, schema, dblink

    table_name = self.denormalize_name(table_name)
    schema = self.denormalize_name(schema or self.default_schema_name)
    return table_name, schema, dblink

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 Oracle constraint reflection to target branch. However, synonym resolution skips denormalization when no synonym is found, which breaks constraint argument preparation.

⚠️ Bug: Synonym resolution skips denormalization when no synonym found

📄 ingestion/src/metadata/ingestion/source/database/oracle/utils.py:422-436

In _prepare_constraint_args, when oracle_resolve_synonyms=True but self._get_synonyms(...) returns no rows (the table is a real table, not a synonym), table_name/schema are never denormalized and are passed to the query as the raw (typically lowercase) input. Since Oracle stores identifiers uppercase, ac.table_name = :table_name won't match and PK/UQ/FK reflection silently returns empty. Upstream SQLAlchemy always falls back to denormalize_name(...) in this case. Fix by denormalizing on the no-synonym fallback path.

Always denormalize on the no-synonym fallback, matching upstream behavior.
def _prepare_constraint_args(self, connection, table_name, schema, **kw):
    dblink = kw.get("dblink", "")
    if dblink and not dblink.startswith("@"):
        dblink = f"@{dblink}"

    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}"
            return table_name, schema, dblink

    table_name = self.denormalize_name(table_name)
    schema = self.denormalize_name(schema or self.default_schema_name)
    return table_name, schema, dblink
🤖 Prompt for agents
Code Review: Backports catalog-aware Oracle constraint reflection to target branch. However, synonym resolution skips denormalization when no synonym is found, which breaks constraint argument preparation.

1. ⚠️ Bug: Synonym resolution skips denormalization when no synonym found
   Files: ingestion/src/metadata/ingestion/source/database/oracle/utils.py:422-436

   In `_prepare_constraint_args`, when `oracle_resolve_synonyms=True` but `self._get_synonyms(...)` returns no rows (the table is a real table, not a synonym), `table_name`/`schema` are never denormalized and are passed to the query as the raw (typically lowercase) input. Since Oracle stores identifiers uppercase, `ac.table_name = :table_name` won't match and PK/UQ/FK reflection silently returns empty. Upstream SQLAlchemy always falls back to `denormalize_name(...)` in this case. Fix by denormalizing on the no-synonym fallback path.

   Fix (Always denormalize on the no-synonym fallback, matching upstream behavior.):
   def _prepare_constraint_args(self, connection, table_name, schema, **kw):
       dblink = kw.get("dblink", "")
       if dblink and not dblink.startswith("@"):
           dblink = f"@{dblink}"
   
       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}"
               return table_name, schema, dblink
   
       table_name = self.denormalize_name(table_name)
       schema = self.denormalize_name(schema or self.default_schema_name)
       return table_name, schema, dblink

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 d9687d7 into 2.0 Aug 20, 2026
70 of 86 checks passed
@IceS2
IceS2 deleted the backport-31676-2.0 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