Skip to content

fix: preserve manually-created partial indexes when partialIndexes preview feature is disabled#5790

Merged
jacek-prisma merged 8 commits intoprisma:mainfrom
jay-l-e-e:fix/preserve-feature-gated-partial-indexes
Mar 9, 2026
Merged

fix: preserve manually-created partial indexes when partialIndexes preview feature is disabled#5790
jacek-prisma merged 8 commits intoprisma:mainfrom
jay-l-e-e:fix/preserve-feature-gated-partial-indexes

Conversation

@jay-l-e-e
Copy link
Copy Markdown
Contributor

@jay-l-e-e jay-l-e-e commented Mar 7, 2026

Fixes #5789 and prisma/prisma#29289

Problem

#5780 stripped partial index predicates when partialIndexes is disabled, but manually-created partial indexes were still dropped as drift.

Solution

  • Track indexes whose predicates were stripped due to feature-gating in SqlSchema::feature_gated_partial_indexes
  • Exclude them from dropped_indexes in the differ

Changes

  • sql-schema-describer: Added feature_gated_partial_indexes: HashSet<IndexId> to track stripped indexes
  • sql-schema-connector: Skip feature-gated partial indexes in dropped_indexes
  • Added regression tests for PostgreSQL, MSSQL, SQLite, and CockroachDB

Summary by CodeRabbit

  • Bug Fixes

    • Manual partial indexes are preserved (not dropped) when the partialIndexes preview feature is disabled, improving schema sync and diff stability.
  • New Features

    • Schema describe outputs now include metadata about stripped partial indexes and an API to query that state.
    • Preview-feature flags are propagated through diff and diagnose flows so tooling respects preview settings.
  • Tests

    • Added cross‑database tests ensuring manual partial indexes are ignored by migrations/diffs when the preview feature is off.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 7, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5c0eb5f7-8340-41a7-aa6d-9b0897b32c4d

📥 Commits

Reviewing files that changed from the base of the PR and between c0afcb5 and 746c460.

📒 Files selected for processing (1)
  • schema-engine/connectors/sql-schema-connector/src/sql_schema_differ/table.rs

Walkthrough

Adds preview-feature-aware handling for partial-index predicates: predicates are stripped and tracked when partialIndexes is off, preview features are propagated through shadow DB and diff paths, connector APIs expose preview features, and tests updated/added to cover behavior across engines.

Changes

