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}