Skip to content

fix: scope index/constraint drop macros to the model's schema - #774

Merged
axellpadilla merged 1 commit into
dbt-msft:masterfrom
joshmarkovic:jm/fix-cross-schema-index-drops
Jul 27, 2026
Merged

fix: scope index/constraint drop macros to the model's schema#774
axellpadilla merged 1 commit into
dbt-msft:masterfrom
joshmarkovic:jm/fix-cross-schema-index-drops

Conversation

@joshmarkovic

Copy link
Copy Markdown
Contributor

Problem

drop_all_indexes_on_table() and the macros it calls matched tables by name only, with no schema predicate:

  • drop_xml_indexes()
  • drop_spatial_indexes()
  • drop_fk_constraints()
  • drop_pk_constraints()

Only the trailing remaining-indexes statement filtered by schema. These are public macros users call from post-hooks.

Impact

Given staging.orders and marts.orders in one database, a post-hook on either model dropped the other's primary key, inbound foreign keys, and XML/spatial indexes.

Before After
Other schema's PK dropped kept
Other schema's inbound FK dropped kept
Other schema's XML/spatial indexes dropped kept
This model's indexes dropped dropped

Changes

  • All four macros now filter on the model's schema.
  • In drop_fk_constraints() the filter applies to the referenced table (the model). The constraint is still dropped from the referencing parent table, which may legitimately live in another schema. Comment added, since this asymmetry is easy to "fix" wrongly later.
  • Generated dynamic SQL wraps identifiers in QUOTENAME().
    • Composite schema.name literals use REPLACE() instead of nested QUOTENAME(), because QUOTENAME() returns NULL above 128 input characters and would silently drop rows from the FOR XML aggregation.

Testing

Extended TestIndexDropsOnlySchema with a PK and an inbound FK in the sibling schema. Verified against SQL Server 2022 that the test fails without the macro change:

  • unfixed: assert 1 == 2 (PK's clustered index gone)
  • unfixed, FK assertion isolated: assert 0 == 1 (inbound FK gone)
  • fixed: passes

Not addressed

These macros aggregate with for xml path(''), which XML-escapes &, < and >. An identifier containing those still produces broken SQL. That is a separate mechanism (fix would be , type).value('.', 'nvarchar(max)')) and is left out of scope here.

drop_xml_indexes(), drop_spatial_indexes(), drop_fk_constraints() and
drop_pk_constraints() matched tables by name alone, so a post-hook calling
drop_all_indexes_on_table() dropped the primary key, inbound foreign keys
and XML/spatial indexes of every same-named table in other schemas of the
same database. Only the trailing remaining-indexes statement filtered by
schema.

All four now filter on the model's schema. In drop_fk_constraints() the
filter applies to the referenced table (the model); the constraint is still
dropped from the referencing parent table, which may live in another schema.

Identifiers in the generated dynamic SQL are wrapped in QUOTENAME(), using
REPLACE() for composite schema.name literals since QUOTENAME() returns NULL
above 128 input characters and would silently drop rows from the FOR XML
aggregation.
@joshmarkovic
joshmarkovic marked this pull request as ready for review July 27, 2026 15:28
@axellpadilla
axellpadilla merged commit fe798ef into dbt-msft:master Jul 27, 2026
15 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