1use nodedb_types::DatabaseId;
6
7use crate::catalog::SqlCatalog;
8use crate::functions::registry::FunctionRegistry;
9use crate::types::{SqlExpr, SqlValue};
10
11pub(super) fn eval_catalog_constant(
12 expr: &SqlExpr,
13 catalog: &dyn SqlCatalog,
14 functions: &FunctionRegistry,
15) -> crate::Result<SqlValue> {
16 if let SqlExpr::Cast {
17 expr: inner,
18 to_type,
19 } = expr
20 && to_type.eq_ignore_ascii_case("regclass")
21 && let SqlExpr::Literal(SqlValue::String(name)) = inner.as_ref()
22 {
23 let normalized = crate::catalog::normalize_regclass_name(name)
24 .ok_or_else(|| crate::SqlError::UnknownTable { name: name.clone() })?;
25 catalog
26 .resolve_regclass(DatabaseId::DEFAULT, 0, name)
27 .ok_or_else(|| crate::SqlError::UnknownTable {
28 name: normalized.clone(),
29 })?;
30 return Ok(SqlValue::String(normalized));
31 }
32 Ok(super::select::helpers::eval_constant_expr(expr, functions))
33}
34
35pub(super) fn validate_expr(
36 expr: &SqlExpr,
37 catalog: &dyn SqlCatalog,
38 database_id: DatabaseId,
39 tenant_id: u64,
40) -> crate::Result<()> {
41 match expr {
42 SqlExpr::Cast { expr, to_type }
43 if to_type.eq_ignore_ascii_case("regclass")
44 && matches!(expr.as_ref(), SqlExpr::Literal(SqlValue::String(_))) =>
45 {
46 let SqlExpr::Literal(SqlValue::String(name)) = expr.as_ref() else {
47 unreachable!();
48 };
49 if catalog
50 .resolve_regclass(database_id, tenant_id, name)
51 .is_none()
52 {
53 return Err(crate::SqlError::UnknownTable { name: name.clone() });
54 }
55 Ok(())
56 }
57 SqlExpr::Cast { expr, .. }
58 | SqlExpr::IsNull { expr, .. }
59 | SqlExpr::UnaryOp { expr, .. } => validate_expr(expr, catalog, database_id, tenant_id),
60 SqlExpr::BinaryOp { left, right, .. } => {
61 validate_expr(left, catalog, database_id, tenant_id)?;
62 validate_expr(right, catalog, database_id, tenant_id)
63 }
64 SqlExpr::InList { expr, list, .. } => {
65 validate_expr(expr, catalog, database_id, tenant_id)?;
66 for item in list {
67 validate_expr(item, catalog, database_id, tenant_id)?;
68 }
69 Ok(())
70 }
71 SqlExpr::ArrayLiteral(items) | SqlExpr::Function { args: items, .. } => {
72 for item in items {
73 validate_expr(item, catalog, database_id, tenant_id)?;
74 }
75 Ok(())
76 }
77 SqlExpr::Between {
78 expr, low, high, ..
79 } => {
80 validate_expr(expr, catalog, database_id, tenant_id)?;
81 validate_expr(low, catalog, database_id, tenant_id)?;
82 validate_expr(high, catalog, database_id, tenant_id)
83 }
84 SqlExpr::Like { expr, pattern, .. } => {
85 validate_expr(expr, catalog, database_id, tenant_id)?;
86 validate_expr(pattern, catalog, database_id, tenant_id)
87 }
88 SqlExpr::Case {
89 operand,
90 when_then,
91 else_expr,
92 } => {
93 if let Some(operand) = operand {
94 validate_expr(operand, catalog, database_id, tenant_id)?;
95 }
96 for (when, then) in when_then {
97 validate_expr(when, catalog, database_id, tenant_id)?;
98 validate_expr(then, catalog, database_id, tenant_id)?;
99 }
100 if let Some(expr) = else_expr {
101 validate_expr(expr, catalog, database_id, tenant_id)?;
102 }
103 Ok(())
104 }
105 _ => Ok(()),
106 }
107}
108
109pub(super) fn fold_expr(
111 expr: SqlExpr,
112 catalog: &dyn SqlCatalog,
113 database_id: DatabaseId,
114 tenant_id: u64,
115) -> SqlExpr {
116 match expr {
117 SqlExpr::Cast {
118 expr: inner_expr,
119 to_type,
120 } => {
121 let upper = to_type.to_ascii_uppercase();
122 if upper == "REGCLASS" {
123 if let SqlExpr::Literal(SqlValue::String(ref name)) = *inner_expr
124 && let Some(oid) = catalog.resolve_regclass(database_id, tenant_id, name)
125 {
126 return SqlExpr::Literal(SqlValue::Int(oid));
127 }
128 } else if upper == "REGTYPE"
129 && let SqlExpr::Literal(SqlValue::String(ref name)) = *inner_expr
130 && let Some(oid) = catalog.resolve_regtype(name)
131 {
132 return SqlExpr::Literal(SqlValue::Int(oid));
133 }
134 SqlExpr::Cast {
135 expr: Box::new(fold_expr(*inner_expr, catalog, database_id, tenant_id)),
136 to_type,
137 }
138 }
139 SqlExpr::BinaryOp { left, op, right } => SqlExpr::BinaryOp {
140 left: Box::new(fold_expr(*left, catalog, database_id, tenant_id)),
141 op,
142 right: Box::new(fold_expr(*right, catalog, database_id, tenant_id)),
143 },
144 SqlExpr::InList {
145 expr,
146 list,
147 negated,
148 } => SqlExpr::InList {
149 expr: Box::new(fold_expr(*expr, catalog, database_id, tenant_id)),
150 list: list
151 .into_iter()
152 .map(|e| fold_expr(e, catalog, database_id, tenant_id))
153 .collect(),
154 negated,
155 },
156 SqlExpr::IsNull { expr, negated } => SqlExpr::IsNull {
157 expr: Box::new(fold_expr(*expr, catalog, database_id, tenant_id)),
158 negated,
159 },
160 SqlExpr::UnaryOp { op, expr } => SqlExpr::UnaryOp {
161 op,
162 expr: Box::new(fold_expr(*expr, catalog, database_id, tenant_id)),
163 },
164 SqlExpr::ArrayLiteral(elems) => SqlExpr::ArrayLiteral(
165 elems
166 .into_iter()
167 .map(|e| fold_expr(e, catalog, database_id, tenant_id))
168 .collect(),
169 ),
170 SqlExpr::Function {
171 name,
172 args,
173 distinct,
174 } => SqlExpr::Function {
175 name,
176 args: args
177 .into_iter()
178 .map(|arg| fold_expr(arg, catalog, database_id, tenant_id))
179 .collect(),
180 distinct,
181 },
182 SqlExpr::Between {
183 expr,
184 low,
185 high,
186 negated,
187 } => SqlExpr::Between {
188 expr: Box::new(fold_expr(*expr, catalog, database_id, tenant_id)),
189 low: Box::new(fold_expr(*low, catalog, database_id, tenant_id)),
190 high: Box::new(fold_expr(*high, catalog, database_id, tenant_id)),
191 negated,
192 },
193 SqlExpr::Like {
194 expr,
195 pattern,
196 negated,
197 case_insensitive,
198 } => SqlExpr::Like {
199 expr: Box::new(fold_expr(*expr, catalog, database_id, tenant_id)),
200 pattern: Box::new(fold_expr(*pattern, catalog, database_id, tenant_id)),
201 negated,
202 case_insensitive,
203 },
204 SqlExpr::Case {
205 operand,
206 when_then,
207 else_expr,
208 } => SqlExpr::Case {
209 operand: operand.map(|e| Box::new(fold_expr(*e, catalog, database_id, tenant_id))),
210 when_then: when_then
211 .into_iter()
212 .map(|(when, then)| {
213 (
214 fold_expr(when, catalog, database_id, tenant_id),
215 fold_expr(then, catalog, database_id, tenant_id),
216 )
217 })
218 .collect(),
219 else_expr: else_expr.map(|e| Box::new(fold_expr(*e, catalog, database_id, tenant_id))),
220 },
221 leaf => leaf,
222 }
223}