vibesql_executor/procedural/
executor.rs1use vibesql_ast::{ProceduralStatement, Statement};
11use vibesql_storage::Database;
12use vibesql_types::SqlValue;
13
14use crate::{
15 errors::ExecutorError,
16 procedural::{ControlFlow, ExecutionContext},
17};
18
19pub fn execute_procedural_statement(
21 stmt: &ProceduralStatement,
22 ctx: &mut ExecutionContext,
23 db: &mut Database,
24) -> Result<ControlFlow, ExecutorError> {
25 match stmt {
26 ProceduralStatement::Declare { name, data_type, default_value } => {
27 let value = if let Some(expr) = default_value {
29 evaluate_expression(expr, db, ctx)?
31 } else {
32 SqlValue::Null
34 };
35
36 let typed_value = cast_to_type(value, data_type)?;
38 ctx.set_variable(name, typed_value);
39 Ok(ControlFlow::Continue)
40 }
41
42 ProceduralStatement::Set { name, value } => {
43 let new_value = evaluate_expression(value, db, ctx)?;
45
46 if let Some(var_name) = name.strip_prefix('@') {
48 db.set_session_variable(var_name, new_value);
50 } else if ctx.has_parameter(name) {
51 if let Some(param) = ctx.get_parameter_mut(name) {
53 *param = new_value;
54 }
55 } else if ctx.has_variable(name) {
56 ctx.set_variable(name, new_value);
58 } else {
59 return Err(ExecutorError::VariableNotFound {
60 variable_name: name.clone(),
61 available_variables: ctx.get_available_names(),
62 });
63 }
64
65 Ok(ControlFlow::Continue)
66 }
67
68 ProceduralStatement::Return(expr) => {
69 let value = evaluate_expression(expr, db, ctx)?;
71 Ok(ControlFlow::Return(value))
72 }
73
74 ProceduralStatement::Leave(label) => {
75 if !ctx.has_label(label) {
77 return Err(ExecutorError::LabelNotFound(label.clone()));
78 }
79 Ok(ControlFlow::Leave(label.clone()))
80 }
81
82 ProceduralStatement::Iterate(label) => {
83 if !ctx.has_label(label) {
85 return Err(ExecutorError::LabelNotFound(label.clone()));
86 }
87 Ok(ControlFlow::Iterate(label.clone()))
88 }
89
90 ProceduralStatement::If { condition, then_statements, else_statements } => {
91 super::control_flow::execute_if(condition, then_statements, else_statements, ctx, db)
92 }
93
94 ProceduralStatement::While { condition, statements } => {
95 super::control_flow::execute_while(condition, statements, ctx, db)
96 }
97
98 ProceduralStatement::Loop { statements } => {
99 super::control_flow::execute_loop(statements, ctx, db)
100 }
101
102 ProceduralStatement::Repeat { statements, condition } => {
103 super::control_flow::execute_repeat(statements, condition, ctx, db)
104 }
105
106 ProceduralStatement::Sql(sql_stmt) => {
107 execute_sql_statement(sql_stmt, db, ctx)?;
109 Ok(ControlFlow::Continue)
110 }
111 }
112}
113
114pub fn evaluate_expression(
119 expr: &vibesql_ast::Expression,
120 _db: &mut Database,
121 ctx: &ExecutionContext,
122) -> Result<SqlValue, ExecutorError> {
123 use vibesql_ast::{BinaryOperator, Expression};
124
125 match expr {
126 Expression::ColumnRef(col_id)
128 if col_id.schema_canonical().is_none() && col_id.table_canonical().is_none() =>
129 {
130 let column = col_id.column_canonical();
131 if let Some(var_name) = column.strip_prefix('@') {
133 _db.get_session_variable(var_name).cloned().ok_or_else(|| {
135 ExecutorError::VariableNotFound {
136 variable_name: format!("@{}", var_name),
137 available_variables: vec![], }
139 })
140 } else {
141 ctx.get_value(column).cloned().ok_or_else(|| ExecutorError::VariableNotFound {
143 variable_name: column.to_string(),
144 available_variables: ctx.get_available_names(),
145 })
146 }
147 }
148
149 Expression::Literal(value) => Ok(value.clone()),
151
152 Expression::BinaryOp { left, op, right } => {
154 let left_val = evaluate_expression(left, _db, ctx)?;
155 let right_val = evaluate_expression(right, _db, ctx)?;
156
157 match op {
158 BinaryOperator::Plus => {
159 match (left_val, right_val) {
161 (SqlValue::Integer(l), SqlValue::Integer(r)) => {
162 Ok(SqlValue::Integer(l + r))
163 }
164 _ => Err(ExecutorError::TypeError(
165 "Binary operation only supports integers in Phase 2".to_string(),
166 )),
167 }
168 }
169 BinaryOperator::Minus => match (left_val, right_val) {
170 (SqlValue::Integer(l), SqlValue::Integer(r)) => Ok(SqlValue::Integer(l - r)),
171 _ => Err(ExecutorError::TypeError(
172 "Binary operation only supports integers in Phase 2".to_string(),
173 )),
174 },
175 BinaryOperator::Multiply => match (left_val, right_val) {
176 (SqlValue::Integer(l), SqlValue::Integer(r)) => Ok(SqlValue::Integer(l * r)),
177 _ => Err(ExecutorError::TypeError(
178 "Binary operation only supports integers in Phase 2".to_string(),
179 )),
180 },
181 BinaryOperator::GreaterThan => match (left_val, right_val) {
182 (SqlValue::Integer(l), SqlValue::Integer(r)) => Ok(SqlValue::Boolean(l > r)),
183 _ => Err(ExecutorError::TypeError(
184 "Comparison only supports integers in Phase 2".to_string(),
185 )),
186 },
187 _ => Err(ExecutorError::UnsupportedFeature(format!(
188 "Binary operator {:?} not yet supported in procedural expressions",
189 op
190 ))),
191 }
192 }
193
194 Expression::Function { name, args, .. } if name.eq_ignore_ascii_case("CONCAT") => {
196 let mut result = String::new();
197 for arg in args {
198 let val = evaluate_expression(arg, _db, ctx)?;
199 result.push_str(&val.to_string());
200 }
201 Ok(SqlValue::Varchar(arcstr::ArcStr::from(result)))
202 }
203
204 _ => Err(ExecutorError::UnsupportedFeature(format!(
206 "Expression type not yet supported in procedures: {:?}",
207 expr
208 ))),
209 }
210}
211
212fn execute_sql_statement(
222 stmt: &Statement,
223 db: &mut Database,
224 ctx: &mut ExecutionContext,
225) -> Result<(), ExecutorError> {
226 match stmt {
227 Statement::Select(select_stmt) => {
228 if let Some(into_vars) = &select_stmt.into_variables {
230 let executor = crate::SelectExecutor::new_with_procedural_context(db, ctx);
232 let results = executor.execute(select_stmt)?;
233
234 if results.len() != 1 {
236 return Err(ExecutorError::SelectIntoRowCount {
237 expected: 1,
238 actual: results.len(),
239 });
240 }
241
242 let row = &results[0];
244
245 if row.values.len() != into_vars.len() {
247 return Err(ExecutorError::SelectIntoColumnCount {
248 expected: into_vars.len(),
249 actual: row.values.len(),
250 });
251 }
252
253 for (var_name, value) in into_vars.iter().zip(row.values.iter()) {
255 ctx.set_variable(var_name, value.clone());
256 }
257
258 Ok(())
259 } else {
260 let executor = crate::SelectExecutor::new_with_procedural_context(db, ctx);
262 let _results = executor.execute(select_stmt)?;
263 Ok(())
264 }
265 }
266 Statement::Insert(insert_stmt) => {
267 let _count =
269 crate::InsertExecutor::execute_with_procedural_context(db, insert_stmt, ctx)?;
270 Ok(())
271 }
272 Statement::Update(update_stmt) => {
273 let _count =
275 crate::UpdateExecutor::execute_with_procedural_context(update_stmt, db, ctx)?;
276 Ok(())
277 }
278 Statement::Delete(delete_stmt) => {
279 let _count =
281 crate::DeleteExecutor::execute_with_procedural_context(delete_stmt, db, ctx)?;
282 Ok(())
283 }
284 _ => {
285 Err(ExecutorError::UnsupportedFeature(format!(
287 "SQL statement type not supported in procedure bodies: {:?}",
288 stmt
289 )))
290 }
291 }
292}
293
294fn cast_to_type(
296 value: SqlValue,
297 target_type: &vibesql_types::DataType,
298) -> Result<SqlValue, ExecutorError> {
299 use vibesql_types::DataType;
300
301 if matches!(value, SqlValue::Null) {
303 return Ok(SqlValue::Null);
304 }
305
306 match target_type {
307 DataType::Integer => match value {
308 SqlValue::Integer(i) => Ok(SqlValue::Integer(i)),
309 SqlValue::Bigint(b) => Ok(SqlValue::Integer(b)),
310 SqlValue::Smallint(s) => Ok(SqlValue::Integer(s as i64)),
311 SqlValue::Varchar(s) | SqlValue::Character(s) => {
312 s.parse::<i64>().map(SqlValue::Integer).map_err(|_| {
313 ExecutorError::TypeError(format!("Cannot convert '{}' to INTEGER", s))
314 })
315 }
316 _ => Err(ExecutorError::TypeError(format!("Cannot convert {:?} to INTEGER", value))),
317 },
318
319 DataType::Varchar { .. } | DataType::Character { .. } => {
320 Ok(SqlValue::Varchar(arcstr::ArcStr::from(value.to_string())))
322 }
323
324 DataType::Boolean => match value {
325 SqlValue::Boolean(b) => Ok(SqlValue::Boolean(b)),
326 SqlValue::Integer(i) => Ok(SqlValue::Boolean(i != 0)),
327 SqlValue::Varchar(ref s) | SqlValue::Character(ref s) => {
328 let s_upper = s.to_uppercase();
329 if s_upper == "TRUE" || s_upper == "T" || s_upper == "1" {
330 Ok(SqlValue::Boolean(true))
331 } else if s_upper == "FALSE" || s_upper == "F" || s_upper == "0" {
332 Ok(SqlValue::Boolean(false))
333 } else {
334 Err(ExecutorError::TypeError(format!("Cannot convert '{}' to BOOLEAN", s)))
335 }
336 }
337 _ => Err(ExecutorError::TypeError(format!("Cannot convert {:?} to BOOLEAN", value))),
338 },
339
340 _ => Ok(value),
342 }
343}
344
345