diff --git a/core/translate.rs b/core/translate.rs index f736abb25..afda047dc 100644 --- a/core/translate.rs +++ b/core/translate.rs @@ -269,6 +269,7 @@ fn translate_select(mut select: Select) -> Result { } program.resolve_label(init_label, program.offset()); program.emit_insn(Insn::Transaction); + program.emit_run_once_insns(); program.emit_insn(Insn::Goto { target_pc: start_offset, }); @@ -501,7 +502,15 @@ fn translate_condition_expr( let e1_reg = program.alloc_register(); let e2_reg = program.alloc_register(); let _ = translate_expr(program, select, e1, e1_reg)?; + match e1.as_ref() { + ast::Expr::Literal(_) => program.move_last_insn_out_of_loop(), + _ => {} + } let _ = translate_expr(program, select, e2, e2_reg)?; + match e2.as_ref() { + ast::Expr::Literal(_) => program.move_last_insn_out_of_loop(), + _ => {} + } if jump_target < 0 { program.add_label_dependency(jump_target, program.offset()); } @@ -1030,6 +1039,7 @@ fn translate_pragma( program.emit_insn(Insn::Halt); program.resolve_label(init_label, program.offset()); program.emit_insn(Insn::Transaction); + program.emit_run_once_insns(); program.emit_insn(Insn::Goto { target_pc: start_offset, }); diff --git a/core/vdbe.rs b/core/vdbe.rs index e56732e6d..58f8dbe29 100644 --- a/core/vdbe.rs +++ b/core/vdbe.rs @@ -225,6 +225,8 @@ pub struct ProgramBuilder { next_free_label: BranchOffset, next_free_cursor_id: usize, insns: Vec, + // for temporarily storing instructions that will be put after Transaction opcode + run_once_insns: Vec, // Each label has a list of InsnReferences that must // be resolved. Lists are indexed by: label.abs() - 1 unresolved_labels: Vec>, @@ -243,6 +245,7 @@ impl ProgramBuilder { unresolved_labels: Vec::new(), next_insn_label: None, cursor_ref: Vec::new(), + run_once_insns: Vec::new(), } } @@ -281,6 +284,18 @@ impl ProgramBuilder { } } + // Emit an instruction that will be put at the end of the program (after Transaction statement). + // This is useful for instructions that otherwise will be unnecessarily repeated in a loop. + // Example: In `SELECT * from users where name='John'`, it is unnecessary to set r[1]='John' as we SCAN users table. + // We could simply set it once before the SCAN started. + pub fn move_last_insn_out_of_loop(&mut self) { + self.run_once_insns.push(self.insns.pop().unwrap()); + } + + pub fn emit_run_once_insns(&mut self) { + self.insns.extend(self.run_once_insns.drain(..)); + } + pub fn emit_insn_with_label_dependency(&mut self, insn: Insn, label: BranchOffset) { self.insns.push(insn); self.add_label_dependency(label, (self.insns.len() - 1) as BranchOffset); @@ -430,6 +445,10 @@ impl ProgramBuilder { } pub fn build(self) -> Program { + assert!( + self.run_once_insns.is_empty(), + "run_once_insns is not empty when build() is called, did you forget to call emit_run_once_insns()?" + ); Program { max_registers: self.next_free_register, insns: self.insns,