use std::collections::HashMap;
use crate::{
evaluator::CombinedExpressionEvaluator, procedural, schema::CombinedSchema,
select::cte::CteResult,
};
pub struct ExecutionContext<'a> {
pub schema: &'a CombinedSchema,
pub database: &'a vibesql_storage::Database,
pub outer_row: Option<&'a vibesql_storage::Row>,
pub outer_schema: Option<&'a CombinedSchema>,
pub outer_rows: Option<&'a [vibesql_storage::Row]>,
pub procedural_context: Option<&'a procedural::ExecutionContext>,
pub trigger_context: Option<&'a crate::trigger_execution::TriggerContext<'a>>,
pub cte_context: Option<&'a HashMap<String, CteResult>>,
pub window_mapping: Option<&'a HashMap<crate::select::WindowFunctionKey, usize>>,
}
impl<'a> ExecutionContext<'a> {
pub fn new(schema: &'a CombinedSchema, database: &'a vibesql_storage::Database) -> Self {
Self {
schema,
database,
outer_row: None,
outer_schema: None,
outer_rows: None,
procedural_context: None,
trigger_context: None,
cte_context: None,
window_mapping: None,
}
}
#[must_use]
pub fn with_outer_context(
mut self,
outer_row: &'a vibesql_storage::Row,
outer_schema: &'a CombinedSchema,
) -> Self {
self.outer_row = Some(outer_row);
self.outer_schema = Some(outer_schema);
self
}
#[must_use]
pub fn with_outer_rows(mut self, outer_rows: &'a [vibesql_storage::Row]) -> Self {
self.outer_rows = Some(outer_rows);
self
}
#[must_use]
pub fn with_procedural_context(mut self, proc_ctx: &'a procedural::ExecutionContext) -> Self {
self.procedural_context = Some(proc_ctx);
self
}
#[must_use]
pub fn with_trigger_context(
mut self,
trigger_ctx: &'a crate::trigger_execution::TriggerContext<'a>,
) -> Self {
self.trigger_context = Some(trigger_ctx);
self
}
#[must_use]
pub fn with_cte_context(mut self, cte_ctx: &'a HashMap<String, CteResult>) -> Self {
self.cte_context = Some(cte_ctx);
self
}
#[must_use]
pub fn with_window_mapping(
mut self,
window_mapping: &'a HashMap<crate::select::WindowFunctionKey, usize>,
) -> Self {
self.window_mapping = Some(window_mapping);
self
}
pub fn create_evaluator(&self) -> CombinedExpressionEvaluator<'a> {
if let Some(trigger_ctx) = self.trigger_context {
let mut evaluator =
CombinedExpressionEvaluator::with_database_and_trigger_context(
self.schema,
self.database,
trigger_ctx,
);
if let Some(outer_rows) = self.outer_rows {
evaluator.set_outer_rows(outer_rows);
}
return evaluator;
}
let mut evaluator = match (
self.outer_row,
self.outer_schema,
self.procedural_context,
self.cte_context,
self.window_mapping,
) {
(Some(outer_row), Some(outer_schema), None, Some(cte_ctx), None) => {
CombinedExpressionEvaluator::with_database_and_outer_context_and_cte(
self.schema,
self.database,
outer_row,
outer_schema,
cte_ctx,
)
}
(Some(outer_row), Some(outer_schema), None, None, None) => {
CombinedExpressionEvaluator::with_database_and_outer_context(
self.schema,
self.database,
outer_row,
outer_schema,
)
}
(None, None, Some(proc_ctx), Some(cte_ctx), None) => {
CombinedExpressionEvaluator::with_database_and_procedural_context_and_cte(
self.schema,
self.database,
proc_ctx,
cte_ctx,
)
}
(None, None, Some(proc_ctx), None, None) => {
CombinedExpressionEvaluator::with_database_and_procedural_context(
self.schema,
self.database,
proc_ctx,
)
}
(None, None, None, Some(cte_ctx), None) => {
CombinedExpressionEvaluator::with_database_and_cte(
self.schema,
self.database,
cte_ctx,
)
}
(None, None, None, Some(cte_ctx), Some(window_map)) => {
CombinedExpressionEvaluator::with_database_and_windows_and_cte(
self.schema,
self.database,
window_map,
cte_ctx,
)
}
(None, None, None, None, Some(window_map)) => {
CombinedExpressionEvaluator::with_database_and_windows(
self.schema,
self.database,
window_map,
)
}
(None, None, None, None, None) => {
CombinedExpressionEvaluator::with_database(self.schema, self.database)
}
_ => CombinedExpressionEvaluator::with_database(self.schema, self.database),
};
if let Some(outer_rows) = self.outer_rows {
evaluator.set_outer_rows(outer_rows);
}
evaluator
}
#[inline]
pub fn has_outer_context(&self) -> bool {
self.outer_row.is_some() && self.outer_schema.is_some()
}
#[inline]
pub fn has_cte_context(&self) -> bool {
self.cte_context.is_some()
}
#[inline]
pub fn has_procedural_context(&self) -> bool {
self.procedural_context.is_some()
}
}
#[cfg(test)]
mod tests {
use vibesql_catalog::TableSchema;
use super::*;
use crate::schema::CombinedSchema;
struct ExecutionContextBuilder<'a> {
schema: &'a CombinedSchema,
database: &'a vibesql_storage::Database,
outer_row: Option<&'a vibesql_storage::Row>,
outer_schema: Option<&'a CombinedSchema>,
procedural_context: Option<&'a procedural::ExecutionContext>,
cte_context: Option<&'a HashMap<String, CteResult>>,
executor_cte_context: Option<&'a HashMap<String, CteResult>>,
}
impl<'a> ExecutionContextBuilder<'a> {
fn new(schema: &'a CombinedSchema, database: &'a vibesql_storage::Database) -> Self {
Self {
schema,
database,
outer_row: None,
outer_schema: None,
procedural_context: None,
cte_context: None,
executor_cte_context: None,
}
}
#[must_use]
fn cte_from_query(mut self, cte_results: &'a HashMap<String, CteResult>) -> Self {
if !cte_results.is_empty() {
self.cte_context = Some(cte_results);
}
self
}
fn build(self) -> ExecutionContext<'a> {
let mut ctx = ExecutionContext::new(self.schema, self.database);
if let (Some(outer_row), Some(outer_schema)) = (self.outer_row, self.outer_schema) {
ctx = ctx.with_outer_context(outer_row, outer_schema);
}
if let Some(proc_ctx) = self.procedural_context {
ctx = ctx.with_procedural_context(proc_ctx);
}
if let Some(cte_ctx) = self.cte_context {
ctx = ctx.with_cte_context(cte_ctx);
} else if let Some(executor_cte) = self.executor_cte_context {
ctx = ctx.with_cte_context(executor_cte);
}
ctx
}
}
fn create_test_schema() -> CombinedSchema {
let table_schema = TableSchema::new("test".to_string(), vec![]);
CombinedSchema::from_table("test".to_string(), table_schema)
}
#[test]
fn test_execution_context_builder_basic() {
let schema = create_test_schema();
let database = vibesql_storage::Database::new();
let ctx = ExecutionContext::new(&schema, &database);
assert!(!ctx.has_outer_context());
assert!(!ctx.has_cte_context());
assert!(!ctx.has_procedural_context());
}
#[test]
fn test_execution_context_with_cte() {
let schema = create_test_schema();
let database = vibesql_storage::Database::new();
let cte_results: HashMap<String, CteResult> = HashMap::new();
let ctx = ExecutionContext::new(&schema, &database).with_cte_context(&cte_results);
assert!(ctx.has_cte_context());
}
#[test]
fn test_execution_context_builder_chain() {
let schema = create_test_schema();
let database = vibesql_storage::Database::new();
let cte_results: HashMap<String, CteResult> = HashMap::new();
let ctx =
ExecutionContextBuilder::new(&schema, &database).cte_from_query(&cte_results).build();
assert!(!ctx.has_cte_context());
}
}