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 OR REPLACE VIEW … WITH [LOCAL|CASCADED] CHECK OPTION` (no
106 /// direct ALTER; pgevolve re-issues the full definition).
107 AlterViewSetCheckOption,
108 /// `CREATE MATERIALIZED VIEW ... WITH NO DATA`.
109 CreateMaterializedView,
110 /// `DROP MATERIALIZED VIEW`.
111 DropMaterializedView,
112 /// `REFRESH MATERIALIZED VIEW [CONCURRENTLY]`.
113 RefreshMaterializedView,
114 /// `ALTER VIEW ... SET (...)`.
115 AlterViewSetReloption,
116 /// `COMMENT ON VIEW / MATERIALIZED VIEW / COLUMN` for views and MVs.
117 CommentOnView,
118
119 // --- v0.2 user-defined type step kinds ---
120 /// `CREATE TYPE` (enum, domain, or composite).
121 CreateType,
122 /// `DROP TYPE`.
123 DropType,
124 /// `ALTER TYPE … ADD VALUE`.
125 AlterTypeAddValue,
126 /// `ALTER TYPE … RENAME VALUE`.
127 AlterTypeRenameValue,
128 /// `ALTER DOMAIN … ADD CONSTRAINT … CHECK (…)`.
129 AlterDomainAddConstraint,
130 /// `ALTER DOMAIN … DROP CONSTRAINT`.
131 AlterDomainDropConstraint,
132 /// `ALTER DOMAIN … SET DEFAULT` / `DROP DEFAULT`.
133 AlterDomainSetDefault,
134 /// `ALTER DOMAIN … SET NOT NULL` / `DROP NOT NULL`.
135 AlterDomainSetNotNull,
136 /// `ALTER TYPE … ADD ATTRIBUTE`.
137 AlterTypeAddAttribute,
138 /// `ALTER TYPE … DROP ATTRIBUTE`.
139 AlterTypeDropAttribute,
140 /// `ALTER TYPE … ALTER ATTRIBUTE … TYPE`.
141 AlterTypeAlterAttributeType,
142 /// `COMMENT ON TYPE` / `COMMENT ON DOMAIN`.
143 CommentOnType,
144
145 // --- v0.2 function / procedure step kinds ---
146 /// `CREATE OR REPLACE FUNCTION`.
147 CreateOrReplaceFunction,
148 /// `DROP FUNCTION`.
149 DropFunction,
150 /// `COMMENT ON FUNCTION`.
151 CommentOnFunction,
152 /// `CREATE OR REPLACE PROCEDURE`.
153 CreateOrReplaceProcedure,
154 /// `DROP PROCEDURE`.
155 DropProcedure,
156 /// `COMMENT ON PROCEDURE`.
157 CommentOnProcedure,
158
159 // --- v0.2 extension step kinds ---
160 /// `CREATE EXTENSION [IF NOT EXISTS] name [WITH SCHEMA s] [VERSION 'v']`.
161 CreateExtension,
162 /// `DROP EXTENSION name CASCADE`. Destructive (intent required).
163 DropExtension,
164 /// `ALTER EXTENSION name UPDATE TO 'v'`.
165 AlterExtensionUpdate,
166 /// `COMMENT ON EXTENSION name IS '...'`.
167 CommentOnExtension,
168
169 // --- v0.2 trigger step kinds ---
170 /// `CREATE [CONSTRAINT] TRIGGER name ... ON table ...`.
171 CreateTrigger,
172 /// `DROP TRIGGER name ON table`.
173 DropTrigger,
174 /// `COMMENT ON TRIGGER name ON table IS '...'`.
175 CommentOnTrigger,
176
177 // --- v0.2 partition step kinds ---
178 /// `ALTER TABLE parent ATTACH PARTITION child FOR VALUES ...`.
179 AttachPartition,
180 /// `ALTER TABLE parent DETACH PARTITION child`.
181 DetachPartition,
182
183 // --- v0.3 cluster / role step kinds ---
184 /// `CREATE ROLE`.
185 CreateRole,
186 /// `DROP ROLE`.
187 DropRole,
188 /// `ALTER ROLE … WITH <options>`.
189 AlterRole,
190 /// `GRANT role TO member`.
191 GrantRoleMembership,
192 /// `REVOKE role FROM member`.
193 RevokeRoleMembership,
194 /// `COMMENT ON ROLE`.
195 CommentOnRole,
196
197 // --- v0.4 tablespace step kinds ---
198 /// `CREATE TABLESPACE`.
199 CreateTablespace,
200 /// `DROP TABLESPACE` — destructive.
201 DropTablespace,
202 /// `ALTER TABLESPACE … OWNER TO …`.
203 AlterTablespaceOwner,
204 /// `ALTER TABLESPACE … SET (…)`.
205 SetTablespaceOptions,
206 /// `COMMENT ON TABLESPACE`.
207 CommentOnTablespace,
208
209 // --- v0.3.1 grant / ownership step kinds ---
210 /// `ALTER <kind> qname OWNER TO new_owner`.
211 AlterObjectOwner,
212 /// `GRANT priv ON <kind> qname TO grantee [WITH GRANT OPTION]`.
213 GrantObjectPrivilege,
214 /// `REVOKE priv ON <kind> qname FROM grantee`.
215 RevokeObjectPrivilege,
216 /// `GRANT priv (col, …) ON TABLE qname TO grantee [WITH GRANT OPTION]`.
217 GrantColumnPrivilege,
218 /// `REVOKE priv (col, …) ON TABLE qname FROM grantee`.
219 RevokeColumnPrivilege,
220 /// `ALTER DEFAULT PRIVILEGES FOR ROLE x [IN SCHEMA y] GRANT/REVOKE priv ON … TO/FROM z`.
221 AlterDefaultPrivileges,
222
223 // --- v0.3.2 row-level security policy step kinds ---
224 /// `CREATE POLICY name ON table …`.
225 CreatePolicy,
226 /// `DROP POLICY name ON table`.
227 DropPolicy,
228 /// `ALTER POLICY name ON table TO … USING (…) WITH CHECK (…)`.
229 AlterPolicy,
230 /// `ALTER TABLE qname { ENABLE | DISABLE } ROW LEVEL SECURITY`.
231 SetTableRowSecurity,
232 /// `ALTER TABLE qname { FORCE | NO FORCE } ROW LEVEL SECURITY`.
233 SetTableForceRowSecurity,
234
235 // --- v0.3.3 reloption step kinds ---
236 /// `ALTER TABLE qname SET (fillfactor = …, …)`.
237 SetTableStorage,
238 /// `ALTER INDEX qname SET (fillfactor = …, …)`.
239 SetIndexStorage,
240 /// `ALTER MATERIALIZED VIEW qname SET (fillfactor = …, …)`.
241 SetMaterializedViewStorage,
242
243 // --- v0.3.4 publication step kinds ---
244 /// `CREATE PUBLICATION …`.
245 CreatePublication,
246 /// `DROP PUBLICATION …`. Destructive (intent required).
247 DropPublication,
248 /// `DROP PUBLICATION old; CREATE PUBLICATION new;` — mode swap. Destructive.
249 ReplacePublication,
250 /// `ALTER PUBLICATION p ADD TABLE x [(cols)] [WHERE (filter)]`.
251 AlterPublicationAddTable,
252 /// `ALTER PUBLICATION p DROP TABLE x`.
253 AlterPublicationDropTable,
254 /// `ALTER PUBLICATION p SET TABLE x (cols) WHERE (filter)`.
255 AlterPublicationSetTable,
256 /// `ALTER PUBLICATION p ADD TABLES IN SCHEMA s` (PG 15+).
257 AlterPublicationAddSchema,
258 /// `ALTER PUBLICATION p DROP TABLES IN SCHEMA s` (PG 15+).
259 AlterPublicationDropSchema,
260 /// `ALTER PUBLICATION p SET (publish = '...')`.
261 AlterPublicationSetPublish,
262 /// `ALTER PUBLICATION p SET (publish_via_partition_root = ...)`.
263 AlterPublicationSetViaRoot,
264 /// `COMMENT ON PUBLICATION p IS '...'`.
265 CommentOnPublication,
266
267 // --- v0.3.5 subscription step kinds ---
268 /// `CREATE SUBSCRIPTION …`.
269 CreateSubscription,
270 /// `DROP SUBSCRIPTION …`. Destructive (intent required).
271 DropSubscription,
272 /// `ALTER SUBSCRIPTION s CONNECTION '...'`.
273 AlterSubscriptionConnection,
274 /// `ALTER SUBSCRIPTION s ADD PUBLICATION p`.
275 AlterSubscriptionAddPublication,
276 /// `ALTER SUBSCRIPTION s DROP PUBLICATION p`.
277 AlterSubscriptionDropPublication,
278 /// `ALTER SUBSCRIPTION s SET (option = value, …)` — sparse-delta.
279 AlterSubscriptionSetOptions,
280 /// `COMMENT ON SUBSCRIPTION s IS '...'`.
281 CommentOnSubscription,
282
283 // --- v0.3.7 statistics step kinds ---
284 /// `CREATE STATISTICS …`.
285 CreateStatistic,
286 /// `DROP STATISTICS …`. Destructive (intent required).
287 DropStatistic,
288 /// `DROP STATISTICS old; CREATE STATISTICS new;` — structural change. Destructive.
289 ReplaceStatistic,
290 /// `ALTER STATISTICS s SET STATISTICS n`.
291 AlterStatisticSetTarget,
292 /// `COMMENT ON STATISTICS s IS '...'`.
293 CommentOnStatistic,
294
295 // --- v0.3.8 collation step kinds ---
296 /// `CREATE COLLATION qname (...)`.
297 CreateCollation,
298 /// `DROP COLLATION qname` — destructive.
299 DropCollation,
300 /// `ALTER COLLATION qname RENAME TO new_name`.
301 RenameCollation,
302 /// `DROP COLLATION old; CREATE COLLATION new;` — structural change.
303 ReplaceCollation,
304 /// `COMMENT ON COLLATION qname IS '...'`.
305 CommentOnCollation,
306
307 // --- v0.4 event trigger step kinds ---
308 /// `CREATE EVENT TRIGGER name ON event [WHEN TAG IN (...)] EXECUTE FUNCTION fn();`.
309 CreateEventTrigger,
310 /// `DROP EVENT TRIGGER name;` — destructive.
311 DropEventTrigger,
312 /// `ALTER EVENT TRIGGER name {ENABLE|DISABLE|ENABLE REPLICA|ENABLE ALWAYS};`.
313 AlterEventTriggerEnable,
314 /// `ALTER EVENT TRIGGER name OWNER TO role;`.
315 AlterEventTriggerOwner,
316 /// `COMMENT ON EVENT TRIGGER name IS '...';`.
317 CommentOnEventTrigger,
318
319 // --- v0.4 aggregate step kinds ---
320 /// `CREATE AGGREGATE qname(argtypes) (SFUNC = …, STYPE = …);`.
321 CreateAggregate,
322 /// `DROP AGGREGATE qname(argtypes);`.
323 DropAggregate,
324 /// `ALTER AGGREGATE qname(argtypes) OWNER TO role;`.
325 AlterAggregateOwner,
326 /// `COMMENT ON AGGREGATE qname(argtypes) IS '...';`.
327 CommentOnAggregate,
328
329 // --- v0.4 cast step kinds ---
330 /// `CREATE CAST (source AS target) …;`.
331 CreateCast,
332 /// `DROP CAST (source AS target);`.
333 DropCast,
334 /// `COMMENT ON CAST (source AS target) IS '...';`.
335 CommentOnCast,
336}
337
338/// One unit of work the executor will attempt.
339///
340/// `step_no` and `intent_id` start at zero / `None` and are assigned later
341/// by [`Plan::from_grouped`](crate::plan::Plan::from_grouped). The rewrite
342/// pass (Phase 6) builds steps without that numbering.
343#[derive(Debug, Clone, PartialEq, Eq)]
344pub struct RawStep {
345 /// 1-indexed step number across the whole plan. `0` until assigned by
346 /// `Plan::from_grouped`.
347 pub step_no: u32,
348 /// What kind of operation.
349 pub kind: StepKind,
350 /// Whether the step is destructive (requires explicit intent approval).
351 pub destructive: bool,
352 /// Human-readable reason for destructiveness, if any.
353 pub destructive_reason: Option<String>,
354 /// Intent id assigned by `Plan::from_grouped`; `None` until then.
355 pub intent_id: Option<u32>,
356 /// IR objects this step affects (used by directive comments).
357 pub targets: Vec<QualifiedName>,
358 /// Final SQL emitted to disk.
359 pub sql: String,
360 /// Whether the step can run inside a transaction.
361 pub transactional: TransactionConstraint,
362}
363
364#[cfg(test)]
365mod tests {
366 use super::*;
367
368 #[test]
369 fn step_kind_serializes_as_snake_case() {
370 let s = serde_json::to_string(&StepKind::CreateIndexConcurrent).unwrap();
371 assert_eq!(s, "\"create_index_concurrent\"");
372 }
373
374 #[allow(clippy::too_many_lines)] // One entry per StepKind variant — extraction would obscure intent.
375 #[test]
376 fn step_kind_round_trips_through_serde() {
377 for kind in [
378 StepKind::CreateSchema,
379 StepKind::DropSchema,
380 StepKind::AlterSchemaComment,
381 StepKind::CreateTable,
382 StepKind::DropTable,
383 StepKind::AlterTableSetComment,
384 StepKind::AddColumn,
385 StepKind::DropColumn,
386 StepKind::AlterColumnType,
387 StepKind::SetColumnNullable,
388 StepKind::SetColumnDefault,
389 StepKind::SetColumnComment,
390 StepKind::SetColumnIdentity,
391 StepKind::SetColumnGenerated,
392 StepKind::SetColumnStorage,
393 StepKind::SetColumnCompression,
394 StepKind::AddConstraint,
395 StepKind::AddConstraintNotValid,
396 StepKind::ValidateConstraint,
397 StepKind::DropConstraint,
398 StepKind::SetConstraintComment,
399 StepKind::CreateIndex,
400 StepKind::CreateIndexConcurrent,
401 StepKind::DropIndex,
402 StepKind::DropIndexConcurrent,
403 StepKind::CreateSequence,
404 StepKind::DropSequence,
405 StepKind::AlterSequence,
406 StepKind::AddCheckForNotNull,
407 StepKind::CreateView,
408 StepKind::DropView,
409 StepKind::AlterViewSetCheckOption,
410 StepKind::CreateMaterializedView,
411 StepKind::DropMaterializedView,
412 StepKind::RefreshMaterializedView,
413 StepKind::AlterViewSetReloption,
414 StepKind::CommentOnView,
415 StepKind::CreateType,
416 StepKind::DropType,
417 StepKind::AlterTypeAddValue,
418 StepKind::AlterTypeRenameValue,
419 StepKind::AlterDomainAddConstraint,
420 StepKind::AlterDomainDropConstraint,
421 StepKind::AlterDomainSetDefault,
422 StepKind::AlterDomainSetNotNull,
423 StepKind::AlterTypeAddAttribute,
424 StepKind::AlterTypeDropAttribute,
425 StepKind::AlterTypeAlterAttributeType,
426 StepKind::CommentOnType,
427 StepKind::CreateOrReplaceFunction,
428 StepKind::DropFunction,
429 StepKind::CommentOnFunction,
430 StepKind::CreateOrReplaceProcedure,
431 StepKind::DropProcedure,
432 StepKind::CommentOnProcedure,
433 StepKind::CreateExtension,
434 StepKind::DropExtension,
435 StepKind::AlterExtensionUpdate,
436 StepKind::CommentOnExtension,
437 StepKind::CreateTrigger,
438 StepKind::DropTrigger,
439 StepKind::CommentOnTrigger,
440 StepKind::AttachPartition,
441 StepKind::DetachPartition,
442 StepKind::CreateRole,
443 StepKind::DropRole,
444 StepKind::AlterRole,
445 StepKind::GrantRoleMembership,
446 StepKind::RevokeRoleMembership,
447 StepKind::CommentOnRole,
448 StepKind::CreateTablespace,
449 StepKind::DropTablespace,
450 StepKind::AlterTablespaceOwner,
451 StepKind::SetTablespaceOptions,
452 StepKind::CommentOnTablespace,
453 StepKind::AlterObjectOwner,
454 StepKind::GrantObjectPrivilege,
455 StepKind::RevokeObjectPrivilege,
456 StepKind::GrantColumnPrivilege,
457 StepKind::RevokeColumnPrivilege,
458 StepKind::AlterDefaultPrivileges,
459 StepKind::CreatePolicy,
460 StepKind::DropPolicy,
461 StepKind::AlterPolicy,
462 StepKind::SetTableRowSecurity,
463 StepKind::SetTableForceRowSecurity,
464 StepKind::SetTableStorage,
465 StepKind::SetIndexStorage,
466 StepKind::SetMaterializedViewStorage,
467 StepKind::CreatePublication,
468 StepKind::DropPublication,
469 StepKind::ReplacePublication,
470 StepKind::AlterPublicationAddTable,
471 StepKind::AlterPublicationDropTable,
472 StepKind::AlterPublicationSetTable,
473 StepKind::AlterPublicationAddSchema,
474 StepKind::AlterPublicationDropSchema,
475 StepKind::AlterPublicationSetPublish,
476 StepKind::AlterPublicationSetViaRoot,
477 StepKind::CommentOnPublication,
478 StepKind::CreateSubscription,
479 StepKind::DropSubscription,
480 StepKind::AlterSubscriptionConnection,
481 StepKind::AlterSubscriptionAddPublication,
482 StepKind::AlterSubscriptionDropPublication,
483 StepKind::AlterSubscriptionSetOptions,
484 StepKind::CommentOnSubscription,
485 StepKind::CreateStatistic,
486 StepKind::DropStatistic,
487 StepKind::ReplaceStatistic,
488 StepKind::AlterStatisticSetTarget,
489 StepKind::CommentOnStatistic,
490 StepKind::CreateCollation,
491 StepKind::DropCollation,
492 StepKind::RenameCollation,
493 StepKind::ReplaceCollation,
494 StepKind::CommentOnCollation,
495 StepKind::CreateEventTrigger,
496 StepKind::DropEventTrigger,
497 StepKind::AlterEventTriggerEnable,
498 StepKind::AlterEventTriggerOwner,
499 StepKind::CommentOnEventTrigger,
500 StepKind::CreateAggregate,
501 StepKind::DropAggregate,
502 StepKind::AlterAggregateOwner,
503 StepKind::CommentOnAggregate,
504 StepKind::CreateCast,
505 StepKind::DropCast,
506 StepKind::CommentOnCast,
507 ] {
508 let json = serde_json::to_string(&kind).unwrap();
509 let back: StepKind = serde_json::from_str(&json).unwrap();
510 assert_eq!(kind, back);
511 }
512 }
513
514 #[test]
515 fn transaction_constraint_serializes_as_snake_case() {
516 assert_eq!(
517 serde_json::to_string(&TransactionConstraint::OutsideTransaction).unwrap(),
518 "\"outside_transaction\"",
519 );
520 }
521}