diff --git a/core/translate/select.rs b/core/translate/select.rs index e8117d022..44079de70 100644 --- a/core/translate/select.rs +++ b/core/translate/select.rs @@ -650,6 +650,7 @@ fn translate_table_open_cursor( * if condition checks pass, it will eventually be set to true */ fn left_join_match_flag_initialize(program: &mut ProgramBuilder, left_join: &LeftJoinBookkeeping) { + program.add_comment(program.offset(), "init LEFT JOIN match flag"); program.emit_insn(Insn::Integer { value: 0, dest: left_join.match_flag_register, @@ -664,6 +665,7 @@ fn left_join_match_flag_set_true(program: &mut ProgramBuilder, left_join: &LeftJ left_join.set_match_flag_true_label, program.offset() as usize, ); + program.add_comment(program.offset(), "record LEFT JOIN hit"); program.emit_insn(Insn::Integer { value: 1, dest: left_join.match_flag_register, diff --git a/core/vdbe/builder.rs b/core/vdbe/builder.rs index 133ec781e..a20560334 100644 --- a/core/vdbe/builder.rs +++ b/core/vdbe/builder.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; use crate::storage::sqlite3_ondisk::DatabaseHeader; @@ -21,6 +21,8 @@ pub struct ProgramBuilder { deferred_label_resolutions: Vec<(BranchOffset, InsnReference)>, // Bitmask of cursors that have emitted a SeekRowid instruction. seekrowid_emitted_bitmask: u64, + // map of instruction index to manual comment (used in EXPLAIN) + comments: HashMap, } impl ProgramBuilder { @@ -36,6 +38,7 @@ impl ProgramBuilder { constant_insns: Vec::new(), deferred_label_resolutions: Vec::new(), seekrowid_emitted_bitmask: 0, + comments: HashMap::new(), } } @@ -90,6 +93,10 @@ impl ProgramBuilder { } } + pub fn add_comment(&mut self, insn_index: BranchOffset, comment: &'static str) { + self.comments.insert(insn_index, comment); + } + // 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. @@ -341,6 +348,7 @@ impl ProgramBuilder { insns: self.insns, cursor_ref: self.cursor_ref, database_header, + comments: self.comments, } } } diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs index 3a955440f..a05eb9f58 100644 --- a/core/vdbe/explain.rs +++ b/core/vdbe/explain.rs @@ -1,7 +1,13 @@ use super::{Insn, InsnReference, OwnedValue, Program}; use std::rc::Rc; -pub fn insn_to_str(program: &Program, addr: InsnReference, insn: &Insn, indent: String) -> String { +pub fn insn_to_str( + program: &Program, + addr: InsnReference, + insn: &Insn, + indent: String, + manual_comment: Option<&'static str>, +) -> String { let (opcode, p1, p2, p3, p4, p5, comment): (&str, i32, i32, i32, OwnedValue, u16, String) = match insn { Insn::Init { target_pc } => ( @@ -680,6 +686,6 @@ pub fn insn_to_str(program: &Program, addr: InsnReference, insn: &Insn, indent: p3, p4.to_string(), p5, - comment + manual_comment.map_or(format!("{}", comment), |mc| format!("{}; {}", comment, mc)) ) } diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 29813c98e..4fa6c6d71 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -34,7 +34,7 @@ use crate::Result; use regex::Regex; use std::borrow::BorrowMut; use std::cell::RefCell; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use std::rc::Rc; pub type BranchOffset = i64; @@ -389,6 +389,7 @@ pub struct Program { pub insns: Vec, pub cursor_ref: Vec<(Option, Option)>, pub database_header: Rc>, + pub comments: HashMap, } impl Program { @@ -1468,12 +1469,24 @@ fn trace_insn(program: &Program, addr: InsnReference, insn: &Insn) { } log::trace!( "{}", - explain::insn_to_str(program, addr, insn, String::new()) + explain::insn_to_str( + program, + addr, + insn, + String::new(), + program.comments.get(&(addr as BranchOffset)).copied() + ) ); } fn print_insn(program: &Program, addr: InsnReference, insn: &Insn, indent: String) { - let s = explain::insn_to_str(program, addr, insn, indent); + let s = explain::insn_to_str( + program, + addr, + insn, + indent, + program.comments.get(&(addr as BranchOffset)).copied(), + ); println!("{}", s); }