use stack-based bytecodes instead of tree-walking for evaluating expressions

main
Ziyang Hu 2 years ago
parent 985c0d758a
commit c110f3be45

@ -214,7 +214,7 @@ goto_stmt = {"%goto" ~ ident}
return_stmt = {"%return" ~ query_script_inner?}
while_block = {"%while" ~ query_script_inner ~ "%loop" ~ imperative_block ~ "%end"}
do_while_block = {"%loop" ~ imperative_block ~ "%while" ~ query_script_inner ~ "%end"}
temp_swap {"%swap" ~ ident ~ ident}
temp_swap = {"%swap" ~ underscore_ident ~ underscore_ident}
/*

@ -21,8 +21,132 @@ use thiserror::Error;
use crate::data::functions::*;
use crate::data::symb::Symbol;
use crate::data::value::{DataValue, LARGEST_UTF_CHAR};
use crate::parse::expr::expr2bytecode;
use crate::parse::SourceSpan;
#[derive(Clone, PartialEq, Eq, serde_derive::Serialize, serde_derive::Deserialize, Debug)]
pub enum ExprByteCode {
/// push 1
Binding {
var: Symbol,
tuple_pos: Option<usize>,
},
/// push 1
Const {
val: DataValue,
#[serde(skip)]
span: SourceSpan,
},
/// pop n, push 1
Apply {
op: &'static Op,
arity: usize,
#[serde(skip)]
span: SourceSpan,
},
/// pop 1
JumpIfFalse {
jump_to: usize,
#[serde(skip)]
span: SourceSpan,
},
/// unchanged
Goto {
jump_to: usize,
#[serde(skip)]
span: SourceSpan,
},
}
#[derive(Error, Diagnostic, Debug)]
#[error("The variable '{0}' is unbound")]
#[diagnostic(code(eval::unbound))]
struct UnboundVariableError(String, #[label] SourceSpan);
#[derive(Error, Diagnostic, Debug)]
#[error("The tuple bound by variable '{0}' is too short: index is {1}, length is {2}")]
#[diagnostic(help("This is definitely a bug. Please report it."))]
#[diagnostic(code(eval::tuple_too_short))]
struct TupleTooShortError(String, usize, usize, #[label] SourceSpan);
pub(crate) fn eval_bytecode_pred(
bytecodes: &[ExprByteCode],
bindings: impl AsRef<[DataValue]>,
stack: &mut Vec<DataValue>,
span: SourceSpan,
) -> Result<bool> {
match eval_bytecode(bytecodes, bindings, stack)? {
DataValue::Bool(b) => Ok(b),
v => bail!(PredicateTypeError(span, v)),
}
}
pub(crate) fn eval_bytecode(
bytecodes: &[ExprByteCode],
bindings: impl AsRef<[DataValue]>,
stack: &mut Vec<DataValue>,
) -> Result<DataValue> {
stack.clear();
let mut pointer = 0;
loop {
if pointer >= bytecodes.len() {
break;
}
let current_instruction = &bytecodes[pointer];
match current_instruction {
ExprByteCode::Binding { var, tuple_pos, .. } => match tuple_pos {
None => {
bail!(UnboundVariableError(var.name.to_string(), var.span))
}
Some(i) => {
let val = bindings
.as_ref()
.get(*i)
.ok_or_else(|| {
TupleTooShortError(
var.name.to_string(),
*i,
bindings.as_ref().len(),
var.span,
)
})?
.clone();
stack.push(val);
pointer += 1;
}
},
ExprByteCode::Const { val, .. } => {
stack.push(val.clone());
pointer += 1;
}
ExprByteCode::Apply { op, arity, span } => {
let frame_start = stack.len() - *arity;
let args_frame = &stack[frame_start..];
let result = (op.inner)(args_frame)
.map_err(|err| EvalRaisedError(*span, err.to_string()))?;
stack.truncate(frame_start);
stack.push(result);
pointer += 1;
}
ExprByteCode::JumpIfFalse { jump_to, span } => {
let val = stack.pop().unwrap();
let cond = val
.get_bool()
.ok_or_else(|| PredicateTypeError(*span, val))?;
if cond {
pointer += 1;
} else {
pointer = *jump_to;
}
}
ExprByteCode::Goto { jump_to, .. } => {
pointer = *jump_to;
}
}
}
Ok(stack.pop().unwrap())
}
#[derive(Clone, PartialEq, Eq, serde_derive::Serialize, serde_derive::Deserialize)]
pub enum Expr {
Binding {
@ -45,11 +169,11 @@ pub enum Expr {
#[serde(skip)]
span: SourceSpan,
},
Try {
clauses: Vec<Expr>,
#[serde(skip)]
span: SourceSpan,
},
// Try {
// clauses: Vec<Expr>,
// #[serde(skip)]
// span: SourceSpan,
// },
}
impl Debug for Expr {
@ -83,13 +207,6 @@ impl Display for Expr {
}
writer.finish()
}
Expr::Try { clauses, .. } => {
let mut writer = f.debug_tuple("try");
for clause in clauses {
writer.field(clause);
}
writer.finish()
}
}
}
}
@ -111,13 +228,15 @@ struct BadEntityId(DataValue, #[label] SourceSpan);
struct EvalRaisedError(#[label] SourceSpan, #[help] String);
impl Expr {
pub(crate) fn compile(&self) -> Vec<ExprByteCode> {
let mut collector = vec![];
expr2bytecode(self, &mut collector);
collector
}
pub(crate) fn span(&self) -> SourceSpan {
match self {
Expr::Binding { var, .. } => var.span,
Expr::Const { span, .. }
| Expr::Apply { span, .. }
| Expr::Cond { span, .. }
| Expr::Try { span, .. } => *span,
Expr::Const { span, .. } | Expr::Apply { span, .. } | Expr::Cond { span, .. } => *span,
}
}
pub(crate) fn get_binding(&self) -> Option<&Symbol> {
@ -190,11 +309,6 @@ impl Expr {
val.fill_binding_indices(binding_map)?;
}
}
Expr::Try { clauses, .. } => {
for clause in clauses {
clause.fill_binding_indices(binding_map)?;
}
}
}
Ok(())
}
@ -223,12 +337,11 @@ impl Expr {
cond.do_binding_indices(coll);
val.do_binding_indices(coll)
}
}
Expr::Try { clauses, .. } => {
for clause in clauses {
clause.do_binding_indices(coll)
}
}
} // Expr::Try { clauses, .. } => {
// for clause in clauses {
// clause.do_binding_indices(coll)
// }
// }
}
}
pub(crate) fn eval_to_const(mut self) -> Result<DataValue> {
@ -301,44 +414,26 @@ impl Expr {
val.collect_bindings(coll)
}
}
Expr::Try { clauses, .. } => {
for clause in clauses {
clause.collect_bindings(coll);
}
}
}
}
pub(crate) fn eval(&self, bindings: impl AsRef<[DataValue]>) -> Result<DataValue> {
match self {
Expr::Binding { var, tuple_pos, .. } => match tuple_pos {
None => {
#[derive(Error, Diagnostic, Debug)]
#[error("The variable '{0}' is unbound")]
#[diagnostic(code(eval::unbound))]
struct UnboundVariableError(String, #[label] SourceSpan);
bail!(UnboundVariableError(var.name.to_string(), var.span))
}
Some(i) => {
#[derive(Error, Diagnostic, Debug)]
#[error("The tuple bound by variable '{0}' is too short: index is {1}, length is {2}")]
#[diagnostic(help("This is definitely a bug. Please report it."))]
#[diagnostic(code(eval::unbound))]
struct TupleTooShortError(String, usize, usize, #[label] SourceSpan);
Ok(bindings
.as_ref()
.get(*i)
.ok_or_else(|| {
TupleTooShortError(
var.name.to_string(),
*i,
bindings.as_ref().len(),
var.span,
)
})?
.clone())
}
Some(i) => Ok(bindings
.as_ref()
.get(*i)
.ok_or_else(|| {
TupleTooShortError(
var.name.to_string(),
*i,
bindings.as_ref().len(),
var.span,
)
})?
.clone()),
},
Expr::Const { val, .. } => Ok(val.clone()),
Expr::Apply { op, args, .. } => {
@ -362,34 +457,11 @@ impl Expr {
}
Ok(DataValue::Null)
}
Expr::Try { clauses, .. } => {
if clauses.is_empty() {
Ok(DataValue::Null)
} else {
for item in clauses.iter().take(clauses.len() - 1) {
let res = item.eval(bindings.as_ref());
if res.is_ok() {
return res;
}
}
clauses[clauses.len() - 1].eval(bindings.as_ref())
}
}
}
}
pub(crate) fn eval_pred(&self, bindings: impl AsRef<[DataValue]>) -> Result<bool> {
match self.eval(bindings)? {
DataValue::Bool(b) => Ok(b),
v => {
bail!(PredicateTypeError(self.span(), v))
}
}
}
pub(crate) fn extract_bound(&self, target: &Symbol) -> Result<ValueRange> {
Ok(match self {
Expr::Binding { .. } | Expr::Const { .. } | Expr::Cond { .. } | Expr::Try { .. } => {
ValueRange::default()
}
Expr::Binding { .. } | Expr::Const { .. } | Expr::Cond { .. } => ValueRange::default(),
Expr::Apply { op, args, .. } => match op.name {
n if n == OP_GE.name || n == OP_GT.name => {
if let Some(symb) = args[0].get_binding() {

@ -14,7 +14,7 @@ use ordered_float::OrderedFloat;
use priority_queue::PriorityQueue;
use smartstring::{LazyCompact, SmartString};
use crate::data::expr::Expr;
use crate::data::expr::{eval_bytecode, Expr};
use crate::data::symb::Symbol;
use crate::data::tuple::Tuple;
use crate::data::value::DataValue;
@ -81,11 +81,13 @@ fn astar(
) -> Result<(f64, Vec<DataValue>)> {
let start_node = &starting[0];
let goal_node = &goal[0];
let eval_heuristic = |node: &Tuple| -> Result<f64> {
let heuristic_bytecode = heuristic.compile();
let mut stack = vec![];
let mut eval_heuristic = |node: &Tuple| -> Result<f64> {
let mut v = node.clone();
v.extend_from_slice(goal);
let t = v;
let cost_val = heuristic.eval(&t)?;
let cost_val = eval_bytecode(&heuristic_bytecode, &t, &mut stack)?;
let cost = cost_val.get_float().ok_or_else(|| {
BadExprValueError(
cost_val,

@ -12,7 +12,7 @@ use miette::Result;
use smartstring::{LazyCompact, SmartString};
use crate::fixed_rule::{FixedRule, FixedRulePayload, NodeNotFoundError};
use crate::data::expr::Expr;
use crate::data::expr::{eval_bytecode_pred, Expr};
use crate::data::symb::Symbol;
use crate::data::value::DataValue;
use crate::parse::SourceSpan;
@ -35,12 +35,15 @@ impl FixedRule for Bfs {
let mut condition = payload.expr_option("condition", None)?;
let binding_map = nodes.get_binding_map(0);
condition.fill_binding_indices(&binding_map)?;
let condition_bytecode = condition.compile();
let condition_span = condition.span();
let binding_indices = condition.binding_indices();
let skip_query_nodes = binding_indices.is_subset(&BTreeSet::from([0]));
let mut visited: BTreeSet<DataValue> = Default::default();
let mut backtrace: BTreeMap<DataValue, DataValue> = Default::default();
let mut found: Vec<(DataValue, DataValue)> = vec![];
let mut stack = vec![];
'outer: for node_tuple in starting_nodes.iter()? {
let node_tuple = node_tuple?;
@ -76,7 +79,7 @@ impl FixedRule for Bfs {
})??
};
if condition.eval_pred(&cand_tuple)? {
if eval_bytecode_pred(&condition_bytecode, &cand_tuple, &mut stack, condition_span)? {
found.push((starting_node.clone(), to_node.clone()));
if found.len() >= limit {
break 'outer;

@ -11,10 +11,10 @@ use std::collections::{BTreeMap, BTreeSet};
use miette::Result;
use smartstring::{LazyCompact, SmartString};
use crate::fixed_rule::{FixedRule, FixedRulePayload, NodeNotFoundError};
use crate::data::expr::Expr;
use crate::data::expr::{eval_bytecode_pred, Expr};
use crate::data::symb::Symbol;
use crate::data::value::DataValue;
use crate::fixed_rule::{FixedRule, FixedRulePayload, NodeNotFoundError};
use crate::parse::SourceSpan;
use crate::runtime::db::Poison;
use crate::runtime::temp_store::RegularTempStore;
@ -35,12 +35,15 @@ impl FixedRule for Dfs {
let mut condition = payload.expr_option("condition", None)?;
let binding_map = nodes.get_binding_map(0);
condition.fill_binding_indices(&binding_map)?;
let condition_bytecode = condition.compile();
let condition_span = condition.span();
let binding_indices = condition.binding_indices();
let skip_query_nodes = binding_indices.is_subset(&BTreeSet::from([0]));
let mut visited: BTreeSet<DataValue> = Default::default();
let mut backtrace: BTreeMap<DataValue, DataValue> = Default::default();
let mut found: Vec<(DataValue, DataValue)> = vec![];
let mut stack = vec![];
'outer: for node_tuple in starting_nodes.iter()? {
let node_tuple = node_tuple?;
@ -49,10 +52,10 @@ impl FixedRule for Dfs {
continue;
}
let mut stack: Vec<DataValue> = vec![];
stack.push(starting_node.clone());
let mut to_visit_stack: Vec<DataValue> = vec![];
to_visit_stack.push(starting_node.clone());
while let Some(candidate) = stack.pop() {
while let Some(candidate) = to_visit_stack.pop() {
if visited.contains(&candidate) {
continue;
}
@ -69,7 +72,8 @@ impl FixedRule for Dfs {
})??
};
if condition.eval_pred(&cand_tuple)? {
if eval_bytecode_pred(&condition_bytecode, &cand_tuple, &mut stack, condition_span)?
{
found.push((starting_node.clone(), candidate.clone()));
if found.len() >= limit {
break 'outer;
@ -85,7 +89,7 @@ impl FixedRule for Dfs {
continue;
}
backtrace.insert(to_node.clone(), candidate.clone());
stack.push(to_node.clone());
to_visit_stack.push(to_node.clone());
poison.check()?;
}
}

@ -15,7 +15,7 @@ use rand::prelude::*;
use smartstring::{LazyCompact, SmartString};
use crate::fixed_rule::{FixedRule, FixedRulePayload, BadExprValueError, NodeNotFoundError};
use crate::data::expr::Expr;
use crate::data::expr::{eval_bytecode, Expr};
use crate::data::symb::Symbol;
use crate::data::value::DataValue;
use crate::parse::SourceSpan;
@ -38,13 +38,17 @@ impl FixedRule for RandomWalk {
let steps = payload.pos_integer_option("steps", None)?;
let mut maybe_weight = payload.expr_option("weight", None).ok();
let mut maybe_weight_bytecode = None;
if let Some(weight) = &mut maybe_weight {
let mut nodes_binding = nodes.get_binding_map(0);
let nodes_arity = nodes.arity()?;
let edges_binding = edges.get_binding_map(nodes_arity);
nodes_binding.extend(edges_binding);
weight.fill_binding_indices(&nodes_binding)?;
maybe_weight_bytecode = Some((weight.compile(), weight.span()));
}
let maybe_weight_bytecode = maybe_weight_bytecode;
let mut stack = vec![];
let mut counter = 0i64;
let mut rng = thread_rng();
@ -69,20 +73,20 @@ impl FixedRule for RandomWalk {
if candidate_steps.is_empty() {
break;
}
let next_step = if let Some(weight_expr) = &maybe_weight {
let next_step = if let Some((weight_expr, span)) = &maybe_weight_bytecode {
let weights: Vec<_> = candidate_steps
.iter()
.map(|t| -> Result<f64> {
let mut cand = current_tuple.clone();
cand.extend_from_slice(t);
Ok(match weight_expr.eval(&cand)? {
Ok(match eval_bytecode(weight_expr, &cand, &mut stack)? {
DataValue::Num(n) => {
let f = n.get_float();
ensure!(
f >= 0.,
BadExprValueError(
DataValue::from(f),
weight_expr.span(),
*span,
"'weight' must evaluate to a non-negative number"
.to_string()
)
@ -91,7 +95,7 @@ impl FixedRule for RandomWalk {
}
v => bail!(BadExprValueError(
v,
weight_expr.span(),
*span,
"'weight' must evaluate to a non-negative number"
.to_string()
)),

@ -12,7 +12,7 @@ use itertools::Itertools;
use miette::{bail, Result};
use smartstring::{LazyCompact, SmartString};
use crate::data::expr::Expr;
use crate::data::expr::{eval_bytecode, Expr};
use crate::data::functions::OP_LIST;
use crate::data::program::WrongFixedRuleOptionError;
use crate::data::symb::Symbol;
@ -72,12 +72,18 @@ impl FixedRule for ReorderSort {
for out in out_list.iter_mut() {
out.fill_binding_indices(&binding_map)?;
}
let out_bytecods = out_list.iter().map(|e| e.compile()).collect_vec();
let sort_by_bytecodes = sort_by.compile();
let mut stack = vec![];
let mut buffer = vec![];
for tuple in in_rel.iter()? {
let tuple = tuple?;
let sorter = sort_by.eval(&tuple)?;
let mut s_tuple: Vec<_> = out_list.iter().map(|ex| ex.eval(&tuple)).try_collect()?;
let sorter = eval_bytecode(&sort_by_bytecodes, &tuple, &mut stack)?;
let mut s_tuple: Vec<_> = out_bytecods
.iter()
.map(|ex| eval_bytecode(ex, &tuple, &mut stack))
.try_collect()?;
s_tuple.push(sorter);
buffer.push(s_tuple);
poison.check()?;

@ -15,7 +15,7 @@ use pest::pratt_parser::{Op, PrattParser};
use smartstring::{LazyCompact, SmartString};
use thiserror::Error;
use crate::data::expr::{get_op, Expr};
use crate::data::expr::{get_op, Expr, ExprByteCode};
use crate::data::functions::{
OP_ADD, OP_AND, OP_COALESCE, OP_CONCAT, OP_DIV, OP_EQ, OP_GE, OP_GT, OP_LE, OP_LIST, OP_LT,
OP_MINUS, OP_MOD, OP_MUL, OP_NEGATE, OP_NEQ, OP_OR, OP_POW, OP_SUB,
@ -53,6 +53,41 @@ lazy_static! {
#[diagnostic(code(parser::invalid_expression))]
pub(crate) struct InvalidExpression(#[label] pub(crate) SourceSpan);
pub(crate) fn expr2bytecode(expr: &Expr, collector: &mut Vec<ExprByteCode>) {
match expr {
Expr::Binding { var, tuple_pos } => collector.push(ExprByteCode::Binding { var: var.clone(), tuple_pos: *tuple_pos }),
Expr::Const { val, span } => collector.push(ExprByteCode::Const { val: val.clone(), span: *span }),
Expr::Apply { op, args, span } => {
let arity = args.len();
for arg in args.iter() {
expr2bytecode(arg, collector);
}
collector.push(ExprByteCode::Apply { op, arity, span: *span })
}
Expr::Cond { clauses, span } => {
let mut return_jump_pos = vec![];
for (cond, val) in clauses {
// +1
expr2bytecode(cond, collector);
// -1
collector.push(ExprByteCode::JumpIfFalse { jump_to: 0, span: *span });
let false_jump_amend_pos = collector.len();
// +1 in this branch
expr2bytecode(val, collector);
collector.push(ExprByteCode::Goto { jump_to: 0, span: *span });
return_jump_pos.push(collector.len());
collector[false_jump_amend_pos] = ExprByteCode::JumpIfFalse {
jump_to: collector.len() + 1,
span: *span,
};
}
for pos in return_jump_pos {
collector[pos] = ExprByteCode::Goto { jump_to: pos, span: *span }
}
}
}
}
pub(crate) fn build_expr(pair: Pair<'_>, param_pool: &BTreeMap<String, DataValue>) -> Result<Expr> {
ensure!(
pair.as_rule() == Rule::expr,
@ -231,10 +266,6 @@ fn build_term(pair: Pair<'_>, param_pool: &BTreeMap<String, DataValue>) -> Resul
struct FuncNotFoundError(String, #[label] SourceSpan);
match ident {
"try" => Expr::Try {
clauses: args,
span,
},
"cond" => {
if args.len() & 1 == 1 {
args.insert(
@ -245,10 +276,20 @@ fn build_term(pair: Pair<'_>, param_pool: &BTreeMap<String, DataValue>) -> Resul
},
)
}
let clauses = args
let mut clauses = args
.chunks(2)
.map(|pair| (pair[0].clone(), pair[1].clone()))
.collect_vec();
clauses.push((
Expr::Const {
val: DataValue::from(true),
span,
},
Expr::Const {
val: DataValue::Null,
span,
},
));
Expr::Cond { clauses, span }
}
"if" => {
@ -264,15 +305,16 @@ fn build_term(pair: Pair<'_>, param_pool: &BTreeMap<String, DataValue>) -> Resul
let cond = args.next().unwrap();
let then = args.next().unwrap();
clauses.push((cond, then));
if let Some(else_clause) = args.next() {
clauses.push((
Expr::Const {
val: DataValue::from(true),
span,
},
else_clause,
))
}
clauses.push((
Expr::Const {
val: DataValue::from(true),
span,
},
args.next().unwrap_or(Expr::Const {
val: DataValue::from(true),
span,
}),
));
Expr::Cond { clauses, span }
}
_ => {

@ -40,6 +40,16 @@ pub(crate) enum CozoScript {
Sys(SysOp),
}
pub(crate) enum ImperativeElement {
JumpIfNot(usize),
Goto(usize),
}
pub(crate) struct ImperativeProgram {
}
impl CozoScript {
pub(crate) fn get_single_program(self) -> Result<InputProgram> {
#[derive(Debug, Error, Diagnostic)]

@ -124,7 +124,7 @@ impl<'a> SessionTx<'a> {
let header = &rule.head;
let mut relation =
self.compile_magic_rule_body(rule, &k, &store_arities, header)?;
relation.fill_binding_indices().with_context(|| {
relation.fill_binding_indices_and_compile().with_context(|| {
format!(
"error encountered when filling binding indices for {relation:#?}"
)

@ -16,7 +16,7 @@ use log::{debug, error};
use miette::{bail, Diagnostic, Result};
use thiserror::Error;
use crate::data::expr::{compute_bounds, Expr};
use crate::data::expr::{compute_bounds, eval_bytecode, eval_bytecode_pred, Expr, ExprByteCode};
use crate::data::program::MagicSymbol;
use crate::data::relation::{ColType, NullableColType};
use crate::data::symb::Symbol;
@ -60,6 +60,7 @@ pub(crate) struct UnificationRA {
pub(crate) parent: Box<RelAlgebra>,
pub(crate) binding: Symbol,
pub(crate) expr: Expr,
pub(crate) expr_bytecode: Vec<ExprByteCode>,
pub(crate) is_multi: bool,
pub(crate) to_eliminate: BTreeSet<Symbol>,
pub(crate) span: SourceSpan,
@ -88,7 +89,7 @@ fn eliminate_from_tuple(mut ret: Tuple, eliminate_indices: &BTreeSet<usize>) ->
}
impl UnificationRA {
fn fill_binding_indices(&mut self) -> Result<()> {
fn fill_binding_indices_and_compile(&mut self) -> Result<()> {
let parent_bindings: BTreeMap<_, _> = self
.parent
.bindings_after_eliminate()
@ -96,7 +97,9 @@ impl UnificationRA {
.enumerate()
.map(|(a, b)| (b, a))
.collect();
self.expr.fill_binding_indices(&parent_bindings)
self.expr.fill_binding_indices(&parent_bindings)?;
self.expr_bytecode = self.expr.compile();
Ok(())
}
pub(crate) fn do_eliminate_temp_vars(&mut self, used: &BTreeSet<Symbol>) -> Result<()> {
for binding in self.parent.bindings_before_eliminate() {
@ -119,12 +122,13 @@ impl UnificationRA {
let mut bindings = self.parent.bindings_after_eliminate();
bindings.push(self.binding.clone());
let eliminate_indices = get_eliminate_indices(&bindings, &self.to_eliminate);
let mut stack = vec![];
Ok(if self.is_multi {
let it = self
.parent
.iter(tx, delta_rule, stores)?
.map_ok(move |tuple| -> Result<Vec<Tuple>> {
let result_list = self.expr.eval(&tuple)?;
let result_list = eval_bytecode(&self.expr_bytecode, &tuple, &mut stack)?;
let result_list = result_list.get_slice().ok_or_else(|| {
#[derive(Debug, Error, Diagnostic)]
#[error("Invalid spread unification")]
@ -152,7 +156,7 @@ impl UnificationRA {
self.parent
.iter(tx, delta_rule, stores)?
.map_ok(move |tuple| -> Result<Tuple> {
let result = self.expr.eval(&tuple)?;
let result = eval_bytecode(&self.expr_bytecode, &tuple, &mut stack)?;
let mut ret = tuple;
ret.push(result);
let ret = ret;
@ -167,7 +171,8 @@ impl UnificationRA {
pub(crate) struct FilteredRA {
pub(crate) parent: Box<RelAlgebra>,
pub(crate) pred: Vec<Expr>,
pub(crate) filters: Vec<Expr>,
pub(crate) filters_bytecodes: Vec<(Vec<ExprByteCode>, SourceSpan)>,
pub(crate) to_eliminate: BTreeSet<Symbol>,
pub(crate) span: SourceSpan,
}
@ -180,14 +185,14 @@ impl FilteredRA {
}
}
let mut nxt = used.clone();
for e in self.pred.iter() {
for e in self.filters.iter() {
nxt.extend(e.bindings());
}
self.parent.eliminate_temp_vars(&nxt)?;
Ok(())
}
fn fill_binding_indices(&mut self) -> Result<()> {
fn fill_binding_indices_and_compile(&mut self) -> Result<()> {
let parent_bindings: BTreeMap<_, _> = self
.parent
.bindings_after_eliminate()
@ -195,8 +200,9 @@ impl FilteredRA {
.enumerate()
.map(|(a, b)| (b, a))
.collect();
for e in self.pred.iter_mut() {
for e in self.filters.iter_mut() {
e.fill_binding_indices(&parent_bindings)?;
self.filters_bytecodes.push((e.compile(), e.span()));
}
Ok(())
}
@ -208,13 +214,14 @@ impl FilteredRA {
) -> Result<TupleIter<'a>> {
let bindings = self.parent.bindings_after_eliminate();
let eliminate_indices = get_eliminate_indices(&bindings, &self.to_eliminate);
let mut stack = vec![];
Ok(Box::new(
self.parent
.iter(tx, delta_rule, stores)?
.filter_map(move |tuple| match tuple {
Ok(t) => {
for p in self.pred.iter() {
match p.eval_pred(&t) {
for (p, span) in self.filters_bytecodes.iter() {
match eval_bytecode_pred(p, &t, &mut stack, *span) {
Ok(false) => return None,
Err(e) => return Some(Err(e)),
Ok(true) => {}
@ -303,7 +310,7 @@ impl Debug for RelAlgebra {
RelAlgebra::Filter(r) => f
.debug_tuple("Filter")
.field(&bindings)
.field(&r.pred)
.field(&r.filters)
.field(&r.parent)
.finish(),
RelAlgebra::Unification(r) => f
@ -326,35 +333,35 @@ impl Debug for RelAlgebra {
pub(crate) struct InvalidTimeTravelScanning(pub(crate) String, #[label] pub(crate) SourceSpan);
impl RelAlgebra {
pub(crate) fn fill_binding_indices(&mut self) -> Result<()> {
pub(crate) fn fill_binding_indices_and_compile(&mut self) -> Result<()> {
match self {
RelAlgebra::Fixed(_) => {}
RelAlgebra::TempStore(d) => {
d.fill_binding_indices()?;
d.fill_binding_indices_and_compile()?;
}
RelAlgebra::Stored(v) => {
v.fill_binding_indices()?;
v.fill_binding_indices_and_compile()?;
}
RelAlgebra::StoredWithValidity(v) => {
v.fill_binding_indices()?;
v.fill_binding_indices_and_compile()?;
}
RelAlgebra::Reorder(r) => {
r.relation.fill_binding_indices()?;
r.relation.fill_binding_indices_and_compile()?;
}
RelAlgebra::Filter(f) => {
f.parent.fill_binding_indices()?;
f.fill_binding_indices()?
f.parent.fill_binding_indices_and_compile()?;
f.fill_binding_indices_and_compile()?
}
RelAlgebra::NegJoin(r) => {
r.left.fill_binding_indices()?;
r.left.fill_binding_indices_and_compile()?;
}
RelAlgebra::Unification(u) => {
u.parent.fill_binding_indices()?;
u.fill_binding_indices()?
u.parent.fill_binding_indices_and_compile()?;
u.fill_binding_indices_and_compile()?
}
RelAlgebra::Join(r) => {
r.left.fill_binding_indices()?;
r.right.fill_binding_indices()?;
r.left.fill_binding_indices_and_compile()?;
r.right.fill_binding_indices_and_compile()?;
}
}
Ok(())
@ -381,6 +388,7 @@ impl RelAlgebra {
bindings,
storage_key,
filters: vec![],
filters_bytecodes: vec![],
span,
})
}
@ -395,6 +403,7 @@ impl RelAlgebra {
bindings,
storage,
filters: vec![],
filters_bytecodes: vec![],
span,
})),
Some(vld) => {
@ -410,6 +419,7 @@ impl RelAlgebra {
bindings,
storage,
filters: vec![],
filters_bytecodes: vec![],
valid_at: vld,
span,
}))
@ -431,21 +441,24 @@ impl RelAlgebra {
let span = filter.span();
RelAlgebra::Filter(FilteredRA {
parent: Box::new(s),
pred: vec![filter],
filters: vec![filter],
filters_bytecodes: vec![],
to_eliminate: Default::default(),
span,
})
}
RelAlgebra::Filter(FilteredRA {
parent,
mut pred,
filters: mut pred,
filters_bytecodes,
to_eliminate,
span,
}) => {
pred.push(filter);
RelAlgebra::Filter(FilteredRA {
parent,
pred,
filters: pred,
filters_bytecodes,
to_eliminate,
span,
})
@ -454,6 +467,7 @@ impl RelAlgebra {
bindings,
storage_key,
mut filters,
filters_bytecodes: filters_asm,
span,
}) => {
filters.push(filter);
@ -461,6 +475,7 @@ impl RelAlgebra {
bindings,
storage_key,
filters,
filters_bytecodes: filters_asm,
span,
})
}
@ -468,6 +483,7 @@ impl RelAlgebra {
bindings,
storage,
mut filters,
filters_bytecodes,
span,
}) => {
filters.push(filter);
@ -475,6 +491,7 @@ impl RelAlgebra {
bindings,
storage,
filters,
filters_bytecodes,
span,
})
}
@ -482,6 +499,7 @@ impl RelAlgebra {
bindings,
storage,
mut filters,
filters_bytecodes: filter_bytecodes,
span,
valid_at,
}) => {
@ -492,6 +510,7 @@ impl RelAlgebra {
filters,
span,
valid_at,
filters_bytecodes: filter_bytecodes,
})
}
RelAlgebra::Join(inner) => {
@ -532,7 +551,8 @@ impl RelAlgebra {
if !remaining.is_empty() {
joined = RelAlgebra::Filter(FilteredRA {
parent: Box::new(joined),
pred: remaining,
filters: remaining,
filters_bytecodes: vec![],
to_eliminate: Default::default(),
span,
});
@ -552,6 +572,7 @@ impl RelAlgebra {
parent: Box::new(self),
binding,
expr,
expr_bytecode: vec![],
is_multi,
to_eliminate: Default::default(),
span,
@ -757,12 +778,13 @@ fn invert_option_err<T>(v: Result<Option<T>>) -> Option<Result<T>> {
}
fn filter_iter(
filters: Vec<Expr>,
filters_bytecodes: Vec<(Vec<ExprByteCode>, SourceSpan)>,
it: impl Iterator<Item = Result<Tuple>>,
) -> impl Iterator<Item = Result<Tuple>> {
let mut stack = vec![];
it.filter_map_ok(move |t| -> Option<Result<Tuple>> {
for p in filters.iter() {
match p.eval_pred(&t) {
for (p, span) in filters_bytecodes.iter() {
match eval_bytecode_pred(p, &t, &mut stack, *span) {
Ok(false) => return None,
Err(e) => {
debug!("{:?}", t);
@ -795,6 +817,7 @@ pub(crate) struct StoredRA {
pub(crate) bindings: Vec<Symbol>,
pub(crate) storage: RelationHandle,
pub(crate) filters: Vec<Expr>,
pub(crate) filters_bytecodes: Vec<(Vec<ExprByteCode>, SourceSpan)>,
pub(crate) span: SourceSpan,
}
@ -803,12 +826,13 @@ pub(crate) struct StoredWithValidityRA {
pub(crate) bindings: Vec<Symbol>,
pub(crate) storage: RelationHandle,
pub(crate) filters: Vec<Expr>,
pub(crate) filters_bytecodes: Vec<(Vec<ExprByteCode>, SourceSpan)>,
pub(crate) valid_at: ValidityTs,
pub(crate) span: SourceSpan,
}
impl StoredWithValidityRA {
fn fill_binding_indices(&mut self) -> Result<()> {
fn fill_binding_indices_and_compile(&mut self) -> Result<()> {
let bindings: BTreeMap<_, _> = self
.bindings
.iter()
@ -818,6 +842,7 @@ impl StoredWithValidityRA {
.collect();
for e in self.filters.iter_mut() {
e.fill_binding_indices(&bindings)?;
self.filters_bytecodes.push((e.compile(), e.span()));
}
Ok(())
}
@ -826,7 +851,7 @@ impl StoredWithValidityRA {
Ok(if self.filters.is_empty() {
Box::new(it)
} else {
Box::new(filter_iter(self.filters.clone(), it))
Box::new(filter_iter(self.filters_bytecodes.clone(), it))
})
}
fn prefix_join<'a>(
@ -861,6 +886,7 @@ impl StoredWithValidityRA {
if !l_bound.iter().all(|v| *v == DataValue::Null)
|| !u_bound.iter().all(|v| *v == DataValue::Bot)
{
let mut stack = vec![];
return Left(
self.storage
.skip_scan_bounded_prefix(
@ -872,8 +898,8 @@ impl StoredWithValidityRA {
)
.map(move |res_found| -> Result<Option<Tuple>> {
let found = res_found?;
for p in self.filters.iter() {
if !p.eval_pred(&found)? {
for (p, span) in self.filters_bytecodes.iter() {
if !eval_bytecode_pred(p, &found, &mut stack, *span)? {
return Ok(None);
}
}
@ -886,13 +912,14 @@ impl StoredWithValidityRA {
}
}
skip_range_check = true;
let mut stack = vec![];
Right(
self.storage
.skip_scan_prefix(tx, &prefix, self.valid_at)
.map(move |res_found| -> Result<Option<Tuple>> {
let found = res_found?;
for p in self.filters.iter() {
if !p.eval_pred(&found)? {
for (p, span) in self.filters_bytecodes.iter() {
if !eval_bytecode_pred(p, &found, &mut stack, *span)? {
return Ok(None);
}
}
@ -914,7 +941,7 @@ impl StoredWithValidityRA {
}
impl StoredRA {
fn fill_binding_indices(&mut self) -> Result<()> {
fn fill_binding_indices_and_compile(&mut self) -> Result<()> {
let bindings: BTreeMap<_, _> = self
.bindings
.iter()
@ -924,6 +951,7 @@ impl StoredRA {
.collect();
for e in self.filters.iter_mut() {
e.fill_binding_indices(&bindings)?;
self.filters_bytecodes.push((e.compile(), e.span()));
}
Ok(())
}
@ -940,6 +968,7 @@ impl StoredRA {
let val_len = self.storage.metadata.non_keys.len();
let all_right_val_indices: BTreeSet<usize> =
(0..val_len).map(|i| left_tuple_len + key_len + i).collect();
let mut stack = vec![];
if self.filters.is_empty() && eliminate_indices.is_superset(&all_right_val_indices) {
let it = left_iter
.map_ok(move |tuple| -> Result<Option<Tuple>> {
@ -948,8 +977,8 @@ impl StoredRA {
.map(|i| tuple[*i].clone())
.collect_vec();
let key = &prefix[0..key_len];
for p in self.filters.iter() {
if !p.eval_pred(key)? {
for (p, span) in self.filters_bytecodes.iter() {
if !eval_bytecode_pred(p, key, &mut stack, *span)? {
return Ok(None);
}
}
@ -982,8 +1011,8 @@ impl StoredRA {
match self.storage.get(tx, key)? {
None => Ok(None),
Some(found) => {
for p in self.filters.iter() {
if !p.eval_pred(&found)? {
for (p, span) in self.filters_bytecodes.iter() {
if !eval_bytecode_pred(p, &found, &mut stack, *span)? {
return Ok(None);
}
}
@ -1038,6 +1067,7 @@ impl StoredRA {
.iter()
.map(|i| tuple[*i].clone())
.collect_vec();
let mut stack = vec![];
if !skip_range_check && !self.filters.is_empty() {
let other_bindings = &self.bindings[right_join_indices.len()..];
@ -1053,8 +1083,8 @@ impl StoredRA {
.scan_bounded_prefix(tx, &prefix, &l_bound, &u_bound)
.map(move |res_found| -> Result<Option<Tuple>> {
let found = res_found?;
for p in self.filters.iter() {
if !p.eval_pred(&found)? {
for (p, span) in self.filters_bytecodes.iter() {
if !eval_bytecode_pred(p, &found, &mut stack, *span)? {
return Ok(None);
}
}
@ -1072,8 +1102,8 @@ impl StoredRA {
.scan_prefix(tx, &prefix)
.map(move |res_found| -> Result<Option<Tuple>> {
let found = res_found?;
for p in self.filters.iter() {
if !p.eval_pred(&found)? {
for (p, span) in self.filters_bytecodes.iter() {
if !eval_bytecode_pred(p, &found, &mut stack, *span)? {
return Ok(None);
}
}
@ -1200,7 +1230,7 @@ impl StoredRA {
Ok(if self.filters.is_empty() {
Box::new(it)
} else {
Box::new(filter_iter(self.filters.clone(), it))
Box::new(filter_iter(self.filters_bytecodes.clone(), it))
})
}
}
@ -1217,11 +1247,12 @@ pub(crate) struct TempStoreRA {
pub(crate) bindings: Vec<Symbol>,
pub(crate) storage_key: MagicSymbol,
pub(crate) filters: Vec<Expr>,
pub(crate) filters_bytecodes: Vec<(Vec<ExprByteCode>, SourceSpan)>,
pub(crate) span: SourceSpan,
}
impl TempStoreRA {
fn fill_binding_indices(&mut self) -> Result<()> {
fn fill_binding_indices_and_compile(&mut self) -> Result<()> {
let bindings: BTreeMap<_, _> = self
.bindings
.iter()
@ -1231,6 +1262,7 @@ impl TempStoreRA {
.collect();
for e in self.filters.iter_mut() {
e.fill_binding_indices(&bindings)?;
self.filters_bytecodes.push((e.compile(), e.span()))
}
Ok(())
}
@ -1254,7 +1286,7 @@ impl TempStoreRA {
Ok(if self.filters.is_empty() {
Box::new(it)
} else {
Box::new(filter_iter(self.filters.clone(), it))
Box::new(filter_iter(self.filters_bytecodes.clone(), it))
})
}
fn neg_join<'a>(
@ -1382,6 +1414,7 @@ impl TempStoreRA {
.iter()
.map(|i| tuple[*i].clone())
.collect_vec();
let mut stack = vec![];
if !skip_range_check && !self.filters.is_empty() {
let other_bindings = &self.bindings[right_join_indices.len()..];
@ -1409,8 +1442,8 @@ impl TempStoreRA {
Ok(Some(ret))
} else {
let found = res_found.into_tuple();
for p in self.filters.iter() {
if !p.eval_pred(&found)? {
for (p, span) in self.filters_bytecodes.iter() {
if !eval_bytecode_pred(p, &found, &mut stack, *span)? {
return Ok(None);
}
}
@ -1439,8 +1472,8 @@ impl TempStoreRA {
Ok(Some(ret))
} else {
let found = res_found.into_tuple();
for p in self.filters.iter() {
if !p.eval_pred(&found)? {
for (p, span) in self.filters_bytecodes.iter() {
if !eval_bytecode_pred(p, &found, &mut stack, *span)? {
return Ok(None);
}
}
@ -2039,7 +2072,6 @@ impl<'a> Iterator for CachedMaterializedIterator<'a> {
#[cfg(test)]
mod tests {
use crate::data::value::DataValue;
use crate::new_cozo_mem;
#[test]
@ -2055,6 +2087,9 @@ mod tests {
)
.unwrap()
.rows;
assert_eq!(res, vec![vec![DataValue::from(1)], vec![DataValue::from(2)]])
assert_eq!(
res,
vec![vec![DataValue::from(1)], vec![DataValue::from(2)]]
)
}
}

@ -676,7 +676,7 @@ impl<'s, S: Storage<'s>> Db<S> {
rel_stack.push(relation);
("reorder", json!(null), json!(null), json!(null))
}
RelAlgebra::Filter(FilteredRA { parent, pred, .. }) => {
RelAlgebra::Filter(FilteredRA { parent, filters: pred, .. }) => {
rel_stack.push(parent);
(
"filter",

Loading…
Cancel
Save