Skip to content

Commit 5d2f3b1

Browse files
am357alancai98
andauthored
Enable Tuple with duplicate keys (#212)
This PR includes the required changes to allow duplicate keys in `Tuple` to conform with PartiQL definition of `Tuple` type as oppose to `Map` with unique keys. Co-authored-by: Alan Cai <caialan@amazon.com>
1 parent 13a8ccf commit 5d2f3b1

File tree

4 files changed

+137
-103
lines changed

4 files changed

+137
-103
lines changed

partiql-eval/src/env.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ pub trait Bindings<T> {
88
impl Bindings<Value> for Tuple {
99
fn get(&self, name: &BindingsName) -> Option<&Value> {
1010
match name {
11-
BindingsName::CaseSensitive(s) => self.0.get(s),
11+
BindingsName::CaseSensitive(s) => self.get(s),
1212
BindingsName::CaseInsensitive(s) => {
1313
//TODO
14-
self.0.get(s)
14+
self.get(s)
1515
}
1616
}
1717
}

partiql-eval/src/eval.rs

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -79,25 +79,25 @@ impl EvalScan {
7979
impl Evaluable for EvalScan {
8080
fn evaluate(&mut self, ctx: &dyn EvalContext) -> Option<Value> {
8181
let mut value = partiql_bag![];
82-
let v = self.expr.evaluate(&Tuple(HashMap::new()), ctx);
82+
let v = self.expr.evaluate(&Tuple::new(), ctx);
8383
let ordered = &v.is_ordered();
8484
let mut at_index_counter: i64 = 0;
8585
if let Some(at_key) = &self.at_key {
8686
for t in v.into_iter() {
87-
let mut out = HashMap::from([(self.as_key.clone(), t)]);
87+
let mut out = Tuple::from([(self.as_key.as_str(), t)]);
8888
let at_id = if *ordered {
8989
at_index_counter.into()
9090
} else {
9191
Missing
9292
};
93-
out.insert(at_key.clone(), at_id);
94-
value.push(Value::Tuple(Box::new(Tuple(out))));
93+
out.insert(&at_key, at_id);
94+
value.push(Value::Tuple(Box::new(out)));
9595
at_index_counter += 1;
9696
}
9797
} else {
9898
for t in v.into_iter() {
99-
let out = HashMap::from([(self.as_key.clone(), t)]);
100-
value.push(Value::Tuple(Box::new(Tuple(out))));
99+
let out = Tuple::from([(self.as_key.as_str(), t)]);
100+
value.push(Value::Tuple(Box::new(out)));
101101
}
102102
}
103103
self.output = Some(Value::Bag(Box::new(value)));
@@ -130,15 +130,15 @@ impl EvalUnpivot {
130130

131131
impl Evaluable for EvalUnpivot {
132132
fn evaluate(&mut self, ctx: &dyn EvalContext) -> Option<Value> {
133-
let result = self.expr.evaluate(&Tuple(HashMap::new()), ctx);
133+
let result = self.expr.evaluate(&Tuple::new(), ctx);
134134
let mut out = vec![];
135135

136136
let tuple = match result {
137137
Value::Tuple(tuple) => *tuple,
138138
other => other.coerce_to_tuple(),
139139
};
140140

141-
let unpivoted = tuple.0.into_iter().map(|(k, v)| {
141+
let unpivoted = tuple.into_iter().map(|(k, v)| {
142142
Tuple::from([(self.as_key.as_str(), v), (self.at_key.as_str(), k.into())])
143143
});
144144

@@ -233,14 +233,13 @@ impl Evaluable for EvalProject {
233233
.clone();
234234
let mut value = partiql_bag![];
235235
for v in input_value.into_iter() {
236-
let out = v.coerce_to_tuple();
237-
238-
let proj: HashMap<String, Value> = self
239-
.exprs
240-
.iter()
241-
.map(|(alias, expr)| (alias.to_string(), expr.evaluate(&out, ctx)))
242-
.collect();
243-
value.push(Value::Tuple(Box::new(Tuple(proj))));
236+
let v_as_tuple = v.coerce_to_tuple();
237+
let mut t = Tuple::new();
238+
239+
self.exprs.iter().for_each(|(alias, expr)| {
240+
t.insert(alias.as_str(), expr.evaluate(&v_as_tuple, ctx));
241+
});
242+
value.push(Value::Tuple(Box::new(t)));
244243
}
245244

246245
self.output = Some(Value::Bag(Box::new(value)));
@@ -269,7 +268,7 @@ impl EvalExpr for EvalPath {
269268
fn path_into(value: Value, path: &EvalPathComponent) -> Value {
270269
match path {
271270
EvalPathComponent::Key(s) => match value {
272-
Value::Tuple(mut tuple) => tuple.0.remove(s).unwrap_or(Missing),
271+
Value::Tuple(mut tuple) => tuple.remove(s).unwrap_or(Missing),
273272
_ => Missing,
274273
},
275274
EvalPathComponent::Index(idx) => match value {
@@ -344,9 +343,7 @@ pub struct EvalVarRef {
344343

345344
impl EvalExpr for EvalVarRef {
346345
fn evaluate(&self, bindings: &Tuple, ctx: &dyn EvalContext) -> Value {
347-
let value = bindings
348-
.get(&self.name)
349-
.or_else(|| ctx.bindings().get(&self.name));
346+
let value = Bindings::get(bindings, &self.name).or_else(|| ctx.bindings().get(&self.name));
350347
value.map_or(Null, |v| v.clone())
351348
}
352349
}
@@ -421,9 +418,9 @@ impl EvalExpr for EvalBinOpExpr {
421418
#[inline]
422419
fn short_circuit(op: &EvalBinOp, value: &Value) -> Option<Value> {
423420
match (op, value) {
424-
(EvalBinOp::And, Value::Boolean(false)) => Some(false.into()),
425-
(EvalBinOp::Or, Value::Boolean(true)) => Some(true.into()),
426-
(_, Value::Missing) => Some(Value::Missing),
421+
(EvalBinOp::And, Boolean(false)) => Some(false.into()),
422+
(EvalBinOp::Or, Boolean(true)) => Some(true.into()),
423+
(_, Missing) => Some(Missing),
427424
_ => None,
428425
}
429426
}

partiql-eval/src/lib.rs

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,8 @@ mod tests {
3434
}
3535

3636
fn data_customer() -> MapBindings<Value> {
37-
fn customer_tuple(id: i64, first_name: &str, balance: i64) -> value::Value {
38-
Tuple(HashMap::from([
39-
("id".into(), id.into()),
40-
("firstName".into(), first_name.into()),
41-
("balance".into(), balance.into()),
42-
]))
43-
.into()
37+
fn customer_tuple(id: i64, first_name: &str, balance: i64) -> Value {
38+
partiql_tuple![("id", id), ("firstName", first_name), ("balance", balance),].into()
4439
}
4540

4641
let customer_val = partiql_bag![
@@ -57,10 +52,10 @@ mod tests {
5752
}
5853

5954
fn data_3_tuple() -> MapBindings<Value> {
60-
fn a_tuple(n: i64) -> value::Value {
61-
Tuple(HashMap::from([("a".into(), n.into())])).into()
55+
fn a_tuple(n: i64) -> Value {
56+
partiql_tuple![("a", n)].into()
6257
}
63-
//let data = List(vec![a_tuple(1), a_tuple(2), a_tuple(3)]);
58+
6459
let data = partiql_list![a_tuple(1), a_tuple(2), a_tuple(3)];
6560

6661
let mut bindings = MapBindings::default();
@@ -81,7 +76,7 @@ mod tests {
8176
at_key: None,
8277
}));
8378

84-
let project = plan.add_operator(BindingsExpr::Project(logical::Project {
79+
let project = plan.add_operator(Project(logical::Project {
8580
exprs: HashMap::from([(
8681
"result".to_string(),
8782
ValueExpr::BinaryExpr(
@@ -103,15 +98,12 @@ mod tests {
10398
let mut bindings = MapBindings::default();
10499
bindings.insert(
105100
"data",
106-
partiql_list![Tuple(HashMap::from([("lhs".into(), lhs)]))].into(),
101+
partiql_list![Tuple::from([("lhs".into(), lhs)])].into(),
107102
);
108103

109104
let result = evaluate(plan, bindings).coerce_to_bag();
110105
assert!(!&result.is_empty());
111-
let expected_result = partiql_bag!(Tuple(HashMap::from([(
112-
"result".into(),
113-
expected_first_elem
114-
)])));
106+
let expected_result = partiql_bag!(Tuple::from([("result".into(), expected_first_elem)]));
115107
assert_eq!(expected_result, result);
116108
}
117109

@@ -448,7 +440,7 @@ mod tests {
448440
at_key: None,
449441
}));
450442

451-
let project = lg.add_operator(BindingsExpr::Project(logical::Project {
443+
let project = lg.add_operator(Project(logical::Project {
452444
exprs: HashMap::from([(
453445
"b".to_string(),
454446
ValueExpr::Path(
@@ -491,7 +483,7 @@ mod tests {
491483

492484
let filter = logical.add_operator(BindingsExpr::Filter(logical::Filter {
493485
expr: ValueExpr::BinaryExpr(
494-
logical::BinaryOp::Gt,
486+
BinaryOp::Gt,
495487
Box::new(ValueExpr::Path(
496488
Box::new(ValueExpr::VarRef(BindingsName::CaseInsensitive(
497489
"customer".into(),
@@ -516,7 +508,7 @@ mod tests {
516508
(
517509
"doubleName".to_string(),
518510
ValueExpr::BinaryExpr(
519-
logical::BinaryOp::Concat,
511+
BinaryOp::Concat,
520512
Box::new(ValueExpr::Path(
521513
Box::new(ValueExpr::VarRef(BindingsName::CaseInsensitive(
522514
"customer".into(),
@@ -544,8 +536,12 @@ mod tests {
544536
(distinct, sink),
545537
]);
546538

547-
if let Value::Bag(b) = evaluate(logical, data_customer()) {
548-
assert_eq!(b.len(), 2);
539+
if let Value::Bag(out) = evaluate(logical, data_customer()) {
540+
let expected = partiql_bag![
541+
partiql_tuple![("firstName", "jason"), ("doubleName", "jasonjason")],
542+
partiql_tuple![("firstName", "miriam"), ("doubleName", "miriammiriam")],
543+
];
544+
assert_eq!(expected, *out);
549545
} else {
550546
panic!("Wrong output")
551547
}

0 commit comments

Comments
 (0)