1use std::collections::BTreeMap;
11use std::fmt::Debug;
12
13use mz_repr::{Datum, ReprColumnType, ReprRelationType, ReprScalarType, Row, RowArena};
14
15use crate::scalar::func::variadic::And;
16use crate::{
17 BinaryFunc, Eval, EvalError, MapFilterProject, MfpPlan, MirScalarExpr, UnaryFunc,
18 UnmaterializableFunc, VariadicFunc, func,
19};
20fn datum_is_nan(datum: Datum) -> bool {
29 match datum {
30 Datum::Float32(f) => f.is_nan(),
31 Datum::Float64(f) => f.is_nan(),
32 Datum::Numeric(n) => n.0.is_nan(),
33 _ => false,
34 }
35}
36
37fn datum_is_infinite(datum: Datum) -> bool {
39 match datum {
40 Datum::Float32(f) => f.is_infinite(),
41 Datum::Float64(f) => f.is_infinite(),
42 Datum::Numeric(n) => n.0.is_infinite(),
43 _ => false,
44 }
45}
46
47#[derive(Clone, Eq, PartialEq, Debug)]
49enum Values<'a> {
50 Empty,
52 Within(Datum<'a>, Datum<'a>),
55 Nested(BTreeMap<Datum<'a>, ResultSpec<'a>>),
59 All,
62}
63
64impl<'a> Values<'a> {
65 fn just(a: Datum<'a>) -> Values<'a> {
66 match a {
67 Datum::Map(datum_map) => Values::Nested(
68 datum_map
69 .iter()
70 .map(|(key, val)| (key.into(), ResultSpec::value(val)))
71 .collect(),
72 ),
73 other => Self::Within(other, other),
74 }
75 }
76
77 fn union(self, other: Values<'a>) -> Values<'a> {
78 match (self, other) {
79 (Values::Empty, r) => r,
80 (r, Values::Empty) => r,
81 (Values::Within(a0, a1), Values::Within(b0, b1)) => {
82 Values::Within(a0.min(b0), a1.max(b1))
83 }
84 (Values::Nested(a), Values::Nested(mut b)) => {
85 let mut merged = BTreeMap::new();
90 for (key, a_spec) in a {
91 if let Some(b_spec) = b.remove(&key) {
92 let unioned = a_spec.union(b_spec);
93 if unioned != ResultSpec::anything() {
94 merged.insert(key, unioned);
95 }
96 }
97 }
98 if merged.is_empty() {
99 Values::All
100 } else {
101 Values::Nested(merged)
102 }
103 }
104 _ => Values::All,
105 }
106 }
107
108 fn intersect(self, other: Values<'a>) -> Values<'a> {
109 match (self, other) {
110 (Values::Empty, _) => Values::Empty,
111 (_, Values::Empty) => Values::Empty,
112 (Values::Within(a0, a1), Values::Within(b0, b1)) => {
113 let min = a0.max(b0);
114 let max = a1.min(b1);
115 if min <= max {
116 Values::Within(min, max)
117 } else {
118 Values::Empty
119 }
120 }
121 (Values::Nested(mut a), Values::Nested(b)) => {
122 for (datum, other_spec) in b {
123 let spec = a.entry(datum).or_insert_with(ResultSpec::anything);
124 *spec = spec.clone().intersect(other_spec);
125 }
126 Values::Nested(a)
127 }
128 (Values::All, v) => v,
129 (v, Values::All) => v,
130 (nested @ Values::Nested(_), Values::Within(_, _))
137 | (Values::Within(_, _), nested @ Values::Nested(_)) => nested,
138 }
139 }
140
141 fn may_contain(&self, value: Datum<'a>) -> bool {
142 match self {
143 Values::Empty => false,
144 Values::Within(min, max) => *min <= value && value <= *max,
145 Values::All => true,
146 Values::Nested(field_map) => match value {
147 Datum::Map(datum_map) => {
148 datum_map
149 .iter()
150 .all(|(key, val)| match field_map.get(&key.into()) {
151 None => true,
152 Some(nested) => nested.may_contain(val),
153 })
154 }
155 _ => false,
156 },
157 }
158 }
159
160 fn as_single(&self) -> Option<Datum<'a>> {
169 match self {
170 Values::Within(a, b) if a == b => Some(*a),
171 _ => None,
172 }
173 }
174}
175
176#[derive(Debug, Clone, Eq, PartialEq)]
181pub struct ResultSpec<'a> {
182 nullable: bool,
184 fallible: bool,
186 values: Values<'a>,
188}
189
190impl<'a> ResultSpec<'a> {
191 pub fn nothing() -> Self {
193 ResultSpec {
194 nullable: false,
195 fallible: false,
196 values: Values::Empty,
197 }
198 }
199
200 pub fn anything() -> Self {
202 ResultSpec {
203 nullable: true,
204 fallible: true,
205 values: Values::All,
206 }
207 }
208
209 pub fn any_infallible() -> Self {
211 ResultSpec {
212 nullable: true,
213 fallible: false,
214 values: Values::All,
215 }
216 }
217
218 pub fn null() -> Self {
220 ResultSpec {
221 nullable: true,
222 ..Self::nothing()
223 }
224 }
225
226 pub fn fails() -> Self {
228 ResultSpec {
229 fallible: true,
230 ..Self::nothing()
231 }
232 }
233
234 pub fn has_type(col: &ReprColumnType, fallible: bool) -> ResultSpec<'a> {
236 let values = match &col.scalar_type {
237 ReprScalarType::Bool => Values::Within(Datum::False, Datum::True),
238 _ => Values::All,
240 };
241 ResultSpec {
242 nullable: col.nullable,
243 fallible,
244 values,
245 }
246 }
247
248 pub fn value(value: Datum<'a>) -> ResultSpec<'a> {
250 match value {
251 Datum::Null => Self::null(),
252 nonnull => ResultSpec {
253 values: Values::just(nonnull),
254 ..Self::nothing()
255 },
256 }
257 }
258
259 pub fn value_between(min: Datum<'a>, max: Datum<'a>) -> ResultSpec<'a> {
268 assert!(!min.is_null());
269 assert!(!max.is_null());
270 if min <= max {
271 ResultSpec {
272 values: Values::Within(min, max),
273 ..ResultSpec::nothing()
274 }
275 } else {
276 ResultSpec::value_all()
277 }
278 }
279
280 pub fn value_all() -> ResultSpec<'a> {
282 ResultSpec {
283 values: Values::All,
284 ..ResultSpec::nothing()
285 }
286 }
287
288 pub fn map_spec(map: BTreeMap<Datum<'a>, ResultSpec<'a>>) -> ResultSpec<'a> {
290 ResultSpec {
291 values: Values::Nested(map),
292 ..ResultSpec::nothing()
293 }
294 }
295
296 pub fn union(self, other: ResultSpec<'a>) -> ResultSpec<'a> {
298 ResultSpec {
299 nullable: self.nullable || other.nullable,
300 fallible: self.fallible || other.fallible,
301 values: self.values.union(other.values),
302 }
303 }
304
305 pub fn intersect(self, other: ResultSpec<'a>) -> ResultSpec<'a> {
307 ResultSpec {
308 nullable: self.nullable && other.nullable,
309 fallible: self.fallible && other.fallible,
310 values: self.values.intersect(other.values),
311 }
312 }
313
314 pub fn may_contain(&self, value: Datum<'a>) -> bool {
316 if value == Datum::Null {
317 return self.nullable;
318 }
319
320 self.values.may_contain(value)
321 }
322
323 pub fn may_fail(&self) -> bool {
325 self.fallible
326 }
327
328 fn is_single_value(&self) -> bool {
336 self.values.as_single().is_some()
337 }
338
339 fn may_be_infinite(&self) -> bool {
343 match &self.values {
344 Values::Within(min, max) => datum_is_infinite(*min) || datum_is_infinite(*max),
345 Values::All => true,
346 Values::Empty | Values::Nested(_) => false,
347 }
348 }
349
350 fn flat_map(
361 &self,
362 is_monotone: bool,
363 mut result_map: impl FnMut(Result<Datum<'a>, EvalError>) -> ResultSpec<'a>,
364 ) -> ResultSpec<'a> {
365 let null_spec = if self.nullable {
366 result_map(Ok(Datum::Null))
367 } else {
368 ResultSpec::nothing()
369 };
370
371 let error_spec = if self.fallible {
372 let map_err = result_map(Err(EvalError::Internal("".into())));
376 let raise_err = ResultSpec::fails();
377 raise_err.union(map_err)
382 } else {
383 ResultSpec::nothing()
384 };
385
386 let values_spec = match self.values {
387 Values::Empty => ResultSpec::nothing(),
388 Values::Within(min, max) if min == max => result_map(Ok(min)),
390 Values::Within(Datum::False, Datum::True) => {
392 result_map(Ok(Datum::False)).union(result_map(Ok(Datum::True)))
393 }
394 Values::Within(min, max) if is_monotone && !datum_is_nan(min) && !datum_is_nan(max) => {
401 let min_result = result_map(Ok(min));
402 let max_result = result_map(Ok(max));
403 match (min_result, max_result) {
412 (
415 ResultSpec {
416 nullable: n1,
417 fallible: f1,
418 values: a_values @ Values::Within(..),
419 },
420 ResultSpec {
421 nullable: n2,
422 fallible: f2,
423 values: b_values @ Values::Within(..),
424 },
425 ) => ResultSpec {
426 nullable: n1 || n2,
427 fallible: f1 || f2,
428 values: a_values.union(b_values),
429 },
430 (
436 ResultSpec {
437 nullable: true,
438 fallible: false,
439 values: Values::Empty,
440 },
441 ResultSpec {
442 nullable: true,
443 fallible: false,
444 values: Values::Empty,
445 },
446 ) => ResultSpec::null(),
447 _ => ResultSpec::anything(),
449 }
450 }
451 Values::Within(_, _) | Values::Nested(_) | Values::All => ResultSpec::anything(),
453 };
454
455 null_spec.union(error_spec).union(values_spec)
456 }
457}
458
459pub trait Interpreter {
468 type Summary: Clone + Debug + Sized;
469
470 fn column(&self, id: usize) -> Self::Summary;
472
473 fn literal(&self, result: &Result<Row, EvalError>, col_type: &ReprColumnType) -> Self::Summary;
476 fn unmaterializable(&self, func: &UnmaterializableFunc) -> Self::Summary;
481
482 fn unary(&self, func: &UnaryFunc, expr: Self::Summary) -> Self::Summary;
484
485 fn binary(&self, func: &BinaryFunc, left: Self::Summary, right: Self::Summary)
487 -> Self::Summary;
488
489 fn variadic(&self, func: &VariadicFunc, exprs: Vec<Self::Summary>) -> Self::Summary;
491
492 fn cond(&self, cond: Self::Summary, then: Self::Summary, els: Self::Summary) -> Self::Summary;
494
495 fn expr(&self, expr: &MirScalarExpr) -> Self::Summary {
497 match expr {
498 MirScalarExpr::Column(id, _name) => self.column(*id),
499 MirScalarExpr::Literal(value, col_type) => self.literal(value, col_type),
500 MirScalarExpr::CallUnmaterializable(func) => self.unmaterializable(func),
501 MirScalarExpr::CallUnary { func, expr } => {
502 let expr_range = self.expr(expr);
503 self.unary(func, expr_range)
504 }
505 MirScalarExpr::CallBinary { func, expr1, expr2 } => {
506 let expr1_range = self.expr(expr1);
507 let expr2_range = self.expr(expr2);
508 self.binary(func, expr1_range, expr2_range)
509 }
510 MirScalarExpr::CallVariadic { func, exprs } => {
511 let exprs: Vec<_> = exprs.into_iter().map(|e| self.expr(e)).collect();
512 self.variadic(func, exprs)
513 }
514 MirScalarExpr::If { cond, then, els } => {
515 let cond_range = self.expr(cond);
516 let then_range = self.expr(then);
517 let els_range = self.expr(els);
518 self.cond(cond_range, then_range, els_range)
519 }
520 }
521 }
522
523 fn mfp_filter(&self, mfp: &MapFilterProject) -> Self::Summary {
526 let mfp_eval = MfpEval::new(self, mfp.input_arity, &mfp.expressions);
527 let predicates = mfp
529 .predicates
530 .iter()
531 .map(|(_, e)| mfp_eval.expr(e))
532 .collect();
533 mfp_eval.variadic(&And.into(), predicates)
534 }
535
536 fn mfp_plan_filter(&self, plan: &MfpPlan) -> Self::Summary {
539 let mfp_eval = MfpEval::new(self, plan.mfp.input_arity, &plan.mfp.expressions);
540 let mut results: Vec<_> = plan
542 .mfp
543 .predicates
544 .iter()
545 .map(|(_, e)| mfp_eval.expr(e))
546 .collect();
547 let mz_now = mfp_eval.unmaterializable(&UnmaterializableFunc::MzNow);
548 for bound in &plan.lower_bounds {
549 let bound_range = mfp_eval.expr(bound);
550 let result = mfp_eval.binary(&BinaryFunc::Lte(func::Lte), bound_range, mz_now.clone());
551 results.push(result);
552 }
553 for bound in &plan.upper_bounds {
554 let bound_range = mfp_eval.expr(bound);
555 let result = mfp_eval.binary(&BinaryFunc::Gte(func::Gte), bound_range, mz_now.clone());
556 results.push(result);
557 }
558 self.variadic(&And.into(), results)
559 }
560}
561
562pub(crate) struct MfpEval<'a, E: Interpreter + ?Sized> {
565 evaluator: &'a E,
566 input_arity: usize,
567 expressions: Vec<E::Summary>,
568}
569
570impl<'a, E: Interpreter + ?Sized> MfpEval<'a, E> {
571 pub(crate) fn new(evaluator: &'a E, input_arity: usize, expressions: &[MirScalarExpr]) -> Self {
572 let mut mfp_eval = MfpEval {
573 evaluator,
574 input_arity,
575 expressions: vec![],
576 };
577 for expr in expressions {
578 let result = mfp_eval.expr(expr);
579 mfp_eval.expressions.push(result);
580 }
581 mfp_eval
582 }
583}
584
585impl<'a, E: Interpreter + ?Sized> Interpreter for MfpEval<'a, E> {
586 type Summary = E::Summary;
587
588 fn column(&self, id: usize) -> Self::Summary {
589 if id < self.input_arity {
590 self.evaluator.column(id)
591 } else {
592 self.expressions[id - self.input_arity].clone()
593 }
594 }
595
596 fn literal(&self, result: &Result<Row, EvalError>, col_type: &ReprColumnType) -> Self::Summary {
597 self.evaluator.literal(result, col_type)
598 }
599
600 fn unmaterializable(&self, func: &UnmaterializableFunc) -> Self::Summary {
601 self.evaluator.unmaterializable(func)
602 }
603
604 fn unary(&self, func: &UnaryFunc, expr: Self::Summary) -> Self::Summary {
605 self.evaluator.unary(func, expr)
606 }
607
608 fn binary(
609 &self,
610 func: &BinaryFunc,
611 left: Self::Summary,
612 right: Self::Summary,
613 ) -> Self::Summary {
614 self.evaluator.binary(func, left, right)
615 }
616
617 fn variadic(&self, func: &VariadicFunc, exprs: Vec<Self::Summary>) -> Self::Summary {
618 self.evaluator.variadic(func, exprs)
619 }
620
621 fn cond(&self, cond: Self::Summary, then: Self::Summary, els: Self::Summary) -> Self::Summary {
622 self.evaluator.cond(cond, then, els)
623 }
624}
625
626struct SpecialUnary {
631 map_fn: for<'a, 'b> fn(&'b ColumnSpecs<'a>, ResultSpec<'a>) -> ResultSpec<'a>,
632 pushdownable: bool,
633}
634
635impl SpecialUnary {
636 fn for_func(func: &UnaryFunc) -> Option<SpecialUnary> {
638 fn eagerly<'b>(
642 spec: ResultSpec<'b>,
643 value_fn: impl FnOnce(Values<'b>) -> ResultSpec<'b>,
644 ) -> ResultSpec<'b> {
645 let result = match spec.values {
646 Values::Empty => ResultSpec::nothing(),
647 other => value_fn(other),
648 };
649 ResultSpec {
650 fallible: spec.fallible || result.fallible,
651 nullable: spec.nullable || result.nullable,
652 values: result.values,
653 }
654 }
655 match func {
656 UnaryFunc::TryParseMonotonicIso8601Timestamp(_) => Some(SpecialUnary {
657 map_fn: |specs, range| {
658 let expr = MirScalarExpr::CallUnary {
659 func: UnaryFunc::TryParseMonotonicIso8601Timestamp(
660 crate::func::TryParseMonotonicIso8601Timestamp,
661 ),
662 expr: Box::new(MirScalarExpr::column(0)),
663 };
664 let eval = |d| specs.eval_result(expr.eval(&[d], specs.arena));
665
666 eagerly(range, |values| {
667 match values {
668 Values::Within(a, b) if a == b => eval(a),
669 Values::Within(a, b) => {
670 let spec = eval(a).union(eval(b));
671 let values_spec = if spec.nullable {
672 ResultSpec::value_all()
678 } else {
679 spec
680 };
681 values_spec.union(ResultSpec::null())
685 }
686 _ => ResultSpec::any_infallible(),
689 }
690 })
691 },
692 pushdownable: true,
693 }),
694 _ => None,
695 }
696 }
697}
698
699struct AbstractFunc {
710 handler: AbstractFuncHandler,
711 pushdownable: (bool, bool),
717}
718
719enum AbstractFuncHandler {
721 Override(for<'a> fn(ResultSpec<'a>, ResultSpec<'a>) -> ResultSpec<'a>),
724 DynamicMonotone(fn(&ResultSpec<'_>, &ResultSpec<'_>) -> (bool, bool)),
731}
732
733impl AbstractFunc {
734 fn for_func(func: &BinaryFunc) -> Option<AbstractFunc> {
736 fn eagerly<'b>(
740 left: ResultSpec<'b>,
741 right: ResultSpec<'b>,
742 value_fn: impl FnOnce(Values<'b>, Values<'b>) -> ResultSpec<'b>,
743 ) -> ResultSpec<'b> {
744 let result = match (left.values, right.values) {
745 (Values::Empty, _) | (_, Values::Empty) => ResultSpec::nothing(),
746 (l, r) => value_fn(l, r),
747 };
748 ResultSpec {
749 fallible: left.fallible || right.fallible || result.fallible,
750 nullable: left.nullable || right.nullable || result.nullable,
751 values: result.values,
752 }
753 }
754
755 fn jsonb_get_string<'b>(
756 left: ResultSpec<'b>,
757 right: ResultSpec<'b>,
758 stringify: bool,
759 ) -> ResultSpec<'b> {
760 eagerly(left, right, |left, right| {
761 let nested_spec = match (left, right) {
762 (Values::Nested(mut map_spec), Values::Within(key, key2)) if key == key2 => {
763 map_spec.remove(&key)
764 }
765 _ => None,
766 };
767
768 if let Some(field_spec) = nested_spec {
769 if stringify {
770 let values = match field_spec.values {
773 Values::Empty => Values::Empty,
774 Values::Within(min @ Datum::String(_), max @ Datum::String(_)) => {
775 Values::Within(min, max)
776 }
777 Values::Within(_, _) | Values::Nested(_) | Values::All => Values::All,
778 };
779 ResultSpec {
780 values,
781 ..field_spec
782 }
783 } else {
784 field_spec
785 }
786 } else {
787 ResultSpec::any_infallible()
792 }
793 })
794 }
795
796 fn eq<'b>(left: ResultSpec<'b>, right: ResultSpec<'b>) -> ResultSpec<'b> {
797 eagerly(left, right, |left, right| {
798 let maybe_true = match left.clone().intersect(right.clone()) {
800 Values::Empty => ResultSpec::nothing(),
801 _ => ResultSpec::value(Datum::True),
802 };
803
804 let maybe_false = match left.union(right) {
807 Values::Within(a, b) if a == b => ResultSpec::nothing(),
808 _ => ResultSpec::value(Datum::False),
809 };
810
811 maybe_true.union(maybe_false)
812 })
813 }
814
815 fn timestamp_plus_interval_monotone(
824 _left: &ResultSpec<'_>,
825 right: &ResultSpec<'_>,
826 ) -> (bool, bool) {
827 let months_zero = matches!(
828 right.values.as_single(),
829 Some(Datum::Interval(i)) if i.months == 0,
830 );
831 (months_zero, months_zero)
832 }
833
834 match func {
835 BinaryFunc::JsonbGetString(_) => Some(AbstractFunc {
836 handler: AbstractFuncHandler::Override(|l, r| jsonb_get_string(l, r, false)),
837 pushdownable: (true, false),
838 }),
839 BinaryFunc::JsonbGetStringStringify(_) => Some(AbstractFunc {
840 handler: AbstractFuncHandler::Override(|l, r| jsonb_get_string(l, r, true)),
841 pushdownable: (true, false),
842 }),
843 BinaryFunc::Eq(_) => Some(AbstractFunc {
844 handler: AbstractFuncHandler::Override(eq),
845 pushdownable: (true, true),
846 }),
847 BinaryFunc::AddTimestampInterval(_)
848 | BinaryFunc::AddTimestampTzInterval(_)
849 | BinaryFunc::SubTimestampInterval(_)
850 | BinaryFunc::SubTimestampTzInterval(_) => Some(AbstractFunc {
851 handler: AbstractFuncHandler::DynamicMonotone(timestamp_plus_interval_monotone),
852 pushdownable: (true, false),
859 }),
860 _ => None,
861 }
862 }
863}
864
865#[derive(Clone, Debug)]
866pub struct ColumnSpec<'a> {
867 pub col_type: ReprColumnType,
868 pub range: ResultSpec<'a>,
869}
870
871#[derive(Clone, Debug)]
877pub struct ColumnSpecs<'a> {
878 pub relation: &'a ReprRelationType,
879 pub columns: Vec<ResultSpec<'a>>,
880 pub unmaterializables: BTreeMap<UnmaterializableFunc, ResultSpec<'a>>,
881 pub arena: &'a RowArena,
882}
883
884impl<'a> ColumnSpecs<'a> {
885 const MAX_EVAL_ARGS: usize = 6;
892
893 pub fn new(relation: &'a ReprRelationType, arena: &'a RowArena) -> Self {
896 let columns = relation
897 .column_types
898 .iter()
899 .map(|ct| ResultSpec::has_type(ct, false))
900 .collect();
901 ColumnSpecs {
902 relation,
903 columns,
904 unmaterializables: Default::default(),
905 arena,
906 }
907 }
908
909 pub fn push_column(&mut self, id: usize, update: ResultSpec<'a>) {
912 let range = self.columns.get_mut(id).expect("valid column id");
913 *range = range.clone().intersect(update);
914 }
915
916 pub fn push_unmaterializable(&mut self, func: UnmaterializableFunc, update: ResultSpec<'a>) {
919 let range = self
920 .unmaterializables
921 .entry(func.clone())
922 .or_insert_with(|| ResultSpec::has_type(&func.output_type(), true));
923 *range = range.clone().intersect(update);
924 }
925
926 fn eval_result<'b, E>(&self, result: Result<Datum<'b>, E>) -> ResultSpec<'a> {
927 match result {
928 Ok(Datum::Null) => ResultSpec {
929 nullable: true,
930 ..ResultSpec::nothing()
931 },
932 Ok(d) => ResultSpec {
933 values: Values::just(self.arena.make_datum(|packer| packer.push(d))),
934 ..ResultSpec::nothing()
935 },
936 Err(_) => ResultSpec {
937 fallible: true,
938 ..ResultSpec::nothing()
939 },
940 }
941 }
942
943 fn set_literal(expr: &mut MirScalarExpr, update: Result<Datum, EvalError>) {
944 match expr {
945 MirScalarExpr::Literal(literal, col_type) => match update {
946 Err(error) => *literal = Err(error),
947 Ok(datum) => {
948 assert!(
949 datum.is_instance_of(col_type),
950 "{datum:?} must be an instance of {col_type:?}"
951 );
952 match literal {
953 Ok(row) => row.packer().push(datum),
955 literal => *literal = Ok(Row::pack_slice(&[datum])),
956 }
957 }
958 },
959 _ => panic!("not a literal"),
960 }
961 }
962
963 fn set_argument(expr: &mut MirScalarExpr, arg: usize, value: Result<Datum, EvalError>) {
964 match (expr, arg) {
965 (MirScalarExpr::CallUnary { expr, .. }, 0) => Self::set_literal(expr, value),
966 (MirScalarExpr::CallBinary { expr1, .. }, 0) => Self::set_literal(expr1, value),
967 (MirScalarExpr::CallBinary { expr2, .. }, 1) => Self::set_literal(expr2, value),
968 (MirScalarExpr::CallVariadic { exprs, .. }, n) if n < exprs.len() => {
969 Self::set_literal(&mut exprs[n], value)
970 }
971 _ => panic!("illegal argument for expression"),
972 }
973 }
974
975 fn placeholder(col_type: ReprColumnType) -> MirScalarExpr {
979 MirScalarExpr::Literal(Err(EvalError::Internal("".into())), col_type)
980 }
981}
982
983impl<'a> Interpreter for ColumnSpecs<'a> {
984 type Summary = ColumnSpec<'a>;
985
986 fn column(&self, id: usize) -> Self::Summary {
987 let col_type = self.relation.column_types[id].clone();
988 let range = self.columns[id].clone();
989 ColumnSpec { col_type, range }
990 }
991
992 fn literal(&self, result: &Result<Row, EvalError>, col_type: &ReprColumnType) -> Self::Summary {
993 let col_type = col_type.clone();
994 let range = self.eval_result(result.as_ref().map(|row| {
995 self.arena
996 .make_datum(|packer| packer.push(row.unpack_first()))
997 }));
998 ColumnSpec { col_type, range }
999 }
1000
1001 fn unmaterializable(&self, func: &UnmaterializableFunc) -> Self::Summary {
1002 let col_type = func.output_type();
1003 let range = self
1004 .unmaterializables
1005 .get(func)
1006 .cloned()
1007 .unwrap_or_else(|| ResultSpec::has_type(&func.output_type(), true));
1008 ColumnSpec { col_type, range }
1009 }
1010
1011 fn unary(&self, func: &UnaryFunc, summary: Self::Summary) -> Self::Summary {
1012 let fallible = func.could_error() || summary.range.fallible;
1013 let input_multivalued = !summary.range.is_single_value();
1021 let mapped_spec = if let Some(special) = SpecialUnary::for_func(func) {
1022 (special.map_fn)(self, summary.range)
1023 } else {
1024 let is_monotone = func.is_monotone();
1025 let mut expr = MirScalarExpr::CallUnary {
1026 func: func.clone(),
1027 expr: Box::new(Self::placeholder(summary.col_type.clone())),
1028 };
1029 summary.range.flat_map(is_monotone, |datum| {
1030 Self::set_argument(&mut expr, 0, datum);
1031 self.eval_result(expr.eval(&[], self.arena))
1032 })
1033 };
1034
1035 let col_type = func.output_type(summary.col_type);
1036
1037 let mut range = mapped_spec.intersect(ResultSpec::has_type(&col_type, fallible));
1038 if fallible && input_multivalued {
1042 range.fallible = true;
1043 }
1044 ColumnSpec { col_type, range }
1045 }
1046
1047 fn binary(
1048 &self,
1049 func: &BinaryFunc,
1050 left: Self::Summary,
1051 right: Self::Summary,
1052 ) -> Self::Summary {
1053 let fallible = func.could_error() || left.range.fallible || right.range.fallible;
1054 let inputs_multivalued = !left.range.is_single_value() || !right.range.is_single_value();
1057 let operand_may_be_infinite = left.range.may_be_infinite() || right.range.may_be_infinite();
1058
1059 let special = AbstractFunc::for_func(func);
1060 let (left_monotonic, right_monotonic) = match &special {
1061 Some(AbstractFunc {
1062 handler: AbstractFuncHandler::DynamicMonotone(monotone_fn),
1063 ..
1064 }) => monotone_fn(&left.range, &right.range),
1065 _ => func.is_monotone(),
1066 };
1067
1068 let mapped_spec = match special {
1069 Some(AbstractFunc {
1070 handler: AbstractFuncHandler::Override(f),
1071 ..
1072 }) => f(left.range, right.range),
1073 _ => {
1074 let mut expr = MirScalarExpr::CallBinary {
1075 func: func.clone(),
1076 expr1: Box::new(Self::placeholder(left.col_type.clone())),
1077 expr2: Box::new(Self::placeholder(right.col_type.clone())),
1078 };
1079 left.range.flat_map(left_monotonic, |left_result| {
1080 Self::set_argument(&mut expr, 0, left_result);
1081 right.range.flat_map(right_monotonic, |right_result| {
1082 Self::set_argument(&mut expr, 1, right_result);
1083 self.eval_result(expr.eval(&[], self.arena))
1084 })
1085 })
1086 }
1087 };
1088
1089 let col_type = func.output_type(&[left.col_type, right.col_type]);
1090
1091 let mut range = mapped_spec.intersect(ResultSpec::has_type(&col_type, fallible));
1092 if fallible && inputs_multivalued {
1095 range.fallible = true;
1096 }
1097 if operand_may_be_infinite && !func.is_infinity_monotone() {
1105 range.values = Values::All;
1106 }
1107 ColumnSpec { col_type, range }
1108 }
1109
1110 fn variadic(&self, func: &VariadicFunc, args: Vec<Self::Summary>) -> Self::Summary {
1111 let fallible = func.could_error() || args.iter().any(|s| s.range.fallible);
1112 let inputs_multivalued = args.iter().any(|s| !s.range.is_single_value());
1113 if func.is_associative() && args.len() > 2 {
1114 return args
1117 .into_iter()
1118 .reduce(|a, b| self.variadic(func, vec![a, b]))
1119 .expect("reducing over a non-empty argument list");
1120 }
1121
1122 let mapped_spec = if args.len() >= Self::MAX_EVAL_ARGS {
1123 ResultSpec::anything()
1124 } else {
1125 fn eval_loop<'a>(
1126 is_monotonic: bool,
1127 expr: &mut MirScalarExpr,
1128 args: &[ColumnSpec<'a>],
1129 index: usize,
1130 datum_map: &mut impl FnMut(&MirScalarExpr) -> ResultSpec<'a>,
1131 ) -> ResultSpec<'a> {
1132 if index >= args.len() {
1133 datum_map(expr)
1134 } else {
1135 args[index].range.flat_map(is_monotonic, |datum| {
1136 ColumnSpecs::set_argument(expr, index, datum);
1137 eval_loop(is_monotonic, expr, args, index + 1, datum_map)
1138 })
1139 }
1140 }
1141
1142 let mut fn_expr = MirScalarExpr::CallVariadic {
1143 func: func.clone(),
1144 exprs: args
1145 .iter()
1146 .map(|spec| Self::placeholder(spec.col_type.clone()))
1147 .collect(),
1148 };
1149 eval_loop(func.is_monotone(), &mut fn_expr, &args, 0, &mut |expr| {
1150 self.eval_result(expr.eval(&[], self.arena))
1151 })
1152 };
1153
1154 let col_types = args.into_iter().map(|spec| spec.col_type).collect();
1155 let col_type = func.output_type(col_types);
1156
1157 let mut range = mapped_spec.intersect(ResultSpec::has_type(&col_type, fallible));
1158 if fallible && inputs_multivalued {
1161 range.fallible = true;
1162 }
1163
1164 ColumnSpec { col_type, range }
1165 }
1166
1167 fn cond(&self, cond: Self::Summary, then: Self::Summary, els: Self::Summary) -> Self::Summary {
1168 let col_type = then
1169 .col_type
1170 .union(&els.col_type)
1171 .expect("failed type union for cond during abstract interpretation");
1172
1173 let range = cond
1174 .range
1175 .flat_map(true, |datum| match datum {
1176 Ok(Datum::True) => then.range.clone(),
1177 Ok(Datum::False) | Ok(Datum::Null) => els.range.clone(),
1182 _ => ResultSpec::fails(),
1183 })
1184 .intersect(ResultSpec::has_type(&col_type, true));
1185
1186 ColumnSpec { col_type, range }
1187 }
1188
1189 fn mfp_filter(&self, mfp: &MapFilterProject) -> Self::Summary {
1203 let mfp_eval = MfpEval::new(self, mfp.input_arity, &mfp.expressions);
1204 let predicates = mfp
1205 .predicates
1206 .iter()
1207 .map(|(_, e)| mfp_eval.expr(e))
1208 .collect();
1209 let mut result = self.variadic(&And.into(), predicates);
1210 if mfp_eval.expressions.iter().any(|s| s.range.fallible) {
1211 result.range.fallible = true;
1212 }
1213 result
1214 }
1215
1216 fn mfp_plan_filter(&self, plan: &MfpPlan) -> Self::Summary {
1217 let mfp_eval = MfpEval::new(self, plan.mfp.input_arity, &plan.mfp.expressions);
1218 let mut results: Vec<_> = plan
1219 .mfp
1220 .predicates
1221 .iter()
1222 .map(|(_, e)| mfp_eval.expr(e))
1223 .collect();
1224 let mz_now = mfp_eval.unmaterializable(&UnmaterializableFunc::MzNow);
1225 for bound in &plan.lower_bounds {
1226 let bound_range = mfp_eval.expr(bound);
1227 let result = mfp_eval.binary(&BinaryFunc::Lte(func::Lte), bound_range, mz_now.clone());
1228 results.push(result);
1229 }
1230 for bound in &plan.upper_bounds {
1231 let bound_range = mfp_eval.expr(bound);
1232 let result = mfp_eval.binary(&BinaryFunc::Gte(func::Gte), bound_range, mz_now.clone());
1233 results.push(result);
1234 }
1235 let mut result = self.variadic(&And.into(), results);
1236 if mfp_eval.expressions.iter().any(|s| s.range.fallible) {
1237 result.range.fallible = true;
1238 }
1239 result
1240 }
1241}
1242
1243#[derive(Debug)]
1252pub struct Trace;
1253
1254#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Ord, Eq)]
1261pub enum TraceSummary {
1262 Constant,
1265 Dynamic,
1270 Unknown,
1273}
1274
1275impl TraceSummary {
1276 fn apply_fn(self, pushdownable: bool) -> Self {
1281 match self {
1282 TraceSummary::Constant => TraceSummary::Constant,
1283 TraceSummary::Dynamic => match pushdownable {
1284 true => TraceSummary::Dynamic,
1285 false => TraceSummary::Unknown,
1286 },
1287 TraceSummary::Unknown => TraceSummary::Unknown,
1288 }
1289 }
1290
1291 pub fn pushdownable(self) -> bool {
1293 match self {
1294 TraceSummary::Constant | TraceSummary::Dynamic => true,
1295 TraceSummary::Unknown => false,
1296 }
1297 }
1298}
1299
1300impl Interpreter for Trace {
1301 type Summary = TraceSummary;
1302
1303 fn column(&self, _id: usize) -> Self::Summary {
1304 TraceSummary::Dynamic
1305 }
1306
1307 fn literal(
1308 &self,
1309 _result: &Result<Row, EvalError>,
1310 _col_type: &ReprColumnType,
1311 ) -> Self::Summary {
1312 TraceSummary::Constant
1313 }
1314
1315 fn unmaterializable(&self, _func: &UnmaterializableFunc) -> Self::Summary {
1316 TraceSummary::Dynamic
1317 }
1318
1319 fn unary(&self, func: &UnaryFunc, expr: Self::Summary) -> Self::Summary {
1320 let pushdownable = match SpecialUnary::for_func(func) {
1321 None => func.is_monotone(),
1322 Some(special) => special.pushdownable,
1323 };
1324 expr.apply_fn(pushdownable)
1325 }
1326
1327 fn binary(
1328 &self,
1329 func: &BinaryFunc,
1330 left: Self::Summary,
1331 right: Self::Summary,
1332 ) -> Self::Summary {
1333 let (left_pushdownable, right_pushdownable) = match AbstractFunc::for_func(func) {
1334 None => func.is_monotone(),
1335 Some(special) => special.pushdownable,
1336 };
1337 left.apply_fn(left_pushdownable)
1338 .max(right.apply_fn(right_pushdownable))
1339 }
1340
1341 fn variadic(&self, func: &VariadicFunc, exprs: Vec<Self::Summary>) -> Self::Summary {
1342 if !func.is_associative() && exprs.len() >= ColumnSpecs::MAX_EVAL_ARGS {
1343 return TraceSummary::Unknown;
1346 }
1347
1348 let pushdownable_fn = func.is_monotone();
1349 exprs
1350 .into_iter()
1351 .map(|pushdownable_arg| pushdownable_arg.apply_fn(pushdownable_fn))
1352 .max()
1353 .unwrap_or(TraceSummary::Constant)
1354 }
1355
1356 fn cond(&self, cond: Self::Summary, then: Self::Summary, els: Self::Summary) -> Self::Summary {
1357 let cond = cond.min(TraceSummary::Dynamic);
1360 cond.max(then).max(els)
1361 }
1362}
1363
1364#[cfg(test)]
1365mod tests {
1366 use itertools::Itertools;
1367 use mz_repr::adt::datetime::DateTimeUnits;
1368 use mz_repr::{Datum, PropDatum, RowArena, SqlScalarType};
1369 use proptest::prelude::*;
1370 use proptest::sample::{Index, select};
1371
1372 use crate::func::*;
1373 use crate::scalar::func::variadic::Concat;
1374 use crate::{BinaryFunc, MirScalarExpr, UnaryFunc};
1375
1376 use super::*;
1377
1378 #[derive(Debug)]
1379 struct ExpressionData {
1380 relation_type: ReprRelationType,
1381 specs: Vec<ResultSpec<'static>>,
1382 rows: Vec<Row>,
1383 expr: MirScalarExpr,
1384 }
1385
1386 const NUM_TYPE: ReprScalarType = ReprScalarType::Numeric;
1391 static SCALAR_TYPES: &[ReprScalarType] = &[
1392 ReprScalarType::Bool,
1393 ReprScalarType::Jsonb,
1394 NUM_TYPE,
1395 ReprScalarType::Int16,
1396 ReprScalarType::Int32,
1397 ReprScalarType::Int64,
1398 ReprScalarType::UInt16,
1399 ReprScalarType::UInt32,
1400 ReprScalarType::UInt64,
1401 ReprScalarType::Float32,
1402 ReprScalarType::Float64,
1403 ReprScalarType::Date,
1404 ReprScalarType::Time,
1405 ReprScalarType::Timestamp,
1406 ReprScalarType::TimestampTz,
1407 ReprScalarType::MzTimestamp,
1408 ReprScalarType::Interval,
1409 ReprScalarType::String,
1410 ];
1411
1412 const INTERESTING_UNARY_FUNCS: &[UnaryFunc] = {
1413 &[
1414 UnaryFunc::CastNumericToMzTimestamp(CastNumericToMzTimestamp),
1415 UnaryFunc::CastTimestampToMzTimestamp(CastTimestampToMzTimestamp),
1416 UnaryFunc::NegNumeric(NegNumeric),
1417 UnaryFunc::NegFloat64(NegFloat64),
1418 UnaryFunc::CastJsonbToNumeric(CastJsonbToNumeric(None)),
1419 UnaryFunc::CastJsonbToBool(CastJsonbToBool),
1420 UnaryFunc::CastJsonbToString(CastJsonbToString),
1421 UnaryFunc::DateTruncTimestamp(DateTruncTimestamp(DateTimeUnits::Epoch)),
1422 UnaryFunc::ExtractTimestamp(ExtractTimestamp(DateTimeUnits::Epoch)),
1423 UnaryFunc::ExtractDate(ExtractDate(DateTimeUnits::Epoch)),
1424 UnaryFunc::Not(Not),
1425 UnaryFunc::IsNull(IsNull),
1426 UnaryFunc::IsFalse(IsFalse),
1427 UnaryFunc::TryParseMonotonicIso8601Timestamp(TryParseMonotonicIso8601Timestamp),
1428 UnaryFunc::NegInt32(NegInt32),
1433 UnaryFunc::NegInt64(NegInt64),
1434 UnaryFunc::CastInt32ToUint32(CastInt32ToUint32),
1435 UnaryFunc::CastInt64ToInt32(CastInt64ToInt32),
1436 UnaryFunc::CastInt64ToNumeric(CastInt64ToNumeric(None)),
1437 UnaryFunc::CastFloat64ToInt64(CastFloat64ToInt64),
1438 UnaryFunc::CastFloat64ToFloat32(CastFloat64ToFloat32),
1439 UnaryFunc::CastFloat32ToFloat64(CastFloat32ToFloat64),
1440 UnaryFunc::CastNumericToInt64(CastNumericToInt64),
1441 UnaryFunc::CeilNumeric(CeilNumeric),
1442 UnaryFunc::FloorNumeric(FloorNumeric),
1443 UnaryFunc::CastDateToTimestamp(CastDateToTimestamp(None)),
1444 UnaryFunc::CastTimestampToTimestampTz(CastTimestampToTimestampTz {
1445 from: None,
1446 to: None,
1447 }),
1448 UnaryFunc::CastTimestampTzToTimestamp(CastTimestampTzToTimestamp {
1449 from: None,
1450 to: None,
1451 }),
1452 UnaryFunc::ExtractTimestamp(ExtractTimestamp(DateTimeUnits::Year)),
1455 UnaryFunc::ExtractTimestamp(ExtractTimestamp(DateTimeUnits::Month)),
1456 UnaryFunc::ExtractTimestampTz(ExtractTimestampTz(DateTimeUnits::Epoch)),
1457 UnaryFunc::ExtractTimestampTz(ExtractTimestampTz(DateTimeUnits::Year)),
1458 UnaryFunc::CastBoolToInt32(CastBoolToInt32),
1462 UnaryFunc::CastBoolToString(CastBoolToString),
1463 UnaryFunc::NegInt16(NegInt16),
1464 UnaryFunc::CastInt16ToInt32(CastInt16ToInt32),
1465 UnaryFunc::CastInt16ToInt64(CastInt16ToInt64),
1466 UnaryFunc::CastInt16ToFloat32(CastInt16ToFloat32),
1467 UnaryFunc::CastInt16ToFloat64(CastInt16ToFloat64),
1468 UnaryFunc::CastInt16ToUint16(CastInt16ToUint16),
1469 UnaryFunc::CastInt16ToNumeric(CastInt16ToNumeric(None)),
1470 UnaryFunc::CastInt32ToInt16(CastInt32ToInt16),
1471 UnaryFunc::CastInt32ToInt64(CastInt32ToInt64),
1472 UnaryFunc::CastInt32ToFloat32(CastInt32ToFloat32),
1473 UnaryFunc::CastInt32ToFloat64(CastInt32ToFloat64),
1474 UnaryFunc::CastInt32ToUint16(CastInt32ToUint16),
1475 UnaryFunc::CastInt32ToNumeric(CastInt32ToNumeric(None)),
1476 UnaryFunc::CastInt32ToMzTimestamp(CastInt32ToMzTimestamp),
1477 UnaryFunc::CastInt64ToInt16(CastInt64ToInt16),
1478 UnaryFunc::CastInt64ToFloat32(CastInt64ToFloat32),
1479 UnaryFunc::CastInt64ToFloat64(CastInt64ToFloat64),
1480 UnaryFunc::CastInt64ToUint64(CastInt64ToUint64),
1481 UnaryFunc::CastInt64ToMzTimestamp(CastInt64ToMzTimestamp),
1482 UnaryFunc::CastUint64ToUint32(CastUint64ToUint32),
1483 UnaryFunc::CastUint64ToInt32(CastUint64ToInt32),
1484 UnaryFunc::CastUint64ToNumeric(CastUint64ToNumeric(None)),
1485 UnaryFunc::CastUint64ToMzTimestamp(CastUint64ToMzTimestamp),
1486 UnaryFunc::NegFloat32(NegFloat32),
1487 UnaryFunc::FloorFloat32(FloorFloat32),
1488 UnaryFunc::CastFloat32ToInt32(CastFloat32ToInt32),
1489 UnaryFunc::CastFloat32ToNumeric(CastFloat32ToNumeric(None)),
1490 UnaryFunc::FloorFloat64(FloorFloat64),
1491 UnaryFunc::CastFloat64ToInt32(CastFloat64ToInt32),
1492 UnaryFunc::CastFloat64ToUint64(CastFloat64ToUint64),
1493 UnaryFunc::CastFloat64ToNumeric(CastFloat64ToNumeric(None)),
1494 UnaryFunc::RoundNumeric(RoundNumeric),
1495 UnaryFunc::TruncNumeric(TruncNumeric),
1496 UnaryFunc::Log10Numeric(Log10Numeric),
1497 UnaryFunc::CastNumericToFloat64(CastNumericToFloat64),
1498 UnaryFunc::CastNumericToInt32(CastNumericToInt32),
1499 UnaryFunc::CastTimestampToDate(CastTimestampToDate),
1500 UnaryFunc::CastDateToMzTimestamp(CastDateToMzTimestamp),
1501 UnaryFunc::StepMzTimestamp(StepMzTimestamp),
1502 UnaryFunc::CastBoolToStringNonstandard(CastBoolToStringNonstandard),
1506 UnaryFunc::CastBoolToInt64(CastBoolToInt64),
1507 UnaryFunc::CastInt16ToUint32(CastInt16ToUint32),
1508 UnaryFunc::CastInt16ToUint64(CastInt16ToUint64),
1509 UnaryFunc::CastInt32ToUint64(CastInt32ToUint64),
1510 UnaryFunc::CastInt64ToUint16(CastInt64ToUint16),
1511 UnaryFunc::CastInt64ToUint32(CastInt64ToUint32),
1512 UnaryFunc::CastUint16ToUint32(CastUint16ToUint32),
1513 UnaryFunc::CastUint16ToUint64(CastUint16ToUint64),
1514 UnaryFunc::CastUint16ToInt16(CastUint16ToInt16),
1515 UnaryFunc::CastUint16ToInt32(CastUint16ToInt32),
1516 UnaryFunc::CastUint16ToFloat32(CastUint16ToFloat32),
1517 UnaryFunc::CastUint16ToFloat64(CastUint16ToFloat64),
1518 UnaryFunc::CastUint16ToNumeric(CastUint16ToNumeric(None)),
1519 UnaryFunc::CastUint16ToInt64(CastUint16ToInt64),
1520 UnaryFunc::BitNotUint16(BitNotUint16),
1521 UnaryFunc::CastUint32ToUint16(CastUint32ToUint16),
1522 UnaryFunc::CastUint32ToUint64(CastUint32ToUint64),
1523 UnaryFunc::CastUint32ToInt32(CastUint32ToInt32),
1524 UnaryFunc::CastUint32ToInt64(CastUint32ToInt64),
1525 UnaryFunc::CastUint32ToFloat32(CastUint32ToFloat32),
1526 UnaryFunc::CastUint32ToFloat64(CastUint32ToFloat64),
1527 UnaryFunc::CastUint32ToNumeric(CastUint32ToNumeric(None)),
1528 UnaryFunc::CastUint32ToInt16(CastUint32ToInt16),
1529 UnaryFunc::CastUint32ToMzTimestamp(CastUint32ToMzTimestamp),
1530 UnaryFunc::BitNotUint32(BitNotUint32),
1531 UnaryFunc::CastUint64ToUint16(CastUint64ToUint16),
1532 UnaryFunc::CastUint64ToInt16(CastUint64ToInt16),
1533 UnaryFunc::CastUint64ToInt64(CastUint64ToInt64),
1534 UnaryFunc::CastUint64ToFloat32(CastUint64ToFloat32),
1535 UnaryFunc::CastUint64ToFloat64(CastUint64ToFloat64),
1536 UnaryFunc::BitNotUint64(BitNotUint64),
1537 UnaryFunc::CastFloat32ToInt16(CastFloat32ToInt16),
1538 UnaryFunc::CastFloat32ToInt64(CastFloat32ToInt64),
1539 UnaryFunc::CastFloat32ToUint16(CastFloat32ToUint16),
1540 UnaryFunc::CastFloat32ToUint32(CastFloat32ToUint32),
1541 UnaryFunc::CastFloat32ToUint64(CastFloat32ToUint64),
1542 UnaryFunc::CastFloat64ToInt16(CastFloat64ToInt16),
1543 UnaryFunc::CastFloat64ToUint16(CastFloat64ToUint16),
1544 UnaryFunc::CastFloat64ToUint32(CastFloat64ToUint32),
1545 UnaryFunc::CastJsonbToInt16(CastJsonbToInt16),
1546 UnaryFunc::CastJsonbToInt32(CastJsonbToInt32),
1547 UnaryFunc::CastJsonbToInt64(CastJsonbToInt64),
1548 UnaryFunc::CastJsonbToFloat32(CastJsonbToFloat32),
1549 UnaryFunc::CastJsonbToFloat64(CastJsonbToFloat64),
1550 UnaryFunc::CastNumericToInt16(CastNumericToInt16),
1551 UnaryFunc::CastNumericToFloat32(CastNumericToFloat32),
1552 UnaryFunc::CastNumericToUint16(CastNumericToUint16),
1553 UnaryFunc::CastNumericToUint32(CastNumericToUint32),
1554 UnaryFunc::CastNumericToUint64(CastNumericToUint64),
1555 UnaryFunc::CastTimestampTzToDate(CastTimestampTzToDate),
1556 UnaryFunc::CastTimestampTzToMzTimestamp(CastTimestampTzToMzTimestamp),
1557 UnaryFunc::DateTruncTimestampTz(DateTruncTimestampTz(DateTimeUnits::Epoch)),
1558 UnaryFunc::CastDateToTimestampTz(CastDateToTimestampTz(None)),
1559 UnaryFunc::ExtractDate(ExtractDate(DateTimeUnits::Year)),
1560 UnaryFunc::ExtractDate(ExtractDate(DateTimeUnits::Day)),
1561 ]
1562 };
1563
1564 fn unary_typecheck(func: &UnaryFunc, arg: &ReprColumnType) -> bool {
1565 use UnaryFunc::*;
1566 match func {
1567 CastNumericToMzTimestamp(_)
1568 | NegNumeric(_)
1569 | CastNumericToInt64(_)
1570 | CeilNumeric(_)
1571 | FloorNumeric(_)
1572 | RoundNumeric(_)
1573 | TruncNumeric(_)
1574 | Log10Numeric(_)
1575 | CastNumericToFloat64(_)
1576 | CastNumericToInt32(_)
1577 | CastNumericToInt16(_)
1578 | CastNumericToFloat32(_)
1579 | CastNumericToUint16(_)
1580 | CastNumericToUint32(_)
1581 | CastNumericToUint64(_) => arg.scalar_type == NUM_TYPE,
1582 NegFloat64(_)
1583 | CastFloat64ToInt64(_)
1584 | CastFloat64ToFloat32(_)
1585 | FloorFloat64(_)
1586 | CastFloat64ToInt32(_)
1587 | CastFloat64ToUint64(_)
1588 | CastFloat64ToNumeric(_)
1589 | CastFloat64ToInt16(_)
1590 | CastFloat64ToUint16(_)
1591 | CastFloat64ToUint32(_) => arg.scalar_type == ReprScalarType::Float64,
1592 CastFloat32ToFloat64(_)
1593 | NegFloat32(_)
1594 | FloorFloat32(_)
1595 | CastFloat32ToInt32(_)
1596 | CastFloat32ToNumeric(_)
1597 | CastFloat32ToInt16(_)
1598 | CastFloat32ToInt64(_)
1599 | CastFloat32ToUint16(_)
1600 | CastFloat32ToUint32(_)
1601 | CastFloat32ToUint64(_) => arg.scalar_type == ReprScalarType::Float32,
1602 NegInt16(_)
1603 | CastInt16ToInt32(_)
1604 | CastInt16ToInt64(_)
1605 | CastInt16ToFloat32(_)
1606 | CastInt16ToFloat64(_)
1607 | CastInt16ToUint16(_)
1608 | CastInt16ToNumeric(_)
1609 | CastInt16ToUint32(_)
1610 | CastInt16ToUint64(_) => arg.scalar_type == ReprScalarType::Int16,
1611 NegInt32(_)
1612 | CastInt32ToUint32(_)
1613 | CastInt32ToInt16(_)
1614 | CastInt32ToInt64(_)
1615 | CastInt32ToFloat32(_)
1616 | CastInt32ToFloat64(_)
1617 | CastInt32ToUint16(_)
1618 | CastInt32ToNumeric(_)
1619 | CastInt32ToMzTimestamp(_)
1620 | CastInt32ToUint64(_) => arg.scalar_type == ReprScalarType::Int32,
1621 NegInt64(_)
1622 | CastInt64ToInt32(_)
1623 | CastInt64ToNumeric(_)
1624 | CastInt64ToInt16(_)
1625 | CastInt64ToFloat32(_)
1626 | CastInt64ToFloat64(_)
1627 | CastInt64ToUint64(_)
1628 | CastInt64ToMzTimestamp(_)
1629 | CastInt64ToUint16(_)
1630 | CastInt64ToUint32(_) => arg.scalar_type == ReprScalarType::Int64,
1631 CastUint16ToUint32(_)
1632 | CastUint16ToUint64(_)
1633 | CastUint16ToInt16(_)
1634 | CastUint16ToInt32(_)
1635 | CastUint16ToFloat32(_)
1636 | CastUint16ToFloat64(_)
1637 | CastUint16ToNumeric(_)
1638 | CastUint16ToInt64(_)
1639 | BitNotUint16(_) => arg.scalar_type == ReprScalarType::UInt16,
1640 CastUint32ToUint16(_)
1641 | CastUint32ToUint64(_)
1642 | CastUint32ToInt32(_)
1643 | CastUint32ToInt64(_)
1644 | CastUint32ToFloat32(_)
1645 | CastUint32ToFloat64(_)
1646 | CastUint32ToNumeric(_)
1647 | CastUint32ToInt16(_)
1648 | CastUint32ToMzTimestamp(_)
1649 | BitNotUint32(_) => arg.scalar_type == ReprScalarType::UInt32,
1650 CastUint64ToUint32(_)
1651 | CastUint64ToInt32(_)
1652 | CastUint64ToNumeric(_)
1653 | CastUint64ToMzTimestamp(_)
1654 | CastUint64ToUint16(_)
1655 | CastUint64ToInt16(_)
1656 | CastUint64ToInt64(_)
1657 | CastUint64ToFloat32(_)
1658 | CastUint64ToFloat64(_)
1659 | BitNotUint64(_) => arg.scalar_type == ReprScalarType::UInt64,
1660 StepMzTimestamp(_) => arg.scalar_type == ReprScalarType::MzTimestamp,
1661 CastBoolToInt32(_)
1662 | CastBoolToString(_)
1663 | CastBoolToStringNonstandard(_)
1664 | CastBoolToInt64(_) => arg.scalar_type == ReprScalarType::Bool,
1665 CastTimestampToMzTimestamp(_)
1666 | CastTimestampToTimestampTz(_)
1667 | CastTimestampToDate(_) => arg.scalar_type == ReprScalarType::Timestamp,
1668 CastTimestampTzToTimestamp(_)
1669 | ExtractTimestampTz(_)
1670 | CastTimestampTzToDate(_)
1671 | CastTimestampTzToMzTimestamp(_)
1672 | DateTruncTimestampTz(_) => arg.scalar_type == ReprScalarType::TimestampTz,
1673 CastJsonbToNumeric(_)
1674 | CastJsonbToBool(_)
1675 | CastJsonbToString(_)
1676 | CastJsonbToInt16(_)
1677 | CastJsonbToInt32(_)
1678 | CastJsonbToInt64(_)
1679 | CastJsonbToFloat32(_)
1680 | CastJsonbToFloat64(_) => arg.scalar_type == ReprScalarType::Jsonb,
1681 ExtractTimestamp(_) | DateTruncTimestamp(_) => {
1682 arg.scalar_type == ReprScalarType::Timestamp
1683 }
1684 ExtractDate(_)
1685 | CastDateToTimestamp(_)
1686 | CastDateToMzTimestamp(_)
1687 | CastDateToTimestampTz(_) => arg.scalar_type == ReprScalarType::Date,
1688 Not(_) => arg.scalar_type == ReprScalarType::Bool,
1689 IsNull(_) => true,
1690 TryParseMonotonicIso8601Timestamp(_) => arg.scalar_type == ReprScalarType::String,
1691 TryCast(f) => unary_typecheck(f.inner(), arg),
1692 _ => false,
1693 }
1694 }
1695
1696 const TRY_CAST_INNER_FUNCS: &[UnaryFunc] = &[
1700 UnaryFunc::CastInt64ToInt32(CastInt64ToInt32),
1701 UnaryFunc::CastInt32ToInt16(CastInt32ToInt16),
1702 UnaryFunc::CastInt64ToUint64(CastInt64ToUint64),
1703 UnaryFunc::CastFloat64ToInt64(CastFloat64ToInt64),
1704 UnaryFunc::CastFloat64ToFloat32(CastFloat64ToFloat32),
1705 UnaryFunc::CastNumericToInt32(CastNumericToInt32),
1706 UnaryFunc::CastNumericToMzTimestamp(CastNumericToMzTimestamp),
1707 UnaryFunc::CastJsonbToNumeric(CastJsonbToNumeric(None)),
1708 UnaryFunc::CastJsonbToBool(CastJsonbToBool),
1709 UnaryFunc::CastTimestampToMzTimestamp(CastTimestampToMzTimestamp),
1710 ];
1711
1712 fn interesting_unary_funcs() -> Vec<UnaryFunc> {
1716 let mut funcs = INTERESTING_UNARY_FUNCS.to_vec();
1717 funcs.extend(TRY_CAST_INNER_FUNCS.iter().map(|cast| {
1718 assert!(
1719 cast.could_error(),
1720 "{cast} never errors, so wrapping it is pointless"
1721 );
1722 UnaryFunc::try_cast_if_fallible(cast.clone())
1723 }));
1724 funcs
1725 }
1726
1727 fn interesting_binary_funcs() -> Vec<BinaryFunc> {
1728 vec![
1729 AddTimestampInterval.into(),
1730 AddNumeric.into(),
1731 SubNumeric.into(),
1732 MulNumeric.into(),
1733 DivNumeric.into(),
1734 AddFloat64.into(),
1735 SubFloat64.into(),
1736 MulFloat64.into(),
1737 DivFloat64.into(),
1738 MulFloat32.into(),
1739 DivFloat32.into(),
1740 RoundNumericBinary.into(),
1741 Eq.into(),
1742 Lt.into(),
1743 Gt.into(),
1744 Lte.into(),
1745 Gte.into(),
1746 DateTruncUnitsTimestamp.into(),
1747 JsonbGetString.into(),
1748 JsonbGetStringStringify.into(),
1749 AddInt32.into(),
1753 SubInt32.into(),
1754 MulInt32.into(),
1755 DivInt32.into(),
1756 AddInt64.into(),
1757 MulInt64.into(),
1758 AddFloat32.into(),
1759 SubFloat32.into(),
1760 TextConcatBinary.into(),
1762 AddDateInterval.into(),
1764 AddTimeInterval.into(),
1765 SubInt64.into(),
1767 DivInt64.into(),
1768 SubTimestamp.into(),
1769 SubDate.into(),
1770 AddInterval.into(),
1771 SubInterval.into(),
1772 AddInt16.into(),
1775 SubInt16.into(),
1776 MulInt16.into(),
1777 DivInt16.into(),
1778 AddUint16.into(),
1779 SubUint16.into(),
1780 MulUint16.into(),
1781 DivUint16.into(),
1782 AddUint32.into(),
1783 SubUint32.into(),
1784 MulUint32.into(),
1785 DivUint32.into(),
1786 AddUint64.into(),
1787 SubUint64.into(),
1788 MulUint64.into(),
1789 DivUint64.into(),
1790 SubTime.into(),
1791 SubTimestampTz.into(),
1792 AddDateTime.into(),
1793 SubDateInterval.into(),
1794 DateBinTimestamp.into(),
1795 ]
1796 }
1797
1798 fn binary_typecheck(func: &BinaryFunc, arg0: &ReprColumnType, arg1: &ReprColumnType) -> bool {
1799 use BinaryFunc::*;
1800 match func {
1801 AddTimestampInterval(_) => {
1802 arg0.scalar_type == ReprScalarType::Timestamp
1803 && arg1.scalar_type == ReprScalarType::Interval
1804 }
1805 AddNumeric(_) | SubNumeric(_) | MulNumeric(_) | DivNumeric(_) => {
1806 arg0.scalar_type == NUM_TYPE && arg1.scalar_type == NUM_TYPE
1807 }
1808 AddFloat64(_) | SubFloat64(_) | MulFloat64(_) | DivFloat64(_) => {
1809 arg0.scalar_type == ReprScalarType::Float64
1810 && arg1.scalar_type == ReprScalarType::Float64
1811 }
1812 MulFloat32(_) | DivFloat32(_) => {
1813 arg0.scalar_type == ReprScalarType::Float32
1814 && arg1.scalar_type == ReprScalarType::Float32
1815 }
1816 RoundNumeric(_) => {
1817 arg0.scalar_type == NUM_TYPE && arg1.scalar_type == ReprScalarType::Int32
1818 }
1819 Eq(_) | Lt(_) | Gt(_) | Lte(_) | Gte(_) => arg0.scalar_type == arg1.scalar_type,
1820 DateTruncTimestamp(_) => {
1821 arg0.scalar_type == ReprScalarType::String
1822 && arg1.scalar_type == ReprScalarType::Timestamp
1823 }
1824 JsonbGetString(_) | JsonbGetStringStringify(_) => {
1825 arg0.scalar_type == ReprScalarType::Jsonb
1826 && arg1.scalar_type == ReprScalarType::String
1827 }
1828 AddInt32(_) | SubInt32(_) | MulInt32(_) | DivInt32(_) => {
1829 arg0.scalar_type == ReprScalarType::Int32
1830 && arg1.scalar_type == ReprScalarType::Int32
1831 }
1832 AddInt64(_) | MulInt64(_) | SubInt64(_) | DivInt64(_) => {
1833 arg0.scalar_type == ReprScalarType::Int64
1834 && arg1.scalar_type == ReprScalarType::Int64
1835 }
1836 SubTimestamp(_) => {
1837 arg0.scalar_type == ReprScalarType::Timestamp
1838 && arg1.scalar_type == ReprScalarType::Timestamp
1839 }
1840 SubDate(_) => {
1841 arg0.scalar_type == ReprScalarType::Date && arg1.scalar_type == ReprScalarType::Date
1842 }
1843 AddInterval(_) | SubInterval(_) => {
1844 arg0.scalar_type == ReprScalarType::Interval
1845 && arg1.scalar_type == ReprScalarType::Interval
1846 }
1847 AddInt16(_) | SubInt16(_) | MulInt16(_) | DivInt16(_) => {
1848 arg0.scalar_type == ReprScalarType::Int16
1849 && arg1.scalar_type == ReprScalarType::Int16
1850 }
1851 AddUint16(_) | SubUint16(_) | MulUint16(_) | DivUint16(_) => {
1852 arg0.scalar_type == ReprScalarType::UInt16
1853 && arg1.scalar_type == ReprScalarType::UInt16
1854 }
1855 AddUint32(_) | SubUint32(_) | MulUint32(_) | DivUint32(_) => {
1856 arg0.scalar_type == ReprScalarType::UInt32
1857 && arg1.scalar_type == ReprScalarType::UInt32
1858 }
1859 AddUint64(_) | SubUint64(_) | MulUint64(_) | DivUint64(_) => {
1860 arg0.scalar_type == ReprScalarType::UInt64
1861 && arg1.scalar_type == ReprScalarType::UInt64
1862 }
1863 SubTime(_) => {
1864 arg0.scalar_type == ReprScalarType::Time && arg1.scalar_type == ReprScalarType::Time
1865 }
1866 SubTimestampTz(_) => {
1867 arg0.scalar_type == ReprScalarType::TimestampTz
1868 && arg1.scalar_type == ReprScalarType::TimestampTz
1869 }
1870 AddDateTime(_) => {
1871 arg0.scalar_type == ReprScalarType::Date && arg1.scalar_type == ReprScalarType::Time
1872 }
1873 SubDateInterval(_) => {
1874 arg0.scalar_type == ReprScalarType::Date
1875 && arg1.scalar_type == ReprScalarType::Interval
1876 }
1877 DateBinTimestamp(_) => {
1878 arg0.scalar_type == ReprScalarType::Interval
1879 && arg1.scalar_type == ReprScalarType::Timestamp
1880 }
1881 AddFloat32(_) | SubFloat32(_) => {
1882 arg0.scalar_type == ReprScalarType::Float32
1883 && arg1.scalar_type == ReprScalarType::Float32
1884 }
1885 TextConcat(_) => {
1886 arg0.scalar_type == ReprScalarType::String
1887 && arg1.scalar_type == ReprScalarType::String
1888 }
1889 AddDateInterval(_) => {
1890 arg0.scalar_type == ReprScalarType::Date
1891 && arg1.scalar_type == ReprScalarType::Interval
1892 }
1893 AddTimeInterval(_) => {
1894 arg0.scalar_type == ReprScalarType::Time
1895 && arg1.scalar_type == ReprScalarType::Interval
1896 }
1897 _ => false,
1898 }
1899 }
1900
1901 const INTERESTING_VARIADIC_FUNCS: &[VariadicFunc] = {
1902 use crate::scalar::func::variadic as v;
1903 use VariadicFunc::*;
1904 &[
1905 Coalesce(v::Coalesce),
1906 Greatest(v::Greatest),
1907 Least(v::Least),
1908 And(v::And),
1909 Or(v::Or),
1910 Concat(v::Concat),
1911 ConcatWs(v::ConcatWs),
1912 ]
1913 };
1914
1915 fn variadic_typecheck(func: &VariadicFunc, args: &[ReprColumnType]) -> bool {
1916 use VariadicFunc::*;
1917 fn all_eq<'a>(
1918 iter: impl IntoIterator<Item = &'a ReprColumnType>,
1919 other: &ReprScalarType,
1920 ) -> bool {
1921 iter.into_iter().all(|t| t.scalar_type == *other)
1922 }
1923 match func {
1924 Coalesce(_) | Greatest(_) | Least(_) => match args {
1925 [] => true,
1926 [first, rest @ ..] => all_eq(rest, &first.scalar_type),
1927 },
1928 And(_) | Or(_) => all_eq(args, &ReprScalarType::Bool),
1929 Concat(_) => all_eq(args, &ReprScalarType::String),
1930 ConcatWs(_) => args.len() > 1 && all_eq(args, &ReprScalarType::String),
1931 _ => false,
1932 }
1933 }
1934
1935 fn gen_datums_for_type(typ: &ReprColumnType) -> BoxedStrategy<Datum<'static>> {
1936 let mut values: Vec<Datum<'static>> = SqlScalarType::from_repr(&typ.scalar_type)
1937 .interesting_datums()
1938 .collect();
1939 if typ.nullable {
1940 values.push(Datum::Null)
1941 }
1942 select(values).boxed()
1943 }
1944
1945 fn gen_column() -> impl Strategy<Value = (ReprColumnType, Datum<'static>, ResultSpec<'static>)>
1946 {
1947 let col_type = (select(SCALAR_TYPES), any::<bool>())
1948 .prop_map(|(t, b)| t.nullable(b))
1949 .prop_filter("need at least one value", |c| {
1950 SqlScalarType::from_repr(&c.scalar_type)
1951 .interesting_datums()
1952 .count()
1953 > 0
1954 });
1955
1956 let result_spec = select(vec![
1957 ResultSpec::nothing(),
1958 ResultSpec::null(),
1959 ResultSpec::anything(),
1960 ResultSpec::value_all(),
1961 ]);
1962
1963 (col_type, result_spec).prop_flat_map(|(col, result_spec)| {
1964 gen_datums_for_type(&col).prop_map(move |datum| {
1965 let result_spec = result_spec.clone().union(ResultSpec::value(datum));
1966 (col.clone(), datum, result_spec)
1967 })
1968 })
1969 }
1970
1971 fn gen_expr_for_relation(
1972 relation: &ReprRelationType,
1973 ) -> BoxedStrategy<(MirScalarExpr, ReprColumnType)> {
1974 let column_gen = {
1975 let column_types = relation.column_types.clone();
1976 any::<Index>()
1977 .prop_map(move |idx| {
1978 let id = idx.index(column_types.len());
1979 (MirScalarExpr::column(id), column_types[id].clone())
1980 })
1981 .boxed()
1982 };
1983
1984 let literal_gen = (select(SCALAR_TYPES), any::<bool>())
1985 .prop_map(|(s, b)| s.nullable(b))
1986 .prop_flat_map(|ct| {
1987 let error_gen = any::<EvalError>().prop_map(Err).boxed();
1988 let value_gen = gen_datums_for_type(&ct)
1989 .prop_map(move |datum| Ok(Row::pack_slice(&[datum])))
1990 .boxed();
1991 error_gen.prop_union(value_gen).prop_map(move |result| {
1992 (MirScalarExpr::Literal(result, ct.clone()), ct.clone())
1993 })
1994 })
1995 .boxed();
1996
1997 column_gen
1998 .prop_union(literal_gen)
1999 .prop_recursive(4, 64, 8, |self_gen| {
2000 let unary_gen = (select(interesting_unary_funcs()), self_gen.clone())
2001 .prop_filter_map("unary func", |(func, (expr_in, type_in))| {
2002 if !unary_typecheck(&func, &type_in) {
2003 return None;
2004 }
2005 let type_out = func.output_type(type_in);
2006 let expr_out = MirScalarExpr::CallUnary {
2007 func,
2008 expr: Box::new(expr_in),
2009 };
2010 Some((expr_out, type_out))
2011 })
2012 .boxed();
2013 let binary_gen = (
2014 select(interesting_binary_funcs()),
2015 self_gen.clone(),
2016 self_gen.clone(),
2017 )
2018 .prop_filter_map(
2019 "binary func",
2020 |(func, (expr_left, type_left), (expr_right, type_right))| {
2021 if !binary_typecheck(&func, &type_left, &type_right) {
2022 return None;
2023 }
2024 let type_out = func.output_type(&[type_left, type_right]);
2025 let expr_out = MirScalarExpr::CallBinary {
2026 func,
2027 expr1: Box::new(expr_left),
2028 expr2: Box::new(expr_right),
2029 };
2030 Some((expr_out, type_out))
2031 },
2032 )
2033 .boxed();
2034 let variadic_gen = (
2035 select(INTERESTING_VARIADIC_FUNCS),
2036 prop::collection::vec(self_gen.clone(), 1..4),
2037 )
2038 .prop_filter_map("variadic func", |(func, exprs)| {
2039 let (exprs_in, type_in): (_, Vec<_>) = exprs.into_iter().unzip();
2040 if !variadic_typecheck(&func, &type_in) {
2041 return None;
2042 }
2043 let type_out = func.output_type(type_in);
2044 let expr_out = MirScalarExpr::CallVariadic {
2045 func,
2046 exprs: exprs_in,
2047 };
2048 Some((expr_out, type_out))
2049 })
2050 .boxed();
2051 let if_gen = {
2058 let bool_type = ReprScalarType::Bool.nullable(true);
2059 let cond_gen = gen_datums_for_type(&bool_type).prop_map(move |datum| {
2060 MirScalarExpr::Literal(Ok(Row::pack_slice(&[datum])), bool_type.clone())
2061 });
2062 (cond_gen, self_gen.clone())
2063 .prop_flat_map(|(cond_expr, (then_expr, then_type))| {
2064 let out_type = then_type.clone();
2065 gen_datums_for_type(&then_type).prop_map(move |datum| {
2066 let els_expr = MirScalarExpr::Literal(
2067 Ok(Row::pack_slice(&[datum])),
2068 out_type.clone(),
2069 );
2070 let expr_out = MirScalarExpr::If {
2071 cond: Box::new(cond_expr.clone()),
2072 then: Box::new(then_expr.clone()),
2073 els: Box::new(els_expr),
2074 };
2075 (expr_out, out_type.clone())
2076 })
2077 })
2078 .boxed()
2079 };
2080
2081 unary_gen
2082 .prop_union(binary_gen)
2083 .boxed()
2084 .prop_union(variadic_gen)
2085 .boxed()
2086 .prop_union(if_gen)
2087 })
2088 .boxed()
2089 }
2090
2091 fn gen_expr_data() -> impl Strategy<Value = ExpressionData> {
2092 let columns = prop::collection::vec(gen_column(), 1..10);
2093 columns.prop_flat_map(|data| {
2094 let (columns, datums, specs): (Vec<_>, Vec<_>, Vec<_>) = data.into_iter().multiunzip();
2095 let relation = ReprRelationType::new(columns);
2096 let row = Row::pack_slice(&datums);
2097 gen_expr_for_relation(&relation).prop_map(move |(expr, _)| ExpressionData {
2098 relation_type: relation.clone(),
2099 specs: specs.clone(),
2100 rows: vec![row.clone()],
2101 expr,
2102 })
2103 })
2104 }
2105
2106 #[mz_ore::test]
2107 #[cfg_attr(miri, ignore)] fn test_trivial_spec_matches() {
2109 fn check(datum: PropDatum) -> Result<(), TestCaseError> {
2110 let datum: Datum = (&datum).into();
2111 let spec = if datum.is_null() {
2112 ResultSpec::null()
2113 } else {
2114 ResultSpec::value(datum)
2115 };
2116 assert!(spec.may_contain(datum));
2117 Ok(())
2118 }
2119
2120 proptest!(|(datum in mz_repr::arb_datum(true))| {
2121 check(datum)?;
2122 });
2123
2124 assert!(ResultSpec::fails().may_fail());
2125 }
2126
2127 #[mz_ore::test]
2128 #[cfg_attr(miri, ignore)] fn test_equivalence() {
2130 fn check(data: ExpressionData) -> Result<(), TestCaseError> {
2131 let ExpressionData {
2132 relation_type,
2133 specs,
2134 rows,
2135 expr,
2136 } = data;
2137
2138 let arena = RowArena::new();
2142 let mut interpreter = ColumnSpecs::new(&relation_type, &arena);
2143 for (id, spec) in specs.into_iter().enumerate() {
2144 interpreter.push_column(id, spec);
2145 }
2146
2147 let spec = interpreter.expr(&expr);
2148
2149 for row in &rows {
2150 let datums: Vec<_> = row.iter().collect();
2151 let eval_result = expr.eval(&datums, &arena);
2152 match eval_result {
2153 Ok(value) => {
2154 assert!(spec.range.may_contain(value))
2155 }
2156 Err(_) => {
2157 assert!(spec.range.may_fail());
2158 }
2159 }
2160 }
2161
2162 Ok(())
2163 }
2164
2165 proptest!(|(data in gen_expr_data())| {
2166 check(data)?;
2167 });
2168 }
2169
2170 fn gen_range_column()
2179 -> impl Strategy<Value = (ReprColumnType, Datum<'static>, ResultSpec<'static>)> {
2180 select(SCALAR_TYPES)
2181 .prop_map(|t| t.nullable(false))
2182 .prop_filter("need at least two distinct values for a range", |c| {
2183 let mut datums: Vec<Datum> = SqlScalarType::from_repr(&c.scalar_type)
2184 .interesting_datums()
2185 .filter(|d| !d.is_null())
2186 .collect();
2187 datums.sort();
2188 datums.dedup();
2189 datums.len() >= 2
2190 })
2191 .prop_flat_map(|col| {
2192 let mut datums: Vec<Datum<'static>> = SqlScalarType::from_repr(&col.scalar_type)
2193 .interesting_datums()
2194 .filter(|d| !d.is_null())
2195 .collect();
2196 datums.sort();
2197 datums.dedup();
2198 (
2199 Just(col),
2200 Just(datums),
2201 any::<Index>(),
2202 any::<Index>(),
2203 any::<Index>(),
2204 )
2205 .prop_map(|(col, datums, a, b, c)| {
2206 let n = datums.len();
2207 let mut idxs = [a.index(n), b.index(n), c.index(n)];
2208 idxs.sort();
2209 let lo = datums[idxs[0]];
2210 let mid = datums[idxs[1]];
2211 let hi = datums[idxs[2]];
2212 let spec = ResultSpec::value_between(lo, hi);
2213 (col, mid, spec)
2214 })
2215 })
2216 }
2217
2218 fn gen_range_expr_data() -> impl Strategy<Value = ExpressionData> {
2219 let columns = prop::collection::vec(gen_range_column(), 1..10);
2220 columns.prop_flat_map(|data| {
2221 let (columns, datums, specs): (Vec<_>, Vec<_>, Vec<_>) = data.into_iter().multiunzip();
2222 let relation = ReprRelationType::new(columns);
2223 let row = Row::pack_slice(&datums);
2224 gen_expr_for_relation(&relation).prop_map(move |(expr, _)| ExpressionData {
2225 relation_type: relation.clone(),
2226 specs: specs.clone(),
2227 rows: vec![row.clone()],
2228 expr,
2229 })
2230 })
2231 }
2232
2233 #[mz_ore::test]
2244 #[cfg_attr(miri, ignore)] fn test_equivalence_ranges() {
2246 fn check(data: ExpressionData) -> Result<(), TestCaseError> {
2247 let ExpressionData {
2248 relation_type,
2249 specs,
2250 rows,
2251 expr,
2252 } = data;
2253
2254 let arena = RowArena::new();
2255 let mut interpreter = ColumnSpecs::new(&relation_type, &arena);
2256 for (id, spec) in specs.into_iter().enumerate() {
2257 interpreter.push_column(id, spec);
2258 }
2259
2260 let spec = interpreter.expr(&expr);
2261
2262 for row in &rows {
2263 let datums: Vec<_> = row.iter().collect();
2264 let eval_result = expr.eval(&datums, &arena);
2265 match eval_result {
2266 Ok(value) => {
2267 prop_assert!(
2268 spec.range.may_contain(value),
2269 "interpreter ruled out a value the evaluator produced \
2270 for an interior input: expr={expr:?} row={row:?} \
2271 value={value:?} spec={:?}",
2272 spec.range,
2273 );
2274 }
2275 Err(_) => {
2276 prop_assert!(
2277 spec.range.may_fail(),
2278 "interpreter ruled out an error the evaluator produced \
2279 for an interior input: expr={expr:?} row={row:?}",
2280 );
2281 }
2282 }
2283 }
2284
2285 Ok(())
2286 }
2287
2288 let default = ProptestConfig::default();
2297 let cases = if std::env::var_os("PROPTEST_CASES").is_some() {
2298 default.cases
2299 } else {
2300 2048
2301 };
2302 let config = ProptestConfig {
2303 cases,
2304 max_local_rejects: cases.saturating_mul(512),
2305 ..default
2306 };
2307 proptest!(config, |(data in gen_range_expr_data())| {
2308 check(data)?;
2309 });
2310 }
2311
2312 #[mz_ore::test]
2321 #[cfg_attr(miri, ignore)] fn test_result_spec_lattice_laws() {
2323 #[derive(Debug, Clone)]
2326 enum Recipe {
2327 Nothing,
2328 Null,
2329 Fails,
2330 Anything,
2331 ValueAll,
2332 Value(PropDatum),
2333 Between(PropDatum, PropDatum),
2334 Map(Vec<(PropDatum, Recipe)>),
2335 Union(Box<Recipe>, Box<Recipe>),
2336 }
2337
2338 fn materialize(recipe: &Recipe) -> ResultSpec<'_> {
2339 match recipe {
2340 Recipe::Nothing => ResultSpec::nothing(),
2341 Recipe::Null => ResultSpec::null(),
2342 Recipe::Fails => ResultSpec::fails(),
2343 Recipe::Anything => ResultSpec::anything(),
2344 Recipe::ValueAll => ResultSpec::value_all(),
2345 Recipe::Value(pd) => ResultSpec::value(pd.into()),
2346 Recipe::Between(a, b) => {
2347 let (a, b): (Datum, Datum) = (a.into(), b.into());
2348 if a.is_null() || b.is_null() {
2349 ResultSpec::nothing()
2350 } else if a <= b {
2351 ResultSpec::value_between(a, b)
2352 } else {
2353 ResultSpec::value_between(b, a)
2354 }
2355 }
2356 Recipe::Map(entries) => {
2357 let mut map = BTreeMap::new();
2358 for (key, val) in entries {
2359 let key: Datum = key.into();
2360 if !key.is_null() {
2361 map.insert(key, materialize(val));
2362 }
2363 }
2364 ResultSpec::map_spec(map)
2365 }
2366 Recipe::Union(a, b) => materialize(a).union(materialize(b)),
2367 }
2368 }
2369
2370 fn recipe_strategy() -> impl Strategy<Value = Recipe> {
2371 let leaf = proptest::strategy::Union::new(vec![
2372 Just(Recipe::Nothing).boxed(),
2373 Just(Recipe::Null).boxed(),
2374 Just(Recipe::Fails).boxed(),
2375 Just(Recipe::Anything).boxed(),
2376 Just(Recipe::ValueAll).boxed(),
2377 mz_repr::arb_datum(false).prop_map(Recipe::Value).boxed(),
2378 (mz_repr::arb_datum(false), mz_repr::arb_datum(false))
2379 .prop_map(|(a, b)| Recipe::Between(a, b))
2380 .boxed(),
2381 ]);
2382 leaf.prop_recursive(3, 24, 4, |inner| {
2383 proptest::strategy::Union::new(vec![
2384 prop::collection::vec((mz_repr::arb_datum(false), inner.clone()), 0..3)
2385 .prop_map(Recipe::Map)
2386 .boxed(),
2387 (inner.clone(), inner.clone())
2388 .prop_map(|(a, b)| Recipe::Union(Box::new(a), Box::new(b)))
2389 .boxed(),
2390 ])
2391 })
2392 }
2393
2394 fn check(a: Recipe, b: Recipe, v: PropDatum) -> Result<(), TestCaseError> {
2395 let a_spec = materialize(&a);
2396 let b_spec = materialize(&b);
2397 let v: Datum = (&v).into();
2398
2399 let in_a = a_spec.may_contain(v);
2400 let in_b = b_spec.may_contain(v);
2401
2402 if in_a || in_b {
2403 prop_assert!(
2404 a_spec.clone().union(b_spec.clone()).may_contain(v),
2405 "union dropped a value: a={a:?} b={b:?} v={v:?}",
2406 );
2407 }
2408 if in_a && in_b {
2409 prop_assert!(
2410 a_spec.intersect(b_spec).may_contain(v),
2411 "intersect dropped a common value: a={a:?} b={b:?} v={v:?}",
2412 );
2413 }
2414 Ok(())
2415 }
2416
2417 proptest!(
2418 ProptestConfig::with_cases(4096),
2419 |(a in recipe_strategy(), b in recipe_strategy(), v in mz_repr::arb_datum(true))| {
2420 check(a, b, v)?;
2421 }
2422 );
2423 }
2424
2425 #[mz_ore::test]
2436 #[cfg_attr(miri, ignore)] fn test_neg_numeric_nan_range() {
2438 use mz_repr::adt::numeric::Numeric;
2439
2440 let neg = MirScalarExpr::CallUnary {
2441 func: UnaryFunc::NegNumeric(NegNumeric),
2442 expr: Box::new(MirScalarExpr::column(0)),
2443 };
2444
2445 let relation = ReprRelationType::new(vec![ReprScalarType::Numeric.nullable(false)]);
2446 let arena = RowArena::new();
2447 let mut interpreter = ColumnSpecs::new(&relation, &arena);
2448 interpreter.push_column(
2449 0,
2450 ResultSpec::value_between(
2451 Datum::from(Numeric::from(f64::NEG_INFINITY)),
2452 Datum::from(Numeric::from(f64::NAN)),
2453 ),
2454 );
2455
2456 let spec = interpreter.expr(&neg);
2457
2458 let actual = neg
2460 .eval(&[Datum::from(Numeric::from(-1.0f64))], &arena)
2461 .expect("eval succeeds");
2462 assert!(
2463 spec.range.may_contain(actual),
2464 "interpreter must not rule out {actual:?}, which the evaluator \
2465 produces for an interior input; got spec {:?}",
2466 spec.range,
2467 );
2468 }
2469
2470 #[mz_ore::test]
2481 #[cfg_attr(miri, ignore)] fn test_fallible_monotone_interior_error() {
2483 use mz_repr::adt::numeric::Numeric;
2484
2485 let cast = MirScalarExpr::CallUnary {
2486 func: UnaryFunc::CastNumericToMzTimestamp(CastNumericToMzTimestamp),
2487 expr: Box::new(MirScalarExpr::column(0)),
2488 };
2489
2490 let relation = ReprRelationType::new(vec![ReprScalarType::Numeric.nullable(false)]);
2491 let arena = RowArena::new();
2492 let mut interpreter = ColumnSpecs::new(&relation, &arena);
2493 interpreter.push_column(
2494 0,
2495 ResultSpec::value_between(
2496 Datum::from(Numeric::from(0.0f64)),
2497 Datum::from(Numeric::from(2.0f64)),
2498 ),
2499 );
2500
2501 let spec = interpreter.expr(&cast);
2502
2503 let interior = Datum::from(Numeric::from(1.5f64));
2506 assert!(
2507 cast.eval(&[interior], &arena).is_err(),
2508 "precondition: a fractional numeric fails to cast to mz_timestamp",
2509 );
2510 assert!(
2511 spec.range.may_fail(),
2512 "interpreter must surface that a monotone-but-fallible function may \
2513 error on an interior value it never sampled; got spec {:?}",
2514 spec.range,
2515 );
2516 }
2517
2518 #[mz_ore::test]
2526 #[cfg_attr(miri, ignore)]
2527 fn test_mfp_unreferenced_fallible_expression() {
2528 use crate::scalar::func::CastStringToUuid;
2529
2530 let mfp = MapFilterProject {
2536 expressions: vec![MirScalarExpr::CallUnary {
2537 func: UnaryFunc::CastStringToUuid(CastStringToUuid),
2538 expr: Box::new(MirScalarExpr::column(0)),
2539 }],
2540 predicates: vec![(
2541 1,
2542 MirScalarExpr::literal_ok(Datum::True, ReprScalarType::Bool),
2543 )],
2544 projection: vec![0, 1],
2545 input_arity: 1,
2546 };
2547
2548 let relation = ReprRelationType::new(vec![ReprScalarType::String.nullable(false)]);
2549 let arena = RowArena::new();
2550 let mut interpreter = ColumnSpecs::new(&relation, &arena);
2551 interpreter.push_column(
2553 0,
2554 ResultSpec::value_between(Datum::String("not-a-uuid"), Datum::String("not-a-uuid")),
2555 );
2556 let spec = interpreter.mfp_filter(&mfp);
2557 assert!(
2558 spec.range.may_fail(),
2559 "an MFP expression that errors on the stats range must propagate \
2560 fallibility, otherwise persist filter pushdown can wrongly discard \
2561 a part that produces error rows",
2562 );
2563 }
2564
2565 #[mz_ore::test]
2575 #[cfg_attr(miri, ignore)]
2576 fn test_mfp_filter_fallibility_equivalence() {
2577 fn check(data: ExpressionData) -> Result<(), TestCaseError> {
2578 let ExpressionData {
2579 relation_type,
2580 specs,
2581 rows,
2582 expr,
2583 } = data;
2584
2585 let input_arity = relation_type.column_types.len();
2586 let mfp = MapFilterProject {
2587 expressions: vec![expr.clone()],
2588 predicates: vec![],
2589 projection: (0..input_arity).collect(),
2590 input_arity,
2591 };
2592
2593 let arena = RowArena::new();
2594 let mut interpreter = ColumnSpecs::new(&relation_type, &arena);
2595 for (id, spec) in specs.into_iter().enumerate() {
2596 interpreter.push_column(id, spec);
2597 }
2598 let summary = interpreter.mfp_filter(&mfp);
2599
2600 for row in &rows {
2601 let datums: Vec<_> = row.iter().collect();
2602 if expr.eval(&datums, &arena).is_err() {
2603 prop_assert!(
2604 summary.range.may_fail(),
2605 "mfp_filter must surface the fallibility of an \
2606 unreferenced MFP expression: row {:?} errored at \
2607 runtime but the interpreter ruled out errors",
2608 row,
2609 );
2610 }
2611 }
2612 Ok(())
2613 }
2614
2615 proptest!(|(data in gen_expr_data())| {
2616 check(data)?;
2617 });
2618 }
2619
2620 #[mz_ore::test]
2621 fn test_mfp() {
2622 use MirScalarExpr::*;
2624
2625 let mfp = MapFilterProject {
2626 expressions: vec![],
2627 predicates: vec![
2628 (
2630 1,
2631 CallUnary {
2632 func: UnaryFunc::IsNull(IsNull),
2633 expr: Box::new(CallBinary {
2634 func: MulInt32.into(),
2635 expr1: Box::new(MirScalarExpr::column(0)),
2636 expr2: Box::new(MirScalarExpr::column(0)),
2637 }),
2638 },
2639 ),
2640 (
2642 1,
2643 CallBinary {
2644 func: Eq.into(),
2645 expr1: Box::new(MirScalarExpr::column(0)),
2646 expr2: Box::new(MirScalarExpr::literal_ok(
2647 Datum::Int32(1727694505),
2648 ReprScalarType::Int32,
2649 )),
2650 },
2651 ),
2652 ],
2653 projection: vec![],
2654 input_arity: 1,
2655 };
2656
2657 let relation = ReprRelationType::new(vec![ReprScalarType::Int32.nullable(true)]);
2658 let arena = RowArena::new();
2659 let mut interpreter = ColumnSpecs::new(&relation, &arena);
2660 interpreter.push_column(0, ResultSpec::value(Datum::Int32(-1294725158)));
2661 let spec = interpreter.mfp_filter(&mfp);
2662 assert!(spec.range.may_fail());
2663 }
2664
2665 #[mz_ore::test]
2666 fn test_concat() {
2667 let expr = MirScalarExpr::call_variadic(
2668 Concat,
2669 vec![
2670 MirScalarExpr::column(0),
2671 MirScalarExpr::literal_ok(Datum::String("a"), ReprScalarType::String),
2672 MirScalarExpr::literal_ok(Datum::String("b"), ReprScalarType::String),
2673 ],
2674 );
2675
2676 let relation = ReprRelationType::new(vec![ReprScalarType::String.nullable(false)]);
2677 let arena = RowArena::new();
2678 let interpreter = ColumnSpecs::new(&relation, &arena);
2679 let spec = interpreter.expr(&expr);
2680 assert!(spec.range.may_contain(Datum::String("blab")));
2681 }
2682
2683 #[mz_ore::test]
2684 fn test_eval_range() {
2685 let period_ms = MirScalarExpr::literal_ok(Datum::Int64(10), ReprScalarType::Int64);
2697 let expr = MirScalarExpr::CallBinary {
2698 func: Gte.into(),
2699 expr1: Box::new(MirScalarExpr::CallUnmaterializable(
2700 UnmaterializableFunc::MzNow,
2701 )),
2702 expr2: Box::new(MirScalarExpr::CallUnary {
2703 func: UnaryFunc::CastInt64ToMzTimestamp(CastInt64ToMzTimestamp),
2704 expr: Box::new(MirScalarExpr::CallBinary {
2705 func: MulInt64.into(),
2706 expr1: Box::new(period_ms.clone()),
2707 expr2: Box::new(MirScalarExpr::CallBinary {
2708 func: DivInt64.into(),
2709 expr1: Box::new(MirScalarExpr::column(0)),
2710 expr2: Box::new(period_ms),
2711 }),
2712 }),
2713 }),
2714 };
2715 let relation = ReprRelationType::new(vec![ReprScalarType::Int64.nullable(false)]);
2716
2717 {
2718 let arena = RowArena::new();
2720 let mut interpreter = ColumnSpecs::new(&relation, &arena);
2721 interpreter.push_unmaterializable(
2722 UnmaterializableFunc::MzNow,
2723 ResultSpec::value_between(
2724 Datum::MzTimestamp(10.into()),
2725 Datum::MzTimestamp(20.into()),
2726 ),
2727 );
2728 interpreter.push_column(0, ResultSpec::value_between(30i64.into(), 40i64.into()));
2729
2730 let range_out = interpreter.expr(&expr).range;
2731 assert!(range_out.may_contain(Datum::False));
2732 assert!(!range_out.may_contain(Datum::True));
2733 assert!(!range_out.may_contain(Datum::Null));
2734 assert!(range_out.may_fail());
2735 }
2736
2737 {
2738 let arena = RowArena::new();
2740 let mut interpreter = ColumnSpecs::new(&relation, &arena);
2741 interpreter.push_unmaterializable(
2742 UnmaterializableFunc::MzNow,
2743 ResultSpec::value_between(
2744 Datum::MzTimestamp(10.into()),
2745 Datum::MzTimestamp(35.into()),
2746 ),
2747 );
2748 interpreter.push_column(0, ResultSpec::value_between(30i64.into(), 40i64.into()));
2749
2750 let range_out = interpreter.expr(&expr).range;
2751 assert!(range_out.may_contain(Datum::False));
2752 assert!(range_out.may_contain(Datum::True));
2753 assert!(!range_out.may_contain(Datum::Null));
2754 assert!(range_out.may_fail());
2755 }
2756 }
2757
2758 #[mz_ore::test]
2759 #[cfg_attr(miri, ignore)] fn test_jsonb() {
2761 let arena = RowArena::new();
2762
2763 let expr = MirScalarExpr::column(0)
2764 .call_binary(
2765 MirScalarExpr::literal_ok(Datum::from("ts"), ReprScalarType::String),
2766 JsonbGetString,
2767 )
2768 .call_unary(CastJsonbToNumeric(None));
2769
2770 let relation = ReprRelationType::new(vec![ReprScalarType::Jsonb.nullable(true)]);
2771 let mut interpreter = ColumnSpecs::new(&relation, &arena);
2772 interpreter.push_column(
2773 0,
2774 ResultSpec::map_spec(
2775 [(
2776 "ts".into(),
2777 ResultSpec::value_between(
2778 Datum::Numeric(100.into()),
2779 Datum::Numeric(300.into()),
2780 ),
2781 )]
2782 .into_iter()
2783 .collect(),
2784 ),
2785 );
2786
2787 let range_out = interpreter.expr(&expr).range;
2788 assert!(!range_out.may_contain(Datum::Numeric(0.into())));
2789 assert!(range_out.may_contain(Datum::Numeric(200.into())));
2790 assert!(!range_out.may_contain(Datum::Numeric(400.into())));
2791 }
2792
2793 #[mz_ore::test]
2794 fn test_nested_union_partial_overlap() {
2795 let a = ResultSpec::map_spec(
2800 [
2801 ("x".into(), ResultSpec::value(Datum::String("a"))),
2802 ("y".into(), ResultSpec::value(Datum::String("b"))),
2803 ("c".into(), ResultSpec::value(Datum::String("c"))),
2804 ]
2805 .into_iter()
2806 .collect(),
2807 );
2808 let b = ResultSpec::map_spec(
2809 [
2810 ("x".into(), ResultSpec::value(Datum::String("a2"))),
2811 ("y".into(), ResultSpec::value(Datum::String("b2"))),
2812 ("z".into(), ResultSpec::value(Datum::String("z"))),
2813 ]
2814 .into_iter()
2815 .collect(),
2816 );
2817
2818 let unioned = a.union(b);
2819
2820 let arena = RowArena::new();
2824 let relation = ReprRelationType::new(vec![ReprScalarType::Jsonb.nullable(false)]);
2825
2826 {
2828 let mut interpreter = ColumnSpecs::new(&relation, &arena);
2829 interpreter.push_column(0, unioned.clone());
2830 let expr = MirScalarExpr::column(0).call_binary(
2831 MirScalarExpr::literal_ok(Datum::from("c"), ReprScalarType::String),
2832 JsonbGetStringStringify,
2833 );
2834 assert!(interpreter.expr(&expr).range.may_contain(Datum::Null));
2835 }
2836
2837 {
2839 let mut interpreter = ColumnSpecs::new(&relation, &arena);
2840 interpreter.push_column(0, unioned.clone());
2841 let expr = MirScalarExpr::column(0).call_binary(
2842 MirScalarExpr::literal_ok(Datum::from("z"), ReprScalarType::String),
2843 JsonbGetStringStringify,
2844 );
2845 assert!(interpreter.expr(&expr).range.may_contain(Datum::Null));
2846 }
2847
2848 {
2850 let mut interpreter = ColumnSpecs::new(&relation, &arena);
2851 interpreter.push_column(0, unioned);
2852 let expr = MirScalarExpr::column(0).call_binary(
2853 MirScalarExpr::literal_ok(Datum::from("x"), ReprScalarType::String),
2854 JsonbGetStringStringify,
2855 );
2856 let x_range = interpreter.expr(&expr).range;
2857 assert!(x_range.may_contain(Datum::String("a")));
2858 assert!(x_range.may_contain(Datum::String("a2")));
2859 }
2860 }
2861
2862 #[mz_ore::test]
2863 #[cfg_attr(miri, ignore)] fn test_case_over_jsonb_columns() {
2865 let arena = RowArena::new();
2869
2870 let expr = MirScalarExpr::If {
2872 cond: Box::new(MirScalarExpr::column(0)),
2873 then: Box::new(MirScalarExpr::column(1)),
2874 els: Box::new(MirScalarExpr::column(2)),
2875 }
2876 .call_binary(
2877 MirScalarExpr::literal_ok(Datum::from("y"), ReprScalarType::String),
2878 JsonbGetStringStringify,
2879 )
2880 .call_unary(UnaryFunc::IsNull(IsNull));
2881
2882 let relation = ReprRelationType::new(vec![
2883 ReprScalarType::Bool.nullable(false),
2884 ReprScalarType::Jsonb.nullable(false),
2885 ReprScalarType::Jsonb.nullable(false),
2886 ]);
2887 let mut interpreter = ColumnSpecs::new(&relation, &arena);
2888 interpreter.push_column(0, ResultSpec::value_between(Datum::False, Datum::True));
2889 interpreter.push_column(
2890 1,
2891 ResultSpec::map_spec(
2892 [("x".into(), ResultSpec::value(Datum::String("a")))]
2893 .into_iter()
2894 .collect(),
2895 ),
2896 );
2897 interpreter.push_column(
2898 2,
2899 ResultSpec::map_spec(
2900 [("y".into(), ResultSpec::value(Datum::String("b")))]
2901 .into_iter()
2902 .collect(),
2903 ),
2904 );
2905
2906 let range_out = interpreter.expr(&expr).range;
2907 assert!(range_out.may_contain(Datum::True));
2910 }
2911
2912 #[mz_ore::test]
2913 fn test_like() {
2914 let arena = RowArena::new();
2915
2916 let expr = MirScalarExpr::CallUnary {
2917 func: UnaryFunc::IsLikeMatch(IsLikeMatch(
2918 crate::like_pattern::compile("%whatever%", true).unwrap(),
2919 )),
2920 expr: Box::new(MirScalarExpr::column(0)),
2921 };
2922
2923 let relation = ReprRelationType::new(vec![ReprScalarType::String.nullable(true)]);
2924 let mut interpreter = ColumnSpecs::new(&relation, &arena);
2925 interpreter.push_column(
2926 0,
2927 ResultSpec::value_between(Datum::String("aardvark"), Datum::String("zebra")),
2928 );
2929
2930 let range_out = interpreter.expr(&expr).range;
2931 assert!(
2932 !range_out.fallible,
2933 "like function should not error on non-error input"
2934 );
2935 assert!(range_out.may_contain(Datum::True));
2936 assert!(range_out.may_contain(Datum::False));
2937 assert!(range_out.may_contain(Datum::Null));
2938 }
2939
2940 #[mz_ore::test]
2941 fn test_try_parse_monotonic_iso8601_timestamp() {
2942 use chrono::NaiveDateTime;
2943
2944 let arena = RowArena::new();
2945
2946 let expr = MirScalarExpr::CallUnary {
2947 func: UnaryFunc::TryParseMonotonicIso8601Timestamp(TryParseMonotonicIso8601Timestamp),
2948 expr: Box::new(MirScalarExpr::column(0)),
2949 };
2950
2951 let relation = ReprRelationType::new(vec![ReprScalarType::String.nullable(true)]);
2952 let mut interpreter = ColumnSpecs::new(&relation, &arena);
2954 interpreter.push_column(
2955 0,
2956 ResultSpec::value_between(
2957 Datum::String("2024-01-11T00:00:00.000Z"),
2958 Datum::String("2024-01-11T20:00:00.000Z"),
2959 ),
2960 );
2961
2962 let timestamp = |ts| {
2963 Datum::Timestamp(
2964 NaiveDateTime::parse_from_str(ts, "%Y-%m-%dT%H:%M:%S")
2965 .unwrap()
2966 .try_into()
2967 .unwrap(),
2968 )
2969 };
2970
2971 let range_out = interpreter.expr(&expr).range;
2972 assert!(!range_out.fallible);
2973 assert!(range_out.nullable);
2974 assert!(!range_out.may_contain(timestamp("2024-01-10T10:00:00")));
2975 assert!(range_out.may_contain(timestamp("2024-01-11T10:00:00")));
2976 assert!(!range_out.may_contain(timestamp("2024-01-12T10:00:00")));
2977
2978 let mut interpreter = ColumnSpecs::new(&relation, &arena);
2980 interpreter.push_column(
2981 0,
2982 ResultSpec::value_between(Datum::String("2024-01-1"), Datum::String("2024-01-2")),
2983 );
2984
2985 let range_out = interpreter.expr(&expr).range;
2986 assert!(!range_out.fallible);
2987 assert!(range_out.nullable);
2988 assert!(range_out.may_contain(timestamp("2024-01-10T10:00:00")));
2989 assert!(range_out.may_contain(timestamp("2024-01-11T10:00:00")));
2990 assert!(range_out.may_contain(timestamp("2024-01-12T10:00:00")));
2991
2992 let mut interpreter = ColumnSpecs::new(&relation, &arena);
2994 interpreter.push_column(
2995 0,
2996 ResultSpec::value_between(
2997 Datum::String("2024-01-1"),
2998 Datum::String("2024-01-12T10:00:00"),
2999 )
3000 .union(ResultSpec::null()),
3001 );
3002
3003 let range_out = interpreter.expr(&expr).range;
3004 assert!(!range_out.fallible);
3005 assert!(range_out.nullable);
3006 assert!(range_out.may_contain(timestamp("2024-01-10T10:00:00")));
3007 assert!(range_out.may_contain(timestamp("2024-01-11T10:00:00")));
3008 assert!(range_out.may_contain(timestamp("2024-01-12T10:00:00")));
3009
3010 let mut interpreter = ColumnSpecs::new(&relation, &arena);
3012 interpreter.push_column(
3013 0,
3014 ResultSpec::value_between(
3015 Datum::String("2024-01-11T10:00:00.000Z"),
3016 Datum::String("2024-01-11T10:00:00.000Z"),
3017 ),
3018 );
3019
3020 let range_out = interpreter.expr(&expr).range;
3021 assert!(!range_out.fallible);
3022 assert!(!range_out.nullable);
3023 assert!(!range_out.may_contain(timestamp("2024-01-10T10:00:00")));
3024 assert!(range_out.may_contain(timestamp("2024-01-11T10:00:00")));
3025 assert!(!range_out.may_contain(timestamp("2024-01-12T10:00:00")));
3026 }
3027
3028 #[mz_ore::test]
3029 fn test_inequality() {
3030 let arena = RowArena::new();
3031
3032 let expr = MirScalarExpr::column(0).call_binary(
3033 MirScalarExpr::CallUnmaterializable(UnmaterializableFunc::MzNow),
3034 Gte,
3035 );
3036
3037 let relation = ReprRelationType::new(vec![ReprScalarType::MzTimestamp.nullable(true)]);
3038 let mut interpreter = ColumnSpecs::new(&relation, &arena);
3039 interpreter.push_column(
3040 0,
3041 ResultSpec::value_between(
3042 Datum::MzTimestamp(1704736444949u64.into()),
3043 Datum::MzTimestamp(1704736444949u64.into()),
3044 )
3045 .union(ResultSpec::null()),
3046 );
3047 interpreter.push_unmaterializable(
3048 UnmaterializableFunc::MzNow,
3049 ResultSpec::value_between(
3050 Datum::MzTimestamp(1704738791000u64.into()),
3051 Datum::MzTimestamp(18446744073709551615u64.into()),
3052 ),
3053 );
3054
3055 let range_out = interpreter.expr(&expr).range;
3056 assert!(
3057 !range_out.fallible,
3058 "<= function should not error on non-error input"
3059 );
3060 assert!(!range_out.may_contain(Datum::True));
3061 assert!(range_out.may_contain(Datum::False));
3062 assert!(range_out.may_contain(Datum::Null));
3063 }
3064
3065 #[mz_ore::test]
3073 #[cfg_attr(miri, ignore)]
3074 fn test_add_timestamp_interval_non_monotone() {
3075 use chrono::NaiveDateTime;
3076 use mz_repr::adt::interval::Interval;
3077 use mz_repr::adt::timestamp::CheckedTimestamp;
3078 use mz_repr::{Datum, Row};
3079
3080 let arena = RowArena::new();
3081
3082 let ts_lit = |s: &str| {
3094 let mut row = Row::default();
3095 row.packer().push(Datum::Timestamp(
3096 CheckedTimestamp::from_timestamplike(
3097 NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S").unwrap(),
3098 )
3099 .unwrap(),
3100 ));
3101 MirScalarExpr::Literal(Ok(row), ReprScalarType::Timestamp.nullable(false))
3102 };
3103 let interval = |months: i32, days: i32, micros: i64| {
3104 Datum::Interval(Interval {
3105 months,
3106 days,
3107 micros,
3108 })
3109 };
3110
3111 let expr = ts_lit("2024-01-31T00:00:00")
3113 .call_binary(MirScalarExpr::column(0), AddTimestampInterval)
3114 .call_binary(ts_lit("2024-03-15T00:00:00"), Gte);
3115
3116 let relation = ReprRelationType::new(vec![ReprScalarType::Interval.nullable(false)]);
3117 let mut interpreter = ColumnSpecs::new(&relation, &arena);
3118 interpreter.push_column(
3119 0,
3120 ResultSpec::value_between(interval(0, 31, 0), interval(1, 0, 0)),
3121 );
3122
3123 let range_out = interpreter.expr(&expr).range;
3124 assert!(
3131 range_out.may_contain(Datum::True),
3132 "interpreter incorrectly ruled out matching rows; \
3133 add_timestamp_interval is not monotone in the interval argument",
3134 );
3135 }
3136
3137 #[mz_ore::test]
3144 #[cfg_attr(miri, ignore)]
3145 fn test_timestamp_plus_interval_dynamic_monotone() {
3146 use chrono::NaiveDateTime;
3147 use mz_repr::adt::interval::Interval;
3148 use mz_repr::adt::timestamp::CheckedTimestamp;
3149 use mz_repr::{Datum, Row};
3150
3151 let arena = RowArena::new();
3152
3153 let ts = |s: &str| {
3154 Datum::Timestamp(
3155 CheckedTimestamp::from_timestamplike(
3156 NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S").unwrap(),
3157 )
3158 .unwrap(),
3159 )
3160 };
3161 let interval_lit = |months: i32, days: i32, micros: i64| {
3162 let mut row = Row::default();
3163 row.packer().push(Datum::Interval(Interval {
3164 months,
3165 days,
3166 micros,
3167 }));
3168 MirScalarExpr::Literal(Ok(row), ReprScalarType::Interval.nullable(false))
3169 };
3170
3171 let relation = ReprRelationType::new(vec![ReprScalarType::Timestamp.nullable(false)]);
3172
3173 {
3179 let expr = MirScalarExpr::column(0)
3180 .call_binary(interval_lit(0, 1, 0), SubTimestampInterval)
3181 .call_binary(
3182 MirScalarExpr::Literal(
3183 Ok({
3184 let mut r = Row::default();
3185 r.packer().push(ts("2024-01-15T00:00:00"));
3186 r
3187 }),
3188 ReprScalarType::Timestamp.nullable(false),
3189 ),
3190 Lt,
3191 );
3192 let mut interpreter = ColumnSpecs::new(&relation, &arena);
3193 interpreter.push_column(
3194 0,
3195 ResultSpec::value_between(ts("2024-01-15T00:00:00"), ts("2024-01-20T00:00:00")),
3196 );
3197 let range_out = interpreter.expr(&expr).range;
3198 assert!(
3199 range_out.may_contain(Datum::True),
3200 "day-only interval should preserve tight bounds",
3201 );
3202 assert!(
3203 range_out.may_contain(Datum::False),
3204 "day-only interval should preserve tight bounds",
3205 );
3206 }
3207
3208 {
3213 let expr = MirScalarExpr::column(0)
3214 .call_binary(interval_lit(0, 1, 0), SubTimestampInterval)
3215 .call_binary(
3216 MirScalarExpr::Literal(
3217 Ok({
3218 let mut r = Row::default();
3219 r.packer().push(ts("2024-01-15T00:00:00"));
3220 r
3221 }),
3222 ReprScalarType::Timestamp.nullable(false),
3223 ),
3224 Lt,
3225 );
3226 let mut interpreter = ColumnSpecs::new(&relation, &arena);
3227 interpreter.push_column(
3228 0,
3229 ResultSpec::value_between(ts("2024-01-17T00:00:00"), ts("2024-01-20T00:00:00")),
3230 );
3231 let range_out = interpreter.expr(&expr).range;
3232 assert!(
3233 !range_out.may_contain(Datum::True),
3234 "day-only interval should narrow out impossible matches",
3235 );
3236 }
3237
3238 {
3243 let expr = MirScalarExpr::column(0)
3244 .call_binary(interval_lit(1, 0, 0), SubTimestampInterval)
3245 .call_binary(
3246 MirScalarExpr::Literal(
3247 Ok({
3248 let mut r = Row::default();
3249 r.packer().push(ts("2024-01-15T00:00:00"));
3250 r
3251 }),
3252 ReprScalarType::Timestamp.nullable(false),
3253 ),
3254 Lt,
3255 );
3256 let mut interpreter = ColumnSpecs::new(&relation, &arena);
3257 interpreter.push_column(
3258 0,
3259 ResultSpec::value_between(ts("2024-01-17T00:00:00"), ts("2024-01-20T00:00:00")),
3260 );
3261 let range_out = interpreter.expr(&expr).range;
3262 assert!(
3263 range_out.may_contain(Datum::True),
3264 "month-bearing interval must conservatively admit True",
3265 );
3266 assert!(
3267 range_out.may_contain(Datum::False),
3268 "month-bearing interval must conservatively admit False",
3269 );
3270 }
3271 }
3272
3273 #[mz_ore::test]
3282 #[cfg_attr(miri, ignore)]
3283 fn proptest_timestamp_plus_interval_monotone_when_months_zero() {
3284 use mz_repr::adt::interval::Interval;
3285 use mz_repr::{Datum, RowArena, SqlScalarType, arb_datum_for_scalar};
3286 use proptest::prelude::*;
3287
3288 let timestamp_strat = || arb_datum_for_scalar(SqlScalarType::Timestamp { precision: None });
3289 let zero_month_interval_strat =
3296 (any::<i32>(), any::<i64>()).prop_map(|(days, micros)| Interval {
3297 months: 0,
3298 days,
3299 micros,
3300 });
3301
3302 let expr = MirScalarExpr::CallBinary {
3303 func: AddTimestampInterval.into(),
3304 expr1: Box::new(MirScalarExpr::column(0)),
3305 expr2: Box::new(MirScalarExpr::column(1)),
3306 };
3307 let arena = RowArena::new();
3308
3309 proptest!(|(
3310 t1 in timestamp_strat(),
3311 t2 in timestamp_strat(),
3312 i in zero_month_interval_strat,
3313 )| {
3314 let t1 = match t1 { PropDatum::Timestamp(t) => t, _ => unreachable!() };
3315 let t2 = match t2 { PropDatum::Timestamp(t) => t, _ => unreachable!() };
3316 let i = Datum::Interval(i);
3317 let r1 = expr.eval(&[Datum::Timestamp(t1), i], &arena);
3318 let r2 = expr.eval(&[Datum::Timestamp(t2), i], &arena);
3319 if let (Ok(Datum::Timestamp(r1)), Ok(Datum::Timestamp(r2))) = (r1, r2) {
3322 prop_assert_eq!(t1.cmp(&t2), r1.cmp(&r2));
3323 }
3324 });
3325 }
3326
3327 #[mz_ore::test]
3332 #[cfg_attr(miri, ignore)]
3333 fn test_date_bin_timestamp_non_monotone() {
3334 use chrono::NaiveDateTime;
3335 use mz_repr::adt::interval::Interval;
3336 use mz_repr::adt::timestamp::CheckedTimestamp;
3337 use mz_repr::{Datum, Row};
3338
3339 let arena = RowArena::new();
3340
3341 let ts_lit = |s: &str| {
3342 let mut row = Row::default();
3343 row.packer().push(Datum::Timestamp(
3344 CheckedTimestamp::from_timestamplike(
3345 NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S").unwrap(),
3346 )
3347 .unwrap(),
3348 ));
3349 MirScalarExpr::Literal(Ok(row), ReprScalarType::Timestamp.nullable(false))
3350 };
3351 let interval = |months: i32, days: i32, micros: i64| {
3352 Datum::Interval(Interval {
3353 months,
3354 days,
3355 micros,
3356 })
3357 };
3358
3359 let expr = MirScalarExpr::column(0)
3376 .call_binary(ts_lit("2024-01-01T12:00:00"), DateBinTimestamp)
3377 .call_binary(ts_lit("2024-01-01T06:00:00"), Gt);
3378
3379 let relation = ReprRelationType::new(vec![ReprScalarType::Interval.nullable(false)]);
3380 let mut interpreter = ColumnSpecs::new(&relation, &arena);
3381 interpreter.push_column(
3382 0,
3383 ResultSpec::value_between(interval(0, 1, 0), interval(0, 2, 0)),
3384 );
3385
3386 let range_out = interpreter.expr(&expr).range;
3387 assert!(
3388 range_out.may_contain(Datum::True),
3389 "date_bin is not monotone in the stride argument; \
3390 interior strides can produce outputs outside the endpoint-bounded \
3391 box, so the interpreter must admit True for `>`-style predicates",
3392 );
3393 }
3394
3395 #[mz_ore::test]
3400 #[cfg_attr(miri, ignore)]
3401 fn test_round_numeric_representation_independent() {
3402 use mz_repr::adt::date::Date;
3403
3404 let arena = RowArena::new();
3405 let lit = |d: Datum, ty: ReprScalarType| {
3406 let mut row = Row::default();
3407 row.packer().push(d);
3408 MirScalarExpr::Literal(Ok(row), ty.nullable(false))
3409 };
3410
3411 let expr = lit(
3414 Datum::Date(Date::from_pg_epoch(0).unwrap()),
3415 ReprScalarType::Date,
3416 )
3417 .call_unary(UnaryFunc::ExtractDate(ExtractDate(DateTimeUnits::Epoch)))
3418 .call_binary(
3419 lit(Datum::Int32(i32::MAX), ReprScalarType::Int32),
3420 BinaryFunc::from(RoundNumericBinary),
3421 );
3422
3423 let relation = ReprRelationType::new(vec![]);
3424 let range = ColumnSpecs::new(&relation, &arena).expr(&expr).range;
3425 match expr.eval(&[], &arena) {
3426 Ok(value) => assert!(
3427 range.may_contain(value),
3428 "interpreter ruled out {value:?}, which the evaluator produced: {range:?}",
3429 ),
3430 Err(_) => assert!(
3431 range.may_fail(),
3432 "interpreter ruled out the error the evaluator produced: {range:?}",
3433 ),
3434 }
3435 }
3436
3437 #[mz_ore::test]
3438 fn test_trace() {
3439 use super::Trace;
3440
3441 let expr = MirScalarExpr::column(0).call_binary(
3442 MirScalarExpr::column(1)
3443 .call_binary(MirScalarExpr::column(3).call_unary(NegInt64), AddInt64),
3444 Gte,
3445 );
3446 let summary = Trace.expr(&expr);
3447 assert!(summary.pushdownable());
3448 }
3449}