Skip to content

Commit

Permalink
Separate binding from evaluation of expression & implement strict eval (
Browse files Browse the repository at this point in the history
#413)

* Add interface for strict mode evaluation.
* Separate binding from evaluation of expression & implement strict eval
* Update conformance test data
* Better conformance test reporting
* Refactor to better categorize expressions
* Remove `is_null_or_missing`
  • Loading branch information
jpschorr authored Aug 2, 2023
1 parent 625aa08 commit 191b10e
Show file tree
Hide file tree
Showing 22 changed files with 2,508 additions and 1,881 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]
### Changed
- *BREAKING:* partiql-eval: `EvaluatorPlanner` construction now takes an `EvaluationMode` parameter.
- *BREAKING:* partiql-eval: Construction of expression evaluators changed to separate binding from evaluation of expression. & implement strict eval
- *BREAKING:* partiql-value: `Value` trait's `is_null_or_missing` renamed to `is_absent`
- *BREAKING:* partiql-eval: `EvaluatorPlanner` construction now takes an `EvaluationMode` parameter.
- *BREAKING:* partiql-eval: `like_to_re_pattern` is no longer public.
- *BREAKING:* partiql-value: Box Decimals in `Value` to assure `Value` fits in 16 bytes.
- *BREAKING:* partiql-logical-planner: moves `NameResolver` to `partiql-ast-passes`
- *BREAKING:* partiql-value: removes `partiql` from value macro_rules; e.g. `partiql_bag` renames to `bag`.
Expand All @@ -20,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- *BREAKING:* partiql-logical: changed modeling of `Project` `exprs` to be a `Vec<(String, ValueExpr)>` rather than a `HashMap<String, ValueExpr>` to support multiple project items with the same alias

### Added
- Strict mode evaluation partial support added.
- Add interface for `STRICT` mode evalution to `EvaluatorPlanner`.
- Add ability for partiql-extension-ion extension encoding/decoding of `Value` to/from Ion `Element`
- Add `partiql-types` crate that includes data models for PartiQL Types.
Expand Down
2 changes: 2 additions & 0 deletions partiql-conformance-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ once_cell = "1"

rust_decimal = "1.27"

thiserror = "1.0"

serde = { version = "1.*", features = ["derive"], optional = true }
serde_json = { version = "1.*", optional = true }

Expand Down
2 changes: 1 addition & 1 deletion partiql-conformance-tests/partiql-tests
103 changes: 69 additions & 34 deletions partiql-conformance-tests/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ use partiql_ast_passes::error::AstTransformationError;
use partiql_catalog::{Catalog, PartiqlCatalog};
use partiql_eval as eval;
use partiql_eval::env::basic::MapBindings;
use partiql_eval::error::PlanErr;
use partiql_eval::eval::{EvalPlan, EvalResult};
use partiql_eval::error::{EvalErr, PlanErr};
use partiql_eval::eval::{EvalPlan, EvalResult, Evaluated};
use partiql_logical as logical;
use partiql_parser::{Parsed, ParserResult};
use partiql_parser::{Parsed, ParserError, ParserResult};
use partiql_value::Value;

use thiserror::Error;

mod test_value;
pub(crate) use test_value::TestValue;

Expand Down Expand Up @@ -111,31 +113,73 @@ pub(crate) fn pass_semantics(statement: &str) {
);
}

#[derive(Error, Debug)]
enum TestError<'a> {
#[error("Parse error: {0:?}")]
Parse(ParserError<'a>),
#[error("Lower error: {0:?}")]
Lower(AstTransformationError),
#[error("Plan error: {0:?}")]
Plan(PlanErr),
#[error("Evaluation error: {0:?}")]
Eval(EvalErr),
}

impl<'a> From<ParserError<'a>> for TestError<'a> {
fn from(err: ParserError<'a>) -> Self {
TestError::Parse(err)
}
}

impl From<AstTransformationError> for TestError<'_> {
fn from(err: AstTransformationError) -> Self {
TestError::Lower(err)
}
}

impl From<PlanErr> for TestError<'_> {
fn from(err: PlanErr) -> Self {
TestError::Plan(err)
}
}

impl From<EvalErr> for TestError<'_> {
fn from(err: EvalErr) -> Self {
TestError::Eval(err)
}
}

#[track_caller]
#[inline]
#[allow(dead_code)]
pub(crate) fn fail_eval(statement: &str, mode: EvaluationMode, env: &Option<TestValue>) {
pub(crate) fn eval<'a>(
statement: &'a str,
mode: EvaluationMode,
env: &Option<TestValue>,
) -> Result<Evaluated, TestError<'a>> {
let catalog = PartiqlCatalog::default();

let parsed = parse(statement);
let lowered_result = lower(&catalog, &parsed.expect("parse"));
let lowered = lowered_result.expect("lower");
let parsed = parse(statement)?;
let lowered = lower(&catalog, &parsed)?;
let bindings = env
.as_ref()
.map(|e| (&e.value).into())
.unwrap_or_else(MapBindings::default);
let plan = compile(mode, &catalog, lowered)?;
Ok(evaluate(plan, bindings)?)
}

