Skip to main content

vibesql_executor/insert/
mod.rs

1mod bulk_transfer;
2pub(crate) mod constraints;
3pub mod defaults;
4mod duplicate_key_update;
5mod execution;
6mod foreign_keys;
7mod on_conflict_update;
8mod replace;
9mod row_validator;
10pub mod validation;
11
12use crate::errors::ExecutorError;
13
14pub use execution::InsertOutcome;
15
16/// Executor for INSERT statements
17pub struct InsertExecutor;
18
19impl InsertExecutor {
20    /// Execute an INSERT statement
21    /// Returns number of rows inserted
22    pub fn execute(
23        db: &mut vibesql_storage::Database,
24        stmt: &vibesql_ast::InsertStmt,
25    ) -> Result<usize, ExecutorError> {
26        // Wrap the top-level statement in SQLite's per-variant RAISE scope
27        // handling (#5417): inside an explicit transaction, RAISE(ABORT) undoes
28        // just this statement, RAISE(FAIL) keeps its partial changes, and
29        // RAISE(ROLLBACK) rolls back the whole transaction.
30        let may_fire = crate::raise_scope::table_may_fire_trigger(db, &stmt.table_name);
31        crate::raise_scope::run_top_level_dml(db, may_fire, |db| execution::execute_insert(db, stmt))
32    }
33
34    /// Execute an INSERT statement, capturing RETURNING rows (SQLite 3.35.0+)
35    ///
36    /// Returns an [`InsertOutcome`] carrying the number of affected rows, the
37    /// number of rows handled via the upsert `ON CONFLICT DO UPDATE` arm, and,
38    /// when the statement carries a RETURNING clause, the projected NEW rows
39    /// (values as actually inserted, including defaults, generated columns,
40    /// and auto INTEGER PRIMARY KEY). Rows skipped by `OR IGNORE` /
41    /// `ON CONFLICT DO NOTHING` are omitted; `ON DUPLICATE KEY UPDATE`
42    /// contributes the post-UPDATE row.
43    ///
44    /// When the statement has no RETURNING clause `returning` is `None`.
45    pub fn execute_returning(
46        db: &mut vibesql_storage::Database,
47        stmt: &vibesql_ast::InsertStmt,
48    ) -> Result<InsertOutcome, ExecutorError> {
49        // Per-variant RAISE scope for RETURNING DML (#5432, follow-on to #5417):
50        // a RAISE fired from a trigger during a RETURNING INSERT gets the same
51        // statement-savepoint scope as the bare `execute` path. When the RAISE
52        // aborts the statement the error propagates (no rows returned) and the
53        // chosen variant's scope is applied.
54        let may_fire = crate::raise_scope::table_may_fire_trigger(db, &stmt.table_name);
55        crate::raise_scope::run_top_level_dml(db, may_fire, |db| {
56            execution::execute_insert_returning(db, stmt)
57        })
58    }
59
60    /// Execute an INSERT statement with procedural context
61    /// Supports procedural variables in VALUES clause
62    /// Returns number of rows inserted
63    pub fn execute_with_procedural_context(
64        db: &mut vibesql_storage::Database,
65        stmt: &vibesql_ast::InsertStmt,
66        procedural_context: &crate::procedural::ExecutionContext,
67    ) -> Result<usize, ExecutorError> {
68        // Per-variant RAISE scope for procedural-context DML (#5432): a RAISE
69        // fired from a trigger inside a procedure/script gets the same
70        // statement-savepoint scope as the bare `execute` path.
71        let may_fire = crate::raise_scope::table_may_fire_trigger(db, &stmt.table_name);
72        crate::raise_scope::run_top_level_dml(db, may_fire, |db| {
73            execution::execute_insert_with_procedural_context(db, stmt, procedural_context)
74        })
75    }
76}
77
78/// Execute an INSERT statement with trigger context
79/// This function is used when executing INSERT statements within trigger bodies
80/// to support OLD/NEW pseudo-variable references
81pub fn execute_insert_with_trigger_context(
82    db: &mut vibesql_storage::Database,
83    stmt: &vibesql_ast::InsertStmt,
84    trigger_context: &crate::trigger_execution::TriggerContext,
85) -> Result<usize, ExecutorError> {
86    execution::execute_insert_with_trigger_context(db, stmt, trigger_context)
87}