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}