Repository navigation
fix: schema-qualify the CREATE INDEX CONCURRENTLY wait query (#641) - #646
Conversation
) The wait directive emitted after CREATE INDEX CONCURRENTLY matched the index by pg_class.relname only. checkWaitStatus reads the first row, so an invalid index with the same name in another schema (e.g. a failed concurrent build left behind in a sibling tenant schema) made apply wait forever. Filter the wait query by relnamespace as well, for the create, change, and rewrite paths, and update the plan fixtures that embed the wait query.
[Medium impact] Fixes index wait query to be schema-qualified. Fix quoted schema names before merging; the new wait predicate can stop otherwise valid index migrations. FindingsSummaryThe PR limits concurrent-index waits to the index's schema.
Scope acknowledged by Reviews (1) · Last reviewed commit: "fix: schema-qualify the CREATE INDEX CON..." · Reviewed by Greptile |
The '<schema>'::regnamespace cast case-folds unquoted names, so a mixed-case schema such as "TenantB" was looked up as tenantb, and an apostrophe in the schema name produced invalid SQL. Join pg_namespace and compare nspname (and relname) against properly escaped string literals. Make the issue pgplex#641 integration test fail after a deadline instead of hanging, cover a mixed-case quoted schema, and regenerate the plan fixtures that embed the wait query.
…t tests and fixtures)
There was a problem hiding this comment.
🟢 Approval recommended
The focused change consistently filters all three rewrite paths by target schema, with matching tests and fixtures and no unresolved blocking issues.
0 open findings
What changed in this PR
Fixes #641 by limiting concurrent-index wait queries to the target schema, preventing invalid same-named indexes in other schemas from blocking apply.
Changes:
- Pass the schema through all three index rewrite paths and filter by exact namespace name.
- Add tests for schema filtering, mixed-case names, and apostrophe escaping.
- Update expected plan outputs to include schema-qualified waits.
| File | Description |
|---|---|
| testdata/diff/online/issue_509_index_rebuild_dropped_column/plan.txt | Qualify rebuilt-index wait. |
| testdata/diff/online/issue_509_index_rebuild_dropped_column/plan.sql | Qualify rebuilt-index wait. |
| testdata/diff/online/issue_509_index_rebuild_dropped_column/plan.json | Qualify rebuilt-index wait. |
| testdata/diff/online/issue_418_partitioned_parent_index/plan.txt | Qualify partition-index wait. |
| testdata/diff/online/issue_418_partitioned_parent_index/plan.sql | Qualify partition-index wait. |
| testdata/diff/online/issue_418_partitioned_parent_index/plan.json | Qualify partition-index wait. |
| testdata/diff/online/issue_286_reserved_keyword_quoting/plan.txt | Qualify quoted-index wait. |
| testdata/diff/online/issue_286_reserved_keyword_quoting/plan.sql | Qualify quoted-index wait. |
| testdata/diff/online/issue_286_reserved_keyword_quoting/plan.json | Qualify quoted-index wait. |
| testdata/diff/online/alter_materialized_view_index/plan.txt | Qualify replacement-index wait. |
| testdata/diff/online/alter_materialized_view_index/plan.sql | Qualify replacement-index wait. |
| testdata/diff/online/alter_materialized_view_index/plan.json | Qualify replacement-index wait. |
| testdata/diff/online/alter_composite_index/plan.txt | Qualify replacement-index waits. |
| testdata/diff/online/alter_composite_index/plan.sql | Qualify replacement-index waits. |
| testdata/diff/online/alter_composite_index/plan.json | Qualify replacement-index waits. |
| testdata/diff/online/add_unique_multi_column_index/plan.txt | Qualify unique-index wait. |
| testdata/diff/online/add_unique_multi_column_index/plan.sql | Qualify unique-index wait. |
| testdata/diff/online/add_unique_multi_column_index/plan.json | Qualify unique-index wait. |
| testdata/diff/online/add_partial_index/plan.txt | Qualify partial-index wait. |
| testdata/diff/online/add_partial_index/plan.sql | Qualify partial-index wait. |
| testdata/diff/online/add_partial_index/plan.json | Qualify partial-index wait. |
| testdata/diff/online/add_materialized_view_index/plan.txt | Qualify materialized-view index wait. |
| testdata/diff/online/add_materialized_view_index/plan.sql | Qualify materialized-view index wait. |
| testdata/diff/online/add_materialized_view_index/plan.json | Qualify materialized-view index wait. |
| testdata/diff/online/add_functional_index/plan.txt | Qualify functional-index wait. |
| testdata/diff/online/add_functional_index/plan.sql | Qualify functional-index wait. |
| testdata/diff/online/add_functional_index/plan.json | Qualify functional-index wait. |
| testdata/diff/online/add_composite_index/plan.txt | Qualify composite-index waits. |
| testdata/diff/online/add_composite_index/plan.sql | Qualify composite-index waits. |
| testdata/diff/online/add_composite_index/plan.json | Qualify composite-index waits. |
| testdata/diff/migrate/v4/plan.txt | Qualify migration index waits. |
| testdata/diff/migrate/v4/plan.sql | Qualify migration index waits. |
| testdata/diff/migrate/v4/plan.json | Qualify migration index waits. |
| testdata/diff/migrate/v2/plan.txt | Qualify migration index waits. |
| testdata/diff/migrate/v2/plan.sql | Qualify migration index waits. |
| testdata/diff/migrate/v2/plan.json | Qualify migration index waits. |
| testdata/diff/dependency/issue_535_domain_depends_on_table/plan.txt | Formatting-only fixture refresh. |
| testdata/diff/dependency/issue_505_drop_trigger_function_with_table/plan.txt | Formatting-only fixture refresh. |
| testdata/diff/dependency/issue_308_view_select_star_column_reorder/plan.txt | Qualify recreated-index wait. |
| testdata/diff/dependency/issue_308_view_select_star_column_reorder/plan.sql | Qualify recreated-index wait. |
| testdata/diff/dependency/issue_308_view_select_star_column_reorder/plan.json | Qualify recreated-index wait. |
| testdata/diff/create_trigger/add_trigger/plan.txt | Formatting-only fixture refresh. |
| testdata/diff/create_table/issue_591_alter_generated_column/plan.txt | Qualify generated-column index waits. |
| testdata/diff/create_table/issue_591_alter_generated_column/plan.sql | Qualify generated-column index waits. |
| testdata/diff/create_table/issue_591_alter_generated_column/plan.json | Qualify generated-column index waits. |
| testdata/diff/create_table/issue_506_fk_depends_on_new_unique/plan.txt | Qualify prerequisite unique-index wait. |
| testdata/diff/create_table/issue_506_fk_depends_on_new_unique/plan.sql | Qualify prerequisite unique-index wait. |
| testdata/diff/create_table/issue_506_fk_depends_on_new_unique/plan.json | Qualify prerequisite unique-index wait. |
| testdata/diff/create_table/issue_295_pgvector_typmod/plan.txt | Qualify vector-index wait. |
| testdata/diff/create_table/issue_295_pgvector_typmod/plan.sql | Qualify vector-index wait. |
| testdata/diff/create_table/issue_295_pgvector_typmod/plan.json | Qualify vector-index wait. |
| testdata/diff/create_index/alter_index_null_order/plan.txt | Qualify replacement-index wait. |
| testdata/diff/create_index/alter_index_null_order/plan.sql | Qualify replacement-index wait. |
| testdata/diff/create_index/alter_index_null_order/plan.json | Qualify replacement-index wait. |
| testdata/diff/create_index/add_index_with_reloptions/plan.txt | Qualify index waits. |
| testdata/diff/create_index/add_index_with_reloptions/plan.sql | Qualify index waits. |
| testdata/diff/create_index/add_index_with_reloptions/plan.json | Qualify index waits. |
| internal/plan/rewrite.go | Filter waits by schema and escape names. |
| internal/plan/rewrite_test.go | Test schema propagation and exact-name escaping. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
tianzhou
left a comment
There was a problem hiding this comment.
LGTM. Thanks for the contribution
Fixes #641
Summary
After
CREATE INDEX CONCURRENTLY, the plan emits apgschema:waitquery that pollspg_index.indisvalid. That query matched the index bypg_class.relnameonly.checkWaitStatusreads the first row it gets back, so an invalid index with the same name in another schema could stallapplyindefinitely. This happens when a concurrent build failed and left an index behind in a sibling tenant schema.generateIndexWaitQueryWithNamenow takes the schema and addsAND c.relnamespace = '<schema>'::regnamespace. All three callers pass the index's schema: the plain create, the change via_pgschema_new, and the diff-driven rewrite.The plan fixtures that embed the wait query have been regenerated with
go test ./cmd -run TestPlanAndApply -generate. Theissue_295_pgvector_typmodfixture is skipped without pgvector, so it was updated by hand to match. Unrelatedpgschema_versionchurn from regeneration was reverted.Validation
Passed locally with Go 1.24.7 on Linux, embedded PostgreSQL 18:
New tests:
internal/plan/rewrite_test.go: the wait SQL is schema-qualified, and it is wired throughgenerateIndexRewrite.cmd/issue_641_integration_test.go: leaves an invalidtenant_a.items_sku_idxafter a failed concurrent unique build, then:done=false, which is the hang;done=truefortenant_b;tenant_bwith the invalid peer present. It finishes in about 0.2s.Scope
This only changes the wait query. It does not detect or clean up invalid leftover indexes. A same-schema invalid index of the same name is still a separate case, and
CREATE INDEX CONCURRENTLY IF NOT EXISTSwould skip it.