Skip to content

Commit 5061309

Browse files
authored
Add arithmetic ops to evaluator; add Decimal partiql-value (#203)
1 parent 01e8ae1 commit 5061309

File tree

7 files changed

+635
-36
lines changed

7 files changed

+635
-36
lines changed

partiql-eval/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ petgraph = "0.6.*"
2626
ordered-float = "3.*"
2727
itertools = "0.10.*"
2828
unicase = "2.*"
29+
rust_decimal = { version = "1.25.0", default-features = false, features = ["std"] }
2930

3031
[dev-dependencies]
3132
criterion = "0.4"

partiql-eval/src/eval.rs

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -297,35 +297,56 @@ pub enum EvalBinop {
297297
Gteq,
298298
Lt,
299299
Lteq,
300+
301+
// Arithmetic ops
302+
Add,
303+
Sub,
304+
Mul,
305+
Div,
306+
Mod,
307+
Exp,
300308
}
301309

302310
impl EvalExpr for EvalBinopExpr {
303311
fn evaluate(&self, bindings: &Tuple, ctx: &dyn EvalContext) -> Value {
304312
let lhs = self.lhs.evaluate(bindings, ctx);
305313
let rhs = self.rhs.evaluate(bindings, ctx);
306-
match self.op {
307-
EvalBinop::And => todo!(),
308-
EvalBinop::Or => todo!(),
309-
EvalBinop::Concat => {
310-
// TODO non-naive concat
311-
let lhs = if let Value::String(s) = lhs {
312-
*s
313-
} else {
314-
format!("{:?}", lhs)
315-
};
316-
let rhs = if let Value::String(s) = rhs {
317-
*s
318-
} else {
319-
format!("{:?}", lhs)
320-
};
321-
Value::String(Box::new(format!("{}{}", lhs, rhs)))
314+
// Missing and Null propagation. Missing has precedence over Null
315+
if lhs == Value::Missing || rhs == Value::Missing {
316+
Value::Missing
317+
} else if lhs == Value::Null || rhs == Value::Null {
318+
Value::Null
319+
} else {
320+
match self.op {
321+
EvalBinop::And => todo!(),
322+
EvalBinop::Or => todo!(),
323+
EvalBinop::Concat => {
324+
// TODO non-naive concat
325+
let lhs = if let Value::String(s) = lhs {
326+
*s
327+
} else {
328+
format!("{:?}", lhs)
329+
};
330+
let rhs = if let Value::String(s) = rhs {
331+
*s
332+
} else {
333+
format!("{:?}", lhs)
334+
};
335+
Value::String(Box::new(format!("{}{}", lhs, rhs)))
336+
}
337+
EvalBinop::Eq => todo!(),
338+
EvalBinop::Neq => todo!(),
339+
EvalBinop::Gt => Boolean(lhs > rhs),
340+
EvalBinop::Gteq => Boolean(lhs >= rhs),
341+
EvalBinop::Lt => Boolean(lhs < rhs),
342+
EvalBinop::Lteq => Boolean(lhs <= rhs),
343+
EvalBinop::Add => lhs + rhs,
344+
EvalBinop::Sub => lhs - rhs,
345+
EvalBinop::Mul => lhs * rhs,
346+
EvalBinop::Div => lhs / rhs,
347+
EvalBinop::Mod => lhs % rhs,
348+
EvalBinop::Exp => todo!("Exponentiation"),
322349
}
323-
EvalBinop::Eq => todo!(),
324-
EvalBinop::Neq => todo!(),
325-
EvalBinop::Gt => Boolean(lhs > rhs),
326-
EvalBinop::Gteq => Boolean(lhs >= rhs),
327-
EvalBinop::Lt => Boolean(lhs < rhs),
328-
EvalBinop::Lteq => Boolean(lhs <= rhs),
329350
}
330351
}
331352
}

partiql-eval/src/lib.rs

Lines changed: 141 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,20 @@ mod tests {
99
use std::rc::Rc;
1010

1111
use partiql_logical as logical;
12-
use partiql_logical::{BindingsExpr, LogicalPlan, PathComponent, ValueExpr};
12+
use partiql_logical::{BinaryOp, BindingsExpr, LogicalPlan, PathComponent, ValueExpr};
1313
use partiql_value as value;
14+
15+
use crate::env::basic::MapBindings;
16+
use crate::plan;
17+
use ordered_float::OrderedFloat;
1418
use partiql_value::{
1519
partiql_bag, partiql_list, partiql_tuple, Bag, BindingsName, List, Tuple, Value,
1620
};
21+
use rust_decimal::Decimal as RustDecimal;
1722

18-
use crate::env::basic::MapBindings;
1923
use crate::eval::{
2024
DagEvaluator, EvalFromAt, EvalOutputAccumulator, Evaluable, Evaluator, Output,
2125
};
22-
use crate::plan;
2326

2427
fn evaluate(logical: BindingsExpr, bindings: MapBindings<Value>) -> Bag {
2528
let output = Rc::new(RefCell::new(EvalOutputAccumulator::default()));
@@ -91,6 +94,23 @@ mod tests {
9194
bindings
9295
}
9396

97+
fn data_arithmetic_tuple() -> MapBindings<Value> {
98+
fn tuple_a_to_v(v: Value) -> value::Value {
99+
Tuple(HashMap::from([("a".into(), v)])).into()
100+
}
101+
// <<{'a': <int>}, {'a': <decimal>}, {'a': <float>}, {'a': NULL}, {'a': MISSING}>>
102+
let data = partiql_list![
103+
tuple_a_to_v(Value::Integer(1)),
104+
tuple_a_to_v(Value::Decimal(RustDecimal::from(1))),
105+
tuple_a_to_v(Value::Real(OrderedFloat(1.5))),
106+
tuple_a_to_v(Value::Null),
107+
tuple_a_to_v(Value::Missing),
108+
];
109+
let mut bindings = MapBindings::default();
110+
bindings.insert("data", data.into());
111+
bindings
112+
}
113+
94114
#[test]
95115
fn select() {
96116
// Plan for `select a as b from data`
@@ -116,6 +136,124 @@ mod tests {
116136
assert_eq!(result.len(), 3);
117137
}
118138

139+
#[test]
140+
fn arithmetic() {
141+
// Tests arithmetic ops using int, real, decimal, null, and missing with values defined from
142+
// `data_arithmetic_tuple`
143+
fn arithmetic_logical(binary_op: BinaryOp, lit: Value) -> BindingsExpr {
144+
BindingsExpr::From(logical::From {
145+
expr: ValueExpr::VarRef(BindingsName::CaseInsensitive("data".into())),
146+
as_key: "data".to_string(),
147+
at_key: None,
148+
out: Box::new(BindingsExpr::Select(logical::Select {
149+
exprs: HashMap::from([(
150+
"b".to_string(),
151+
ValueExpr::BinaryExpr(
152+
binary_op,
153+
Box::new(ValueExpr::Path(
154+
Box::new(ValueExpr::VarRef(BindingsName::CaseInsensitive(
155+
"data".into(),
156+
))),
157+
vec![PathComponent::Key("a".to_string())],
158+
)),
159+
Box::new(ValueExpr::Lit(Box::new(lit))),
160+
),
161+
)]),
162+
out: Box::new(BindingsExpr::Output),
163+
})),
164+
})
165+
}
166+
// Plan for `select a + <lit> as b from data`
167+
println!("Add+++++++++++++++++++++++++++++");
168+
let logical = arithmetic_logical(BinaryOp::Add, Value::Integer(1));
169+
let result = evaluate(logical, data_arithmetic_tuple());
170+
println!("{:?}", result);
171+
let logical = arithmetic_logical(BinaryOp::Add, Value::Decimal(RustDecimal::new(11, 1)));
172+
let result = evaluate(logical, data_arithmetic_tuple());
173+
println!("{:?}", result);
174+
let logical = arithmetic_logical(BinaryOp::Add, Value::Real(OrderedFloat(1.5)));
175+
let result = evaluate(logical, data_arithmetic_tuple());
176+
println!("{:?}", result);
177+
let logical = arithmetic_logical(BinaryOp::Add, Value::Null);
178+
let result = evaluate(logical, data_arithmetic_tuple());
179+
println!("{:?}", result);
180+
let logical = arithmetic_logical(BinaryOp::Add, Value::Missing);
181+
let result = evaluate(logical, data_arithmetic_tuple());
182+
println!("{:?}", result);
183+
184+
// Plan for `select a - <lit> as b from data`
185+
println!("Sub-----------------------------");
186+
let logical = arithmetic_logical(BinaryOp::Sub, Value::Integer(1));
187+
let result = evaluate(logical, data_arithmetic_tuple());
188+
println!("{:?}", result);
189+
let logical = arithmetic_logical(BinaryOp::Sub, Value::Decimal(RustDecimal::new(11, 1)));
190+
let result = evaluate(logical, data_arithmetic_tuple());
191+
println!("{:?}", result);
192+
let logical = arithmetic_logical(BinaryOp::Sub, Value::Real(OrderedFloat(1.5)));
193+
let result = evaluate(logical, data_arithmetic_tuple());
194+
println!("{:?}", result);
195+
let logical = arithmetic_logical(BinaryOp::Sub, Value::Null);
196+
let result = evaluate(logical, data_arithmetic_tuple());
197+
println!("{:?}", result);
198+
let logical = arithmetic_logical(BinaryOp::Sub, Value::Missing);
199+
let result = evaluate(logical, data_arithmetic_tuple());
200+
println!("{:?}", result);
201+
202+
// Plan for `select a * <lit> as b from data`
203+
println!("Mul*****************************");
204+
let logical = arithmetic_logical(BinaryOp::Mul, Value::Integer(1));
205+
let result = evaluate(logical, data_arithmetic_tuple());
206+
println!("{:?}", result);
207+
let logical = arithmetic_logical(BinaryOp::Mul, Value::Decimal(RustDecimal::new(11, 1)));
208+
let result = evaluate(logical, data_arithmetic_tuple());
209+
println!("{:?}", result);
210+
let logical = arithmetic_logical(BinaryOp::Mul, Value::Real(OrderedFloat(1.5)));
211+
let result = evaluate(logical, data_arithmetic_tuple());
212+
println!("{:?}", result);
213+
let logical = arithmetic_logical(BinaryOp::Mul, Value::Null);
214+
let result = evaluate(logical, data_arithmetic_tuple());
215+
println!("{:?}", result);
216+
let logical = arithmetic_logical(BinaryOp::Mul, Value::Missing);
217+
let result = evaluate(logical, data_arithmetic_tuple());
218+
println!("{:?}", result);
219+
220+
// Plan for `select a / <lit> as b from data`
221+
println!("Div/////////////////////////////");
222+
let logical = arithmetic_logical(BinaryOp::Div, Value::Integer(1));
223+
let result = evaluate(logical, data_arithmetic_tuple());
224+
println!("{:?}", result);
225+
let logical = arithmetic_logical(BinaryOp::Div, Value::Decimal(RustDecimal::new(11, 1)));
226+
let result = evaluate(logical, data_arithmetic_tuple());
227+
println!("{:?}", result);
228+
let logical = arithmetic_logical(BinaryOp::Div, Value::Real(OrderedFloat(1.5)));
229+
let result = evaluate(logical, data_arithmetic_tuple());
230+
println!("{:?}", result);
231+
let logical = arithmetic_logical(BinaryOp::Div, Value::Null);
232+
let result = evaluate(logical, data_arithmetic_tuple());
233+
println!("{:?}", result);
234+
let logical = arithmetic_logical(BinaryOp::Div, Value::Missing);
235+
let result = evaluate(logical, data_arithmetic_tuple());
236+
println!("{:?}", result);
237+
238+
// Plan for `select a % <lit> as b from data`
239+
println!("Mod%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
240+
let logical = arithmetic_logical(BinaryOp::Mod, Value::Integer(1));
241+
let result = evaluate(logical, data_arithmetic_tuple());
242+
println!("{:?}", result);
243+
let logical = arithmetic_logical(BinaryOp::Mod, Value::Decimal(RustDecimal::new(11, 1)));
244+
let result = evaluate(logical, data_arithmetic_tuple());
245+
println!("{:?}", result);
246+
let logical = arithmetic_logical(BinaryOp::Mod, Value::Real(OrderedFloat(1.5)));
247+
let result = evaluate(logical, data_arithmetic_tuple());
248+
println!("{:?}", result);
249+
let logical = arithmetic_logical(BinaryOp::Mod, Value::Null);
250+
let result = evaluate(logical, data_arithmetic_tuple());
251+
println!("{:?}", result);
252+
let logical = arithmetic_logical(BinaryOp::Mod, Value::Missing);
253+
let result = evaluate(logical, data_arithmetic_tuple());
254+
println!("{:?}", result);
255+
}
256+
119257
#[test]
120258
fn select_dag() {
121259
// Plan for `select a as b from data`

partiql-eval/src/plan.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ impl EvaluatorPlanner {
132132
BinaryOp::Gteq => EvalBinop::Gteq,
133133
BinaryOp::Lt => EvalBinop::Lt,
134134
BinaryOp::Lteq => EvalBinop::Gteq,
135+
BinaryOp::Add => EvalBinop::Add,
136+
BinaryOp::Sub => EvalBinop::Sub,
137+
BinaryOp::Mul => EvalBinop::Mul,
138+
BinaryOp::Div => EvalBinop::Div,
139+
BinaryOp::Mod => EvalBinop::Mod,
140+
BinaryOp::Exp => EvalBinop::Exp,
135141
};
136142
Box::new(EvalBinopExpr { op, lhs, rhs })
137143
}

partiql-logical/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ pub enum BinaryOp {
3030
Gteq,
3131
Lt,
3232
Lteq,
33+
34+
// Arithmetic ops
35+
Add,
36+
Sub,
37+
Mul,
38+
Div,
39+
Mod,
40+
Exp,
3341
}
3442

3543
#[derive(Clone, Debug)]

partiql-value/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ edition.workspace = true
2323
ordered-float = "3.*"
2424
itertools = "0.10.*"
2525
unicase = "2.*"
26+
rust_decimal = { version = "1.25.0", default-features = false, features = ["std"] }
27+
rust_decimal_macros = "1.26"
2628

2729
[dev-dependencies]
2830
criterion = "0.4"

0 commit comments

Comments
 (0)