use crate::functions::FunctionRegistry;
use crate::session::manager::get_session;
use crate::session::models::{Session, UserSession};
use crate::storage::{StorageManager, Value};
use crate::types::GqlType;
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct ExecutionContext {
pub session_id: String,
pub variables: HashMap<String, Value>,
pub variable_types: HashMap<String, GqlType>,
#[allow(dead_code)]
pub schema_types: HashMap<String, GqlType>,
pub current_graph: Option<std::sync::Arc<crate::storage::GraphCache>>,
pub storage_manager: Option<Arc<StorageManager>>,
pub function_registry: Option<Arc<FunctionRegistry>>,
pub current_user: Option<String>,
#[allow(dead_code)]
pub current_transaction: Option<String>,
pub warnings: Vec<String>,
}
impl ExecutionContext {
pub fn new(session_id: String, storage_manager: Arc<StorageManager>) -> Self {
Self {
session_id,
variables: HashMap::new(),
variable_types: HashMap::new(),
schema_types: HashMap::new(),
current_graph: None,
storage_manager: Some(storage_manager),
function_registry: None,
current_user: None,
current_transaction: None,
warnings: Vec::new(),
}
}
pub fn add_warning(&mut self, warning: String) {
self.warnings.push(warning);
}
pub fn get_warnings(&self) -> &[String] {
&self.warnings
}
#[allow(dead_code)] pub fn clear_warnings(&mut self) {
self.warnings.clear();
}
pub fn with_function_registry(mut self, function_registry: Arc<FunctionRegistry>) -> Self {
self.function_registry = Some(function_registry);
self
}
pub fn get_session(&self) -> Option<Arc<std::sync::RwLock<UserSession>>> {
get_session(&self.session_id)
}
pub fn get_variable(&self, name: &str) -> Option<Value> {
if let Some(session_arc) = self.get_session() {
if let Ok(user_session) = session_arc.read() {
if let Some(value) = user_session.parameters.get(name) {
return Some(value.clone());
}
}
}
self.variables.get(name).cloned()
}
pub fn set_variable(&mut self, name: String, value: Value) {
self.variables.insert(name, value);
}
#[allow(dead_code)] pub fn set_variable_with_type(&mut self, name: String, value: Value, value_type: GqlType) {
self.variables.insert(name.clone(), value);
self.variable_types.insert(name, value_type);
}
#[allow(dead_code)] pub fn get_variable_type(&self, name: &str) -> Option<&GqlType> {
self.variable_types.get(name)
}
pub fn transaction_state(&self) -> Option<Arc<crate::session::SessionTransactionState>> {
let session_arc = self.get_session()?;
let user_session = session_arc.read().ok()?;
Some(user_session.transaction_state.clone())
}
pub fn get_current_graph_name(&self) -> Option<String> {
let session_arc = self.get_session()?;
let user_session = session_arc.read().ok()?;
user_session.current_graph.clone()
}
pub fn get_current_schema(&self) -> Option<String> {
let session_arc = self.get_session()?;
let user_session = session_arc.read().ok()?;
user_session.current_schema.clone()
}
#[allow(dead_code)] pub fn set_schema_type(&mut self, name: String, schema_type: GqlType) {
self.schema_types.insert(name, schema_type);
}
#[allow(dead_code)] pub fn get_schema_type(&self, name: &str) -> Option<&GqlType> {
self.schema_types.get(name)
}
pub fn clear_locals(&mut self) {
self.variables.clear();
self.variable_types.clear();
}
pub fn set_current_graph(&mut self, graph: std::sync::Arc<crate::storage::GraphCache>) {
self.current_graph = Some(graph);
}
#[allow(dead_code)] pub fn session_id(&self) -> &str {
&self.session_id
}
pub fn log_operation_to_wal(
&self,
operation_type: crate::txn::state::OperationType,
description: String,
) -> Result<(), crate::exec::error::ExecutionError> {
if let Some(transaction_state) = self.transaction_state() {
transaction_state.log_operation_to_wal(operation_type, description)
} else {
log::debug!(
"Skipping WAL logging - no transaction state available: {:?} - {}",
operation_type,
description
);
Ok(())
}
}
pub fn log_transaction_operation(
&self,
operation: crate::txn::UndoOperation,
) -> Result<(), crate::exec::error::ExecutionError> {
if let Some(transaction_state) = self.transaction_state() {
transaction_state.log_transaction_operation(operation)
} else {
Err(crate::exec::error::ExecutionError::RuntimeError(
"No transaction state available for transaction logging".to_string(),
))
}
}
pub fn get_graph_name(&self) -> Result<String, crate::exec::error::ExecutionError> {
if let Some(graph_name) = self.get_current_graph_name() {
if graph_name.starts_with('/') && graph_name.matches('/').count() >= 2 {
Ok(graph_name)
} else {
Err(crate::exec::error::ExecutionError::RuntimeError(
format!("Session graph name '{}' is not in full path format. Use /<schema-name>/<graph-name> format.", graph_name)
))
}
} else {
Err(crate::exec::error::ExecutionError::RuntimeError(
"No graph context available. Use SESSION SET GRAPH with full path /<schema-name>/<graph-name> format.".to_string()
))
}
}
#[allow(dead_code)] pub fn from_session(session: &Session) -> Self {
let storage = session.storage.clone();
let mut context = Self::new("session".to_string(), storage);
context.current_user = Some(session.username.clone());
context
}
#[allow(dead_code)] pub fn set_current_user(&mut self, user: String) {
self.current_user = Some(user);
}
#[allow(dead_code)] pub fn set_current_transaction(&mut self, transaction_id: String) {
self.current_transaction = Some(transaction_id);
}
pub fn evaluate_simple_expression(
&self,
expr: &crate::ast::ast::Expression,
) -> Result<Value, crate::exec::error::ExecutionError> {
use crate::ast::ast::Expression;
use crate::exec::result::Row;
use crate::functions::FunctionContext;
match expr {
Expression::Literal(literal) => {
Ok(Self::literal_to_value(literal))
}
Expression::FunctionCall(func_call) => {
let function_registry = self.function_registry.as_ref().ok_or_else(|| {
crate::exec::error::ExecutionError::RuntimeError(
"Function registry not available in execution context".to_string(),
)
})?;
let function = function_registry.get(&func_call.name).ok_or_else(|| {
crate::exec::error::ExecutionError::UnsupportedOperator(format!(
"Function not found: {}",
func_call.name
))
})?;
let mut evaluated_args = Vec::new();
for arg in &func_call.arguments {
let value = self.evaluate_simple_expression(arg)?;
evaluated_args.push(value);
}
let temp_row = Row::new();
let function_context = FunctionContext::with_storage(
vec![temp_row],
self.variables.clone(),
evaluated_args,
self.storage_manager.clone(),
self.current_graph.clone(),
self.get_current_graph_name(),
);
function.execute(&function_context).map_err(|e| {
crate::exec::error::ExecutionError::UnsupportedOperator(format!(
"Function execution error: {}",
e
))
})
}
Expression::Variable(var) => {
self.get_variable(&var.name).ok_or_else(|| {
crate::exec::error::ExecutionError::ExpressionError(format!(
"Variable not found: {}",
var.name
))
})
}
_ => {
Err(crate::exec::error::ExecutionError::ExpressionError(
format!(
"Expression type not supported in simple evaluation: {:?}",
expr
),
))
}
}
}
fn literal_to_value(literal: &crate::ast::ast::Literal) -> Value {
match literal {
crate::ast::ast::Literal::String(s) => Value::String(s.clone()),
crate::ast::ast::Literal::Integer(i) => Value::Number(*i as f64),
crate::ast::ast::Literal::Float(f) => Value::Number(*f),
crate::ast::ast::Literal::Boolean(b) => Value::Boolean(*b),
crate::ast::ast::Literal::Null => Value::Null,
crate::ast::ast::Literal::DateTime(dt) => Value::String(dt.clone()),
crate::ast::ast::Literal::Duration(dur) => Value::String(dur.clone()),
crate::ast::ast::Literal::TimeWindow(tw) => Value::String(tw.clone()),
crate::ast::ast::Literal::Vector(vec) => {
Value::Vector(vec.iter().map(|&f| f as f32).collect())
}
crate::ast::ast::Literal::List(list) => {
let converted: Vec<Value> =
list.iter().map(|lit| Self::literal_to_value(lit)).collect();
Value::List(converted)
}
}
}
}