Skip to main content

safe_migrate/analysis/
expr_ir.rs

1// FILE: src/analysis/expr_ir.rs
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
5pub enum ExprIr {
6    Literal(String),
7    ColumnRef(String),
8    FunctionCall {
9        name: String,
10        args: Vec<ExprIr>,
11    },
12    BinaryOp {
13        left: Box<ExprIr>,
14        op: String,
15        right: Box<ExprIr>,
16    },
17    Cast {
18        expr: Box<ExprIr>,
19        target_type: String,
20    },
21    Omitted, // Added to prevent positional shifting in incomplete expressions (e.g., arr[2:])
22}
23
24impl ExprIr {
25    pub fn is_volatile(&self) -> bool {
26        match self {
27            ExprIr::FunctionCall { name, args } => {
28                // Synthetic wrapper functions for nested expressions
29                // e.g. <case>, <array>, <between>, <slice>
30                if name.starts_with('<') && name.ends_with('>') {
31                    return args.iter().any(|a| a.is_volatile());
32                }
33
34                const VOLATILE: &[&str] = &[
35                    "now",
36                    "clock_timestamp",
37                    "transaction_timestamp",
38                    "statement_timestamp",
39                    "timeofday",
40                    "random",
41                    "setseed",
42                    "txid_current",
43                    "txid_current_snapshot",
44                    "txid_snapshot_xip",
45                    "txid_snapshot_xmax",
46                    "txid_snapshot_xmin",
47                    "nextval",
48                    "currval",
49                    "lastval",
50                    "setval",
51                    "gen_random_uuid",
52                    "uuid_generate_v1",
53                    "uuid_generate_v1mc",
54                    "uuid_generate_v4",
55                ];
56
57                VOLATILE.contains(&name.to_lowercase().as_str())
58            }
59            ExprIr::BinaryOp { left, right, .. } => left.is_volatile() || right.is_volatile(),
60            ExprIr::Cast { expr, .. } => expr.is_volatile(),
61            ExprIr::Literal(_) | ExprIr::ColumnRef(_) | ExprIr::Omitted => false,
62        }
63    }
64}