1use crate::executor::expression::evaluator::operations::{
4 BinaryOperationEvaluator, UnaryOperationEvaluator,
5};
6use crate::executor::expression::evaluator::ExpressionEvaluator;
7use crate::executor::expression::ExpressionError;
8use crate::executor::streaming::chunk::core::DataChunk;
9use crate::executor::streaming::context::BorrowedRowContext;
10use crate::executor::streaming::slot::SlotId;
11use crate::executor::streaming::subquery::EvalEnv;
12use linkrs_core::types::expr::Expression;
13use linkrs_core::Value;
14use std::collections::HashMap;
15
16use super::kind::typed_literal_batch;
17use super::typed::{
18 typed_binary_batch, typed_cast_batch, typed_column_batch, typed_unary_batch, TypedBatch,
19};
20
21impl DataChunk {
22 fn eval_len(&self) -> usize {
28 if self.rows.is_empty() {
29 self.typed_len().unwrap_or(0)
30 } else {
31 self.rows.len()
32 }
33 }
34
35 pub fn evaluate_expressions(
36 &mut self,
37 expressions: &[Expression],
38 env: Option<&EvalEnv>,
39 ) -> Result<Vec<Vec<Value>>, ExpressionError> {
40 if self.eval_len() == 0 {
41 return Ok(vec![Vec::new(); expressions.len()]);
42 }
43 if expressions
44 .iter()
45 .all(|e| matches!(e, Expression::Variable(_)))
46 {
47 let mut columns = Vec::with_capacity(expressions.len());
48 for expr in expressions {
49 if let Expression::Variable(name) = expr {
50 let slot = self
51 .layout
52 .slot_id(name)
53 .ok_or_else(|| ExpressionError::undefined_variable(name))?;
54 let col = self
55 .get_column(slot)
56 .ok_or_else(|| ExpressionError::undefined_variable(name))?;
57 columns.push(col);
58 }
59 }
60 for _ in 0..expressions.len() {
61 self.count_columnar(true);
62 }
63 return Ok(columns);
64 }
65 if expressions
66 .iter()
67 .all(|e| matches!(e, Expression::Literal(_)))
68 {
69 let mut columns = Vec::with_capacity(expressions.len());
70 let len = self.eval_len();
71 for expr in expressions {
72 if let Expression::Literal(v) = expr {
73 columns.push(vec![v.clone(); len]);
74 }
75 }
76 for _ in 0..expressions.len() {
77 self.count_columnar(true);
78 }
79 return Ok(columns);
80 }
81 let mut results = Vec::with_capacity(expressions.len());
82 for expr in expressions {
83 results.push(self.evaluate_expression(expr, env)?);
84 }
85 Ok(results)
86 }
87
88 pub fn try_evaluate_expressions_typed(
98 &mut self,
99 expressions: &[Expression],
100 env: Option<&EvalEnv>,
101 ) -> Result<Option<Vec<Vec<Value>>>, ExpressionError> {
102 if self.eval_len() == 0 {
103 return Ok(Some(vec![Vec::new(); expressions.len()]));
104 }
105 let mut out = Vec::with_capacity(expressions.len());
106 for expr in expressions {
107 match self.try_eval_typed_batch(expr, env)? {
108 Some(batch) => out.push(batch.into_values()),
109 None => return Ok(None),
110 }
111 }
112 Ok(Some(out))
113 }
114
115 pub fn evaluate_expression(
116 &mut self,
117 expression: &Expression,
118 env: Option<&EvalEnv>,
119 ) -> Result<Vec<Value>, ExpressionError> {
120 if self.eval_len() == 0 {
121 return Ok(Vec::new());
122 }
123 if let Ok((result, typed_hit)) = self.try_evaluate_columnar(expression, env) {
124 self.count_columnar(true);
125 if typed_hit {
126 self.count_typed_hit();
127 }
128 return Ok(result);
129 }
130 self.count_columnar(false);
131 debug_assert!(
132 !self.columnar_promise_holds(expression),
133 "flat column promise broken: expression {:?} should have hit the \
134 columnar path but fell back to per-row evaluation",
135 expression
136 );
137 self.evaluate_expression_per_row(expression, env)
138 }
139
140 pub fn evaluate_expression_visible(
141 &mut self,
142 expression: &Expression,
143 env: Option<&EvalEnv>,
144 ) -> Result<Vec<Value>, ExpressionError> {
145 let Some(sel) = self.selection().map(|s| s.to_vec()) else {
146 return self.evaluate_expression(expression, env);
147 };
148 if let Expression::Literal(value) = expression {
149 self.count_columnar(true);
150 self.count_selection_pushed();
151 return Ok(vec![value.clone(); sel.len()]);
152 }
153 if let Expression::Parameter(name) = expression {
154 let value = env
155 .and_then(|env| env.params.as_ref())
156 .and_then(|p| p.get(name).cloned())
157 .ok_or_else(|| ExpressionError::undefined_parameter(name))?;
158 self.count_columnar(true);
159 self.count_selection_pushed();
160 return Ok(vec![value; sel.len()]);
161 }
162 if let Expression::SessionVariable(name) = expression {
163 let value = env
164 .and_then(|env| env.session_variables.as_ref())
165 .and_then(|v| v.get(name).cloned())
166 .ok_or_else(|| {
167 ExpressionError::type_error(format!(
168 "Session variable `{}` is not defined",
169 name
170 ))
171 })?;
172 self.count_columnar(true);
173 self.count_selection_pushed();
174 return Ok(vec![value; sel.len()]);
175 }
176 let slot: Option<SlotId> = match expression {
177 Expression::Variable(name) => self.layout.slot_id(name),
178 Expression::Property { object, property }
179 if matches!(object.as_ref(), Expression::Variable(_)) =>
180 {
181 if let Expression::Variable(var) = object.as_ref() {
182 self.layout.slot_id(&format!("{}.{}", var, property))
183 } else {
184 None
185 }
186 }
187 _ => None,
188 };
189 if let Some(slot) = slot {
190 self.ensure_typed_columns();
193 let mut out = Vec::with_capacity(sel.len());
194 for &i in &sel {
195 match self.get_typed_by_slot(i, slot) {
196 Some(v) => out.push(v),
197 None => return Err(ExpressionError::undefined_variable("column slot")),
198 }
199 }
200 self.count_columnar(true);
201 self.count_selection_pushed();
202 return Ok(out);
203 }
204 let layout = self.get_layout();
205 let mut out = Vec::with_capacity(sel.len());
206 for &i in &sel {
207 let row = &self.rows[i];
208 let mut ctx = match env {
209 Some(env) => BorrowedRowContext::with_env(row, layout.clone(), env),
210 None => BorrowedRowContext::new(row, layout.clone()),
211 };
212 out.push(ExpressionEvaluator::evaluate(expression, &mut ctx)?);
213 }
214 self.count_columnar(false);
215 self.count_selection_pushed();
216 Ok(out)
217 }
218
219 fn try_evaluate_columnar(
220 &mut self,
221 expression: &Expression,
222 env: Option<&EvalEnv>,
223 ) -> Result<(Vec<Value>, bool), ExpressionError> {
224 let mut col_cache: HashMap<String, Vec<Value>> = HashMap::new();
225 self.collect_variables(expression, &mut col_cache);
226 let mut typed_hit = false;
227 let result = self.eval_with_cache(expression, &col_cache, env, &mut typed_hit)?;
228 Ok((result, typed_hit))
229 }
230
231 fn collect_variables(
232 &mut self,
233 expr: &Expression,
234 col_cache: &mut HashMap<String, Vec<Value>>,
235 ) {
236 match expr {
237 Expression::Variable(name) => {
238 if !col_cache.contains_key(name) {
239 if let Some(slot) = self.layout.slot_id(name) {
240 if let Some(col) = self.get_column(slot) {
241 col_cache.insert(name.clone(), col);
242 }
243 }
244 }
245 }
246 Expression::Binary { left, right, .. } => {
247 self.collect_variables(left, col_cache);
248 self.collect_variables(right, col_cache);
249 }
250 Expression::Unary { operand, .. } => {
251 self.collect_variables(operand, col_cache);
252 }
253 Expression::TypeCast { expression, .. } => {
254 self.collect_variables(expression, col_cache);
255 }
256 _ => {}
257 }
258 }
259
260 fn eval_with_cache(
261 &mut self,
262 expression: &Expression,
263 col_cache: &HashMap<String, Vec<Value>>,
264 env: Option<&EvalEnv>,
265 typed_used: &mut bool,
266 ) -> Result<Vec<Value>, ExpressionError> {
267 if let Some(batch) = self.try_eval_typed_batch(expression, env)? {
268 *typed_used = true;
269 return Ok(batch.into_values());
270 }
271 match expression {
272 Expression::Literal(v) => Ok(vec![v.clone(); self.rows.len()]),
273
274 Expression::Variable(name) => {
275 if let Some(col) = col_cache.get(name) {
276 return Ok(col.clone());
277 }
278 let slot = self
279 .layout
280 .slot_id(name)
281 .ok_or_else(|| ExpressionError::undefined_variable(name))?;
282 self.get_column(slot)
283 .ok_or_else(|| ExpressionError::undefined_variable(name))
284 }
285
286 Expression::Parameter(name) => {
287 let val = env
288 .and_then(|env| env.params.as_ref())
289 .and_then(|p| p.get(name).cloned())
290 .ok_or_else(|| ExpressionError::undefined_parameter(name))?;
291 Ok(vec![val; self.eval_len()])
292 }
293
294 Expression::SessionVariable(name) => {
295 let val = env
296 .and_then(|env| env.session_variables.as_ref())
297 .and_then(|v| v.get(name).cloned())
298 .ok_or_else(|| {
299 ExpressionError::type_error(format!(
300 "Session variable `{}` is not defined",
301 name
302 ))
303 })?;
304 Ok(vec![val; self.eval_len()])
305 }
306
307 Expression::Unary { op, operand } => {
308 let values = self.eval_with_cache(operand, col_cache, env, typed_used)?;
309 values
310 .into_iter()
311 .map(|v| UnaryOperationEvaluator::evaluate(op, &v))
312 .collect()
313 }
314
315 Expression::Binary { left, op, right } => {
316 let left_values = self.eval_with_cache(left, col_cache, env, typed_used)?;
317 let right_values = self.eval_with_cache(right, col_cache, env, typed_used)?;
318 left_values
319 .into_iter()
320 .zip(right_values)
321 .map(|(l, r)| BinaryOperationEvaluator::evaluate(&l, op, &r))
322 .collect()
323 }
324
325 Expression::TypeCast {
326 expression,
327 target_type,
328 } => {
329 let values = self.eval_with_cache(expression, col_cache, env, typed_used)?;
330 values
331 .into_iter()
332 .map(|v| ExpressionEvaluator::eval_type_cast(&v, target_type))
333 .collect()
334 }
335
336 Expression::Property { object, property } => {
337 if let Expression::Variable(var_name) = object.as_ref() {
338 let compound = format!("{}.{}", var_name, property);
339 if let Some(slot) = self.layout.slot_id(&compound) {
340 if let Some(col) = self.get_column(slot) {
341 return Ok(col);
342 }
343 }
344 return Err(ExpressionError::type_error(
345 "Property access requires per-row evaluation",
346 ));
347 }
348 Err(ExpressionError::type_error(
349 "Property access requires per-row evaluation",
350 ))
351 }
352
353 _ => Err(ExpressionError::type_error(
354 "Expression requires per-row evaluation",
355 )),
356 }
357 }
358
359 fn evaluate_expression_per_row(
360 &self,
361 expression: &Expression,
362 env: Option<&EvalEnv>,
363 ) -> Result<Vec<Value>, ExpressionError> {
364 let layout = self.get_layout();
365 let mut results = Vec::with_capacity(self.rows.len());
366 for row in &self.rows {
367 let mut ctx = match env {
368 Some(env) => BorrowedRowContext::with_env(row, layout.clone(), env),
369 None => BorrowedRowContext::new(row, layout.clone()),
370 };
371 results.push(ExpressionEvaluator::evaluate(expression, &mut ctx)?);
372 }
373 Ok(results)
374 }
375
376 fn try_eval_typed_batch(
379 &mut self,
380 expression: &Expression,
381 env: Option<&EvalEnv>,
382 ) -> Result<Option<TypedBatch>, ExpressionError> {
383 match expression {
384 Expression::Literal(v) => Ok(typed_literal_batch(v, self.eval_len())),
385 Expression::Parameter(name) => {
386 let val = env
387 .and_then(|env| env.params.as_ref())
388 .and_then(|p| p.get(name).cloned())
389 .ok_or_else(|| ExpressionError::undefined_parameter(name))?;
390 Ok(typed_literal_batch(&val, self.eval_len()))
391 }
392 Expression::SessionVariable(name) => {
393 let val = env
394 .and_then(|env| env.session_variables.as_ref())
395 .and_then(|v| v.get(name).cloned())
396 .ok_or_else(|| {
397 ExpressionError::type_error(format!(
398 "Session variable `{}` is not defined",
399 name
400 ))
401 })?;
402 Ok(typed_literal_batch(&val, self.eval_len()))
403 }
404 Expression::Variable(name) => {
405 let slot = match self.layout.slot_id(name) {
406 Some(slot) => slot,
407 None => return Ok(None),
408 };
409 self.ensure_typed_columns();
412 Ok(self.typed_column(slot).and_then(typed_column_batch))
413 }
414 Expression::Property { object, property } => {
415 let Expression::Variable(var_name) = object.as_ref() else {
420 return Ok(None);
421 };
422 let compound = format!("{}.{}", var_name, property);
423 let slot = match self.layout.slot_id(&compound) {
424 Some(slot) => slot,
425 None => return Ok(None),
426 };
427 self.ensure_typed_columns();
428 Ok(self.typed_column(slot).and_then(typed_column_batch))
429 }
430 Expression::Unary { op, operand } => {
431 let Some(batch) = self.try_eval_typed_batch(operand, env)? else {
432 return Ok(None);
433 };
434 Ok(typed_unary_batch(op, batch))
435 }
436 Expression::Binary { left, op, right } => {
437 let Some(left_batch) = self.try_eval_typed_batch(left, env)? else {
438 return Ok(None);
439 };
440 let Some(right_batch) = self.try_eval_typed_batch(right, env)? else {
441 return Ok(None);
442 };
443 Ok(typed_binary_batch(op, &left_batch, &right_batch))
444 }
445 Expression::TypeCast {
446 expression,
447 target_type,
448 } => {
449 let Some(batch) = self.try_eval_typed_batch(expression, env)? else {
450 return Ok(None);
451 };
452 Ok(typed_cast_batch(batch, target_type))
453 }
454 _ => Ok(None),
455 }
456 }
457
458 #[cfg(debug_assertions)]
459 pub(super) fn columnar_promise_holds(&self, expr: &Expression) -> bool {
460 use linkrs_core::types::operators::{BinaryOperator, UnaryOperator};
461 match expr {
462 Expression::Literal(_) | Expression::Parameter(_) | Expression::Variable(_) => true,
463 Expression::Unary { op, operand } => {
464 if matches!(op, UnaryOperator::Minus | UnaryOperator::Not)
467 && self.expr_references_nullable_column(operand)
468 {
469 return false;
470 }
471 self.columnar_promise_holds(operand)
472 }
473 Expression::Binary { left, op, right } => {
474 if matches!(
477 op,
478 BinaryOperator::Add
479 | BinaryOperator::Subtract
480 | BinaryOperator::Multiply
481 | BinaryOperator::Divide
482 | BinaryOperator::Modulo
483 | BinaryOperator::Exponent
484 | BinaryOperator::And
485 | BinaryOperator::Or
486 ) && (self.expr_references_nullable_column(left)
487 || self.expr_references_nullable_column(right))
488 {
489 return false;
490 }
491 self.columnar_promise_holds(left) && self.columnar_promise_holds(right)
492 }
493 Expression::TypeCast { expression, .. } => self.columnar_promise_holds(expression),
494 Expression::Property { object, property } => {
495 if let Expression::Variable(var) = object.as_ref() {
496 let compound = format!("{}.{}", var, property);
497 return self.layout.slot_id(&compound).is_some();
498 }
499 false
500 }
501 _ => false,
502 }
503 }
504
505 #[cfg(debug_assertions)]
507 fn expr_references_nullable_column(&self, expr: &Expression) -> bool {
508 let slot_has_null = |slot: usize| {
509 self.rows
510 .iter()
511 .any(|row| row.get(slot).is_some_and(Value::is_null))
512 };
513 match expr {
514 Expression::Variable(name) => self.layout.slot_id(name).is_some_and(&slot_has_null),
515 Expression::Property { object, property } => {
516 if let Expression::Variable(var) = object.as_ref() {
517 let compound = format!("{}.{}", var, property);
518 return self.layout.slot_id(&compound).is_some_and(slot_has_null);
519 }
520 false
521 }
522 Expression::Unary { operand, .. } => self.expr_references_nullable_column(operand),
523 Expression::Binary { left, right, .. } => {
524 self.expr_references_nullable_column(left)
525 || self.expr_references_nullable_column(right)
526 }
527 Expression::TypeCast { expression, .. } => {
528 self.expr_references_nullable_column(expression)
529 }
530 _ => false,
531 }
532 }
533
534 #[cfg(not(debug_assertions))]
535 fn columnar_promise_holds(&self, _expr: &Expression) -> bool {
536 false
537 }
538}