let plan = compile(mode, &catalog, lowered);
match plan {
Ok(plan) => {
let out = evaluate(plan, bindings);
#[track_caller]
#[inline]
#[allow(dead_code)]
pub(crate) fn fail_eval(statement: &str, mode: EvaluationMode, env: &Option<TestValue>) {
let result = eval(statement, mode, env);

assert!(
out.is_err(),
"When evaluating (mode = {mode:#?}) `{statement}`, expected `Err(_)`, but was `{out:#?}`"
);
}
Err(_) => {}
match result {
Ok(result) => panic!("When evaluating (mode = {mode:#?}) `{statement}`, expected `Err(_)`, but was `{result:#?}`"),
Err(TestError::Parse(_)) => panic!("When evaluating (mode = {mode:#?}) `{statement}`, unexpected parse error"),
Err(TestError::Lower(err)) => panic!("When evaluating (mode = {mode:#?}) `{statement}`, unexpected lowering error `{err:?}`"),
Err(TestError::Plan(_)) | Err(TestError::Eval(_)) => {}
}
}

Expand All @@ -148,25 +192,16 @@ pub(crate) fn pass_eval(
env: &Option<TestValue>,
expected: &TestValue,
) {
let catalog = PartiqlCatalog::default();

let parsed = parse(statement);
let lowered_result = lower(&catalog, &parsed.expect("parse"));
let lowered = lowered_result.expect("lower");
let bindings = env
.as_ref()
.map(|e| (&e.value).into())
.unwrap_or_else(MapBindings::default);
let plan = compile(mode, &catalog, lowered).expect("compile");
let out = evaluate(plan, bindings);

match out {
match eval(statement, mode, env) {
Ok(v) => assert_eq!(v.result, expected.value),
Err(err) => {
panic!(
"When evaluating (mode = {mode:#?}) `{statement}`, expected `Ok(_)`, but was `Err({err:#?})`"
)
Err(TestError::Parse(_)) => {
panic!("When evaluating (mode = {mode:#?}) `{statement}`, unexpected parse error")
}
Err(TestError::Lower(err)) => panic!("When evaluating (mode = {mode:#?}) `{statement}`, unexpected lowering error `{err:?}`"),
Err(TestError::Plan(err)) => panic!("When evaluating (mode = {mode:#?}) `{statement}`, unexpected planning error `{err:?}`"),
Err(TestError::Eval(err)) => panic!(
"When evaluating (mode = {mode:#?}) `{statement}`, expected `Ok(_)`, but was `Err({err:#?})`"
)
}
}

Expand Down
1 change: 1 addition & 0 deletions partiql-eval/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ bench = false
partiql-logical = { path = "../partiql-logical", version = "0.5.*" }
partiql-value = { path = "../partiql-value", version = "0.5.*" }
partiql-catalog = { path = "../partiql-catalog", version = "0.5.*" }
partiql-types = { path = "../partiql-types", version = "0.5.*" }
petgraph = "0.6.*"
ordered-float = "3.*"
itertools = "0.10.*"
Expand Down
Loading

1 comment on commit 191b10e

@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: 191b10e Previous: 625aa08 Ratio
parse-1 6260 ns/iter (± 158) 5379 ns/iter (± 231) 1.16
parse-15 58068 ns/iter (± 1031) 50408 ns/iter (± 2260) 1.15
parse-30 115922 ns/iter (± 1136) 100417 ns/iter (± 6670) 1.15
compile-1 5801 ns/iter (± 144) 4700 ns/iter (± 173) 1.23
compile-15 41542 ns/iter (± 1008) 36433 ns/iter (± 1775) 1.14
compile-30 84941 ns/iter (± 1662) 74912 ns/iter (± 7558) 1.13
plan-1 75941 ns/iter (± 2244) 93098 ns/iter (± 3312) 0.82
plan-15 1238880 ns/iter (± 21167) 1459815 ns/iter (± 74896) 0.85
plan-30 2469528 ns/iter (± 36134) 2989160 ns/iter (± 186553) 0.83
eval-1 27253400 ns/iter (± 913364) 20888650 ns/iter (± 673268) 1.30
eval-15 161582161 ns/iter (± 2640630) 114418618 ns/iter (± 3853937) 1.41
eval-30 310471135 ns/iter (± 4703593) 221320721 ns/iter (± 10783798) 1.40
join 18380 ns/iter (± 475) 13619 ns/iter (± 789) 1.35
simple 8009 ns/iter (± 130) 6240 ns/iter (± 208) 1.28
simple-no 824 ns/iter (± 19) 673 ns/iter (± 26) 1.22
numbers 63 ns/iter (± 1) 43 ns/iter (± 1) 1.47
parse-simple 856 ns/iter (± 25) 731 ns/iter (± 31) 1.17
parse-ion 2637 ns/iter (± 77) 2295 ns/iter (± 87) 1.15
parse-group 8520 ns/iter (± 173) 7637 ns/iter (± 400) 1.12
parse-complex 23148 ns/iter (± 503) 20476 ns/iter (± 834) 1.13
parse-complex-fexpr 33829 ns/iter (± 726) 29737 ns/iter (± 1340) 1.14

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

Please sign in to comment.