uqa_execution/relational/
project.rs1use uqa_core::Value;
10
11use super::{
12 BackwardScanSupport, Batch, DefaultExpressionEvaluator, ExecResult, PhysicalOperator,
13 PhysicalScanDirection, RowSchema, SQLParam, ScalarExpr, SharedExpressionEvaluator,
14};
15use crate::batch::ProjectedSlot;
16
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
21pub enum ProjectionTarget {
22 Column(String),
23 Internal(uqa_sql::ast::InternalColumnRef),
24}
25
26impl From<String> for ProjectionTarget {
27 fn from(value: String) -> Self {
28 Self::Column(value)
29 }
30}
31
32impl From<&str> for ProjectionTarget {
33 fn from(value: &str) -> Self {
34 Self::Column(value.to_string())
35 }
36}
37
38pub struct Project<'a> {
42 child: Box<dyn PhysicalOperator + 'a>,
43 computed: Vec<ScalarExpr>,
44 evaluator: SharedExpressionEvaluator<'a>,
45 schema: RowSchema,
46 ordering: Vec<crate::PhysicalOrder>,
47}
48
49fn projection_layout(
50 input: &RowSchema,
51 projections: Vec<(ProjectionTarget, ScalarExpr)>,
52 evaluator: &SharedExpressionEvaluator<'_>,
53 pass_through: bool,
54) -> (RowSchema, Vec<ScalarExpr>) {
55 let mut projected = Vec::new();
56 let mut projected_internal = Vec::new();
57 let mut computed = Vec::new();
58 for (target, expression) in projections {
59 if let ScalarExpr::QualifiedStar(qualifier) = &expression {
60 let ProjectionTarget::Column(_) = target else {
61 unreachable!("an internal projection target cannot expand a qualified star");
62 };
63 if pass_through {
64 continue;
65 }
66 for (column, logical, _, ty) in input.qualified_star_position_layout(qualifier) {
67 if logical.is_some_and(|position| !evaluator.star_position_visible(input, position))
68 {
69 continue;
70 }
71 let slot = logical
72 .and_then(|logical| input.physical_slot(logical))
73 .or_else(|| {
74 input.physical_slot_for_identity(&crate::ColumnIdentity::qualified(
75 qualifier, &column,
76 ))
77 });
78 projected.push((column, ty, ProjectedSlot::Input(slot)));
79 }
80 continue;
81 }
82 if matches!(expression, ScalarExpr::Star) {
83 let ProjectionTarget::Column(_) = target else {
84 unreachable!("an internal projection target cannot expand a star");
85 };
86 if pass_through {
87 continue;
88 }
89 for (logical, column) in input.iter().enumerate() {
90 if !evaluator.star_position_visible(input, logical) {
91 continue;
92 }
93 projected.push((
94 input.public_name(logical).unwrap_or(column).to_string(),
95 input.column_type(logical).cloned(),
96 ProjectedSlot::Input(input.physical_slot(logical)),
97 ));
98 }
99 continue;
100 }
101
102 let ty = evaluator.expression_type(&expression, input).ok().flatten();
103 let source = if let Some(logical) = crate::order_expression_position(input, &expression) {
104 ProjectedSlot::Input(input.physical_slot(logical))
105 } else {
106 let position = computed.len();
107 computed.push(expression);
108 ProjectedSlot::Computed(position)
109 };
110 match target {
111 ProjectionTarget::Column(name) => projected.push((name, ty, source)),
112 ProjectionTarget::Internal(column) => {
113 projected_internal.push((column, ty, source));
114 }
115 }
116 }
117 let computed_count = computed.len();
118 (
119 RowSchema::project_with_sources(
120 input,
121 projected,
122 projected_internal,
123 computed_count,
124 pass_through,
125 ),
126 computed,
127 )
128}
129
130impl Project<'static> {
131 pub fn new(
132 child: Box<dyn PhysicalOperator>,
133 projections: Vec<(String, ScalarExpr)>,
134 params: Vec<SQLParam>,
135 ) -> Self {
136 Self::with_evaluator(
137 child,
138 projections,
139 DefaultExpressionEvaluator::shared(params),
140 )
141 }
142
143 pub fn with_targets(
144 child: Box<dyn PhysicalOperator>,
145 projections: Vec<(ProjectionTarget, ScalarExpr)>,
146 params: Vec<SQLParam>,
147 ) -> Self {
148 Self::with_target_evaluator(
149 child,
150 projections,
151 DefaultExpressionEvaluator::shared(params),
152 )
153 }
154
155 pub fn appending(
158 child: Box<dyn PhysicalOperator>,
159 projections: Vec<(String, ScalarExpr)>,
160 params: Vec<SQLParam>,
161 ) -> Self {
162 Self::appending_with_evaluator(
163 child,
164 projections,
165 DefaultExpressionEvaluator::shared(params),
166 )
167 }
168
169 pub fn appending_targets(
170 child: Box<dyn PhysicalOperator>,
171 projections: Vec<(ProjectionTarget, ScalarExpr)>,
172 params: Vec<SQLParam>,
173 ) -> Self {
174 Self::appending_target_evaluator(
175 child,
176 projections,
177 DefaultExpressionEvaluator::shared(params),
178 )
179 }
180}
181
182impl<'a> Project<'a> {
183 pub fn with_evaluator(
184 child: Box<dyn PhysicalOperator + 'a>,
185 projections: Vec<(String, ScalarExpr)>,
186 evaluator: SharedExpressionEvaluator<'a>,
187 ) -> Self {
188 Self::with_target_evaluator(
189 child,
190 projections
191 .into_iter()
192 .map(|(name, expression)| (ProjectionTarget::Column(name), expression))
193 .collect(),
194 evaluator,
195 )
196 }
197
198 pub fn with_target_evaluator(
199 child: Box<dyn PhysicalOperator + 'a>,
200 projections: Vec<(ProjectionTarget, ScalarExpr)>,
201 evaluator: SharedExpressionEvaluator<'a>,
202 ) -> Self {
203 let projections = projections
204 .into_iter()
205 .map(|(target, expression)| {
206 (
207 target,
208 evaluator.bind_type_introspection(expression, child.row_schema()),
209 )
210 })
211 .collect::<Vec<_>>();
212 let (schema, computed) =
213 projection_layout(child.row_schema(), projections, &evaluator, false);
214 Self {
215 child,
216 computed,
217 evaluator,
218 schema,
219 ordering: Vec::new(),
220 }
221 }
222
223 pub fn appending_with_evaluator(
224 child: Box<dyn PhysicalOperator + 'a>,
225 projections: Vec<(String, ScalarExpr)>,
226 evaluator: SharedExpressionEvaluator<'a>,
227 ) -> Self {
228 Self::appending_target_evaluator(
229 child,
230 projections
231 .into_iter()
232 .map(|(name, expression)| (ProjectionTarget::Column(name), expression))
233 .collect(),
234 evaluator,
235 )
236 }
237
238 pub fn appending_target_evaluator(
239 child: Box<dyn PhysicalOperator + 'a>,
240 projections: Vec<(ProjectionTarget, ScalarExpr)>,
241 evaluator: SharedExpressionEvaluator<'a>,
242 ) -> Self {
243 let projections = projections
244 .into_iter()
245 .map(|(target, expression)| {
246 (
247 target,
248 evaluator.bind_type_introspection(expression, child.row_schema()),
249 )
250 })
251 .collect::<Vec<_>>();
252 let ordering = child
253 .output_ordering()
254 .iter()
255 .take_while(|order| {
256 projections.iter().all(|(target, expression)| {
257 let ProjectionTarget::Column(name) = target else {
258 return true;
259 };
260 child
261 .row_schema()
262 .columns()
263 .iter()
264 .position(|column| column == name)
265 != Some(order.position)
266 || matches!(expression, ScalarExpr::Star | ScalarExpr::QualifiedStar(_))
267 || crate::order_expression_position(child.row_schema(), expression)
268 == Some(order.position)
269 })
270 })
271 .cloned()
272 .collect();
273 let (schema, computed) =
274 projection_layout(child.row_schema(), projections, &evaluator, true);
275 Self {
276 child,
277 computed,
278 evaluator,
279 schema,
280 ordering,
281 }
282 }
283
284 fn project_batch(&self, batch: Batch) -> ExecResult<Batch> {
285 if self.computed.is_empty() {
286 return Ok(Batch::from_physical_rows(self.schema.clone(), batch.rows));
287 }
288 let mut out = Vec::with_capacity(batch.rows.len());
289 for row in batch.rows {
290 let values = self
291 .computed
292 .iter()
293 .map(|expression| {
294 self.evaluator
295 .evaluate_physical(expression, &batch.schema, &row)
296 })
297 .collect::<ExecResult<Vec<Value>>>()?;
298 out.push(row.append_values(values));
299 }
300 Ok(Batch::from_physical_rows(self.schema.clone(), out))
301 }
302}
303
304impl PhysicalOperator for Project<'_> {
305 fn row_schema(&self) -> &RowSchema {
306 &self.schema
307 }
308
309 fn output_ordering(&self) -> &[crate::PhysicalOrder] {
310 &self.ordering
311 }
312
313 fn backward_scan_support(&self) -> BackwardScanSupport {
314 let child = self.child.backward_scan_support();
315 if child == BackwardScanSupport::Native || self.computed.is_empty() {
316 child
317 } else {
318 BackwardScanSupport::Unsupported
319 }
320 }
321
322 fn open(&mut self) -> ExecResult<()> {
323 self.child.open()
324 }
325
326 fn next(&mut self) -> ExecResult<Option<Batch>> {
327 let Some(batch) = self.child.next()? else {
328 return Ok(None);
329 };
330 self.project_batch(batch).map(Some)
331 }
332
333 fn next_direction(&mut self, direction: PhysicalScanDirection) -> ExecResult<Option<Batch>> {
334 let Some(batch) = self.child.next_direction(direction)? else {
335 return Ok(None);
336 };
337 self.project_batch(batch).map(Some)
338 }
339
340 fn rewind(&mut self) -> ExecResult<()> {
341 self.child.rewind()
342 }
343
344 fn close(&mut self) -> ExecResult<()> {
345 self.child.close()
346 }
347}
348
349