Skip to content

Commit

Permalink
Introduce Datum, an interface to introspecting Values (#533)
Browse files Browse the repository at this point in the history
* Introduce `Datum`, an interface to introspecting `Value`s

* Use `Datum` and `DatumCategory` in evaluator
  • Loading branch information
jpschorr authored Jan 14, 2025
1 parent 76764b7 commit e21bd72
Show file tree
Hide file tree
Showing 11 changed files with 374 additions and 103 deletions.
3 changes: 2 additions & 1 deletion extension/partiql-extension-ion-functions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ mod tests {
use partiql_eval::eval::BasicContext;
use partiql_eval::plan::EvaluationMode;
use partiql_parser::{Parsed, ParserResult};
use partiql_value::datum::Datum;
use partiql_value::{bag, tuple, DateTime, Value};

#[track_caller]
Expand Down Expand Up @@ -229,7 +230,7 @@ mod tests {
.unwrap_or_default();
let out = evaluate(&catalog, lowered, bindings);

assert!(out.is_bag());
assert!(out.is_sequence());
assert_eq!(&out, expected);
}

Expand Down
7 changes: 3 additions & 4 deletions partiql-eval/src/eval/evaluable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use std::collections::hash_map::Entry;
use std::collections::HashSet;
use std::fmt::{Debug, Formatter};

use partiql_value::datum::Datum;
use std::rc::Rc;

#[macro_export]
Expand Down Expand Up @@ -110,17 +111,16 @@ impl Evaluable for EvalScan {
Value::Tuple(t) => bag![*t],
_ => bag![tuple![]],
};

let mut value = bag![];
bindings.iter().for_each(|binding| {
let binding_tuple = binding.as_tuple_ref();
let v = self.expr.evaluate(&binding_tuple, ctx).into_owned();
let ordered = &v.is_ordered();
let mut at_index_counter: i64 = 0;
if let Some(at_key) = &self.at_key {
let ordered = v.is_ordered();
for t in v {
let mut out = Tuple::from([(self.as_key.as_str(), t)]);
let at_id = if *ordered {
let at_id = if ordered {
at_index_counter.into()
} else {
Missing
Expand Down Expand Up @@ -1233,7 +1233,6 @@ impl EvalExprQuery {
impl Evaluable for EvalExprQuery {
fn evaluate<'a, 'c>(&mut self, ctx: &'c dyn EvalContext<'c>) -> Value {
let input_value = self.input.take().unwrap_or(Value::Null).coerce_into_tuple();

self.expr.evaluate(&input_value, ctx).into_owned()
}

Expand Down
1 change: 1 addition & 0 deletions partiql-eval/src/eval/expr/coll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use std::fmt::Debug;
use std::hash::Hash;

use crate::eval::eval_expr_wrapper::UnaryValueExpr;
use partiql_value::datum::Datum;
use std::ops::ControlFlow;

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
Expand Down
6 changes: 5 additions & 1 deletion partiql-eval/src/eval/expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub(crate) use operators::*;

use crate::eval::EvalContext;

use partiql_value::datum::DatumLowerError;
use partiql_value::{Tuple, Value};
use std::borrow::Cow;
use std::fmt::Debug;
Expand All @@ -37,7 +38,7 @@ pub trait EvalExpr: Debug {
'c: 'a;
}

#[derive(Error, Debug, Clone, PartialEq)]
#[derive(Error, Debug)]
#[non_exhaustive]
/// An error in binding an expression for evaluation
pub enum BindError {
Expand All @@ -52,6 +53,9 @@ pub enum BindError {
#[error("Not yet implemented: {0}")]
NotYetImplemented(String),

#[error("Error lowering literal value: {0}")]
LiteralValue(#[from] DatumLowerError),

/// Any other error.
#[error("Bind error: unknown error")]
Unknown,
Expand Down
35 changes: 23 additions & 12 deletions partiql-eval/src/eval/expr/operators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,30 @@ use std::fmt::{Debug, Formatter};

use std::marker::PhantomData;

use partiql_value::datum::{
DatumCategory, DatumCategoryRef, DatumLowerResult, DatumValue, SequenceDatum, TupleDatum,
};
use std::ops::ControlFlow;

/// Represents a literal in (sub)query, e.g. `1` in `a + 1`.
#[derive(Clone)]
pub(crate) struct EvalLitExpr {
pub(crate) lit: Value,
pub(crate) val: Value,
}

impl EvalLitExpr {
pub(crate) fn new(val: Value) -> Self {
Self { val }
}

fn lower(&self) -> DatumLowerResult<EvalLitExpr> {
self.val.clone().into_lower().map(Self::new)
}
}

impl Debug for EvalLitExpr {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.lit.fmt(f)
self.val.fmt(f)
}
}

Expand All @@ -38,7 +51,7 @@ impl BindEvalExpr for EvalLitExpr {
self,
_args: Vec<Box<dyn EvalExpr>>,
) -> Result<Box<dyn EvalExpr>, BindError> {
Ok(Box::new(self.clone()))
Ok(Box::new(self.lower()?))
}
}

Expand All @@ -51,7 +64,7 @@ impl EvalExpr for EvalLitExpr {
where
'c: 'a,
{
Cow::Borrowed(&self.lit)
Cow::Borrowed(&self.val)
}
}

Expand Down Expand Up @@ -379,10 +392,9 @@ impl BindEvalExpr for EvalFnExists {
args: Vec<Box<dyn EvalExpr>>,
) -> Result<Box<dyn EvalExpr>, BindError> {
UnaryValueExpr::create_with_any::<{ STRICT }, _>(args, |v| {
Value::from(match v {
Value::Bag(b) => !b.is_empty(),
Value::List(l) => !l.is_empty(),
Value::Tuple(t) => !t.is_empty(),
Value::from(match v.category() {
DatumCategoryRef::Tuple(tuple) => !tuple.is_empty(),
DatumCategoryRef::Sequence(seq) => !seq.is_empty(),
_ => false,
})
})
Expand Down Expand Up @@ -430,10 +442,9 @@ impl BindEvalExpr for EvalFnCardinality {
]
.into_any_of(&mut bld);

UnaryValueExpr::create_typed::<{ STRICT }, _>([collections], args, |v| match v {
Value::List(l) => Value::from(l.len()),
Value::Bag(b) => Value::from(b.len()),
Value::Tuple(t) => Value::from(t.len()),
UnaryValueExpr::create_typed::<{ STRICT }, _>([collections], args, |v| match v.category() {
DatumCategoryRef::Tuple(tuple) => Value::from(tuple.len()),
DatumCategoryRef::Sequence(seq) => Value::from(seq.len()),
_ => Missing,
})
}
Expand Down
59 changes: 33 additions & 26 deletions partiql-eval/src/eval/expr/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ use partiql_value::Value::Missing;
use partiql_value::{BindingsName, Tuple, Value};

use partiql_catalog::context::Bindings;
use partiql_value::datum::{
DatumCategory, DatumCategoryOwned, DatumCategoryRef, OwnedSequenceView, OwnedTupleView,
RefSequenceView, RefTupleView,
};
use std::borrow::Cow;
use std::fmt::{Debug, Formatter};

Expand Down Expand Up @@ -84,38 +88,43 @@ impl EvalPathComponent {
value: &'a Value,
bindings: &'a Tuple,
ctx: &'c dyn EvalContext<'c>,
) -> Option<&'a Value> {
match (self, value) {
(EvalPathComponent::Key(k), Value::Tuple(tuple)) => tuple.get(k),
(EvalPathComponent::Index(idx), Value::List(list)) => list.get(*idx),
(EvalPathComponent::KeyExpr(ke), Value::Tuple(tuple)) => {
as_name(ke.evaluate(bindings, ctx).borrow()).and_then(|key| tuple.get(&key))
) -> Option<Cow<'a, Value>> {
let category = value.category();
match (self, category) {
(EvalPathComponent::Key(k), DatumCategoryRef::Tuple(tuple)) => tuple.get_val(k),
(EvalPathComponent::Index(idx), DatumCategoryRef::Sequence(seq)) => seq.get_val(*idx),
(EvalPathComponent::KeyExpr(ke), DatumCategoryRef::Tuple(tuple)) => {
as_name(ke.evaluate(bindings, ctx).borrow()).and_then(|key| tuple.get_val(&key))
}
(EvalPathComponent::IndexExpr(ie), Value::List(list)) => {
as_int(ie.evaluate(bindings, ctx).borrow()).and_then(|i| list.get(i))
(EvalPathComponent::IndexExpr(ie), DatumCategoryRef::Sequence(seq)) => {
as_int(ie.evaluate(bindings, ctx).borrow()).and_then(|i| seq.get_val(i))
}
_ => None,
}
}

#[inline]
fn take_val<'c>(
fn take_val<'a, 'c>(
&self,
value: Value,
bindings: &Tuple,
ctx: &'c dyn EvalContext<'c>,
) -> Option<Value> {
match (self, value) {
(EvalPathComponent::Key(k), Value::Tuple(tuple)) => tuple.take_val(k),
(EvalPathComponent::Index(idx), Value::List(list)) => list.take_val(*idx),
(EvalPathComponent::KeyExpr(ke), Value::Tuple(tuple)) => {
) -> Option<Cow<'a, Value>> {
let category = value.into_category();
match (self, category) {
(EvalPathComponent::Key(k), DatumCategoryOwned::Tuple(tuple)) => tuple.take_val(k),
(EvalPathComponent::Index(idx), DatumCategoryOwned::Sequence(seq)) => {
seq.take_val(*idx)
}
(EvalPathComponent::KeyExpr(ke), DatumCategoryOwned::Tuple(tuple)) => {
as_name(ke.evaluate(bindings, ctx).borrow()).and_then(|key| tuple.take_val(&key))
}
(EvalPathComponent::IndexExpr(ie), Value::List(list)) => {
as_int(ie.evaluate(bindings, ctx).borrow()).and_then(|i| list.take_val(i))
(EvalPathComponent::IndexExpr(ie), DatumCategoryOwned::Sequence(seq)) => {
as_int(ie.evaluate(bindings, ctx).borrow()).and_then(|i| seq.take_val(i))
}
_ => None,
}
.map(Cow::Owned)
}
}

Expand All @@ -128,17 +137,15 @@ impl EvalExpr for EvalPath {
where
'c: 'a,
{
let value = self.expr.evaluate(bindings, ctx);
let evaluated = self.expr.evaluate(bindings, ctx);
let mut path_componenents = self.components.iter();
match value {
Cow::Borrowed(borrowed) => path_componenents
.try_fold(borrowed, |v, path| path.get_val(v, bindings, ctx))
.map(Cow::Borrowed),
Cow::Owned(owned) => path_componenents
.try_fold(owned, |v, path| path.take_val(v, bindings, ctx))
.map(Cow::Owned),
}
.unwrap_or(Cow::Owned(Value::Missing))

path_componenents
.try_fold(evaluated, |value, path| match value {
Cow::Borrowed(borrowed) => path.get_val(borrowed, bindings, ctx),
Cow::Owned(owned) => path.take_val(owned, bindings, ctx),
})
.unwrap_or(Cow::Owned(Value::Missing))
}
}

Expand Down
13 changes: 6 additions & 7 deletions partiql-eval/src/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ use crate::eval::expr::{
use crate::eval::EvalPlan;
use partiql_catalog::catalog::{Catalog, FunctionEntryFunction};
use partiql_value::Value;
use partiql_value::Value::Null;

#[macro_export]
macro_rules! correct_num_args_or_err {
Expand Down Expand Up @@ -361,6 +360,9 @@ impl<'c> EvaluatorPlanner<'c> {
}
BindError::NotYetImplemented(name) => PlanningError::NotYetImplemented(name),
BindError::ArgumentConstraint(msg) => PlanningError::IllegalState(msg),
BindError::LiteralValue(err) => {
PlanningError::IllegalState(format!("Literal error: {}", err))
}
};

self.err(err)
Expand Down Expand Up @@ -392,10 +394,7 @@ impl<'c> EvaluatorPlanner<'c> {
),
ValueExpr::Lit(lit) => (
"literal",
EvalLitExpr {
lit: Value::from(lit.as_ref().clone()),
}
.bind::<{ STRICT }>(vec![]),
EvalLitExpr::new(Value::from(lit.as_ref().clone())).bind::<{ STRICT }>(vec![]),
),
ValueExpr::Path(expr, components) => (
"path",
Expand Down Expand Up @@ -515,7 +514,7 @@ impl<'c> EvaluatorPlanner<'c> {
// If no `ELSE` clause is specified, use implicit `ELSE NULL` (see section 6.9, pg 142 of SQL-92 spec)
None => self.unwrap_bind(
"simple case default",
EvalLitExpr { lit: Null }.bind::<{ STRICT }>(vec![]),
EvalLitExpr::new(Value::Null).bind::<{ STRICT }>(vec![]),
),
Some(def) => self.plan_value::<{ STRICT }>(def),
};
Expand All @@ -540,7 +539,7 @@ impl<'c> EvaluatorPlanner<'c> {
// If no `ELSE` clause is specified, use implicit `ELSE NULL` (see section 6.9, pg 142 of SQL-92 spec)
None => self.unwrap_bind(
"searched case default",
EvalLitExpr { lit: Null }.bind::<{ STRICT }>(vec![]),
EvalLitExpr::new(Value::Null).bind::<{ STRICT }>(vec![]),
),
Some(def) => self.plan_value::<{ STRICT }>(def.as_ref()),
};
Expand Down
Loading

1 comment on commit e21bd72

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PartiQL (rust) Benchmark

Benchmark suite Current: e21bd72 Previous: 76764b7 Ratio
arith_agg-avg 775516 ns/iter (± 2472) 770153 ns/iter (± 2588) 1.01
arith_agg-avg_distinct 860718 ns/iter (± 3610) 859709 ns/iter (± 8950) 1.00
arith_agg-count 825762 ns/iter (± 13338) 820097 ns/iter (± 16502) 1.01
arith_agg-count_distinct 856013 ns/iter (± 2178) 851393 ns/iter (± 6059) 1.01
arith_agg-min 827807 ns/iter (± 3572) 825236 ns/iter (± 2850) 1.00
arith_agg-min_distinct 863961 ns/iter (± 2032) 855755 ns/iter (± 6185) 1.01
arith_agg-max 833242 ns/iter (± 13387) 835713 ns/iter (± 32001) 1.00
arith_agg-max_distinct 872569 ns/iter (± 2150) 867964 ns/iter (± 15459) 1.01
arith_agg-sum 828631 ns/iter (± 2553) 828048 ns/iter (± 2813) 1.00
arith_agg-sum_distinct 859246 ns/iter (± 3067) 859180 ns/iter (± 2473) 1.00
arith_agg-avg-count-min-max-sum 988641 ns/iter (± 11710) 977325 ns/iter (± 4155) 1.01
arith_agg-avg-count-min-max-sum-group_by 1257536 ns/iter (± 16268) 1240333 ns/iter (± 4115) 1.01
arith_agg-avg-count-min-max-sum-group_by-group_as 1872303 ns/iter (± 7934) 1836969 ns/iter (± 23523) 1.02
arith_agg-avg_distinct-count_distinct-min_distinct-max_distinct-sum_distinct 1194270 ns/iter (± 12517) 1178308 ns/iter (± 12217) 1.01
arith_agg-avg_distinct-count_distinct-min_distinct-max_distinct-sum_distinct-group_by 1479657 ns/iter (± 6104) 1448869 ns/iter (± 19573) 1.02
arith_agg-avg_distinct-count_distinct-min_distinct-max_distinct-sum_distinct-group_by-group_as 2077505 ns/iter (± 13519) 2044763 ns/iter (± 5312) 1.02
parse-1 5522 ns/iter (± 62) 5434 ns/iter (± 27) 1.02
parse-15 46801 ns/iter (± 209) 47958 ns/iter (± 172) 0.98
parse-30 91843 ns/iter (± 355) 92866 ns/iter (± 333) 0.99
compile-1 4343 ns/iter (± 8) 4178 ns/iter (± 13) 1.04
compile-15 31329 ns/iter (± 75) 31003 ns/iter (± 209) 1.01
compile-30 63609 ns/iter (± 1169) 63253 ns/iter (± 281) 1.01
plan-1 71217 ns/iter (± 434) 67876 ns/iter (± 389) 1.05
plan-15 1102206 ns/iter (± 11946) 1069256 ns/iter (± 24766) 1.03
plan-30 2194003 ns/iter (± 12838) 2193025 ns/iter (± 37115) 1.00
eval-1 11976854 ns/iter (± 103202) 12079025 ns/iter (± 163991) 0.99
eval-15 78254362 ns/iter (± 1207080) 77791834 ns/iter (± 1120096) 1.01
eval-30 148466525 ns/iter (± 476208) 147818887 ns/iter (± 2140975) 1.00
join 9902 ns/iter (± 23) 9949 ns/iter (± 39) 1.00
simple 2540 ns/iter (± 15) 2592 ns/iter (± 13) 0.98
simple-no 488 ns/iter (± 4) 486 ns/iter (± 1) 1.00
numbers 48 ns/iter (± 0) 48 ns/iter (± 0) 1
parse-simple 746 ns/iter (± 14) 704 ns/iter (± 3) 1.06
parse-ion 2294 ns/iter (± 47) 2316 ns/iter (± 22) 0.99
parse-group 7003 ns/iter (± 18) 7264 ns/iter (± 23) 0.96
parse-complex 18215 ns/iter (± 56) 18563 ns/iter (± 109) 0.98
parse-complex-fexpr 25649 ns/iter (± 253) 26129 ns/iter (± 126) 0.98

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.