diff --git a/core/error.rs b/core/error.rs index 2f22dd706..5e5ac89bf 100644 --- a/core/error.rs +++ b/core/error.rs @@ -55,6 +55,8 @@ pub enum LimboError { IntegerOverflow, #[error("Schema is locked for write")] SchemaLocked, + #[error("Runtime error: database table is locked")] + TableLocked, #[error("Error: Resource is read-only")] ReadOnly, #[error("Database is busy")] diff --git a/core/vdbe/execute.rs b/core/vdbe/execute.rs index 4f73be5b1..6a6d55657 100644 --- a/core/vdbe/execute.rs +++ b/core/vdbe/execute.rs @@ -334,6 +334,13 @@ pub fn op_checkpoint( else { unreachable!("unexpected Insn {:?}", insn) }; + if !program.connection.auto_commit.get() { + // TODO: sqlite returns "Runtime error: database table is locked (6)" when a table is in use + // when a checkpoint is attempted. We don't have table locks, so return TableLocked for any + // attempt to checkpoint in an interactive transaction. This does not end the transaction, + // however. + return Err(LimboError::TableLocked); + } let result = program.connection.checkpoint(*checkpoint_mode); match result { Ok(CheckpointResult { diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 5d9d1fb67..1e67932d3 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -757,7 +757,10 @@ pub fn handle_program_error( err: &LimboError, ) -> Result<()> { match err { + // Transaction errors, e.g. trying to start a nested transaction, do not cause a rollback. LimboError::TxError(_) => {} + // Table locked errors, e.g. trying to checkpoint in an interactive transaction, do not cause a rollback. + LimboError::TableLocked => {} _ => { let state = connection.transaction_state.get(); if let TransactionState::Write { schema_did_change } = state {