Merge branch 'main' of github.com:benclmnt/limbo into feat/where-and

This commit is contained in:
Bennett Clement
2024-07-16 08:10:24 +08:00
4 changed files with 231 additions and 62 deletions

View File

@@ -83,6 +83,12 @@ echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
cargo bench --bench benchmark -- --profile-time=5
```
## FAQ
### How is Limbo different from libSQL?
Limbo is a research project to build a SQLite compatible in-process database in Rust with native async support. The libSQL project, on the other hand, is an open source, open contribution fork of SQLite, with focus on production features such as replication, backups, encryption, and so on. There is no hard dependency between the two projects. Of course, if Limbo becomes widely successful, we might consider merging with libSQL, but that is something that will be decided in the future.
## Publications
* Pekka Enberg, Sasu Tarkoma, Jon Crowcroft Ashwin Rao (2024). Serverless Runtime / Database Co-Design With Asynchronous I/O. In _EdgeSys 24_. [[PDF]](https://penberg.org/papers/penberg-edgesys24.pdf)

View File

@@ -306,7 +306,7 @@ fn emit_limit_insn(limit_info: &Option<LimitInfo>, program: &mut ProgramBuilder)
fn translate_where(select: &Select, program: &mut ProgramBuilder) -> Result<Option<BranchOffset>> {
if let Some(w) = &select.where_clause {
let label = program.allocate_label();
translate_condition_expr(program, select, w, label)?;
translate_condition_expr(program, select, w, label, false)?;
Ok(Some(label))
} else {
Ok(None)
@@ -339,8 +339,8 @@ fn translate_conditions(
ast::JoinConstraint::On(expr) => {
// Combine where clause and join condition
let label = program.allocate_label();
translate_condition_expr(program, select, where_clause, label)?;
translate_condition_expr(program, select, expr, label)?;
translate_condition_expr(program, select, where_clause, label, false)?;
translate_condition_expr(program, select, expr, label, false)?;
Ok(Some(label))
}
ast::JoinConstraint::Using(_) => {
@@ -351,13 +351,13 @@ fn translate_conditions(
(None, None) => Ok(None),
(Some(where_clause), None) => {
let label = program.allocate_label();
translate_condition_expr(program, select, where_clause, label)?;
translate_condition_expr(program, select, where_clause, label, false)?;
Ok(Some(label))
}
(None, Some(join)) => match join {
ast::JoinConstraint::On(expr) => {
let label = program.allocate_label();
translate_condition_expr(program, select, expr, label)?;
translate_condition_expr(program, select, expr, label, false)?;
Ok(Some(label))
}
ast::JoinConstraint::Using(_) => {
@@ -568,7 +568,8 @@ fn translate_condition_expr(
program: &mut ProgramBuilder,
select: &Select,
expr: &ast::Expr,
jump_target: BranchOffset,
target_jump: BranchOffset,
jump_if_true: bool, // if true jump to target on op == true, if false invert op
) -> Result<()> {
match expr {
ast::Expr::Binary(e1, op, e2) => match op {
@@ -591,56 +592,166 @@ fn translate_condition_expr_leaf(
) -> Result<()> {
match expr {
ast::Expr::Between { .. } => todo!(),
ast::Expr::Binary(e1, op, e2) => {
let e1_reg = program.alloc_register();
let e2_reg = program.alloc_register();
let _ = translate_expr(program, select, e1, e1_reg)?;
match e1.as_ref() {
ast::Expr::Literal(_) => program.mark_last_insn_constant(),
_ => {}
ast::Expr::Binary(lhs, ast::Operator::And, rhs) => {
if jump_if_true {
let label = program.allocate_label();
let _ = translate_condition_expr(program, select, lhs, label, false);
let _ = translate_condition_expr(program, select, rhs, target_jump, true);
program.resolve_label(label, program.offset());
} else {
let _ = translate_condition_expr(program, select, lhs, target_jump, false);
let _ = translate_condition_expr(program, select, rhs, target_jump, false);
}
let _ = translate_expr(program, select, e2, e2_reg)?;
match e2.as_ref() {
ast::Expr::Literal(_) => program.mark_last_insn_constant(),
_ => {}
}
ast::Expr::Binary(lhs, ast::Operator::Or, rhs) => {
if jump_if_true {
let _ = translate_condition_expr(program, select, lhs, target_jump, true);
let _ = translate_condition_expr(program, select, rhs, target_jump, true);
} else {
let label = program.allocate_label();
let _ = translate_condition_expr(program, select, lhs, label, true);
let _ = translate_condition_expr(program, select, rhs, target_jump, false);
program.resolve_label(label, program.offset());
}
if jump_target < 0 {
program.add_label_dependency(jump_target, program.offset());
}
ast::Expr::Binary(lhs, op, rhs) => {
let lhs_reg = program.alloc_register();
let rhs_reg = program.alloc_register();
let _ = translate_expr(program, select, lhs, lhs_reg);
let _ = translate_expr(program, select, rhs, rhs_reg);
match op {
ast::Operator::Greater => {
if jump_if_true {
program.emit_insn_with_label_dependency(
Insn::Gt {
lhs: lhs_reg,
rhs: rhs_reg,
target_pc: target_jump,
},
target_jump,
)
} else {
program.emit_insn_with_label_dependency(
Insn::Le {
lhs: lhs_reg,
rhs: rhs_reg,
target_pc: target_jump,
},
target_jump,
)
}
}
ast::Operator::GreaterEquals => {
if jump_if_true {
program.emit_insn_with_label_dependency(
Insn::Ge {
lhs: lhs_reg,
rhs: rhs_reg,
target_pc: target_jump,
},
target_jump,
)
} else {
program.emit_insn_with_label_dependency(
Insn::Lt {
lhs: lhs_reg,
rhs: rhs_reg,
target_pc: target_jump,
},
target_jump,
)
}
}
ast::Operator::Less => {
if jump_if_true {
program.emit_insn_with_label_dependency(
Insn::Lt {
lhs: lhs_reg,
rhs: rhs_reg,
target_pc: target_jump,
},
target_jump,
)
} else {
program.emit_insn_with_label_dependency(
Insn::Ge {
lhs: lhs_reg,
rhs: rhs_reg,
target_pc: target_jump,
},
target_jump,
)
}
}
ast::Operator::LessEquals => {
if jump_if_true {
program.emit_insn_with_label_dependency(
Insn::Le {
lhs: lhs_reg,
rhs: rhs_reg,
target_pc: target_jump,
},
target_jump,
)
} else {
program.emit_insn_with_label_dependency(
Insn::Gt {
lhs: lhs_reg,
rhs: rhs_reg,
target_pc: target_jump,
},
target_jump,
)
}
}
ast::Operator::Equals => {
if jump_if_true {
program.emit_insn_with_label_dependency(
Insn::Eq {
lhs: lhs_reg,
rhs: rhs_reg,
target_pc: target_jump,
},
target_jump,
)
} else {
program.emit_insn_with_label_dependency(
Insn::Ne {
lhs: lhs_reg,
rhs: rhs_reg,
target_pc: target_jump,
},
target_jump,
)
}
}
ast::Operator::NotEquals => {
if jump_if_true {
program.emit_insn_with_label_dependency(
Insn::Ne {
lhs: lhs_reg,
rhs: rhs_reg,
target_pc: target_jump,
},
target_jump,
)
} else {
program.emit_insn_with_label_dependency(
Insn::Eq {
lhs: lhs_reg,
rhs: rhs_reg,
target_pc: target_jump,
},
target_jump,
)
}
}
ast::Operator::Is => todo!(),
ast::Operator::IsNot => todo!(),
_ => {
todo!("op {:?} not implemented", op);
}
}
program.emit_insn(match op {
ast::Operator::NotEquals => Insn::Eq {
lhs: e1_reg,
rhs: e2_reg,
target_pc: jump_target,
},
ast::Operator::Equals => Insn::Ne {
lhs: e1_reg,
rhs: e2_reg,
target_pc: jump_target,
},
ast::Operator::Less => Insn::Ge {
lhs: e1_reg,
rhs: e2_reg,
target_pc: jump_target,
},
ast::Operator::LessEquals => Insn::Gt {
lhs: e1_reg,
rhs: e2_reg,
target_pc: jump_target,
},
ast::Operator::Greater => Insn::Le {
lhs: e1_reg,
rhs: e2_reg,
target_pc: jump_target,
},
ast::Operator::GreaterEquals => Insn::Lt {
lhs: e1_reg,
rhs: e2_reg,
target_pc: jump_target,
},
other => todo!("{:?}", other),
});
Ok(())
}
ast::Expr::Literal(lit) => match lit {
ast::Literal::Numeric(val) => {
@@ -651,22 +762,30 @@ fn translate_condition_expr_leaf(
value: int_value,
dest: reg,
});
if jump_target < 0 {
program.add_label_dependency(jump_target, program.offset());
if target_jump < 0 {
program.add_label_dependency(target_jump, program.offset());
}
program.emit_insn(Insn::IfNot {
reg,
target_pc: jump_target,
target_pc: target_jump,
});
Ok(())
} else {
anyhow::bail!("Parse error: unsupported literal type in condition");
}
}
_ => todo!(),
},
_ => todo!(),
ast::Expr::InList { lhs, not, rhs } => {}
ast::Expr::Like {
lhs,
not,
op,
rhs,
escape,
} => {}
_ => todo!("op {:?} not implemented", expr),
}
Ok(())
}
fn wrap_eval_jump_expr(

View File

@@ -345,10 +345,6 @@ impl ProgramBuilder {
);
let label_references = &mut self.unresolved_labels[label_index];
assert!(
!label_references.is_empty(),
"Trying to resolve an empty created label, all labels must be resolved for now."
);
for insn_reference in label_references.iter() {
let insn = &mut self.insns[*insn_reference];
match insn {

View File

@@ -205,3 +205,51 @@ do_execsql_test select-add {
select u.age + 1 from users u where u.age = 91 limit 1;
} {92}
do_execsql_test select-where-and {
select first_name, age from users where first_name = 'Jamie' and age > 80
} {Jamie|94
Jamie|88
Jamie|99
Jamie|92
Jamie|87
Jamie|88
}
do_execsql_test select-where-or {
select first_name, age from users where first_name = 'Jamie' and age > 80
} {Jamie|94
Jamie|88
Jamie|99
Jamie|92
Jamie|87
Jamie|88
}
do_execsql_test select-where-and-or {
select first_name, age from users where first_name = 'Jamie' or age = 1 and age = 2
} {Jamie|94
Jamie|88
Jamie|31
Jamie|26
Jamie|71
Jamie|50
Jamie|28
Jamie|46
Jamie|17
Jamie|64
Jamie|76
Jamie|99
Jamie|92
Jamie|47
Jamie|27
Jamie|54
Jamie|47
Jamie|15
Jamie|12
Jamie|71
Jamie|87
Jamie|34
Jamie|88
Jamie|41
Jamie|73
}