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}