akar_processor/processor/mapper/
map_projection.rs1use super::ExecutionContext;
2use crate::expression_evaluator::ExpressionEvaluator;
3use crate::physical_operator::*;
4use crate::processor::projection_helper::resolve_projection_column_index;
5use akar_common::error::ProcessorError;
6use akar_common::vector::DataChunk;
7use akar_parser::ast::Expression;
8use akar_planner::logical_operator::LogicalOperator;
9use std::sync::{Arc, Mutex};
10
11fn projection_needs_expression_eval(expr: &Expression) -> bool {
12 matches!(
13 expr,
14 Expression::FunctionCall(_, _)
15 | Expression::Constant(_)
16 | Expression::BinaryOp(_, _, _)
17 | Expression::UnaryOp(_, _)
18 | Expression::List(_)
19 | Expression::Map(_)
20 | Expression::Parameter(_)
21 | Expression::ExistsSubquery(_)
22 | Expression::ListPredicate { .. }
23 )
24}
25
26fn expression_field_name(alias: Option<&str>, expr: &Expression) -> String {
30 if let Some(a) = alias {
31 return a.to_string();
32 }
33 match expr {
34 Expression::PropertyAccess(obj, prop) => {
35 if let Expression::Variable(var) = &**obj {
36 format!("{var}.{prop}")
37 } else {
38 prop.clone()
39 }
40 }
41 Expression::Variable(name) => name.clone(),
42 _ => String::new(),
43 }
44}
45
46fn resolve_sort_keys(
61 sort_keys: &[(Expression, bool)],
62 input: &[DataChunk],
63 ctx: &mut ExecutionContext,
64) -> Result<(Vec<(u32, bool)>, Vec<DataChunk>, usize), ProcessorError> {
65 let base_cols = input.first().map(|c| c.num_fields()).unwrap_or(0);
66 let mut resolved = Vec::with_capacity(sort_keys.len());
67 let mut computed: Vec<Expression> = Vec::new();
68 for (expr, asc) in sort_keys.iter() {
69 let col = input.first().and_then(|c| resolve_projection_column_index(expr, c));
70 match col {
71 Some(idx) => resolved.push((idx as u32, *asc)),
72 None => {
73 resolved.push(((base_cols + computed.len()) as u32, *asc));
74 computed.push(expr.clone());
75 }
76 }
77 }
78 if computed.is_empty() {
79 return Ok((resolved, input.to_vec(), 0));
80 }
81 let registry = ctx
82 .function_registry
83 .clone()
84 .ok_or_else(|| "No function registry available for computed ORDER BY key".to_string())?;
85 let mut eval = ExpressionEvaluator::new(registry);
86 if let Some(ref seq_fn) = ctx.sequence_fn {
87 eval = eval.with_sequence_fn(seq_fn.clone());
88 }
89 if let Some(ref subquery_fn) = ctx.subquery_fn {
90 eval = eval.with_subquery_fn(subquery_fn.clone());
91 }
92 let mut augmented = Vec::with_capacity(input.len());
93 for chunk in input {
94 let mut fields = chunk.fields.clone();
95 let mut field_types = chunk.field_types.clone();
96 for expr in &computed {
97 let vv = eval.evaluate_arrow(expr, chunk)?;
98 fields.push(vv.array);
99 field_types.push(vv.physical_type);
100 }
101 augmented.push(DataChunk {
102 fields,
103 field_types,
104 size: chunk.size,
105 field_names: chunk.field_names.clone(),
106 sel_vector: None,
107 });
108 }
109 Ok((resolved, augmented, computed.len()))
110}
111
112fn strip_sort_columns(chunks: &mut [DataChunk], extra: usize) {
115 if extra == 0 {
116 return;
117 }
118 for chunk in chunks {
119 let keep = chunk.fields.len().saturating_sub(extra);
120 chunk.fields.truncate(keep);
121 chunk.field_types.truncate(keep);
122 chunk.field_names.truncate(keep);
123 }
124}
125
126fn resolve_projection_column_expand(expr: &Expression, chunk: &DataChunk) -> Option<Vec<usize>> {
135 match expr {
136 Expression::PropertyAccess(obj, prop) => {
137 let col_name = if let Expression::Variable(var) = &**obj {
138 format!("{}.{}", var, prop)
139 } else {
140 prop.clone()
141 };
142 if !chunk.field_names.is_empty() {
143 if let Some(idx) = chunk.field_names.iter().position(|n| n == &col_name || n == prop) {
144 return Some(vec![idx]);
145 }
146 }
147 if let Expression::Variable(var) = &**obj
148 && let Ok(idx) = var.parse::<usize>()
149 {
150 return Some(vec![idx]);
151 }
152 None
153 }
154 Expression::Variable(name) => {
155 if let Ok(idx) = name.parse::<usize>() {
156 return Some(vec![idx]);
157 }
158 if !chunk.field_names.is_empty() {
159 if let Some(idx) = chunk.field_names.iter().position(|n| n == name) {
160 return Some(vec![idx]);
161 }
162 let prefix = format!("{}.", name);
163 let idxs: Vec<usize> = chunk
164 .field_names
165 .iter()
166 .enumerate()
167 .filter(|(_, n)| n.starts_with(&prefix))
168 .map(|(i, _)| i)
169 .collect();
170 if !idxs.is_empty() {
171 return Some(idxs);
172 }
173 }
174 None
175 }
176 _ => None,
177 }
178}
179
180fn property_base_is_chunk_column(expr: &Expression, chunk: &DataChunk) -> bool {
184 if let Expression::PropertyAccess(base, _) = expr
185 && let Expression::Variable(var) = &**base
186 && !chunk.field_names.is_empty()
187 {
188 return chunk.field_names.iter().any(|n| n == var);
189 }
190 false
191}
192
193pub fn map_and_execute_projection(
194 op: &LogicalOperator,
195 current_input: Vec<DataChunk>,
196 ctx: &mut ExecutionContext,
197) -> Result<Vec<DataChunk>, ProcessorError> {
198 match op {
199 LogicalOperator::Projection(p) => {
200 let input = if p.children.is_empty() {
201 current_input
202 } else {
203 ctx.execute_children(&p.children)?
204 };
205
206 let result = if p.expressions.is_empty() {
207 input
208 } else {
209 let needs_eval = p
217 .expressions
218 .iter()
219 .any(|be| projection_needs_expression_eval(&be.expression))
220 || input.first().is_some_and(|chunk| {
221 p.expressions.iter().any(|be| {
222 matches!(
223 &be.expression,
224 Expression::PropertyAccess(_, _) | Expression::Variable(_)
225 ) && resolve_projection_column_expand(&be.expression, chunk).is_none()
226 && property_base_is_chunk_column(&be.expression, chunk)
227 })
228 });
229
230 if needs_eval {
231 let registry = ctx
232 .function_registry
233 .clone()
234 .ok_or_else(|| "No function registry available for expression projection".to_string())?;
235
236 let mut eval = ExpressionEvaluator::new(registry);
237 if let Some(ref seq_fn) = ctx.sequence_fn {
238 eval = eval.with_sequence_fn(seq_fn.clone());
239 }
240 if let Some(ref subquery_fn) = ctx.subquery_fn {
241 eval = eval.with_subquery_fn(subquery_fn.clone());
242 }
243
244 let mut output = Vec::with_capacity(input.len());
245 for chunk in input {
246 let mut fields = Vec::with_capacity(p.expressions.len());
247 let mut field_types = Vec::with_capacity(p.expressions.len());
248 for be in &p.expressions {
249 let result_vec = eval.evaluate_arrow(&be.expression, &chunk)?;
253 let pt = result_vec.physical_type;
254 let arr = result_vec.array.clone();
255 fields.push(arr);
256 field_types.push(pt);
257 }
258 let size = fields.first().map(|f| f.len()).unwrap_or(chunk.size);
259 let field_names = p
260 .expressions
261 .iter()
262 .map(|be| expression_field_name(be.alias.as_deref(), &be.expression))
263 .collect();
264 output.push(DataChunk {
265 fields,
266 field_types,
267 size,
268 field_names,
269 sel_vector: None,
270 });
271 }
272 output
273 } else {
274 let column_indices: Vec<usize> = if let Some(first_chunk) = input.first() {
275 let mut all: Option<Vec<usize>> = Some(Vec::new());
276 for be in &p.expressions {
277 match resolve_projection_column_expand(&be.expression, first_chunk) {
278 Some(idxs) => all.as_mut().expect("all is Some").extend(idxs),
279 None => {
280 all = None;
281 break;
282 }
283 }
284 }
285 all.unwrap_or_default()
286 } else {
287 Vec::new()
288 };
289 let column_indices = if !column_indices.is_empty() {
290 column_indices
291 } else {
292 (0..p.expressions.len()).collect()
293 };
294 let rename = column_indices.len() == p.expressions.len() && !p.expressions.is_empty();
295 let proj = PhysicalProjection { column_indices };
296 let mut result = proj.execute(input)?;
297 if rename {
301 let names: Vec<String> = p
302 .expressions
303 .iter()
304 .map(|be| expression_field_name(be.alias.as_deref(), &be.expression))
305 .collect();
306 for chunk in &mut result {
307 if chunk.fields.len() == names.len() {
308 chunk.field_names = names.clone();
309 }
310 }
311 }
312 result
313 }
314 };
315 Ok(result)
316 }
317 LogicalOperator::Filter(f) => {
318 let evaluator = ctx.function_registry.clone().map(|reg| {
319 let mut eval = ExpressionEvaluator::new(reg);
320 if let Some(ref seq_fn) = ctx.sequence_fn {
321 eval = eval.with_sequence_fn(seq_fn.clone());
322 }
323 if let Some(ref subquery_fn) = ctx.subquery_fn {
324 eval = eval.with_subquery_fn(subquery_fn.clone());
325 }
326 Arc::new(Mutex::new(eval))
327 });
328 let filter = if let Some(eval) = evaluator {
329 PhysicalFilter::with_evaluator(f.expression.clone(), eval)
330 } else {
331 PhysicalFilter::new(f.expression.clone())
332 };
333 let result = filter.execute(current_input)?;
334 Ok(result)
335 }
336 LogicalOperator::Limit(l) => {
337 let limit = PhysicalLimit {
338 limit: l.limit,
339 offset: l.offset,
340 };
341 let result = limit.execute(current_input)?;
342 Ok(result)
343 }
344 LogicalOperator::TopK(tk) => {
345 let (sort_keys, augmented, extra) = resolve_sort_keys(&tk.sort_keys, ¤t_input, ctx)?;
346 let topk = PhysicalTopK {
347 sort_keys,
348 limit: tk.limit,
349 offset: tk.offset,
350 };
351 let mut result = topk.execute(augmented)?;
352 strip_sort_columns(&mut result, extra);
353 Ok(result)
354 }
355 LogicalOperator::OrderBy(o) => {
356 let (sort_keys, augmented, extra) = resolve_sort_keys(&o.sort_keys, ¤t_input, ctx)?;
357 let order = PhysicalOrderBy { sort_keys };
358 let mut result = order.execute(augmented)?;
359 strip_sort_columns(&mut result, extra);
360 Ok(result)
361 }
362 LogicalOperator::Flatten(f) => {
363 let input = if f.children.is_empty() {
364 current_input
365 } else {
366 ctx.execute_children(&f.children)?
367 };
368 let flatten = PhysicalFlatten::new(f.group_pos);
369 flatten.execute(input)
370 }
371 LogicalOperator::Unwind(uw) => {
372 let unwind = PhysicalUnwind {
373 expression: uw.expression.clone(),
374 variable: uw.variable.clone(),
375 };
376 let result = unwind.execute(current_input)?;
377 Ok(result)
378 }
379 LogicalOperator::Partitioner(p) => {
380 const MORSEL_SIZE: usize = 1024;
381 let partitioner = Partitioner::new(MORSEL_SIZE);
382 let morsels = partitioner.execute(current_input)?;
383
384 let mut results = Vec::new();
385 for _morsel in morsels {
386 let child_result = ctx.execute_children(&p.children)?;
387 results.extend(child_result);
388 }
389 Ok(results)
390 }
391 _ => Err(format!("Not a projection/filter operator: {:?}", op).into()),
392 }
393}