Skip to main content

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            window_definitions: None,
94            order_by: None,
95            limit: None,
96            offset: None,
97            set_operation: None,
98            values: None,
99        };
100
101        // Execute the SELECT
102        let executor = crate::SelectExecutor::new(db);
103        let rows = executor.execute(&select_stmt)?;
104
105        // Check the result
106        if rows.is_empty() {
107            // No rows returned - this shouldn't happen for a scalar expression
108            // Treat as violation to be safe
109            return Err(ExecutorError::AssertionViolation {
110                assertion_name: assertion_name.to_string(),
111            });
112        }
113
114        // Get the result value
115        let result = &rows[0].values[0];
116
117        // Check if the result is TRUE
118        match result {
119            vibesql_types::SqlValue::Boolean(true) => Ok(()),
120            vibesql_types::SqlValue::Boolean(false) => Err(ExecutorError::AssertionViolation {
121                assertion_name: assertion_name.to_string(),
122            }),
123            vibesql_types::SqlValue::Null => {
124                // NULL is treated as unknown, which in SQL standard means the assertion passes
125                // (only FALSE triggers a violation)
126                Ok(())
127            }
128            vibesql_types::SqlValue::Integer(0) => {
129                // SQLite compatibility: 0 is FALSE
130                Err(ExecutorError::AssertionViolation {
131                    assertion_name: assertion_name.to_string(),
132                })
133            }
134            vibesql_types::SqlValue::Integer(_) => {
135                // SQLite compatibility: non-zero is TRUE
136                Ok(())
137            }
138            _ => {
139                // Other types - treat as TRUE if not explicitly false
140                Ok(())
141            }
142        }
143    }
144}