Skip to main content

Module evaluator

Module evaluator 

Source
Expand description

Scalar expression evaluator — single owner of value-level evaluation for SQL scalar Expr trees.

Today scalar expression evaluation is fragmented:

  • query::filter::Predicate::evaluate runs comparison predicates against a single column value.
  • query::filter_compiled::CompiledFilter::evaluate runs the compiled filter opcode tape over a row slot, but still defers to Predicate::evaluate per opcode for the actual comparison logic.
  • Several inlined arms in query::core and query::executor compute projections, DEFAULT / CHECK expressions, RETURNING columns, COMPUTED columns, and ON CONFLICT updates by walking the AST and dispatching arithmetic / function calls inline.
  • query::expr_typing knows how to type an Expr against a Scope but never produces a Value — it stops at TypedExpr.

Operator / function / cast resolution is centralised in schema::coercion_spine, but value-level dispatch is not — so adding a new operator overload, a new implicit cast edge, or a new built-in function requires synchronised edits across each of the inline evaluators above.

This module is the first vertical slice of the deep evaluator interface tracked by draft-53-scalar-expression-evaluator. It exposes:

  • Row: pluggable column-binding lookup so callers can wire either a planner-resolved slot vector or a flat column-name map.
  • evaluate: the single entry point. Walks an Expr, resolves operators / functions / casts through coercion_spine, applies the implicit casts the spine asks for, and produces a Value.
  • EvalError: typed diagnostic surface — every failure mode the evaluator can encounter at runtime is enumerated here so callers don’t have to parse strings.

The slice is intentionally additive: callers in filter.rs, filter_compiled.rs, and executor.rs still own their own evaluation paths. Subsequent slices migrate those callers onto evaluate once the dispatch surface has proven out under the focused test set in this module.

§Semantics summary

  • NULL propagation. Arithmetic, comparison, concat, and CAST propagate Value::Null — any Null operand short-circuits to Null. AND / OR follow SQL three-valued logic.
  • Arithmetic overflow. Signed checked arithmetic; overflow surfaces as EvalError::ArithmeticOverflow rather than wrap or panic.
  • Division. Division by zero surfaces as EvalError::DivisionByZero. Integer / always promotes to Float per the operator catalog; integer % stays integer.
  • Implicit cast triggers. When the operator catalog has no exact-type overload, the spine returns the per-operand coercions; this evaluator applies them via [coerce::coerce_via_catalog] before dispatch.
  • Unknown function rejection. Calls that don’t resolve in the built-in function catalog produce EvalError::UnknownFunction. Variadic / catalog-resolved functions still return through the same dispatch.

Enums§

EvalError
Errors surfaced by evaluate. Every variant is a runtime failure shape — type-resolution failures live in expr_typing::TypeError, not here.

Traits§

Row
Pluggable row-binding lookup. The evaluator stays agnostic of whether the caller has a slot vector indexed by planner-assigned position, a flat HashMap<String, Value>, or a graph binding — every consumer just provides a Row impl that resolves a FieldRef to a Value.

Functions§

evaluate
Evaluate a scalar Expr against a row binding. Single entry point for the deep evaluator interface — every recursive call folds back through this function so the resolution surface stays uniform.