diff --git a/core/vdbe/builder.rs b/core/vdbe/builder.rs index 7b3699a04..662db7835 100644 --- a/core/vdbe/builder.rs +++ b/core/vdbe/builder.rs @@ -270,6 +270,10 @@ impl ProgramBuilder { } => { *end_offset = to_offset; } + Insn::SeekRowid { target_pc, .. } => { + assert!(*target_pc < 0); + *target_pc = to_offset; + } _ => { todo!("missing resolve_label for {:?}", insn); } diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs index 98f3d0c91..6e2ef9f54 100644 --- a/core/vdbe/explain.rs +++ b/core/vdbe/explain.rs @@ -383,6 +383,28 @@ pub fn insn_to_str(program: &Program, addr: InsnReference, insn: &Insn, indent: .unwrap_or(format!("cursor {}", cursor_id).as_str()) ), ), + Insn::SeekRowid { + cursor_id, + src_reg, + target_pc, + } => ( + "SeekRowid", + *cursor_id as i32, + *src_reg as i32, + *target_pc as i32, + OwnedValue::Text(Rc::new("".to_string())), + 0, + format!( + "if (r[{}]!={}.rowid) goto {}", + src_reg, + &program.cursor_ref[*cursor_id] + .1 + .as_ref() + .map(|x| x.get_name()) + .unwrap_or(format!("cursor {}", cursor_id).as_str()), + target_pc + ), + ), Insn::DecrJumpZero { reg, target_pc } => ( "DecrJumpZero", *reg as i32, diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 69190dfe8..914e94d70 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -222,6 +222,13 @@ pub enum Insn { dest: usize, }, + // Seek to a rowid in the cursor. If not found, jump to the given PC. Otherwise, continue to the next instruction. + SeekRowid { + cursor_id: CursorID, + src_reg: usize, + target_pc: BranchOffset, + }, + // Decrement the given register and jump to the given PC if the result is zero. DecrJumpZero { reg: usize, @@ -779,6 +786,34 @@ impl Program { } state.pc += 1; } + Insn::SeekRowid { + cursor_id, + src_reg, + target_pc, + } => { + let cursor = cursors.get_mut(cursor_id).unwrap(); + let rowid = match &state.registers[*src_reg] { + OwnedValue::Integer(rowid) => *rowid as u64, + _ => { + return Err(LimboError::InternalError( + "SeekRowid: the value in the register is not an integer".into(), + )); + } + }; + match cursor.seek_rowid(rowid)? { + CursorResult::Ok(found) => { + if !found { + state.pc = *target_pc; + } else { + state.pc += 1; + } + } + CursorResult::IO => { + // If there is I/O, the instruction is restarted. + return Ok(StepResult::IO); + } + } + } Insn::DecrJumpZero { reg, target_pc } => { assert!(*target_pc >= 0); match state.registers[*reg] {