optimized eval implementation

main
Ziyang Hu 2 years ago
parent d393d4cf5f
commit 4b0cc31bec

@ -5,6 +5,7 @@ use crate::data::value::{StaticValue, Value};
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::result;
use crate::data::op::{Op, OpAdd, OpAnd, OpCoalesce, OpDiv, OpEq, OpGe, OpGt, OpIsNull, OpLe, OpLt, OpMinus, OpMod, OpMul, OpNe, OpNegate, OpNotNull, OpOr, OpPow, OpStrCat, OpSub};
#[derive(thiserror::Error, Debug)]
pub(crate) enum EvalError {
@ -45,6 +46,12 @@ impl RowEvalContext for () {
pub(crate) trait ExprEvalContext {}
impl<'a> Expr<'a> {
pub(crate) fn partial_eval<C: ExprEvalContext + 'a>(&'a self, ctx: &'a C) -> Result<Self> {
todo!()
}
pub(crate) fn optimize_ops(self) -> Self {
todo!()
}
pub(crate) fn row_eval<C: RowEvalContext + 'a>(&'a self, ctx: &'a C) -> Result<Value<'a>> {
let res: Value = match self {
Expr::Const(v) => v.clone(),
@ -102,6 +109,151 @@ impl<'a> Expr<'a> {
}
v => return Err(EvalError::IndexAccess(*idx, v.to_static())),
},
// optimized implementations, not really necessary
Expr::Add(args) => {
OpAdd.eval_two_non_null(match args.as_ref().0.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
}, match args.as_ref().1.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
})?
}
Expr::Sub(args) => {
OpSub.eval_two_non_null(match args.as_ref().0.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
}, match args.as_ref().1.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
})?
}
Expr::Mul(args) => {
OpMul.eval_two_non_null(match args.as_ref().0.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
}, match args.as_ref().1.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
})?
}
Expr::Div(args) => {
OpDiv.eval_two_non_null(match args.as_ref().0.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
}, match args.as_ref().1.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
})?
}
Expr::Pow(args) => {
OpPow.eval_two_non_null(match args.as_ref().0.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
}, match args.as_ref().1.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
})?
}
Expr::Mod(args) => {
OpMod.eval_two_non_null(match args.as_ref().0.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
}, match args.as_ref().1.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
})?
}
Expr::StrCat(args) => {
OpStrCat.eval_two_non_null(match args.as_ref().0.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
}, match args.as_ref().1.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
})?
}
Expr::Eq(args) => {
OpEq.eval_two_non_null(match args.as_ref().0.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
}, match args.as_ref().1.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
})?
}
Expr::Ne(args) => {
OpNe.eval_two_non_null(match args.as_ref().0.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
}, match args.as_ref().1.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
})?
}
Expr::Gt(args) => {
OpGt.eval_two_non_null(match args.as_ref().0.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
}, match args.as_ref().1.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
})?
}
Expr::Ge(args) => {
OpGe.eval_two_non_null(match args.as_ref().0.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
}, match args.as_ref().1.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
})?
}
Expr::Lt(args) => {
OpLt.eval_two_non_null(match args.as_ref().0.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
}, match args.as_ref().1.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
})?
}
Expr::Le(args) => {
OpLe.eval_two_non_null(match args.as_ref().0.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
}, match args.as_ref().1.row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
})?
}
Expr::Negate(arg) => {
OpNegate.eval_one_non_null(match arg.as_ref().row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
})?
}
Expr::Minus(arg) => {
OpMinus.eval_one_non_null(match arg.as_ref().row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v
})?
}
Expr::IsNull(arg) => {
OpIsNull.eval_one(arg.as_ref().row_eval(ctx)?)?
}
Expr::NotNull(arg) => {
OpNotNull.eval_one(arg.as_ref().row_eval(ctx)?)?
}
Expr::Coalesce(args) => {
OpCoalesce.eval_two(args.as_ref().0.row_eval(ctx)? , args.as_ref().1.row_eval(ctx)?)?
}
Expr::Or(args) => {
OpOr.eval_two(args.as_ref().0.row_eval(ctx)? , args.as_ref().1.row_eval(ctx)?)?
}
Expr::And(args) => {
OpAnd.eval_two(args.as_ref().0.row_eval(ctx)? , args.as_ref().1.row_eval(ctx)?)?
}
};
Ok(res)
}

