Skip to content

Commit a279276

Browse files
authored
Add SELECT VALUE planning and evaluation (#215)
* Add `SELECT VALUE` planning and evaluation *Issue #, if available:* Closes #216 *Description of changes:* This commit adds the planning and evaluation of `SELECT VALUE` statements. It also: - Addresses a previous comment with regards to adding `thiserror` to `EvaluationErr`. - Adds `Debug` to `Tuple` type. The following cases are covered in tests: #### Section 6.1 (basic `SELECT VALUE`) ```SQL -- Expected: <<2, 4, 6>> SELECT VALUE 2*v.a FROM [{'a': 1}, {'a': 2}, {'a': 3}] AS v; ``` #### Section 6.1.1 _Tuple constructors_ ```SQL -- Expected: <<{'a': 1, 'b': 1}, {'a': 2, 'b': 2}>> SELECT VALUE {'a':v.a, 'b':v.b} FROM [{'a': 1, 'b': 1}, {'a': 2, 'b': 2}] AS v; -- Treatment of mistyped attribute names -- Expected: <<{'legit': 1}, {}>> SELECT VALUE {v.a: v.b} FROM [{'a': 'legit', 'b': 1}, {'a': 400, 'b': 2}] AS v; -- Treatment of duplicate attribute names -- Expected: <<{'same': 1, 'same': 2}>> SELECT VALUE {v.a: v.b, v.c: v.d} FROM [{'a': 'same', 'b': 1, 'c': 'same', 'd': 2}] AS v; ``` #### Section 6.1.2 _Array Constructors_ ```SQL -- Expected: <<[1, 1], [2, 2]>> SELECT VALUE [v.a, v.b] FROM [{'a': 1, 'b': 1}, {'a': 2, 'b': 2}] AS v; ``` #### Section 6.1.3 _Bag Constructors_ ```SQL -- Expected: << <<1, 1>>, <<2, 2>> >> SELECT VALUE <<v.a, v.b>> FROM [{'a': 1, 'b': 1}, {'a': 2, 'b': 2}] AS v; ``` #### Section 6.1.4 _Treatment of MISSING in SELECT VALUE_ ```SQL -- Expected: <<{'a': 1, 'b': 1}, {'a': 2}>> SELECT VALUE {'a': v.a, 'b': v.b} FROM [{'a': 1, 'b': 1}, {'a': 2}] AS v; -- Expected: <<[1, 1], [2, MISSING]>> SELECT VALUE [v.a, v.b] FROM [{'a': 1, 'b': 1}, {'a': 2}] AS v; -- Expected: <<1, MISSING>> SELECT VALUE v.b FROM [{'a': 1, 'b': 1}, {'a': 2}] AS v; ```
1 parent 1e3e2c7 commit a279276

File tree

6 files changed

+742
-37
lines changed

6 files changed

+742
-37
lines changed

partiql-eval/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ itertools = "0.10.*"
2828
unicase = "2.*"
2929
rust_decimal = { version = "1.25.0", default-features = false, features = ["std"] }
3030
rust_decimal_macros = "1.26"
31+
thiserror = "1.0.*"
32+
assert_matches = "1.5.*"
3133

3234
[dev-dependencies]
3335
criterion = "0.4"

partiql-eval/src/eval.rs

Lines changed: 128 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ use itertools::Itertools;
22
use std::collections::HashMap;
33
use std::fmt::Debug;
44

5+
use thiserror::Error;
6+
57
use petgraph::algo::toposort;
68
use petgraph::prelude::StableGraph;
79
use petgraph::{Directed, Incoming, Outgoing};
810

911
use partiql_value::Value::{Boolean, Missing, Null};
1012
use partiql_value::{
11-
partiql_bag, Bag, BinaryAnd, BinaryOr, BindingsName, NullableEq, NullableOrd, Tuple, UnaryPlus,
12-
Value,
13+
partiql_bag, Bag, BinaryAnd, BinaryOr, BindingsName, List, NullableEq, NullableOrd, Tuple,
14+
UnaryPlus, Value,
1315
};
1416

1517
use crate::env::basic::MapBindings;
@@ -40,7 +42,9 @@ pub struct EvalErr {
4042
pub errors: Vec<EvaluationError>,
4143
}
4244

45+
#[derive(Error, Debug)]
4346
pub enum EvaluationError {
47+
#[error("Evaluation Error: malformed evaluation plan detected `{}`", _0)]
4448
InvalidEvaluationPlan(String),
4549
}
4650

@@ -231,7 +235,10 @@ impl Evaluable for EvalProject {
231235
.as_ref()
232236
.expect("Error in retrieving input value")
233237
.clone();
234-
let mut value = partiql_bag![];
238+
239+
let ordered = &input_value.is_ordered();
240+
let mut value = vec![];
241+
235242
for v in input_value.into_iter() {
236243
let v_as_tuple = v.coerce_to_tuple();
237244
let mut t = Tuple::new();
@@ -242,14 +249,131 @@ impl Evaluable for EvalProject {
242249
value.push(Value::Tuple(Box::new(t)));
243250
}
244251

245-
self.output = Some(Value::Bag(Box::new(value)));
252+
self.output = match ordered {
253+
true => Some(Value::List(Box::new(List::from(value)))),
254+
false => Some(Value::Bag(Box::new(Bag::from(value)))),
255+
};
256+
257+
self.output.clone()
258+
}
259+
260+
fn update_input(&mut self, input: &Value) {
261+
self.input = Some(input.clone());
262+
}
263+
}
264+
265+
#[derive(Debug)]
266+
pub struct EvalProjectValue {
267+
pub expr: Box<dyn EvalExpr>,
268+
pub input: Option<Value>,
269+
pub output: Option<Value>,
270+
}
271+
272+
impl EvalProjectValue {
273+
pub fn new(expr: Box<dyn EvalExpr>) -> Self {
274+
EvalProjectValue {
275+
expr,
276+
input: None,
277+
output: None,
278+
}
279+
}
280+
}
281+
282+
impl Evaluable for EvalProjectValue {
283+
fn evaluate(&mut self, ctx: &dyn EvalContext) -> Option<Value> {
284+
let input_value = self
285+
.input
286+
.as_ref()
287+
.expect("Error in retrieving input value")
288+
.clone();
289+
290+
let ordered = &input_value.is_ordered();
291+
let mut value = vec![];
292+
293+
for v in input_value.into_iter() {
294+
let out = v.coerce_to_tuple();
295+
let evaluated = self.expr.evaluate(&out, ctx);
296+
value.push(evaluated);
297+
}
298+
299+
self.output = match ordered {
300+
true => Some(Value::List(Box::new(List::from(value)))),
301+
false => Some(Value::Bag(Box::new(Bag::from(value)))),
302+
};
303+
246304
self.output.clone()
247305
}
306+
248307
fn update_input(&mut self, input: &Value) {
249308
self.input = Some(input.clone());
250309
}
251310
}
252311

312+
#[derive(Debug)]
313+
pub struct EvalTupleExpr {
314+
pub attrs: Vec<Box<dyn EvalExpr>>,
315+
pub vals: Vec<Box<dyn EvalExpr>>,
316+
}
317+
318+
impl EvalExpr for EvalTupleExpr {
319+
fn evaluate(&self, bindings: &Tuple, ctx: &dyn EvalContext) -> Value {
320+
let mut t = Tuple::new();
321+
self.attrs
322+
.iter()
323+
.filter_map(|attr| {
324+
let expr = attr.evaluate(bindings, ctx);
325+
match expr {
326+
Value::String(s) => Some(*s),
327+
_ => None,
328+
}
329+
})
330+
.zip(self.vals.iter())
331+
.for_each(|(k, v)| {
332+
let evaluated = v.evaluate(bindings, ctx);
333+
// Spec. section 6.1.4
334+
if evaluated != Value::Missing {
335+
t.insert(k.as_str(), evaluated.clone());
336+
}
337+
});
338+
339+
Value::Tuple(Box::new(t))
340+
}
341+
}
342+
343+
#[derive(Debug)]
344+
pub struct EvalListExpr {
345+
pub elements: Vec<Box<dyn EvalExpr>>,
346+
}
347+
348+
impl EvalExpr for EvalListExpr {
349+
fn evaluate(&self, bindings: &Tuple, ctx: &dyn EvalContext) -> Value {
350+
let evaluated_elements: Vec<Value> = self
351+
.elements
352+
.iter()
353+
.map(|val| val.evaluate(bindings, ctx))
354+
.collect();
355+
356+
Value::List(Box::new(List::from(evaluated_elements)))
357+
}
358+
}
359+
360+
#[derive(Debug)]
361+
pub struct EvalBagExpr {
362+
pub elements: Vec<Box<dyn EvalExpr>>,
363+
}
364+
365+
impl EvalExpr for EvalBagExpr {
366+
fn evaluate(&self, bindings: &Tuple, ctx: &dyn EvalContext) -> Value {
367+
let evaluated_elements: Vec<Value> = self
368+
.elements
369+
.iter()
370+
.map(|val| val.evaluate(bindings, ctx))
371+
.collect();
372+
373+
Value::Bag(Box::new(Bag::from(evaluated_elements)))
374+
}
375+
}
376+
253377
#[derive(Debug)]
254378
pub enum EvalPathComponent {
255379
Key(String),

0 commit comments

Comments
 (0)