Skip to content

fix: preserve partition-index names and attachments in dumps - #643

Merged
tianzhou merged 6 commits into
pgplex:mainfrom
zikolach:fix/dump-partition-index-attachments
Oct 14, 2026
Merged

tianzhou merged 6 commits into
pgplex:mainfrom
zikolach:fix/dump-partition-index-attachments

Conversation

@zikolach

@zikolach zikolach commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Both single-file and multi-file dumps can create extra indexes when attached partition indexes have custom names. The dump generator creates parent indexes recursively, so PostgreSQL creates or selects child indexes before the exporter creates the named child indexes. Replay can therefore change index names, attachment relationships, and validity even when the SQL executes successfully.

The dump generator now creates partition tables and leaf indexes first, creates standalone partitioned indexes with ON ONLY, and emits explicit ALTER INDEX ... ATTACH PARTITION statements from the inspected relationships. For example, event_lookup retains its attached child early_custom_lookup, while an equivalent index named early_unattached_lookup remains unattached.

  • The inspector reads the parent index name and schema from pg_inherits.
  • The multi-file formatter keeps deferred parent-index statements in numbered indexes/ files.
  • The generator postpones FKs that need those partitioned unique indexes until the indexes and attachments exist.
  • Migration generation retains its existing recursive index strategy. Only dump uses explicit index attachments.

This builds on merged #642. It contains no FK-fix commits from that PR.

Nested-partition prerequisite

The nested-index regression exposed an existing table-generation defect: an intermediate partition lost its own PARTITION BY clause. PostgreSQL then rejected its child tables with "archive_events_early" is not partitioned.

A separate commit preserves that clause for intermediate partitions in both dump and migration generation. A native plan/apply fixture covers nested table creation, an intermediate column-default override, and an empty follow-up plan.

Ignored partition-index dependencies

If .pgschemaignore excludes an attached child index or its table while retaining
the parent index, dump returns an error naming both indexes before generating
SQL or writing files. Existing output files remain unchanged. This also applies
to nested partitions and indexes without dependent FKs. Ignored unattached indexes
and consistently excluded index families remain supported.

The inspector records the attachment metadata before filtering. That metadata is
excluded from serialization and fingerprints. Migration generation retains its
existing ignore behavior.

Validation

Passed locally with Go 1.24.7 on macOS:

go test -count=1 -v ./...
PGSCHEMA_POSTGRES_VERSION=14 go test -count=1 -v ./cmd/dump -run '^TestDumpCommand_(IgnoredPartitionIndexes|PartitionIndexIdentity)$'
go vet ./...
go build -o /tmp/pgschema-partition-review .

The full suite passed on PostgreSQL 18, including the ignore tests. The partition-index regression passed all eight combinations of single/multi-file output, --no-comments, and --qualify-schema on PostgreSQL 14 and 18.

The regression compares replayed index and FK catalogs with an independently created source and requires an empty plan against that source. It covers custom names, two partition levels, unattached equivalent indexes, incomplete and invalid parents, an empty partitioned table, unique and partial indexes, expressions, operator classes, included columns, quoted names, comments, and deterministic output.

The ignored-index regression passed nine scenarios in both output modes, with and without existing output files, on PostgreSQL 14 and 18. It verifies actionable errors, no partial output, unchanged existing files, and successful replay for allowed configurations. Unit tests also verify schema-qualified dependency matching, unchanged migration generation, and exclusion of inspection metadata from serialization.

Formatting and whitespace checks also pass. The SQL in the query binding matches the source query. Upstream Linux CI remains pending.

Scope

The explicit attachment strategy applies to standalone indexes in the managed schema. Constraint-backed primary, unique, and exclusion indexes remain under existing constraint management. This does not change migration-time index reconciliation or add cross-schema partition reconstruction support.

AI assistance

AI assisted with implementation, test preparation, and source review. The validation results above are from executed tests.

Recursive parent-index creation generated default-named child indexes before dumps recreated explicitly named indexes. Replay therefore added redundant indexes, lost original attachments, and could change parent validity.

Record parent-index identity in the IR. During export, create parent indexes with ON ONLY after tables and leaf indexes, then attach only the recorded children. Keep parent-index statements in ordered multi-file index files and defer foreign keys that require partitioned unique indexes. Leave migration index generation unchanged.

Add single-file and multi-file replay regressions that compare index catalogs and plans against independently created sources. Cover custom names, unattached equivalent indexes, invalid parents, uniqueness, operator classes, formatting flags, and deterministic output. Validate on PostgreSQL 14 and 18 and retain the existing FK and dependency-order tests.
@greptile-apps

greptile-apps Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

[Medium impact] Adds partition index attachment tracking to schema dumps.

Fix the ignored-child case before merging, because it can produce a dump that cannot be loaded.

Findings

  1. P1 Ignored indexes break dump replay ▶

Summary

The PR gives dumps a separate path that preserves standalone partition-index names and attachments.

  • Database dumps keep partition index names and attachments.
  • Intermediate partitions keep their own partition clause.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Inspect selected schema] --> B[Create tables and leaf indexes]
  B --> C[Create nested parent indexes with ON ONLY]
  C --> D[Attach recorded child indexes]
  D --> E[Create root indexes and attach children]
  E --> F[Add deferred foreign keys]
  F --> G[Write one SQL file or ordered includes]
Loading

Reviews (1) · Last reviewed commit: "fix(diff): preserve partition keys on ne..." · Reviewed by Greptile

