Skip to content

Commit 4253c37

Browse files
authored
Implement PIVOT in evaluator (#286)
1 parent 55a62fe commit 4253c37

File tree

5 files changed

+64
-1
lines changed

5 files changed

+64
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010

1111
### Added
1212
- Adds some benchmarks for parsing, compiling, planning, & evaluation
13+
- Implements `PIVOT` operator in evaluator
1314

1415
### Fixes
1516

partiql-eval/src/eval.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,47 @@ impl Evaluable for EvalJoin {
383383
}
384384
}
385385

386+
/// Represents an evaluation `Pivot` operator; the `Pivot` enables turning a collection into a
387+
/// tuple. For `Pivot` operational semantics, see section `6.2` of
388+
/// [PartiQL Specification — August 1, 2019](https://partiql.org/assets/PartiQL-Specification.pdf).
389+
#[derive(Debug)]
390+
pub struct EvalPivot {
391+
pub input: Option<Value>,
392+
pub key: Box<dyn EvalExpr>,
393+
pub value: Box<dyn EvalExpr>,
394+
}
395+
396+
impl EvalPivot {
397+
pub fn new(key: Box<dyn EvalExpr>, value: Box<dyn EvalExpr>) -> Self {
398+
EvalPivot {
399+
input: None,
400+
key,
401+
value,
402+
}
403+
}
404+
}
405+
406+
impl Evaluable for EvalPivot {
407+
fn evaluate(&mut self, ctx: &dyn EvalContext) -> Option<Value> {
408+
let input_value = self.input.take().expect("Error in retrieving input value");
409+
410+
let mut out: Tuple = partiql_tuple![];
411+
for binding in input_value.into_iter() {
412+
let binding = binding.coerce_to_tuple();
413+
let key = self.key.evaluate(&binding, ctx);
414+
if let Value::String(s) = key {
415+
let value = self.value.evaluate(&binding, ctx);
416+
out.insert(&s, value)
417+
}
418+
}
419+
Some(Value::Tuple(Box::new(out)))
420+
}
421+
422+
fn update_input(&mut self, input: Value, _branch_num: u8) {
423+
self.input = Some(input);
424+
}
425+
}
426+
386427
/// Represents an evaluation `Unpivot` operator; the `Unpivot` enables ranging over the
387428
/// attribute-value pairs of a tuple. For `Unpivot` operational semantics, see section `5.2` of
388429
/// [PartiQL Specification — August 1, 2019](https://partiql.org/assets/PartiQL-Specification.pdf).

partiql-eval/src/plan.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ impl EvaluatorPlanner {
8787
}),
8888
BindingsOp::Distinct => Box::new(eval::EvalDistinct::new()),
8989
BindingsOp::Sink => Box::new(eval::EvalSink { input: None }),
90+
BindingsOp::Pivot(logical::Pivot { key, value }) => Box::new(eval::EvalPivot::new(
91+
self.plan_values(key),
92+
self.plan_values(value),
93+
)),
9094
BindingsOp::Unpivot(logical::Unpivot {
9195
expr,
9296
as_key,

partiql-logical-planner/src/lower.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,13 @@ impl<'ast> Visitor<'ast> for AstToLogical {
621621

622622
logical::BindingsOp::Project(logical::Project { exprs })
623623
}
624-
ProjectionKind::ProjectPivot(_) => todo!("ProjectionKind::ProjectPivot"),
624+
ProjectionKind::ProjectPivot(_) => {
625+
assert_eq!(env.len(), 2);
626+
let mut iter = env.into_iter();
627+
let key = iter.next().unwrap();
628+
let value = iter.next().unwrap();
629+
logical::BindingsOp::Pivot(logical::Pivot { key, value })
630+
}
625631
ProjectionKind::ProjectValue(_) => {
626632
assert_eq!(env.len(), 1);
627633
let expr = env.into_iter().next().unwrap();

partiql-logical/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ impl OpId {
162162
#[derive(Debug, Clone, Default, Eq, PartialEq)]
163163
pub enum BindingsOp {
164164
Scan(Scan),
165+
Pivot(Pivot),
165166
Unpivot(Unpivot),
166167
Filter(Filter),
167168
OrderBy,
@@ -187,6 +188,16 @@ pub struct Scan {
187188
pub at_key: Option<String>,
188189
}
189190

191+
/// [`Pivot`] represents a PIVOT operator, e.g. `PIVOT sp.price AT sp."symbol` in
192+
/// `PIVOT sp.price AT sp."symbol" FROM todaysStockPrices sp`. For `Pivot` operational semantics,
193+
/// see section `6.2` of
194+
/// [PartiQL Specification — August 1, 2019](https://partiql.org/assets/PartiQL-Specification.pdf).
195+
#[derive(Debug, Clone, Eq, PartialEq)]
196+
pub struct Pivot {
197+
pub key: ValueExpr,
198+
pub value: ValueExpr,
199+
}
200+
190201
/// [`Unpivot`] bridges from [`ValueExpr`]s to [`BindingsOp`]s.
191202
#[derive(Debug, Clone, Eq, PartialEq)]
192203
pub struct Unpivot {

0 commit comments

Comments
 (0)