1use super::{
10 AggregateClassifier, Expr, FrameBound, OrderBy, QueryPlan, ScalarExpr, ScalarFrameBound,
11 ScalarOrder, ScalarWindowFrame, ScalarWindowSpec, WindowSpec,
12};
13use crate::schema::retention::CatalogRetentionError;
14use resources::{Control, Lowering, Result};
15use source::{Node, Source};
16use uqa_core::{
17 memory::{Budgeted, MemoryBudget},
18 CancellationToken,
19};
20
21mod binding;
22mod resources;
23mod source;
24mod window;
25
26impl super::ExpressionPlan {
27 pub fn lower_column_budgeted(
29 expression: &Expr,
30 budget: &MemoryBudget,
31 original: &CancellationToken,
32 invoking: &CancellationToken,
33 ) -> Result<Budgeted<ScalarExpr>> {
34 let mut lowering = Lowering {
35 control: Some(Control::new(budget, original, invoking)),
36 };
37 let scalar = lowering.expression(
38 Source::Borrowed(expression),
39 &super::NoRegisteredAggregates,
40 &mut Vec::new(),
41 )?;
42 lowering.finish(scalar)
43 }
44}
45
46pub(super) fn lower_scalar_expression(
47 expression: Expr,
48 aggregates: &dyn AggregateClassifier,
49 subqueries: &mut Vec<QueryPlan>,
50) -> ScalarExpr {
51 Lowering { control: None }
52 .expression(Source::Owned(expression), aggregates, subqueries)
53 .expect("owned lowering has no admission failure")
54}
55
56impl Lowering<'_> {
57 #[expect(
58 clippy::too_many_lines,
59 reason = "plan lowering preserves exhaustive variants and structural identities"
60 )]
61 fn expression(
62 &mut self,
63 expression: Source<'_, Expr>,
64 aggregates: &dyn AggregateClassifier,
65 subqueries: &mut Vec<QueryPlan>,
66 ) -> Result<ScalarExpr> {
67 self.check()?;
68 Ok(match expression.node() {
69 Node::Star => ScalarExpr::Star,
70 Node::QualifiedStar(name) => ScalarExpr::QualifiedStar(self.text(name)?),
71 Node::Default => ScalarExpr::Default,
72 Node::Column(name) => ScalarExpr::Column(self.text(name)?),
73 Node::QualifiedColumn { qualifier, column } => ScalarExpr::QualifiedColumn {
74 qualifier: self.text(qualifier)?,
75 column: self.text(column)?,
76 },
77 Node::InternalColumn(column) => ScalarExpr::InternalColumn(column),
78 Node::Literal(value) => ScalarExpr::Literal(self.value(value)?),
79 Node::TypedLiteral { value, ty } => ScalarExpr::TypedLiteral {
80 value: self.value(value)?,
81 ty: self.text(ty)?,
82 bound_type: None,
83 parameter_index: None,
84 },
85 Node::Param(index) => ScalarExpr::Param(index),
86 Node::Func {
87 name,
88 binding,
89 args,
90 distinct,
91 order_by,
92 filter,
93 } => ScalarExpr::Func {
94 name: self.text(name)?,
95 binding: binding.map(|binding| self.binding(binding)).transpose()?,
96 args: self.map(args, |this, argument| {
97 this.expression(argument, aggregates, subqueries)
98 })?,
99 distinct,
100 order_by: self.map(order_by, |this, order| {
101 this.order(order, aggregates, subqueries)
102 })?,
103 filter: filter
104 .map(|filter| self.child(filter, aggregates, subqueries))
105 .transpose()?,
106 },
107 Node::Array(items) => ScalarExpr::Array(self.map(items, |this, item| {
108 this.expression(item, aggregates, subqueries)
109 })?),
110 Node::Row(items) => ScalarExpr::Row(self.map(items, |this, item| {
111 this.expression(item, aggregates, subqueries)
112 })?),
113 Node::Binary { op, lhs, rhs } => ScalarExpr::Binary {
114 op,
115 lhs: self.child(lhs, aggregates, subqueries)?,
116 rhs: self.child(rhs, aggregates, subqueries)?,
117 },
118 Node::UnaryMinus(expression) => {
119 ScalarExpr::UnaryMinus(self.child(expression, aggregates, subqueries)?)
120 }
121 Node::Not(expression) => {
122 ScalarExpr::Not(self.child(expression, aggregates, subqueries)?)
123 }
124 Node::And(items) => ScalarExpr::And(self.map(items, |this, item| {
125 this.expression(item, aggregates, subqueries)
126 })?),
127 Node::Or(items) => ScalarExpr::Or(self.map(items, |this, item| {
128 this.expression(item, aggregates, subqueries)
129 })?),
130 Node::IsNull { expr, negated } => ScalarExpr::IsNull {
131 expr: self.child(expr, aggregates, subqueries)?,
132 negated,
133 },
134 Node::Between { expr, low, high } => ScalarExpr::Between {
135 expr: self.child(expr, aggregates, subqueries)?,
136 low: self.child(low, aggregates, subqueries)?,
137 high: self.child(high, aggregates, subqueries)?,
138 },
139 Node::InList {
140 expr,
141 list,
142 negated,
143 } => ScalarExpr::InList {
144 expr: self.child(expr, aggregates, subqueries)?,
145 list: self.map(list, |this, item| {
146 this.expression(item, aggregates, subqueries)
147 })?,
148 negated,
149 },
150 Node::WindowCall { name, args, spec } => ScalarExpr::WindowCall {
151 name: self.text(name)?,
152 args: self.map(args, |this, argument| {
153 this.expression(argument, aggregates, subqueries)
154 })?,
155 spec: self.window(spec, aggregates, subqueries)?,
156 },
157 Node::Case {
158 base,
159 when,
160 else_branch,
161 } => ScalarExpr::Case {
162 base: base
163 .map(|base| self.child(base, aggregates, subqueries))
164 .transpose()?,
165 when: self.map(when, |this, pair| {
166 let (condition, result) = pair.pair();
167 Ok((
168 this.expression(condition, aggregates, subqueries)?,
169 this.expression(result, aggregates, subqueries)?,
170 ))
171 })?,
172 else_branch: else_branch
173 .map(|branch| self.child(branch, aggregates, subqueries))
174 .transpose()?,
175 },
176 Node::Cast { expr, ty } => ScalarExpr::Cast {
177 expr: self.child(expr, aggregates, subqueries)?,
178 ty: self.text(ty)?,
179 },
180 Node::ScalarSubquery(query) => {
181 ScalarExpr::ScalarSubquery(self.query(query, aggregates, subqueries)?)
182 }
183 Node::Exists { body, negated } => ScalarExpr::Exists {
184 subquery: self.query(body, aggregates, subqueries)?,
185 negated,
186 },
187 Node::InSubquery {
188 expr,
189 body,
190 negated,
191 } => {
192 let expr = self.child(expr, aggregates, subqueries)?;
193 ScalarExpr::InSubquery {
194 expr,
195 subquery: self.query(body, aggregates, subqueries)?,
196 negated,
197 }
198 }
199 })
200 }
201
202 fn child(
203 &mut self,
204 expression: Source<'_, Box<Expr>>,
205 aggregates: &dyn AggregateClassifier,
206 subqueries: &mut Vec<QueryPlan>,
207 ) -> Result<Box<ScalarExpr>> {
208 self.boxed(|this| this.expression(expression.unbox(), aggregates, subqueries))
209 }
210
211 fn query(
212 &self,
213 query: Source<'_, Box<crate::ast::SelectStmt>>,
214 aggregates: &dyn AggregateClassifier,
215 subqueries: &mut Vec<QueryPlan>,
216 ) -> Result<usize> {
217 self.check()?;
218 let Source::Owned(query) = query else {
219 return Err(CatalogRetentionError::UnexpectedSubquery);
220 };
221 let id = subqueries.len();
222 subqueries.push(QueryPlan::lower_with(*query, aggregates));
223 Ok(id)
224 }
225}
226
227pub(crate) fn is_builtin_aggregate(name: &str) -> bool {
228 crate::ast::is_builtin_aggregate_function(name)
229}
230
231#[cfg(test)]
232mod tests;