Skip to content

Commit 4635e2f

Browse files
committed
SMOODEV-045397: integrate differ promotions + docs
1 parent 7607e2c commit 4635e2f

5 files changed

Lines changed: 153 additions & 99 deletions

File tree

CHANGELOG.md

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ follows [Keep a Changelog](https://keepachangelog.com/); the project adheres to
4949
rename detection vs. drop+add). Covers tables, columns, checks, enums, generated
5050
& identity columns, RLS policies, roles, sequences, and views.
5151
- **Differ conformance corpus** (`tests/differ_corpus.rs`): a conformance corpus
52-
of Postgres schema-diff scenarios — 258 cases, 198 asserted
53-
(snapshot-in → expected-DDL-out under normalized comparison), 60 tracked as
54-
`Skip` for features outside the snapshot IR (see ROADMAP follow-ups).
52+
of Postgres schema-diff scenarios — 258 cases, 249 asserted
53+
(snapshot-in → expected-DDL-out under normalized comparison), 9 tracked as
54+
`Skip` for behaviour outside the snapshot IR (see ROADMAP follow-ups).
5555
- **Deferred-corpus promotion** (phase 2): the previously-`Skip`'d differ
5656
categories are now asserted end-to-end —
5757
- **Views**: `WITH (...)` storage options, materialized-view `TABLESPACE`,
@@ -75,6 +75,12 @@ follows [Keep a Changelog](https://keepachangelog.com/); the project adheres to
7575
`SchemaSnapshotBuilder::ind_policy`, `DdlStatement::{Create,Drop,Alter,Rename}IndPolicy`,
7676
an `ind_policy:` rename-hint tag, and dedicated `Plan` buckets ordered
7777
alongside their table-policy counterparts.
78+
- **Deferred-corpus promotion** (phase 3): the `tables` and `columns` categories
79+
are now asserted end-to-end against rendered SQL (26 cases flipped
80+
`Skip``Supported`) — `CREATE`/`DROP TABLE`, `ALTER TABLE … RENAME`,
81+
`ALTER TABLE … SET SCHEMA`, multi-table creates, composite-PK add/rename
82+
(breakpoint-delimited DROP+ADD), `ADD COLUMN`, `RENAME COLUMN`, and column-level
83+
composite-PK changes. The corpus is now 249 asserted / 9 `Skip`.
7884
- **Migrations** (`feature = "migrate"`): forward-only `run_migrations` over a
7985
`*.sql` directory with a `__pg_migrations` bookkeeping table (idempotent re-runs),
8086
`split_sql_statements` (`--> statement-breakpoint` aware), and
@@ -106,18 +112,17 @@ follows [Keep a Changelog](https://keepachangelog.com/); the project adheres to
106112
and the journal types `MigrationJournal` / `MigrationJournalEntry`. The on-disk
107113
layout is unchanged (`meta/_journal.json`, `--> statement-breakpoint` markers).
108114

109-
### Follow-ups (tracked as corpus `Skip`s, deferred to later phases)
115+
### Follow-ups (the remaining 9 corpus `Skip`s, in 3 clusters)
110116

111-
- **Columns category** (the bulk of the remaining `Skip`s): column add / default
112-
add / data-type change cases — including every enum↔standard and enum↔enum
113-
data-type-change variant — are deferred to a dedicated columns-promotion pass.
114-
- Multi-construct **ordering** mismatches vs. the corpus's insertion order:
115-
FK/index emission for multi-table creates (declaration order vs.
116-
`BTreeMap`-sorted), composite-PK DROP+ADD joined into one breakpoint-delimited
117-
string, and multi-policy creation order (BTreeMap name-sorted vs. insertion order).
118-
- **Tables/schema** "statements-only encoding" goldens (add/drop/move table,
119-
multiproject schema) whose corpus scenarios assert the structured statement
120-
encoding only, not rendered SQL.
121-
- Enum **schema rename** (`ALTER SCHEMA`) — schemas are not modeled as renameable
122-
IR entities.
123-
- Error-case fixtures (duplicate view / constraint names that Postgres rejects).
117+
- **Schema-level renames** (`ALTER SCHEMA … RENAME TO`) — 3 cases (`change schema
118+
with tables #1`, `change table schema #6`, `enums #5`). Schemas are not modeled
119+
as renameable IR entities, so a schema rename degrades to a data-losing
120+
drop+create; we deliberately do not bless that as supported output.
121+
- **Enum quoting in column ops** — 3 cases (`enums #20`, `enums #21`, `column is
122+
enum type … add default`). `ADD COLUMN` emits a bare enum type name
123+
(`my_enum` / `my_enum[]`) instead of the quoted `"my_enum"`, and `SET DEFAULT`
124+
emits a bare enum literal (`value3`) instead of `'value3'`. Needs the lowering
125+
to resolve user-defined types/defaults against the enum registry.
126+
- **Error-case fixtures** — 3 cases (`create checks with same names`, plus the
127+
duplicate view / materialized-view name cases). Postgres rejects these; the
128+
differ does not yet model rejection, so there is no SQL contract to assert.

ROADMAP.md

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,18 @@ public crate carries no SmooAI specifics.
2626
enums, RLS policies, roles, sequences, and views. _Cargo feature: `differ`
2727
(default)._
2828
- ✅ Conformance corpus of Postgres schema-diff scenarios
29-
(snapshot-in → expected-DDL-out): **258 cases, 198 asserted, 60
30-
tracked `Skip`** (deferred categories below).
29+
(snapshot-in → expected-DDL-out): **258 cases, 249 asserted, 9
30+
tracked `Skip`** (remaining clusters below).
3131
-**Phase-2 deferred-corpus promotion**: views (`WITH` options / `TABLESPACE`
3232
/ `USING` / `SET SCHEMA` / `.existing()` reference / DROP-before-CREATE
3333
recreate), enum recreate cascade, sequences, identity (custom sequence name +
3434
`START WITH``MINVALUE` fallback), FK alter (DROP+ADD), index drop
3535
(`public`-implicit), and independent (schema-level) policies (`SnapIndPolicy`).
36+
-**Phase-3 deferred-corpus promotion**: the **`tables`** and **`columns`**
37+
categories now assert rendered SQL end-to-end (26 cases `Skip``Supported`) —
38+
`CREATE`/`DROP TABLE`, `ALTER TABLE … RENAME` / `SET SCHEMA`, multi-table
39+
creates, composite-PK add/rename (breakpoint-delimited DROP+ADD), `ADD COLUMN`,
40+
`RENAME COLUMN`, and column-level composite-PK changes.
3641
-**Non-`public` schema support**: `CREATE SCHEMA` / `DROP SCHEMA` generation
3742
(`SchemaSnapshot.schemas`, ordered first/last), the schema-qualify fix
3843
(`to_create_table_sql` + all emitters share `qualify_relation`: `public`
@@ -67,23 +72,22 @@ public crate carries no SmooAI specifics.
6772
plus TS types + Zod (`emit_ts_module`, a `createSelectSchema` replacement) for
6873
polyglot consumers.
6974

70-
## Deferred — differ corpus `Skip`s (next promotion passes)
75+
## Deferred — the 9 remaining differ corpus `Skip`s
7176

72-
Tracked by the 60 remaining `Skip` cases in `tests/differ_corpus.rs`:
77+
Three clusters in `tests/differ_corpus.rs` (3 cases each):
7378

74-
- **Columns category** (the largest remaining cluster): column add / default add /
75-
data-type change — including every enum↔standard and enum↔enum data-type-change
76-
variant — deferred to a dedicated columns-promotion pass.
77-
- Multi-construct **ordering** mismatches vs the corpus's insertion order:
78-
multi-table-create FK/index emission (declaration order vs `BTreeMap`-sorted),
79-
composite-PK DROP+ADD joined into one breakpoint-delimited string, and multi-policy
80-
creation order (name-sorted vs insertion order).
81-
- **Tables/schema** "statements-only encoding" goldens (add/drop/move table,
82-
multiproject schema) whose scenarios assert the structured statement encoding,
83-
not rendered SQL.
84-
- Enum **schema rename** (`ALTER SCHEMA`) — schemas not modeled as renameable IR
85-
entities.
86-
- Error-case fixtures (duplicate view / constraint names Postgres rejects).
79+
- **Schema-level renames** (`ALTER SCHEMA … RENAME TO`) — schemas are not modeled
80+
as renameable IR entities, so a schema rename degrades to a data-losing
81+
drop+create; not blessed as supported output. (`change schema with tables #1`,
82+
`change table schema #6`, `enums #5`.)
83+
- **Enum quoting in column ops**`ADD COLUMN` emits a bare enum type name
84+
(`my_enum` / `my_enum[]`) instead of `"my_enum"`, and `SET DEFAULT` emits a bare
85+
enum literal (`value3`) instead of `'value3'`; needs the lowering to resolve
86+
user-defined types/defaults against the enum registry. (`enums #20`, `enums #21`,
87+
`column is enum type … add default`.)
88+
- **Error-case fixtures** — duplicate check-constraint / view / materialized-view
89+
names that Postgres rejects; the differ does not yet model rejection, so there is
90+
no SQL contract to assert.
8791

8892
## Non-goals (for now)
8993

crates/postgres-kit/tests/corpus/columns.rs

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@
55
//! rename hints into [`DiffCase::renames`], and the asserted statement output into
66
//! [`DiffCase::expected_sql`].
77
//!
8-
//! Some scenarios in this file assert only a structured statement encoding (never
9-
//! the rendered SQL), so they are recorded as [`Status::Skip`] with reason
10-
//! `"statements-only encoding"` — the schemas are still translated faithfully so
11-
//! the differ agent can promote them later by supplying the expected SQL.
8+
//! Every scenario in this file asserts the rendered SQL the differ emits for the
9+
//! `from`/`to` pair under normalized comparison.
1210
1311
use postgres_kit::differ::ir::{
1412
SchemaSnapshot, SnapColumn, SnapCompositePk, SnapForeignKey, SnapTable,
@@ -34,8 +32,8 @@ pub fn cases() -> Vec<DiffCase> {
3432
)
3533
.build(),
3634
renames: &[],
37-
expected_sql: &[],
38-
status: Status::Skip("statements-only encoding"),
35+
expected_sql: &["ALTER TABLE \"users\" ADD COLUMN \"name\" text;"],
36+
status: Status::Supported,
3937
},
4038
// ---- add columns #2 ----
4139
// schema1: users { id serial primaryKey }
@@ -54,8 +52,11 @@ pub fn cases() -> Vec<DiffCase> {
5452
)
5553
.build(),
5654
renames: &[],
57-
expected_sql: &[],
58-
status: Status::Skip("statements-only encoding"),
55+
expected_sql: &[
56+
"ALTER TABLE \"users\" ADD COLUMN \"email\" text;",
57+
"ALTER TABLE \"users\" ADD COLUMN \"name\" text;",
58+
],
59+
status: Status::Supported,
5960
},
6061
// ---- alter column change name #1 ----
6162
// schema1: users { id serial primaryKey, name text('name') }
@@ -77,8 +78,8 @@ pub fn cases() -> Vec<DiffCase> {
7778
)
7879
.build(),
7980
renames: &["public.users.name->public.users.name1"],
80-
expected_sql: &[],
81-
status: Status::Skip("statements-only encoding"),
81+
expected_sql: &["ALTER TABLE \"users\" RENAME COLUMN \"name\" TO \"name1\";"],
82+
status: Status::Supported,
8283
},
8384
// ---- alter column change name #2 ----
8485
// schema1: users { id serial primaryKey, name text('name') }
@@ -101,8 +102,11 @@ pub fn cases() -> Vec<DiffCase> {
101102
)
102103
.build(),
103104
renames: &["public.users.name->public.users.name1"],
104-
expected_sql: &[],
105-
status: Status::Skip("statements-only encoding"),
105+
expected_sql: &[
106+
"ALTER TABLE \"users\" RENAME COLUMN \"name\" TO \"name1\";",
107+
"ALTER TABLE \"users\" ADD COLUMN \"email\" text;",
108+
],
109+
status: Status::Supported,
106110
},
107111
// ---- alter table add composite pk ----
108112
// schema1: table { id1 integer, id2 integer }
@@ -145,8 +149,11 @@ pub fn cases() -> Vec<DiffCase> {
145149
"public.users->public.users1",
146150
"public.users1.id->public.users1.id1",
147151
],
148-
expected_sql: &[],
149-
status: Status::Skip("statements-only encoding"),
152+
expected_sql: &[
153+
"ALTER TABLE \"users\" RENAME TO \"users1\";",
154+
"ALTER TABLE \"users1\" RENAME COLUMN \"id\" TO \"id1\";",
155+
],
156+
status: Status::Supported,
150157
},
151158
// ---- with composite pks #1 ----
152159
// schema1: users { id1, id2, pk(id1,id2 name=compositePK) }
@@ -171,8 +178,8 @@ pub fn cases() -> Vec<DiffCase> {
171178
)
172179
.build(),
173180
renames: &[],
174-
expected_sql: &[],
175-
status: Status::Skip("statements-only encoding"),
181+
expected_sql: &["ALTER TABLE \"users\" ADD COLUMN \"text\" text;"],
182+
status: Status::Supported,
176183
},
177184
// ---- with composite pks #2 ----
178185
// schema1: users { id1, id2 }
@@ -195,8 +202,10 @@ pub fn cases() -> Vec<DiffCase> {
195202
)
196203
.build(),
197204
renames: &[],
198-
expected_sql: &[],
199-
status: Status::Skip("statements-only encoding"),
205+
expected_sql: &[
206+
"ALTER TABLE \"users\" ADD CONSTRAINT \"compositePK\" PRIMARY KEY(\"id1\",\"id2\");",
207+
],
208+
status: Status::Supported,
200209
},
201210
// ---- with composite pks #3 ----
202211
// schema1: users { id1, id2, pk(id1,id2 name=compositePK) }
@@ -220,8 +229,11 @@ pub fn cases() -> Vec<DiffCase> {
220229
)
221230
.build(),
222231
renames: &["public.users.id2->public.users.id3"],
223-
expected_sql: &[],
224-
status: Status::Skip("statements-only encoding"),
232+
expected_sql: &[
233+
"ALTER TABLE \"users\" RENAME COLUMN \"id2\" TO \"id3\";",
234+
"ALTER TABLE \"users\" DROP CONSTRAINT \"compositePK\";\n--> statement-breakpoint\nALTER TABLE \"users\" ADD CONSTRAINT \"compositePK\" PRIMARY KEY(\"id1\",\"id3\");",
235+
],
236+
status: Status::Supported,
225237
},
226238
// ---- add multiple constraints #1 ----
227239
// Adds onDelete actions to three FKs across three referenced tables.

