diff --git a/core/translate/analyze.rs b/core/translate/analyze.rs index ece9a558b..665e43e0f 100644 --- a/core/translate/analyze.rs +++ b/core/translate/analyze.rs @@ -97,6 +97,7 @@ pub fn translate_analyze( program.emit_insn(Insn::Delete { cursor_id, table_name: "sqlite_stat1".to_string(), + is_part_of_update: false, }); program.emit_insn(Insn::Next { cursor_id, diff --git a/core/translate/emitter.rs b/core/translate/emitter.rs index 67ead10dc..0d82c8965 100644 --- a/core/translate/emitter.rs +++ b/core/translate/emitter.rs @@ -859,6 +859,7 @@ fn emit_delete_insns( program.emit_insn(Insn::Delete { cursor_id: main_table_cursor_id, table_name: table_name.to_string(), + is_part_of_update: false, }); if let Some(index) = iteration_index { @@ -867,6 +868,7 @@ fn emit_delete_insns( program.emit_insn(Insn::Delete { cursor_id: iteration_index_cursor, table_name: index.name.clone(), + is_part_of_update: false, }); } } @@ -1690,6 +1692,7 @@ fn emit_update_insns( program.emit_insn(Insn::Delete { cursor_id: target_table_cursor_id, table_name: table_name.to_string(), + is_part_of_update: true, }); } diff --git a/core/translate/index.rs b/core/translate/index.rs index 5fa07dc0d..b680f560c 100644 --- a/core/translate/index.rs +++ b/core/translate/index.rs @@ -595,6 +595,7 @@ pub fn translate_drop_index( program.emit_insn(Insn::Delete { cursor_id: sqlite_schema_cursor_id, table_name: "sqlite_schema".to_string(), + is_part_of_update: false, }); program.resolve_label(next_label, program.offset()); diff --git a/core/translate/schema.rs b/core/translate/schema.rs index 53f234c76..5bf0e3682 100644 --- a/core/translate/schema.rs +++ b/core/translate/schema.rs @@ -745,6 +745,7 @@ pub fn translate_drop_table( program.emit_insn(Insn::Delete { cursor_id: sqlite_schema_cursor_id_0, table_name: SQLITE_TABLEID.to_string(), + is_part_of_update: false, }); program.resolve_label(next_label, program.offset()); @@ -945,6 +946,7 @@ pub fn translate_drop_table( program.emit_insn(Insn::Delete { cursor_id: sqlite_schema_cursor_id_1, table_name: SQLITE_TABLEID.to_string(), + is_part_of_update: false, }); program.emit_insn(Insn::Insert { cursor: sqlite_schema_cursor_id_1, @@ -1005,6 +1007,7 @@ pub fn translate_drop_table( program.emit_insn(Insn::Delete { cursor_id: seq_cursor_id, table_name: "sqlite_sequence".to_string(), + is_part_of_update: false, }); program.resolve_label(continue_loop_label, program.offset()); diff --git a/core/translate/upsert.rs b/core/translate/upsert.rs index a3df77bc6..0418402dd 100644 --- a/core/translate/upsert.rs +++ b/core/translate/upsert.rs @@ -726,6 +726,7 @@ pub fn emit_upsert( program.emit_insn(Insn::Delete { cursor_id: tbl_cursor_id, table_name: table.get_name().to_string(), + is_part_of_update: true, }); program.emit_insn(Insn::Insert { cursor: tbl_cursor_id, diff --git a/core/translate/view.rs b/core/translate/view.rs index b9b5ddcc0..47f0822d7 100644 --- a/core/translate/view.rs +++ b/core/translate/view.rs @@ -113,6 +113,7 @@ pub fn translate_create_materialized_view( program.emit_insn(Insn::Delete { cursor_id: view_cursor_id, table_name: normalized_view_name.clone(), + is_part_of_update: false, }); program.emit_insn(Insn::Next { cursor_id: view_cursor_id, @@ -409,6 +410,7 @@ pub fn translate_drop_view( program.emit_insn(Insn::Delete { cursor_id: sqlite_schema_cursor_id, table_name: "sqlite_schema".to_string(), + is_part_of_update: false, }); program.resolve_label(skip_delete_label, program.offset()); diff --git a/core/vdbe/execute.rs b/core/vdbe/execute.rs index e56bd4fdb..82b850ec1 100644 --- a/core/vdbe/execute.rs +++ b/core/vdbe/execute.rs @@ -6046,7 +6046,8 @@ pub fn op_delete( load_insn!( Delete { cursor_id, - table_name + table_name, + is_part_of_update, }, insn ); @@ -6131,9 +6132,13 @@ pub fn op_delete( } state.op_delete_state.sub_state = OpDeleteSubState::MaybeCaptureRecord; - program - .n_change - .fetch_add(1, std::sync::atomic::Ordering::SeqCst); + if !is_part_of_update { + // DELETEs do not count towards the total changes if they are part of an UPDATE statement, + // i.e. the DELETE and subsequent INSERT of a row are the same "change". + program + .n_change + .fetch_add(1, std::sync::atomic::Ordering::SeqCst); + } state.pc += 1; Ok(InsnFunctionStepResult::Step) } diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs index ca5b74ef3..69434de17 100644 --- a/core/vdbe/explain.rs +++ b/core/vdbe/explain.rs @@ -1141,7 +1141,7 @@ pub fn insn_to_row( flag.0 as u16, format!("intkey=r[{key_reg}] data=r[{record_reg}]"), ), - Insn::Delete { cursor_id, table_name } => ( + Insn::Delete { cursor_id, table_name, .. } => ( "Delete", *cursor_id as i32, 0, diff --git a/core/vdbe/insn.rs b/core/vdbe/insn.rs index 64bae3947..a774edad5 100644 --- a/core/vdbe/insn.rs +++ b/core/vdbe/insn.rs @@ -791,6 +791,8 @@ pub enum Insn { Delete { cursor_id: CursorID, table_name: String, + /// Whether the DELETE is part of an UPDATE statement. If so, it doesn't count towards the change counter. + is_part_of_update: bool, }, /// If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error if no matching index entry