fix: only collapse genuinely owned sequences into SERIAL (#573) - #577
Conversation
pgschema treated any integer column with a nextval() default as a SERIAL column. The inspector query also synthesized sequence "ownership" from any column default that referenced the sequence. Together these caused a sequence that is merely referenced by a column (a shared Hibernate-style global sequence, or a custom-named sequence) to vanish from dumps and plans, while the column was rendered as SERIAL. On apply, PostgreSQL then created <table>_<column>_seq and rewired the default to it. - Derive ownership only from pg_depend; drop the nextval() fallback join. - Add Column.IsSerial, set during normalization only when the default's sequence is owned by the column and carries PostgreSQL's default <table>_<column>_seq name. Everything else keeps its explicit DEFAULT and an explicit CREATE SEQUENCE. - Emit OWNED BY via a deferred ALTER SEQUENCE when the owning column is created in the same migration, mirroring pg_dump's ordering. Fixes #573, #574, #576 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Greptile SummaryThis PR narrows SERIAL recognition to conventionally named, column-owned sequences and preserves shared or custom sequence defaults explicitly.
Confidence Score: 3/5This PR should not merge until explicit sequence ownership is ordered after added columns and structural sequence changes survive transitions away from SERIAL. An owned sequence targeting a new column on an existing table is attached before that column exists, and sequence parameter changes are silently dropped when only one side of a transition is classified as SERIAL. Files Needing Attention: internal/diff/diff.go Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart LR
Inspect[Inspect sequences and pg_depend ownership] --> Normalize[Normalize columns and mark genuine SERIAL]
Normalize --> Compare[Compare old and desired IR]
Compare --> Serial{Is implicit SERIAL?}
Serial -->|Yes| TableDDL[Create table with SERIAL]
Serial -->|No| SeqDDL[Create explicit sequence]
SeqDDL --> ColumnDDL[Create or alter owning column]
ColumnDDL --> Ownership[ALTER SEQUENCE OWNED BY]
|
There was a problem hiding this comment.
🟡 Changes recommended
The new deferred ALTER SEQUENCE ... OWNED BY step is currently labeled as a “create” operation in diff metadata, which makes plan summaries/JSON misleading and should be corrected before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Fixes sequence/serial handling so pgschema only collapses genuine SERIAL-backed sequences (owned and default <table>_<column>_seq naming), preventing shared or custom-named sequences from disappearing in dumps/plans and avoiding non-idempotent apply behavior (addresses #573, #574, #576).
Changes:
- Tightened sequence ownership detection to rely solely on
pg_depend(removed “infer ownership from column default nextval()” fallback). - Added IR-level
Column.IsSerialderived during normalization using PostgresmakeObjectNametruncation logic andnextval()parsing. - Updated diff generation to skip/create sequences based on
IsSerial, and to deferOWNED BYto anALTER SEQUENCE ... OWNED BYwhen the owning column is created later in the same migration.
File summaries
| File | Description |
|---|---|
| testdata/dump/sakila/pgschema.sql | Updates fixture to emit explicit sequences and explicit DEFAULT nextval(...) for non-owned sequences instead of rendering as SERIAL. |
| testdata/diff/create_sequence/issue_573_shared_sequence_default/new.sql | New regression case covering shared sequence defaults, custom-named owned sequence, and genuine serial. |
| testdata/diff/create_sequence/issue_573_shared_sequence_default/diff.sql | Expected DDL now includes explicit CREATE SEQUENCE and deferred ALTER SEQUENCE ... OWNED BY. |
| testdata/diff/create_sequence/issue_573_shared_sequence_default/plan.sql | Expected plan SQL for the new regression case. |
| testdata/diff/create_sequence/issue_573_shared_sequence_default/plan.txt | Expected human plan output for the new regression case. |
| testdata/diff/create_sequence/issue_573_shared_sequence_default/plan.json | Expected JSON plan output for the new regression case. |
| testdata/diff/create_sequence/issue_573_shared_sequence_default/old.sql | Empty baseline for the new regression case. |
| ir/queries/queries.sql | Removes ownership inference from information_schema.columns and documents pg_depend-only ownership. |
| ir/queries/queries.sql.go | Regenerated sqlc output reflecting the updated sequences query. |
| ir/ir.go | Adds non-serialized Column.IsSerial flag to IR for accurate SERIAL rendering decisions. |
| ir/normalize.go | Adds markSerialColumns, serialSequenceName, and nextvalSequenceName to compute IsSerial. |
| ir/normalize_test.go | Adds unit tests for serial sequence naming, nextval parsing, and serial marking behavior. |
| internal/diff/table.go | Switches SERIAL detection to rely on Column.IsSerial instead of “integer + contains nextval”. |
| internal/diff/diff.go | Changes sequence skip logic to key off serial-ness; adds deferred sequence ownership emission. |
| internal/diff/sequence.go | Adds deferred OWNED BY handling and makes create SQL optionally omit inline ownership. |
| internal/diff/qualify_schema_test.go | Updates tests for new generateSequenceSQL signature. |
| internal/diff/identifier_quote_test.go | Updates tests for new generateSequenceSQL signature. |
Review details
Files not reviewed (1)
- ir/queries/queries.sql.go: Generated file
- Files reviewed: 15/17 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Skip sequences owned by an ignored table in the inspector, so ignore tests no longer see their sequences surface once ownership is no longer the trigger for hiding them. - Skip an added sequence whose owner column is absent from the desired state (ignored or in another schema); it cannot be created with its OWNED BY anyway. This matches the previous behavior for such sequences. - Emit the deferred ALTER SEQUENCE ... OWNED BY in the modify phase, after ALTER TABLE ... ADD COLUMN, so it also works when the owning column is added to an existing table (review finding). The issue_573 fixture now covers that scenario. - Count multi-step object additions once in the plan summary (CREATE SEQUENCE plus deferred OWNED BY), keyed on the source object so overloaded functions sharing a path stay separate (review finding). - Replace the query test that asserted ownership inferred from column defaults with one asserting pg_depend-only ownership. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Addressed the review feedback and the CI failures in 5626b5c:
|
There was a problem hiding this comment.
🔵 Needs a closer look
It changes core schema introspection, IR normalization, and migration ordering behavior around sequences/SERIAL across multiple subsystems and warrants final human validation across supported PostgreSQL versions and fixtures.
Review details
Files not reviewed (1)
- ir/queries/queries.sql.go: Generated file
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
ir/normalize.go:41
- The comment says sequences are normalized before
markSerialColumns(schema)runs, butnormalizeSchemadoesn't normalize sequences (only tables/functions/etc). This can confuse future readers about ordering assumptions; the call only needs normalized column types/defaults plus the already-inspected sequence metadata.
- Files reviewed: 19/20 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🟢 Approval recommended
The changes align with the stated issues and are backed by targeted regression fixtures plus unit tests, and the updated diff/planning behavior appears consistent and internally coherent.
Review details
Files not reviewed (1)
- ir/queries/queries.sql.go: Generated file
- Files reviewed: 19/20 changed files
- Comments generated: 0 new
- Review effort level: Lite
Summary
pgschema treated any integer column with a
nextval()default as a SERIAL column, and the sequence inspector query synthesized "ownership" for any sequence merely referenced by a column default. As a result a shared sequence (Hibernate-stylensl_global_seqused by many tables) or a custom-named sequence disappeared from dumps and plans while the column was rendered asSERIAL/BIGSERIAL. On apply PostgreSQL created<table>_<column>_seqand rewired the default to it, so plans were wrong and not idempotent.Changes:
pg_depend(SERIAL /OWNED BY/ identity). The fallback join that inferred ownership fromcolumn_default LIKE '%nextval%'is removed.Column.IsSerial, set during normalization only when the default's sequence is owned by the column and has PostgreSQL's default<table>_<column>_seqname (withmakeObjectNametruncation). Only such columns render as SERIAL; everything else keeps its explicitDEFAULT nextval(...)and gets an explicitCREATE SEQUENCE.IsSerialinstead of raw ownership. For an explicitly created sequence whose owning column is created in the same migration,OWNED BYis deferred to anALTER SEQUENCE ... OWNED BYafter the tables, matching pg_dump ordering.The Sakila dump fixture changes because that schema declares its sequences explicitly without
OWNED BY; they are now dumped explicitly, which is the faithful representation (SERIAL would have made them owned on re-apply).Fixes #573
Fixes #574
Fixes #576
Test plan
testdata/diff/create_sequence/issue_573_shared_sequence_defaultcovering a shared sequence used by several tables, a custom-named owned sequence, and a genuinebigserialfor contrast. Passes plan, apply, and the second-plan idempotency check.serialSequenceName,nextvalSequenceName, andmarkSerialColumns.All of the above pass locally.
🤖 Generated with Claude Code