mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-31 23:14:21 +01:00
Merge 'Support nested parenthesized conditional expressions in translator' from Preston Thorpe
This PR fixes queries like:
```sql
SELECT count(*) FROM users WHERE ((age > 25 OR age < 18) AND (city = 'Boston' OR state = 'MA'));
```
Previously we would return `7516` rows instead of `146`, due to
disregarding the final `AND` from within the nested `OR` conditions,
unconditionally short circuiting when a TRUE is found in an OR
expression without any surrounding relevant context.
or:
```sql
SELECT * FROM users WHERE (((age > 18 AND city = 'New Mario') OR age = 92) AND city = 'Lake Paul');
```
Previously we would incorrectly return the top row:
```
9984|Leah|Russell|..|..|6733 Weber Crossing|New Mario|SC|57707|78
9989|Timothy|Harrison|..|..|782 Wright Harbors|Lake Paul|ID|52330|92
```
Added localized jump targets for OR expressions within AND blocks to
prevent premature short-circuiting and `parent operator` to condition
metadata to trigger them.
If parent operator should instead be another function param on
`translate_conditional_expr`, instead of a field in condition_metadata,
let me know and I can change it.
EDIT: sorry I realize I should have included the other cleanup changes
in a different PR. I will fix this after work
Reviewed-by: Jussi Saurio <kacperoza@gmail.com>
Closes #633
This commit is contained in:
@@ -18,6 +18,47 @@ pub struct ConditionMetadata {
|
||||
pub jump_if_condition_is_true: bool,
|
||||
pub jump_target_when_true: BranchOffset,
|
||||
pub jump_target_when_false: BranchOffset,
|
||||
pub parent_op: Option<ast::Operator>,
|
||||
}
|
||||
|
||||
fn emit_cond_jump(program: &mut ProgramBuilder, cond_meta: ConditionMetadata, reg: usize) {
|
||||
if cond_meta.jump_if_condition_is_true {
|
||||
program.emit_insn(Insn::If {
|
||||
reg,
|
||||
target_pc: cond_meta.jump_target_when_true,
|
||||
null_reg: reg,
|
||||
});
|
||||
} else {
|
||||
program.emit_insn(Insn::IfNot {
|
||||
reg,
|
||||
target_pc: cond_meta.jump_target_when_false,
|
||||
null_reg: reg,
|
||||
});
|
||||
}
|
||||
}
|
||||
macro_rules! emit_cmp_insn {
|
||||
(
|
||||
$program:expr,
|
||||
$cond:expr,
|
||||
$op_true:ident,
|
||||
$op_false:ident,
|
||||
$lhs:expr,
|
||||
$rhs:expr
|
||||
) => {{
|
||||
if $cond.jump_if_condition_is_true {
|
||||
$program.emit_insn(Insn::$op_true {
|
||||
lhs: $lhs,
|
||||
rhs: $rhs,
|
||||
target_pc: $cond.jump_target_when_true,
|
||||
});
|
||||
} else {
|
||||
$program.emit_insn(Insn::$op_false {
|
||||
lhs: $lhs,
|
||||
rhs: $rhs,
|
||||
target_pc: $cond.jump_target_when_false,
|
||||
});
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
pub fn translate_condition_expr(
|
||||
@@ -38,6 +79,8 @@ pub fn translate_condition_expr(
|
||||
lhs,
|
||||
ConditionMetadata {
|
||||
jump_if_condition_is_true: false,
|
||||
// Mark that the parent op for sub-expressions is AND
|
||||
parent_op: Some(ast::Operator::And),
|
||||
..condition_metadata
|
||||
},
|
||||
resolver,
|
||||
@@ -46,134 +89,91 @@ pub fn translate_condition_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
rhs,
|
||||
condition_metadata,
|
||||
ConditionMetadata {
|
||||
parent_op: Some(ast::Operator::And),
|
||||
..condition_metadata
|
||||
},
|
||||
resolver,
|
||||
);
|
||||
}
|
||||
ast::Expr::Binary(lhs, ast::Operator::Or, rhs) => {
|
||||
let jump_target_when_false = program.allocate_label();
|
||||
let _ = translate_condition_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
lhs,
|
||||
ConditionMetadata {
|
||||
// If the first condition is true, we don't need to evaluate the second condition.
|
||||
if matches!(condition_metadata.parent_op, Some(ast::Operator::And)) {
|
||||
// we are inside a bigger AND expression, so we do NOT jump to parent's 'true' if LHS or RHS is true.
|
||||
// we only short-circuit the parent's false label if LHS and RHS are both false.
|
||||
let local_true_label = program.allocate_label();
|
||||
let local_false_label = program.allocate_label();
|
||||
|
||||
// evaluate LHS in normal OR fashion, short-circuit local if true
|
||||
let lhs_metadata = ConditionMetadata {
|
||||
jump_if_condition_is_true: true,
|
||||
jump_target_when_true: local_true_label,
|
||||
jump_target_when_false: local_false_label,
|
||||
parent_op: Some(ast::Operator::Or),
|
||||
};
|
||||
translate_condition_expr(program, referenced_tables, lhs, lhs_metadata, resolver)?;
|
||||
|
||||
// if lhs was false, we land here:
|
||||
program.resolve_label(local_false_label, program.offset());
|
||||
|
||||
// evaluate rhs with normal OR: short-circuit if true, go to local_true
|
||||
let rhs_metadata = ConditionMetadata {
|
||||
jump_if_condition_is_true: true,
|
||||
jump_target_when_true: local_true_label,
|
||||
jump_target_when_false: condition_metadata.jump_target_when_false,
|
||||
// if rhs is also false => parent's false
|
||||
parent_op: Some(ast::Operator::Or),
|
||||
};
|
||||
translate_condition_expr(program, referenced_tables, rhs, rhs_metadata, resolver)?;
|
||||
|
||||
// if we get here, both lhs+rhs are false: explicit jump to parent's false
|
||||
program.emit_insn(Insn::Goto {
|
||||
target_pc: condition_metadata.jump_target_when_false,
|
||||
});
|
||||
// local_true: we do not jump to parent's "true" label because the parent is AND,
|
||||
// so we want to keep evaluating the rest
|
||||
program.resolve_label(local_true_label, program.offset());
|
||||
} else {
|
||||
let jump_target_when_false = program.allocate_label();
|
||||
|
||||
let lhs_metadata = ConditionMetadata {
|
||||
jump_if_condition_is_true: true,
|
||||
jump_target_when_false,
|
||||
parent_op: Some(ast::Operator::Or),
|
||||
..condition_metadata
|
||||
},
|
||||
resolver,
|
||||
);
|
||||
program.resolve_label(jump_target_when_false, program.offset());
|
||||
let _ = translate_condition_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
rhs,
|
||||
condition_metadata,
|
||||
resolver,
|
||||
);
|
||||
};
|
||||
|
||||
translate_condition_expr(program, referenced_tables, lhs, lhs_metadata, resolver)?;
|
||||
|
||||
// if LHS was false, we land here:
|
||||
program.resolve_label(jump_target_when_false, program.offset());
|
||||
let rhs_metadata = ConditionMetadata {
|
||||
parent_op: Some(ast::Operator::Or),
|
||||
..condition_metadata
|
||||
};
|
||||
translate_condition_expr(program, referenced_tables, rhs, rhs_metadata, resolver)?;
|
||||
}
|
||||
}
|
||||
ast::Expr::Binary(lhs, op, rhs) => {
|
||||
let lhs_reg = program.alloc_register();
|
||||
let _ = translate_expr(program, Some(referenced_tables), lhs, lhs_reg, resolver);
|
||||
if let ast::Expr::Literal(_) = lhs.as_ref() {
|
||||
program.mark_last_insn_constant()
|
||||
}
|
||||
let rhs_reg = program.alloc_register();
|
||||
let _ = translate_expr(program, Some(referenced_tables), rhs, rhs_reg, resolver);
|
||||
if let ast::Expr::Literal(_) = rhs.as_ref() {
|
||||
program.mark_last_insn_constant()
|
||||
}
|
||||
let lhs_reg = translate_and_mark(program, Some(referenced_tables), lhs, resolver)?;
|
||||
let rhs_reg = translate_and_mark(program, Some(referenced_tables), rhs, resolver)?;
|
||||
match op {
|
||||
ast::Operator::Greater => {
|
||||
if condition_metadata.jump_if_condition_is_true {
|
||||
program.emit_insn(Insn::Gt {
|
||||
lhs: lhs_reg,
|
||||
rhs: rhs_reg,
|
||||
target_pc: condition_metadata.jump_target_when_true,
|
||||
})
|
||||
} else {
|
||||
program.emit_insn(Insn::Le {
|
||||
lhs: lhs_reg,
|
||||
rhs: rhs_reg,
|
||||
target_pc: condition_metadata.jump_target_when_false,
|
||||
})
|
||||
}
|
||||
emit_cmp_insn!(program, condition_metadata, Gt, Le, lhs_reg, rhs_reg)
|
||||
}
|
||||
ast::Operator::GreaterEquals => {
|
||||
if condition_metadata.jump_if_condition_is_true {
|
||||
program.emit_insn(Insn::Ge {
|
||||
lhs: lhs_reg,
|
||||
rhs: rhs_reg,
|
||||
target_pc: condition_metadata.jump_target_when_true,
|
||||
})
|
||||
} else {
|
||||
program.emit_insn(Insn::Lt {
|
||||
lhs: lhs_reg,
|
||||
rhs: rhs_reg,
|
||||
target_pc: condition_metadata.jump_target_when_false,
|
||||
})
|
||||
}
|
||||
emit_cmp_insn!(program, condition_metadata, Ge, Lt, lhs_reg, rhs_reg)
|
||||
}
|
||||
ast::Operator::Less => {
|
||||
if condition_metadata.jump_if_condition_is_true {
|
||||
program.emit_insn(Insn::Lt {
|
||||
lhs: lhs_reg,
|
||||
rhs: rhs_reg,
|
||||
target_pc: condition_metadata.jump_target_when_true,
|
||||
})
|
||||
} else {
|
||||
program.emit_insn(Insn::Ge {
|
||||
lhs: lhs_reg,
|
||||
rhs: rhs_reg,
|
||||
target_pc: condition_metadata.jump_target_when_false,
|
||||
})
|
||||
}
|
||||
emit_cmp_insn!(program, condition_metadata, Lt, Ge, lhs_reg, rhs_reg)
|
||||
}
|
||||
ast::Operator::LessEquals => {
|
||||
if condition_metadata.jump_if_condition_is_true {
|
||||
program.emit_insn(Insn::Le {
|
||||
lhs: lhs_reg,
|
||||
rhs: rhs_reg,
|
||||
target_pc: condition_metadata.jump_target_when_true,
|
||||
})
|
||||
} else {
|
||||
program.emit_insn(Insn::Gt {
|
||||
lhs: lhs_reg,
|
||||
rhs: rhs_reg,
|
||||
target_pc: condition_metadata.jump_target_when_false,
|
||||
})
|
||||
}
|
||||
emit_cmp_insn!(program, condition_metadata, Le, Gt, lhs_reg, rhs_reg)
|
||||
}
|
||||
ast::Operator::Equals => {
|
||||
if condition_metadata.jump_if_condition_is_true {
|
||||
program.emit_insn(Insn::Eq {
|
||||
lhs: lhs_reg,
|
||||
rhs: rhs_reg,
|
||||
target_pc: condition_metadata.jump_target_when_true,
|
||||
})
|
||||
} else {
|
||||
program.emit_insn(Insn::Ne {
|
||||
lhs: lhs_reg,
|
||||
rhs: rhs_reg,
|
||||
target_pc: condition_metadata.jump_target_when_false,
|
||||
})
|
||||
}
|
||||
emit_cmp_insn!(program, condition_metadata, Eq, Ne, lhs_reg, rhs_reg)
|
||||
}
|
||||
ast::Operator::NotEquals => {
|
||||
if condition_metadata.jump_if_condition_is_true {
|
||||
program.emit_insn(Insn::Ne {
|
||||
lhs: lhs_reg,
|
||||
rhs: rhs_reg,
|
||||
target_pc: condition_metadata.jump_target_when_true,
|
||||
})
|
||||
} else {
|
||||
program.emit_insn(Insn::Eq {
|
||||
lhs: lhs_reg,
|
||||
rhs: rhs_reg,
|
||||
target_pc: condition_metadata.jump_target_when_false,
|
||||
})
|
||||
}
|
||||
emit_cmp_insn!(program, condition_metadata, Ne, Eq, lhs_reg, rhs_reg)
|
||||
}
|
||||
ast::Operator::Is => todo!(),
|
||||
ast::Operator::IsNot => todo!(),
|
||||
@@ -191,19 +191,7 @@ pub fn translate_condition_expr(
|
||||
value: int_value,
|
||||
dest: reg,
|
||||
});
|
||||
if condition_metadata.jump_if_condition_is_true {
|
||||
program.emit_insn(Insn::If {
|
||||
reg,
|
||||
target_pc: condition_metadata.jump_target_when_true,
|
||||
null_reg: reg,
|
||||
})
|
||||
} else {
|
||||
program.emit_insn(Insn::IfNot {
|
||||
reg,
|
||||
target_pc: condition_metadata.jump_target_when_false,
|
||||
null_reg: reg,
|
||||
})
|
||||
}
|
||||
emit_cond_jump(program, condition_metadata, reg);
|
||||
} else {
|
||||
crate::bail_parse_error!("unsupported literal type in condition");
|
||||
}
|
||||
@@ -214,19 +202,7 @@ pub fn translate_condition_expr(
|
||||
value: string.clone(),
|
||||
dest: reg,
|
||||
});
|
||||
if condition_metadata.jump_if_condition_is_true {
|
||||
program.emit_insn(Insn::If {
|
||||
reg,
|
||||
target_pc: condition_metadata.jump_target_when_true,
|
||||
null_reg: reg,
|
||||
})
|
||||
} else {
|
||||
program.emit_insn(Insn::IfNot {
|
||||
reg,
|
||||
target_pc: condition_metadata.jump_target_when_false,
|
||||
null_reg: reg,
|
||||
})
|
||||
}
|
||||
emit_cond_jump(program, condition_metadata, reg);
|
||||
}
|
||||
unimpl => todo!("literal {:?} not implemented", unimpl),
|
||||
},
|
||||
@@ -352,18 +328,8 @@ pub fn translate_condition_expr(
|
||||
match op {
|
||||
ast::LikeOperator::Like | ast::LikeOperator::Glob => {
|
||||
let pattern_reg = program.alloc_register();
|
||||
let column_reg = program.alloc_register();
|
||||
let mut constant_mask = 0;
|
||||
let _ = translate_expr(
|
||||
program,
|
||||
Some(referenced_tables),
|
||||
lhs,
|
||||
column_reg,
|
||||
resolver,
|
||||
)?;
|
||||
if let ast::Expr::Literal(_) = lhs.as_ref() {
|
||||
program.mark_last_insn_constant();
|
||||
}
|
||||
let _ = translate_and_mark(program, Some(referenced_tables), lhs, resolver);
|
||||
let _ = translate_expr(
|
||||
program,
|
||||
Some(referenced_tables),
|
||||
@@ -371,7 +337,7 @@ pub fn translate_condition_expr(
|
||||
pattern_reg,
|
||||
resolver,
|
||||
)?;
|
||||
if let ast::Expr::Literal(_) = rhs.as_ref() {
|
||||
if matches!(rhs.as_ref(), ast::Expr::Literal(_)) {
|
||||
program.mark_last_insn_constant();
|
||||
constant_mask = 1;
|
||||
}
|
||||
@@ -394,19 +360,7 @@ pub fn translate_condition_expr(
|
||||
ast::LikeOperator::Regexp => todo!(),
|
||||
}
|
||||
if !*not {
|
||||
if condition_metadata.jump_if_condition_is_true {
|
||||
program.emit_insn(Insn::If {
|
||||
reg: cur_reg,
|
||||
target_pc: condition_metadata.jump_target_when_true,
|
||||
null_reg: cur_reg,
|
||||
});
|
||||
} else {
|
||||
program.emit_insn(Insn::IfNot {
|
||||
reg: cur_reg,
|
||||
target_pc: condition_metadata.jump_target_when_false,
|
||||
null_reg: cur_reg,
|
||||
});
|
||||
}
|
||||
emit_cond_jump(program, condition_metadata, cur_reg);
|
||||
} else if condition_metadata.jump_if_condition_is_true {
|
||||
program.emit_insn(Insn::IfNot {
|
||||
reg: cur_reg,
|
||||
@@ -422,16 +376,18 @@ pub fn translate_condition_expr(
|
||||
}
|
||||
}
|
||||
ast::Expr::Parenthesized(exprs) => {
|
||||
// TODO: this is probably not correct; multiple expressions in a parenthesized expression
|
||||
// are reserved for special cases like `(a, b) IN ((1, 2), (3, 4))`.
|
||||
for expr in exprs {
|
||||
if exprs.len() == 1 {
|
||||
let _ = translate_condition_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
expr,
|
||||
&exprs[0],
|
||||
condition_metadata,
|
||||
resolver,
|
||||
);
|
||||
} else {
|
||||
crate::bail_parse_error!(
|
||||
"parenthesized condtional should have exactly one expression"
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => todo!("op {:?} not implemented", expr),
|
||||
@@ -835,7 +791,7 @@ pub fn translate_expr(
|
||||
unreachable!("this is always ast::Expr::Cast")
|
||||
}
|
||||
ScalarFunc::Changes => {
|
||||
if let Some(_) = args {
|
||||
if args.is_some() {
|
||||
crate::bail_parse_error!(
|
||||
"{} fucntion with more than 0 arguments",
|
||||
srf
|
||||
@@ -1070,12 +1026,8 @@ pub fn translate_expr(
|
||||
);
|
||||
};
|
||||
for arg in args {
|
||||
let reg = program.alloc_register();
|
||||
let _ =
|
||||
translate_expr(program, referenced_tables, arg, reg, resolver)?;
|
||||
if let ast::Expr::Literal(_) = arg {
|
||||
program.mark_last_insn_constant()
|
||||
}
|
||||
translate_and_mark(program, referenced_tables, arg, resolver);
|
||||
}
|
||||
program.emit_insn(Insn::Function {
|
||||
// Only constant patterns for LIKE are supported currently, so this
|
||||
@@ -1113,12 +1065,11 @@ pub fn translate_expr(
|
||||
srf.to_string()
|
||||
);
|
||||
};
|
||||
|
||||
let regs = program.alloc_register();
|
||||
translate_expr(program, referenced_tables, &args[0], regs, resolver)?;
|
||||
let reg =
|
||||
translate_and_mark(program, referenced_tables, &args[0], resolver)?;
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: regs,
|
||||
start_reg: reg,
|
||||
dest: target_register,
|
||||
func: func_ctx,
|
||||
});
|
||||
@@ -1144,12 +1095,10 @@ pub fn translate_expr(
|
||||
if let Some(args) = args {
|
||||
for arg in args.iter() {
|
||||
// register containing result of each argument expression
|
||||
let target_reg = program.alloc_register();
|
||||
_ = translate_expr(
|
||||
let _ = translate_and_mark(
|
||||
program,
|
||||
referenced_tables,
|
||||
arg,
|
||||
target_reg,
|
||||
resolver,
|
||||
)?;
|
||||
}
|
||||
@@ -1181,15 +1130,14 @@ pub fn translate_expr(
|
||||
let str_reg = program.alloc_register();
|
||||
let start_reg = program.alloc_register();
|
||||
let length_reg = program.alloc_register();
|
||||
|
||||
translate_expr(
|
||||
let str_reg = translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
&args[0],
|
||||
str_reg,
|
||||
resolver,
|
||||
)?;
|
||||
translate_expr(
|
||||
let _ = translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
&args[1],
|
||||
@@ -1205,7 +1153,6 @@ pub fn translate_expr(
|
||||
resolver,
|
||||
)?;
|
||||
}
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: str_reg,
|
||||
@@ -1225,8 +1172,8 @@ pub fn translate_expr(
|
||||
} else {
|
||||
crate::bail_parse_error!("hex function with no arguments",);
|
||||
};
|
||||
let regs = program.alloc_register();
|
||||
translate_expr(program, referenced_tables, &args[0], regs, resolver)?;
|
||||
let regs =
|
||||
translate_and_mark(program, referenced_tables, &args[0], resolver)?;
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: regs,
|
||||
@@ -1266,12 +1213,10 @@ pub fn translate_expr(
|
||||
if let Some(args) = args {
|
||||
for arg in args.iter() {
|
||||
// register containing result of each argument expression
|
||||
let target_reg = program.alloc_register();
|
||||
_ = translate_expr(
|
||||
let _ = translate_and_mark(
|
||||
program,
|
||||
referenced_tables,
|
||||
arg,
|
||||
target_reg,
|
||||
resolver,
|
||||
)?;
|
||||
}
|
||||
@@ -1285,7 +1230,7 @@ pub fn translate_expr(
|
||||
Ok(target_register)
|
||||
}
|
||||
ScalarFunc::TotalChanges => {
|
||||
if let Some(_) = args {
|
||||
if args.is_some() {
|
||||
crate::bail_parse_error!(
|
||||
"{} fucntion with more than 0 arguments",
|
||||
srf.to_string()
|
||||
@@ -1321,11 +1266,7 @@ pub fn translate_expr(
|
||||
};
|
||||
|
||||
for arg in args.iter() {
|
||||
let reg = program.alloc_register();
|
||||
translate_expr(program, referenced_tables, arg, reg, resolver)?;
|
||||
if let ast::Expr::Literal(_) = arg {
|
||||
program.mark_last_insn_constant();
|
||||
}
|
||||
translate_and_mark(program, referenced_tables, arg, resolver)?;
|
||||
}
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
@@ -1347,12 +1288,7 @@ pub fn translate_expr(
|
||||
crate::bail_parse_error!("min function with no arguments");
|
||||
};
|
||||
for arg in args {
|
||||
let reg = program.alloc_register();
|
||||
let _ =
|
||||
translate_expr(program, referenced_tables, arg, reg, resolver)?;
|
||||
if let ast::Expr::Literal(_) = arg {
|
||||
program.mark_last_insn_constant()
|
||||
}
|
||||
translate_and_mark(program, referenced_tables, arg, resolver)?;
|
||||
}
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
@@ -1375,12 +1311,7 @@ pub fn translate_expr(
|
||||
crate::bail_parse_error!("max function with no arguments");
|
||||
};
|
||||
for arg in args {
|
||||
let reg = program.alloc_register();
|
||||
let _ =
|
||||
translate_expr(program, referenced_tables, arg, reg, resolver)?;
|
||||
if let ast::Expr::Literal(_) = arg {
|
||||
program.mark_last_insn_constant()
|
||||
}
|
||||
translate_and_mark(program, referenced_tables, arg, resolver)?;
|
||||
}
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
@@ -1416,7 +1347,7 @@ pub fn translate_expr(
|
||||
resolver,
|
||||
)?;
|
||||
let second_reg = program.alloc_register();
|
||||
translate_expr(
|
||||
let _ = translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
&args[1],
|
||||
@@ -1467,34 +1398,30 @@ pub fn translate_expr(
|
||||
srf.to_string()
|
||||
);
|
||||
};
|
||||
|
||||
let str_reg = program.alloc_register();
|
||||
let pattern_reg = program.alloc_register();
|
||||
let replacement_reg = program.alloc_register();
|
||||
|
||||
translate_expr(
|
||||
let _ = translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
&args[0],
|
||||
str_reg,
|
||||
resolver,
|
||||
)?;
|
||||
translate_expr(
|
||||
let _ = translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
&args[1],
|
||||
pattern_reg,
|
||||
resolver,
|
||||
)?;
|
||||
|
||||
translate_expr(
|
||||
let _ = translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
&args[2],
|
||||
replacement_reg,
|
||||
resolver,
|
||||
)?;
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: str_reg,
|
||||
@@ -1561,12 +1488,12 @@ pub fn translate_expr(
|
||||
};
|
||||
let mut start_reg = None;
|
||||
if let Some(arg) = args.first() {
|
||||
let reg = program.alloc_register();
|
||||
start_reg = Some(reg);
|
||||
translate_expr(program, referenced_tables, arg, reg, resolver)?;
|
||||
if let ast::Expr::Literal(_) = arg {
|
||||
program.mark_last_insn_constant()
|
||||
}
|
||||
start_reg = Some(translate_and_mark(
|
||||
program,
|
||||
referenced_tables,
|
||||
arg,
|
||||
resolver,
|
||||
)?);
|
||||
}
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
@@ -1608,10 +1535,8 @@ pub fn translate_expr(
|
||||
crate::bail_parse_error!("{} function with no arguments", math_func);
|
||||
};
|
||||
|
||||
let reg = program.alloc_register();
|
||||
|
||||
translate_expr(program, referenced_tables, &args[0], reg, resolver)?;
|
||||
|
||||
let reg =
|
||||
translate_and_mark(program, referenced_tables, &args[0], resolver)?;
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: reg,
|
||||
@@ -1633,20 +1558,12 @@ pub fn translate_expr(
|
||||
} else {
|
||||
crate::bail_parse_error!("{} function with no arguments", math_func);
|
||||
};
|
||||
|
||||
let reg1 = program.alloc_register();
|
||||
let _ =
|
||||
translate_expr(program, referenced_tables, &args[0], reg1, resolver)?;
|
||||
let reg2 = program.alloc_register();
|
||||
|
||||
translate_expr(program, referenced_tables, &args[0], reg1, resolver)?;
|
||||
if let ast::Expr::Literal(_) = &args[0] {
|
||||
program.mark_last_insn_constant();
|
||||
}
|
||||
|
||||
translate_expr(program, referenced_tables, &args[1], reg2, resolver)?;
|
||||
if let ast::Expr::Literal(_) = &args[1] {
|
||||
program.mark_last_insn_constant();
|
||||
}
|
||||
|
||||
let _ =
|
||||
translate_expr(program, referenced_tables, &args[1], reg2, resolver)?;
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: target_register + 1,
|
||||
@@ -1670,7 +1587,6 @@ pub fn translate_expr(
|
||||
};
|
||||
|
||||
let regs = program.alloc_registers(args.len());
|
||||
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
translate_expr(program, referenced_tables, arg, regs + i, resolver)?;
|
||||
}
|
||||
@@ -1917,7 +1833,6 @@ fn translate_variable_sized_function_parameter_list(
|
||||
|
||||
for arg in args.iter() {
|
||||
translate_expr(program, referenced_tables, arg, current_reg, resolver)?;
|
||||
|
||||
current_reg += 1;
|
||||
}
|
||||
|
||||
@@ -1950,6 +1865,20 @@ pub fn maybe_apply_affinity(col_type: Type, target_register: usize, program: &mu
|
||||
}
|
||||
}
|
||||
|
||||
pub fn translate_and_mark(
|
||||
program: &mut ProgramBuilder,
|
||||
referenced_tables: Option<&[TableReference]>,
|
||||
expr: &ast::Expr,
|
||||
resolver: &Resolver,
|
||||
) -> Result<usize> {
|
||||
let target_register = program.alloc_register();
|
||||
translate_expr(program, referenced_tables, expr, target_register, resolver)?;
|
||||
if matches!(expr, ast::Expr::Literal(_)) {
|
||||
program.mark_last_insn_constant();
|
||||
}
|
||||
Ok(target_register)
|
||||
}
|
||||
|
||||
/// Get an appropriate name for an expression.
|
||||
/// If the query provides an alias (e.g. `SELECT a AS b FROM t`), use that (e.g. `b`).
|
||||
/// If the expression is a column from a table, use the column name (e.g. `a`).
|
||||
|
||||
@@ -382,6 +382,7 @@ pub fn emit_group_by<'a>(
|
||||
jump_if_condition_is_true: false,
|
||||
jump_target_when_false: group_by_end_without_emitting_row_label,
|
||||
jump_target_when_true: BranchOffset::Placeholder, // not used. FIXME: this is a bug. HAVING can have e.g. HAVING a OR b.
|
||||
parent_op: None,
|
||||
},
|
||||
&t_ctx.resolver,
|
||||
)?;
|
||||
|
||||
@@ -225,6 +225,7 @@ pub fn open_loop(
|
||||
jump_if_condition_is_true: false,
|
||||
jump_target_when_true,
|
||||
jump_target_when_false: next,
|
||||
parent_op: None,
|
||||
};
|
||||
translate_condition_expr(
|
||||
program,
|
||||
@@ -273,6 +274,7 @@ pub fn open_loop(
|
||||
jump_if_condition_is_true: false,
|
||||
jump_target_when_true,
|
||||
jump_target_when_false,
|
||||
parent_op: None,
|
||||
};
|
||||
for predicate in predicates.iter() {
|
||||
translate_condition_expr(
|
||||
@@ -345,6 +347,7 @@ pub fn open_loop(
|
||||
jump_if_condition_is_true: false,
|
||||
jump_target_when_true,
|
||||
jump_target_when_false: next,
|
||||
parent_op: None,
|
||||
};
|
||||
translate_condition_expr(
|
||||
program,
|
||||
@@ -529,6 +532,7 @@ pub fn open_loop(
|
||||
jump_if_condition_is_true: false,
|
||||
jump_target_when_true,
|
||||
jump_target_when_false: next,
|
||||
parent_op: None,
|
||||
};
|
||||
translate_condition_expr(
|
||||
program,
|
||||
|
||||
@@ -343,3 +343,25 @@ do_execsql_test where-between-true-and-2 {
|
||||
select id from users where id between true and 2;
|
||||
} {1
|
||||
2}
|
||||
|
||||
do_execsql_test nested-parens-conditionals-or-and-or {
|
||||
SELECT count(*) FROM users WHERE ((age > 25 OR age < 18) AND (city = 'Boston' OR state = 'MA'));
|
||||
} {146}
|
||||
|
||||
do_execsql_test nested-parens-conditionals-and-or-and {
|
||||
SELECT * FROM users WHERE (((age > 18 AND city = 'New Mario') OR age = 92) AND city = 'Lake Paul');
|
||||
} {{9989|Timothy|Harrison|woodsmichael@example.net|+1-447-830-5123|782 Wright Harbors|Lake Paul|ID|52330|92}}
|
||||
|
||||
|
||||
do_execsql_test nested-parens-conditionals-and-double-or {
|
||||
SELECT * FROM users WHERE ((age > 30 OR age < 20) AND (state = 'NY' OR state = 'CA')) AND first_name glob 'An*' order by id;
|
||||
} {{1738|Angelica|Pena|jacksonjonathan@example.net|(867)536-1578x039|663 Jacqueline Estate Apt. 652|Clairehaven|NY|64172|74
|
||||
1811|Andrew|Mckee|jchen@example.net|359.939.9548|19809 Blair Junction Apt. 438|New Lawrencefort|NY|26240|42
|
||||
3773|Andrew|Peterson|cscott@example.com|(405)410-4972x90408|90513 Munoz Radial Apt. 786|Travisfurt|CA|52951|43
|
||||
3875|Anthony|Cordova|ocross@example.org|+1-356-999-4070x557|77081 Aguilar Turnpike|Michaelfurt|CA|73353|37
|
||||
4909|Andrew|Carson|michelle31@example.net|823.423.1516|78514 Luke Springs|Lake Crystal|CA|49481|74
|
||||
5498|Anna|Hall|elizabethheath@example.org|9778473725|5803 Taylor Tunnel|New Nicholaston|NY|21825|14
|
||||
6340|Angela|Freeman|juankelly@example.net|501.372.4720|3912 Ricardo Mission|West Nancyville|NY|60823|34
|
||||
8171|Andrea|Lee|dgarrison@example.com|001-594-430-0646|452 Anthony Stravenue|Sandraville|CA|28572|12
|
||||
9110|Anthony|Barrett|steven05@example.net|(562)928-9177x8454|86166 Foster Inlet Apt. 284|North Jeffreyburgh|CA|80147|97
|
||||
9279|Annette|Lynn|joanne37@example.com|(272)700-7181|2676 Laura Points Apt. 683|Tristanville|NY|48646|91}}
|
||||
|
||||
Reference in New Issue
Block a user