Skip to main content

nodedb_query/expr/
types.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! SqlExpr AST node definitions.
4
5use nodedb_types::Value;
6
7/// The column name that references the whole row.
8///
9/// SQL `*` as a function argument (`to_jsonb(*)`) lowers to
10/// `Column(WHOLE_ROW_COLUMN)`, which evaluates to the row document itself.
11pub const WHOLE_ROW_COLUMN: &str = "*";
12
13/// A serializable SQL expression that can be evaluated against a document.
14#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
15pub enum SqlExpr {
16    /// Column reference: extract field value from the document.
17    /// [`WHOLE_ROW_COLUMN`] is the whole document.
18    Column(String),
19    /// Literal value.
20    Literal(Value),
21    /// Binary operation: left op right.
22    BinaryOp {
23        left: Box<SqlExpr>,
24        op: BinaryOp,
25        right: Box<SqlExpr>,
26    },
27    /// Unary negation: -expr or NOT expr.
28    Negate(Box<SqlExpr>),
29    /// Scalar function call.
30    Function { name: String, args: Vec<SqlExpr> },
31    /// CAST(expr AS type).
32    Cast {
33        expr: Box<SqlExpr>,
34        to_type: CastType,
35    },
36    /// CASE WHEN cond1 THEN val1 ... ELSE default END.
37    Case {
38        operand: Option<Box<SqlExpr>>,
39        when_thens: Vec<(SqlExpr, SqlExpr)>,
40        else_expr: Option<Box<SqlExpr>>,
41    },
42    /// COALESCE(expr1, expr2, ...): first non-null value.
43    Coalesce(Vec<SqlExpr>),
44    /// NULLIF(expr1, expr2): returns NULL if expr1 = expr2, else expr1.
45    NullIf(Box<SqlExpr>, Box<SqlExpr>),
46    /// IS NULL / IS NOT NULL.
47    IsNull { expr: Box<SqlExpr>, negated: bool },
48    /// OLD column reference: extract field value from the pre-update document.
49    /// Used in TRANSITION CHECK predicates. Resolves against the OLD row
50    /// when evaluated via `eval_with_old()`. Returns NULL in normal `eval()`.
51    OldColumn(String),
52    /// `EXCLUDED.col` reference from `INSERT ... ON CONFLICT DO UPDATE`:
53    /// the column value from the row proposed for insertion that
54    /// triggered the conflict. Resolves against the incoming row when
55    /// evaluated via `eval_with_excluded()`. Returns NULL in plain
56    /// `eval()` / `eval_with_old()`.
57    ExcludedColumn(String),
58}
59
60/// Binary operators.
61#[derive(
62    Debug,
63    Clone,
64    Copy,
65    PartialEq,
66    Eq,
67    serde::Serialize,
68    serde::Deserialize,
69    zerompk::ToMessagePack,
70    zerompk::FromMessagePack,
71)]
72#[msgpack(c_enum)]
73pub enum BinaryOp {
74    Add,
75    Sub,
76    Mul,
77    Div,
78    Mod,
79    Eq,
80    NotEq,
81    Gt,
82    GtEq,
83    Lt,
84    LtEq,
85    And,
86    Or,
87    Concat,
88}
89
90/// Target types for CAST.
91#[derive(
92    Debug,
93    Clone,
94    PartialEq,
95    Eq,
96    serde::Serialize,
97    serde::Deserialize,
98    zerompk::ToMessagePack,
99    zerompk::FromMessagePack,
100)]
101#[msgpack(c_enum)]
102pub enum CastType {
103    Int,
104    Float,
105    String,
106    Bool,
107}
108
109/// A computed projection column: alias + expression.
110#[derive(
111    Debug,
112    Clone,
113    serde::Serialize,
114    serde::Deserialize,
115    zerompk::ToMessagePack,
116    zerompk::FromMessagePack,
117)]
118pub struct ComputedColumn {
119    pub alias: String,
120    pub expr: SqlExpr,
121}
122
123/// A GROUP BY key specification for aggregate physical execution.
124///
125/// A bare-column key extracts its value from the document field named by
126/// `field` and emits it in the aggregate output row under `output_name`. For a
127/// plain column these are the same string. `expr` carries a computed-key
128/// expression to evaluate per row; it is always `None` for bare-column keys.
129#[derive(
130    Debug,
131    Clone,
132    PartialEq,
133    serde::Serialize,
134    serde::Deserialize,
135    zerompk::ToMessagePack,
136    zerompk::FromMessagePack,
137)]
138pub struct GroupKeySpec {
139    /// Output column name for this group key (alias, else the raw column name).
140    pub output_name: String,
141    /// Bare-column extraction key. `Some` for a plain `Column` group key.
142    pub field: Option<String>,
143    /// Computed-key expression, evaluated per row. `None` for bare-column keys.
144    pub expr: Option<SqlExpr>,
145}
146
147impl GroupKeySpec {
148    /// Build a bare-column group key: extract from `name` and emit under the
149    /// same column name, with no computed expression.
150    pub fn column(name: impl Into<String>) -> Self {
151        let name = name.into();
152        Self {
153            output_name: name.clone(),
154            field: Some(name),
155            expr: None,
156        }
157    }
158}