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}