Cohort / File(s) Summary
Schema describer core
schema-engine/sql-schema-describer/src/lib.rs, schema-engine/sql-schema-describer/src/stripped_partial_indexes.rs, schema-engine/sql-schema-describer/src/walkers/index.rs
Add stripped_partial_indexes field and StrippedPartialIndexes type; replace clear_index_predicates() with strip_partial_index_predicates(); add index_is_stripped_partial() and IndexWalker::is_stripped_partial().
SQL schema connector
schema-engine/connectors/sql-schema-connector/src/lib.rs, schema-engine/connectors/sql-schema-connector/src/sql_schema_differ/table.rs
Introduce apply_partial_index_feature_gating() (free fn + impl wrapper), propagate preview_features to gating sites, replace previous predicate-clearing calls, and skip stripped partial indexes in TableDiffer dropped-index logic.
Connector interface & implementations
schema-engine/connectors/schema-connector/src/schema_connector.rs, schema-engine/connectors/mongodb-schema-connector/src/lib.rs
Add SchemaConnector::preview_features() accessor; change ExternalShadowDatabase::DriverAdapter and ConnectionString variants to include preview_features; add MongoDbSchemaConnector::preview_features().
Commands / diff integration
schema-engine/commands/src/commands/diagnose_migration_history.rs, schema-engine/commands/src/commands/diff.rs, schema-engine/core/src/commands/diff_cli.rs, schema-engine/core/src/state.rs
Propagate/merge preview features into diff and diagnose flows; update DriverAdapter construction to include preview features; add diff_cli initial_preview_features parameter and EngineState::preview_features().
Tests — migration and diagnose
schema-engine/sql-migration-tests/tests/migrations/indexes/partial.rs, schema-engine/sql-migration-tests/tests/migrations/diagnose_migration_history_tests.rs, schema-engine/sql-migration-tests/tests/migrations/diff.rs
Add tests asserting manual partial indexes are ignored when partialIndexes is off; add diff/diagnose tests and helpers for seeded initial datamodels and preview-feature-aware diffing.
Test infra
schema-engine/sql-migration-tests/src/test_api.rs
Add TestApi::preview_features() and pass preview features into diff/test CLI call sites.
Describer test snapshots
schema-engine/sql-schema-describer/tests/describers/*_describer_tests.rs
Update describer test expectations to include the new stripped_partial_indexes field in SqlSchema snapshots across engines.

Possibly related issues

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.98% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective: preserving manually-created partial indexes when the partialIndexes preview feature is disabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 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.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 082c92c9-4a0d-4f11-b6b1-7426f8a9499b

📥 Commits

Reviewing files that changed from the base of the PR and between 94a226b and 77a10d3.

📒 Files selected for processing (5)
  • schema-engine/connectors/sql-schema-connector/src/lib.rs
  • schema-engine/connectors/sql-schema-connector/src/sql_schema_differ/table.rs
  • schema-engine/sql-migration-tests/tests/migrations/indexes/partial.rs
  • schema-engine/sql-schema-describer/src/lib.rs
  • schema-engine/sql-schema-describer/src/walkers/index.rs

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6deb0f99-1de8-4cf6-88cc-f64ef6aff53e

📥 Commits

Reviewing files that changed from the base of the PR and between 77a10d3 and ba46c8b.

📒 Files selected for processing (1)
  • schema-engine/sql-schema-describer/src/lib.rs

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0788a199-39b6-4287-b65e-126eeba11095

📥 Commits

Reviewing files that changed from the base of the PR and between ba46c8b and e4489e9.

📒 Files selected for processing (7)
  • schema-engine/commands/src/commands/diagnose_migration_history.rs
  • schema-engine/commands/src/commands/diff.rs
  • schema-engine/connectors/mongodb-schema-connector/src/lib.rs
  • schema-engine/connectors/schema-connector/src/schema_connector.rs
  • schema-engine/connectors/sql-schema-connector/src/lib.rs
  • schema-engine/sql-migration-tests/tests/migrations/diagnose_migration_history_tests.rs
  • schema-engine/sql-migration-tests/tests/migrations/diff.rs

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
schema-engine/sql-migration-tests/src/test_api.rs (1)

221-227: ⚠️ Potential issue | 🟠 Major

Pass the active preview-feature set into diff_cli().

Line 225 hard-codes BitFlags::empty(), so this helper always drives the diff CLI down the "feature disabled" path. TestApi already tracks preview features later in this file (Lines 596-607), so tests that enable a preview feature can silently exercise the wrong diff behavior here.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e5fd1dd1-2d9f-4127-a687-7d6214d9f840

📥 Commits

Reviewing files that changed from the base of the PR and between e4489e9 and e0d921f.

📒 Files selected for processing (12)
  • schema-engine/commands/src/commands/diff.rs
  • schema-engine/core/src/commands/diff_cli.rs
  • schema-engine/core/src/state.rs
  • schema-engine/sql-migration-tests/src/test_api.rs
  • schema-engine/sql-migration-tests/tests/migrations/diff.rs
  • schema-engine/sql-schema-describer/src/feature_gated_partial_indexes.rs
  • schema-engine/sql-schema-describer/src/lib.rs
  • schema-engine/sql-schema-describer/tests/describers/mssql_describer_tests.rs
  • schema-engine/sql-schema-describer/tests/describers/mysql_describer_tests.rs
  • schema-engine/sql-schema-describer/tests/describers/postgres_describer_tests.rs
  • schema-engine/sql-schema-describer/tests/describers/postgres_describer_tests/cockroach_describer_tests.rs
  • schema-engine/sql-schema-describer/tests/describers/sqlite_describer_tests.rs

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
schema-engine/connectors/sql-schema-connector/src/sql_schema_differ/table.rs (1)

63-69: ⚠️ Potential issue | 🟠 Major

Ignore stripped partials in all index matching paths, not just drops.

Line 68 only fixes the drop path. created_indexes() and index_pairs() still consume stripped partial indexes through previous_indexes()/next_indexes(), and once the predicate has been nulled they can be matched as ordinary indexes. That can turn a manual partial index into a false “already exists” or rename candidate instead of ignoring it.

Suggested fix
 pub fn dropped_indexes<'a>(&'a self) -> impl Iterator<Item = IndexWalker<'schema>> + 'a {
     self.previous_indexes().filter(move |previous_index| {
         !self
             .next_indexes()
             .any(|next_index| indexes_match(*previous_index, next_index, self.db.flavour))
-            && !previous_index.is_stripped_partial()
     })
 }
 
 fn previous_indexes(&self) -> impl Iterator<Item = IndexWalker<'schema>> {
-    self.previous().indexes().filter(|idx| !idx.is_primary_key())
+    self.previous()
+        .indexes()
+        .filter(|idx| !idx.is_primary_key() && !idx.is_stripped_partial())
 }
 
 fn next_indexes(&self) -> impl Iterator<Item = IndexWalker<'schema>> {
-    self.next().indexes().filter(|idx| !idx.is_primary_key())
+    self.next()
+        .indexes()
+        .filter(|idx| !idx.is_primary_key() && !idx.is_stripped_partial())
 }

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2d7bbb17-1ea9-46a9-a86d-2e7d0d97a445

📥 Commits

Reviewing files that changed from the base of the PR and between e0d921f and 1b9b7e5.

📒 Files selected for processing (11)
  • schema-engine/connectors/sql-schema-connector/src/lib.rs
  • schema-engine/connectors/sql-schema-connector/src/sql_schema_differ/table.rs
  • schema-engine/sql-migration-tests/tests/migrations/diff.rs
  • schema-engine/sql-schema-describer/src/lib.rs
  • schema-engine/sql-schema-describer/src/stripped_partial_indexes.rs
  • schema-engine/sql-schema-describer/src/walkers/index.rs
  • schema-engine/sql-schema-describer/tests/describers/mssql_describer_tests.rs
  • schema-engine/sql-schema-describer/tests/describers/mysql_describer_tests.rs
  • schema-engine/sql-schema-describer/tests/describers/postgres_describer_tests.rs
  • schema-engine/sql-schema-describer/tests/describers/postgres_describer_tests/cockroach_describer_tests.rs
  • schema-engine/sql-schema-describer/tests/describers/sqlite_describer_tests.rs

@jay-l-e-e
Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 7, 2026

✅ Actions performed

Reviews resumed.

@jay-l-e-e
Copy link
Copy Markdown
Contributor Author

Hi @jacek-prisma. Please review these changes! Thank you.

@jay-l-e-e
Copy link
Copy Markdown
Contributor Author

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)

schema-engine/connectors/sql-schema-connector/src/sql_schema_differ/table.rs (1)> 63-69: ⚠️ Potential issue | 🟠 Major

Ignore stripped partials in all index matching paths, not just drops.
Line 68 only fixes the drop path. created_indexes() and index_pairs() still consume stripped partial indexes through previous_indexes()/next_indexes(), and once the predicate has been nulled they can be matched as ordinary indexes. That can turn a manual partial index into a false “already exists” or rename candidate instead of ignoring it.

Suggested fix

 pub fn dropped_indexes<'a>(&'a self) -> impl Iterator<Item = IndexWalker<'schema>> + 'a {
     self.previous_indexes().filter(move |previous_index| {
         !self
             .next_indexes()
             .any(|next_index| indexes_match(*previous_index, next_index, self.db.flavour))
-            && !previous_index.is_stripped_partial()
     })
 }
 
 fn previous_indexes(&self) -> impl Iterator<Item = IndexWalker<'schema>> {
-    self.previous().indexes().filter(|idx| !idx.is_primary_key())
+    self.previous()
+        .indexes()
+        .filter(|idx| !idx.is_primary_key() && !idx.is_stripped_partial())
 }
 
 fn next_indexes(&self) -> impl Iterator<Item = IndexWalker<'schema>> {
-    self.next().indexes().filter(|idx| !idx.is_primary_key())
+    self.next()
+        .indexes()
+        .filter(|idx| !idx.is_primary_key() && !idx.is_stripped_partial())
 }

ℹ️ Review info

Note:
I tried this suggestion, but it causes regressions in existing tests:

  • db_partial_index_not_recreated_without_preview_feature_mssql
  • db_partial_index_not_recreated_without_preview_feature_postgres
  • db_partial_index_not_recreated_without_preview_feature_sqlite

The issue is that stripped partial indexes must still participate in existence matching so the differ recognizes "an index with these columns already exists" and skips CreateIndex. Excluding them from previous_indexes() makes the differ think the DB has no such index, so it tries to create one — which fails on MSSQL with index already exists.

The correct behavior is:

  • Drop path: exclude stripped partials (so we don't drop manual partial indexes)
  • Match/create paths: keep them visible (so we don't recreate indexes that already exist)

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 9, 2026

Merging this PR will not alter performance

✅ 11 untouched benchmarks
⏩ 11 skipped benchmarks1


Comparing jay-l-e-e:fix/preserve-feature-gated-partial-indexes (746c460) with main (94a226b)

Open in CodSpeed

Footnotes

  1. 11 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@jacek-prisma jacek-prisma merged commit ab73dcf into prisma:main Mar 9, 2026
94 of 99 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.

Manually-created partial indexes dropped when partialIndexes preview feature is disabled

2 participants