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}