Merge 'Support indent for Goto opcode when executing explain' from meteorgan

it works as expected
```
limbo> explain insert into tb1 select * from tb2 union select * from tb3;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     29    0                    0   Start at 29
1     InitCoroutine      1     21    2                    0
2     OpenEphemeral      0     0     0                    0   cursor=0 is_table=false
3     OpenRead           1     3     0                    0   table=tb2, root=3
4     Rewind             1     9     0                    0   Rewind table tb2
5       Column           1     0     2                    0   r[2]=tb2.age
6       MakeRecord       2     1     3                    0   r[3]=mkrec(r[2..2]); for union_dedupe
7       IdxInsert        0     3     0                    0   key=r[3]
8     Next               1     5     0                    0
9     OpenRead           2     4     0                    0   table=tb3, root=4
10    Rewind             2     15    0                    0   Rewind table tb3
11      Column           2     0     2                    0   r[2]=tb3.age
12      MakeRecord       2     1     4                    0   r[4]=mkrec(r[2..2]); for union_dedupe
13      IdxInsert        0     4     0                    0   key=r[4]
14    Next               2     11    0                    0
15    Rewind             0     18    0                    0   Rewind  union_dedupe
16      Column           0     0     2                    0   r[2]=union_dedupe.age
17      Yield            1     0     0                    0
18    Next               0     16    0                    0
19    Close              0     0     0                    0
20    EndCoroutine       1     0     0                    0
21    OpenWrite          3     2     0                    0   root=2; tb1
22      Yield            1     28    0                    0
23      Copy             2     7     0                    0   r[7]=r[2]
24      NewRowid         3     6     0                    0   r[6]=rowid
25      MakeRecord       7     1     8                    0   r[8]=mkrec(r[7..7])
26      Insert           3     8     6     tb1            0   intkey=r[6] data=r[8]
27    Goto               0     22    0                    0
28    Halt               0     0     0                    0
29    Transaction        0     1     0                    0   write=true
30    Goto               0     1     0                    0
```

Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #1775
This commit is contained in:
Jussi Saurio
2025-06-20 21:57:40 +03:00

View File

@@ -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<usize> {
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> {