From 449ab408534af74ffdb5565728a7fbe0612b6724 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Mon, 15 Jul 2024 19:01:12 +0300 Subject: [PATCH 1/3] Add FAQ entry about Limbo and libSQL --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index e3f959502..28176a109 100644 --- a/README.md +++ b/README.md @@ -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 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) From 0a7d0588d75bad29694aa8b2ff90bab4ba0c3d6e Mon Sep 17 00:00:00 2001 From: Pere Diaz Bou Date: Mon, 15 Jul 2024 14:49:47 +0200 Subject: [PATCH 2/3] core: implement AND and OR + complex conditions. this also fixes NULL print to empty string Signed-off-by: Pere Diaz Bou --- core/translate.rs | 235 ++++++++++++++++++++++++++++++++++------------ core/vdbe.rs | 4 - testing/all.test | 48 ++++++++++ 3 files changed, 225 insertions(+), 62 deletions(-) diff --git a/core/translate.rs b/core/translate.rs index 405d824eb..fea1096d8 100644 --- a/core/translate.rs +++ b/core/translate.rs @@ -306,7 +306,7 @@ fn emit_limit_insn(limit_info: &Option, program: &mut ProgramBuilder) fn translate_where(select: &Select, program: &mut ProgramBuilder) -> Result> { 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,60 +568,171 @@ 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::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) => { @@ -632,22 +743,30 @@ fn translate_condition_expr( 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( diff --git a/core/vdbe.rs b/core/vdbe.rs index a00018301..731cee22d 100644 --- a/core/vdbe.rs +++ b/core/vdbe.rs @@ -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 { diff --git a/testing/all.test b/testing/all.test index f376f70a8..9932115fe 100755 --- a/testing/all.test +++ b/testing/all.test @@ -192,3 +192,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 +} From 881c12026f216118ab3db780c06d28cce03d696e Mon Sep 17 00:00:00 2001 From: Pere Diaz Bou Date: Mon, 15 Jul 2024 19:59:49 +0200 Subject: [PATCH 3/3] README: fix typo Signed-off-by: Pere Diaz Bou --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 28176a109..839821afe 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ cargo bench --bench benchmark -- --profile-time=5 ### 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 something that will be decided in the future. +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