Skip to main content

pgevolve_core/plan/
raw_step.rs

1//! [`RawStep`] — the smallest unit of work the executor will attempt.
2//!
3//! After the rewrite pass, every step's SQL is fixed: the executor performs
4//! no further transformation. `intent_id` is populated later, in the plan
5//! serializer, once destructive intents have been collated.
6
7use serde::{Deserialize, Serialize};
8
9use crate::identifier::QualifiedName;
10
11/// Whether a step can run inside a `BEGIN; ... COMMIT;` block.
12///
13/// Used by [`group_steps`](super::grouping::group_steps) to partition the
14/// step list into transactional vs. non-transactional groups. `CONCURRENTLY`
15/// index ops are the typical [`OutsideTransaction`](Self::OutsideTransaction)
16/// case in v0.1.
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
18#[serde(rename_all = "snake_case")]
19pub enum TransactionConstraint {
20    /// May execute inside a `BEGIN ... COMMIT`.
21    InTransaction,
22    /// Must execute outside a transaction (e.g., `CREATE INDEX CONCURRENTLY`).
23    OutsideTransaction,
24}
25
26/// What kind of operation a [`RawStep`] performs.
27///
28/// Serialized via `serde` as the `kind=` value in the plan's
29/// `-- @pgevolve step ...` directive comments (spec §7.1). The
30/// `snake_case` rename keeps the on-disk form stable across renames here.
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
32#[serde(rename_all = "snake_case")]
33pub enum StepKind {
34    /// `CREATE SCHEMA`.
35    CreateSchema,
36    /// `DROP SCHEMA`.
37    DropSchema,
38    /// `COMMENT ON SCHEMA`.
39    AlterSchemaComment,
40
41    /// `CREATE TABLE`.
42    CreateTable,
43    /// `DROP TABLE`.
44    DropTable,
45    /// `COMMENT ON TABLE`.
46    AlterTableSetComment,
47
48    /// `ALTER TABLE ... ADD COLUMN`.
49    AddColumn,
50    /// `ALTER TABLE ... DROP COLUMN`.
51    DropColumn,
52    /// `ALTER TABLE ... ALTER COLUMN ... TYPE`.
53    AlterColumnType,
54    /// `ALTER TABLE ... ALTER COLUMN ... SET/DROP NOT NULL`.
55    SetColumnNullable,
56    /// `ALTER TABLE ... ALTER COLUMN ... SET/DROP DEFAULT`.
57    SetColumnDefault,
58    /// `COMMENT ON COLUMN`.
59    SetColumnComment,
60    /// `ALTER TABLE ... ALTER COLUMN ... ADD/DROP IDENTITY`.
61    SetColumnIdentity,
62    /// `ALTER TABLE ... ALTER COLUMN ... SET/DROP EXPRESSION`.
63    SetColumnGenerated,
64    /// `ALTER TABLE ... ALTER COLUMN ... SET STORAGE`.
65    SetColumnStorage,
66    /// `ALTER TABLE ... ALTER COLUMN ... SET COMPRESSION`.
67    SetColumnCompression,
68
69    /// `ALTER TABLE ... ADD CONSTRAINT` (validated immediately).
70    AddConstraint,
71    /// `ALTER TABLE ... ADD CONSTRAINT ... NOT VALID`.
72    AddConstraintNotValid,
73    /// `ALTER TABLE ... VALIDATE CONSTRAINT`.
74    ValidateConstraint,
75    /// `ALTER TABLE ... DROP CONSTRAINT`.
76    DropConstraint,
77    /// `COMMENT ON CONSTRAINT`.
78    SetConstraintComment,
79
80    /// `CREATE INDEX`.
81    CreateIndex,
82    /// `CREATE INDEX CONCURRENTLY`.
83    CreateIndexConcurrent,
84    /// `DROP INDEX`.
85    DropIndex,
86    /// `DROP INDEX CONCURRENTLY`.
87    DropIndexConcurrent,
88
89    /// `CREATE SEQUENCE`.
90    CreateSequence,
91    /// `DROP SEQUENCE`.
92    DropSequence,
93    /// `ALTER SEQUENCE`.
94    AlterSequence,
95
96    /// Intermediate `ADD CONSTRAINT __pgevolve_chk_<col> CHECK (col IS NOT NULL) NOT VALID`
97    /// step in the SET NOT NULL pattern (spec §6.5).
98    AddCheckForNotNull,
99
100    // --- v0.2 view / materialized-view step kinds ---
101    /// `CREATE [OR REPLACE] VIEW`.
102    CreateView,
103    /// `DROP VIEW`.
104    DropView,
105    /// `CREATE MATERIALIZED VIEW ... WITH NO DATA`.
106    CreateMaterializedView,
107    /// `DROP MATERIALIZED VIEW`.
108    DropMaterializedView,
109    /// `REFRESH MATERIALIZED VIEW [CONCURRENTLY]`.
110    RefreshMaterializedView,
111    /// `ALTER VIEW ... SET (...)`.
112    AlterViewSetReloption,
113    /// `COMMENT ON VIEW / MATERIALIZED VIEW / COLUMN` for views and MVs.
114    CommentOnView,
115
116    // --- v0.2 user-defined type step kinds ---
117    /// `CREATE TYPE` (enum, domain, or composite).
118    CreateType,
119    /// `DROP TYPE`.
120    DropType,
121    /// `ALTER TYPE … ADD VALUE`.
122    AlterTypeAddValue,
123    /// `ALTER TYPE … RENAME VALUE`.
124    AlterTypeRenameValue,
125    /// `ALTER DOMAIN … ADD CONSTRAINT … CHECK (…)`.
126    AlterDomainAddConstraint,
127    /// `ALTER DOMAIN … DROP CONSTRAINT`.
128    AlterDomainDropConstraint,
129    /// `ALTER DOMAIN … SET DEFAULT` / `DROP DEFAULT`.
130    AlterDomainSetDefault,
131    /// `ALTER DOMAIN … SET NOT NULL` / `DROP NOT NULL`.
132    AlterDomainSetNotNull,
133    /// `ALTER TYPE … ADD ATTRIBUTE`.
134    AlterTypeAddAttribute,
135    /// `ALTER TYPE … DROP ATTRIBUTE`.
136    AlterTypeDropAttribute,
137    /// `ALTER TYPE … ALTER ATTRIBUTE … TYPE`.
138    AlterTypeAlterAttributeType,
139    /// `COMMENT ON TYPE` / `COMMENT ON DOMAIN`.
140    CommentOnType,
141
142    // --- v0.2 function / procedure step kinds ---
143    /// `CREATE OR REPLACE FUNCTION`.
144    CreateOrReplaceFunction,
145    /// `DROP FUNCTION`.
146    DropFunction,
147    /// `COMMENT ON FUNCTION`.
148    CommentOnFunction,
149    /// `CREATE OR REPLACE PROCEDURE`.
150    CreateOrReplaceProcedure,
151    /// `DROP PROCEDURE`.
152    DropProcedure,
153    /// `COMMENT ON PROCEDURE`.
154    CommentOnProcedure,
155
156    // --- v0.2 extension step kinds ---
157    /// `CREATE EXTENSION [IF NOT EXISTS] name [WITH SCHEMA s] [VERSION 'v']`.
158    CreateExtension,
159    /// `DROP EXTENSION name CASCADE`. Destructive (intent required).
160    DropExtension,
161    /// `ALTER EXTENSION name UPDATE TO 'v'`.
162    AlterExtensionUpdate,
163    /// `COMMENT ON EXTENSION name IS '...'`.
164    CommentOnExtension,
165
166    // --- v0.2 trigger step kinds ---
167    /// `CREATE [CONSTRAINT] TRIGGER name ... ON table ...`.
168    CreateTrigger,
169    /// `DROP TRIGGER name ON table`.
170    DropTrigger,
171    /// `COMMENT ON TRIGGER name ON table IS '...'`.
172    CommentOnTrigger,
173
174    // --- v0.2 partition step kinds ---
175    /// `ALTER TABLE parent ATTACH PARTITION child FOR VALUES ...`.
176    AttachPartition,
177    /// `ALTER TABLE parent DETACH PARTITION child`.
178    DetachPartition,
179
180    // --- v0.3 cluster / role step kinds ---
181    /// `CREATE ROLE`.
182    CreateRole,
183    /// `DROP ROLE`.
184    DropRole,
185    /// `ALTER ROLE … WITH <options>`.
186    AlterRole,
187    /// `GRANT role TO member`.
188    GrantRoleMembership,
189    /// `REVOKE role FROM member`.
190    RevokeRoleMembership,
191    /// `COMMENT ON ROLE`.
192    CommentOnRole,
193
194    // --- v0.3.1 grant / ownership step kinds ---
195    /// `ALTER <kind> qname OWNER TO new_owner`.
196    AlterObjectOwner,
197    /// `GRANT priv ON <kind> qname TO grantee [WITH GRANT OPTION]`.
198    GrantObjectPrivilege,
199    /// `REVOKE priv ON <kind> qname FROM grantee`.
200    RevokeObjectPrivilege,
201    /// `GRANT priv (col, …) ON TABLE qname TO grantee [WITH GRANT OPTION]`.
202    GrantColumnPrivilege,
203    /// `REVOKE priv (col, …) ON TABLE qname FROM grantee`.
204    RevokeColumnPrivilege,
205    /// `ALTER DEFAULT PRIVILEGES FOR ROLE x [IN SCHEMA y] GRANT/REVOKE priv ON … TO/FROM z`.
206    AlterDefaultPrivileges,
207
208    // --- v0.3.2 row-level security policy step kinds ---
209    /// `CREATE POLICY name ON table …`.
210    CreatePolicy,
211    /// `DROP POLICY name ON table`.
212    DropPolicy,
213    /// `ALTER POLICY name ON table TO … USING (…) WITH CHECK (…)`.
214    AlterPolicy,
215    /// `ALTER TABLE qname { ENABLE | DISABLE } ROW LEVEL SECURITY`.
216    SetTableRowSecurity,
217    /// `ALTER TABLE qname { FORCE | NO FORCE } ROW LEVEL SECURITY`.
218    SetTableForceRowSecurity,
219
220    // --- v0.3.3 reloption step kinds ---
221    /// `ALTER TABLE qname SET (fillfactor = …, …)`.
222    SetTableStorage,
223    /// `ALTER INDEX qname SET (fillfactor = …, …)`.
224    SetIndexStorage,
225    /// `ALTER MATERIALIZED VIEW qname SET (fillfactor = …, …)`.
226    SetMaterializedViewStorage,
227
228    // --- v0.3.4 publication step kinds ---
229    /// `CREATE PUBLICATION …`.
230    CreatePublication,
231    /// `DROP PUBLICATION …`. Destructive (intent required).
232    DropPublication,
233    /// `DROP PUBLICATION old; CREATE PUBLICATION new;` — mode swap. Destructive.
234    ReplacePublication,
235    /// `ALTER PUBLICATION p ADD TABLE x [(cols)] [WHERE (filter)]`.
236    AlterPublicationAddTable,
237    /// `ALTER PUBLICATION p DROP TABLE x`.
238    AlterPublicationDropTable,
239    /// `ALTER PUBLICATION p SET TABLE x (cols) WHERE (filter)`.
240    AlterPublicationSetTable,
241    /// `ALTER PUBLICATION p ADD TABLES IN SCHEMA s` (PG 15+).
242    AlterPublicationAddSchema,
243    /// `ALTER PUBLICATION p DROP TABLES IN SCHEMA s` (PG 15+).
244    AlterPublicationDropSchema,
245    /// `ALTER PUBLICATION p SET (publish = '...')`.
246    AlterPublicationSetPublish,
247    /// `ALTER PUBLICATION p SET (publish_via_partition_root = ...)`.
248    AlterPublicationSetViaRoot,
249    /// `COMMENT ON PUBLICATION p IS '...'`.
250    CommentOnPublication,
251}
252
253/// One unit of work the executor will attempt.
254///
255/// `step_no` and `intent_id` start at zero / `None` and are assigned later
256/// by [`Plan::from_grouped`](crate::plan::Plan::from_grouped). The rewrite
257/// pass (Phase 6) builds steps without that numbering.
258#[derive(Debug, Clone, PartialEq, Eq)]
259pub struct RawStep {
260    /// 1-indexed step number across the whole plan. `0` until assigned by
261    /// `Plan::from_grouped`.
262    pub step_no: u32,
263    /// What kind of operation.
264    pub kind: StepKind,
265    /// Whether the step is destructive (requires explicit intent approval).
266    pub destructive: bool,
267    /// Human-readable reason for destructiveness, if any.
268    pub destructive_reason: Option<String>,
269    /// Intent id assigned by `Plan::from_grouped`; `None` until then.
270    pub intent_id: Option<u32>,
271    /// IR objects this step affects (used by directive comments).
272    pub targets: Vec<QualifiedName>,
273    /// Final SQL emitted to disk.
274    pub sql: String,
275    /// Whether the step can run inside a transaction.
276    pub transactional: TransactionConstraint,
277}
278
279#[cfg(test)]
280mod tests {
281    use super::*;
282
283    #[test]
284    fn step_kind_serializes_as_snake_case() {
285        let s = serde_json::to_string(&StepKind::CreateIndexConcurrent).unwrap();
286        assert_eq!(s, "\"create_index_concurrent\"");
287    }
288
289    #[test]
290    fn step_kind_round_trips_through_serde() {
291        for kind in [
292            StepKind::CreateSchema,
293            StepKind::DropSchema,
294            StepKind::AlterSchemaComment,
295            StepKind::CreateTable,
296            StepKind::DropTable,
297            StepKind::AlterTableSetComment,
298            StepKind::AddColumn,
299            StepKind::DropColumn,
300            StepKind::AlterColumnType,
301            StepKind::SetColumnNullable,
302            StepKind::SetColumnDefault,
303            StepKind::SetColumnComment,
304            StepKind::SetColumnIdentity,
305            StepKind::SetColumnGenerated,
306            StepKind::SetColumnStorage,
307            StepKind::SetColumnCompression,
308            StepKind::AddConstraint,
309            StepKind::AddConstraintNotValid,
310            StepKind::ValidateConstraint,
311            StepKind::DropConstraint,
312            StepKind::SetConstraintComment,
313            StepKind::CreateIndex,
314            StepKind::CreateIndexConcurrent,
315            StepKind::DropIndex,
316            StepKind::DropIndexConcurrent,
317            StepKind::CreateSequence,
318            StepKind::DropSequence,
319            StepKind::AlterSequence,
320            StepKind::AddCheckForNotNull,
321            StepKind::CreateView,
322            StepKind::DropView,
323            StepKind::CreateMaterializedView,
324            StepKind::DropMaterializedView,
325            StepKind::RefreshMaterializedView,
326            StepKind::AlterViewSetReloption,
327            StepKind::CommentOnView,
328            StepKind::CreateType,
329            StepKind::DropType,
330            StepKind::AlterTypeAddValue,
331            StepKind::AlterTypeRenameValue,
332            StepKind::AlterDomainAddConstraint,
333            StepKind::AlterDomainDropConstraint,
334            StepKind::AlterDomainSetDefault,
335            StepKind::AlterDomainSetNotNull,
336            StepKind::AlterTypeAddAttribute,
337            StepKind::AlterTypeDropAttribute,
338            StepKind::AlterTypeAlterAttributeType,
339            StepKind::CommentOnType,
340            StepKind::CreateOrReplaceFunction,
341            StepKind::DropFunction,
342            StepKind::CommentOnFunction,
343            StepKind::CreateOrReplaceProcedure,
344            StepKind::DropProcedure,
345            StepKind::CommentOnProcedure,
346            StepKind::CreateExtension,
347            StepKind::DropExtension,
348            StepKind::AlterExtensionUpdate,
349            StepKind::CommentOnExtension,
350            StepKind::CreateTrigger,
351            StepKind::DropTrigger,
352            StepKind::CommentOnTrigger,
353            StepKind::AttachPartition,
354            StepKind::DetachPartition,
355            StepKind::CreateRole,
356            StepKind::DropRole,
357            StepKind::AlterRole,
358            StepKind::GrantRoleMembership,
359            StepKind::RevokeRoleMembership,
360            StepKind::CommentOnRole,
361            StepKind::AlterObjectOwner,
362            StepKind::GrantObjectPrivilege,
363            StepKind::RevokeObjectPrivilege,
364            StepKind::GrantColumnPrivilege,
365            StepKind::RevokeColumnPrivilege,
366            StepKind::AlterDefaultPrivileges,
367            StepKind::CreatePolicy,
368            StepKind::DropPolicy,
369            StepKind::AlterPolicy,
370            StepKind::SetTableRowSecurity,
371            StepKind::SetTableForceRowSecurity,
372            StepKind::SetTableStorage,
373            StepKind::SetIndexStorage,
374            StepKind::SetMaterializedViewStorage,
375            StepKind::CreatePublication,
376            StepKind::DropPublication,
377            StepKind::ReplacePublication,
378            StepKind::AlterPublicationAddTable,
379            StepKind::AlterPublicationDropTable,
380            StepKind::AlterPublicationSetTable,
381            StepKind::AlterPublicationAddSchema,
382            StepKind::AlterPublicationDropSchema,
383            StepKind::AlterPublicationSetPublish,
384            StepKind::AlterPublicationSetViaRoot,
385            StepKind::CommentOnPublication,
386        ] {
387            let json = serde_json::to_string(&kind).unwrap();
388            let back: StepKind = serde_json::from_str(&json).unwrap();
389            assert_eq!(kind, back);
390        }
391    }
392
393    #[test]
394    fn transaction_constraint_serializes_as_snake_case() {
395        assert_eq!(
396            serde_json::to_string(&TransactionConstraint::OutsideTransaction).unwrap(),
397            "\"outside_transaction\"",
398        );
399    }
400}