Skip to main content

uqa_execution/relational/
evaluator.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Expression-evaluation and row-predicate seams.
8
9use super::{
10    eval_scalar, Arc, ExecResult, RowSchema, SQLParam, ScalarEvalContext, ScalarExpr, Value,
11};
12use uqa_sql::ast::ColumnType;
13use uqa_sql::expr::RowLookup;
14use uqa_sql::SQLError;
15
16use crate::PhysicalRow;
17
18pub trait ExpressionEvaluator: Send + Sync {
19    fn evaluate(&self, expression: &ScalarExpr, row: &dyn RowLookup) -> ExecResult<Value>;
20
21    fn evaluate_physical(
22        &self,
23        expression: &ScalarExpr,
24        schema: &RowSchema,
25        row: &PhysicalRow,
26    ) -> ExecResult<Value> {
27        self.evaluate(expression, &schema.view(row))
28    }
29
30    /// Bound SQL parameters used by static type resolution. Implementations
31    /// that do not evaluate parameters may keep the empty default.
32    fn parameters(&self) -> &[SQLParam] {
33        &[]
34    }
35
36    fn expression_type(
37        &self,
38        expression: &ScalarExpr,
39        schema: &RowSchema,
40    ) -> Result<Option<ColumnType>, SQLError> {
41        crate::scalar_type(expression, schema, self.parameters())
42    }
43
44    /// Bind overloads and common-type coercions while this evaluator's declared-type context is still available.
45    fn bind_type_introspection(&self, expression: ScalarExpr, schema: &RowSchema) -> ScalarExpr {
46        crate::bind_type_introspection(expression, schema, self.parameters())
47    }
48
49    fn star_column_visible(&self, _column: &str) -> bool {
50        true
51    }
52
53    /// Decide wildcard visibility from the bound logical attribute rather
54    /// than its possibly colliding SQL label.
55    fn star_position_visible(&self, schema: &RowSchema, position: usize) -> bool {
56        schema.wildcard_position_visible(position)
57            && schema
58                .columns()
59                .get(position)
60                .is_none_or(|column| self.star_column_visible(column))
61    }
62
63    fn project_star(&self, row: &dyn RowLookup) -> ExecResult<Vec<(String, Value)>> {
64        let mut output = Vec::new();
65        row.visit_columns(&mut |column, value| {
66            if self.star_column_visible(column) {
67                output.push((column.to_string(), value.clone()));
68            }
69        });
70        Ok(output)
71    }
72}
73
74pub type SharedExpressionEvaluator<'a> = Arc<dyn ExpressionEvaluator + 'a>;
75
76pub trait RowPredicate: Send + Sync {
77    fn keep_physical(&self, schema: &RowSchema, row: &PhysicalRow) -> ExecResult<bool>;
78}
79
80pub type SharedRowPredicate<'a> = Arc<dyn RowPredicate + 'a>;
81
82pub(super) struct DefaultExpressionEvaluator {
83    params: Vec<SQLParam>,
84}
85
86impl DefaultExpressionEvaluator {
87    pub(super) fn shared(params: Vec<SQLParam>) -> SharedExpressionEvaluator<'static> {
88        Arc::new(Self { params })
89    }
90}
91
92impl ExpressionEvaluator for DefaultExpressionEvaluator {
93    fn evaluate(&self, expression: &ScalarExpr, row: &dyn RowLookup) -> ExecResult<Value> {
94        let context = ScalarEvalContext::from_row_lookup(row, &self.params);
95        Ok(eval_scalar(expression, &context)?)
96    }
97
98    fn evaluate_physical(
99        &self,
100        expression: &ScalarExpr,
101        schema: &RowSchema,
102        row: &PhysicalRow,
103    ) -> ExecResult<Value> {
104        let view = schema.view(row);
105        let context =
106            ScalarEvalContext::from_row_lookup(&view, &self.params).with_row_schema(schema);
107        Ok(eval_scalar(expression, &context)?)
108    }
109
110    fn parameters(&self) -> &[SQLParam] {
111        &self.params
112    }
113}