vibesql_executor/procedural/
control_flow.rs

1//! Control flow execution for procedural statements
2//!
3//! Implements:
4//! - IF/ELSEIF/ELSE conditionals
5//! - WHILE loops
6//! - LOOP (infinite loop with LEAVE)
7//! - REPEAT/UNTIL loops
8
9use crate::errors::ExecutorError;
10use crate::procedural::{ControlFlow, ExecutionContext};
11use vibesql_ast::ProceduralStatement;
12use vibesql_storage::Database;
13use vibesql_types::SqlValue;
14
15// Use the evaluate_expression function from the executor module
16use super::executor::evaluate_expression;
17
18/// Execute IF statement
19pub fn execute_if(
20    condition: &vibesql_ast::Expression,
21    then_statements: &[ProceduralStatement],
22    else_statements: &Option<Vec<ProceduralStatement>>,
23    ctx: &mut ExecutionContext,
24    db: &mut Database,
25) -> Result<ControlFlow, ExecutorError> {
26    // Evaluate condition
27    let condition_value = evaluate_expression(condition, db, ctx)?;
28
29    // Check if condition is true
30    let is_true = match condition_value {
31        SqlValue::Boolean(b) => b,
32        SqlValue::Integer(i) => i != 0,
33        SqlValue::Null => false,
34        _ => {
35            return Err(ExecutorError::TypeError(format!(
36                "IF condition must evaluate to boolean, got {:?}",
37                condition_value
38            )))
39        }
40    };
41
42    // Execute appropriate branch
43    if is_true {
44        for stmt in then_statements {
45            let flow = super::executor::execute_procedural_statement(stmt, ctx, db)?;
46            if flow != ControlFlow::Continue {
47                return Ok(flow);
48            }
49        }
50    } else if let Some(else_stmts) = else_statements {
51        for stmt in else_stmts {
52            let flow = super::executor::execute_procedural_statement(stmt, ctx, db)?;
53            if flow != ControlFlow::Continue {
54                return Ok(flow);
55            }
56        }
57    }
58
59    Ok(ControlFlow::Continue)
60}
61
62/// Execute WHILE loop
63pub fn execute_while(
64    condition: &vibesql_ast::Expression,
65    statements: &[ProceduralStatement],
66    ctx: &mut ExecutionContext,
67    db: &mut Database,
68) -> Result<ControlFlow, ExecutorError> {
69    loop {
70        // Evaluate condition
71        let condition_value = evaluate_expression(condition, db, ctx)?;
72
73        // Check if condition is true
74        let is_true = match condition_value {
75            SqlValue::Boolean(b) => b,
76            SqlValue::Integer(i) => i != 0,
77            SqlValue::Null => false,
78            _ => {
79                return Err(ExecutorError::TypeError(format!(
80                    "WHILE condition must evaluate to boolean, got {:?}",
81                    condition_value
82                )))
83            }
84        };
85
86        if !is_true {
87            break;
88        }
89
90        // Execute loop body
91        for stmt in statements {
92            let flow = super::executor::execute_procedural_statement(stmt, ctx, db)?;
93            match flow {
94                ControlFlow::Continue => {}
95                ControlFlow::Return(_) => return Ok(flow),
96                ControlFlow::Leave(_) => return Ok(flow),
97                ControlFlow::Iterate(_) => break, // Continue to next iteration
98            }
99        }
100    }
101
102    Ok(ControlFlow::Continue)
103}
104
105/// Execute LOOP (infinite loop until LEAVE)
106pub fn execute_loop(
107    statements: &[ProceduralStatement],
108    ctx: &mut ExecutionContext,
109    db: &mut Database,
110) -> Result<ControlFlow, ExecutorError> {
111    loop {
112        for stmt in statements {
113            let flow = super::executor::execute_procedural_statement(stmt, ctx, db)?;
114            match flow {
115                ControlFlow::Continue => {}
116                ControlFlow::Return(_) => return Ok(flow),
117                ControlFlow::Leave(_) => return Ok(ControlFlow::Continue), // Exit loop
118                ControlFlow::Iterate(_) => break, // Continue to next iteration
119            }
120        }
121    }
122}
123
124/// Execute REPEAT/UNTIL loop
125pub fn execute_repeat(
126    statements: &[ProceduralStatement],
127    condition: &vibesql_ast::Expression,
128    ctx: &mut ExecutionContext,
129    db: &mut Database,
130) -> Result<ControlFlow, ExecutorError> {
131    loop {
132        // Execute loop body first
133        for stmt in statements {
134            let flow = super::executor::execute_procedural_statement(stmt, ctx, db)?;
135            match flow {
136                ControlFlow::Continue => {}
137                ControlFlow::Return(_) => return Ok(flow),
138                ControlFlow::Leave(_) => return Ok(flow),
139                ControlFlow::Iterate(_) => break, // Continue to next iteration
140            }
141        }
142
143        // Evaluate condition after body
144        let condition_value = evaluate_expression(condition, db, ctx)?;
145
146        // Check if condition is true (exit condition)
147        let should_exit = match condition_value {
148            SqlValue::Boolean(b) => b,
149            SqlValue::Integer(i) => i != 0,
150            SqlValue::Null => false,
151            _ => {
152                return Err(ExecutorError::TypeError(format!(
153                    "REPEAT UNTIL condition must evaluate to boolean, got {:?}",
154                    condition_value
155                )))
156            }
157        };
158
159        if should_exit {
160            break;
161        }
162    }
163
164    Ok(ControlFlow::Continue)
165}