uqa_execution/relational/
project.rs1use uqa_core::Value;
10
11use super::{
12 Batch, DefaultExpressionEvaluator, ExecResult, PhysicalOperator, RowSchema, SQLParam,
13 ScalarExpr, SharedExpressionEvaluator,
14};
15use crate::batch::ProjectedSlot;
16
17pub struct Project<'a> {
21 child: Box<dyn PhysicalOperator + 'a>,
22 computed: Vec<ScalarExpr>,
23 evaluator: SharedExpressionEvaluator<'a>,
24 schema: RowSchema,
25 ordering: Vec<crate::PhysicalOrder>,
26}
27
28fn projection_layout(
29 input: &RowSchema,
30 projections: Vec<(String, ScalarExpr)>,
31 evaluator: &SharedExpressionEvaluator<'_>,
32 pass_through: bool,
33) -> (RowSchema, Vec<ScalarExpr>) {
34 let mut projected = Vec::new();
35 let mut computed = Vec::new();
36 for (name, expression) in projections {
37 if let ScalarExpr::QualifiedStar(qualifier) = &expression {
38 if pass_through {
39 continue;
40 }
41 for (column, logical, _, ty) in input.qualified_star_position_layout(qualifier) {
42 if !evaluator.star_column_visible(&column) {
43 continue;
44 }
45 let slot = logical
46 .and_then(|logical| input.physical_slot(logical))
47 .or_else(|| {
48 input.physical_slot_for_identity(&crate::ColumnIdentity::qualified(
49 qualifier, &column,
50 ))
51 });
52 projected.push((column, ty, ProjectedSlot::Input(slot)));
53 }
54 continue;
55 }
56 if matches!(expression, ScalarExpr::Star) {
57 if pass_through {
58 continue;
59 }
60 for (logical, column) in input.iter().enumerate() {
61 if !evaluator.star_column_visible(column) {
62 continue;
63 }
64 projected.push((
65 input.public_name(logical).unwrap_or(column).to_string(),
66 input.column_type(logical).cloned(),
67 ProjectedSlot::Input(input.physical_slot(logical)),
68 ));
69 }
70 continue;
71 }
72
73 let ty = evaluator.expression_type(&expression, input).ok().flatten();
74 if let Some(logical) = crate::order_expression_position(input, &expression) {
75 projected.push((name, ty, ProjectedSlot::Input(input.physical_slot(logical))));
76 } else {
77 projected.push((name, ty, ProjectedSlot::Computed));
78 computed.push(expression);
79 }
80 }
81 (
82 RowSchema::project_with_sources(input, projected, pass_through),
83 computed,
84 )
85}
86
87impl Project<'static> {
88 pub fn new(
89 child: Box<dyn PhysicalOperator>,
90 projections: Vec<(String, ScalarExpr)>,
91 params: Vec<SQLParam>,
92 ) -> Self {
93 Self::with_evaluator(
94 child,
95 projections,
96 DefaultExpressionEvaluator::shared(params),
97 )
98 }
99
100 pub fn appending(
103 child: Box<dyn PhysicalOperator>,
104 projections: Vec<(String, ScalarExpr)>,
105 params: Vec<SQLParam>,
106 ) -> Self {
107 Self::appending_with_evaluator(
108 child,
109 projections,
110 DefaultExpressionEvaluator::shared(params),
111 )
112 }
113}
114
115impl<'a> Project<'a> {
116 pub fn with_evaluator(
117 child: Box<dyn PhysicalOperator + 'a>,
118 projections: Vec<(String, ScalarExpr)>,
119 evaluator: SharedExpressionEvaluator<'a>,
120 ) -> Self {
121 let params = evaluator.parameters();
122 let projections = projections
123 .into_iter()
124 .map(|(name, expression)| {
125 (
126 name,
127 crate::bind_type_introspection(expression, child.row_schema(), params),
128 )
129 })
130 .collect::<Vec<_>>();
131 let (schema, computed) =
132 projection_layout(child.row_schema(), projections, &evaluator, false);
133 Self {
134 child,
135 computed,
136 evaluator,
137 schema,
138 ordering: Vec::new(),
139 }
140 }
141
142 pub fn appending_with_evaluator(
143 child: Box<dyn PhysicalOperator + 'a>,
144 projections: Vec<(String, ScalarExpr)>,
145 evaluator: SharedExpressionEvaluator<'a>,
146 ) -> Self {
147 let params = evaluator.parameters();
148 let projections = projections
149 .into_iter()
150 .map(|(name, expression)| {
151 (
152 name,
153 crate::bind_type_introspection(expression, child.row_schema(), params),
154 )
155 })
156 .collect::<Vec<_>>();
157 let ordering = child
158 .output_ordering()
159 .iter()
160 .take_while(|order| {
161 projections.iter().all(|(name, expression)| {
162 child
163 .row_schema()
164 .columns()
165 .iter()
166 .position(|column| column == name)
167 != Some(order.position)
168 || matches!(expression, ScalarExpr::Star | ScalarExpr::QualifiedStar(_))
169 || crate::order_expression_position(child.row_schema(), expression)
170 == Some(order.position)
171 })
172 })
173 .cloned()
174 .collect();
175 let (schema, computed) =
176 projection_layout(child.row_schema(), projections, &evaluator, true);
177 Self {
178 child,
179 computed,
180 evaluator,
181 schema,
182 ordering,
183 }
184 }
185}
186
187impl PhysicalOperator for Project<'_> {
188 fn row_schema(&self) -> &RowSchema {
189 &self.schema
190 }
191
192 fn output_ordering(&self) -> &[crate::PhysicalOrder] {
193 &self.ordering
194 }
195
196 fn open(&mut self) -> ExecResult<()> {
197 self.child.open()
198 }
199
200 fn next(&mut self) -> ExecResult<Option<Batch>> {
201 let Some(batch) = self.child.next()? else {
202 return Ok(None);
203 };
204 if self.computed.is_empty() {
205 return Ok(Some(Batch::from_physical_rows(
206 self.schema.clone(),
207 batch.rows,
208 )));
209 }
210 let mut out = Vec::with_capacity(batch.rows.len());
211 for row in batch.rows {
212 let values = self
213 .computed
214 .iter()
215 .map(|expression| {
216 self.evaluator
217 .evaluate_physical(expression, &batch.schema, &row)
218 })
219 .collect::<ExecResult<Vec<Value>>>()?;
220 out.push(row.append_values(values));
221 }
222 Ok(Some(Batch::from_physical_rows(self.schema.clone(), out)))
223 }
224
225 fn close(&mut self) -> ExecResult<()> {
226 self.child.close()
227 }
228}
229
230