vibesql_executor/update/mod.rs
1//! UPDATE statement execution
2//!
3//! This module provides UPDATE statement execution with the following architecture:
4//!
5//! - `row_selector`: Handles WHERE clause evaluation and primary key index optimization
6//! - `value_updater`: Applies assignment expressions to rows
7//! - `constraints`: Validates NOT NULL, PRIMARY KEY, UNIQUE, and CHECK constraints
8//! - `foreign_keys`: Validates foreign key constraints and child references
9//! - `fast_path`: Fast path optimizations for single-row PK updates
10//! - `triggers`: Trigger execution and view update handling
11//! - `index_sync`: Index maintenance coordination for REPLACE operations
12//! - `executor`: Core execution orchestration
13//!
14//! The main `UpdateExecutor` orchestrates these components to implement SQL's two-phase
15//! update semantics: first collect all updates evaluating against original rows, then
16//! apply all updates atomically.
17//!
18//! ## Performance Optimizations
19//!
20//! The executor includes a fast path for single-row primary key updates that:
21//! - Skips trigger checks when no triggers exist for the table
22//! - Avoids schema cloning
23//! - Uses single-pass execution instead of two-phase
24//! - Minimizes allocations
25
26mod constraints;
27mod executor;
28mod fast_path;
29mod foreign_keys;
30mod from_clause;
31mod index_sync;
32mod row_selector;
33mod triggers;
34mod value_updater;
35
36use vibesql_ast::UpdateStmt;
37use vibesql_storage::Database;
38
39use crate::errors::ExecutorError;
40
41// Re-export for external use
42pub use triggers::execute_update_with_trigger_context;
43
44/// Executor for UPDATE statements
45pub struct UpdateExecutor;
46
47impl UpdateExecutor {
48 /// Execute an UPDATE statement
49 ///
50 /// # Arguments
51 ///
52 /// * `stmt` - The UPDATE statement AST node
53 /// * `database` - The database to update
54 ///
55 /// # Returns
56 ///
57 /// Number of rows updated or error
58 ///
59 /// # Examples
60 ///
61 /// ```
62 /// use vibesql_ast::{Assignment, Expression, UpdateStmt};
63 /// use vibesql_catalog::{ColumnSchema, TableSchema};
64 /// use vibesql_executor::UpdateExecutor;
65 /// use vibesql_storage::Database;
66 /// use vibesql_types::{DataType, SqlValue};
67 ///
68 /// let mut db = Database::new();
69 ///
70 /// // Create table
71 /// let schema = TableSchema::new(
72 /// "employees".to_string(),
73 /// vec![
74 /// ColumnSchema::new("id".to_string(), DataType::Integer, false),
75 /// ColumnSchema::new("salary".to_string(), DataType::Integer, false),
76 /// ],
77 /// );
78 /// db.create_table(schema).unwrap();
79 ///
80 /// // Insert a row
81 /// db.insert_row(
82 /// "employees",
83 /// vibesql_storage::Row::new(vec![SqlValue::Integer(1), SqlValue::Integer(50000)]),
84 /// )
85 /// .unwrap();
86 ///
87 /// // Update salary
88 /// let stmt = UpdateStmt {
89 /// with_clause: None,
90 /// table_name: "employees".to_string(),
91 /// quoted: false,
92 /// alias: None,
93 /// assignments: vec![Assignment {
94 /// column: "salary".to_string(),
95 /// value: Expression::Literal(SqlValue::Integer(60000)),
96 /// }],
97 /// from_clause: None,
98 /// where_clause: None,
99 /// conflict_clause: None,
100 /// };
101 ///
102 /// let count = UpdateExecutor::execute(&stmt, &mut db).unwrap();
103 /// assert_eq!(count, 1);
104 /// ```
105 pub fn execute(stmt: &UpdateStmt, database: &mut Database) -> Result<usize, ExecutorError> {
106 executor::execute_internal(stmt, database, None, None, None)
107 }
108
109 /// Execute an UPDATE statement with procedural context
110 /// Supports procedural variables in SET and WHERE clauses
111 pub fn execute_with_procedural_context(
112 stmt: &UpdateStmt,
113 database: &mut Database,
114 procedural_context: &crate::procedural::ExecutionContext,
115 ) -> Result<usize, ExecutorError> {
116 executor::execute_internal(stmt, database, None, Some(procedural_context), None)
117 }
118
119 /// Execute an UPDATE statement with trigger context
120 /// This allows UPDATE statements within trigger bodies to reference OLD/NEW pseudo-variables
121 pub fn execute_with_trigger_context(
122 stmt: &UpdateStmt,
123 database: &mut Database,
124 trigger_context: &crate::trigger_execution::TriggerContext,
125 ) -> Result<usize, ExecutorError> {
126 executor::execute_internal(stmt, database, None, None, Some(trigger_context))
127 }
128
129 /// Execute an UPDATE statement with optional pre-fetched schema
130 ///
131 /// This method allows cursor-level schema caching to reduce redundant catalog lookups.
132 /// If schema is provided, skips the catalog lookup step.
133 ///
134 /// # Arguments
135 ///
136 /// * `stmt` - The UPDATE statement AST node
137 /// * `database` - The database to update
138 /// * `schema` - Optional pre-fetched schema (from cursor cache)
139 ///
140 /// # Returns
141 ///
142 /// Number of rows updated or error
143 pub fn execute_with_schema(
144 stmt: &UpdateStmt,
145 database: &mut Database,
146 schema: Option<&vibesql_catalog::TableSchema>,
147 ) -> Result<usize, ExecutorError> {
148 executor::execute_internal(stmt, database, schema, None, None)
149 }
150}