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