use crate::errors::ExecutorError;
#[allow(dead_code)]
pub fn evaluate_insert_expression(
expr: &vibesql_ast::Expression,
column: &vibesql_catalog::ColumnSchema,
procedural_context: Option<&crate::procedural::ExecutionContext>,
) -> Result<vibesql_types::SqlValue, ExecutorError> {
evaluate_insert_expression_with_trigger_context(
expr,
column,
procedural_context,
None,
None,
None,
)
}
pub fn evaluate_insert_expression_with_trigger_context(
expr: &vibesql_ast::Expression,
column: &vibesql_catalog::ColumnSchema,
procedural_context: Option<&crate::procedural::ExecutionContext>,
trigger_context: Option<&crate::trigger_execution::TriggerContext>,
database: Option<&vibesql_storage::Database>,
cte_results: Option<&std::collections::HashMap<String, crate::select::cte::CteResult>>,
) -> Result<vibesql_types::SqlValue, ExecutorError> {
match expr {
vibesql_ast::Expression::Literal(lit) => Ok(lit.clone()),
vibesql_ast::Expression::Default => {
if let Some(default_expr) = &column.default_value {
evaluate_default_expression(default_expr)
} else {
Ok(vibesql_types::SqlValue::Null)
}
}
vibesql_ast::Expression::ColumnRef(col_id)
if col_id.schema_canonical().is_none() && col_id.table_canonical().is_none() =>
{
let col_name = col_id.column_canonical();
if let Some(ctx) = procedural_context {
if let Some(value) = ctx.get_value(col_name) {
return Ok(value.clone());
}
}
Err(ExecutorError::UnsupportedExpression(format!(
"Column reference '{}' not supported in INSERT VALUES. Did you mean to use a procedural variable?",
col_name
)))
}
vibesql_ast::Expression::PseudoVariable { .. } => {
if let (Some(ctx), Some(db)) = (trigger_context, database) {
let dummy_row = vibesql_storage::Row::new(vec![]);
let evaluator =
crate::ExpressionEvaluator::with_trigger_context(ctx.table_schema, db, ctx);
evaluator.eval(expr, &dummy_row)
} else {
Err(ExecutorError::UnsupportedExpression(
"Pseudo-variables (OLD/NEW) are only valid within trigger bodies".to_string(),
))
}
}
_ => {
if let (Some(ctx), Some(db)) = (trigger_context, database) {
let dummy_row = vibesql_storage::Row::new(vec![]);
let mut evaluator =
crate::ExpressionEvaluator::with_trigger_context(ctx.table_schema, db, ctx);
if let Some(ctes) = cte_results {
evaluator = evaluator.with_cte_context(ctes);
}
evaluator.eval(expr, &dummy_row)
} else if let Some(db) = database {
let dummy_schema =
vibesql_catalog::TableSchema::new("__insert_expr__".to_string(), vec![]);
let dummy_row = vibesql_storage::Row::new(vec![]);
let mut evaluator = crate::ExpressionEvaluator::with_database(&dummy_schema, db);
if let Some(ctes) = cte_results {
evaluator = evaluator.with_cte_context(ctes);
}
evaluator.eval(expr, &dummy_row)
} else {
Err(ExecutorError::UnsupportedExpression(
"Complex expressions in INSERT VALUES require database context".to_string(),
))
}
}
}
}
pub fn evaluate_default_expression(
expr: &vibesql_ast::Expression,
) -> Result<vibesql_types::SqlValue, ExecutorError> {
match expr {
vibesql_ast::Expression::Literal(lit) => Ok(lit.clone()),
vibesql_ast::Expression::UnaryOp { op, expr } => {
let inner = evaluate_default_expression(expr)?;
match op {
vibesql_ast::UnaryOperator::Plus => Ok(inner), vibesql_ast::UnaryOperator::Minus => {
match inner {
vibesql_types::SqlValue::Integer(i) => {
Ok(vibesql_types::SqlValue::Integer(-i))
}
vibesql_types::SqlValue::Bigint(i) => {
Ok(vibesql_types::SqlValue::Bigint(-i))
}
vibesql_types::SqlValue::Smallint(i) => {
Ok(vibesql_types::SqlValue::Smallint(-i))
}
vibesql_types::SqlValue::Float(f) => Ok(vibesql_types::SqlValue::Float(-f)),
vibesql_types::SqlValue::Real(f) => Ok(vibesql_types::SqlValue::Real(-f)),
vibesql_types::SqlValue::Double(f) => {
Ok(vibesql_types::SqlValue::Double(-f))
}
vibesql_types::SqlValue::Numeric(f) => {
Ok(vibesql_types::SqlValue::Numeric(-f))
}
_ => Err(ExecutorError::UnsupportedExpression(format!(
"Cannot apply unary minus to {:?}",
inner
))),
}
}
_ => Err(ExecutorError::UnsupportedExpression(format!(
"Unary operator {:?} not supported in DEFAULT expressions",
op
))),
}
}
vibesql_ast::Expression::NextValue { sequence_name } => {
Err(ExecutorError::UnsupportedExpression(format!(
"Sequence '{}' requires database context - this should have been handled earlier",
sequence_name
)))
}
vibesql_ast::Expression::ColumnRef(col_id)
if col_id.schema_canonical().is_none() && col_id.table_canonical().is_none() =>
{
let col_name = col_id.column_canonical();
Ok(vibesql_types::SqlValue::Varchar(arcstr::ArcStr::from(col_name)))
}
vibesql_ast::Expression::Function { name, .. } => {
match name.to_uppercase().as_str() {
"CURRENT_DATE" => {
use chrono::Datelike;
let now = chrono::Local::now();
let date =
vibesql_types::Date::new(now.year(), now.month() as u8, now.day() as u8)
.map_err(|e| {
ExecutorError::UnsupportedFeature(format!(
"Failed to create date: {}",
e
))
})?;
Ok(vibesql_types::SqlValue::Date(date))
}
"CURRENT_TIME" => {
use chrono::Timelike;
let now = chrono::Local::now();
let time_naive = now.time();
let time = vibesql_types::Time::new(
time_naive.hour() as u8,
time_naive.minute() as u8,
time_naive.second() as u8,
time_naive.nanosecond(),
)
.map_err(|e| {
ExecutorError::UnsupportedFeature(format!("Failed to create time: {}", e))
})?;
Ok(vibesql_types::SqlValue::Time(time))
}
"CURRENT_TIMESTAMP" => {
use chrono::{Datelike, Timelike};
let now = chrono::Local::now();
let time_naive = now.time();
let date =
vibesql_types::Date::new(now.year(), now.month() as u8, now.day() as u8)
.map_err(|e| {
ExecutorError::UnsupportedFeature(format!(
"Failed to create date: {}",
e
))
})?;
let time = vibesql_types::Time::new(
time_naive.hour() as u8,
time_naive.minute() as u8,
time_naive.second() as u8,
time_naive.nanosecond(),
)
.map_err(|e| {
ExecutorError::UnsupportedFeature(format!("Failed to create time: {}", e))
})?;
Ok(vibesql_types::SqlValue::Timestamp(vibesql_types::Timestamp::new(
date, time,
)))
}
"CURRENT_USER" | "USER" | "SESSION_USER" => {
Ok(vibesql_types::SqlValue::Varchar(arcstr::ArcStr::from("public")))
}
"CURRENT_ROLE" => {
Ok(vibesql_types::SqlValue::Varchar(arcstr::ArcStr::from("public")))
}
_ => Err(ExecutorError::UnsupportedExpression(format!(
"Function '{}' not supported in DEFAULT expressions",
name
))),
}
}
vibesql_ast::Expression::CurrentDate => {
use chrono::Datelike;
let now = chrono::Local::now();
let date = vibesql_types::Date::new(now.year(), now.month() as u8, now.day() as u8)
.map_err(|e| {
ExecutorError::UnsupportedFeature(format!("Failed to create date: {}", e))
})?;
Ok(vibesql_types::SqlValue::Date(date))
}
vibesql_ast::Expression::CurrentTime { .. } => {
use chrono::Timelike;
let now = chrono::Local::now();
let time_naive = now.time();
let time = vibesql_types::Time::new(
time_naive.hour() as u8,
time_naive.minute() as u8,
time_naive.second() as u8,
time_naive.nanosecond(),
)
.map_err(|e| {
ExecutorError::UnsupportedFeature(format!("Failed to create time: {}", e))
})?;
Ok(vibesql_types::SqlValue::Time(time))
}
vibesql_ast::Expression::CurrentTimestamp { .. } => {
use chrono::{Datelike, Timelike};
let now = chrono::Local::now();
let time_naive = now.time();
let date = vibesql_types::Date::new(now.year(), now.month() as u8, now.day() as u8)
.map_err(|e| {
ExecutorError::UnsupportedFeature(format!("Failed to create date: {}", e))
})?;
let time = vibesql_types::Time::new(
time_naive.hour() as u8,
time_naive.minute() as u8,
time_naive.second() as u8,
time_naive.nanosecond(),
)
.map_err(|e| {
ExecutorError::UnsupportedFeature(format!("Failed to create time: {}", e))
})?;
Ok(vibesql_types::SqlValue::Timestamp(vibesql_types::Timestamp::new(date, time)))
}
_ => Err(ExecutorError::UnsupportedExpression(
"Only literals and functions are supported in DEFAULT expressions".to_string(),
)),
}
}
#[allow(dead_code)]
pub fn apply_default_values(
schema: &vibesql_catalog::TableSchema,
row_values: &mut [vibesql_types::SqlValue],
database: &mut vibesql_storage::Database,
) -> Result<Option<i64>, ExecutorError> {
apply_default_values_with_table_name(schema, row_values, database, &schema.name)
}
pub fn apply_default_values_with_table_name(
schema: &vibesql_catalog::TableSchema,
row_values: &mut [vibesql_types::SqlValue],
database: &mut vibesql_storage::Database,
storage_table_name: &str,
) -> Result<Option<i64>, ExecutorError> {
apply_default_values_with_batch_context(schema, row_values, database, storage_table_name, None)
}
pub fn apply_default_values_with_batch_context(
schema: &vibesql_catalog::TableSchema,
row_values: &mut [vibesql_types::SqlValue],
database: &mut vibesql_storage::Database,
storage_table_name: &str,
batch_max_ipk: Option<i64>,
) -> Result<Option<i64>, ExecutorError> {
let mut first_generated_id: Option<i64> = None;
if let Some(ipk_idx) = schema.get_integer_primary_key_index() {
if row_values[ipk_idx] == vibesql_types::SqlValue::Null {
let table_max = compute_next_integer_pk_value(database, storage_table_name, ipk_idx)?;
let next_val = match batch_max_ipk {
Some(batch_max) => table_max.max(batch_max + 1),
None => table_max,
};
row_values[ipk_idx] = vibesql_types::SqlValue::Integer(next_val);
if first_generated_id.is_none() {
first_generated_id = Some(next_val);
}
}
}
for (col_idx, col) in schema.columns.iter().enumerate() {
if row_values[col_idx] == vibesql_types::SqlValue::Null {
if let Some(default_expr) = &col.default_value {
let default_value = match default_expr {
vibesql_ast::Expression::NextValue { sequence_name } => {
let seq =
database.catalog.get_sequence_mut(sequence_name).map_err(|e| {
ExecutorError::UnsupportedExpression(format!(
"Sequence error: {:?}",
e
))
})?;
let next_val = seq.next_value().map_err(|e| {
ExecutorError::ConstraintViolation(format!("Sequence error: {}", e))
})?;
if first_generated_id.is_none() {
first_generated_id = Some(next_val);
}
vibesql_types::SqlValue::Integer(next_val)
}
_ => evaluate_default_expression(default_expr)?,
};
let coerced_value = super::validation::coerce_value(default_value, &col.data_type)?;
row_values[col_idx] = coerced_value;
}
}
}
Ok(first_generated_id)
}
fn compute_next_integer_pk_value(
database: &vibesql_storage::Database,
table_name: &str,
pk_col_idx: usize,
) -> Result<i64, ExecutorError> {
let table = match database.get_table(table_name) {
Some(t) => t,
None => {
return Ok(1);
}
};
let mut max_val: i64 = 0;
for row in table.scan() {
if let Some(value) = row.get(pk_col_idx) {
let int_val = match value {
vibesql_types::SqlValue::Integer(i) => *i,
vibesql_types::SqlValue::Bigint(i) => *i,
vibesql_types::SqlValue::Null => continue, _ => continue, };
if int_val > max_val {
max_val = int_val;
}
}
}
Ok(max_val + 1)
}
pub fn apply_generated_columns(
schema: &vibesql_catalog::TableSchema,
row_values: &mut [vibesql_types::SqlValue],
_database: &vibesql_storage::Database,
) -> Result<(), ExecutorError> {
let temp_row = vibesql_storage::Row::new(row_values.to_vec());
let evaluator = crate::ExpressionEvaluator::new(schema)
.with_schema_context(crate::evaluator::SchemaExprContext::GeneratedColumn);
for (col_idx, col) in schema.columns.iter().enumerate() {
if let Some(generated_expr) = &col.generated_expr {
let generated_value = evaluator.eval(generated_expr, &temp_row)?;
let coerced_value = super::validation::coerce_value(generated_value, &col.data_type)?;
row_values[col_idx] = coerced_value;
}
}
Ok(())
}