Add SeekLE/SeekLT operations to VDBE

This commit is contained in:
Jussi Saurio
2025-04-08 10:19:38 +03:00
parent 431ef2fa6a
commit 3e42a62cd0
6 changed files with 115 additions and 93 deletions

View File

@@ -1203,11 +1203,13 @@ pub enum CursorResult<T> {
IO,
}
#[derive(Clone, PartialEq, Debug)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum SeekOp {
EQ,
GE,
GT,
LE,
LT,
}
#[derive(Clone, PartialEq, Debug)]

View File

@@ -413,6 +413,12 @@ impl ProgramBuilder {
Insn::SeekGT { target_pc, .. } => {
resolve(target_pc, "SeekGT");
}
Insn::SeekLE { target_pc, .. } => {
resolve(target_pc, "SeekLE");
}
Insn::SeekLT { target_pc, .. } => {
resolve(target_pc, "SeekLT");
}
Insn::IdxGE { target_pc, .. } => {
resolve(target_pc, "IdxGE");
}

View File

@@ -1892,97 +1892,69 @@ pub fn op_deferred_seek(
Ok(InsnFunctionStepResult::Step)
}
pub fn op_seek_ge(
pub fn op_seek(
program: &Program,
state: &mut ProgramState,
insn: &Insn,
pager: &Rc<Pager>,
mv_store: Option<&Rc<MvStore>>,
) -> Result<InsnFunctionStepResult> {
let Insn::SeekGE {
let (Insn::SeekGE {
cursor_id,
start_reg,
num_regs,
target_pc,
is_index,
} = insn
else {
unreachable!("unexpected Insn {:?}", insn)
};
assert!(target_pc.is_offset());
if *is_index {
let found = {
let mut cursor = state.get_cursor(*cursor_id);
let cursor = cursor.as_btree_mut();
let record_from_regs = make_record(&state.registers, start_reg, num_regs);
let found =
return_if_io!(cursor.seek(SeekKey::IndexKey(&record_from_regs), SeekOp::GE));
found
};
if !found {
state.pc = target_pc.to_offset_int();
} else {
state.pc += 1;
}
} else {
let pc = {
let mut cursor = state.get_cursor(*cursor_id);
let cursor = cursor.as_btree_mut();
let rowid = match state.registers[*start_reg].get_owned_value() {
OwnedValue::Null => {
// All integer values are greater than null so we just rewind the cursor
return_if_io!(cursor.rewind());
None
}
OwnedValue::Integer(rowid) => Some(*rowid as u64),
_ => {
return Err(LimboError::InternalError(
"SeekGE: the value in the register is not an integer".into(),
));
}
};
match rowid {
Some(rowid) => {
let found = return_if_io!(cursor.seek(SeekKey::TableRowId(rowid), SeekOp::GE));
if !found {
target_pc.to_offset_int()
} else {
state.pc + 1
}
}
None => state.pc + 1,
}
};
state.pc = pc;
}
Ok(InsnFunctionStepResult::Step)
}
pub fn op_seek_gt(
program: &Program,
state: &mut ProgramState,
insn: &Insn,
pager: &Rc<Pager>,
mv_store: Option<&Rc<MvStore>>,
) -> Result<InsnFunctionStepResult> {
let Insn::SeekGT {
| Insn::SeekGT {
cursor_id,
start_reg,
num_regs,
target_pc,
is_index,
} = insn
}
| Insn::SeekLE {
cursor_id,
start_reg,
num_regs,
target_pc,
is_index,
}
| Insn::SeekLT {
cursor_id,
start_reg,
num_regs,
target_pc,
is_index,
}) = insn
else {
unreachable!("unexpected Insn {:?}", insn)
};
assert!(target_pc.is_offset());
assert!(
target_pc.is_offset(),
"target_pc should be an offset, is: {:?}",
target_pc
);
let op = match insn {
Insn::SeekGE { .. } => SeekOp::GE,
Insn::SeekGT { .. } => SeekOp::GT,
Insn::SeekLE { .. } => SeekOp::LE,
Insn::SeekLT { .. } => SeekOp::LT,
_ => unreachable!("unexpected Insn {:?}", insn),
};
let op_name = match op {
SeekOp::GE => "SeekGE",
SeekOp::GT => "SeekGT",
SeekOp::LE => "SeekLE",
SeekOp::LT => "SeekLT",
_ => unreachable!("unexpected SeekOp {:?}", op),
};
if *is_index {
let found = {
let mut cursor = state.get_cursor(*cursor_id);
let cursor = cursor.as_btree_mut();
let record_from_regs = make_record(&state.registers, start_reg, num_regs);
let found =
return_if_io!(cursor.seek(SeekKey::IndexKey(&record_from_regs), SeekOp::GT));
let found = return_if_io!(cursor.seek(SeekKey::IndexKey(&record_from_regs), op));
found
};
if !found {
@@ -2002,14 +1974,15 @@ pub fn op_seek_gt(
}
OwnedValue::Integer(rowid) => Some(*rowid as u64),
_ => {
return Err(LimboError::InternalError(
"SeekGT: the value in the register is not an integer".into(),
));
return Err(LimboError::InternalError(format!(
"{}: the value in the register is not an integer",
op_name
)));
}
};
let found = match rowid {
Some(rowid) => {
let found = return_if_io!(cursor.seek(SeekKey::TableRowId(rowid), SeekOp::GT));
let found = return_if_io!(cursor.seek(SeekKey::TableRowId(rowid), op));
if !found {
target_pc.to_offset_int()
} else {

View File

@@ -736,23 +736,35 @@ pub fn insn_to_str(
start_reg,
num_regs: _,
target_pc,
} => (
"SeekGT",
*cursor_id as i32,
target_pc.to_debug_int(),
*start_reg as i32,
OwnedValue::build_text(""),
0,
"".to_string(),
),
Insn::SeekGE {
}
| Insn::SeekGE {
is_index: _,
cursor_id,
start_reg,
num_regs: _,
target_pc,
}
| Insn::SeekLE {
is_index: _,
cursor_id,
start_reg,
num_regs: _,
target_pc,
}
| Insn::SeekLT {
is_index: _,
cursor_id,
start_reg,
num_regs: _,
target_pc,
} => (
"SeekGE",
match insn {
Insn::SeekGT { .. } => "SeekGT",
Insn::SeekGE { .. } => "SeekGE",
Insn::SeekLE { .. } => "SeekLE",
Insn::SeekLT { .. } => "SeekLT",
_ => unreachable!(),
},
*cursor_id as i32,
target_pc.to_debug_int(),
*start_reg as i32,
@@ -1213,9 +1225,9 @@ pub fn insn_to_str(
0,
"".to_string(),
),
Insn::LastAsync { .. } => (
Insn::LastAsync { cursor_id } => (
"LastAsync",
0,
*cursor_id as i32,
0,
0,
OwnedValue::build_text(""),
@@ -1240,27 +1252,27 @@ pub fn insn_to_str(
0,
where_clause.clone(),
),
Insn::LastAwait { .. } => (
Insn::LastAwait { cursor_id, .. } => (
"LastAwait",
0,
*cursor_id as i32,
0,
0,
OwnedValue::build_text(""),
0,
"".to_string(),
),
Insn::PrevAsync { .. } => (
Insn::PrevAsync { cursor_id } => (
"PrevAsync",
0,
*cursor_id as i32,
0,
0,
OwnedValue::build_text(""),
0,
"".to_string(),
),
Insn::PrevAwait { .. } => (
Insn::PrevAwait { cursor_id, .. } => (
"PrevAwait",
0,
*cursor_id as i32,
0,
0,
OwnedValue::build_text(""),

View File

@@ -501,6 +501,30 @@ pub enum Insn {
/// The P4 register values beginning with P3 form an unpacked index key that omits the PRIMARY KEY. Compare this key value against the index that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID fields at the end.
/// If the P1 index entry is greater or equal than the key value then jump to P2. Otherwise fall through to the next instruction.
// If cursor_id refers to an SQL table (B-Tree that uses integer keys), use the value in start_reg as the key.
// If cursor_id refers to an SQL index, then start_reg is the first in an array of num_regs registers that are used as an unpacked index key.
// Seek to the first index entry that is less than or equal to the given key. If not found, jump to the given PC. Otherwise, continue to the next instruction.
SeekLE {
is_index: bool,
cursor_id: CursorID,
start_reg: usize,
num_regs: usize,
target_pc: BranchOffset,
},
// If cursor_id refers to an SQL table (B-Tree that uses integer keys), use the value in start_reg as the key.
// If cursor_id refers to an SQL index, then start_reg is the first in an array of num_regs registers that are used as an unpacked index key.
// Seek to the first index entry that is less than the given key. If not found, jump to the given PC. Otherwise, continue to the next instruction.
SeekLT {
is_index: bool,
cursor_id: CursorID,
start_reg: usize,
num_regs: usize,
target_pc: BranchOffset,
},
// The P4 register values beginning with P3 form an unpacked index key that omits the PRIMARY KEY. Compare this key value against the index that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID fields at the end.
// If the P1 index entry is greater or equal than the key value then jump to P2. Otherwise fall through to the next instruction.
IdxGE {
cursor_id: CursorID,
start_reg: usize,
@@ -1306,8 +1330,10 @@ impl Insn {
Insn::SeekRowid { .. } => execute::op_seek_rowid,
Insn::DeferredSeek { .. } => execute::op_deferred_seek,
Insn::SeekGE { .. } => execute::op_seek_ge,
Insn::SeekGT { .. } => execute::op_seek_gt,
Insn::SeekGE { .. } => execute::op_seek,
Insn::SeekGT { .. } => execute::op_seek,
Insn::SeekLE { .. } => execute::op_seek,
Insn::SeekLT { .. } => execute::op_seek,
Insn::SeekEnd { .. } => execute::op_seek_end,
Insn::IdxGE { .. } => execute::op_idx_ge,
Insn::IdxGT { .. } => execute::op_idx_gt,

View File

@@ -561,7 +561,10 @@ fn get_indent_count(indent_count: usize, curr_insn: &Insn, prev_insn: Option<&In
| Insn::LastAwait { .. }
| Insn::SorterSort { .. }
| Insn::SeekGE { .. }
| Insn::SeekGT { .. } => indent_count + 1,
| Insn::SeekGT { .. }
| Insn::SeekLE { .. }
| Insn::SeekLT { .. } => indent_count + 1,
_ => indent_count,
}
} else {