partiql_eval/
lib.rs

1#![deny(rust_2018_idioms)]
2#![deny(clippy::all)]
3
4pub mod env;
5pub mod error;
6pub mod eval;
7pub mod plan;
8pub mod test_value;
9
10#[cfg(test)]
11mod tests {
12    use assert_matches::assert_matches;
13
14    use crate::env::basic::MapBindings;
15    use crate::plan;
16    use partiql_catalog::catalog::PartiqlCatalog;
17    use partiql_catalog::context::SystemContext;
18    use rust_decimal_macros::dec;
19
20    use partiql_logical as logical;
21    use partiql_logical::BindingsOp::{Distinct, Project, ProjectAll, ProjectValue};
22
23    use crate::eval::BasicContext;
24    use crate::plan::EvaluationMode;
25    use partiql_logical::{
26        BagExpr, BetweenExpr, BinaryOp, BindingsOp, CoalesceExpr, ExprQuery, IsTypeExpr, JoinKind,
27        ListExpr, LogicalPlan, NullIfExpr, PathComponent, TupleExpr, Type, ValueExpr, VarRefType,
28    };
29    use partiql_value as value;
30    use partiql_value::Value::{Missing, Null};
31    use partiql_value::{bag, list, tuple, Bag, BindingsName, DateTime, List, Tuple, Value};
32
33    fn evaluate(logical: LogicalPlan<BindingsOp>, bindings: MapBindings<Value>) -> Value {
34        let catalog = PartiqlCatalog::default().to_shared_catalog();
35        let mut planner = plan::EvaluatorPlanner::new(EvaluationMode::Permissive, &catalog);
36        let plan = planner.compile(&logical).expect("Expect no plan error");
37        let sys = SystemContext {
38            now: DateTime::from_system_now_utc(),
39        };
40        let ctx = BasicContext::new(bindings, sys);
41        if let Ok(out) = plan.execute(&ctx) {
42            out.result
43        } else {
44            Missing
45        }
46    }
47
48    fn data_customer() -> MapBindings<Value> {
49        fn customer_tuple(id: i64, first_name: &str, balance: i64) -> Value {
50            tuple![("id", id), ("firstName", first_name), ("balance", balance),].into()
51        }
52
53        let customer_val = bag![
54            customer_tuple(5, "jason", 100),
55            customer_tuple(4, "sisko", 0),
56            customer_tuple(3, "jason", -30),
57            customer_tuple(2, "miriam", 20),
58            customer_tuple(1, "miriam", 10),
59        ];
60
61        let mut bindings = MapBindings::default();
62        bindings.insert("customer", customer_val.into());
63        bindings
64    }
65
66    fn data_3_tuple() -> MapBindings<Value> {
67        fn a_tuple(n: i64) -> Value {
68            tuple![("a", n)].into()
69        }
70
71        let data = list![a_tuple(1), a_tuple(2), a_tuple(3)];
72
73        let mut bindings = MapBindings::default();
74        bindings.insert("data", data.into());
75        bindings
76    }
77
78    fn scan(name: &str, as_key: &str) -> BindingsOp {
79        BindingsOp::Scan(logical::Scan {
80            expr: ValueExpr::VarRef(
81                BindingsName::CaseInsensitive(name.to_string().into()),
82                VarRefType::Global,
83            ),
84            as_key: as_key.to_string(),
85            at_key: None,
86        })
87    }
88
89    fn path_var(name: &str, component: &str) -> ValueExpr {
90        ValueExpr::Path(
91            Box::new(ValueExpr::VarRef(
92                BindingsName::CaseInsensitive(name.to_string().into()),
93                VarRefType::Local,
94            )),
95            vec![PathComponent::Key(BindingsName::CaseInsensitive(
96                component.to_string().into(),
97            ))],
98        )
99    }
100
101    fn join_data() -> MapBindings<Value> {
102        let customers = list![
103            tuple![("id", 5), ("name", "Joe")],
104            tuple![("id", 7), ("name", "Mary")],
105        ];
106
107        let orders = list![
108            tuple![("custId", 7), ("productId", 101)],
109            tuple![("custId", 7), ("productId", 523)],
110        ];
111
112        let mut bindings = MapBindings::default();
113        bindings.insert("customers", customers.into());
114        bindings.insert("orders", orders.into());
115        bindings
116    }
117
118    fn join_data_sensors() -> MapBindings<Value> {
119        let sensors = list![
120            tuple![("readings", list![tuple![("v", 1.3)], tuple![("v", 2)],])],
121            tuple![(
122                "readings",
123                list![tuple![("v", 0.7)], tuple![("v", 0.8)], tuple![("v", 0.9)],]
124            )],
125        ];
126        let mut bindings = MapBindings::default();
127        bindings.insert("sensors", sensors.into());
128        bindings
129    }
130
131    fn join_data_sensors_with_empty_table() -> MapBindings<Value> {
132        let sensors = list![
133            tuple![("readings", list![tuple![("v", 1.3)], tuple![("v", 2)],])],
134            tuple![(
135                "readings",
136                list![tuple![("v", 0.7)], tuple![("v", 0.8)], tuple![("v", 0.9)],]
137            )],
138            tuple![("readings", list![])],
139        ];
140        let mut bindings = MapBindings::default();
141        bindings.insert("sensors", sensors.into());
142        bindings
143    }
144
145    fn case_when_data() -> MapBindings<Value> {
146        let nums = list![
147            tuple![("a", 1)],
148            tuple![("a", 2)],
149            tuple![("a", 3)],
150            tuple![("a", Null)],
151            tuple![("a", Missing)],
152            tuple![("a", "foo")],
153        ];
154
155        let mut bindings = MapBindings::default();
156        bindings.insert("nums", nums.into());
157        bindings
158    }
159
160    // Creates the plan: `SELECT <lhs> <op> <rhs> AS result FROM data` where <lhs> comes from data
161    // Evaluates the plan and asserts the result is a bag of the tuple mapping to `expected_first_elem`
162    // (i.e. <<{'result': <expected_first_elem>}>>)
163    // TODO: once eval conformance tests added and/or modified evaluation API (to support other values
164    //  in evaluator output), change or delete tests using this function
165    #[track_caller]
166    fn eval_bin_op<I: Into<logical::Lit>>(
167        op: BinaryOp,
168        lhs: Value,
169        rhs_lit: I,
170        expected_first_elem: Value,
171    ) {
172        let mut plan = LogicalPlan::new();
173        let scan = plan.add_operator(BindingsOp::Scan(logical::Scan {
174            expr: ValueExpr::VarRef(
175                BindingsName::CaseInsensitive("data".into()),
176                VarRefType::Global,
177            ),
178            as_key: "data".to_string(),
179            at_key: None,
180        }));
181
182        let project = plan.add_operator(Project(logical::Project {
183            exprs: Vec::from([(
184                "result".to_string(),
185                ValueExpr::BinaryExpr(
186                    op,
187                    Box::new(ValueExpr::Path(
188                        Box::new(ValueExpr::VarRef(
189                            BindingsName::CaseInsensitive("data".into()),
190                            VarRefType::Local,
191                        )),
192                        vec![PathComponent::Key(BindingsName::CaseInsensitive(
193                            "lhs".to_string().into(),
194                        ))],
195                    )),
196                    Box::new(ValueExpr::Lit(Box::new(rhs_lit.into()))),
197                ),
198            )]),
199        }));
200
201        let sink = plan.add_operator(BindingsOp::Sink);
202        plan.extend_with_flows(&[(scan, project), (project, sink)]);
203
204        let mut bindings = MapBindings::default();
205        bindings.insert("data", list![Tuple::from([("lhs", lhs)])].into());
206
207        let result = evaluate(plan, bindings).coerce_into_bag();
208        assert!(!&result.is_empty());
209        let expected_result = if expected_first_elem != Missing {
210            bag!(Tuple::from([("result", expected_first_elem)]))
211        } else {
212            // Filter tuples with `MISSING` vals
213            bag!(Tuple::new())
214        };
215        assert_eq!(expected_result, result);
216    }
217
218    #[test]
219    fn arithmetic_ops() {
220        // Addition
221        // Plan for `select lhs + rhs as result from data`
222        eval_bin_op(
223            BinaryOp::Add,
224            Value::from(1),
225            Value::from(2),
226            Value::from(3),
227        );
228        eval_bin_op(
229            BinaryOp::Add,
230            Value::from(1),
231            Value::from(2.),
232            Value::from(3.),
233        );
234        eval_bin_op(
235            BinaryOp::Add,
236            Value::from(1.),
237            Value::from(2),
238            Value::from(3.),
239        );
240        eval_bin_op(
241            BinaryOp::Add,
242            Value::from(1.),
243            Value::from(2.),
244            Value::from(3.),
245        );
246        eval_bin_op(
247            BinaryOp::Add,
248            Value::from(1),
249            Value::from(dec!(2.)),
250            Value::from(dec!(3.)),
251        );
252        eval_bin_op(
253            BinaryOp::Add,
254            Value::from(1.),
255            Value::from(dec!(2.)),
256            Value::from(dec!(3.)),
257        );
258        eval_bin_op(
259            BinaryOp::Add,
260            Value::from(dec!(1.)),
261            Value::from(2),
262            Value::from(dec!(3.)),
263        );
264        eval_bin_op(
265            BinaryOp::Add,
266            Value::from(dec!(1.)),
267            Value::from(2.),
268            Value::from(dec!(3.)),
269        );
270        eval_bin_op(
271            BinaryOp::Add,
272            Value::from(dec!(1.)),
273            Value::from(dec!(2.)),
274            Value::from(dec!(3.)),
275        );
276        eval_bin_op(BinaryOp::Add, Null, Null, Null);
277        eval_bin_op(BinaryOp::Add, Missing, Missing, Missing);
278
279        // Subtraction
280        // Plan for `select lhs - rhs as result from data`
281        eval_bin_op(
282            BinaryOp::Sub,
283            Value::from(1),
284            Value::from(2),
285            Value::from(-1),
286        );
287        eval_bin_op(
288            BinaryOp::Sub,
289            Value::from(1),
290            Value::from(2.),
291            Value::from(-1.),
292        );
293        eval_bin_op(
294            BinaryOp::Sub,
295            Value::from(1.),
296            Value::from(2),
297            Value::from(-1.),
298        );
299        eval_bin_op(
300            BinaryOp::Sub,
301            Value::from(1.),
302            Value::from(2.),
303            Value::from(-1.),
304        );
305        eval_bin_op(
306            BinaryOp::Sub,
307            Value::from(1),
308            Value::from(dec!(2.)),
309            Value::from(dec!(-1.)),
310        );
311        eval_bin_op(
312            BinaryOp::Sub,
313            Value::from(1.),
314            Value::from(dec!(2.)),
315            Value::from(dec!(-1.)),
316        );
317        eval_bin_op(
318            BinaryOp::Sub,
319            Value::from(dec!(1.)),
320            Value::from(2),
321            Value::from(dec!(-1.)),
322        );
323        eval_bin_op(
324            BinaryOp::Sub,
325            Value::from(dec!(1.)),
326            Value::from(2.),
327            Value::from(dec!(-1.)),
328        );
329        eval_bin_op(
330            BinaryOp::Sub,
331            Value::from(dec!(1.)),
332            Value::from(dec!(2.)),
333            Value::from(dec!(-1.)),
334        );
335        eval_bin_op(BinaryOp::Sub, Null, Null, Null);
336        eval_bin_op(BinaryOp::Sub, Missing, Missing, Missing);
337
338        // Multiplication
339        // Plan for `select lhs * rhs as result from data`
340        eval_bin_op(
341            BinaryOp::Mul,
342            Value::from(1),
343            Value::from(2),
344            Value::from(2),
345        );
346        eval_bin_op(
347            BinaryOp::Mul,
348            Value::from(1),
349            Value::from(2.),
350            Value::from(2.),
351        );
352        eval_bin_op(
353            BinaryOp::Mul,
354            Value::from(1.),
355            Value::from(2),
356            Value::from(2.),
357        );
358        eval_bin_op(
359            BinaryOp::Mul,
360            Value::from(1.),
361            Value::from(2.),
362            Value::from(2.),
363        );
364        eval_bin_op(
365            BinaryOp::Mul,
366            Value::from(1),
367            Value::from(dec!(2.)),
368            Value::from(dec!(2.)),
369        );
370        eval_bin_op(
371            BinaryOp::Mul,
372            Value::from(1.),
373            Value::from(dec!(2.)),
374            Value::from(dec!(2.)),
375        );
376        eval_bin_op(
377            BinaryOp::Mul,
378            Value::from(dec!(1.)),
379            Value::from(2),
380            Value::from(dec!(2.)),
381        );
382        eval_bin_op(
383            BinaryOp::Mul,
384            Value::from(dec!(1.)),
385            Value::from(2.),
386            Value::from(dec!(2.)),
387        );
388        eval_bin_op(
389            BinaryOp::Mul,
390            Value::from(dec!(1.)),
391            Value::from(dec!(2.)),
392            Value::from(dec!(2.)),
393        );
394        eval_bin_op(BinaryOp::Mul, Null, Null, Null);
395        eval_bin_op(BinaryOp::Mul, Missing, Missing, Missing);
396
397        // Division
398        // Plan for `select lhs / rhs as result from data`
399        eval_bin_op(
400            BinaryOp::Div,
401            Value::from(1),
402            Value::from(2),
403            Value::from(0),
404        );
405        eval_bin_op(
406            BinaryOp::Div,
407            Value::from(1),
408            Value::from(2.),
409            Value::from(0.5),
410        );
411        eval_bin_op(
412            BinaryOp::Div,
413            Value::from(1.),
414            Value::from(2),
415            Value::from(0.5),
416        );
417        eval_bin_op(
418            BinaryOp::Div,
419            Value::from(1.),
420            Value::from(2.),
421            Value::from(0.5),
422        );
423        eval_bin_op(
424            BinaryOp::Div,
425            Value::from(1),
426            Value::from(dec!(2.)),
427            Value::from(dec!(0.5)),
428        );
429        eval_bin_op(
430            BinaryOp::Div,
431            Value::from(1.),
432            Value::from(dec!(2.)),
433            Value::from(dec!(0.5)),
434        );
435        eval_bin_op(
436            BinaryOp::Div,
437            Value::from(dec!(1.)),
438            Value::from(2),
439            Value::from(dec!(0.5)),
440        );
441        eval_bin_op(
442            BinaryOp::Div,
443            Value::from(dec!(1.)),
444            Value::from(2.),
445            Value::from(dec!(0.5)),
446        );
447        eval_bin_op(
448            BinaryOp::Div,
449            Value::from(dec!(1.)),
450            Value::from(dec!(2.)),
451            Value::from(dec!(0.5)),
452        );
453        eval_bin_op(BinaryOp::Div, Null, Null, Null);
454        eval_bin_op(BinaryOp::Div, Missing, Missing, Missing);
455
456        // Modulo
457        // Plan for `select lhs % rhs as result from data`
458        eval_bin_op(
459            BinaryOp::Mod,
460            Value::from(1),
461            Value::from(2),
462            Value::from(1),
463        );
464        eval_bin_op(
465            BinaryOp::Mod,
466            Value::from(1),
467            Value::from(2.),
468            Value::from(1.),
469        );
470        eval_bin_op(
471            BinaryOp::Mod,
472            Value::from(1.),
473            Value::from(2),
474            Value::from(1.),
475        );
476        eval_bin_op(
477            BinaryOp::Mod,
478            Value::from(1.),
479            Value::from(2.),
480            Value::from(1.),
481        );
482        eval_bin_op(
483            BinaryOp::Mod,
484            Value::from(1),
485            Value::from(dec!(2.)),
486            Value::from(dec!(1.)),
487        );
488        eval_bin_op(
489            BinaryOp::Mod,
490            Value::from(1.),
491            Value::from(dec!(2.)),
492            Value::from(dec!(1.)),
493        );
494        eval_bin_op(
495            BinaryOp::Mod,
496            Value::from(dec!(1.)),
497            Value::from(2),
498            Value::from(dec!(1.)),
499        );
500        eval_bin_op(
501            BinaryOp::Mod,
502            Value::from(dec!(1.)),
503            Value::from(2.),
504            Value::from(dec!(1.)),
505        );
506        eval_bin_op(
507            BinaryOp::Mod,
508            Value::from(dec!(1.)),
509            Value::from(dec!(2.)),
510            Value::from(dec!(1.)),
511        );
512        eval_bin_op(BinaryOp::Mod, Null, Null, Null);
513        eval_bin_op(BinaryOp::Mod, Missing, Missing, Missing);
514    }
515
516    #[test]
517    fn in_expr() {
518        eval_bin_op(
519            BinaryOp::In,
520            Value::from(1),
521            Value::from(list![1, 2, 3]),
522            Value::from(true),
523        );
524        // We still need to define the rules of coercion for `IN` RHS.
525        // See also:
526        // - https://github.com/partiql/partiql-docs/pull/13
527        // - https://github.com/partiql/partiql-lang-kotlin/issues/524
528        // - https://github.com/partiql/partiql-lang-kotlin/pull/621#issuecomment-1147754213
529        eval_bin_op(
530            BinaryOp::In,
531            Value::from(tuple![("a", 2)]),
532            Value::from(list![tuple![("a", 6)], tuple![("b", 12)], tuple![("a", 2)]]),
533            Value::from(true),
534        );
535        eval_bin_op(
536            BinaryOp::In,
537            Value::from(10),
538            Value::from(bag!["a", "b", 11]),
539            Value::from(false),
540        );
541        eval_bin_op(BinaryOp::In, Value::from(1), Value::from(1), Null);
542        eval_bin_op(
543            BinaryOp::In,
544            Value::from(1),
545            Value::from(list![10, Missing, "b"]),
546            Null,
547        );
548        eval_bin_op(
549            BinaryOp::In,
550            Missing,
551            Value::from(list![1, Missing, "b"]),
552            Null,
553        );
554        eval_bin_op(
555            BinaryOp::In,
556            Null,
557            Value::from(list![1, Missing, "b"]),
558            Null,
559        );
560        eval_bin_op(
561            BinaryOp::In,
562            Value::from(1),
563            Value::from(list![1, Null, "b"]),
564            Value::from(true),
565        );
566        eval_bin_op(
567            BinaryOp::In,
568            Value::from(1),
569            Value::from(list![3, Null]),
570            Null,
571        );
572    }
573
574    #[test]
575    fn comparison_ops() {
576        // Lt
577        // Plan for `select lhs < rhs as result from data`
578        eval_bin_op(
579            BinaryOp::Lt,
580            Value::from(1),
581            Value::from(2.),
582            Value::from(true),
583        );
584        eval_bin_op(
585            BinaryOp::Lt,
586            Value::from("abc"),
587            Value::from("def"),
588            Value::from(true),
589        );
590        eval_bin_op(BinaryOp::Lt, Missing, Value::from(2.), Missing);
591        eval_bin_op(BinaryOp::Lt, Null, Value::from(2.), Null);
592        eval_bin_op(BinaryOp::Lt, Value::from(1), Value::from("foo"), Missing);
593
594        // Gt
595        // Plan for `select lhs > rhs as result from data`
596        eval_bin_op(
597            BinaryOp::Gt,
598            Value::from(1),
599            Value::from(2.),
600            Value::from(false),
601        );
602        eval_bin_op(
603            BinaryOp::Gt,
604            Value::from("abc"),
605            Value::from("def"),
606            Value::from(false),
607        );
608        eval_bin_op(BinaryOp::Gt, Missing, Value::from(2.), Missing);
609        eval_bin_op(BinaryOp::Gt, Null, Value::from(2.), Null);
610        eval_bin_op(BinaryOp::Gt, Value::from(1), Value::from("foo"), Missing);
611
612        // Lteq
613        // Plan for `select lhs <= rhs as result from data`
614        eval_bin_op(
615            BinaryOp::Lteq,
616            Value::from(1),
617            Value::from(2.),
618            Value::from(true),
619        );
620        eval_bin_op(
621            BinaryOp::Lteq,
622            Value::from("abc"),
623            Value::from("def"),
624            Value::from(true),
625        );
626        eval_bin_op(BinaryOp::Lteq, Missing, Value::from(2.), Missing);
627        eval_bin_op(BinaryOp::Lt, Null, Value::from(2.), Null);
628        eval_bin_op(BinaryOp::Lteq, Value::from(1), Value::from("foo"), Missing);
629
630        // Gteq
631        // Plan for `select lhs >= rhs as result from data`
632        eval_bin_op(
633            BinaryOp::Gteq,
634            Value::from(1),
635            Value::from(2.),
636            Value::from(false),
637        );
638        eval_bin_op(
639            BinaryOp::Gteq,
640            Value::from("abc"),
641            Value::from("def"),
642            Value::from(false),
643        );
644        eval_bin_op(BinaryOp::Gteq, Missing, Value::from(2.), Missing);
645        eval_bin_op(BinaryOp::Gteq, Null, Value::from(2.), Null);
646        eval_bin_op(BinaryOp::Gteq, Value::from(1), Value::from("foo"), Missing);
647    }
648
649    #[test]
650    fn and_or_null() {
651        #[track_caller]
652        fn eval_to_null<I: Into<logical::Lit>>(op: BinaryOp, lhs: I, rhs: I) {
653            let mut plan = LogicalPlan::new();
654            let expq = plan.add_operator(BindingsOp::ExprQuery(ExprQuery {
655                expr: ValueExpr::BinaryExpr(
656                    op,
657                    Box::new(ValueExpr::Lit(Box::new(lhs.into()))),
658                    Box::new(ValueExpr::Lit(Box::new(rhs.into()))),
659                ),
660            }));
661
662            let sink = plan.add_operator(BindingsOp::Sink);
663            plan.add_flow(expq, sink);
664
665            let result = evaluate(plan, MapBindings::default());
666            assert_eq!(result, Value::Null);
667        }
668
669        eval_to_null(BinaryOp::And, Value::Null, Value::Boolean(true));
670        eval_to_null(BinaryOp::And, Value::Missing, Value::Boolean(true));
671        eval_to_null(BinaryOp::And, Value::Boolean(true), Value::Null);
672        eval_to_null(BinaryOp::And, Value::Boolean(true), Value::Missing);
673        eval_to_null(BinaryOp::Or, Value::Null, Value::Boolean(false));
674        eval_to_null(BinaryOp::Or, Value::Missing, Value::Boolean(false));
675        eval_to_null(BinaryOp::Or, Value::Boolean(false), Value::Null);
676        eval_to_null(BinaryOp::Or, Value::Boolean(false), Value::Missing);
677    }
678
679    #[test]
680    fn between_op() {
681        #[track_caller]
682        fn eval_between_op(value: Value, from: Value, to: Value, expected_first_elem: Value) {
683            let mut plan = LogicalPlan::new();
684            let scan = plan.add_operator(BindingsOp::Scan(logical::Scan {
685                expr: ValueExpr::VarRef(
686                    BindingsName::CaseInsensitive("data".into()),
687                    VarRefType::Global,
688                ),
689                as_key: "data".to_string(),
690                at_key: None,
691            }));
692
693            let project = plan.add_operator(Project(logical::Project {
694                exprs: Vec::from([(
695                    "result".to_string(),
696                    ValueExpr::BetweenExpr(BetweenExpr {
697                        value: Box::new(ValueExpr::Path(
698                            Box::new(ValueExpr::VarRef(
699                                BindingsName::CaseInsensitive("data".into()),
700                                VarRefType::Local,
701                            )),
702                            vec![PathComponent::Key(BindingsName::CaseInsensitive(
703                                "value".to_string().into(),
704                            ))],
705                        )),
706                        from: Box::new(ValueExpr::Lit(Box::new(from.into()))),
707                        to: Box::new(ValueExpr::Lit(Box::new(to.into()))),
708                    }),
709                )]),
710            }));
711
712            let sink = plan.add_operator(BindingsOp::Sink);
713            plan.extend_with_flows(&[(scan, project), (project, sink)]);
714
715            let mut bindings = MapBindings::default();
716            bindings.insert("data", list![Tuple::from([("value", value)])].into());
717
718            let result = evaluate(plan, bindings).coerce_into_bag();
719            assert!(!&result.is_empty());
720            let expected_result = bag!(Tuple::from([("result", expected_first_elem)]));
721            assert_eq!(expected_result, result);
722        }
723        eval_between_op(
724            Value::from(2),
725            Value::from(1),
726            Value::from(3),
727            Value::from(true),
728        );
729        eval_between_op(
730            Value::from(2),
731            Value::from(1.),
732            Value::from(dec!(3.)),
733            Value::from(true),
734        );
735        eval_between_op(
736            Value::from(1),
737            Value::from(2),
738            Value::from(3),
739            Value::from(false),
740        );
741        eval_between_op(Null, Value::from(1), Value::from(3), Null);
742        eval_between_op(Value::from(2), Null, Value::from(3), Null);
743        eval_between_op(Value::from(2), Value::from(1), Null, Null);
744        eval_between_op(Missing, Value::from(1), Value::from(3), Null);
745        eval_between_op(Value::from(2), Missing, Value::from(3), Null);
746        eval_between_op(Value::from(2), Value::from(1), Missing, Null);
747        // left part of AND evaluates to false
748        eval_between_op(Value::from(1), Value::from(2), Null, Value::from(false));
749        eval_between_op(Value::from(1), Value::from(2), Missing, Value::from(false));
750        // right part of AND evaluates to false
751        eval_between_op(Value::from(2), Null, Value::from(1), Value::from(false));
752        eval_between_op(Value::from(2), Missing, Value::from(1), Value::from(false));
753    }
754
755    #[test]
756    fn select_with_join_and_on() {
757        // Similar to ex 9 from spec with projected columns from different tables with an inner JOIN and ON condition
758        // SELECT c.id, c.name, o.custId, o.productId FROM customers AS c, orders AS o ON c.id = o.custId
759        let from_lhs = scan("customers", "c");
760        let from_rhs = scan("orders", "o");
761
762        let mut lg = LogicalPlan::new();
763        let project = lg.add_operator(Project(logical::Project {
764            exprs: Vec::from([
765                ("id".to_string(), path_var("c", "id")),
766                ("name".to_string(), path_var("c", "name")),
767                ("custId".to_string(), path_var("o", "custId")),
768                ("productId".to_string(), path_var("o", "productId")),
769            ]),
770        }));
771
772        let join = lg.add_operator(BindingsOp::Join(logical::Join {
773            kind: JoinKind::Cross,
774            left: Box::new(from_lhs),
775            right: Box::new(from_rhs),
776            on: Some(ValueExpr::BinaryExpr(
777                BinaryOp::Eq,
778                Box::new(path_var("c", "id")),
779                Box::new(path_var("o", "custId")),
780            )),
781        }));
782
783        let sink = lg.add_operator(BindingsOp::Sink);
784        lg.add_flow_with_branch_num(join, project, 0);
785        lg.add_flow_with_branch_num(project, sink, 0);
786
787        let out = evaluate(lg, join_data());
788        println!("{:?}", &out);
789
790        assert_matches!(out, Value::Bag(bag) => {
791            let expected = bag![
792                tuple![("custId", 7), ("name", "Mary"), ("id", 7), ("productId", 101)],
793                tuple![("custId", 7), ("name", "Mary"), ("id", 7), ("productId", 523)],
794            ];
795            assert_eq!(*bag, expected);
796        });
797    }
798
799    #[test]
800    fn select_with_cross_join_sensors() {
801        // Similar to example 10 from PartiQL spec. Equivalent to query:
802        //  SELECT r.v AS v FROM sensors AS s, s.readings AS r
803        // Above demonstrates LATERAL JOINs since the RHS of the JOIN uses bindings defined from the
804        // LHS scan
805        let mut lg = LogicalPlan::new();
806
807        let from_lhs = scan("sensors", "s");
808        let from_rhs = BindingsOp::Scan(logical::Scan {
809            expr: path_var("s", "readings"),
810            as_key: "r".to_string(),
811            at_key: None,
812        });
813
814        let project = lg.add_operator(Project(logical::Project {
815            exprs: Vec::from([("v".to_string(), path_var("r", "v"))]),
816        }));
817
818        let join = lg.add_operator(BindingsOp::Join(logical::Join {
819            kind: JoinKind::Cross,
820            left: Box::new(from_lhs),
821            right: Box::new(from_rhs),
822            on: None,
823        }));
824
825        let sink = lg.add_operator(BindingsOp::Sink);
826        lg.add_flow_with_branch_num(join, project, 0);
827        lg.add_flow_with_branch_num(project, sink, 0);
828
829        let out = evaluate(lg, join_data_sensors());
830        println!("{:?}", &out);
831
832        assert_matches!(out, Value::Bag(bag) => {
833            let expected = bag![
834                tuple![("v", 1.3)],
835                tuple![("v", 2)],
836                tuple![("v", 0.7)],
837                tuple![("v", 0.8)],
838                tuple![("v", 0.9)],
839            ];
840            assert_eq!(*bag, expected);
841        });
842    }
843
844    #[test]
845    fn select_with_cross_join_sensors_with_empty_table() {
846        // Similar to example 11 from PartiQL spec. Equivalent to query:
847        //  SELECT r.v AS v FROM sensors AS s, s.readings AS r
848        // Above uses a different `sensors` table which includes an empty list for `readings` than
849        // example 10. This demonstrates that binding tuple is excluded for CROSS JOINs
850        let mut lg = LogicalPlan::new();
851
852        let from_lhs = scan("sensors", "s");
853        let from_rhs = BindingsOp::Scan(logical::Scan {
854            expr: path_var("s", "readings"),
855            as_key: "r".to_string(),
856            at_key: None,
857        });
858
859        let project = lg.add_operator(Project(logical::Project {
860            exprs: Vec::from([("v".to_string(), path_var("r", "v"))]),
861        }));
862
863        let join = lg.add_operator(BindingsOp::Join(logical::Join {
864            kind: JoinKind::Cross,
865            left: Box::new(from_lhs),
866            right: Box::new(from_rhs),
867            on: None,
868        }));
869
870        let sink = lg.add_operator(BindingsOp::Sink);
871        lg.add_flow_with_branch_num(join, project, 0);
872        lg.add_flow_with_branch_num(project, sink, 0);
873
874        let out = evaluate(lg, join_data_sensors_with_empty_table());
875        println!("{:?}", &out);
876
877        assert_matches!(out, Value::Bag(bag) => {
878            let expected = bag![
879                tuple![("v", 1.3)],
880                tuple![("v", 2)],
881                tuple![("v", 0.7)],
882                tuple![("v", 0.8)],
883                tuple![("v", 0.9)],
884            ];
885            assert_eq!(*bag, expected);
886        });
887    }
888
889    #[test]
890    fn select_with_left_join_sensors_with_empty_table() {
891        // Similar to example 11 from PartiQL spec. Equivalent to query:
892        //  SELECT r AS r FROM sensors AS s LEFT CROSS JOIN s.readings AS r
893        // Above uses a different `sensors` table which includes an empty list for `readings` than
894        // example 10. This demonstrates that empty binding tuples are included for LEFT (CROSS)
895        // JOINs and defined by a binding tuple with each variable mapping to NULL (see the last
896        // tuple in the result).
897        let mut lg = LogicalPlan::new();
898
899        let from_lhs = scan("sensors", "s");
900        let from_rhs = BindingsOp::Scan(logical::Scan {
901            expr: path_var("s", "readings"),
902            as_key: "r".to_string(),
903            at_key: None,
904        });
905
906        let project = lg.add_operator(Project(logical::Project {
907            exprs: Vec::from([(
908                "r".to_string(),
909                ValueExpr::VarRef(BindingsName::CaseInsensitive("r".into()), VarRefType::Local),
910            )]),
911        }));
912
913        let join = lg.add_operator(BindingsOp::Join(logical::Join {
914            kind: JoinKind::Left,
915            left: Box::new(from_lhs),
916            right: Box::new(from_rhs),
917            on: Some(ValueExpr::Lit(Box::new(Value::from(true).into()))),
918        }));
919
920        let sink = lg.add_operator(BindingsOp::Sink);
921        lg.add_flow_with_branch_num(join, project, 0);
922        lg.add_flow_with_branch_num(project, sink, 0);
923
924        let out = evaluate(lg, join_data_sensors_with_empty_table());
925        println!("{:?}", &out);
926
927        assert_matches!(out, Value::Bag(bag) => {
928            let expected = bag![
929                tuple![("r", tuple![("v", 1.3)])],
930                tuple![("r", tuple![("v", 2)])],
931                tuple![("r", tuple![("v", 0.7)])],
932                tuple![("r", tuple![("v", 0.8)])],
933                tuple![("r", tuple![("v", 0.9)])],
934                tuple![("r", Null)],
935            ];
936            assert_eq!(*bag, expected);
937        });
938    }
939
940    fn simple_case_expr_with_default() -> logical::SimpleCase {
941        logical::SimpleCase {
942            expr: Box::new(path_var("n", "a")),
943            cases: vec![
944                (
945                    Box::new(ValueExpr::Lit(Box::new(Value::Integer(1).into()))),
946                    Box::new(ValueExpr::Lit(Box::new(
947                        Value::from("one".to_string()).into(),
948                    ))),
949                ),
950                (
951                    Box::new(ValueExpr::Lit(Box::new(Value::Integer(2).into()))),
952                    Box::new(ValueExpr::Lit(Box::new(
953                        Value::from("two".to_string()).into(),
954                    ))),
955                ),
956            ],
957            default: Some(Box::new(ValueExpr::Lit(Box::new(
958                Value::from("other".to_string()).into(),
959            )))),
960        }
961    }
962
963    fn searched_case_expr_with_default() -> logical::SearchedCase {
964        logical::SearchedCase {
965            cases: vec![
966                (
967                    Box::new(ValueExpr::BinaryExpr(
968                        BinaryOp::Eq,
969                        Box::new(path_var("n", "a")),
970                        Box::new(ValueExpr::Lit(Box::new(Value::Integer(1).into()))),
971                    )),
972                    Box::new(ValueExpr::Lit(Box::new(
973                        Value::from("one".to_string()).into(),
974                    ))),
975                ),
976                (
977                    Box::new(ValueExpr::BinaryExpr(
978                        BinaryOp::Eq,
979                        Box::new(path_var("n", "a")),
980                        Box::new(ValueExpr::Lit(Box::new(Value::Integer(2).into()))),
981                    )),
982                    Box::new(ValueExpr::Lit(Box::new(
983                        Value::from("two".to_string()).into(),
984                    ))),
985                ),
986            ],
987            default: Some(Box::new(ValueExpr::Lit(Box::new(
988                Value::from("other".to_string()).into(),
989            )))),
990        }
991    }
992
993    #[test]
994    fn simple_case_when_expr_with_default() {
995        let mut lg = LogicalPlan::new();
996        // SELECT n.a,
997        //        CASE n.a WHEN 1 THEN 'one'
998        //               WHEN 2 THEN 'two'
999        //               ELSE 'other'
1000        //        END AS b
1001        // FROM nums AS n
1002        let scan = lg.add_operator(scan("nums", "n"));
1003
1004        let project_logical = Project(logical::Project {
1005            exprs: Vec::from([
1006                ("a".to_string(), path_var("n", "a")),
1007                (
1008                    "b".to_string(),
1009                    ValueExpr::SimpleCase(simple_case_expr_with_default()),
1010                ),
1011            ]),
1012        });
1013        let project = lg.add_operator(project_logical);
1014        let sink = lg.add_operator(BindingsOp::Sink);
1015        lg.add_flow(scan, project);
1016        lg.add_flow(project, sink);
1017
1018        let out = evaluate(lg, case_when_data());
1019        println!("{:?}", &out);
1020
1021        assert_matches!(out, Value::Bag(bag) => {
1022            let expected = bag![
1023                tuple![("a", 1), ("b", "one")],
1024                tuple![("a", 2), ("b", "two")],
1025                tuple![("a", 3), ("b", "other")],
1026                tuple![("a", Null), ("b", "other")],
1027                tuple![("b", "other")],
1028                tuple![("a", "foo"), ("b", "other")],
1029            ];
1030            assert_eq!(*bag, expected);
1031        });
1032    }
1033
1034    #[test]
1035    fn simple_case_when_expr_without_default() {
1036        let mut lg = LogicalPlan::new();
1037        // SELECT n.a,
1038        //        CASE n.a WHEN 1 THEN 'one'
1039        //               WHEN 2 THEN 'two'
1040        //        END AS b
1041        // FROM nums AS n
1042        let scan = lg.add_operator(scan("nums", "n"));
1043        let project_logical_no_default = Project(logical::Project {
1044            exprs: Vec::from([
1045                ("a".to_string(), path_var("n", "a")),
1046                (
1047                    "b".to_string(),
1048                    ValueExpr::SimpleCase(logical::SimpleCase {
1049                        default: None,
1050                        ..simple_case_expr_with_default()
1051                    }),
1052                ),
1053            ]),
1054        });
1055        let project = lg.add_operator(project_logical_no_default);
1056        let sink = lg.add_operator(BindingsOp::Sink);
1057        lg.add_flow(scan, project);
1058        lg.add_flow(project, sink);
1059
1060        let out = evaluate(lg, case_when_data());
1061        println!("{:?}", &out);
1062
1063        assert_matches!(out, Value::Bag(bag) => {
1064            let expected = bag![
1065                tuple![("a", 1), ("b", "one")],
1066                tuple![("a", 2), ("b", "two")],
1067                tuple![("a", 3), ("b", Null)],
1068                tuple![("a", Null), ("b", Null)],
1069                tuple![("b", Null)],
1070                tuple![("a", "foo"), ("b", Null)],
1071            ];
1072            assert_eq!(*bag, expected);
1073        });
1074    }
1075
1076    #[test]
1077    fn searched_case_when_expr_with_default() {
1078        let mut lg = LogicalPlan::new();
1079        // SELECT n.a,
1080        //        CASE WHEN n.a = 1 THEN 'one'
1081        //             WHEN n.a = 2 THEN 'two'
1082        //             ELSE 'other'
1083        //        END AS b
1084        // FROM nums AS n
1085        let scan = lg.add_operator(scan("nums", "n"));
1086
1087        let project_logical = Project(logical::Project {
1088            exprs: Vec::from([
1089                ("a".to_string(), path_var("n", "a")),
1090                (
1091                    "b".to_string(),
1092                    ValueExpr::SearchedCase(searched_case_expr_with_default()),
1093                ),
1094            ]),
1095        });
1096        let project = lg.add_operator(project_logical);
1097        let sink = lg.add_operator(BindingsOp::Sink);
1098        lg.add_flow(scan, project);
1099        lg.add_flow(project, sink);
1100
1101        let out = evaluate(lg, case_when_data());
1102        println!("{:?}", &out);
1103
1104        assert_matches!(out, Value::Bag(bag) => {
1105            let expected = bag![
1106                tuple![("a", 1), ("b", "one")],
1107                tuple![("a", 2), ("b", "two")],
1108                tuple![("a", 3), ("b", "other")],
1109                tuple![("a", Null), ("b", "other")],
1110                tuple![("b", "other")],
1111                tuple![("a", "foo"), ("b", "other")],
1112            ];
1113            assert_eq!(*bag, expected);
1114        });
1115    }
1116
1117    #[test]
1118    fn searched_case_when_expr_without_default() {
1119        let mut lg = LogicalPlan::new();
1120        // SELECT n.a,
1121        //        CASE WHEN n.a = 1 THEN 'one'
1122        //             WHEN n.a = 2 THEN 'two'
1123        //        END AS b
1124        // FROM nums AS n
1125        let scan = lg.add_operator(scan("nums", "n"));
1126        let project_logical_no_default = Project(logical::Project {
1127            exprs: Vec::from([
1128                ("a".to_string(), path_var("n", "a")),
1129                (
1130                    "b".to_string(),
1131                    ValueExpr::SearchedCase(logical::SearchedCase {
1132                        default: None,
1133                        ..searched_case_expr_with_default()
1134                    }),
1135                ),
1136            ]),
1137        });
1138        let project = lg.add_operator(project_logical_no_default);
1139        let sink = lg.add_operator(BindingsOp::Sink);
1140        lg.add_flow(scan, project);
1141        lg.add_flow(project, sink);
1142
1143        let out = evaluate(lg, case_when_data());
1144        println!("{:?}", &out);
1145
1146        assert_matches!(out, Value::Bag(bag) => {
1147            let expected = bag![
1148                tuple![("a", 1), ("b", "one")],
1149                tuple![("a", 2), ("b", "two")],
1150                tuple![("a", 3), ("b", Null)],
1151                tuple![("a", Null), ("b", Null)],
1152                tuple![("b", Null)],
1153                tuple![("a", "foo"), ("b", Null)],
1154            ];
1155            assert_eq!(*bag, expected);
1156        });
1157    }
1158
1159    // Creates the plan: `SELECT <expr> IS [<not>] <is_type> AS result FROM data` where <expr> comes from data
1160    // Evaluates the plan and asserts the result is a bag of the tuple mapping to `expected_first_elem`
1161    // (i.e. <<{'result': <expected_first_elem>}>>)
1162    fn eval_is_op(not: bool, expr: Value, is_type: Type, expected_first_elem: Value) {
1163        let mut plan = LogicalPlan::new();
1164        let scan = plan.add_operator(BindingsOp::Scan(logical::Scan {
1165            expr: ValueExpr::VarRef(
1166                BindingsName::CaseInsensitive("data".into()),
1167                VarRefType::Global,
1168            ),
1169            as_key: "data".to_string(),
1170            at_key: None,
1171        }));
1172
1173        let project = plan.add_operator(Project(logical::Project {
1174            exprs: Vec::from([(
1175                "result".to_string(),
1176                ValueExpr::IsTypeExpr(IsTypeExpr {
1177                    not,
1178                    expr: Box::new(ValueExpr::Path(
1179                        Box::new(ValueExpr::VarRef(
1180                            BindingsName::CaseInsensitive("data".into()),
1181                            VarRefType::Local,
1182                        )),
1183                        vec![PathComponent::Key(BindingsName::CaseInsensitive(
1184                            "expr".to_string().into(),
1185                        ))],
1186                    )),
1187                    is_type,
1188                }),
1189            )]),
1190        }));
1191
1192        let sink = plan.add_operator(BindingsOp::Sink);
1193        plan.extend_with_flows(&[(scan, project), (project, sink)]);
1194
1195        let mut bindings = MapBindings::default();
1196        bindings.insert("data", list![Tuple::from([("expr", expr)])].into());
1197
1198        let result = evaluate(plan, bindings).coerce_into_bag();
1199        assert!(!&result.is_empty());
1200        assert_eq!(bag!(Tuple::from([("result", expected_first_elem)])), result);
1201    }
1202
1203    #[test]
1204    fn is_type_null_missing() {
1205        // IS MISSING
1206        eval_is_op(false, Value::from(1), Type::MissingType, Value::from(false));
1207        eval_is_op(false, Value::Missing, Type::MissingType, Value::from(true));
1208        eval_is_op(false, Value::Null, Type::MissingType, Value::from(false));
1209
1210        // IS NOT MISSING
1211        eval_is_op(true, Value::from(1), Type::MissingType, Value::from(true));
1212        eval_is_op(true, Value::Missing, Type::MissingType, Value::from(false));
1213        eval_is_op(true, Value::Null, Type::MissingType, Value::from(true));
1214
1215        // IS NULL
1216        eval_is_op(false, Value::from(1), Type::NullType, Value::from(false));
1217        eval_is_op(false, Value::Missing, Type::NullType, Value::from(true));
1218        eval_is_op(false, Value::Null, Type::NullType, Value::from(true));
1219
1220        // IS NOT NULL
1221        eval_is_op(true, Value::from(1), Type::NullType, Value::from(true));
1222        eval_is_op(true, Value::Missing, Type::NullType, Value::from(false));
1223        eval_is_op(true, Value::Null, Type::NullType, Value::from(false));
1224    }
1225
1226    // Creates the plan: `SELECT NULLIF(<lhs>, <rhs>) AS result FROM data` where <lhs> comes from data
1227    // Evaluates the plan and asserts the result is a bag of the tuple mapping to `expected_first_elem`
1228    // (i.e. <<{'result': <expected_first_elem>}>>)
1229    fn eval_null_if_op(lhs: Value, rhs: Value, expected_first_elem: Value) {
1230        let mut plan = LogicalPlan::new();
1231        let scan = plan.add_operator(BindingsOp::Scan(logical::Scan {
1232            expr: ValueExpr::VarRef(
1233                BindingsName::CaseInsensitive("data".into()),
1234                VarRefType::Global,
1235            ),
1236            as_key: "data".to_string(),
1237            at_key: None,
1238        }));
1239
1240        let project = plan.add_operator(Project(logical::Project {
1241            exprs: Vec::from([(
1242                "result".to_string(),
1243                ValueExpr::NullIfExpr(NullIfExpr {
1244                    lhs: Box::new(ValueExpr::Path(
1245                        Box::new(ValueExpr::VarRef(
1246                            BindingsName::CaseInsensitive("data".into()),
1247                            VarRefType::Local,
1248                        )),
1249                        vec![PathComponent::Key(BindingsName::CaseInsensitive(
1250                            "lhs".to_string().into(),
1251                        ))],
1252                    )),
1253                    rhs: Box::new(ValueExpr::Lit(Box::new(rhs.into()))),
1254                }),
1255            )]),
1256        }));
1257
1258        let sink = plan.add_operator(BindingsOp::Sink);
1259        plan.extend_with_flows(&[(scan, project), (project, sink)]);
1260
1261        let mut bindings = MapBindings::default();
1262        bindings.insert("data", list![Tuple::from([("lhs", lhs)])].into());
1263
1264        let result = evaluate(plan, bindings).coerce_into_bag();
1265        assert!(!&result.is_empty());
1266        let expected_result = if expected_first_elem != Missing {
1267            bag!(Tuple::from([("result", expected_first_elem)]))
1268        } else {
1269            // Filter tuples with `MISSING` vals
1270            bag!(Tuple::new())
1271        };
1272        assert_eq!(expected_result, result);
1273    }
1274
1275    #[test]
1276    fn test_null_if_op() {
1277        eval_null_if_op(Value::from(1), Value::from(1), Value::Null);
1278        eval_null_if_op(Value::from(1), Value::from("foo"), Value::from(1));
1279        eval_null_if_op(Value::from("foo"), Value::from(1), Value::from("foo"));
1280        eval_null_if_op(Null, Null, Value::Null);
1281        eval_null_if_op(Missing, Null, Value::Missing);
1282        eval_null_if_op(Null, Missing, Value::Null);
1283    }
1284
1285    // Creates the plan: `SELECT COALESCE(data.arg1, data.arg2, ..., argN) AS result FROM data` where arg1...argN comes from data
1286    // Evaluates the plan and asserts the result is a bag of the tuple mapping to `expected_first_elem`
1287    // (i.e. <<{'result': <expected_first_elem>}>>)
1288    fn eval_coalesce_op(elements: Vec<Value>, expected_first_elem: Value) {
1289        let mut plan = LogicalPlan::new();
1290        let scan = plan.add_operator(BindingsOp::Scan(logical::Scan {
1291            expr: ValueExpr::VarRef(
1292                BindingsName::CaseInsensitive("data".into()),
1293                VarRefType::Global,
1294            ),
1295            as_key: "data".to_string(),
1296            at_key: None,
1297        }));
1298
1299        fn index_to_valueexpr(i: usize) -> ValueExpr {
1300            ValueExpr::Path(
1301                Box::new(ValueExpr::VarRef(
1302                    BindingsName::CaseInsensitive("data".into()),
1303                    VarRefType::Local,
1304                )),
1305                vec![PathComponent::Key(BindingsName::CaseInsensitive(
1306                    format!("arg{i}").into(),
1307                ))],
1308            )
1309        }
1310
1311        let project = plan.add_operator(Project(logical::Project {
1312            exprs: Vec::from([(
1313                "result".to_string(),
1314                ValueExpr::CoalesceExpr(CoalesceExpr {
1315                    elements: (0..elements.len()).map(index_to_valueexpr).collect(),
1316                }),
1317            )]),
1318        }));
1319
1320        let sink = plan.add_operator(BindingsOp::Sink);
1321        plan.extend_with_flows(&[(scan, project), (project, sink)]);
1322
1323        let mut bindings = MapBindings::default();
1324        let mut data = Tuple::new();
1325        // Bindings created from `elements`. For each `e` in elements with index `i`, adds tuple pair ('argi', e)
1326        elements
1327            .into_iter()
1328            .enumerate()
1329            .for_each(|(i, e)| data.insert(&format!("arg{i}"), e));
1330        bindings.insert("data", list![data].into());
1331
1332        let result = evaluate(plan, bindings).coerce_into_bag();
1333        assert!(!&result.is_empty());
1334        assert_eq!(bag!(Tuple::from([("result", expected_first_elem)])), result);
1335    }
1336
1337    #[test]
1338    fn test_coalesce_op() {
1339        // 1 elem
1340        eval_coalesce_op(vec![Value::from(1)], Value::from(1));
1341        eval_coalesce_op(vec![Null], Null);
1342        eval_coalesce_op(vec![Missing], Null);
1343
1344        // Multiple elems
1345        eval_coalesce_op(vec![Missing, Null, Value::from(1)], Value::from(1));
1346        eval_coalesce_op(vec![Missing, Null, Value::from(1)], Value::from(1));
1347        eval_coalesce_op(vec![Missing, Null, Null], Null);
1348        eval_coalesce_op(
1349            vec![
1350                Missing,
1351                Null,
1352                Missing,
1353                Null,
1354                Missing,
1355                Null,
1356                Value::from(1),
1357                Value::from(2),
1358                Missing,
1359                Null,
1360            ],
1361            Value::from(1),
1362        );
1363        eval_coalesce_op(
1364            vec![
1365                Missing, Null, Missing, Null, Missing, Null, Missing, Null, Missing, Null,
1366            ],
1367            Null,
1368        );
1369    }
1370
1371    #[test]
1372    fn expr_query() {
1373        let mut lg = LogicalPlan::new();
1374        let expq = lg.add_operator(BindingsOp::ExprQuery(ExprQuery {
1375            expr: ValueExpr::BinaryExpr(
1376                BinaryOp::Add,
1377                Box::new(ValueExpr::Lit(Box::new(40.into()))),
1378                Box::new(ValueExpr::Lit(Box::new(2.into()))),
1379            ),
1380        }));
1381
1382        let sink = lg.add_operator(BindingsOp::Sink);
1383
1384        lg.add_flow(expq, sink);
1385
1386        let out = evaluate(lg, MapBindings::default());
1387        println!("{:?}", &out);
1388        assert_matches!(out, Value::Integer(42));
1389    }
1390
1391    #[test]
1392    fn paths() {
1393        fn test(expr: ValueExpr, expected: Value) {
1394            let mut lg = LogicalPlan::new();
1395            let expq = lg.add_operator(BindingsOp::ExprQuery(ExprQuery { expr }));
1396
1397            let sink = lg.add_operator(BindingsOp::Sink);
1398            lg.add_flow(expq, sink);
1399
1400            let out = evaluate(lg, MapBindings::default());
1401            println!("{:?}", &out);
1402            assert_eq!(out, expected);
1403        }
1404        let list = ValueExpr::Lit(Box::new(Value::List(Box::new(list![1, 2, 3])).into()));
1405
1406        // `[1,2,3][0]` -> `1`
1407        let index = ValueExpr::Path(Box::new(list.clone()), vec![PathComponent::Index(0)]);
1408        test(index, Value::Integer(1));
1409
1410        // `[1,2,3][1+1]` -> `3`
1411        let index_expr = ValueExpr::BinaryExpr(
1412            BinaryOp::Add,
1413            Box::new(ValueExpr::Lit(Box::new(1.into()))),
1414            Box::new(ValueExpr::Lit(Box::new(1.into()))),
1415        );
1416        let index = ValueExpr::Path(
1417            Box::new(list),
1418            vec![PathComponent::IndexExpr(Box::new(index_expr))],
1419        );
1420        test(index, Value::Integer(3));
1421
1422        // `{'a':10}[''||'a']` -> `10`
1423        let tuple = ValueExpr::Lit(Box::new(Value::Tuple(Box::new(tuple![("a", 10)])).into()));
1424        let index_expr = ValueExpr::BinaryExpr(
1425            BinaryOp::Concat,
1426            Box::new(ValueExpr::Lit(Box::new("".into()))),
1427            Box::new(ValueExpr::Lit(Box::new("a".into()))),
1428        );
1429        let index = ValueExpr::Path(
1430            Box::new(tuple),
1431            vec![PathComponent::KeyExpr(Box::new(index_expr))],
1432        );
1433        test(index, Value::Integer(10));
1434    }
1435
1436    #[test]
1437    fn select() {
1438        let mut lg = LogicalPlan::new();
1439
1440        let from = lg.add_operator(scan("data", "data"));
1441
1442        let project = lg.add_operator(Project(logical::Project {
1443            exprs: Vec::from([(
1444                "b".to_string(),
1445                ValueExpr::Path(
1446                    Box::new(ValueExpr::VarRef(
1447                        BindingsName::CaseInsensitive("data".into()),
1448                        VarRefType::Local,
1449                    )),
1450                    vec![PathComponent::Key(BindingsName::CaseInsensitive(
1451                        "a".to_string().into(),
1452                    ))],
1453                ),
1454            )]),
1455        }));
1456
1457        let sink = lg.add_operator(BindingsOp::Sink);
1458
1459        lg.add_flow(from, project);
1460        lg.add_flow(project, sink);
1461
1462        let out = evaluate(lg, data_3_tuple());
1463        println!("{:?}", &out);
1464        assert_matches!(out, Value::Bag(bag) => {
1465            let expected = bag![
1466                tuple![("b", 1)],
1467                tuple![("b", 2)],
1468                tuple![("b", 3)],
1469            ];
1470            assert_eq!(*bag, expected);
1471        });
1472    }
1473
1474    #[test]
1475    fn select_star() {
1476        // TODO `SELECT *` is underspecified w.r.t. nested data
1477        let mut lg = LogicalPlan::new();
1478
1479        let from = lg.add_operator(scan("data", "data"));
1480
1481        let project = lg.add_operator(ProjectAll(Default::default()));
1482
1483        let sink = lg.add_operator(BindingsOp::Sink);
1484
1485        lg.add_flow(from, project);
1486        lg.add_flow(project, sink);
1487
1488        let out = evaluate(lg, data_3_tuple());
1489        println!("{:?}", &out);
1490        assert_matches!(out, Value::Bag(bag) => {
1491            let expected = bag![
1492                tuple![("a", 1)],
1493                tuple![("a", 2)],
1494                tuple![("a", 3)],
1495            ];
1496            assert_eq!(*bag, expected);
1497        });
1498    }
1499
1500    // Spec 6.1:
1501    //  SELECT VALUE 2*v.a
1502    //  FROM [{'a': 1}, {'a': 2}, {'a': 3}] AS v;
1503    //  Expected: <<2, 4, 6>>
1504    #[test]
1505    fn select_value() {
1506        // Plan for `select value a as b from data`
1507        let mut lg = LogicalPlan::new();
1508
1509        let from = lg.add_operator(scan("data", "v"));
1510
1511        let va = path_var("v", "a");
1512        let select_value = lg.add_operator(ProjectValue(logical::ProjectValue {
1513            expr: ValueExpr::BinaryExpr(
1514                BinaryOp::Mul,
1515                Box::new(va),
1516                Box::new(ValueExpr::Lit(Box::new(Value::Integer(2).into()))),
1517            ),
1518        }));
1519
1520        let sink = lg.add_operator(BindingsOp::Sink);
1521
1522        lg.add_flow(from, select_value);
1523        lg.add_flow(select_value, sink);
1524
1525        let out = evaluate(lg, data_3_tuple());
1526        println!("{:?}", &out);
1527        assert_matches!(out, Value::Bag(bag) => {
1528            let expected = bag![2, 4, 6];
1529            assert_eq!(*bag, expected);
1530        });
1531    }
1532
1533    // Spec 6.1.1 — Tuple constructors
1534    //  SELECT VALUE {'a': v.a, 'b': v.b}
1535    //  FROM [{'a': 1, 'b': 1}, {'a': 2, 'b': 2}] AS v;
1536    //  Expected: <<{'a': 1, 'b': 1}, {'a': 2, 'b': 2}>>
1537    #[test]
1538    fn select_value_tuple_constructor_1() {
1539        let mut lg = LogicalPlan::new();
1540
1541        let from = lg.add_operator(scan("data", "v"));
1542
1543        let va = path_var("v", "a");
1544        let vb = path_var("v", "b");
1545
1546        let mut tuple_expr = TupleExpr::new();
1547        tuple_expr.attrs.push(ValueExpr::Lit(Box::new("a".into())));
1548        tuple_expr.attrs.push(ValueExpr::Lit(Box::new("b".into())));
1549        tuple_expr.values.push(va);
1550        tuple_expr.values.push(vb);
1551
1552        let select_value = lg.add_operator(ProjectValue(logical::ProjectValue {
1553            expr: ValueExpr::TupleExpr(tuple_expr),
1554        }));
1555
1556        let sink = lg.add_operator(BindingsOp::Sink);
1557
1558        lg.add_flow(from, select_value);
1559        lg.add_flow(select_value, sink);
1560
1561        let data = bag![tuple![("a", 1), ("b", 1)], tuple![("a", 2), ("b", 2)],];
1562
1563        let mut bindings: MapBindings<Value> = MapBindings::default();
1564        bindings.insert("data", data.into());
1565
1566        let out = evaluate(lg, bindings);
1567        println!("{:?}", &out);
1568        assert_matches!(out, Value::Bag(bag) => {
1569            let expected = bag![
1570                tuple![("a", 1), ("b", 1)],
1571                tuple![("a", 2), ("b", 2)],
1572            ];
1573            assert_eq!(*bag, expected);
1574        });
1575    }
1576
1577    // Spec 6.1.1 — Tuple constructors
1578    //  SELECT VALUE {'test': 2*v.a }
1579    //  FROM [{'a': 1}, {'a': 2}, {'a': 3}] AS v;
1580    //  Expected: <<{'test': 2}, {'test': 4}, {'test': 6}>>
1581    #[test]
1582    fn select_value_tuple_constructor_2() {
1583        let mut lg = LogicalPlan::new();
1584
1585        let from = lg.add_operator(scan("data", "v"));
1586
1587        let va = path_var("v", "a");
1588        let mut tuple_expr = TupleExpr::new();
1589        tuple_expr
1590            .attrs
1591            .push(ValueExpr::Lit(Box::new("test".into())));
1592        tuple_expr.values.push(ValueExpr::BinaryExpr(
1593            BinaryOp::Mul,
1594            Box::new(va),
1595            Box::new(ValueExpr::Lit(Box::new(Value::Integer(2).into()))),
1596        ));
1597
1598        let project = lg.add_operator(ProjectValue(logical::ProjectValue {
1599            expr: ValueExpr::TupleExpr(tuple_expr),
1600        }));
1601
1602        let sink = lg.add_operator(BindingsOp::Sink);
1603
1604        lg.add_flow(from, project);
1605        lg.add_flow(project, sink);
1606
1607        let out = evaluate(lg, data_3_tuple());
1608        println!("{:?}", &out);
1609        assert_matches!(out, Value::Bag(bag) => {
1610            let expected = bag![
1611                tuple![("test", 2)],
1612                tuple![("test", 4)],
1613                tuple![("test", 6)],
1614            ];
1615            assert_eq!(*bag, expected);
1616        });
1617    }
1618
1619    // Spec 6.1.1 — Treatment of mistyped attribute names in permissive mode
1620    //  SELECT VALUE {v.a: v.b}
1621    //  FROM [{'a': 'legit', 'b': 1}, {'a': 400, 'b': 2}] AS v;
1622    //  Expected: <<{'legit': 1}, {}>>
1623    #[test]
1624    fn select_value_with_tuple_mistype_attr() {
1625        let mut lg = LogicalPlan::new();
1626
1627        let from = lg.add_operator(scan("data", "v"));
1628
1629        let va = path_var("v", "a");
1630        let vb = path_var("v", "b");
1631
1632        let mut tuple_expr = TupleExpr::new();
1633        tuple_expr.attrs.push(va);
1634        tuple_expr.values.push(vb);
1635
1636        let select_value = lg.add_operator(ProjectValue(logical::ProjectValue {
1637            expr: ValueExpr::TupleExpr(tuple_expr),
1638        }));
1639
1640        let sink = lg.add_operator(BindingsOp::Sink);
1641
1642        lg.add_flow(from, select_value);
1643        lg.add_flow(select_value, sink);
1644
1645        let data = list![
1646            tuple![("a", "legit"), ("b", 1)],
1647            tuple![("a", 400), ("b", 2)],
1648        ];
1649
1650        let mut bindings: MapBindings<Value> = MapBindings::default();
1651        bindings.insert("data", data.into());
1652
1653        let out = evaluate(lg, bindings);
1654        println!("{:?}", &out);
1655        assert_matches!(out, Value::Bag(bag) => {
1656            let expected = bag![tuple![("legit", 1)], tuple![]];
1657            assert_eq!(*bag, expected);
1658        });
1659    }
1660
1661    // Spec 6.1.1 — Treatment of duplicate attribute names
1662    //  SELECT VALUE {v.a: v.b, v.c: v.d}
1663    //  FROM [{'a': 'same', 'b': 1, 'c': 'same', 'd': 2}] AS v;
1664    //  Expected: <<{'same': 1, 'same': 2}>>
1665    #[test]
1666    fn select_value_with_duplicate_attrs() {
1667        let mut lg = LogicalPlan::new();
1668        let from = lg.add_operator(scan("data", "v"));
1669
1670        let va = path_var("v", "a");
1671        let vb = path_var("v", "b");
1672        let vc = path_var("v", "c");
1673        let vd = path_var("v", "d");
1674
1675        let mut tuple_expr = TupleExpr::new();
1676        tuple_expr.attrs.push(va);
1677        tuple_expr.values.push(vb);
1678        tuple_expr.attrs.push(vc);
1679        tuple_expr.values.push(vd);
1680
1681        let select_value = lg.add_operator(ProjectValue(logical::ProjectValue {
1682            expr: ValueExpr::TupleExpr(tuple_expr),
1683        }));
1684
1685        let sink = lg.add_operator(BindingsOp::Sink);
1686
1687        lg.add_flow(from, select_value);
1688        lg.add_flow(select_value, sink);
1689
1690        let data = list![tuple![("a", "same"), ("b", 1), ("c", "same"), ("d", 2)]];
1691
1692        let mut bindings: MapBindings<Value> = MapBindings::default();
1693        bindings.insert("data", data.into());
1694
1695        let out = evaluate(lg, bindings);
1696        println!("{:?}", &out);
1697        assert_matches!(out, Value::Bag(bag) => {
1698            let expected = bag![tuple![("same", 1), ("same", 2)]];
1699            assert_eq!(*bag, expected);
1700        });
1701    }
1702
1703    // Spec 6.1.2 — Array Constructors
1704    //  SELECT VALUE [v.a, v.b]
1705    //  FROM [{'a': 1, 'b': 1}, {'a': 2, 'b': 2}] AS v;
1706    //  Expected: <<[1, 1], [2, 2]>>
1707    #[test]
1708    fn select_value_array_constructor_1() {
1709        let mut lg = LogicalPlan::new();
1710
1711        let from = lg.add_operator(scan("data", "v"));
1712
1713        let va = path_var("v", "a");
1714        let vb = path_var("v", "b");
1715
1716        let mut list_expr = ListExpr::new();
1717        list_expr.elements.push(va);
1718        list_expr.elements.push(vb);
1719
1720        let select_value = lg.add_operator(ProjectValue(logical::ProjectValue {
1721            expr: ValueExpr::ListExpr(list_expr),
1722        }));
1723
1724        let sink = lg.add_operator(BindingsOp::Sink);
1725
1726        lg.add_flow(from, select_value);
1727        lg.add_flow(select_value, sink);
1728
1729        let data = list![tuple![("a", 1), ("b", 1)], tuple![("a", 2), ("b", 2)],];
1730
1731        let mut bindings: MapBindings<Value> = MapBindings::default();
1732        bindings.insert("data", data.into());
1733
1734        let out = evaluate(lg, bindings);
1735        println!("{:?}", &out);
1736        assert_matches!(out, Value::Bag(bag) => {
1737            let expected = bag![list![1, 1], list![2, 2]];
1738            assert_eq!(*bag, expected);
1739        });
1740    }
1741
1742    // Spec 6.1.2 — Array Constructors
1743    //  SELECT VALUE [2*v.a]
1744    //  FROM [{'a': 1, 'b': 1}, {'a': 2, 'b': 2}] AS v;
1745    //  Expected: <<[2], [4], [6]>>
1746    #[test]
1747    fn select_value_with_array_constructor_2() {
1748        let mut lg = LogicalPlan::new();
1749
1750        let from = lg.add_operator(scan("data", "v"));
1751
1752        let va = path_var("v", "a");
1753        let mut list_expr = ListExpr::new();
1754        list_expr.elements.push(ValueExpr::BinaryExpr(
1755            BinaryOp::Mul,
1756            Box::new(va),
1757            Box::new(ValueExpr::Lit(Box::new(Value::Integer(2).into()))),
1758        ));
1759
1760        let select_value = lg.add_operator(ProjectValue(logical::ProjectValue {
1761            expr: ValueExpr::ListExpr(list_expr),
1762        }));
1763
1764        let sink = lg.add_operator(BindingsOp::Sink);
1765
1766        lg.add_flow(from, select_value);
1767        lg.add_flow(select_value, sink);
1768
1769        let out = evaluate(lg, data_3_tuple());
1770        println!("{:?}", &out);
1771        assert_matches!(out, Value::Bag(bag) => {
1772            let expected = bag![list![2], list![4], list![6]];
1773            assert_eq!(*bag, expected);
1774        });
1775    }
1776
1777    // Spec 6.1.3 — Bag Constructors
1778    //  SELECT VALUE <<v.a, v.b>>
1779    //  FROM [{'a': 1, 'b': 1}, {'a': 2, 'b': 2}] AS v;
1780    //  Expected: << <<1, 1>>, <<2, 2>> >>
1781    #[test]
1782    fn select_value_bag_constructor() {
1783        let mut lg = LogicalPlan::new();
1784
1785        let from = lg.add_operator(scan("data", "v"));
1786
1787        let va = path_var("v", "a");
1788        let vb = path_var("v", "b");
1789
1790        let mut bag_expr = BagExpr::new();
1791        bag_expr.elements.push(va);
1792        bag_expr.elements.push(vb);
1793
1794        let select_value = lg.add_operator(ProjectValue(logical::ProjectValue {
1795            expr: ValueExpr::BagExpr(bag_expr),
1796        }));
1797
1798        let sink = lg.add_operator(BindingsOp::Sink);
1799
1800        lg.add_flow(from, select_value);
1801        lg.add_flow(select_value, sink);
1802
1803        let data = list![tuple![("a", 1), ("b", 1)], tuple![("a", 2), ("b", 2)],];
1804
1805        let mut bindings: MapBindings<Value> = MapBindings::default();
1806        bindings.insert("data", data.into());
1807
1808        let out = evaluate(lg, bindings);
1809        println!("{:?}", &out);
1810        assert_matches!(out, Value::Bag(bag) => {
1811            let expected = bag![bag![1, 1], bag![2, 2]];
1812            assert_eq!(*bag, expected);
1813        });
1814    }
1815
1816    // Spec 6.1.4 — Treatment of MISSING in SELECT VALUE
1817    //  SELECT VALUE {'a': v.a, 'b': v.b}
1818    //  FROM [{'a': 1, 'b': 1}, {'a': 2}] AS v;
1819    //  Expected: <<{'a':1, 'b':1}, {'a':2}>>
1820    #[test]
1821    fn missing_in_select_value_for_tuple() {
1822        let mut lg = LogicalPlan::new();
1823        let from = lg.add_operator(scan("data", "v"));
1824
1825        let va = path_var("v", "a");
1826        let vb = path_var("v", "b");
1827
1828        let mut tuple_expr = TupleExpr::new();
1829        tuple_expr.attrs.push(ValueExpr::Lit(Box::new("a".into())));
1830        tuple_expr.values.push(va);
1831        tuple_expr.attrs.push(ValueExpr::Lit(Box::new("b".into())));
1832        tuple_expr.values.push(vb);
1833
1834        let select_value = lg.add_operator(ProjectValue(logical::ProjectValue {
1835            expr: ValueExpr::TupleExpr(tuple_expr),
1836        }));
1837
1838        let sink = lg.add_operator(BindingsOp::Sink);
1839
1840        lg.add_flow(from, select_value);
1841        lg.add_flow(select_value, sink);
1842
1843        let data = list![tuple![("a", 1), ("b", 1)], tuple![("a", 2)]];
1844
1845        let mut bindings: MapBindings<Value> = MapBindings::default();
1846        bindings.insert("data", data.into());
1847
1848        let out = evaluate(lg, bindings);
1849        println!("{:?}", &out);
1850        assert_matches!(out, Value::Bag(bag) => {
1851            let expected =
1852                bag![tuple![("a", 1), ("b", 1)], tuple![("a", 2)],];
1853            assert_eq!(*bag, expected);
1854        });
1855    }
1856
1857    // Spec 6.1.4 — Treatment of MISSING in SELECT VALUE
1858    //  SELECT VALUE [v.a, v.b]
1859    //  FROM [{'a': 1, 'b': 1}, {'a': 2}] AS v;
1860    // Expected: <<[1, 1], [2, MISSING]>>
1861    #[test]
1862    fn missing_in_select_value_for_list() {
1863        let mut lg = LogicalPlan::new();
1864        let from = lg.add_operator(scan("data", "v"));
1865
1866        let va = path_var("v", "a");
1867        let vb = path_var("v", "b");
1868
1869        let mut list_expr = ListExpr::new();
1870        list_expr.elements.push(va);
1871        list_expr.elements.push(vb);
1872
1873        let select_value = lg.add_operator(ProjectValue(logical::ProjectValue {
1874            expr: ValueExpr::ListExpr(list_expr),
1875        }));
1876
1877        let sink = lg.add_operator(BindingsOp::Sink);
1878
1879        lg.add_flow(from, select_value);
1880        lg.add_flow(select_value, sink);
1881
1882        let data = list![tuple![("a", 1), ("b", 1)], tuple![("a", 2)]];
1883
1884        let mut bindings: MapBindings<Value> = MapBindings::default();
1885        bindings.insert("data", data.into());
1886
1887        let out = evaluate(lg, bindings);
1888        println!("{:?}", &out);
1889        assert_matches!(out, Value::Bag(bag) => {
1890            let expected = bag![list![1, 1], list![2, Value::Missing]];
1891            assert_eq!(*bag, expected);
1892        });
1893    }
1894
1895    // Spec 6.1.4 — Treatment of MISSING in SELECT VALUE
1896    //  SELECT VALUE v.b
1897    //  FROM [{'a':1, 'b':1}, {'a':2}] AS v;
1898    //  Expected: <<1, MISSING>>
1899    #[test]
1900    fn missing_in_select_value_for_bag_1() {
1901        let mut lg = LogicalPlan::new();
1902        let from = lg.add_operator(scan("data", "v"));
1903
1904        let vb = path_var("v", "b");
1905
1906        let select_value = lg.add_operator(ProjectValue(logical::ProjectValue { expr: vb }));
1907
1908        let sink = lg.add_operator(BindingsOp::Sink);
1909
1910        lg.add_flow(from, select_value);
1911        lg.add_flow(select_value, sink);
1912
1913        let data = list![tuple![("a", 1), ("b", 1)], tuple![("a", 2)]];
1914
1915        let mut bindings: MapBindings<Value> = MapBindings::default();
1916        bindings.insert("data", data.into());
1917
1918        let out = evaluate(lg, bindings);
1919        println!("{:?}", &out);
1920        assert_matches!(out, Value::Bag(bag) => {
1921            let expected = bag![1, Value::Missing];
1922            assert_eq!(*bag, expected);
1923        });
1924    }
1925
1926    // Spec 6.1.4 — Treatment of MISSING in SELECT VALUE
1927    //  SELECT VALUE <<v.a, v.b>>
1928    //  FROM [{'a': 1, 'b': 1}, {'a': 2}] AS v;
1929    // Expected: << <<1, 1>>, <<2, MISSING>> >>
1930    #[test]
1931    fn missing_in_select_value_for_bag_2() {
1932        let mut lg = LogicalPlan::new();
1933        let from = lg.add_operator(scan("data", "v"));
1934
1935        let va = path_var("v", "a");
1936        let vb = path_var("v", "b");
1937
1938        let mut bag_expr = BagExpr::new();
1939        bag_expr.elements.push(va);
1940        bag_expr.elements.push(vb);
1941
1942        let select_value = lg.add_operator(ProjectValue(logical::ProjectValue {
1943            expr: ValueExpr::BagExpr(bag_expr),
1944        }));
1945
1946        let sink = lg.add_operator(BindingsOp::Sink);
1947
1948        lg.add_flow(from, select_value);
1949        lg.add_flow(select_value, sink);
1950
1951        let data = list![tuple![("a", 1), ("b", 1)], tuple![("a", 2)]];
1952
1953        let mut bindings: MapBindings<Value> = MapBindings::default();
1954        bindings.insert("data", data.into());
1955
1956        let out = evaluate(lg, bindings);
1957        println!("{:?}", &out);
1958        assert_matches!(out, Value::Bag(bag) => {
1959            let expected = bag![bag![1, 1], bag![2, Value::Missing]];
1960            assert_eq!(*bag, expected);
1961        });
1962    }
1963
1964    #[test]
1965    fn select_distinct() {
1966        // Plan for `SELECT DISTINCT firstName, (firstName || firstName) AS doubleName FROM customer WHERE balance > 0`
1967        let mut logical = LogicalPlan::new();
1968
1969        let scan = logical.add_operator(scan("customer", "customer"));
1970
1971        let filter = logical.add_operator(BindingsOp::Filter(logical::Filter {
1972            expr: ValueExpr::BinaryExpr(
1973                BinaryOp::Gt,
1974                Box::new(ValueExpr::Path(
1975                    Box::new(ValueExpr::VarRef(
1976                        BindingsName::CaseInsensitive("customer".into()),
1977                        VarRefType::Local,
1978                    )),
1979                    vec![PathComponent::Key(BindingsName::CaseInsensitive(
1980                        "balance".to_string().into(),
1981                    ))],
1982                )),
1983                Box::new(ValueExpr::Lit(Box::new(Value::Integer(0).into()))),
1984            ),
1985        }));
1986
1987        let project = logical.add_operator(Project(logical::Project {
1988            exprs: Vec::from([
1989                (
1990                    "firstName".to_string(),
1991                    ValueExpr::Path(
1992                        Box::new(ValueExpr::VarRef(
1993                            BindingsName::CaseInsensitive("customer".into()),
1994                            VarRefType::Local,
1995                        )),
1996                        vec![PathComponent::Key(BindingsName::CaseInsensitive(
1997                            "firstName".to_string().into(),
1998                        ))],
1999                    ),
2000                ),
2001                (
2002                    "doubleName".to_string(),
2003                    ValueExpr::BinaryExpr(
2004                        BinaryOp::Concat,
2005                        Box::new(ValueExpr::Path(
2006                            Box::new(ValueExpr::VarRef(
2007                                BindingsName::CaseInsensitive("customer".into()),
2008                                VarRefType::Local,
2009                            )),
2010                            vec![PathComponent::Key(BindingsName::CaseInsensitive(
2011                                "firstName".to_string().into(),
2012                            ))],
2013                        )),
2014                        Box::new(ValueExpr::Path(
2015                            Box::new(ValueExpr::VarRef(
2016                                BindingsName::CaseInsensitive("customer".into()),
2017                                VarRefType::Local,
2018                            )),
2019                            vec![PathComponent::Key(BindingsName::CaseInsensitive(
2020                                "firstName".to_string().into(),
2021                            ))],
2022                        )),
2023                    ),
2024                ),
2025            ]),
2026        }));
2027
2028        let distinct = logical.add_operator(Distinct);
2029        let sink = logical.add_operator(BindingsOp::Sink);
2030
2031        logical.extend_with_flows(&[
2032            (scan, filter),
2033            (filter, project),
2034            (project, distinct),
2035            (distinct, sink),
2036        ]);
2037
2038        let out = evaluate(logical, data_customer());
2039        println!("{:?}", &out);
2040        assert_matches!(out, Value::Bag(bag) => {
2041            let expected = bag![
2042                tuple![("firstName", "jason"), ("doubleName", "jasonjason")],
2043                tuple![("firstName", "miriam"), ("doubleName", "miriammiriam")],
2044            ];
2045            assert_eq!(*bag, expected);
2046        });
2047    }
2048
2049    #[test]
2050    fn select_with_in_as_predicate() {
2051        // Plan for `SELECT a AS b FROM data WHERE a IN [1]`
2052        let mut logical = LogicalPlan::new();
2053
2054        let scan = logical.add_operator(scan("data", "data"));
2055
2056        let filter = logical.add_operator(BindingsOp::Filter(logical::Filter {
2057            expr: ValueExpr::BinaryExpr(
2058                BinaryOp::In,
2059                Box::new(ValueExpr::Path(
2060                    Box::new(ValueExpr::VarRef(
2061                        BindingsName::CaseInsensitive("data".into()),
2062                        VarRefType::Local,
2063                    )),
2064                    vec![PathComponent::Key(BindingsName::CaseInsensitive(
2065                        "a".to_string().into(),
2066                    ))],
2067                )),
2068                Box::new(ValueExpr::Lit(Box::new(list![1].into()))),
2069            ),
2070        }));
2071
2072        let project = logical.add_operator(Project(logical::Project {
2073            exprs: Vec::from([(
2074                "b".to_string(),
2075                ValueExpr::Path(
2076                    Box::new(ValueExpr::VarRef(
2077                        BindingsName::CaseInsensitive("data".into()),
2078                        VarRefType::Local,
2079                    )),
2080                    vec![PathComponent::Key(BindingsName::CaseInsensitive(
2081                        "a".to_string().into(),
2082                    ))],
2083                ),
2084            )]),
2085        }));
2086
2087        let sink = logical.add_operator(BindingsOp::Sink);
2088
2089        logical.extend_with_flows(&[(scan, filter), (filter, project), (project, sink)]);
2090
2091        let out = evaluate(logical, data_3_tuple());
2092        println!("{:?}", &out);
2093        assert_matches!(out, Value::Bag(bag) => {
2094            let expected = bag![
2095                tuple![("b", 1)],
2096            ];
2097            assert_eq!(*bag, expected);
2098        });
2099    }
2100
2101    #[test]
2102    fn subquery_in_from() {
2103        // SELECT t.a, s FROM data AS t, (SELECT v.a*2 AS u FROM t AS v) AS s;
2104        let mut subq_plan = LogicalPlan::new();
2105        let subq_scan = subq_plan.add_operator(scan("t", "v"));
2106        let va = path_var("v", "a");
2107        let subq_project = subq_plan.add_operator(Project(logical::Project {
2108            exprs: Vec::from([(
2109                "u".to_string(),
2110                ValueExpr::BinaryExpr(
2111                    BinaryOp::Mul,
2112                    Box::new(va),
2113                    Box::new(ValueExpr::Lit(Box::new(Value::Integer(2).into()))),
2114                ),
2115            )]),
2116        }));
2117        let subq_sink = subq_plan.add_operator(BindingsOp::Sink);
2118
2119        subq_plan.add_flow(subq_scan, subq_project);
2120        subq_plan.add_flow(subq_project, subq_sink);
2121
2122        let mut lg = LogicalPlan::new();
2123
2124        let from_lhs = scan("data", "t");
2125        let from_rhs = BindingsOp::Scan(logical::Scan {
2126            expr: ValueExpr::SubQueryExpr(logical::SubQueryExpr { plan: subq_plan }),
2127            as_key: "s".to_string(),
2128            at_key: None,
2129        });
2130
2131        let join = lg.add_operator(BindingsOp::Join(logical::Join {
2132            kind: JoinKind::Cross,
2133            left: Box::new(from_lhs),
2134            right: Box::new(from_rhs),
2135            on: None,
2136        }));
2137
2138        let ta = path_var("t", "a");
2139        let su = path_var("s", "u");
2140        let project = lg.add_operator(Project(logical::Project {
2141            exprs: Vec::from([("ta".to_string(), ta), ("su".to_string(), su)]),
2142        }));
2143
2144        let sink = lg.add_operator(BindingsOp::Sink);
2145
2146        lg.add_flow_with_branch_num(join, project, 0);
2147        lg.add_flow_with_branch_num(project, sink, 0);
2148
2149        let data = list![tuple![("a", 1)], tuple![("a", 2)], tuple![("a", 3)],];
2150
2151        let mut bindings: MapBindings<Value> = MapBindings::default();
2152        bindings.insert("data", data.into());
2153
2154        let out = evaluate(lg, bindings);
2155        println!("{:?}", &out);
2156        assert_matches!(out, Value::Bag(bag) => {
2157            let expected = bag![
2158                tuple![
2159                    ("ta", 1),
2160                    ("su", 2),
2161                ],
2162                tuple![
2163                    ("ta", 2),
2164                    ("su", 4),
2165                ],
2166                tuple![
2167                    ("ta", 3),
2168                    ("su", 6),
2169                ],
2170            ];
2171            assert_eq!(*bag, expected);
2172        });
2173    }
2174
2175    #[test]
2176    fn subquery_in_project() {
2177        // SELECT t.a, (SELECT v.a*2 AS u FROM t AS v) AS s FROM data AS t;
2178        let mut subq_plan = LogicalPlan::new();
2179        let subq_scan = subq_plan.add_operator(scan("t", "v"));
2180        let va = path_var("v", "a");
2181        let subq_project = subq_plan.add_operator(Project(logical::Project {
2182            exprs: Vec::from([(
2183                "u".to_string(),
2184                ValueExpr::BinaryExpr(
2185                    BinaryOp::Mul,
2186                    Box::new(va),
2187                    Box::new(ValueExpr::Lit(Box::new(Value::Integer(2).into()))),
2188                ),
2189            )]),
2190        }));
2191        let subq_sink = subq_plan.add_operator(BindingsOp::Sink);
2192
2193        subq_plan.add_flow(subq_scan, subq_project);
2194        subq_plan.add_flow(subq_project, subq_sink);
2195
2196        let mut lg = LogicalPlan::new();
2197        let from = lg.add_operator(scan("data", "t"));
2198        let ta = path_var("t", "a");
2199        let project = lg.add_operator(Project(logical::Project {
2200            exprs: Vec::from([
2201                ("ta".to_string(), ta),
2202                (
2203                    "s".to_string(),
2204                    ValueExpr::SubQueryExpr(logical::SubQueryExpr { plan: subq_plan }),
2205                ),
2206            ]),
2207        }));
2208
2209        let sink = lg.add_operator(BindingsOp::Sink);
2210
2211        lg.add_flow(from, project);
2212        lg.add_flow(project, sink);
2213
2214        let data = list![tuple![("a", 1), ("b", 1)], tuple![("a", 2), ("b", 2)]];
2215
2216        let mut bindings: MapBindings<Value> = MapBindings::default();
2217        bindings.insert("data", data.into());
2218
2219        let out = evaluate(lg, bindings);
2220        println!("{:?}", &out);
2221        assert_matches!(out, Value::Bag(bag) => {
2222            let expected = bag![
2223                tuple![
2224                    ("ta", 1),
2225                    ("s", bag![tuple![("u", 2)]]),
2226                ],
2227                tuple![
2228                    ("ta", 2),
2229                    ("s", bag![tuple![("u", 4)]]),
2230                ],
2231            ];
2232            assert_eq!(*bag, expected);
2233        });
2234    }
2235
2236    mod clause_from {
2237        use crate::eval::evaluable::{EvalScan, Evaluable};
2238        use crate::eval::expr::{EvalGlobalVarRef, EvalPath, EvalPathComponent};
2239        use crate::eval::BasicContext;
2240        use partiql_catalog::context::SystemContext;
2241        use partiql_value::{bag, list, BindingsName, DateTime};
2242
2243        use super::*;
2244
2245        fn some_ordered_table() -> List {
2246            list![tuple![("a", 0), ("b", 0)], tuple![("a", 1), ("b", 1)],]
2247        }
2248
2249        fn some_unordered_table() -> Bag {
2250            Bag::from(some_ordered_table())
2251        }
2252
2253        // Spec 5.1
2254        #[test]
2255        fn basic() {
2256            let mut p0: MapBindings<Value> = MapBindings::default();
2257            p0.insert("someOrderedTable", some_ordered_table().into());
2258
2259            let sys = SystemContext {
2260                now: DateTime::from_system_now_utc(),
2261            };
2262            let ctx = BasicContext::new(p0, sys);
2263
2264            let scan = EvalScan::new_with_at_key(
2265                Box::new(EvalGlobalVarRef {
2266                    name: BindingsName::CaseInsensitive("someOrderedTable".to_string().into()),
2267                }),
2268                "x",
2269                "y",
2270            );
2271
2272            let res = scan.evaluate([None, None], &ctx);
2273
2274            // <<{ y: 0, x:  { b: 0, a: 0 } },  { x:  { b: 1, a: 1 }, y: 1 }>>
2275            let expected = bag![
2276                tuple![("x", tuple![("a", 0), ("b", 0)]), ("y", 0)],
2277                tuple![("x", tuple![("a", 1), ("b", 1)]), ("y", 1)],
2278            ];
2279            assert_eq!(Value::Bag(Box::new(expected)), res);
2280        }
2281
2282        // Spec 5.1.1
2283        #[test]
2284        fn mistype_at_on_bag() {
2285            let mut p0: MapBindings<Value> = MapBindings::default();
2286            p0.insert("someUnorderedTable", some_unordered_table().into());
2287
2288            let sys = SystemContext {
2289                now: DateTime::from_system_now_utc(),
2290            };
2291            let ctx = BasicContext::new(p0, sys);
2292
2293            let scan = EvalScan::new_with_at_key(
2294                Box::new(EvalGlobalVarRef {
2295                    name: BindingsName::CaseInsensitive("someUnorderedTable".to_string().into()),
2296                }),
2297                "x",
2298                "y",
2299            );
2300
2301            let res = scan.evaluate([None, None], &ctx);
2302
2303            // <<{ y: MISSING, x:  { b: 0, a: 0 } },  { x:  { b: 1, a: 1 }, y: MISSING }>>
2304            let expected = bag![
2305                tuple![
2306                    ("x", tuple![("a", 0), ("b", 0)]),
2307                    ("y", value::Value::Missing)
2308                ],
2309                tuple![
2310                    ("x", tuple![("a", 1), ("b", 1)]),
2311                    ("y", value::Value::Missing)
2312                ],
2313            ];
2314            assert_eq!(Value::Bag(Box::new(expected)), res);
2315        }
2316
2317        // Spec 5.1.1
2318        #[test]
2319        fn mistype_scalar() {
2320            let mut p0: MapBindings<Value> = MapBindings::default();
2321            p0.insert("someOrderedTable", some_ordered_table().into());
2322
2323            let table_ref = EvalGlobalVarRef {
2324                name: BindingsName::CaseInsensitive("someOrderedTable".to_string().into()),
2325            };
2326            let path_to_scalar = EvalPath {
2327                expr: Box::new(table_ref),
2328                components: vec![
2329                    EvalPathComponent::Index(0),
2330                    EvalPathComponent::Key(BindingsName::CaseInsensitive("a".into())),
2331                ],
2332            };
2333            let scan = EvalScan::new(Box::new(path_to_scalar), "x");
2334
2335            let sys = SystemContext {
2336                now: DateTime::from_system_now_utc(),
2337            };
2338            let ctx = BasicContext::new(p0, sys);
2339            let scan_res = scan.evaluate([None, None], &ctx);
2340
2341            let expected = bag![tuple![("x", 0)]];
2342            assert_eq!(Value::Bag(Box::new(expected)), scan_res);
2343        }
2344
2345        // Spec 5.1.1
2346        #[test]
2347        fn mistype_absent() {
2348            let mut p0: MapBindings<Value> = MapBindings::default();
2349            p0.insert("someOrderedTable", some_ordered_table().into());
2350
2351            let table_ref = EvalGlobalVarRef {
2352                name: BindingsName::CaseInsensitive("someOrderedTable".to_string().into()),
2353            };
2354            let path_to_scalar = EvalPath {
2355                expr: Box::new(table_ref),
2356                components: vec![
2357                    EvalPathComponent::Index(0),
2358                    EvalPathComponent::Key(BindingsName::CaseInsensitive("c".into())),
2359                ],
2360            };
2361            let scan = EvalScan::new(Box::new(path_to_scalar), "x");
2362
2363            let sys = SystemContext {
2364                now: DateTime::from_system_now_utc(),
2365            };
2366            let ctx = BasicContext::new(p0, sys);
2367            let res = scan.evaluate([None, None], &ctx);
2368
2369            let expected = bag![tuple![("x", value::Value::Missing)]];
2370            assert_eq!(Value::Bag(Box::new(expected)), res);
2371        }
2372    }
2373
2374    mod clause_unpivot {
2375        use partiql_value::{bag, BindingsName, DateTime, Tuple};
2376
2377        use crate::eval::evaluable::{EvalUnpivot, Evaluable};
2378        use crate::eval::expr::EvalGlobalVarRef;
2379        use crate::eval::BasicContext;
2380
2381        use super::*;
2382
2383        fn just_a_tuple() -> Tuple {
2384            tuple![("amzn", 840.05), ("tdc", 31.06)]
2385        }
2386
2387        // Spec 5.2
2388        #[test]
2389        fn basic() {
2390            let mut p0: MapBindings<Value> = MapBindings::default();
2391            p0.insert("justATuple", just_a_tuple().into());
2392
2393            let unpivot = EvalUnpivot::new(
2394                Box::new(EvalGlobalVarRef {
2395                    name: BindingsName::CaseInsensitive("justATuple".to_string().into()),
2396                }),
2397                "price",
2398                Some("symbol".into()),
2399            );
2400
2401            let sys = SystemContext {
2402                now: DateTime::from_system_now_utc(),
2403            };
2404            let ctx = BasicContext::new(p0, sys);
2405            let res = unpivot.evaluate([None, None], &ctx);
2406
2407            let expected = bag![
2408                tuple![("symbol", "tdc"), ("price", 31.06)],
2409                tuple![("symbol", "amzn"), ("price", 840.05)],
2410            ];
2411            assert_eq!(Value::Bag(Box::new(expected)), res);
2412        }
2413
2414        // Spec 5.2.1
2415        #[test]
2416        fn mistype_non_tuple() {
2417            let mut p0: MapBindings<Value> = MapBindings::default();
2418            p0.insert("nonTuple", Value::from(1));
2419
2420            let unpivot = EvalUnpivot::new(
2421                Box::new(EvalGlobalVarRef {
2422                    name: BindingsName::CaseInsensitive("nonTuple".to_string().into()),
2423                }),
2424                "x",
2425                Some("y".into()),
2426            );
2427
2428            let sys = SystemContext {
2429                now: DateTime::from_system_now_utc(),
2430            };
2431            let ctx = BasicContext::new(p0, sys);
2432            let res = unpivot.evaluate([None, None], &ctx);
2433
2434            let expected = bag![tuple![("x", 1), ("y", "_1")]];
2435            assert_eq!(Value::Bag(Box::new(expected)), res);
2436        }
2437    }
2438}