mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-08-04 01:58:16 +00:00
Add vdbe bitwise operators: and, or, not
This commit is contained in:
parent
dddf850111
commit
d5391dc716
9 changed files with 407 additions and 4 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -19,5 +19,8 @@ env
|
|||
dist/
|
||||
.tmp/
|
||||
|
||||
|
||||
/testing/testing.db-wal
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
|
|
@ -252,9 +252,9 @@ Feature support of [sqlite expr syntax](https://www.sqlite.org/lang_expr.html).
|
|||
| AggStep | Yes |
|
||||
| And | No |
|
||||
| AutoCommit | No |
|
||||
| BitAnd | No |
|
||||
| BitNot | No |
|
||||
| BitOr | No |
|
||||
| BitAnd | Yes |
|
||||
| BitNot | Yes |
|
||||
| BitOr | Yes |
|
||||
| Blob | Yes |
|
||||
| Checkpoint | No |
|
||||
| Clear | No |
|
||||
|
|
|
@ -694,6 +694,20 @@ pub fn translate_expr(
|
|||
dest: target_register,
|
||||
});
|
||||
}
|
||||
ast::Operator::BitwiseAnd => {
|
||||
program.emit_insn(Insn::BitAnd {
|
||||
lhs: e1_reg,
|
||||
rhs: e2_reg,
|
||||
dest: target_register,
|
||||
});
|
||||
}
|
||||
ast::Operator::BitwiseOr => {
|
||||
program.emit_insn(Insn::BitOr {
|
||||
lhs: e1_reg,
|
||||
rhs: e2_reg,
|
||||
dest: target_register,
|
||||
});
|
||||
}
|
||||
other_unimplemented => todo!("{:?}", other_unimplemented),
|
||||
}
|
||||
Ok(target_register)
|
||||
|
@ -1615,6 +1629,45 @@ pub fn translate_expr(
|
|||
}
|
||||
Ok(target_register)
|
||||
}
|
||||
(UnaryOperator::BitwiseNot, ast::Expr::Literal(ast::Literal::Numeric(num_val))) => {
|
||||
let maybe_int = num_val.parse::<i64>();
|
||||
if let Ok(maybe_int) = maybe_int {
|
||||
program.emit_insn(Insn::Integer {
|
||||
value: !maybe_int,
|
||||
dest: target_register,
|
||||
});
|
||||
Ok(target_register)
|
||||
} else {
|
||||
let num_val = num_val.parse::<f64>()? as i64;
|
||||
program.emit_insn(Insn::Integer {
|
||||
value: !num_val,
|
||||
dest: target_register,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
}
|
||||
(UnaryOperator::BitwiseNot, ast::Expr::Literal(ast::Literal::Null)) => {
|
||||
program.emit_insn(Insn::Null {
|
||||
dest: target_register,
|
||||
dest_end: None,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
(UnaryOperator::BitwiseNot, _) => {
|
||||
let reg = program.alloc_register();
|
||||
translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
expr,
|
||||
reg,
|
||||
precomputed_exprs_to_registers,
|
||||
)?;
|
||||
program.emit_insn(Insn::BitNot {
|
||||
reg,
|
||||
dest: target_register,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
_ => todo!(),
|
||||
},
|
||||
ast::Expr::Variable(_) => todo!(),
|
||||
|
|
|
@ -70,6 +70,11 @@ fn resolve_aggregates(expr: &ast::Expr, aggs: &mut Vec<Aggregate>) -> bool {
|
|||
contains_aggregates |= resolve_aggregates(rhs, aggs);
|
||||
contains_aggregates
|
||||
}
|
||||
ast::Expr::Unary(_, expr) => {
|
||||
let mut contains_aggregates = false;
|
||||
contains_aggregates |= resolve_aggregates(expr, aggs);
|
||||
contains_aggregates
|
||||
}
|
||||
// TODO: handle other expressions that may contain aggregates
|
||||
_ => false,
|
||||
}
|
||||
|
|
|
@ -55,6 +55,33 @@ pub fn insn_to_str(
|
|||
0,
|
||||
format!("r[{}]=r[{}]/r[{}]", dest, lhs, rhs),
|
||||
),
|
||||
Insn::BitAnd { lhs, rhs, dest } => (
|
||||
"BitAnd",
|
||||
*lhs as i32,
|
||||
*rhs as i32,
|
||||
*dest as i32,
|
||||
OwnedValue::Text(Rc::new("".to_string())),
|
||||
0,
|
||||
format!("r[{}]=r[{}]&r[{}]", dest, lhs, rhs),
|
||||
),
|
||||
Insn::BitOr { lhs, rhs, dest } => (
|
||||
"BitOr",
|
||||
*lhs as i32,
|
||||
*rhs as i32,
|
||||
*dest as i32,
|
||||
OwnedValue::Text(Rc::new("".to_string())),
|
||||
0,
|
||||
format!("r[{}]=r[{}]|r[{}]", dest, lhs, rhs),
|
||||
),
|
||||
Insn::BitNot { reg, dest } => (
|
||||
"BitNot",
|
||||
*reg as i32,
|
||||
*dest as i32,
|
||||
0,
|
||||
OwnedValue::Text(Rc::new("".to_string())),
|
||||
0,
|
||||
format!("r[{}]=~r[{}]", dest, reg),
|
||||
),
|
||||
Insn::Null { dest, dest_end } => (
|
||||
"Null",
|
||||
0,
|
||||
|
|
209
core/vdbe/mod.rs
209
core/vdbe/mod.rs
|
@ -119,6 +119,23 @@ pub enum Insn {
|
|||
start_reg_b: usize,
|
||||
count: usize,
|
||||
},
|
||||
// Place the result of rhs bitwise AND lhs in third register.
|
||||
BitAnd {
|
||||
lhs: usize,
|
||||
rhs: usize,
|
||||
dest: usize,
|
||||
},
|
||||
// Place the result of rhs bitwise OR lhs in third register.
|
||||
BitOr {
|
||||
lhs: usize,
|
||||
rhs: usize,
|
||||
dest: usize,
|
||||
},
|
||||
// Place the result of bitwise NOT register P1 in dest register.
|
||||
BitNot {
|
||||
reg: usize,
|
||||
dest: usize,
|
||||
},
|
||||
// Jump to the instruction at address P1, P2, or P3 depending on whether in the most recent Compare instruction the P1 vector was less than, equal to, or greater than the P2 vector, respectively.
|
||||
Jump {
|
||||
target_pc_lt: BranchOffset,
|
||||
|
@ -838,7 +855,7 @@ impl Program {
|
|||
}
|
||||
(OwnedValue::Integer(i), OwnedValue::Float(f))
|
||||
| (OwnedValue::Float(f), OwnedValue::Integer(i)) => {
|
||||
state.registers[dest] = OwnedValue::Float(*i as f64 * *f as f64);
|
||||
state.registers[dest] = OwnedValue::Float(*i as f64 * { *f });
|
||||
}
|
||||
(OwnedValue::Null, _) | (_, OwnedValue::Null) => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
|
@ -1011,6 +1028,196 @@ impl Program {
|
|||
}
|
||||
state.pc += 1;
|
||||
}
|
||||
Insn::BitAnd { lhs, rhs, dest } => {
|
||||
let lhs = *lhs;
|
||||
let rhs = *rhs;
|
||||
let dest = *dest;
|
||||
match (&state.registers[lhs], &state.registers[rhs]) {
|
||||
// handle 0 and null cases
|
||||
(OwnedValue::Null, _) | (_, OwnedValue::Null) => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
}
|
||||
(_, OwnedValue::Integer(0))
|
||||
| (OwnedValue::Integer(0), _)
|
||||
| (_, OwnedValue::Float(0.0))
|
||||
| (OwnedValue::Float(0.0), _) => {
|
||||
state.registers[dest] = OwnedValue::Integer(0);
|
||||
}
|
||||
(OwnedValue::Integer(lh), OwnedValue::Integer(rh)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(lh & rh);
|
||||
}
|
||||
(OwnedValue::Float(lh), OwnedValue::Float(rh)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(*lh as i64 & *rh as i64);
|
||||
}
|
||||
(OwnedValue::Float(lh), OwnedValue::Integer(rh)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(*lh as i64 & rh);
|
||||
}
|
||||
(OwnedValue::Integer(lh), OwnedValue::Float(rh)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(lh & *rh as i64);
|
||||
}
|
||||
(OwnedValue::Agg(aggctx), other) | (other, OwnedValue::Agg(aggctx)) => {
|
||||
match other {
|
||||
OwnedValue::Agg(aggctx2) => {
|
||||
match (aggctx.final_value(), aggctx2.final_value()) {
|
||||
(OwnedValue::Integer(lh), OwnedValue::Integer(rh)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(lh & rh);
|
||||
}
|
||||
(OwnedValue::Float(lh), OwnedValue::Float(rh)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Integer(*lh as i64 & *rh as i64);
|
||||
}
|
||||
(OwnedValue::Integer(lh), OwnedValue::Float(rh)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Integer(lh & *rh as i64);
|
||||
}
|
||||
(OwnedValue::Float(lh), OwnedValue::Integer(rh)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Integer(*lh as i64 & rh);
|
||||
}
|
||||
_ => {
|
||||
unimplemented!(
|
||||
"{:?} {:?}",
|
||||
aggctx.final_value(),
|
||||
aggctx2.final_value()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
other => match (aggctx.final_value(), other) {
|
||||
(OwnedValue::Null, _) | (_, OwnedValue::Null) => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
}
|
||||
(_, OwnedValue::Integer(0))
|
||||
| (OwnedValue::Integer(0), _)
|
||||
| (_, OwnedValue::Float(0.0))
|
||||
| (OwnedValue::Float(0.0), _) => {
|
||||
state.registers[dest] = OwnedValue::Integer(0);
|
||||
}
|
||||
(OwnedValue::Integer(lh), OwnedValue::Integer(rh)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(lh & rh);
|
||||
}
|
||||
(OwnedValue::Float(lh), OwnedValue::Integer(rh)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Integer(*lh as i64 & rh);
|
||||
}
|
||||
(OwnedValue::Integer(lh), OwnedValue::Float(rh)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Integer(lh & *rh as i64);
|
||||
}
|
||||
_ => {
|
||||
unimplemented!("{:?} {:?}", aggctx.final_value(), other);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unimplemented!("{:?} {:?}", state.registers[lhs], state.registers[rhs]);
|
||||
}
|
||||
}
|
||||
state.pc += 1;
|
||||
}
|
||||
Insn::BitOr { lhs, rhs, dest } => {
|
||||
let lhs = *lhs;
|
||||
let rhs = *rhs;
|
||||
let dest = *dest;
|
||||
match (&state.registers[lhs], &state.registers[rhs]) {
|
||||
(OwnedValue::Null, _) | (_, OwnedValue::Null) => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
}
|
||||
(OwnedValue::Integer(lh), OwnedValue::Integer(rh)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(lh | rh);
|
||||
}
|
||||
(OwnedValue::Float(lh), OwnedValue::Integer(rh)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(*lh as i64 | rh);
|
||||
}
|
||||
(OwnedValue::Integer(lh), OwnedValue::Float(rh)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(lh | *rh as i64);
|
||||
}
|
||||
(OwnedValue::Float(lh), OwnedValue::Float(rh)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(*lh as i64 | *rh as i64);
|
||||
}
|
||||
(OwnedValue::Agg(aggctx), other) | (other, OwnedValue::Agg(aggctx)) => {
|
||||
match other {
|
||||
OwnedValue::Agg(aggctx2) => {
|
||||
let final_lhs = aggctx.final_value();
|
||||
let final_rhs = aggctx2.final_value();
|
||||
match (final_lhs, final_rhs) {
|
||||
(OwnedValue::Integer(lh), OwnedValue::Integer(rh)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(lh | rh);
|
||||
}
|
||||
(OwnedValue::Float(lh), OwnedValue::Float(rh)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Integer(*lh as i64 | *rh as i64);
|
||||
}
|
||||
(OwnedValue::Integer(lh), OwnedValue::Float(rh)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Integer(lh | *rh as i64);
|
||||
}
|
||||
(OwnedValue::Float(lh), OwnedValue::Integer(rh)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Integer(*lh as i64 | rh);
|
||||
}
|
||||
_ => {
|
||||
unimplemented!("{:?} {:?}", final_lhs, final_rhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
other => match (aggctx.final_value(), other) {
|
||||
(OwnedValue::Null, _) | (_, OwnedValue::Null) => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
}
|
||||
(OwnedValue::Integer(lh), OwnedValue::Integer(rh)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(lh | rh);
|
||||
}
|
||||
(OwnedValue::Float(lh), OwnedValue::Integer(rh)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Integer(*lh as i64 | rh);
|
||||
}
|
||||
(OwnedValue::Integer(lh), OwnedValue::Float(rh)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Integer(lh | *rh as i64);
|
||||
}
|
||||
_ => {
|
||||
unimplemented!("{:?} {:?}", aggctx.final_value(), other);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unimplemented!("{:?} {:?}", state.registers[lhs], state.registers[rhs]);
|
||||
}
|
||||
}
|
||||
state.pc += 1;
|
||||
}
|
||||
Insn::BitNot { reg, dest } => {
|
||||
let reg = *reg;
|
||||
let dest = *dest;
|
||||
match &state.registers[reg] {
|
||||
OwnedValue::Integer(i) => state.registers[dest] = OwnedValue::Integer(!i),
|
||||
OwnedValue::Float(f) => {
|
||||
state.registers[dest] = OwnedValue::Integer(!{ *f as i64 })
|
||||
}
|
||||
OwnedValue::Null => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
}
|
||||
OwnedValue::Agg(aggctx) => match aggctx.final_value() {
|
||||
OwnedValue::Integer(i) => {
|
||||
state.registers[dest] = OwnedValue::Integer(!i);
|
||||
}
|
||||
OwnedValue::Float(f) => {
|
||||
state.registers[dest] = OwnedValue::Integer(!{ *f as i64 });
|
||||
}
|
||||
OwnedValue::Null => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
}
|
||||
_ => unimplemented!("{:?}", aggctx),
|
||||
},
|
||||
_ => {
|
||||
unimplemented!("{:?}", state.registers[reg]);
|
||||
}
|
||||
}
|
||||
state.pc += 1;
|
||||
}
|
||||
Insn::Null { dest, dest_end } => {
|
||||
if let Some(dest_end) = dest_end {
|
||||
for i in *dest..=*dest_end {
|
||||
|
|
|
@ -261,3 +261,107 @@ do_execsql_test divide-agg-int-agg-float {
|
|||
do_execsql_test divide-agg-float-agg-int {
|
||||
SELECT min(price) / min(id) from products
|
||||
} {1.0}
|
||||
|
||||
|
||||
do_execsql_test bitwise-and-int-null {
|
||||
SELECT 1234 & NULL
|
||||
} {}
|
||||
|
||||
do_execsql_test bitwise-and-int-int {
|
||||
SELECT 1234 & 1234
|
||||
} {1234}
|
||||
|
||||
do_execsql_test bitwise-and-int-float {
|
||||
SELECT 660 & 261.8
|
||||
} {4}
|
||||
|
||||
do_execsql_test bitwise-and-float-float {
|
||||
SELECT 660.63 & 261.8
|
||||
} {4}
|
||||
|
||||
do_execsql_test bitwise-and-float-int-rev {
|
||||
SELECT 261.8 & 660
|
||||
} {4}
|
||||
|
||||
do_execsql_test bitwise-and-float-int-rev {
|
||||
SELECT SUM(id) from products
|
||||
} {66}
|
||||
|
||||
do_execsql_test bitwise-and-int-agg-int {
|
||||
SELECT 8261 & sum(id) from products
|
||||
} {64}
|
||||
|
||||
do_execsql_test bitwise-and-int-agg-float {
|
||||
SELECT 1036.6 & sum(id) from products
|
||||
} {0}
|
||||
|
||||
do_execsql_test bitwise-and-int-agg-int-agg {
|
||||
SELECT sum(id) & sum(id) from products
|
||||
} {66}
|
||||
|
||||
|
||||
do_execsql_test bitwise-or-int-null {
|
||||
SELECT 1234 | NULL
|
||||
} {}
|
||||
|
||||
do_execsql_test bitwise-or-null-int {
|
||||
SELECT NULL | 1234
|
||||
} {}
|
||||
|
||||
do_execsql_test bitwise-or-int-int {
|
||||
SELECT 4321 | 1234
|
||||
} {5363}
|
||||
|
||||
do_execsql_test bitwise-or-int-float {
|
||||
SELECT 660 | 1234.0
|
||||
} {1750}
|
||||
|
||||
do_execsql_test bitwise-or-int-agg {
|
||||
SELECT 18823 | sum(id) from products
|
||||
} {18887}
|
||||
|
||||
do_execsql_test bitwise-or-float-float {
|
||||
SELECT 1234.6 | 5432.2
|
||||
} {5626}
|
||||
|
||||
do_execsql_test bitwise-and-int-agg-int-agg {
|
||||
SELECT sum(id) | sum(id) from products
|
||||
} {66}
|
||||
|
||||
|
||||
do_execsql_test bitwise-not-null {
|
||||
SELECT ~NULL
|
||||
} {}
|
||||
|
||||
do_execsql_test bitwise-not-int {
|
||||
SELECT ~1234
|
||||
} {-1235}
|
||||
|
||||
do_execsql_test bitwise-not-float {
|
||||
SELECT ~823.34
|
||||
} {-824}
|
||||
|
||||
do_execsql_test bitwise-not-scalar-float {
|
||||
SELECT ~abs(693.9)
|
||||
} {-694}
|
||||
|
||||
do_execsql_test bitwise-not-scalar-int {
|
||||
SELECT ~abs(7566)
|
||||
} {-7567}
|
||||
|
||||
do_execsql_test bitwise-not-agg-int {
|
||||
SELECT ~sum(693)
|
||||
} {-694}
|
||||
|
||||
do_execsql_test bitwise-not-agg-and-agg {
|
||||
SELECT ~sum(693) & sum(-302)
|
||||
} {-958}
|
||||
|
||||
do_execsql_test bitwise-not-agg-int {
|
||||
SELECT ~sum(693)
|
||||
} {-694}
|
||||
|
||||
do_execsql_test bitwise-not-zero {
|
||||
SELECT ~0
|
||||
} {-1}
|
||||
|
||||
|
|
|
@ -792,6 +792,7 @@ impl ToTokens for Operator {
|
|||
Self::ArrowRightShift => s.append(TK_PTR, Some("->>")),
|
||||
Self::BitwiseAnd => s.append(TK_BITAND, None),
|
||||
Self::BitwiseOr => s.append(TK_BITOR, None),
|
||||
Self::BitwiseNot => s.append(TK_BITNOT, None),
|
||||
Self::Concat => s.append(TK_CONCAT, None),
|
||||
Self::Equals => s.append(TK_EQ, None),
|
||||
Self::Divide => s.append(TK_SLASH, None),
|
||||
|
|
|
@ -583,6 +583,8 @@ pub enum Operator {
|
|||
BitwiseAnd,
|
||||
/// `|`
|
||||
BitwiseOr,
|
||||
/// `~`
|
||||
BitwiseNot,
|
||||
/// String concatenation (`||`)
|
||||
Concat,
|
||||
/// `=` or `==`
|
||||
|
@ -630,6 +632,7 @@ impl From<YYCODETYPE> for Operator {
|
|||
x if x == TK_NE as YYCODETYPE => Self::NotEquals,
|
||||
x if x == TK_BITAND as YYCODETYPE => Self::BitwiseAnd,
|
||||
x if x == TK_BITOR as YYCODETYPE => Self::BitwiseOr,
|
||||
x if x == TK_BITNOT as YYCODETYPE => Self::BitwiseNot,
|
||||
x if x == TK_LSHIFT as YYCODETYPE => Self::LeftShift,
|
||||
x if x == TK_RSHIFT as YYCODETYPE => Self::RightShift,
|
||||
x if x == TK_PLUS as YYCODETYPE => Self::Add,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue