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