From 5c7d1423e784a742eb28e920dbf2773e9e88b920 Mon Sep 17 00:00:00 2001 From: meteorgan Date: Tue, 17 Jun 2025 23:55:39 +0800 Subject: [PATCH] Support indent for Goto opcode when executing explain --- core/vdbe/mod.rs | 76 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 046715e4d..34672c68f 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -471,11 +471,10 @@ impl Program { let mut buff = String::with_capacity(1024); buff.push_str("addr opcode p1 p2 p3 p4 p5 comment\n"); buff.push_str("---- ----------------- ---- ---- ---- ------------- -- -------\n"); - let mut indent_count: usize = 0; let indent = " "; - let mut prev_insn: Option<&Insn> = None; + let indent_counts = get_indent_counts(&self.insns); for (addr, (insn, _)) in self.insns.iter().enumerate() { - indent_count = get_indent_count(indent_count, insn, prev_insn); + let indent_count = indent_counts[addr]; print_insn( self, addr as InsnReference, @@ -484,7 +483,6 @@ impl Program { &mut buff, ); buff.push('\n'); - prev_insn = Some(insn); } buff } @@ -565,27 +563,63 @@ fn print_insn(program: &Program, addr: InsnReference, insn: &Insn, indent: Strin w.push_str(&s); } -fn get_indent_count(indent_count: usize, curr_insn: &Insn, prev_insn: Option<&Insn>) -> usize { - let indent_count = if let Some(insn) = prev_insn { +// The indenting rules are(from SQLite): +// +// * For each "Next", "Prev", "VNext" or "VPrev" instruction, increase the ident number for +// all opcodes that occur between the p2 jump destination and the opcode itself. +// +// * Do the previous for "Return" instructions for when P2 is positive. +// +// * For each "Goto", if the jump destination is earlier in the program and ends on one of: +// Yield SeekGt SeekLt RowSetRead Rewind +// or if the P1 parameter is one instead of zero, then increase the indent number for all +// opcodes between the earlier instruction and "Goto" +fn get_indent_counts(insns: &Vec<(Insn, InsnFunction)>) -> Vec { + let mut indents = vec![0; insns.len()]; + + for (i, (insn, _)) in insns.iter().enumerate() { + let mut start = 0; + let mut end = 0; match insn { - Insn::Rewind { .. } - | Insn::Last { .. } - | Insn::SorterSort { .. } - | Insn::SeekGE { .. } - | Insn::SeekGT { .. } - | Insn::SeekLE { .. } - | Insn::SeekLT { .. } => indent_count + 1, + Insn::Next { pc_if_next, .. } | Insn::VNext { pc_if_next, .. } => { + let dest = pc_if_next.to_debug_int() as usize; + if dest < i { + start = dest; + end = i; + } + } + Insn::Prev { pc_if_prev, .. } => { + let dest = pc_if_prev.to_debug_int() as usize; + if dest < i { + start = dest; + end = i; + } + } - _ => indent_count, + Insn::Goto { target_pc } => { + let dest = target_pc.to_debug_int() as usize; + if dest < i + && matches!( + insns.get(dest).map(|(insn, _)| insn), + Some(Insn::Yield { .. }) + | Some(Insn::SeekGT { .. }) + | Some(Insn::SeekLT { .. }) + | Some(Insn::Rewind { .. }) + ) + { + start = dest; + end = i; + } + } + + _ => {} + } + for i in start..end { + indents[i] += 1; } - } else { - indent_count - }; - - match curr_insn { - Insn::Next { .. } | Insn::SorterNext { .. } | Insn::Prev { .. } => indent_count - 1, - _ => indent_count, } + + indents } pub trait FromValueRow<'a> {