Skip to content

Commit 6e16561

Browse files
authored
Add errors from BaseTableExpr's to the evaluator (#447)
1 parent a177f63 commit 6e16561

File tree

9 files changed

+300
-22
lines changed

9 files changed

+300
-22
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
- Adds quotes to the attributes of PartiQL tuple's debug output so it can be read and transformed using Kotlin `partiql-cli`
1111
- Adds u8, u16, u32, u64, and u128 support to partiql_value::Value::from(type)
1212
- [breaking] Changes the interface to `EvalPlan` to accept an `EvalContext`
13+
- [breaking] Changes `EvaluationError` to not implement `Clone`
14+
- [breaking] Changes the structure of `EvalPlan`
1315

1416
### Added
1517
- Add `partiql-extension-visualize` for visualizing AST and logical plan
1618
- Add a `SessionContext` containing both a system-level and a user-level context object usable by expression evaluation
1719

1820
### Fixed
1921
- Fixed `ORDER BY`'s ability to see into projection aliases
22+
- Fixed errors in `BaseTableExpr`s get added to the evaluation context
23+
- Fixed certain errors surfacing in Permissive evaluation mode, when they should only be present in Strict mode
2024

2125
## [0.6.0] - 2023-10-31
2226
### Changed

partiql-catalog/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub struct ObjectId {
4646
}
4747

4848
pub type BaseTableExprResultError = Box<dyn Error>;
49+
4950
pub type BaseTableExprResultValueIter<'a> =
5051
Box<dyn 'a + Iterator<Item = Result<Value, BaseTableExprResultError>>>;
5152
pub type BaseTableExprResult<'a> =

partiql-eval/src/error.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::eval::evaluable::Evaluable;
22
use crate::eval::expr::EvalExpr;
33
use crate::eval::EvalContext;
4+
use partiql_catalog::BaseTableExprResultError;
45
use partiql_value::{Tuple, Value};
56
use std::borrow::Cow;
67
use thiserror::Error;
@@ -30,7 +31,7 @@ pub struct EvalErr {
3031
}
3132

