mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-08-04 10:08:20 +00:00
Merge 'Fix case and emit' from Nikita Sivukhin
(after fixing complicated b-tree bugs - I want to end my day with the joy of fixing simple bugs) This PR fix bug in emit (which was introduced after switch from `HashMap` to `Vec`) and also align `CASE` codegen in case of `NULL` result from `WHEN` clause with SQLite behaviour. Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com> Closes #953
This commit is contained in:
commit
31886e8f4c
4 changed files with 71 additions and 11 deletions
|
@ -792,7 +792,8 @@ pub fn translate_expr(
|
|||
lhs: base_reg,
|
||||
rhs: expr_reg,
|
||||
target_pc: next_case_label,
|
||||
flags: CmpInsFlags::default(),
|
||||
// A NULL result is considered untrue when evaluating WHEN terms.
|
||||
flags: CmpInsFlags::default().jump_if_null(),
|
||||
}),
|
||||
// CASE WHEN 0 THEN 0 ELSE 1 becomes ifnot 0 branch to next clause
|
||||
None => program.emit_insn(Insn::IfNot {
|
||||
|
|
|
@ -110,10 +110,8 @@ impl ProgramBuilder {
|
|||
|
||||
pub fn emit_insn(&mut self, insn: Insn) {
|
||||
if let Some(label) = self.next_insn_label {
|
||||
self.label_to_resolved_offset.insert(
|
||||
label.to_label_value() as usize,
|
||||
Some(self.insns.len() as InsnReference),
|
||||
);
|
||||
self.label_to_resolved_offset[label.to_label_value() as usize] =
|
||||
Some(self.insns.len() as InsnReference);
|
||||
self.next_insn_label = None;
|
||||
}
|
||||
self.insns.push(insn);
|
||||
|
|
|
@ -108,6 +108,14 @@ do_execsql_test select_base_case_else {
|
|||
select case 1 when 0 then 'zero' when 1 then 'one' else 'two' end;
|
||||
} {one}
|
||||
|
||||
do_execsql_test select_base_case_null_result {
|
||||
select case NULL when 0 then 'first' else 'second' end;
|
||||
select case NULL when NULL then 'first' else 'second' end;
|
||||
select case 0 when 0 then 'first' else 'second' end;
|
||||
} {second
|
||||
second
|
||||
first}
|
||||
|
||||
do_execsql_test select_base_case_noelse_null {
|
||||
select case 'null else' when 0 then 0 when 1 then 1 end;
|
||||
} {}
|
||||
|
|
|
@ -163,6 +163,7 @@ mod tests {
|
|||
"SELECT ((NULL) IS NOT TRUE <= ((NOT (FALSE))))",
|
||||
"SELECT ifnull(0, NOT 0)",
|
||||
"SELECT like('a%', 'a') = 1",
|
||||
"SELECT CASE ( NULL < NULL ) WHEN ( 0 ) THEN ( NULL ) ELSE ( 2.0 ) END;",
|
||||
] {
|
||||
let limbo = limbo_exec_row(&limbo_conn, query);
|
||||
let sqlite = sqlite_exec_row(&sqlite_conn, query);
|
||||
|
@ -209,15 +210,66 @@ mod tests {
|
|||
.push(expr)
|
||||
.build();
|
||||
|
||||
let (like_pattern, like_pattern_builder) = g.create_handle();
|
||||
like_pattern_builder
|
||||
.choice()
|
||||
.option_str("%")
|
||||
.option_str("_")
|
||||
.option_symbol(rand_str("", 1))
|
||||
.repeat(1..10, "")
|
||||
.build();
|
||||
|
||||
let (glob_pattern, glob_pattern_builder) = g.create_handle();
|
||||
glob_pattern_builder
|
||||
.choice()
|
||||
.option_str("*")
|
||||
.option_str("**")
|
||||
.option_str("A")
|
||||
.option_str("B")
|
||||
.repeat(1..10, "")
|
||||
.build();
|
||||
|
||||
let (case_expr, case_expr_builder) = g.create_handle();
|
||||
case_expr_builder
|
||||
.concat(" ")
|
||||
.push_str("CASE (")
|
||||
.push(expr)
|
||||
.push_str(")")
|
||||
.push(
|
||||
g.create()
|
||||
.concat(" ")
|
||||
.push_str("WHEN (")
|
||||
.push(expr)
|
||||
.push_str(") THEN (")
|
||||
.push(expr)
|
||||
.push_str(")")
|
||||
.repeat(1..5, " ")
|
||||
.build(),
|
||||
)
|
||||
.push_str("ELSE (")
|
||||
.push(expr)
|
||||
.push_str(") END")
|
||||
.build();
|
||||
|
||||
scalar_builder
|
||||
.choice()
|
||||
.option(
|
||||
g.create()
|
||||
.concat("")
|
||||
.push_str("like('")
|
||||
.push_symbol(rand_str("", 2))
|
||||
.push(like_pattern)
|
||||
.push_str("', '")
|
||||
.push_symbol(rand_str("", 2))
|
||||
.push(like_pattern)
|
||||
.push_str("')")
|
||||
.build(),
|
||||
)
|
||||
.option(
|
||||
g.create()
|
||||
.concat("")
|
||||
.push_str("glob('")
|
||||
.push(glob_pattern)
|
||||
.push_str("', '")
|
||||
.push(glob_pattern)
|
||||
.push_str("')")
|
||||
.build(),
|
||||
)
|
||||
|
@ -247,10 +299,11 @@ mod tests {
|
|||
|
||||
expr_builder
|
||||
.choice()
|
||||
.option_w(unary_infix_op, 1.0)
|
||||
.option_w(bin_op, 1.0)
|
||||
.option_w(paren, 1.0)
|
||||
.option_w(scalar, 1.0)
|
||||
.option_w(case_expr, 1.0)
|
||||
.option_w(unary_infix_op, 2.0)
|
||||
.option_w(bin_op, 3.0)
|
||||
.option_w(paren, 2.0)
|
||||
.option_w(scalar, 4.0)
|
||||
// unfortunately, sqlite behaves weirdly when IS operator is used with TRUE/FALSE constants
|
||||
// e.g. 8 IS TRUE == 1 (although 8 = TRUE == 0)
|
||||
// so, we do not use TRUE/FALSE constants as they will produce diff with sqlite results
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue