1mod coercion;
10mod constructs;
11mod operators;
12
13use super::{values, Field, Node};
14use crate::ast::{BinaryOp, Expr, FunctionBinding};
15use crate::catalog::type_metadata::{pg_type_collation_oid, pg_type_modifier, pg_type_oid};
16use crate::type_resolution::{
17 binary_operator_catalog_entry, binary_operator_types, common_context_expression_type,
18 FunctionTypeResolver,
19};
20use crate::{ColumnType, RowSchema, SQLError};
21
22pub struct RoutineIdentity {
23 pub oid: i64,
24 pub argument_types: Vec<ColumnType>,
25 pub result_type: ColumnType,
26}
27
28pub trait ExpressionRoutines {
29 fn resolve(
30 &self,
31 name: &str,
32 binding: Option<&FunctionBinding>,
33 argument_types: &[Option<ColumnType>],
34 ) -> Result<RoutineIdentity, SQLError>;
35}
36
37pub struct ExpressionContext<'a> {
38 pub schema: &'a RowSchema,
39 pub domain_value: Option<&'a ColumnType>,
40 pub types: Option<&'a dyn FunctionTypeResolver>,
41 pub routines: &'a dyn ExpressionRoutines,
42}
43
44struct TypedNode {
45 node: Node,
46 ty: ColumnType,
47}
48
49impl ExpressionContext<'_> {
50 pub fn check(&self, expression: &Expr) -> Result<Node, SQLError> {
51 self.encode(expression, Some(&ColumnType::Boolean))
52 .map(|value| value.node)
53 }
54
55 fn expression_type(&self, expression: &Expr) -> Result<Option<ColumnType>, SQLError> {
56 let plan = crate::plan::ExpressionPlan::lower(expression.clone());
57 common_context_expression_type(&plan.scalar, self.schema, &[], self.types)
58 }
59
60 fn encode(
61 &self,
62 expression: &Expr,
63 expected: Option<&ColumnType>,
64 ) -> Result<TypedNode, SQLError> {
65 let value = match expression {
66 Expr::Column(column) => self.column(column, None)?,
67 Expr::QualifiedColumn { qualifier, column } => self.column(column, Some(qualifier))?,
68 Expr::Literal(value) => self.literal(expression, value, expected)?,
69 Expr::TypedLiteral { value, ty } => {
70 let ty = self.resolve_type(ty)?;
71 TypedNode {
72 node: values::constant(value, &ty)?,
73 ty,
74 }
75 }
76 Expr::Binary { op, lhs, rhs } => self.binary(*op, lhs, rhs)?,
77 Expr::UnaryMinus(argument) => self.unary_minus(argument)?,
78 Expr::Array(elements) => self.array(elements, expected)?,
79 Expr::InList {
80 expr,
81 list,
82 negated,
83 } => self.in_list(expr, list, *negated)?,
84 Expr::Case {
85 base,
86 when,
87 else_branch,
88 } => self.case(expression, base.as_deref(), when, else_branch.as_deref())?,
89 Expr::And(items) => self.boolean("and", items)?,
90 Expr::Or(items) => self.boolean("or", items)?,
91 Expr::Not(item) => self.boolean("not", std::slice::from_ref(item.as_ref()))?,
92 Expr::IsNull { expr, negated } => {
93 let arg = self.encode(expr, None)?;
94 TypedNode {
95 node: Node::new(
96 "NULLTEST",
97 [
98 ("arg", arg.node.into()),
99 ("nulltesttype", i64::from(*negated).into()),
100 ("argisrow", false.into()),
101 ("location", (-1).into()),
102 ],
103 ),
104 ty: ColumnType::Boolean,
105 }
106 }
107 Expr::Between { expr, low, high } => self.boolean(
108 "and",
109 &[
110 Expr::Binary {
111 op: BinaryOp::GreaterEqual,
112 lhs: expr.clone(),
113 rhs: low.clone(),
114 },
115 Expr::Binary {
116 op: BinaryOp::LessEqual,
117 lhs: expr.clone(),
118 rhs: high.clone(),
119 },
120 ],
121 )?,
122 Expr::Func {
123 name,
124 binding,
125 args,
126 distinct: false,
127 order_by,
128 filter: None,
129 } if order_by.is_empty() => {
130 if let Some(value) = self.construct(expression, name, binding.as_ref(), args)? {
131 value
132 } else {
133 self.function(name, binding.as_ref(), args)?
134 }
135 }
136 Expr::Cast { expr, ty } => {
137 let ty = self.resolve_type(ty)?;
138 let unknown = self.expression_type(expr)?.is_none();
139 let mut input_type = &ty;
140 while let ColumnType::Domain { base, .. } = input_type {
141 input_type = base;
142 }
143 let input_type = input_type.without_type_modifiers();
144 let inner = self.encode(expr, unknown.then_some(&input_type))?;
145 Self::coerce(inner, &ty, 1)?
146 }
147 _ => {
148 return Err(SQLError::Unsupported(
149 "catalog expression node encoding for this expression".into(),
150 ))
151 }
152 };
153 if let Some(ty) = expected {
154 Self::coerce(value, ty, 2)
155 } else {
156 Ok(value)
157 }
158 }
159
160 fn literal(
161 &self,
162 expression: &Expr,
163 value: &uqa_core::Value,
164 expected: Option<&ColumnType>,
165 ) -> Result<TypedNode, SQLError> {
166 let source = self.expression_type(expression)?;
167 let ty = source
168 .as_ref()
169 .or(expected)
170 .cloned()
171 .unwrap_or(ColumnType::Text);
172 let value = crate::type_resolution::coerce_common_context_value(
173 value.clone(),
174 source.as_ref(),
175 Some(&ty),
176 )?;
177 Ok(TypedNode {
178 node: values::constant(&value, &ty)?,
179 ty,
180 })
181 }
182
183 fn resolve_type(&self, name: &str) -> Result<ColumnType, SQLError> {
184 if let Some(resolver) = self.types {
185 if let Some(ty) = resolver.resolve_type_name(name)? {
186 return Ok(ty);
187 }
188 }
189 ColumnType::from_sql_name(name)
190 }
191
192 fn column(&self, name: &str, qualifier: Option<&str>) -> Result<TypedNode, SQLError> {
193 if let Some(ty) = self.domain_value {
194 if name != "value" || qualifier.is_some() {
195 return Err(SQLError::UnknownColumn(name.into()));
196 }
197 return Ok(TypedNode {
198 node: Node::new(
199 "COERCETODOMAINVALUE",
200 [
201 ("typeId", pg_type_oid(ty).into()),
202 ("typeMod", pg_type_modifier(ty).into()),
203 ("collation", pg_type_collation_oid(ty).into()),
204 ("location", (-1).into()),
205 ],
206 ),
207 ty: ty.clone(),
208 });
209 }
210 let position = qualifier
211 .map_or_else(
212 || self.schema.unqualified_position(name),
213 |qualifier| self.schema.qualified_position(qualifier, name),
214 )
215 .ok_or_else(|| SQLError::UnknownColumn(name.into()))?;
216 let ty = self
217 .schema
218 .column_type(position)
219 .ok_or_else(|| SQLError::Internal("catalog column has no declared type".into()))?;
220 let ordinal = i64::try_from(position + 1)
221 .map_err(|_| SQLError::Internal("column ordinal overflow".into()))?;
222 Ok(TypedNode {
223 node: Node::new(
224 "VAR",
225 [
226 ("varno", 1.into()),
227 ("varattno", ordinal.into()),
228 ("vartype", pg_type_oid(ty).into()),
229 ("vartypmod", pg_type_modifier(ty).into()),
230 ("varcollid", pg_type_collation_oid(ty).into()),
231 ("varnullingrels", Field::List(vec![Field::Atom("b".into())])),
232 ("varlevelsup", 0.into()),
233 ("varreturningtype", 0.into()),
234 ("varnosyn", 1.into()),
235 ("varattnosyn", ordinal.into()),
236 ("location", (-1).into()),
237 ],
238 ),
239 ty: ty.clone(),
240 })
241 }
242
243 fn binary(&self, op: BinaryOp, lhs: &Expr, rhs: &Expr) -> Result<TypedNode, SQLError> {
244 let types = [self.expression_type(lhs)?, self.expression_type(rhs)?];
245 let [left, right, result] =
246 binary_operator_types(op, types[0].as_ref(), types[1].as_ref())?;
247 let identity = binary_operator_catalog_entry(op, [&left, &right])?;
248 let arguments = [
249 self.encode(lhs, Some(&left))?,
250 self.encode(rhs, Some(&right))?,
251 ];
252 Ok(operator_node(
253 identity.oid,
254 identity.function_oid,
255 arguments,
256 result,
257 ))
258 }
259
260 fn boolean(&self, operator: &str, args: &[Expr]) -> Result<TypedNode, SQLError> {
261 let args = args
262 .iter()
263 .map(|arg| {
264 self.encode(arg, Some(&ColumnType::Boolean))
265 .map(|value| value.node.into())
266 })
267 .collect::<Result<_, _>>()?;
268 Ok(TypedNode {
269 node: Node::new(
270 "BOOLEXPR",
271 [
272 ("boolop", Field::Atom(operator.into())),
273 ("args", Field::List(args)),
274 ("location", (-1).into()),
275 ],
276 ),
277 ty: ColumnType::Boolean,
278 })
279 }
280
281 fn function(
282 &self,
283 name: &str,
284 binding: Option<&FunctionBinding>,
285 arguments: &[Expr],
286 ) -> Result<TypedNode, SQLError> {
287 let types = arguments
288 .iter()
289 .map(|arg| self.expression_type(arg))
290 .collect::<Result<Vec<_>, _>>()?;
291 let routine = self.routines.resolve(name, binding, &types)?;
292 if routine.argument_types.len() != arguments.len() {
293 return Err(SQLError::Internal(
294 "catalog routine arity differs from bound arguments".into(),
295 ));
296 }
297 let arguments = arguments
298 .iter()
299 .zip(&routine.argument_types)
300 .map(|(arg, ty)| self.encode(arg, Some(ty)))
301 .collect::<Result<Vec<_>, _>>()?;
302 let collation = arguments
303 .iter()
304 .map(|argument| pg_type_collation_oid(&argument.ty))
305 .find(|oid| *oid != 0)
306 .unwrap_or(0);
307 Ok(TypedNode {
308 node: Node::new(
309 "FUNCEXPR",
310 [
311 ("funcid", routine.oid.into()),
312 ("funcresulttype", pg_type_oid(&routine.result_type).into()),
313 ("funcretset", false.into()),
314 ("funcvariadic", false.into()),
315 ("funcformat", 0.into()),
316 (
317 "funccollid",
318 pg_type_collation_oid(&routine.result_type).into(),
319 ),
320 ("inputcollid", collation.into()),
321 (
322 "args",
323 Field::List(arguments.into_iter().map(|arg| arg.node.into()).collect()),
324 ),
325 ("location", (-1).into()),
326 ],
327 ),
328 ty: routine.result_type,
329 })
330 }
331}
332
333fn operator_node(
334 oid: i64,
335 function_oid: i64,
336 arguments: impl IntoIterator<Item = TypedNode>,
337 result: ColumnType,
338) -> TypedNode {
339 let arguments: Vec<_> = arguments.into_iter().collect();
340 let input_collation = arguments
341 .iter()
342 .map(|argument| pg_type_collation_oid(&argument.ty))
343 .find(|oid| *oid != 0)
344 .unwrap_or(0);
345 TypedNode {
346 node: Node::new(
347 "OPEXPR",
348 [
349 ("opno", oid.into()),
350 ("opfuncid", function_oid.into()),
351 ("opresulttype", pg_type_oid(&result).into()),
352 ("opretset", false.into()),
353 ("opcollid", pg_type_collation_oid(&result).into()),
354 ("inputcollid", input_collation.into()),
355 (
356 "args",
357 Field::List(arguments.into_iter().map(|arg| arg.node.into()).collect()),
358 ),
359 ("location", (-1).into()),
360 ],
361 ),
362 ty: result,
363 }
364}