3233
/// An error that can happen during evaluation.
33-
#[derive(Error, Debug, Clone, PartialEq, Eq, Hash)]
34+
#[derive(Error, Debug)]
3435
#[non_exhaustive]
3536
pub enum EvaluationError {
3637
/// Internal error that was not due to user input or API violation.
@@ -42,9 +43,13 @@ pub enum EvaluationError {
4243
/// Feature has not yet been implemented.
4344
#[error("Not yet implemented: {0}")]
4445
NotYetImplemented(String),
46+
47+
/// Error originating in an extension
48+
#[error("Base Table Expression Error")]
49+
ExtensionResultError(#[from] BaseTableExprResultError),
4550
}
4651

47-
/// Used when an error occurs during the the logical to eval plan conversion. Allows the conversion
52+
/// Used when an error occurs during the logical to eval plan conversion. Allows the conversion
4853
/// to continue in order to report multiple errors.
4954
#[derive(Debug)]
5055
pub(crate) struct ErrorNode {}

partiql-eval/src/eval/expr/base_table.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@ impl EvalExpr for EvalFnBaseTableExpr {
3737
let bag: Result<Bag, _> = it.collect();
3838
match bag {
3939
Ok(b) => Value::from(b),
40-
Err(_) => {
41-
// TODO hook into pending eval errors
40+
Err(err) => {
41+
ctx.add_error(err.into());
4242
Missing
4343
}
4444
}
4545
}
46-
Err(_) => {
47-
// TODO hook into pending eval errors
46+
Err(err) => {
47+
ctx.add_error(err.into());
4848
Missing
4949
}
5050
};

partiql-eval/src/eval/mod.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use petgraph::visit::EdgeRef;
2323
use unicase::UniCase;
2424

2525
use crate::eval::evaluable::{EvalType, Evaluable};
26+
use crate::plan::EvaluationMode;
2627

2728
pub(crate) mod eval_expr_wrapper;
2829
pub mod evaluable;
@@ -31,11 +32,14 @@ pub mod expr;
3132
/// Represents a PartiQL evaluation query plan which is a plan that can be evaluated to produce
3233
/// a result. The plan uses a directed `petgraph::StableGraph`.
3334
#[derive(Debug)]
34-
pub struct EvalPlan(pub StableGraph<Box<dyn Evaluable>, u8, Directed>);
35+
pub struct EvalPlan {
36+
mode: EvaluationMode,
37+
plan_graph: StableGraph<Box<dyn Evaluable>, u8, Directed>,
38+
}
3539

3640
impl Default for EvalPlan {
3741
fn default() -> Self {
38-
Self::new()
42+
Self::new(EvaluationMode::Permissive, Default::default())
3943
}
4044
}
4145

@@ -48,13 +52,16 @@ fn err_illegal_state(msg: impl AsRef<str>) -> EvalErr {
4852

4953
impl EvalPlan {
5054
/// Creates a new evaluation plan.
51-
fn new() -> Self {
52-
EvalPlan(StableGraph::<Box<dyn Evaluable>, u8, Directed>::new())
55+
pub fn new(
56+
mode: EvaluationMode,
57+
plan_graph: StableGraph<Box<dyn Evaluable>, u8, Directed>,
58+
) -> Self {
59+
EvalPlan { mode, plan_graph }
5360
}
5461

5562
#[inline]
5663
fn plan_graph(&mut self) -> &mut StableGraph<Box<dyn Evaluable>, u8> {
57-
&mut self.0
64+
&mut self.plan_graph
5865
}
5966

6067
#[inline]
@@ -73,7 +80,7 @@ impl EvalPlan {
7380
// that all v ∈ V \{v0} are reachable from v0. Note that this is the definition of trees
7481
// without the condition |E| = |V | − 1. Hence, all trees are DAGs.
7582
// Reference: https://link.springer.com/article/10.1007/s00450-009-0061-0
76-
let ops = toposort(&self.0, None).map_err(|e| EvalErr {
83+
let ops = toposort(&self.plan_graph, None).map_err(|e| EvalErr {
7784
errors: vec![EvaluationError::InvalidEvaluationPlan(format!(
7885
"Malformed evaluation plan detected: {e:?}"
7986
))],
@@ -101,7 +108,7 @@ impl EvalPlan {
101108
result = Some(src.evaluate(ctx));
102109

103110
// return on first evaluation error
104-
if ctx.has_errors() {
111+
if ctx.has_errors() && self.mode == EvaluationMode::Strict {
105112
return Err(EvalErr {
106113
errors: ctx.errors(),
107114
});
@@ -127,7 +134,7 @@ impl EvalPlan {
127134
}
128135

129136
pub fn to_dot_graph(&self) -> String {
130-
format!("{:?}", Dot::with_config(&self.0, &[]))
137+
format!("{:?}", Dot::with_config(&self.plan_graph, &[]))
131138
}
132139
}
133140

partiql-eval/src/plan.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ macro_rules! correct_num_args_or_err {
4949
};
5050
}
5151

52+
#[derive(Debug, Eq, PartialEq)]
5253
pub enum EvaluationMode {
5354
Strict,
5455
Permissive,
@@ -129,22 +130,26 @@ impl<'c> EvaluatorPlanner<'c> {
129130
fn plan_eval<const STRICT: bool>(&mut self, lg: &LogicalPlan<BindingsOp>) -> EvalPlan {
130131
let flows = lg.flows();
131132

132-
let mut graph: StableGraph<_, _> = Default::default();
133+
let mut plan_graph: StableGraph<_, _> = Default::default();
133134
let mut seen = HashMap::new();
134135

135136
for (s, d, branch_num) in flows {
136137
let mut add_node = |op_id: &OpId| {
137138
let logical_op = lg.operator(*op_id).unwrap();
138-
*seen
139-
.entry(*op_id)
140-
.or_insert_with(|| graph.add_node(self.get_eval_node::<{ STRICT }>(logical_op)))
139+
*seen.entry(*op_id).or_insert_with(|| {
140+
plan_graph.add_node(self.get_eval_node::<{ STRICT }>(logical_op))
141+
})
141142
};
142143

143144
let (s, d) = (add_node(s), add_node(d));
144-
graph.add_edge(s, d, *branch_num);
145+
plan_graph.add_edge(s, d, *branch_num);
145146
}
146-
147-
EvalPlan(graph)
147+
let mode = if STRICT {
148+
EvaluationMode::Strict
149+
} else {
150+
EvaluationMode::Permissive
151+
};
152+
EvalPlan::new(mode, plan_graph)
148153
}
149154

150155
fn get_eval_node<const STRICT: bool>(&mut self, be: &BindingsOp) -> Box<dyn Evaluable> {

partiql/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ itertools = "0.12"
3939
criterion = "0.5"
4040
rand = "0.8"
4141

42+
assert_matches = "1.5"
43+
4244
[[bench]]
4345
name = "bench_eval_multi_like"
4446
harness = false

0 commit comments

Comments
 (0)