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 {
24 name,
25 data_type,
26 default_value,
27 } => {
28 let value = if let Some(expr) = default_value {
30 evaluate_expression(expr, db, ctx)?
32 } else {
33 SqlValue::Null
35 };
36
37 let typed_value = cast_to_type(value, data_type)?;
39 ctx.set_variable(name, typed_value);
40 Ok(ControlFlow::Continue)
41 }
42
43 ProceduralStatement::Set { name, value } => {
44 let new_value = evaluate_expression(value, db, ctx)?;
46
47 if let Some(var_name) = name.strip_prefix('@') {
49 db.set_session_variable(var_name, new_value);
51 } else if ctx.has_parameter(name) {
52 if let Some(param) = ctx.get_parameter_mut(name) {
54 *param = new_value;
55 }
56 } else if ctx.has_variable(name) {
57 ctx.set_variable(name, new_value);
59 } else {
60 return Err(ExecutorError::VariableNotFound {
61 variable_name: name.clone(),
62 available_variables: ctx.get_available_names(),
63 });
64 }
65
66 Ok(ControlFlow::Continue)
67 }
68
69 ProceduralStatement::Return(expr) => {
70 let value = evaluate_expression(expr, db, ctx)?;
72 Ok(ControlFlow::Return(value))
73 }
74
75 ProceduralStatement::Leave(label) => {
76 if !ctx.has_label(label) {
78 return Err(ExecutorError::LabelNotFound(label.clone()));
79 }
80 Ok(ControlFlow::Leave(label.clone()))
81 }
82
83 ProceduralStatement::Iterate(label) => {
84 if !ctx.has_label(label) {
86 return Err(ExecutorError::LabelNotFound(label.clone()));
87 }
88 Ok(ControlFlow::Iterate(label.clone()))
89 }
90
91 ProceduralStatement::If {
92 condition,
93 then_statements,
94 else_statements,
95 } => super::control_flow::execute_if(condition, then_statements, else_statements, ctx, db),
96
97 ProceduralStatement::While {
98 condition,
99 statements,
100 } => super::control_flow::execute_while(condition, statements, ctx, db),
101
102 ProceduralStatement::Loop { statements } => {
103 super::control_flow::execute_loop(statements, ctx, db)
104 }
105
106 ProceduralStatement::Repeat {
107 statements,
108 condition,
109 } => super::control_flow::execute_repeat(statements, condition, ctx, db),
110
111 ProceduralStatement::Sql(sql_stmt) => {
112 execute_sql_statement(sql_stmt, db, ctx)?;
114 Ok(ControlFlow::Continue)
115 }
116 }
117}
118
119pub fn evaluate_expression(
124 expr: &vibesql_ast::Expression,
125 _db: &mut Database,
126 ctx: &ExecutionContext,
127) -> Result<SqlValue, ExecutorError> {
128 use vibesql_ast::{BinaryOperator, Expression};
129
130 match expr {
131 Expression::ColumnRef { table: None, column } => {
133 if let Some(var_name) = column.strip_prefix('@') {
135 _db.get_session_variable(var_name)
137 .cloned()
138 .ok_or_else(|| ExecutorError::VariableNotFound {
139 variable_name: format!("@{}", var_name),
140 available_variables: vec![], })
142 } else {
143 ctx.get_value(column)
145 .cloned()
146 .ok_or_else(|| ExecutorError::VariableNotFound {
147 variable_name: column.clone(),
148 available_variables: ctx.get_available_names(),
149 })
150 }
151 }
152
153 Expression::Literal(value) => Ok(value.clone()),
155
156 Expression::BinaryOp { left, op, right } => {
158 let left_val = evaluate_expression(left, _db, ctx)?;
159 let right_val = evaluate_expression(right, _db, ctx)?;
160
161 match op {
162 BinaryOperator::Plus => {
163 match (left_val, right_val) {
165 (SqlValue::Integer(l), SqlValue::Integer(r)) => {
166 Ok(SqlValue::Integer(l + r))
167 }
168 _ => Err(ExecutorError::TypeError(
169 "Binary operation only supports integers in Phase 2".to_string()
170 ))
171 }
172 }
173 BinaryOperator::Minus => {
174 match (left_val, right_val) {
175 (SqlValue::Integer(l), SqlValue::Integer(r)) => {
176 Ok(SqlValue::Integer(l - r))
177 }
178 _ => Err(ExecutorError::TypeError(
179 "Binary operation only supports integers in Phase 2".to_string()
180 ))
181 }
182 }
183 BinaryOperator::Multiply => {
184 match (left_val, right_val) {
185 (SqlValue::Integer(l), SqlValue::Integer(r)) => {
186 Ok(SqlValue::Integer(l * r))
187 }
188 _ => Err(ExecutorError::TypeError(
189 "Binary operation only supports integers in Phase 2".to_string()
190 ))
191 }
192 }
193 BinaryOperator::GreaterThan => {
194 match (left_val, right_val) {
195 (SqlValue::Integer(l), SqlValue::Integer(r)) => {
196 Ok(SqlValue::Boolean(l > r))
197 }
198 _ => Err(ExecutorError::TypeError(
199 "Comparison only supports integers in Phase 2".to_string()
200 ))
201 }
202 }
203 _ => Err(ExecutorError::UnsupportedFeature(format!(
204 "Binary operator {:?} not yet supported in procedural expressions", op
205 )))
206 }
207 }
208
209 Expression::Function { name, args, .. } if name.eq_ignore_ascii_case("CONCAT") => {
211 let mut result = String::new();
212 for arg in args {
213 let val = evaluate_expression(arg, _db, ctx)?;
214 result.push_str(&val.to_string());
215 }
216 Ok(SqlValue::Varchar(result))
217 }
218
219 _ => Err(ExecutorError::UnsupportedFeature(format!(
221 "Expression type not yet supported in procedures: {:?}", expr
222 )))
223 }
224}
225
226fn execute_sql_statement(
236 stmt: &Statement,
237 db: &mut Database,
238 ctx: &mut ExecutionContext,
239) -> Result<(), ExecutorError> {
240 match stmt {
241 Statement::Select(select_stmt) => {
242 if let Some(into_vars) = &select_stmt.into_variables {
244 let executor = crate::SelectExecutor::new_with_procedural_context(db, ctx);
246 let results = executor.execute(select_stmt)?;
247
248 if results.len() != 1 {
250 return Err(ExecutorError::SelectIntoRowCount {
251 expected: 1,
252 actual: results.len(),
253 });
254 }
255
256 let row = &results[0];
258
259 if row.values.len() != into_vars.len() {
261 return Err(ExecutorError::SelectIntoColumnCount {
262 expected: into_vars.len(),
263 actual: row.values.len(),
264 });
265 }
266
267 for (var_name, value) in into_vars.iter().zip(row.values.iter()) {
269 ctx.set_variable(var_name, value.clone());
270 }
271
272 Ok(())
273 } else {
274 let executor = crate::SelectExecutor::new_with_procedural_context(db, ctx);
276 let _results = executor.execute(select_stmt)?;
277 Ok(())
278 }
279 }
280 Statement::Insert(insert_stmt) => {
281 let _count = crate::InsertExecutor::execute_with_procedural_context(db, insert_stmt, ctx)?;
283 Ok(())
284 }
285 Statement::Update(update_stmt) => {
286 let _count = crate::UpdateExecutor::execute_with_procedural_context(update_stmt, db, ctx)?;
288 Ok(())
289 }
290 Statement::Delete(delete_stmt) => {
291 let _count = crate::DeleteExecutor::execute_with_procedural_context(delete_stmt, db, ctx)?;
293 Ok(())
294 }
295 _ => {
296 Err(ExecutorError::UnsupportedFeature(format!(
298 "SQL statement type not supported in procedure bodies: {:?}",
299 stmt
300 )))
301 }
302 }
303}
304
305fn cast_to_type(value: SqlValue, target_type: &vibesql_types::DataType) -> Result<SqlValue, ExecutorError> {
307 use vibesql_types::DataType;
308
309 if matches!(value, SqlValue::Null) {
311 return Ok(SqlValue::Null);
312 }
313
314 match target_type {
315 DataType::Integer => match value {
316 SqlValue::Integer(i) => Ok(SqlValue::Integer(i)),
317 SqlValue::Bigint(b) => Ok(SqlValue::Integer(b)),
318 SqlValue::Smallint(s) => Ok(SqlValue::Integer(s as i64)),
319 SqlValue::Varchar(s) | SqlValue::Character(s) => {
320 s.parse::<i64>()
321 .map(SqlValue::Integer)
322 .map_err(|_| ExecutorError::TypeError(format!("Cannot convert '{}' to INTEGER", s)))
323 }
324 _ => Err(ExecutorError::TypeError(format!(
325 "Cannot convert {:?} to INTEGER",
326 value
327 ))),
328 },
329
330 DataType::Varchar { .. } | DataType::Character { .. } => {
331 Ok(SqlValue::Varchar(value.to_string()))
333 }
334
335 DataType::Boolean => match value {
336 SqlValue::Boolean(b) => Ok(SqlValue::Boolean(b)),
337 SqlValue::Integer(i) => Ok(SqlValue::Boolean(i != 0)),
338 SqlValue::Varchar(ref s) | SqlValue::Character(ref s) => {
339 let s_upper = s.to_uppercase();
340 if s_upper == "TRUE" || s_upper == "T" || s_upper == "1" {
341 Ok(SqlValue::Boolean(true))
342 } else if s_upper == "FALSE" || s_upper == "F" || s_upper == "0" {
343 Ok(SqlValue::Boolean(false))
344 } else {
345 Err(ExecutorError::TypeError(format!(
346 "Cannot convert '{}' to BOOLEAN",
347 s
348 )))
349 }
350 }
351 _ => Err(ExecutorError::TypeError(format!(
352 "Cannot convert {:?} to BOOLEAN",
353 value
354 ))),
355 },
356
357 _ => Ok(value),
359 }
360}
361
362