Comment thread internal/diff/partition_index.go
Reuse explicit index creation and attachment ordering for complete new partition hierarchies. Preserve recursive migration behavior for existing or filtered hierarchies and defer dependent foreign keys until unique indexes are ready.

Cover native apply, direct SQL replay, incremental migrations, filtering, and transactional attachment failure with independent synthetic fixtures.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Attachment discovery misses constraint-backed child indexes, potentially producing invalid parents and failing dependent foreign keys.

1 open finding
What changed in this PR

Preserves partition-index names and attachment relationships in dumps and complete new-hierarchy migrations.

Changes:

  • Inspects index attachment metadata and generates explicit ON ONLY indexes and attachments.
  • Preserves nested partition clauses and defers dependent foreign keys.
  • Adds dump, migration, ignore, formatting, and documentation coverage.
File Description
testdata/​diff/​create_table/​nested_partitions/​plan.txt Adds expected text plan.
testdata/​diff/​create_table/​nested_partitions/​plan.sql Adds expected plan SQL.
testdata/​diff/​create_table/​nested_partitions/​plan.json Adds expected JSON plan.
testdata/​diff/​create_table/​nested_partitions/​old.sql Defines empty initial schema.
testdata/​diff/​create_table/​nested_partitions/​new.sql Defines nested partitions.
testdata/​diff/​create_table/​nested_partitions/​diff.sql Verifies nested partition DDL.
ir/​queries/​queries.sql.go Updates generated index-query binding.
ir/​queries/​queries.sql Reads index-parent relationships.
ir/​ir.go Adds partition attachment metadata.
ir/​inspector.go Populates attachment and hierarchy metadata.
ir/​index_test.go Tests transient metadata serialization.
internal/​dump/​formatter.go Adds numbered partition-index files.
internal/​diff/​table.go Preserves nested partitioning and defers FKs.
internal/​diff/​qualify_schema_test.go Updates generator calls.
internal/​diff/​partition_index.go Plans explicit index creation and attachment.
internal/​diff/​partition_index_test.go Tests ordering, filtering, and eligibility.
internal/​diff/​index.go Supports CREATE INDEX ... ON ONLY.
internal/​diff/​diff.go Adds dump-specific generation flow.
docs/​cli/​plan.mdx Documents new-hierarchy migration behavior.
docs/​cli/​ignore.mdx Documents attachment dependency errors.
docs/​cli/​dump.mdx Documents explicit attachment output.
cmd/​plan/​plan.go Normalizes attachment schema metadata.
cmd/​plan/​plan_test.go Tests attachment normalization.
cmd/​partition_index_migration_test.go Adds plan/apply regressions.
cmd/​dump/​partition_index_integration_test.go Tests dump identity and replay.
cmd/​dump/​partition_index_ignore_test.go Tests ignored dependency handling.
cmd/​dump/​dump.go Uses partition-aware dump generation.
Files not reviewed (1)
  • ir/queries/queries.sql.go: Generated file

🧠 Review effort: Balanced


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread ir/queries/queries.sql
Read catalog attachment edges independently of standalone-index filtering. Keep backing indexes under constraint generation while including them in explicit attachment scheduling and dependency validation.

Reject excluded descendants beneath attached constraint-owned indexes before writing dump output. Cover unique and primary-key children, nested constraints, native migrations, and both dump formats with synthetic regressions.
@zikolach

Copy link
Copy Markdown
Contributor Author

@tianzhou I addressed the feedback and tested changes on my use case for which this fix was made. I would appreciate if you can have another look and verify the change. Please let me know if I can help?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Attachment steps are rendered as concurrent index rebuilds in plan output.

1 open finding
1 resolved since last review
Files not reviewed (1)
  • ir/queries/queries.sql.go: Generated file

🧠 Review effort: Balanced

Comment thread internal/diff/partition_index.go
@zikolach

Copy link
Copy Markdown
Contributor Author

The attachment-rendering fix is in 83da9e1, and CI passed. I replied to and resolved the corresponding review thread. GitHub denied my request for another Copilot review because my account lacks permission to request reviewers. Could a maintainer request a fresh Copilot review of the latest commit?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

It changes catalog inspection and dependency-sensitive DDL ordering across dump and migration paths, warranting final human review despite extensive coverage.

0 open findings

1 resolved since last review
Files not reviewed (1)
  • ir/queries/queries.sql.go: Generated file

🧠 Review effort: Balanced

@tianzhou tianzhou left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. Thanks for the contribution

@tianzhou
tianzhou merged commit bdaff36 into pgplex:main Oct 14, 2026
1 check passed
@zikolach

Copy link
Copy Markdown
Contributor Author

Following up on this review, I opened #645 because this PR is already merged. Further testing found that PostgreSQL can give an inherited index a name needed by another index, causing dump replay to fail. The follow-up restores the original names before creating the other indexes.

tianzhou added a commit that referenced this pull request Oct 15, 2026
…#658)

* docs: simplify the partitioned-index notes for dump, ignore, and plan

The notes added in #643 ran to several paragraphs each, with worked
examples and repeated explanations of constraint-backed indexes. Trim
each to a short paragraph in the same style as the circular-foreign-key
note from #642, keeping the behavior a reader needs: creation order,
the indexes/ output directory, the dump error for ignored attached
children, and when plan falls back to recursive index creation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs: keep the standalone-index scope in the plan partitioned-index note

The explicit ON ONLY plus ATTACH PARTITION schedule only covers standalone
indexes; indexes backing PRIMARY KEY and UNIQUE constraints are still
created through their constraints. Restore the qualifier and the caveat
that the simplification dropped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
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.

3 participants