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