diff --git a/COMPAT.md b/COMPAT.md index e64369bbe..cf47b09a9 100644 --- a/COMPAT.md +++ b/COMPAT.md @@ -412,7 +412,7 @@ Modifiers: | AggStep | Yes | | | AggStep | Yes | | | And | Yes | | -| AutoCommit | No | | +| AutoCommit | Yes | | | BitAnd | Yes | | | BitNot | Yes | | | BitOr | Yes | | diff --git a/core/error.rs b/core/error.rs index cfe1a827d..a648117c9 100644 --- a/core/error.rs +++ b/core/error.rs @@ -19,6 +19,8 @@ pub enum LimboError { ConversionError(String), #[error("Env variable error: {0}")] EnvVarError(#[from] std::env::VarError), + #[error("Transaction error: {0}")] + TxError(String), #[error("I/O error: {0}")] IOError(#[from] std::io::Error), #[cfg(all(target_os = "linux", feature = "io_uring"))] diff --git a/core/lib.rs b/core/lib.rs index dbeb04906..c8a76a60b 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -161,6 +161,7 @@ impl Database { pager, schema: schema.clone(), header, + auto_commit: RefCell::new(true), transaction_state: RefCell::new(TransactionState::None), last_insert_rowid: Cell::new(0), last_change: Cell::new(0), @@ -179,6 +180,7 @@ impl Database { schema: self.schema.clone(), header: self.header.clone(), last_insert_rowid: Cell::new(0), + auto_commit: RefCell::new(true), transaction_state: RefCell::new(TransactionState::None), last_change: Cell::new(0), total_changes: Cell::new(0), @@ -263,6 +265,7 @@ pub struct Connection { pager: Rc, schema: Rc>, header: Rc>, + auto_commit: RefCell, transaction_state: RefCell, last_insert_rowid: Cell, last_change: Cell, diff --git a/core/vdbe/builder.rs b/core/vdbe/builder.rs index 68d0b2f4a..b86d65b97 100644 --- a/core/vdbe/builder.rs +++ b/core/vdbe/builder.rs @@ -448,7 +448,6 @@ impl ProgramBuilder { database_header, comments: self.comments, connection, - auto_commit: true, parameters: self.parameters, n_change: Cell::new(0), change_cnt_on, diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs index 79c96a44e..1b956cc9e 100644 --- a/core/vdbe/explain.rs +++ b/core/vdbe/explain.rs @@ -1238,6 +1238,18 @@ pub fn insn_to_str( 0, "".to_string(), ), + Insn::AutoCommit { + auto_commit, + rollback, + } => ( + "AutoCommit", + *auto_commit as i32, + *rollback as i32, + 0, + OwnedValue::build_text(""), + 0, + format!("auto_commit={}, rollback={}", auto_commit, rollback), + ), }; format!( "{:<4} {:<17} {:<4} {:<4} {:<4} {:<13} {:<2} {}", diff --git a/core/vdbe/insn.rs b/core/vdbe/insn.rs index e58de109c..d1b27399d 100644 --- a/core/vdbe/insn.rs +++ b/core/vdbe/insn.rs @@ -320,6 +320,12 @@ pub enum Insn { write: bool, }, + // Set database auto-commit mode and potentially rollback. + AutoCommit { + auto_commit: bool, + rollback: bool, + }, + // Branch to the given PC. Goto { target_pc: BranchOffset, diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 81f793e44..9979f3e1e 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -411,7 +411,6 @@ pub struct Program { pub comments: Option>, pub parameters: crate::parameters::Parameters, pub connection: Weak, - pub auto_commit: bool, pub n_change: Cell, pub change_cnt_on: bool, pub result_columns: Vec, @@ -1131,17 +1130,18 @@ impl Program { ))); } } - tracing::trace!("Halt auto_commit {}", self.auto_commit); let connection = self .connection .upgrade() .expect("only weak ref to connection?"); + let auto_commit = *connection.auto_commit.borrow(); + tracing::trace!("Halt auto_commit {}", auto_commit); let current_state = connection.transaction_state.borrow().clone(); if current_state == TransactionState::Read { pager.end_read_tx()?; return Ok(StepResult::Done); } - return if self.auto_commit { + return if auto_commit { match pager.end_tx() { Ok(crate::storage::wal::CheckpointStatus::IO) => Ok(StepResult::IO), Ok(crate::storage::wal::CheckpointStatus::Done(_)) => { @@ -1195,6 +1195,34 @@ impl Program { } state.pc += 1; } + Insn::AutoCommit { + auto_commit, + rollback, + } => { + let conn = self.connection.upgrade().unwrap(); + if *auto_commit != *conn.auto_commit.borrow() { + if *rollback { + todo!("Rollback is not implemented"); + } else { + conn.auto_commit.replace(*auto_commit); + } + } else { + if !*auto_commit { + return Err(LimboError::TxError( + "cannot start a transaction within a transaction".to_string(), + )); + } else if *rollback { + return Err(LimboError::TxError( + "cannot rollback - no transaction is active".to_string(), + )); + } else { + return Err(LimboError::TxError( + "cannot commit - no transaction is active".to_string(), + )); + } + } + state.pc += 1; + } Insn::Goto { target_pc } => { assert!(target_pc.is_offset()); state.pc = target_pc.to_offset_int();