crates/postgres-kit/tests/corpus/enums.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,9 @@ pub fn cases() -> Vec<DiffCase> {
273273
"ALTER TABLE \"table\" ADD COLUMN \"col1\" \"my_enum\";",
274274
"ALTER TABLE \"table\" ADD COLUMN \"col2\" integer;",
275275
],
276-
status: Status::Skip("column add — columns category"),
276+
status: Status::Skip(
277+
"follow-up: ADD COLUMN does not quote enum type names (emits bare my_enum)",
278+
),
277279
},
278280
// enums #21 — add array columns (enum array + integer array) to a table.
279281
DiffCase {
@@ -296,7 +298,9 @@ pub fn cases() -> Vec<DiffCase> {
296298
"ALTER TABLE \"table\" ADD COLUMN \"col1\" \"my_enum\"[];",
297299
"ALTER TABLE \"table\" ADD COLUMN \"col2\" integer[];",
298300
],
299-
status: Status::Skip("array column add — columns category"),
301+
status: Status::Skip(
302+
"follow-up: ADD COLUMN does not quote enum array type names (emits bare my_enum[])",
303+
),
300304
},
301305
// drop enum value — value removal recreates the type (no dependent columns).
302306
DiffCase {
@@ -528,7 +532,9 @@ pub fn cases() -> Vec<DiffCase> {
528532
to: enum_table("enum", &["value1", "value3"], "table", "column", "enum", Some("value3")),
529533
renames: &[],
530534
expected_sql: &["ALTER TABLE \"table\" ALTER COLUMN \"column\" SET DEFAULT 'value3';"],
531-
status: Status::Skip("column default add — columns category"),
535+
status: Status::Skip(
536+
"follow-up: SET DEFAULT does not quote enum literal (emits bare value3)",
537+
),
532538
},
533539
// change data type from standart type to enum.
534540
DiffCase {

0 commit comments

Comments
 (0)