Skip to main content

reifydb_engine/expression/
context.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use std::sync::LazyLock;
5
6use reifydb_core::{
7	interface::{
8		catalog::property::{ColumnPropertyKind, ColumnSaturationPolicy, DEFAULT_COLUMN_SATURATION_POLICY},
9		evaluate::TargetColumn,
10	},
11	value::column::columns::Columns,
12};
13use reifydb_extension::transform::context::TransformContext;
14use reifydb_routine::function::registry::Functions;
15use reifydb_runtime::context::RuntimeContext;
16use reifydb_type::{params::Params, value::identity::IdentityId};
17
18use crate::{
19	arena::QueryArena,
20	vm::{stack::SymbolTable, volcano::query::QueryContext},
21};
22
23/// Session-scoped evaluation context — holds the 7 fields that are invariant
24/// within a given operator. Provides factory methods to produce `EvalContext`
25/// values that vary only in `columns` and `row_count`.
26#[derive(Clone, Copy)]
27pub struct EvalSession<'a> {
28	pub params: &'a Params,
29	pub symbols: &'a SymbolTable,
30	pub functions: &'a Functions,
31	pub runtime_context: &'a RuntimeContext,
32	pub arena: Option<&'a QueryArena>,
33	pub identity: IdentityId,
34	pub is_aggregate_context: bool,
35}
36
37impl<'a> EvalSession<'a> {
38	/// Main constructor — produces an `EvalContext` with `target=None` and `take=None`.
39	pub fn eval(&self, columns: Columns, row_count: usize) -> EvalContext<'a> {
40		EvalContext {
41			target: None,
42			columns,
43			row_count,
44			take: None,
45			params: self.params,
46			symbols: self.symbols,
47			is_aggregate_context: self.is_aggregate_context,
48			functions: self.functions,
49			runtime_context: self.runtime_context,
50			arena: self.arena,
51			identity: self.identity,
52		}
53	}
54
55	/// Shorthand for `eval(Columns::empty(), 1)`.
56	pub fn eval_empty(&self) -> EvalContext<'a> {
57		self.eval(Columns::empty(), 1)
58	}
59
60	/// Shorthand for `eval(columns, 1)` with `take=Some(1)`.
61	pub fn eval_join(&self, columns: Columns) -> EvalContext<'a> {
62		let mut ctx = self.eval(columns, 1);
63		ctx.take = Some(1);
64		ctx
65	}
66
67	/// Build from a `TransformContext` + stored `QueryContext` (volcano nodes with input).
68	pub fn from_transform(ctx: &'a TransformContext, stored: &'a QueryContext) -> Self {
69		Self {
70			params: ctx.params,
71			symbols: &stored.symbols,
72			functions: ctx.functions,
73			runtime_context: ctx.runtime_context,
74			arena: None,
75			identity: stored.identity,
76			is_aggregate_context: false,
77		}
78	}
79
80	/// Build from a `QueryContext` (without-input nodes, joins, inline).
81	pub fn from_query(ctx: &'a QueryContext) -> Self {
82		Self {
83			params: &ctx.params,
84			symbols: &ctx.symbols,
85			functions: &ctx.services.functions,
86			runtime_context: &ctx.services.runtime_context,
87			arena: None,
88			identity: ctx.identity,
89			is_aggregate_context: false,
90		}
91	}
92
93	/// Build a testing session with static empty values.
94	pub fn testing() -> EvalSession<'static> {
95		static EMPTY_PARAMS: LazyLock<Params> = LazyLock::new(|| Params::None);
96		static EMPTY_SYMBOL_TABLE: LazyLock<SymbolTable> = LazyLock::new(|| SymbolTable::new());
97		static EMPTY_FUNCTIONS: LazyLock<Functions> = LazyLock::new(|| Functions::empty());
98		static DEFAULT_RUNTIME_CONTEXT: LazyLock<RuntimeContext> = LazyLock::new(|| RuntimeContext::default());
99		EvalSession {
100			params: &EMPTY_PARAMS,
101			symbols: &EMPTY_SYMBOL_TABLE,
102			functions: &EMPTY_FUNCTIONS,
103			runtime_context: &DEFAULT_RUNTIME_CONTEXT,
104			arena: None,
105			identity: IdentityId::root(),
106			is_aggregate_context: false,
107		}
108	}
109}
110
111pub struct EvalContext<'a> {
112	pub target: Option<TargetColumn>,
113	pub columns: Columns,
114	pub row_count: usize,
115	pub take: Option<usize>,
116	pub params: &'a Params,
117	pub symbols: &'a SymbolTable,
118	// TODO: This is a temporary hack to support aggregate functions in StandardColumnEvaluator
119	// Should be replaced with proper function detection or separate aggregation methods
120	pub is_aggregate_context: bool,
121	pub functions: &'a Functions,
122	pub runtime_context: &'a RuntimeContext,
123	pub arena: Option<&'a QueryArena>,
124	pub identity: IdentityId,
125}
126
127impl<'a> EvalContext<'a> {
128	pub fn testing() -> Self {
129		EvalSession::testing().eval_empty()
130	}
131
132	pub(crate) fn saturation_policy(&self) -> ColumnSaturationPolicy {
133		self.target
134			.as_ref()
135			.and_then(|t| {
136				t.properties().into_iter().find_map(|p| match p {
137					ColumnPropertyKind::Saturation(policy) => Some(policy),
138				})
139			})
140			.unwrap_or(DEFAULT_COLUMN_SATURATION_POLICY.clone())
141	}
142}
143
144/// Compile-time context for resolving functions and UDFs.
145pub struct CompileContext<'a> {
146	pub functions: &'a Functions,
147	pub symbols: &'a SymbolTable,
148}