fix handling of offset parameter set through variable

- before the fix db generated following plan:

turso> EXPLAIN SELECT * FROM users LIMIT ? OFFSET ?;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     16    0                    0   Start at 16
1     Variable           1     1     0                    0   r[1]=parameter(1); OFFSET expr
2     MustBeInt          1     0     0                    0
3     Variable           2     2     0                    0   r[2]=parameter(2); OFFSET expr
4     MustBeInt          2     0     0                    0
5     OffsetLimit        1     3     2                    0   if r[1]>0 then r[3]=r[1]+max(0,r[2]) else r[3]=(-1)
6     OpenRead           0     2     0                    0   table=users, root=2, iDb=0
7     Rewind             0     15    0                    0   Rewind table users
8       Variable         2     2     0                    0   r[2]=parameter(2); OFFSET expr
9       MustBeInt        2     0     0                    0
10      IfPos            2     14    1                    0   r[2]>0 -> r[2]-=1, goto 14
11      Column           0     0     4                    0   r[4]=users.x
12      ResultRow        4     1     0                    0   output=r[4]
13      DecrJumpZero     1     15    0                    0   if (--r[1]==0) goto 15
14    Next               0     8     0                    0
15    Halt               0     0     0                    0
16    Transaction        0     1     1                    0   iDb=0 tx_mode=Read
17    Goto               0     1     0                    0

- the problem here is that Variable value is re-read at step 8 - which is wrong
This commit is contained in:
Nikita Sivukhin
2025-09-26 15:02:55 +04:00
parent 1b2d7ea534
commit 63a9fa8c28
3 changed files with 8 additions and 39 deletions

View File

@@ -154,13 +154,7 @@ pub fn emit_order_by(
});
program.preassign_label_to_next_insn(sort_loop_start_label);
emit_offset(
program,
plan,
sort_loop_next_label,
t_ctx.reg_offset,
&t_ctx.resolver,
);
emit_offset(program, sort_loop_next_label, t_ctx.reg_offset);
program.emit_insn(Insn::SorterData {
cursor_id: sort_cursor,

View File

@@ -32,7 +32,7 @@ pub fn emit_select_result(
limit_ctx: Option<LimitCtx>,
) -> Result<()> {
if let (Some(jump_to), Some(_)) = (offset_jump_to, label_on_limit_reached) {
emit_offset(program, plan, jump_to, reg_offset, resolver);
emit_offset(program, jump_to, reg_offset);
}
let start_reg = reg_result_cols_start;
@@ -162,39 +162,14 @@ pub fn emit_result_row_and_limit(
Ok(())
}
pub fn emit_offset(
program: &mut ProgramBuilder,
plan: &SelectPlan,
jump_to: BranchOffset,
reg_offset: Option<usize>,
resolver: &Resolver,
) {
let Some(offset_expr) = &plan.offset else {
pub fn emit_offset(program: &mut ProgramBuilder, jump_to: BranchOffset, reg_offset: Option<usize>) {
let Some(reg_offset) = &reg_offset else {
return;
};
if let Some(val) = try_fold_expr_to_i64(offset_expr) {
if val > 0 {
program.add_comment(program.offset(), "OFFSET const");
program.emit_insn(Insn::IfPos {
reg: reg_offset.expect("reg_offset must be Some"),
target_pc: jump_to,
decrement_by: 1,
});
}
return;
}
let r = reg_offset.expect("reg_offset must be Some");
program.add_comment(program.offset(), "OFFSET expr");
_ = translate_expr(program, None, offset_expr, r, resolver);
program.emit_insn(Insn::MustBeInt { reg: r });
program.add_comment(program.offset(), "OFFSET const");
program.emit_insn(Insn::IfPos {
reg: r,
reg: *reg_offset,
target_pc: jump_to,
decrement_by: 1,
});

View File

@@ -34,7 +34,7 @@ fn emit_values_when_single_row(
t_ctx: &TranslateCtx,
) -> Result<usize> {
let end_label = program.allocate_label();
emit_offset(program, plan, end_label, t_ctx.reg_offset, &t_ctx.resolver);
emit_offset(program, end_label, t_ctx.reg_offset);
let first_row = &plan.values[0];
let row_len = first_row.len();
let start_reg = program.alloc_registers(row_len);
@@ -87,7 +87,7 @@ fn emit_toplevel_values(
});
let goto_label = program.allocate_label();
emit_offset(program, plan, goto_label, t_ctx.reg_offset, &t_ctx.resolver);
emit_offset(program, goto_label, t_ctx.reg_offset);
let row_len = plan.values[0].len();
let copy_start_reg = program.alloc_registers(row_len);
for i in 0..row_len {