mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-12 11:44:21 +01:00
Merge 'Add ability to annotate instructions with comments' from Jussi Saurio
Similar to what SQLite does. ``` limbo> EXPLAIN SELECT u.age, p.name FROM users u LEFT JOIN products p ON u.first_name = p.name; addr opcode p1 p2 p3 p4 p5 comment ---- ----------------- ---- ---- ---- ------------- -- ------- 0 Init 0 25 0 0 Start at 25 1 OpenReadAsync 0 2 0 0 table=u, root=2 2 OpenReadAwait 0 0 0 0 3 OpenReadAsync 1 3 0 0 table=p, root=3 4 OpenReadAwait 0 0 0 0 5 RewindAsync 0 0 0 0 6 RewindAwait 0 -5 0 0 Rewind table u 7 Integer 0 1 0 0 r[1]=0; init LEFT JOIN match flag 8 RewindAsync 1 0 0 0 9 RewindAwait 1 -11 0 0 Rewind table p 10 Column 0 1 2 0 r[2]=u.first_name 11 Column 1 1 3 0 r[3]=p.name 12 Ne 2 3 17 0 if r[2]!=r[3] goto 17 13 Integer 1 1 0 0 r[1]=1; record LEFT JOIN hit 14 Column 0 9 4 0 r[4]=u.age 15 Column 1 1 5 0 r[5]=p.name 16 ResultRow 4 2 0 0 output=r[4..5] 17 NextAsync 1 0 0 0 18 NextAwait 1 9 0 0 19 IfPos 1 22 0 0 r[1]>0 -> r[1]-=0, goto 22 20 NullRow 1 0 0 0 Set cursor 1 to a (pseudo) NULL row 21 Goto 0 13 0 0 22 NextAsync 0 0 0 0 23 NextAwait 0 6 0 0 24 Halt 0 0 0 0 25 Transaction 0 0 0 0 26 Goto 0 1 0 0 ``` Closes #258
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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<BranchOffset, &'static str>,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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<Insn>,
|
||||
pub cursor_ref: Vec<(Option<String>, Option<Table>)>,
|
||||
pub database_header: Rc<RefCell<DatabaseHeader>>,
|
||||
pub comments: HashMap<BranchOffset, &'static str>,
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user