VDBE: handle subtransaction commits/aborts in op_halt

This commit is contained in:
Jussi Saurio
2025-10-21 12:12:02 +03:00
parent f0548c280f
commit 904cbe535d

View File

@@ -1,5 +1,5 @@
#![allow(unused_variables)]
use crate::error::{SQLITE_CONSTRAINT_FOREIGNKEY, SQLITE_CONSTRAINT_UNIQUE};
use crate::error::SQLITE_CONSTRAINT_UNIQUE;
use crate::function::AlterTableFunc;
use crate::mvcc::database::CheckpointStateMachine;
use crate::numeric::{NullableInteger, Numeric};
@@ -21,7 +21,7 @@ use crate::util::{
normalize_ident, rewrite_column_references_if_needed, rewrite_fk_parent_cols_if_self_ref,
};
use crate::vdbe::insn::InsertFlags;
use crate::vdbe::{registers_to_ref_values, TxnCleanup};
use crate::vdbe::{registers_to_ref_values, EndStatement, TxnCleanup};
use crate::vector::{vector32_sparse, vector_concat, vector_distance_jaccard, vector_slice};
use crate::{
error::{
@@ -2149,8 +2149,8 @@ pub fn halt(
description: &str,
) -> Result<InsnFunctionStepResult> {
if err_code > 0 {
// invalidate page cache in case of error
pager.clear_page_cache(false);
// Any non-FK constraint violation causes the statement subtransaction to roll back.
state.end_statement(&program.connection, &pager, EndStatement::RollbackSavepoint)?;
}
match err_code {
0 => {}
@@ -2169,9 +2169,6 @@ pub fn halt(
"UNIQUE constraint failed: {description} (19)"
)));
}
SQLITE_CONSTRAINT_FOREIGNKEY => {
return Err(LimboError::Constraint(format!("{description} (19)")));
}
_ => {
return Err(LimboError::Constraint(format!(
"undocumented halt error code {description}"
@@ -2181,23 +2178,46 @@ pub fn halt(
let auto_commit = program.connection.auto_commit.load(Ordering::SeqCst);
tracing::trace!("halt(auto_commit={})", auto_commit);
// Check for immediate foreign key violations.
// Any immediate violation causes the statement subtransaction to roll back.
if program.connection.foreign_keys_enabled()
&& state
.fk_immediate_violations_during_stmt
.load(Ordering::Acquire)
> 0
{
state.end_statement(&program.connection, &pager, EndStatement::RollbackSavepoint)?;
return Err(LimboError::Constraint(
"foreign key constraint failed".to_string(),
));
}
if auto_commit {
// In autocommit mode, a statement that leaves deferred violations must fail here.
if program.connection.foreign_keys_enabled()
&& program
// In autocommit mode, a statement that leaves deferred violations must fail here,
// and it also ends the transaction.
if program.connection.foreign_keys_enabled() {
let deferred_violations = program
.connection
.fk_deferred_violations
.swap(0, Ordering::AcqRel)
> 0
{
return Err(LimboError::Constraint(
"foreign key constraint failed".to_string(),
));
.swap(0, Ordering::AcqRel);
if deferred_violations > 0 {
pager.rollback_tx(&program.connection);
program.connection.set_tx_state(TransactionState::None);
program.connection.auto_commit.store(true, Ordering::SeqCst);
return Err(LimboError::Constraint(
"foreign key constraint failed".to_string(),
));
}
}
state.end_statement(&program.connection, &pager, EndStatement::ReleaseSavepoint)?;
program
.commit_txn(pager.clone(), state, mv_store, false)
.map(Into::into)
} else {
// Even if deferred violations are present, the statement subtransaction completes successfully when
// it is part of an interactive transaction.
state.end_statement(&program.connection, &pager, EndStatement::ReleaseSavepoint)?;
Ok(InsnFunctionStepResult::Done)
}
}