@ -1,4 +1,4 @@
use crate::data::op::{AggOp, Op, UnresolvedOp};
use crate::data::op::{AggOp, Op, OpAdd, OpAnd, OpCoalesce, OpDiv, OpEq, OpGe, OpGt, OpIsNull, OpLe, OpLt, OpMinus, OpMod, OpMul, OpNe, OpNegate, OpNotNull, OpOr, OpPow, OpStrCat, OpSub, UnresolvedOp};
use crate::data::tuple_set::{ColId, TableId, TupleSetIdx};
use crate::data::value::{StaticValue, Value};
use std::collections::BTreeMap;
@ -32,6 +32,27 @@ pub(crate) enum Expr<'a> {
ApplyAgg(Arc<dyn AggOp + Send + Sync>, Vec<Expr<'a>>, Vec<Expr<'a>>),
FieldAcc(String, Box<Expr<'a>>),
IdxAcc(usize, Box<Expr<'a>>),
// optimized
Add(Box<(Expr<'a>, Expr<'a>)>),
Sub(Box<(Expr<'a>, Expr<'a>)>),
Mul(Box<(Expr<'a>, Expr<'a>)>),
Div(Box<(Expr<'a>, Expr<'a>)>),
Pow(Box<(Expr<'a>, Expr<'a>)>),
Mod(Box<(Expr<'a>, Expr<'a>)>),
StrCat(Box<(Expr<'a>, Expr<'a>)>),
Eq(Box<(Expr<'a>, Expr<'a>)>),
Ne(Box<(Expr<'a>, Expr<'a>)>),
Gt(Box<(Expr<'a>, Expr<'a>)>),
Ge(Box<(Expr<'a>, Expr<'a>)>),
Lt(Box<(Expr<'a>, Expr<'a>)>),
Le(Box<(Expr<'a>, Expr<'a>)>),
Negate(Box<Expr<'a>>),
Minus(Box<Expr<'a>>),
IsNull(Box<Expr<'a>>),
NotNull(Box<Expr<'a>>),
Coalesce(Box<(Expr<'a>, Expr<'a>)>),
Or(Box<(Expr<'a>, Expr<'a>)>),
And(Box<(Expr<'a>, Expr<'a>)>),
}
impl<'a> PartialEq for Expr<'a> {
@ -74,6 +95,26 @@ impl<'a> Debug for Expr<'a> {
.collect::<Vec<_>>()
.join(" ")
),
Expr::Add(args) => write!(f, "(`+ {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Sub(args) => write!(f, "(`- {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Mul(args) => write!(f, "(`* {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Div(args) => write!(f, "(`/ {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Pow(args) => write!(f, "(`** {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Mod(args) => write!(f, "(`% {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::StrCat(args) => write!(f, "(`++ {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Eq(args) => write!(f, "(`== {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Ne(args) => write!(f, "(`!= {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Gt(args) => write!(f, "(`> {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Ge(args) => write!(f, "(`>= {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Lt(args) => write!(f, "(`< {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Le(args) => write!(f, "(`<= {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Negate(arg) => write!(f, "(`! {:?})", arg.as_ref()),
Expr::Minus(arg) => write!(f, "(`-- {:?})", arg.as_ref()),
Expr::IsNull(arg) => write!(f, "(`is_null {:?})", arg.as_ref()),
Expr::NotNull(arg) => write!(f, "(`not_null {:?})", arg.as_ref()),
Expr::Coalesce(args) => write!(f, "(`~ {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Or(args) => write!(f, "(`|| {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::And(args) => write!(f, "(`&& {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::ApplyAgg(op, a_args, args) => write!(
f,
"[|{} {} | {}|]",
@ -137,7 +178,7 @@ impl<'a> TryFrom<Value<'a>> for Expr<'a> {
v => {
return Err(ExprError::ConversionFailure(
Value::Dict(BTreeMap::from([(k, v)])).to_static(),
))
));
}
},
"Variable" => {
@ -251,6 +292,26 @@ impl<'a> TryFrom<Value<'a>> for Expr<'a> {
}
}
fn build_value_from_binop<'a>(name: &str, (left, right): (Expr<'a>, Expr<'a>)) -> Value<'a> {
build_tagged_value(
"Apply",
vec![
Value::from(name.to_string()),
Value::from(vec![Value::from(left), Value::from(right)]),
].into(),
)
}
fn build_value_from_uop<'a>(name: &str, arg: Expr<'a>) -> Value<'a> {
build_tagged_value(
"Apply",
vec![
Value::from(name.to_string()),
Value::from(vec![Value::from(arg)]),
].into(),
)
}
impl<'a> From<Expr<'a>> for Value<'a> {
fn from(expr: Expr<'a>) -> Self {
match expr {
@ -275,7 +336,7 @@ impl<'a> From<Expr<'a>> for Value<'a> {
cid.is_key.into(),
Value::from(cid.id as i64),
]
.into(),
.into(),
),
Expr::TupleSetIdx(sid) => build_tagged_value(
"TupleSetIdx",
@ -284,15 +345,35 @@ impl<'a> From<Expr<'a>> for Value<'a> {
Value::from(sid.t_set as i64),
Value::from(sid.col_idx as i64),
]
.into(),
.into(),
),
Expr::Add(arg) => build_value_from_binop(OpAdd.name(), *arg),
Expr::Sub(arg) => build_value_from_binop(OpSub.name(), *arg),
Expr::Mul(arg) => build_value_from_binop(OpMul.name(), *arg),
Expr::Div(arg) => build_value_from_binop(OpDiv.name(), *arg),
Expr::Pow(arg) => build_value_from_binop(OpPow.name(), *arg),
Expr::Mod(arg) => build_value_from_binop(OpMod.name(), *arg),
Expr::StrCat(arg) => build_value_from_binop(OpStrCat.name(), *arg),
Expr::Eq(arg) => build_value_from_binop(OpEq.name(), *arg),
Expr::Ne(arg) => build_value_from_binop(OpNe.name(), *arg),
Expr::Gt(arg) => build_value_from_binop(OpGt.name(), *arg),
Expr::Ge(arg) => build_value_from_binop(OpGe.name(), *arg),
Expr::Lt(arg) => build_value_from_binop(OpLt.name(), *arg),
Expr::Le(arg) => build_value_from_binop(OpLe.name(), *arg),
Expr::Negate(arg) => build_value_from_uop(OpNegate.name(), *arg),
Expr::Minus(arg) => build_value_from_uop(OpMinus.name(), *arg),
Expr::IsNull(arg) => build_value_from_uop(OpIsNull.name(), *arg),
Expr::NotNull(arg) => build_value_from_uop(OpNotNull.name(), *arg),
Expr::Coalesce(arg) => build_value_from_binop(OpCoalesce.name(), *arg),
Expr::Or(arg) => build_value_from_binop(OpOr.name(), *arg),
Expr::And(arg) => build_value_from_binop(OpAnd.name(), *arg),
Expr::Apply(op, args) => build_tagged_value(
"Apply",
vec![
Value::from(op.name().to_string()),
args.into_iter().map(Value::from).collect::<Vec<_>>().into(),
]
.into(),
.into(),
),
Expr::ApplyAgg(op, a_args, args) => build_tagged_value(
"ApplyAgg",
@ -305,7 +386,7 @@ impl<'a> From<Expr<'a>> for Value<'a> {
.into(),
args.into_iter().map(Value::from).collect::<Vec<_>>().into(),
]
.into(),
.into(),
),
Expr::FieldAcc(f, v) => {
build_tagged_value("FieldAcc", vec![f.into(), Value::from(*v)].into())

@ -73,6 +73,18 @@ pub(crate) trait Op: Send + Sync {
self.name()
)
}
fn eval_one<'a>(&self, _arg: Value<'a>) -> Result<Value<'a>> {
panic!(
"Required method `eval_one` not implemented for `{}`",
self.name()
)
}
fn eval_two<'a>(&self, _left: Value<'a>, _right: Value<'a>) -> Result<Value<'a>> {
panic!(
"Required method `eval_two` not implemented for `{}`",
self.name()
)
}
fn expr_eval(&self, ctx: &dyn ExprEvalContext, args: ()) -> () {}
}
@ -470,6 +482,9 @@ impl Op for OpIsNull {
fn eval<'a>(&self, has_null: bool, _args: Vec<Value<'a>>) -> Result<Value<'a>> {
Ok(has_null.into())
}
fn eval_one<'a>(&self, arg: Value<'a>) -> Result<Value<'a>> {
Ok((arg == Value::Null).into())
}
}
pub(crate) struct OpNotNull;
@ -484,6 +499,9 @@ impl Op for OpNotNull {
fn eval<'a>(&self, has_null: bool, _args: Vec<Value<'a>>) -> Result<Value<'a>> {
Ok((!has_null).into())
}
fn eval_one<'a>(&self, arg: Value<'a>) -> Result<Value<'a>> {
Ok((arg != Value::Null).into())
}
}
pub(crate) struct OpCoalesce;
@ -506,6 +524,12 @@ impl Op for OpCoalesce {
}
Ok(Value::Null)
}
fn eval_two<'a>(&self, left: Value<'a>, right: Value<'a>) -> Result<Value<'a>> {
match (left, right) {
(Value::Null, v) => Ok(v),
(l, _r) => Ok(l)
}
}
}
pub(crate) struct OpOr;
@ -538,6 +562,19 @@ impl Op for OpOr {
Ok(Value::Bool(false))
}
}
fn eval_two<'a>(&self, left: Value<'a>, right: Value<'a>) -> Result<Value<'a>> {
match (left, right) {
(Value::Null, Value::Bool(true)) => Ok(true.into()),
(Value::Null, Value::Bool(false)) => Ok(Value::Null),
(Value::Bool(true), Value::Null) => Ok(true.into()),
(Value::Bool(false), Value::Null) => Ok(Value::Null),
(Value::Bool(l), Value::Bool(r)) => Ok((l || r).into()),
(l, r) => Err(EvalError::OpTypeMismatch(
self.name().to_string(),
vec![l.to_static(), r.to_static()],
))
}
}
}
pub(crate) struct OpAnd;
@ -570,6 +607,19 @@ impl Op for OpAnd {
Ok(Value::Bool(true))
}
}
fn eval_two<'a>(&self, left: Value<'a>, right: Value<'a>) -> Result<Value<'a>> {
match (left, right) {
(Value::Null, Value::Bool(false)) => Ok(false.into()),
(Value::Null, Value::Bool(true)) => Ok(Value::Null),
(Value::Bool(false), Value::Null) => Ok(false.into()),
(Value::Bool(true), Value::Null) => Ok(Value::Null),
(Value::Bool(l), Value::Bool(r)) => Ok((l && r).into()),
(l, r) => Err(EvalError::OpTypeMismatch(
self.name().to_string(),
vec![l.to_static(), r.to_static()],
))
}
}
}
pub(crate) struct OpConcat;

Loading…
Cancel
Save