vibesql_executor/advanced_objects/
assertions.rs

1//! Executor for ASSERTION objects (SQL:1999 Feature F671/F672)
2
3use vibesql_ast::*;
4use vibesql_storage::Database;
5
6use crate::errors::ExecutorError;
7
8/// Execute CREATE ASSERTION statement (SQL:1999 Feature F671/F672)
9pub fn execute_create_assertion(
10    stmt: &CreateAssertionStmt,
11    db: &mut Database,
12) -> Result<(), ExecutorError> {
13    use vibesql_catalog::Assertion;
14
15    let assertion = Assertion::new(stmt.assertion_name.clone(), (*stmt.check_condition).clone());
16
17    db.catalog.create_assertion(assertion)?;
18    Ok(())
19}
20
21/// Execute DROP ASSERTION statement (SQL:1999 Feature F671/F672)
22pub fn execute_drop_assertion(
23    stmt: &DropAssertionStmt,
24    db: &mut Database,
25) -> Result<(), ExecutorError> {
26    db.catalog.drop_assertion(&stmt.assertion_name, stmt.cascade)?;
27    Ok(())
28}
29
30/// Assertion checker for runtime constraint enforcement (SQL:1999 Feature F671/F672)
31///
32/// This struct provides methods to check all database assertions after DML operations.
33/// Assertions are schema-level constraints that can span multiple tables.
34pub struct AssertionChecker;
35
36impl AssertionChecker {
37    /// Check all assertions in the database
38    ///
39    /// This should be called after INSERT, UPDATE, or DELETE operations to ensure
40    /// that no assertion constraints are violated.
41    ///
42    /// # Arguments
43    /// * `db` - Database reference (immutable, we only read data)
44    ///
45    /// # Returns
46    /// * `Ok(())` if all assertions pass
47    /// * `Err(ExecutorError::AssertionViolation)` if any assertion is violated
48    pub fn check_all_assertions(db: &Database) -> Result<(), ExecutorError> {
49        // Collect assertions first to avoid borrowing issues
50        let assertions: Vec<_> = db
51            .catalog
52            .get_all_assertions()
53            .map(|a| (a.name.clone(), a.check_condition.clone()))
54            .collect();
55
56        // If no assertions, return early
57        if assertions.is_empty() {
58            return Ok(());
59        }
60
61        // Check each assertion
62        for (assertion_name, check_condition) in assertions {
63            Self::check_assertion(db, &assertion_name, &check_condition)?;
64        }
65
66        Ok(())
67    }
68
69    /// Check a single assertion
70    ///
71    /// Evaluates the assertion's CHECK condition and returns an error if it evaluates to FALSE.
72    fn check_assertion(
73        db: &Database,
74        assertion_name: &str,
75        check_condition: &Expression,
76    ) -> Result<(), ExecutorError> {
77        // Build a SELECT statement that evaluates the check condition
78        // SELECT (check_condition) -- should return TRUE if assertion holds
79        let select_stmt = SelectStmt {
80            with_clause: None,
81            distinct: false,
82            select_list: vec![SelectItem::Expression {
83                expr: check_condition.clone(),
84                alias: None,
85                source_text: None,
86            }],
87            into_table: None,
88            into_variables: None,
89            from: None,
90            where_clause: None,
91            group_by: None,
92            having: None,
93            order_by: None,
94            limit: None,
95            offset: None,
96            set_operation: None,
97            values: None,
98        };
99
100        // Execute the SELECT
101        let executor = crate::SelectExecutor::new(db);
102        let rows = executor.execute(&select_stmt)?;
103
104        // Check the result
105        if rows.is_empty() {
106            // No rows returned - this shouldn't happen for a scalar expression
107            // Treat as violation to be safe
108            return Err(ExecutorError::AssertionViolation {
109                assertion_name: assertion_name.to_string(),
110            });
111        }
112
113        // Get the result value
114        let result = &rows[0].values[0];
115
116        // Check if the result is TRUE
117        match result {
118            vibesql_types::SqlValue::Boolean(true) => Ok(()),
119            vibesql_types::SqlValue::Boolean(false) => Err(ExecutorError::AssertionViolation {
120                assertion_name: assertion_name.to_string(),
121            }),
122            vibesql_types::SqlValue::Null => {
123                // NULL is treated as unknown, which in SQL standard means the assertion passes
124                // (only FALSE triggers a violation)
125                Ok(())
126            }
127            vibesql_types::SqlValue::Integer(0) => {
128                // SQLite compatibility: 0 is FALSE
129                Err(ExecutorError::AssertionViolation {
130                    assertion_name: assertion_name.to_string(),
131                })
132            }
133            vibesql_types::SqlValue::Integer(_) => {
134                // SQLite compatibility: non-zero is TRUE
135                Ok(())
136            }
137            _ => {
138                // Other types - treat as TRUE if not explicitly false
139                Ok(())
140            }
141        }
142    }
143}