iceberg/expr/visitors/
strict_projection.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use std::collections::HashMap;
19
20use fnv::FnvHashSet;
21
22use crate::expr::visitors::bound_predicate_visitor::{BoundPredicateVisitor, visit};
23use crate::expr::{BoundPredicate, BoundReference, Predicate};
24use crate::spec::{Datum, PartitionField, PartitionSpecRef};
25use crate::{Error, ErrorKind};
26
27// # TODO
28// Remove this after delete support
29#[allow(dead_code)]
30pub(crate) struct StrictProjection {
31    partition_spec: PartitionSpecRef,
32    cached_parts: HashMap<i32, Vec<PartitionField>>,
33}
34
35#[allow(dead_code)]
36impl StrictProjection {
37    pub(crate) fn new(partition_spec: PartitionSpecRef) -> Self {
38        Self {
39            partition_spec,
40            cached_parts: HashMap::new(),
41        }
42    }
43
44    fn get_parts_for_field_id(&mut self, field_id: i32) -> &Vec<PartitionField> {
45        if let std::collections::hash_map::Entry::Vacant(e) = self.cached_parts.entry(field_id) {
46            let mut parts: Vec<PartitionField> = vec![];
47            for partition_spec_field in self.partition_spec.fields() {
48                if partition_spec_field.source_id == field_id {
49                    parts.push(partition_spec_field.clone())
50                }
51            }
52
53            e.insert(parts);
54        }
55
56        &self.cached_parts[&field_id]
57    }
58
59    pub(crate) fn strict_project(
60        &mut self,
61        predicate: &BoundPredicate,
62    ) -> crate::Result<Predicate> {
63        visit(self, predicate)
64    }
65
66    fn get_parts(
67        &mut self,
68        reference: &BoundReference,
69        predicate: &BoundPredicate,
70    ) -> Result<Predicate, Error> {
71        let field_id = reference.field().id;
72
73        // This could be made a bit neater if `try_reduce` ever becomes stable
74        self.get_parts_for_field_id(field_id).iter().try_fold(
75            Predicate::AlwaysFalse,
76            |res, part| {
77                // consider (ts > 2019-01-01T01:00:00) with day(ts) and hour(ts)
78                // projections: d >= 2019-01-02 and h >= 2019-01-01-02 (note the inclusive bounds).
79                // any timestamp where either projection predicate is true must match the original
80                // predicate. For example, ts = 2019-01-01T03:00:00 matches the hour projection but not
81                // the day, but does match the original predicate.
82                Ok(
83                    if let Some(pred_for_part) =
84                        part.transform.strict_project(&part.name, predicate)?
85                    {
86                        if res == Predicate::AlwaysFalse {
87                            pred_for_part
88                        } else {
89                            res.or(pred_for_part)
90                        }
91                    } else {
92                        res
93                    },
94                )
95            },
96        )
97    }
98}
99
100impl BoundPredicateVisitor for StrictProjection {
101    type T = Predicate;
102
103    fn always_true(&mut self) -> crate::Result<Self::T> {
104        Ok(Predicate::AlwaysTrue)
105    }
106
107    fn always_false(&mut self) -> crate::Result<Self::T> {
108        Ok(Predicate::AlwaysFalse)
109    }
110
111    fn and(&mut self, lhs: Self::T, rhs: Self::T) -> crate::Result<Self::T> {
112        Ok(lhs.and(rhs))
113    }
114
115    fn or(&mut self, lhs: Self::T, rhs: Self::T) -> crate::Result<Self::T> {
116        Ok(lhs.or(rhs))
117    }
118
119    fn not(&mut self, _inner: Self::T) -> crate::Result<Self::T> {
120        Err(Error::new(
121            ErrorKind::Unexpected,
122            "StrictProjection should not be performed against Predicates that contain a Not operator. Ensure that \"Rewrite Not\" gets applied to the originating Predicate before binding it.",
123        ))
124    }
125
126    fn is_null(
127        &mut self,
128        reference: &BoundReference,
129        predicate: &BoundPredicate,
130    ) -> crate::Result<Self::T> {
131        self.get_parts(reference, predicate)
132    }
133
134    fn not_null(
135        &mut self,
136        reference: &BoundReference,
137        predicate: &BoundPredicate,
138    ) -> crate::Result<Self::T> {
139        self.get_parts(reference, predicate)
140    }
141
142    fn is_nan(
143        &mut self,
144        reference: &BoundReference,
145        predicate: &BoundPredicate,
146    ) -> crate::Result<Self::T> {
147        self.get_parts(reference, predicate)
148    }
149
150    fn not_nan(
151        &mut self,
152        reference: &BoundReference,
153        predicate: &BoundPredicate,
154    ) -> crate::Result<Self::T> {
155        self.get_parts(reference, predicate)
156    }
157
158    fn less_than(
159        &mut self,
160        reference: &BoundReference,
161        _literal: &Datum,
162        predicate: &BoundPredicate,
163    ) -> crate::Result<Self::T> {
164        self.get_parts(reference, predicate)
165    }
166
167    fn less_than_or_eq(
168        &mut self,
169        reference: &BoundReference,
170        _literal: &Datum,
171        predicate: &BoundPredicate,
172    ) -> crate::Result<Self::T> {
173        self.get_parts(reference, predicate)
174    }
175
176    fn greater_than(
177        &mut self,
178        reference: &BoundReference,
179        _literal: &Datum,
180        predicate: &BoundPredicate,
181    ) -> crate::Result<Self::T> {
182        self.get_parts(reference, predicate)
183    }
184
185    fn greater_than_or_eq(
186        &mut self,
187        reference: &BoundReference,
188        _literal: &Datum,
189        predicate: &BoundPredicate,
190    ) -> crate::Result<Self::T> {
191        self.get_parts(reference, predicate)
192    }
193
194    fn eq(
195        &mut self,
196        reference: &BoundReference,
197        _literal: &Datum,
198        predicate: &BoundPredicate,
199    ) -> crate::Result<Self::T> {
200        self.get_parts(reference, predicate)
201    }
202
203    fn not_eq(
204        &mut self,
205        reference: &BoundReference,
206        _literal: &Datum,
207        predicate: &BoundPredicate,
208    ) -> crate::Result<Self::T> {
209        self.get_parts(reference, predicate)
210    }
211
212    fn starts_with(
213        &mut self,
214        reference: &BoundReference,
215        _literal: &Datum,
216        predicate: &BoundPredicate,
217    ) -> crate::Result<Self::T> {
218        self.get_parts(reference, predicate)
219    }
220
221    fn not_starts_with(
222        &mut self,
223        reference: &BoundReference,
224        _literal: &Datum,
225        predicate: &BoundPredicate,
226    ) -> crate::Result<Self::T> {
227        self.get_parts(reference, predicate)
228    }
229
230    fn r#in(
231        &mut self,
232        reference: &BoundReference,
233        _literals: &FnvHashSet<Datum>,
234        predicate: &BoundPredicate,
235    ) -> crate::Result<Self::T> {
236        self.get_parts(reference, predicate)
237    }
238
239    fn not_in(
240        &mut self,
241        reference: &BoundReference,
242        _literals: &FnvHashSet<Datum>,
243        predicate: &BoundPredicate,
244    ) -> crate::Result<Self::T> {
245        self.get_parts(reference, predicate)
246    }
247}
248
249#[cfg(test)]
250mod tests {
251    use std::sync::Arc;
252
253    use uuid::Uuid;
254
255    use crate::expr::visitors::strict_projection::StrictProjection;
256    use crate::expr::{Bind, Reference};
257    use crate::spec::{
258        Datum, NestedField, PartitionSpec, PrimitiveLiteral, PrimitiveType, Schema, Transform, Type,
259    };
260
261    #[tokio::test]
262    async fn test_strict_projection_month_epoch() {
263        let schema = Arc::new(
264            Schema::builder()
265                .with_fields(vec![
266                    Arc::new(NestedField::required(
267                        1,
268                        "col1",
269                        Type::Primitive(PrimitiveType::Date),
270                    )),
271                    Arc::new(NestedField::required(
272                        2,
273                        "col2",
274                        Type::Primitive(PrimitiveType::Timestamp),
275                    )),
276                    Arc::new(NestedField::required(
277                        3,
278                        "col3",
279                        Type::Primitive(PrimitiveType::Timestamptz),
280                    )),
281                    Arc::new(NestedField::required(
282                        4,
283                        "col4",
284                        Type::Primitive(PrimitiveType::TimestampNs),
285                    )),
286                    Arc::new(NestedField::required(
287                        5,
288                        "col5",
289                        Type::Primitive(PrimitiveType::TimestamptzNs),
290                    )),
291                ])
292                .build()
293                .unwrap(),
294        );
295        let partition_spec = Arc::new(
296            PartitionSpec::builder(schema.clone())
297                .with_spec_id(1)
298                .add_partition_field("col1", "pcol1", Transform::Month)
299                .unwrap()
300                .add_partition_field("col2", "pcol2", Transform::Month)
301                .unwrap()
302                .add_partition_field("col3", "pcol3", Transform::Month)
303                .unwrap()
304                .add_partition_field("col4", "pcol4", Transform::Month)
305                .unwrap()
306                .add_partition_field("col5", "pcol5", Transform::Month)
307                .unwrap()
308                .build()
309                .unwrap(),
310        );
311
312        let mut strict_projection = StrictProjection::new(partition_spec.clone());
313
314        // test eq: (col1 = 1970-01-01) AND (col2 = 1970-01-01) AND (col3 = 1970-01-01) AND (col4 = 1970-01-01) AND (col5 = 1970-01-01)
315        let predicate = Reference::new("col1")
316            .equal_to(Datum::date(0))
317            .and(Reference::new("col2").equal_to(Datum::timestamp_micros(0)))
318            .and(Reference::new("col3").equal_to(Datum::timestamptz_micros(0)))
319            .and(Reference::new("col4").equal_to(Datum::timestamp_nanos(0)))
320            .and(Reference::new("col5").equal_to(Datum::timestamptz_nanos(0)))
321            .bind(schema.clone(), false)
322            .unwrap();
323        let result = strict_projection.strict_project(&predicate).unwrap();
324        assert_eq!(result.to_string(), "FALSE".to_string());
325
326        // test not eq: (col1 != 1970-01-01) AND (col2 != 1970-01-01) AND (col3 != 1970-01-01) AND (col4 != 1970-01-01) AND (col5 != 1970-01-01)
327        let predicate = Reference::new("col1")
328            .not_equal_to(Datum::date(0))
329            .and(Reference::new("col2").not_equal_to(Datum::timestamp_micros(0)))
330            .and(Reference::new("col3").not_equal_to(Datum::timestamptz_micros(0)))
331            .and(Reference::new("col4").not_equal_to(Datum::timestamp_nanos(0)))
332            .and(Reference::new("col5").not_equal_to(Datum::timestamptz_nanos(0)))
333            .bind(schema.clone(), false)
334            .unwrap();
335        let result = strict_projection.strict_project(&predicate).unwrap();
336        assert_eq!(result.to_string(), "((((pcol1 != 0) AND (pcol2 != 0)) AND (pcol3 != 0)) AND (pcol4 != 0)) AND (pcol5 != 0)".to_string());
337
338        // test less: (col1 < 1970-01-01) AND (col2 < 1970-01-01) AND (col3 < 1970-01-01) AND (col4 < 1970-01-01) AND (col5 < 1970-01-01)
339        let predicate = Reference::new("col1")
340            .less_than(Datum::date(0))
341            .and(Reference::new("col2").less_than(Datum::timestamp_micros(0)))
342            .and(Reference::new("col3").less_than(Datum::timestamptz_micros(0)))
343            .and(Reference::new("col4").less_than(Datum::timestamp_nanos(0)))
344            .and(Reference::new("col5").less_than(Datum::timestamptz_nanos(0)))
345            .bind(schema.clone(), false)
346            .unwrap();
347        let result = strict_projection.strict_project(&predicate).unwrap();
348        assert_eq!(
349            result.to_string(),
350            "((((pcol1 < 0) AND (pcol2 < 0)) AND (pcol3 < 0)) AND (pcol4 < 0)) AND (pcol5 < 0)"
351                .to_string()
352        );
353
354        // test less or eq: (col1 <= 1970-01-01) AND (col2 <= 1970-01-01) AND (col3 <= 1970-01-01) AND (col4 <= 1970-01-01) AND (col5 <= 1970-01-01)
355        let predicate = Reference::new("col1")
356            .less_than_or_equal_to(Datum::date(0))
357            .and(Reference::new("col2").less_than_or_equal_to(Datum::timestamp_micros(0)))
358            .and(Reference::new("col3").less_than_or_equal_to(Datum::timestamptz_micros(0)))
359            .and(Reference::new("col4").less_than_or_equal_to(Datum::timestamp_nanos(0)))
360            .and(Reference::new("col5").less_than_or_equal_to(Datum::timestamptz_nanos(0)))
361            .bind(schema.clone(), false)
362            .unwrap();
363        let result = strict_projection.strict_project(&predicate).unwrap();
364        assert_eq!(
365            result.to_string(),
366            "((((pcol1 < 0) AND (pcol2 < 0)) AND (pcol3 < 0)) AND (pcol4 < 0)) AND (pcol5 < 0)"
367                .to_string()
368        );
369
370        // test greater: (col1 > 1970-01-01) AND (col2 > 1970-01-01) AND (col3 > 1970-01-01) AND (col4 > 1970-01-01) AND (col5 > 1970-01-01)
371        let predicate = Reference::new("col1")
372            .greater_than(Datum::date(0))
373            .and(Reference::new("col2").greater_than(Datum::timestamp_micros(0)))
374            .and(Reference::new("col3").greater_than(Datum::timestamptz_micros(0)))
375            .and(Reference::new("col4").greater_than(Datum::timestamp_nanos(0)))
376            .and(Reference::new("col5").greater_than(Datum::timestamptz_nanos(0)))
377            .bind(schema.clone(), false)
378            .unwrap();
379        let result = strict_projection.strict_project(&predicate).unwrap();
380        assert_eq!(
381            result.to_string(),
382            "((((pcol1 > 0) AND (pcol2 > 0)) AND (pcol3 > 0)) AND (pcol4 > 0)) AND (pcol5 > 0)"
383                .to_string()
384        );
385
386        // test greater or eq: (col1 >= 1970-01-01) AND (col2 >= 1970-01-01) AND (col3 >= 1970-01-01) AND (col4 >= 1970-01-01) AND (col5 >= 1970-01-01)
387        let predicate = Reference::new("col1")
388            .greater_than_or_equal_to(Datum::date(0))
389            .and(Reference::new("col2").greater_than_or_equal_to(Datum::timestamp_micros(0)))
390            .and(Reference::new("col3").greater_than_or_equal_to(Datum::timestamptz_micros(0)))
391            .and(Reference::new("col4").greater_than_or_equal_to(Datum::timestamp_nanos(0)))
392            .and(Reference::new("col5").greater_than_or_equal_to(Datum::timestamptz_nanos(0)))
393            .bind(schema.clone(), false)
394            .unwrap();
395        let result = strict_projection.strict_project(&predicate).unwrap();
396        assert_eq!(
397            result.to_string(),
398            "((((pcol1 > -1) AND (pcol2 > -1)) AND (pcol3 > -1)) AND (pcol4 > -1)) AND (pcol5 > -1)"
399                .to_string()
400        );
401
402        // test not in
403        let predicate =
404            Reference::new("col1")
405                .is_not_in(
406                    vec![
407                        Datum::date_from_str("1970-01-01").unwrap(),
408                        Datum::date_from_str("1969-12-31").unwrap(),
409                    ]
410                    .into_iter(),
411                )
412                .and(Reference::new("col2").is_not_in(
413                    vec![Datum::timestamp_micros(0), Datum::timestamp_micros(-1)].into_iter(),
414                ))
415                .and(Reference::new("col3").is_not_in(
416                    vec![Datum::timestamptz_micros(0), Datum::timestamptz_micros(-1)].into_iter(),
417                ))
418                .and(Reference::new("col4").is_not_in(
419                    vec![Datum::timestamp_nanos(0), Datum::timestamp_nanos(-1)].into_iter(),
420                ))
421                .and(Reference::new("col5").is_not_in(
422                    vec![Datum::timestamptz_nanos(0), Datum::timestamptz_nanos(-1)].into_iter(),
423                ))
424                .bind(schema.clone(), false)
425                .unwrap();
426        let result = strict_projection.strict_project(&predicate).unwrap();
427        assert_eq!(result.to_string(), "((((pcol1 NOT IN (0, -1)) AND (pcol2 NOT IN (0, -1))) AND (pcol3 NOT IN (0, -1))) AND (pcol4 NOT IN (0, -1))) AND (pcol5 NOT IN (0, -1))".to_string());
428
429        // test in
430        let predicate =
431            Reference::new("col1")
432                .is_in(
433                    vec![
434                        Datum::date_from_str("1970-01-01").unwrap(),
435                        Datum::date_from_str("1969-12-31").unwrap(),
436                    ]
437                    .into_iter(),
438                )
439                .and(Reference::new("col2").is_in(
440                    vec![Datum::timestamp_micros(0), Datum::timestamp_micros(-1)].into_iter(),
441                ))
442                .bind(schema.clone(), false)
443                .unwrap();
444        let result = strict_projection.strict_project(&predicate).unwrap();
445        assert_eq!(result.to_string(), "FALSE".to_string());
446    }
447
448    #[tokio::test]
449    async fn test_strict_projection_month_lower_bound() {
450        let schema = Arc::new(
451            Schema::builder()
452                .with_fields(vec![
453                    Arc::new(NestedField::required(
454                        1,
455                        "col1",
456                        Type::Primitive(PrimitiveType::Date),
457                    )),
458                    Arc::new(NestedField::required(
459                        2,
460                        "col2",
461                        Type::Primitive(PrimitiveType::Timestamp),
462                    )),
463                    Arc::new(NestedField::required(
464                        3,
465                        "col3",
466                        Type::Primitive(PrimitiveType::Timestamptz),
467                    )),
468                    Arc::new(NestedField::required(
469                        4,
470                        "col4",
471                        Type::Primitive(PrimitiveType::TimestampNs),
472                    )),
473                    Arc::new(NestedField::required(
474                        5,
475                        "col5",
476                        Type::Primitive(PrimitiveType::TimestamptzNs),
477                    )),
478                ])
479                .build()
480                .unwrap(),
481        );
482        let partition_spec = Arc::new(
483            PartitionSpec::builder(schema.clone())
484                .with_spec_id(1)
485                .add_partition_field("col1", "pcol1", Transform::Month)
486                .unwrap()
487                .add_partition_field("col2", "pcol2", Transform::Month)
488                .unwrap()
489                .add_partition_field("col3", "pcol3", Transform::Month)
490                .unwrap()
491                .add_partition_field("col4", "pcol4", Transform::Month)
492                .unwrap()
493                .add_partition_field("col5", "pcol5", Transform::Month)
494                .unwrap()
495                .build()
496                .unwrap(),
497        );
498
499        let mut strict_projection = StrictProjection::new(partition_spec.clone());
500
501        // test eq: (col1 = 2017-01-01) AND (col2 = 2017-01-01) AND (col3 = 2017-01-01) AND (col4 = 2017-01-01) AND (col5 = 2017-01-01)
502        let predicate = Reference::new("col1")
503            .equal_to(Datum::date_from_str("2017-01-01").unwrap())
504            .and(Reference::new("col2").equal_to(Datum::timestamp_micros(1483228800000000)))
505            .and(Reference::new("col3").equal_to(Datum::timestamptz_micros(1483228800000000)))
506            .and(Reference::new("col4").equal_to(Datum::timestamp_nanos(1483228800000000000)))
507            .and(Reference::new("col5").equal_to(Datum::timestamptz_nanos(1483228800000000000)))
508            .bind(schema.clone(), false)
509            .unwrap();
510        let result = strict_projection.strict_project(&predicate).unwrap();
511        assert_eq!(result.to_string(), "FALSE".to_string());
512
513        // test not eq: (col1 != 2017-01-01) AND (col2 != 2017-01-01) AND (col3 != 2017-01-01) AND (col4 != 2017-01-01) AND (col5 != 2017-01-01)
514        let predicate = Reference::new("col1")
515            .not_equal_to(Datum::date_from_str("2017-01-01").unwrap())
516            .and(Reference::new("col2").not_equal_to(Datum::timestamp_micros(1483228800000000)))
517            .and(Reference::new("col3").not_equal_to(Datum::timestamptz_micros(1483228800000000)))
518            .and(Reference::new("col4").not_equal_to(Datum::timestamp_nanos(1483228800000000000)))
519            .and(Reference::new("col5").not_equal_to(Datum::timestamptz_nanos(1483228800000000000)))
520            .bind(schema.clone(), false)
521            .unwrap();
522        let result = strict_projection.strict_project(&predicate).unwrap();
523        assert_eq!(result.to_string(), "((((pcol1 != 564) AND (pcol2 != 564)) AND (pcol3 != 564)) AND (pcol4 != 564)) AND (pcol5 != 564)".to_string());
524
525        // test less: (col1 < 2017-01-01) AND (col2 < 2017-01-01) AND (col3 < 2017-01-01) AND (col4 < 2017-01-01) AND (col5 < 2017-01-01)
526        let predicate = Reference::new("col1")
527            .less_than(Datum::date_from_str("2017-01-01").unwrap())
528            .and(Reference::new("col2").less_than(Datum::timestamp_micros(1483228800000000)))
529            .and(Reference::new("col3").less_than(Datum::timestamptz_micros(1483228800000000)))
530            .and(Reference::new("col4").less_than(Datum::timestamp_nanos(1483228800000000000)))
531            .and(Reference::new("col5").less_than(Datum::timestamptz_nanos(1483228800000000000)))
532            .bind(schema.clone(), false)
533            .unwrap();
534        let result = strict_projection.strict_project(&predicate).unwrap();
535        assert_eq!(
536            result.to_string(),
537            "((((pcol1 < 564) AND (pcol2 < 564)) AND (pcol3 < 564)) AND (pcol4 < 564)) AND (pcol5 < 564)"
538                .to_string()
539        );
540
541        // test less or eq: (col1 <= 2017-01-01) AND (col2 <= 2017-01-01) AND (col3 <= 2017-01-01) AND (col4 <= 2017-01-01) AND (col5 <= 2017-01-01)
542        let predicate = Reference::new("col1")
543            .less_than_or_equal_to(Datum::date_from_str("2017-01-01").unwrap())
544            .and(
545                Reference::new("col2")
546                    .less_than_or_equal_to(Datum::timestamp_micros(1483228800000000)),
547            )
548            .and(
549                Reference::new("col3")
550                    .less_than_or_equal_to(Datum::timestamptz_micros(1483228800000000)),
551            )
552            .and(
553                Reference::new("col4")
554                    .less_than_or_equal_to(Datum::timestamp_nanos(1483228800000000000)),
555            )
556            .and(
557                Reference::new("col5")
558                    .less_than_or_equal_to(Datum::timestamptz_nanos(1483228800000000000)),
559            )
560            .bind(schema.clone(), false)
561            .unwrap();
562        let result = strict_projection.strict_project(&predicate).unwrap();
563        assert_eq!(
564            result.to_string(),
565            "((((pcol1 < 564) AND (pcol2 < 564)) AND (pcol3 < 564)) AND (pcol4 < 564)) AND (pcol5 < 564)"
566                .to_string()
567        );
568
569        // test greater: (col1 > 2017-01-01) AND (col2 > 2017-01-01) AND (col3 > 2017-01-01) AND (col4 > 2017-01-01) AND (col5 > 2017-01-01)
570        let predicate = Reference::new("col1")
571            .greater_than(Datum::date_from_str("2017-01-01").unwrap())
572            .and(Reference::new("col2").greater_than(Datum::timestamp_micros(1483228800000000)))
573            .and(Reference::new("col3").greater_than(Datum::timestamptz_micros(1483228800000000)))
574            .and(Reference::new("col4").greater_than(Datum::timestamp_nanos(1483228800000000000)))
575            .and(Reference::new("col5").greater_than(Datum::timestamptz_nanos(1483228800000000000)))
576            .bind(schema.clone(), false)
577            .unwrap();
578        let result = strict_projection.strict_project(&predicate).unwrap();
579        assert_eq!(
580            result.to_string(),
581            "((((pcol1 > 564) AND (pcol2 > 564)) AND (pcol3 > 564)) AND (pcol4 > 564)) AND (pcol5 > 564)"
582                .to_string()
583        );
584
585        // test greater or eq: (col1 >= 2017-01-01) AND (col2 >= 2017-01-01) AND (col3 >= 2017-01-01) AND (col4 >= 2017-01-01) AND (col5 >= 2017-01-01)
586        let predicate = Reference::new("col1")
587            .greater_than_or_equal_to(Datum::date_from_str("2017-01-01").unwrap())
588            .and(
589                Reference::new("col2")
590                    .greater_than_or_equal_to(Datum::timestamp_micros(1483228800000000)),
591            )
592            .and(
593                Reference::new("col3")
594                    .greater_than_or_equal_to(Datum::timestamptz_micros(1483228800000000)),
595            )
596            .and(
597                Reference::new("col4")
598                    .greater_than_or_equal_to(Datum::timestamp_nanos(1483228800000000000)),
599            )
600            .and(
601                Reference::new("col5")
602                    .greater_than_or_equal_to(Datum::timestamptz_nanos(1483228800000000000)),
603            )
604            .bind(schema.clone(), false)
605            .unwrap();
606        let result = strict_projection.strict_project(&predicate).unwrap();
607        assert_eq!(
608            result.to_string(),
609            "((((pcol1 > 563) AND (pcol2 > 563)) AND (pcol3 > 563)) AND (pcol4 > 563)) AND (pcol5 > 563)"
610                .to_string()
611        );
612
613        // test not in (col1 NOT IN (2017-01-01, 2017-12-02)) AND (col2 NOT IN (2017-01-01, 2017-12-02)) AND (col3 NOT IN (2017-01-01, 2017-12-02)) AND (col4 NOT IN (2017-01-01, 2017-12-02)) AND (col5 NOT IN (2017-01-01, 2017-12-02))
614        let predicate = Reference::new("col1")
615            .is_not_in(
616                vec![
617                    Datum::date_from_str("2017-01-01").unwrap(),
618                    Datum::date_from_str("2017-12-02").unwrap(),
619                ]
620                .into_iter(),
621            )
622            .and(
623                Reference::new("col2").is_not_in(
624                    vec![
625                        Datum::timestamp_micros(1483228800000000),
626                        Datum::timestamp_micros(1512182400000000),
627                    ]
628                    .into_iter(),
629                ),
630            )
631            .and(
632                Reference::new("col3").is_not_in(
633                    vec![
634                        Datum::timestamptz_micros(1483228800000000),
635                        Datum::timestamptz_micros(1512182400000000),
636                    ]
637                    .into_iter(),
638                ),
639            )
640            .and(
641                Reference::new("col4").is_not_in(
642                    vec![
643                        Datum::timestamp_nanos(1483228800000000000),
644                        Datum::timestamp_nanos(1512182400000000000),
645                    ]
646                    .into_iter(),
647                ),
648            )
649            .and(
650                Reference::new("col5").is_not_in(
651                    vec![
652                        Datum::timestamptz_nanos(1483228800000000000),
653                        Datum::timestamptz_nanos(1512182400000000000),
654                    ]
655                    .into_iter(),
656                ),
657            )
658            .bind(schema.clone(), false)
659            .unwrap();
660        let result = strict_projection.strict_project(&predicate).unwrap();
661        assert_eq!(result.to_string(), "((((pcol1 NOT IN (575, 564)) AND (pcol2 NOT IN (575, 564))) AND (pcol3 NOT IN (575, 564))) AND (pcol4 NOT IN (575, 564))) AND (pcol5 NOT IN (575, 564))".to_string());
662
663        // test in
664        let predicate = Reference::new("col1")
665            .is_in(
666                vec![
667                    Datum::date_from_str("2017-01-01").unwrap(),
668                    Datum::date_from_str("2017-01-02").unwrap(),
669                ]
670                .into_iter(),
671            )
672            .and(
673                Reference::new("col2").is_in(
674                    vec![
675                        Datum::timestamp_micros(1483228800000000),
676                        Datum::timestamp_micros(1483315200000000),
677                    ]
678                    .into_iter(),
679                ),
680            )
681            .bind(schema.clone(), false)
682            .unwrap();
683        let result = strict_projection.strict_project(&predicate).unwrap();
684        assert_eq!(result.to_string(), "FALSE".to_string());
685    }
686
687    #[tokio::test]
688    async fn test_strict_projection_negative_month_lower_bound() {
689        let schema = Arc::new(
690            Schema::builder()
691                .with_fields(vec![
692                    Arc::new(NestedField::required(
693                        1,
694                        "col1",
695                        Type::Primitive(PrimitiveType::Date),
696                    )),
697                    Arc::new(NestedField::required(
698                        2,
699                        "col2",
700                        Type::Primitive(PrimitiveType::Timestamp),
701                    )),
702                    Arc::new(NestedField::required(
703                        3,
704                        "col3",
705                        Type::Primitive(PrimitiveType::Timestamptz),
706                    )),
707                    Arc::new(NestedField::required(
708                        4,
709                        "col4",
710                        Type::Primitive(PrimitiveType::TimestampNs),
711                    )),
712                    Arc::new(NestedField::required(
713                        5,
714                        "col5",
715                        Type::Primitive(PrimitiveType::TimestamptzNs),
716                    )),
717                ])
718                .build()
719                .unwrap(),
720        );
721
722        let partition_spec = Arc::new(
723            PartitionSpec::builder(schema.clone())
724                .with_spec_id(1)
725                .add_partition_field("col1", "pcol1", Transform::Month)
726                .unwrap()
727                .add_partition_field("col2", "pcol2", Transform::Month)
728                .unwrap()
729                .add_partition_field("col3", "pcol3", Transform::Month)
730                .unwrap()
731                .add_partition_field("col4", "pcol4", Transform::Month)
732                .unwrap()
733                .add_partition_field("col5", "pcol5", Transform::Month)
734                .unwrap()
735                .build()
736                .unwrap(),
737        );
738
739        let mut strict_projection = StrictProjection::new(partition_spec.clone());
740
741        // test less: (col1 < 1969-01-01) AND (col2 < 1969-01-01) AND (col3 < 1969-01-01) AND (col4 < 1969-01-01) AND (col5 < 1969-01-01)
742        let predicate = Reference::new("col1")
743            .less_than(Datum::date_from_str("1969-01-01").unwrap())
744            .and(Reference::new("col2").less_than(Datum::timestamp_micros(-31536000000000)))
745            .and(Reference::new("col3").less_than(Datum::timestamptz_micros(-31536000000000)))
746            .and(Reference::new("col4").less_than(Datum::timestamp_nanos(-31536000000000000)))
747            .and(Reference::new("col5").less_than(Datum::timestamptz_nanos(-31536000000000000)))
748            .bind(schema.clone(), false)
749            .unwrap();
750        let result = strict_projection.strict_project(&predicate).unwrap();
751        assert_eq!(
752            result.to_string(),
753            "((((pcol1 < -12) AND (pcol2 < -12)) AND (pcol3 < -12)) AND (pcol4 < -12)) AND (pcol5 < -12)"
754                .to_string()
755        );
756
757        // test less or eq: (col1 <= 1969-01-01) AND (col2 <= 1969-01-01) AND (col3 <= 1969-01-01) AND (col4 <= 1969-01-01) AND (col5 <= 1969-01-01)
758        let predicate = Reference::new("col1")
759            .less_than_or_equal_to(Datum::date_from_str("1969-01-01").unwrap())
760            .and(
761                Reference::new("col2")
762                    .less_than_or_equal_to(Datum::timestamp_micros(-31536000000000)),
763            )
764            .and(
765                Reference::new("col3")
766                    .less_than_or_equal_to(Datum::timestamptz_micros(-31536000000000)),
767            )
768            .and(
769                Reference::new("col4")
770                    .less_than_or_equal_to(Datum::timestamp_nanos(-31536000000000000)),
771            )
772            .and(
773                Reference::new("col5")
774                    .less_than_or_equal_to(Datum::timestamptz_nanos(-31536000000000000)),
775            )
776            .bind(schema.clone(), false)
777            .unwrap();
778        let result = strict_projection.strict_project(&predicate).unwrap();
779        assert_eq!(
780            result.to_string(),
781            "((((pcol1 < -12) AND (pcol2 < -12)) AND (pcol3 < -12)) AND (pcol4 < -12)) AND (pcol5 < -12)"
782                .to_string()
783        );
784
785        // test greater: (col1 > 1969-01-01) AND (col2 > 1969-01-01) AND (col3 > 1969-01-01) AND (col4 > 1969-01-01) AND (col5 > 1969-01-01)
786        let predicate = Reference::new("col1")
787            .greater_than(Datum::date_from_str("1969-01-01").unwrap())
788            .and(Reference::new("col2").greater_than(Datum::timestamp_micros(-31536000000000)))
789            .and(Reference::new("col3").greater_than(Datum::timestamptz_micros(-31536000000000)))
790            .and(Reference::new("col4").greater_than(Datum::timestamp_nanos(-31536000000000000)))
791            .and(Reference::new("col5").greater_than(Datum::timestamptz_nanos(-31536000000000000)))
792            .bind(schema.clone(), false)
793            .unwrap();
794        let result = strict_projection.strict_project(&predicate).unwrap();
795        assert_eq!(
796            result.to_string(),
797            "((((pcol1 > -12) AND (pcol2 > -12)) AND (pcol3 > -12)) AND (pcol4 > -12)) AND (pcol5 > -12)"
798                .to_string()
799        );
800
801        // test greater or eq: (col1 >= 1969-01-01) AND (col2 >= 1969-01-01) AND (col3 >= 1969-01-01) AND (col4 >= 1969-01-01) AND (col5 >= 1969-01-01)
802        let predicate = Reference::new("col1")
803            .greater_than_or_equal_to(Datum::date_from_str("1969-01-01").unwrap())
804            .and(
805                Reference::new("col2")
806                    .greater_than_or_equal_to(Datum::timestamp_micros(-31536000000000)),
807            )
808            .and(
809                Reference::new("col3")
810                    .greater_than_or_equal_to(Datum::timestamptz_micros(-31536000000000)),
811            )
812            .and(
813                Reference::new("col4")
814                    .greater_than_or_equal_to(Datum::timestamp_nanos(-31536000000000000)),
815            )
816            .and(
817                Reference::new("col5")
818                    .greater_than_or_equal_to(Datum::timestamptz_nanos(-31536000000000000)),
819            )
820            .bind(schema.clone(), false)
821            .unwrap();
822        let result = strict_projection.strict_project(&predicate).unwrap();
823        assert_eq!(
824            result.to_string(),
825            "((((pcol1 > -13) AND (pcol2 > -13)) AND (pcol3 > -13)) AND (pcol4 > -13)) AND (pcol5 > -13)"
826                .to_string()
827        );
828
829        // test not eq: (col1 != 1969-01-01) AND (col2 != 1969-01-01) AND (col3 != 1969-01-01) AND (col4 != 1969-01-01) AND (col5 != 1969-01-01)
830        let predicate = Reference::new("col1")
831            .not_equal_to(Datum::date_from_str("1969-01-01").unwrap())
832            .and(Reference::new("col2").not_equal_to(Datum::timestamp_micros(-31536000000000)))
833            .and(Reference::new("col3").not_equal_to(Datum::timestamptz_micros(-31536000000000)))
834            .and(Reference::new("col4").not_equal_to(Datum::timestamp_nanos(-31536000000000000)))
835            .and(Reference::new("col5").not_equal_to(Datum::timestamptz_nanos(-31536000000000000)))
836            .bind(schema.clone(), false)
837            .unwrap();
838        let result = strict_projection.strict_project(&predicate).unwrap();
839        assert_eq!(result.to_string(), "((((pcol1 != -12) AND (pcol2 != -12)) AND (pcol3 != -12)) AND (pcol4 != -12)) AND (pcol5 != -12)".to_string());
840
841        // test not in: (col1 NOT IN (1969-01-01, 1969-12-31)) AND (col2 NOT IN (1969-01-01, 1969-12-31)) AND (col3 NOT IN (1969-01-01, 1969-12-31)) AND (col4 NOT IN (1969-01-01, 1969-12-31)) AND (col5 NOT IN (1969-01-01, 1969-12-31))
842        let predicate = Reference::new("col1")
843            .is_not_in(
844                vec![
845                    Datum::date_from_str("1969-01-01").unwrap(),
846                    Datum::date_from_str("1969-12-31").unwrap(),
847                ]
848                .into_iter(),
849            )
850            .and(
851                Reference::new("col2").is_not_in(
852                    vec![
853                        Datum::timestamp_micros(-31536000000000),
854                        Datum::timestamp_micros(-86400000000),
855                    ]
856                    .into_iter(),
857                ),
858            )
859            .and(
860                Reference::new("col3").is_not_in(
861                    vec![
862                        Datum::timestamptz_micros(-86400000000),
863                        Datum::timestamptz_micros(-31536000000000),
864                    ]
865                    .into_iter(),
866                ),
867            )
868            .and(
869                Reference::new("col4").is_not_in(
870                    vec![
871                        Datum::timestamp_nanos(-86400000000000),
872                        Datum::timestamp_nanos(-31536000000000000),
873                    ]
874                    .into_iter(),
875                ),
876            )
877            .and(
878                Reference::new("col5").is_not_in(
879                    vec![
880                        Datum::timestamptz_nanos(-86400000000000),
881                        Datum::timestamptz_nanos(-31536000000000000),
882                    ]
883                    .into_iter(),
884                ),
885            )
886            .bind(schema.clone(), false)
887            .unwrap();
888        let result = strict_projection.strict_project(&predicate).unwrap();
889        assert_eq!(result.to_string(), "((((pcol1 NOT IN (-1, -12)) AND (pcol2 NOT IN (-1, -12))) AND (pcol3 NOT IN (-1, -12))) AND (pcol4 NOT IN (-1, -12))) AND (pcol5 NOT IN (-1, -12))".to_string());
890
891        // test in
892        let predicate = Reference::new("col1")
893            .is_in(
894                vec![
895                    Datum::date_from_str("1969-01-01").unwrap(),
896                    Datum::date_from_str("1969-12-31").unwrap(),
897                ]
898                .into_iter(),
899            )
900            .and(
901                Reference::new("col2").is_in(
902                    vec![
903                        Datum::timestamp_micros(-315619200000000),
904                        Datum::timestamp_micros(-86400000000),
905                    ]
906                    .into_iter(),
907                ),
908            )
909            .bind(schema.clone(), false)
910            .unwrap();
911        let result = strict_projection.strict_project(&predicate).unwrap();
912        assert_eq!(result.to_string(), "FALSE".to_string());
913    }
914
915    #[tokio::test]
916    async fn test_strict_projection_month_upper_bound() {
917        let schema = Arc::new(
918            Schema::builder()
919                .with_fields(vec![
920                    Arc::new(NestedField::required(
921                        1,
922                        "col1",
923                        Type::Primitive(PrimitiveType::Date),
924                    )),
925                    Arc::new(NestedField::required(
926                        2,
927                        "col2",
928                        Type::Primitive(PrimitiveType::Timestamp),
929                    )),
930                    Arc::new(NestedField::required(
931                        3,
932                        "col3",
933                        Type::Primitive(PrimitiveType::Timestamptz),
934                    )),
935                    Arc::new(NestedField::required(
936                        4,
937                        "col4",
938                        Type::Primitive(PrimitiveType::TimestampNs),
939                    )),
940                    Arc::new(NestedField::required(
941                        5,
942                        "col5",
943                        Type::Primitive(PrimitiveType::TimestamptzNs),
944                    )),
945                ])
946                .build()
947                .unwrap(),
948        );
949
950        let partition_spec = Arc::new(
951            PartitionSpec::builder(schema.clone())
952                .with_spec_id(1)
953                .add_partition_field("col1", "pcol1", Transform::Month)
954                .unwrap()
955                .add_partition_field("col2", "pcol2", Transform::Month)
956                .unwrap()
957                .add_partition_field("col3", "pcol3", Transform::Month)
958                .unwrap()
959                .add_partition_field("col4", "pcol4", Transform::Month)
960                .unwrap()
961                .add_partition_field("col5", "pcol5", Transform::Month)
962                .unwrap()
963                .build()
964                .unwrap(),
965        );
966
967        let mut strict_projection = StrictProjection::new(partition_spec.clone());
968
969        // test less: (col1 < 2017-12-31) AND (col2 < 2017-12-31) AND (col3 < 2017-12-31) AND (col4 < 2017-12-31) AND (col5 < 2017-12-31)
970        let predicate = Reference::new("col1")
971            .less_than(Datum::date_from_str("2017-12-31").unwrap())
972            .and(Reference::new("col2").less_than(Datum::timestamp_micros(1514764799000000)))
973            .and(Reference::new("col3").less_than(Datum::timestamptz_micros(1514764799000000)))
974            .and(Reference::new("col4").less_than(Datum::timestamp_nanos(1514764799000000000)))
975            .and(Reference::new("col5").less_than(Datum::timestamptz_nanos(1514764799000000000)))
976            .bind(schema.clone(), false)
977            .unwrap();
978        let result = strict_projection.strict_project(&predicate).unwrap();
979        assert_eq!(
980            result.to_string(),
981            "((((pcol1 < 575) AND (pcol2 < 575)) AND (pcol3 < 575)) AND (pcol4 < 575)) AND (pcol5 < 575)"
982                .to_string()
983        );
984
985        // test less or eq: (col1 <= 2017-12-31) AND (col2 <= 2017-12-31) AND (col3 <= 2017-12-31) AND (col4 <= 2017-12-31) AND (col5 <= 2017-12-31)
986        let predicate = Reference::new("col1")
987            .less_than_or_equal_to(Datum::date_from_str("2017-12-31").unwrap())
988            .and(
989                Reference::new("col2")
990                    .less_than_or_equal_to(Datum::timestamp_micros(1514764799000000)),
991            )
992            .and(
993                Reference::new("col3")
994                    .less_than_or_equal_to(Datum::timestamptz_micros(1514764799000000)),
995            )
996            .and(
997                Reference::new("col4")
998                    .less_than_or_equal_to(Datum::timestamp_nanos(1514764799000000000)),
999            )
1000            .and(
1001                Reference::new("col5")
1002                    .less_than_or_equal_to(Datum::timestamptz_nanos(1514764799000000000)),
1003            )
1004            .bind(schema.clone(), false)
1005            .unwrap();
1006        let result = strict_projection.strict_project(&predicate).unwrap();
1007        assert_eq!(
1008            result.to_string(),
1009            "((((pcol1 < 576) AND (pcol2 < 575)) AND (pcol3 < 575)) AND (pcol4 < 575)) AND (pcol5 < 575)"
1010                .to_string()
1011        );
1012
1013        // test greater: (col1 > 2017-12-31) AND (col2 > 2017-12-31) AND (col3 > 2017-12-31) AND (col4 > 2017-12-31) AND (col5 > 2017-12-31)
1014        let predicate = Reference::new("col1")
1015            .greater_than(Datum::date_from_str("2017-12-31").unwrap())
1016            .and(Reference::new("col2").greater_than(Datum::timestamp_micros(1514764799000000)))
1017            .and(Reference::new("col3").greater_than(Datum::timestamptz_micros(1514764799000000)))
1018            .and(Reference::new("col4").greater_than(Datum::timestamp_nanos(1514764799000000000)))
1019            .and(Reference::new("col5").greater_than(Datum::timestamptz_nanos(1514764799000000000)))
1020            .bind(schema.clone(), false)
1021            .unwrap();
1022        let result = strict_projection.strict_project(&predicate).unwrap();
1023        assert_eq!(
1024            result.to_string(),
1025            "((((pcol1 > 575) AND (pcol2 > 575)) AND (pcol3 > 575)) AND (pcol4 > 575)) AND (pcol5 > 575)"
1026                .to_string()
1027        );
1028
1029        // test greater or eq: (col1 >= 2017-12-31) AND (col2 >= 2017-12-31) AND (col3 >= 2017-12-31) AND (col4 >= 2017-12-31) AND (col5 >= 2017-12-31)
1030        let predicate = Reference::new("col1")
1031            .greater_than_or_equal_to(Datum::date_from_str("2017-12-31").unwrap())
1032            .and(
1033                Reference::new("col2")
1034                    .greater_than_or_equal_to(Datum::timestamp_micros(1514764799000000)),
1035            )
1036            .and(
1037                Reference::new("col3")
1038                    .greater_than_or_equal_to(Datum::timestamptz_micros(1514764799000000)),
1039            )
1040            .and(
1041                Reference::new("col4")
1042                    .greater_than_or_equal_to(Datum::timestamp_nanos(1514764799000000000)),
1043            )
1044            .and(
1045                Reference::new("col5")
1046                    .greater_than_or_equal_to(Datum::timestamptz_nanos(1514764799000000000)),
1047            )
1048            .bind(schema.clone(), false)
1049            .unwrap();
1050        let result = strict_projection.strict_project(&predicate).unwrap();
1051        assert_eq!(
1052            result.to_string(),
1053            "((((pcol1 > 575) AND (pcol2 > 575)) AND (pcol3 > 575)) AND (pcol4 > 575)) AND (pcol5 > 575)"
1054                .to_string()
1055        );
1056
1057        // test not eq: (col1 != 2017-12-31) AND (col2 != 2017-12-31) AND (col3 != 2017-12-31) AND (col4 != 2017-12-31) AND (col5 != 2017-12-31)
1058        let predicate = Reference::new("col1")
1059            .not_equal_to(Datum::date_from_str("2017-12-31").unwrap())
1060            .and(Reference::new("col2").not_equal_to(Datum::timestamp_micros(1514764799000000)))
1061            .and(Reference::new("col3").not_equal_to(Datum::timestamptz_micros(1514764799000000)))
1062            .and(Reference::new("col4").not_equal_to(Datum::timestamp_nanos(1514764799000000000)))
1063            .and(Reference::new("col5").not_equal_to(Datum::timestamptz_nanos(1514764799000000000)))
1064            .bind(schema.clone(), false)
1065            .unwrap();
1066        let result = strict_projection.strict_project(&predicate).unwrap();
1067        assert_eq!(result.to_string(), "((((pcol1 != 575) AND (pcol2 != 575)) AND (pcol3 != 575)) AND (pcol4 != 575)) AND (pcol5 != 575)".to_string());
1068
1069        // test not in: (col1 NOT IN (2017-12-31, 2017-01-01)) AND (col2 NOT IN (2017-12-31, 2017-01-01)) AND (col3 NOT IN (2017-12-31, 2017-01-01)) AND (col4 NOT IN (2017-12-31, 2017-01-01)) AND (col5 NOT IN (2017-12-31, 2017-01-01))
1070        let predicate = Reference::new("col1")
1071            .is_not_in(
1072                vec![
1073                    Datum::date_from_str("2017-12-31").unwrap(),
1074                    Datum::date_from_str("2017-01-01").unwrap(),
1075                ]
1076                .into_iter(),
1077            )
1078            .and(
1079                Reference::new("col2").is_not_in(
1080                    vec![
1081                        Datum::timestamp_micros(1514764799000000),
1082                        Datum::timestamp_micros(1483228800000000),
1083                    ]
1084                    .into_iter(),
1085                ),
1086            )
1087            .and(
1088                Reference::new("col3").is_not_in(
1089                    vec![
1090                        Datum::timestamptz_micros(1514764799000000),
1091                        Datum::timestamptz_micros(1483228800000000),
1092                    ]
1093                    .into_iter(),
1094                ),
1095            )
1096            .and(
1097                Reference::new("col4").is_not_in(
1098                    vec![
1099                        Datum::timestamp_nanos(1514764799000000000),
1100                        Datum::timestamp_nanos(1483228800000000000),
1101                    ]
1102                    .into_iter(),
1103                ),
1104            )
1105            .and(
1106                Reference::new("col5").is_not_in(
1107                    vec![
1108                        Datum::timestamptz_nanos(1514764799000000000),
1109                        Datum::timestamptz_nanos(1483228800000000000),
1110                    ]
1111                    .into_iter(),
1112                ),
1113            )
1114            .bind(schema.clone(), false)
1115            .unwrap();
1116        let result = strict_projection.strict_project(&predicate).unwrap();
1117        assert_eq!(result.to_string(), "((((pcol1 NOT IN (575, 564)) AND (pcol2 NOT IN (575, 564))) AND (pcol3 NOT IN (575, 564))) AND (pcol4 NOT IN (575, 564))) AND (pcol5 NOT IN (575, 564))".to_string());
1118
1119        // test in
1120        let predicate = Reference::new("col1")
1121            .is_in(
1122                vec![
1123                    Datum::date_from_str("2017-12-31").unwrap(),
1124                    Datum::date_from_str("2017-01-01").unwrap(),
1125                ]
1126                .into_iter(),
1127            )
1128            .and(
1129                Reference::new("col2").is_in(
1130                    vec![
1131                        Datum::timestamp_micros(1514764799000000),
1132                        Datum::timestamp_micros(1483228800000000),
1133                    ]
1134                    .into_iter(),
1135                ),
1136            )
1137            .bind(schema.clone(), false)
1138            .unwrap();
1139        let result = strict_projection.strict_project(&predicate).unwrap();
1140        assert_eq!(result.to_string(), "FALSE".to_string());
1141    }
1142
1143    #[tokio::test]
1144    async fn test_strict_projection_negative_month_upper_bound() {
1145        let schema = Arc::new(
1146            Schema::builder()
1147                .with_fields(vec![
1148                    Arc::new(NestedField::required(
1149                        1,
1150                        "col1",
1151                        Type::Primitive(PrimitiveType::Date),
1152                    )),
1153                    Arc::new(NestedField::required(
1154                        2,
1155                        "col2",
1156                        Type::Primitive(PrimitiveType::Timestamp),
1157                    )),
1158                    Arc::new(NestedField::required(
1159                        3,
1160                        "col3",
1161                        Type::Primitive(PrimitiveType::Timestamptz),
1162                    )),
1163                    Arc::new(NestedField::required(
1164                        4,
1165                        "col4",
1166                        Type::Primitive(PrimitiveType::TimestampNs),
1167                    )),
1168                    Arc::new(NestedField::required(
1169                        5,
1170                        "col5",
1171                        Type::Primitive(PrimitiveType::TimestamptzNs),
1172                    )),
1173                ])
1174                .build()
1175                .unwrap(),
1176        );
1177
1178        let partition_spec = Arc::new(
1179            PartitionSpec::builder(schema.clone())
1180                .with_spec_id(1)
1181                .add_partition_field("col1", "pcol1", Transform::Month)
1182                .unwrap()
1183                .add_partition_field("col2", "pcol2", Transform::Month)
1184                .unwrap()
1185                .add_partition_field("col3", "pcol3", Transform::Month)
1186                .unwrap()
1187                .add_partition_field("col4", "pcol4", Transform::Month)
1188                .unwrap()
1189                .add_partition_field("col5", "pcol5", Transform::Month)
1190                .unwrap()
1191                .build()
1192                .unwrap(),
1193        );
1194
1195        let mut strict_projection = StrictProjection::new(partition_spec.clone());
1196
1197        // test less: (col1 < 1969-12-31) AND (col2 < 1969-12-31) AND (col3 < 1969-12-31) AND (col4 < 1969-12-31) AND (col5 < 1969-12-31)
1198        let predicate = Reference::new("col1")
1199            .less_than(Datum::date_from_str("1969-12-31").unwrap())
1200            .and(Reference::new("col2").less_than(Datum::timestamp_micros(-86400000000)))
1201            .and(Reference::new("col3").less_than(Datum::timestamptz_micros(-86400000000)))
1202            .and(Reference::new("col4").less_than(Datum::timestamp_nanos(-86400000000000)))
1203            .and(Reference::new("col5").less_than(Datum::timestamptz_nanos(-86400000000000)))
1204            .bind(schema.clone(), false)
1205            .unwrap();
1206        let result = strict_projection.strict_project(&predicate).unwrap();
1207        assert_eq!(
1208            result.to_string(),
1209            "((((pcol1 < -1) AND (pcol2 < -1)) AND (pcol3 < -1)) AND (pcol4 < -1)) AND (pcol5 < -1)"
1210                .to_string()
1211        );
1212
1213        // test less or eq: (col1 <= 1969-12-31) AND (col2 <= 1969-12-31) AND (col3 <= 1969-12-31) AND (col4 <= 1969-12-31) AND (col5 <= 1969-12-31)
1214        let predicate = Reference::new("col1")
1215            .less_than_or_equal_to(Datum::date_from_str("1969-12-31").unwrap())
1216            .and(
1217                Reference::new("col2").less_than_or_equal_to(Datum::timestamp_micros(-86400000000)),
1218            )
1219            .and(
1220                Reference::new("col3")
1221                    .less_than_or_equal_to(Datum::timestamptz_micros(-86400000000)),
1222            )
1223            .and(
1224                Reference::new("col4")
1225                    .less_than_or_equal_to(Datum::timestamp_nanos(-86400000000000)),
1226            )
1227            .and(
1228                Reference::new("col5")
1229                    .less_than_or_equal_to(Datum::timestamptz_nanos(-86400000000000)),
1230            )
1231            .bind(schema.clone(), false)
1232            .unwrap();
1233        let result = strict_projection.strict_project(&predicate).unwrap();
1234        assert_eq!(
1235            result.to_string(),
1236            "((((pcol1 < 0) AND (pcol2 < -1)) AND (pcol3 < -1)) AND (pcol4 < -1)) AND (pcol5 < -1)"
1237                .to_string()
1238        );
1239
1240        // test greater: (col1 > 1969-12-31) AND (col2 > 1969-12-31) AND (col3 > 1969-12-31) AND (col4 > 1969-12-31) AND (col5 > 1969-12-31)
1241        let predicate = Reference::new("col1")
1242            .greater_than(Datum::date_from_str("1969-12-31").unwrap())
1243            .and(Reference::new("col2").greater_than(Datum::timestamp_micros(-86400000000)))
1244            .and(Reference::new("col3").greater_than(Datum::timestamptz_micros(-86400000000)))
1245            .and(Reference::new("col4").greater_than(Datum::timestamp_nanos(-86400000000000)))
1246            .and(Reference::new("col5").greater_than(Datum::timestamptz_nanos(-86400000000000)))
1247            .bind(schema.clone(), false)
1248            .unwrap();
1249        let result = strict_projection.strict_project(&predicate).unwrap();
1250        assert_eq!(
1251            result.to_string(),
1252            "((((pcol1 > -1) AND (pcol2 > -1)) AND (pcol3 > -1)) AND (pcol4 > -1)) AND (pcol5 > -1)"
1253                .to_string()
1254        );
1255
1256        // test greater or eq: (col1 >= 1969-12-31) AND (col2 >= 1969-12-31) AND (col3 >= 1969-12-31) AND (col4 >= 1969-12-31) AND (col5 >= 1969-12-31)
1257        let predicate = Reference::new("col1")
1258            .greater_than_or_equal_to(Datum::date_from_str("1969-12-31").unwrap())
1259            .and(
1260                Reference::new("col2")
1261                    .greater_than_or_equal_to(Datum::timestamp_micros(-86400000000)),
1262            )
1263            .and(
1264                Reference::new("col3")
1265                    .greater_than_or_equal_to(Datum::timestamptz_micros(-86400000000)),
1266            )
1267            .and(
1268                Reference::new("col4")
1269                    .greater_than_or_equal_to(Datum::timestamp_nanos(-86400000000000)),
1270            )
1271            .and(
1272                Reference::new("col5")
1273                    .greater_than_or_equal_to(Datum::timestamptz_nanos(-86400000000000)),
1274            )
1275            .bind(schema.clone(), false)
1276            .unwrap();
1277        let result = strict_projection.strict_project(&predicate).unwrap();
1278        assert_eq!(
1279            result.to_string(),
1280            "((((pcol1 > -1) AND (pcol2 > -1)) AND (pcol3 > -1)) AND (pcol4 > -1)) AND (pcol5 > -1)"
1281                .to_string()
1282        );
1283
1284        // test not eq: (col1 != 1969-12-31) AND (col2 != 1969-12-31) AND (col3 != 1969-12-31) AND (col4 != 1969-12-31) AND (col5 != 1969-12-31)
1285        let predicate = Reference::new("col1")
1286            .not_equal_to(Datum::date_from_str("1969-12-31").unwrap())
1287            .and(Reference::new("col2").not_equal_to(Datum::timestamp_micros(-86400000000)))
1288            .and(Reference::new("col3").not_equal_to(Datum::timestamptz_micros(-86400000000)))
1289            .and(Reference::new("col4").not_equal_to(Datum::timestamp_nanos(-86400000000000)))
1290            .and(Reference::new("col5").not_equal_to(Datum::timestamptz_nanos(-86400000000000)))
1291            .bind(schema.clone(), false)
1292            .unwrap();
1293        let result = strict_projection.strict_project(&predicate).unwrap();
1294        assert_eq!(result.to_string(), "((((pcol1 != -1) AND (pcol2 != -1)) AND (pcol3 != -1)) AND (pcol4 != -1)) AND (pcol5 != -1)".to_string());
1295    }
1296
1297    #[tokio::test]
1298    async fn test_strict_projection_day() {
1299        let schema = Arc::new(
1300            Schema::builder()
1301                .with_fields(vec![
1302                    Arc::new(NestedField::required(
1303                        1,
1304                        "col1",
1305                        Type::Primitive(PrimitiveType::Date),
1306                    )),
1307                    Arc::new(NestedField::required(
1308                        2,
1309                        "col2",
1310                        Type::Primitive(PrimitiveType::Timestamp),
1311                    )),
1312                    Arc::new(NestedField::required(
1313                        3,
1314                        "col3",
1315                        Type::Primitive(PrimitiveType::Timestamptz),
1316                    )),
1317                    Arc::new(NestedField::required(
1318                        4,
1319                        "col4",
1320                        Type::Primitive(PrimitiveType::TimestampNs),
1321                    )),
1322                    Arc::new(NestedField::required(
1323                        5,
1324                        "col5",
1325                        Type::Primitive(PrimitiveType::TimestamptzNs),
1326                    )),
1327                ])
1328                .build()
1329                .unwrap(),
1330        );
1331
1332        let partition_spec = Arc::new(
1333            PartitionSpec::builder(schema.clone())
1334                .with_spec_id(1)
1335                .add_partition_field("col1", "pcol1", Transform::Day)
1336                .unwrap()
1337                .add_partition_field("col2", "pcol2", Transform::Day)
1338                .unwrap()
1339                .add_partition_field("col3", "pcol3", Transform::Day)
1340                .unwrap()
1341                .add_partition_field("col4", "pcol4", Transform::Day)
1342                .unwrap()
1343                .add_partition_field("col5", "pcol5", Transform::Day)
1344                .unwrap()
1345                .build()
1346                .unwrap(),
1347        );
1348
1349        let mut strict_projection = StrictProjection::new(partition_spec.clone());
1350
1351        // test less: (col1 < 2017-01-01) AND (col2 < 2017-01-01) AND (col3 < 2017-01-01) AND (col4 < 2017-01-01) AND (col5 < 2017-01-01)
1352        let predicate = Reference::new("col1")
1353            .less_than(Datum::date_from_str("2017-01-01").unwrap())
1354            .and(Reference::new("col2").less_than(Datum::timestamp_micros(1483228800000000)))
1355            .and(Reference::new("col3").less_than(Datum::timestamptz_micros(1483228800000000)))
1356            .and(Reference::new("col4").less_than(Datum::timestamp_nanos(1483228800000000000)))
1357            .and(Reference::new("col5").less_than(Datum::timestamptz_nanos(1483228800000000000)))
1358            .bind(schema.clone(), false)
1359            .unwrap();
1360        let result = strict_projection.strict_project(&predicate).unwrap();
1361        assert_eq!(
1362            result.to_string(),
1363            "((((pcol1 < 2017-01-01) AND (pcol2 < 2017-01-01)) AND (pcol3 < 2017-01-01)) AND (pcol4 < 2017-01-01)) AND (pcol5 < 2017-01-01)"
1364                .to_string()
1365        );
1366
1367        // test less or eq: (col1 <= 2017-01-01) AND (col2 <= 2017-01-01) AND (col3 <= 2017-01-01) AND (col4 <= 2017-01-01) AND (col5 <= 2017-01-01)
1368        let predicate = Reference::new("col1")
1369            .less_than_or_equal_to(Datum::date_from_str("2017-01-01").unwrap())
1370            .and(
1371                Reference::new("col2")
1372                    .less_than_or_equal_to(Datum::timestamp_micros(1483228800000000)),
1373            )
1374            .and(
1375                Reference::new("col3")
1376                    .less_than_or_equal_to(Datum::timestamptz_micros(1483228800000000)),
1377            )
1378            .and(
1379                Reference::new("col4")
1380                    .less_than_or_equal_to(Datum::timestamp_nanos(1483228800000000000)),
1381            )
1382            .and(
1383                Reference::new("col5")
1384                    .less_than_or_equal_to(Datum::timestamptz_nanos(1483228800000000000)),
1385            )
1386            .bind(schema.clone(), false)
1387            .unwrap();
1388        let result = strict_projection.strict_project(&predicate).unwrap();
1389        assert_eq!(
1390            result.to_string(),
1391            "((((pcol1 < 2017-01-02) AND (pcol2 < 2017-01-01)) AND (pcol3 < 2017-01-01)) AND (pcol4 < 2017-01-01)) AND (pcol5 < 2017-01-01)"
1392                .to_string()
1393        );
1394
1395        // test greater: (col1 > 2017-01-01) AND (col2 > 2017-01-01) AND (col3 > 2017-01-01) AND (col4 > 2017-01-01) AND (col5 > 2017-01-01)
1396        let predicate = Reference::new("col1")
1397            .greater_than(Datum::date_from_str("2017-01-01").unwrap())
1398            .and(Reference::new("col2").greater_than(Datum::timestamp_micros(1483228800000000)))
1399            .and(Reference::new("col3").greater_than(Datum::timestamptz_micros(1483228800000000)))
1400            .and(Reference::new("col4").greater_than(Datum::timestamp_nanos(1483228800000000000)))
1401            .and(Reference::new("col5").greater_than(Datum::timestamptz_nanos(1483228800000000000)))
1402            .bind(schema.clone(), false)
1403            .unwrap();
1404        let result = strict_projection.strict_project(&predicate).unwrap();
1405        assert_eq!(
1406            result.to_string(),
1407            "((((pcol1 > 2017-01-01) AND (pcol2 > 2017-01-01)) AND (pcol3 > 2017-01-01)) AND (pcol4 > 2017-01-01)) AND (pcol5 > 2017-01-01)"
1408                .to_string()
1409        );
1410
1411        // test greater or eq: (col1 >= 2017-01-01) AND (col2 >= 2017-01-01) AND (col3 >= 2017-01-01) AND (col4 >= 2017-01-01) AND (col5 >= 2017-01-01)
1412        let predicate = Reference::new("col1")
1413            .greater_than_or_equal_to(Datum::date_from_str("2017-01-01").unwrap())
1414            .and(
1415                Reference::new("col2")
1416                    .greater_than_or_equal_to(Datum::timestamp_micros(1483228800000000)),
1417            )
1418            .and(
1419                Reference::new("col3")
1420                    .greater_than_or_equal_to(Datum::timestamptz_micros(1483228800000000)),
1421            )
1422            .and(
1423                Reference::new("col4")
1424                    .greater_than_or_equal_to(Datum::timestamp_nanos(1483228800000000000)),
1425            )
1426            .and(
1427                Reference::new("col5")
1428                    .greater_than_or_equal_to(Datum::timestamptz_nanos(1483228800000000000)),
1429            )
1430            .bind(schema.clone(), false)
1431            .unwrap();
1432        let result = strict_projection.strict_project(&predicate).unwrap();
1433        assert_eq!(
1434            result.to_string(),
1435            "((((pcol1 > 2016-12-31) AND (pcol2 > 2016-12-31)) AND (pcol3 > 2016-12-31)) AND (pcol4 > 2016-12-31)) AND (pcol5 > 2016-12-31)"
1436                .to_string()
1437        );
1438
1439        // test not eq: (col1 != 2017-01-01) AND (col2 != 2017-01-01) AND (col3 != 2017-01-01) AND (col4 != 2017-01-01) AND (col5 != 2017-01-01)
1440        let predicate = Reference::new("col1")
1441            .not_equal_to(Datum::date_from_str("2017-01-01").unwrap())
1442            .and(Reference::new("col2").not_equal_to(Datum::timestamp_micros(1483228800000000)))
1443            .and(Reference::new("col3").not_equal_to(Datum::timestamptz_micros(1483228800000000)))
1444            .and(Reference::new("col4").not_equal_to(Datum::timestamp_nanos(1483228800000000000)))
1445            .and(Reference::new("col5").not_equal_to(Datum::timestamptz_nanos(1483228800000000000)))
1446            .bind(schema.clone(), false)
1447            .unwrap();
1448        let result = strict_projection.strict_project(&predicate).unwrap();
1449        assert_eq!(result.to_string(), "((((pcol1 != 2017-01-01) AND (pcol2 != 2017-01-01)) AND (pcol3 != 2017-01-01)) AND (pcol4 != 2017-01-01)) AND (pcol5 != 2017-01-01)".to_string());
1450
1451        // test not in: (col1 NOT IN (2017-01-01, 2017-12-31)) AND (col2 NOT IN (2017-01-01, 2017-12-31)) AND (col3 NOT IN (2017-01-01, 2017-12-31)) AND (col4 NOT IN (2017-01-01, 2017-12-31)) AND (col5 NOT IN (2017-01-01, 2017-12-31))
1452        let predicate = Reference::new("col1")
1453            .is_not_in(
1454                vec![
1455                    Datum::date_from_str("2017-01-01").unwrap(),
1456                    Datum::date_from_str("2017-12-31").unwrap(),
1457                ]
1458                .into_iter(),
1459            )
1460            .and(
1461                Reference::new("col2").is_not_in(
1462                    vec![
1463                        Datum::timestamp_micros(1483228800000000),
1464                        Datum::timestamp_micros(1514764799000000),
1465                    ]
1466                    .into_iter(),
1467                ),
1468            )
1469            .and(
1470                Reference::new("col3").is_not_in(
1471                    vec![
1472                        Datum::timestamptz_micros(1483228800000000),
1473                        Datum::timestamptz_micros(1514764799000000),
1474                    ]
1475                    .into_iter(),
1476                ),
1477            )
1478            .and(
1479                Reference::new("col4").is_not_in(
1480                    vec![
1481                        Datum::timestamp_nanos(1483228800000000000),
1482                        Datum::timestamp_nanos(1514764799000000000),
1483                    ]
1484                    .into_iter(),
1485                ),
1486            )
1487            .and(
1488                Reference::new("col5").is_not_in(
1489                    vec![
1490                        Datum::timestamptz_nanos(1483228800000000000),
1491                        Datum::timestamptz_nanos(1514764799000000000),
1492                    ]
1493                    .into_iter(),
1494                ),
1495            )
1496            .bind(schema.clone(), false)
1497            .unwrap();
1498        let result = strict_projection.strict_project(&predicate).unwrap();
1499        assert_eq!(result.to_string(), "((((pcol1 NOT IN (2017-01-01, 2017-12-31)) AND (pcol2 NOT IN (2017-01-01, 2017-12-31))) AND (pcol3 NOT IN (2017-01-01, 2017-12-31))) AND (pcol4 NOT IN (2017-01-01, 2017-12-31))) AND (pcol5 NOT IN (2017-01-01, 2017-12-31))".to_string());
1500
1501        // test in
1502        let predicate = Reference::new("col1")
1503            .is_in(
1504                vec![
1505                    Datum::date_from_str("2017-01-01").unwrap(),
1506                    Datum::date_from_str("2017-12-31").unwrap(),
1507                ]
1508                .into_iter(),
1509            )
1510            .and(
1511                Reference::new("col2").is_in(
1512                    vec![
1513                        Datum::timestamp_micros(1483228800000000),
1514                        Datum::timestamp_micros(1514764799000000),
1515                    ]
1516                    .into_iter(),
1517                ),
1518            )
1519            .bind(schema.clone(), false)
1520            .unwrap();
1521        let result = strict_projection.strict_project(&predicate).unwrap();
1522        assert_eq!(result.to_string(), "FALSE".to_string());
1523    }
1524
1525    #[tokio::test]
1526    async fn test_strict_projection_negative_day() {
1527        let schema = Arc::new(
1528            Schema::builder()
1529                .with_fields(vec![
1530                    Arc::new(NestedField::required(
1531                        1,
1532                        "col1",
1533                        Type::Primitive(PrimitiveType::Date),
1534                    )),
1535                    Arc::new(NestedField::required(
1536                        2,
1537                        "col2",
1538                        Type::Primitive(PrimitiveType::Timestamp),
1539                    )),
1540                    Arc::new(NestedField::required(
1541                        3,
1542                        "col3",
1543                        Type::Primitive(PrimitiveType::Timestamptz),
1544                    )),
1545                    Arc::new(NestedField::required(
1546                        4,
1547                        "col4",
1548                        Type::Primitive(PrimitiveType::TimestampNs),
1549                    )),
1550                    Arc::new(NestedField::required(
1551                        5,
1552                        "col5",
1553                        Type::Primitive(PrimitiveType::TimestamptzNs),
1554                    )),
1555                ])
1556                .build()
1557                .unwrap(),
1558        );
1559
1560        let partition_spec = Arc::new(
1561            PartitionSpec::builder(schema.clone())
1562                .with_spec_id(1)
1563                .add_partition_field("col1", "pcol1", Transform::Day)
1564                .unwrap()
1565                .add_partition_field("col2", "pcol2", Transform::Day)
1566                .unwrap()
1567                .add_partition_field("col3", "pcol3", Transform::Day)
1568                .unwrap()
1569                .add_partition_field("col4", "pcol4", Transform::Day)
1570                .unwrap()
1571                .add_partition_field("col5", "pcol5", Transform::Day)
1572                .unwrap()
1573                .build()
1574                .unwrap(),
1575        );
1576
1577        let mut strict_projection = StrictProjection::new(partition_spec.clone());
1578
1579        // test less: (col1 < 1969-12-30) AND (col2 < 1969-12-30) AND (col3 < 1969-12-30) AND (col4 < 1969-12-30) AND (col5 < 1969-12-30)
1580        let predicate = Reference::new("col1")
1581            .less_than(Datum::date_from_str("1969-12-30").unwrap())
1582            .and(Reference::new("col2").less_than(Datum::timestamp_micros(-172800000000)))
1583            .and(Reference::new("col3").less_than(Datum::timestamptz_micros(-172800000000)))
1584            .and(Reference::new("col4").less_than(Datum::timestamp_nanos(-172800000000000)))
1585            .and(Reference::new("col5").less_than(Datum::timestamptz_nanos(-172800000000000)))
1586            .bind(schema.clone(), false)
1587            .unwrap();
1588        let result = strict_projection.strict_project(&predicate).unwrap();
1589        assert_eq!(
1590            result.to_string(),
1591            "((((pcol1 < 1969-12-30) AND (pcol2 < 1969-12-30)) AND (pcol3 < 1969-12-30)) AND (pcol4 < 1969-12-30)) AND (pcol5 < 1969-12-30)"
1592                .to_string()
1593        );
1594
1595        // test less or eq: (col1 <= 1969-12-30) AND (col2 <= 1969-12-30) AND (col3 <= 1969-12-30) AND (col4 <= 1969-12-30) AND (col5 <= 1969-12-30)
1596        let predicate = Reference::new("col1")
1597            .less_than_or_equal_to(Datum::date_from_str("1969-12-30").unwrap())
1598            .and(
1599                Reference::new("col2")
1600                    .less_than_or_equal_to(Datum::timestamp_micros(-172800000000)),
1601            )
1602            .and(
1603                Reference::new("col3")
1604                    .less_than_or_equal_to(Datum::timestamptz_micros(-172800000000)),
1605            )
1606            .and(
1607                Reference::new("col4")
1608                    .less_than_or_equal_to(Datum::timestamp_nanos(-172800000000000)),
1609            )
1610            .and(
1611                Reference::new("col5")
1612                    .less_than_or_equal_to(Datum::timestamptz_nanos(-172800000000000)),
1613            )
1614            .bind(schema.clone(), false)
1615            .unwrap();
1616        let result = strict_projection.strict_project(&predicate).unwrap();
1617        assert_eq!(
1618            result.to_string(),
1619            "((((pcol1 < 1969-12-31) AND (pcol2 < 1969-12-30)) AND (pcol3 < 1969-12-30)) AND (pcol4 < 1969-12-30)) AND (pcol5 < 1969-12-30)"
1620                .to_string()
1621        );
1622
1623        // test greater: (col1 > 1969-12-30) AND (col2 > 1969-12-30) AND (col3 > 1969-12-30) AND (col4 > 1969-12-30) AND (col5 > 1969-12-30)
1624        let predicate = Reference::new("col1")
1625            .greater_than(Datum::date_from_str("1969-12-30").unwrap())
1626            .and(Reference::new("col2").greater_than(Datum::timestamp_micros(-172800000000)))
1627            .and(Reference::new("col3").greater_than(Datum::timestamptz_micros(-172800000000)))
1628            .and(Reference::new("col4").greater_than(Datum::timestamp_nanos(-172800000000000)))
1629            .and(Reference::new("col5").greater_than(Datum::timestamptz_nanos(-172800000000000)))
1630            .bind(schema.clone(), false)
1631            .unwrap();
1632        let result = strict_projection.strict_project(&predicate).unwrap();
1633        assert_eq!(
1634            result.to_string(),
1635            "((((pcol1 > 1969-12-30) AND (pcol2 > 1969-12-30)) AND (pcol3 > 1969-12-30)) AND (pcol4 > 1969-12-30)) AND (pcol5 > 1969-12-30)"
1636                .to_string()
1637        );
1638
1639        // test greater or eq: (col1 >= 1969-12-30) AND (col2 >= 1969-12-30) AND (col3 >= 1969-12-30) AND (col4 >= 1969-12-30) AND (col5 >= 1969-12-30)
1640        let predicate = Reference::new("col1")
1641            .greater_than_or_equal_to(Datum::date_from_str("1969-12-30").unwrap())
1642            .and(
1643                Reference::new("col2")
1644                    .greater_than_or_equal_to(Datum::timestamp_micros(-172800000000)),
1645            )
1646            .and(
1647                Reference::new("col3")
1648                    .greater_than_or_equal_to(Datum::timestamptz_micros(-172800000000)),
1649            )
1650            .and(
1651                Reference::new("col4")
1652                    .greater_than_or_equal_to(Datum::timestamp_nanos(-172800000000000)),
1653            )
1654            .and(
1655                Reference::new("col5")
1656                    .greater_than_or_equal_to(Datum::timestamptz_nanos(-172800000000000)),
1657            )
1658            .bind(schema.clone(), false)
1659            .unwrap();
1660        let result = strict_projection.strict_project(&predicate).unwrap();
1661        assert_eq!(
1662            result.to_string(),
1663            "((((pcol1 > 1969-12-29) AND (pcol2 > 1969-12-29)) AND (pcol3 > 1969-12-29)) AND (pcol4 > 1969-12-29)) AND (pcol5 > 1969-12-29)"
1664                .to_string()
1665        );
1666
1667        // test not eq: (col1 != 1969-12-30) AND (col2 != 1969-12-30) AND (col3 != 1969-12-30) AND (col4 != 1969-12-30) AND (col5 != 1969-12-30)
1668        let predicate = Reference::new("col1")
1669            .not_equal_to(Datum::date_from_str("1969-12-30").unwrap())
1670            .and(Reference::new("col2").not_equal_to(Datum::timestamp_micros(-172800000000)))
1671            .and(Reference::new("col3").not_equal_to(Datum::timestamptz_micros(-172800000000)))
1672            .and(Reference::new("col4").not_equal_to(Datum::timestamp_nanos(-172800000000000)))
1673            .and(Reference::new("col5").not_equal_to(Datum::timestamptz_nanos(-172800000000000)))
1674            .bind(schema.clone(), false)
1675            .unwrap();
1676        let result = strict_projection.strict_project(&predicate).unwrap();
1677        assert_eq!(result.to_string(), "((((pcol1 != 1969-12-30) AND (pcol2 != 1969-12-30)) AND (pcol3 != 1969-12-30)) AND (pcol4 != 1969-12-30)) AND (pcol5 != 1969-12-30)".to_string());
1678
1679        // test not in: (col1 NOT IN (1969-12-30, 1969-12-31)) AND (col2 NOT IN (1969-12-30, 1969-12-31)) AND (col3 NOT IN (1969-12-30, 1969-12-31)) AND (col4 NOT IN (1969-12-30, 1969-12-31)) AND (col5 NOT IN (1969-12-30, 1969-12-31))
1680        let predicate = Reference::new("col1")
1681            .is_not_in(
1682                vec![
1683                    Datum::date_from_str("1969-12-30").unwrap(),
1684                    Datum::date_from_str("1969-12-31").unwrap(),
1685                ]
1686                .into_iter(),
1687            )
1688            .and(
1689                Reference::new("col2").is_not_in(
1690                    vec![
1691                        Datum::timestamp_micros(-172800000000),
1692                        Datum::timestamp_micros(-1),
1693                    ]
1694                    .into_iter(),
1695                ),
1696            )
1697            .and(
1698                Reference::new("col3").is_not_in(
1699                    vec![
1700                        Datum::timestamptz_micros(-172800000000),
1701                        Datum::timestamptz_micros(-1),
1702                    ]
1703                    .into_iter(),
1704                ),
1705            )
1706            .and(
1707                Reference::new("col4").is_not_in(
1708                    vec![
1709                        Datum::timestamp_nanos(-172800000000000),
1710                        Datum::timestamp_nanos(-1),
1711                    ]
1712                    .into_iter(),
1713                ),
1714            )
1715            .and(
1716                Reference::new("col5").is_not_in(
1717                    vec![
1718                        Datum::timestamptz_nanos(-172800000000000),
1719                        Datum::timestamptz_nanos(-1),
1720                    ]
1721                    .into_iter(),
1722                ),
1723            )
1724            .bind(schema.clone(), false)
1725            .unwrap();
1726        let result = strict_projection.strict_project(&predicate).unwrap();
1727        assert_eq!(result.to_string(), "((((pcol1 NOT IN (1969-12-31, 1969-12-30)) AND (pcol2 NOT IN (1969-12-31, 1969-12-30))) AND (pcol3 NOT IN (1969-12-31, 1969-12-30))) AND (pcol4 NOT IN (1969-12-31, 1969-12-30))) AND (pcol5 NOT IN (1969-12-31, 1969-12-30))".to_string());
1728
1729        // test in
1730        let predicate = Reference::new("col1")
1731            .is_in(
1732                vec![
1733                    Datum::date_from_str("1969-12-30").unwrap(),
1734                    Datum::date_from_str("1969-12-31").unwrap(),
1735                ]
1736                .into_iter(),
1737            )
1738            .and(
1739                Reference::new("col2").is_in(
1740                    vec![
1741                        Datum::timestamp_micros(-172800000000),
1742                        Datum::timestamp_micros(-1),
1743                    ]
1744                    .into_iter(),
1745                ),
1746            )
1747            .bind(schema.clone(), false)
1748            .unwrap();
1749        let result = strict_projection.strict_project(&predicate).unwrap();
1750        assert_eq!(result.to_string(), "FALSE".to_string());
1751    }
1752
1753    #[tokio::test]
1754    async fn test_strict_projection_year_lower_bound() {
1755        let schema = Arc::new(
1756            Schema::builder()
1757                .with_fields(vec![
1758                    Arc::new(NestedField::required(
1759                        1,
1760                        "col1",
1761                        Type::Primitive(PrimitiveType::Date),
1762                    )),
1763                    Arc::new(NestedField::required(
1764                        2,
1765                        "col2",
1766                        Type::Primitive(PrimitiveType::Timestamp),
1767                    )),
1768                    Arc::new(NestedField::required(
1769                        3,
1770                        "col3",
1771                        Type::Primitive(PrimitiveType::Timestamptz),
1772                    )),
1773                    Arc::new(NestedField::required(
1774                        4,
1775                        "col4",
1776                        Type::Primitive(PrimitiveType::TimestampNs),
1777                    )),
1778                    Arc::new(NestedField::required(
1779                        5,
1780                        "col5",
1781                        Type::Primitive(PrimitiveType::TimestamptzNs),
1782                    )),
1783                ])
1784                .build()
1785                .unwrap(),
1786        );
1787
1788        let partition_spec = Arc::new(
1789            PartitionSpec::builder(schema.clone())
1790                .with_spec_id(1)
1791                .add_partition_field("col1", "pcol1", Transform::Year)
1792                .unwrap()
1793                .add_partition_field("col2", "pcol2", Transform::Year)
1794                .unwrap()
1795                .add_partition_field("col3", "pcol3", Transform::Year)
1796                .unwrap()
1797                .add_partition_field("col4", "pcol4", Transform::Year)
1798                .unwrap()
1799                .add_partition_field("col5", "pcol5", Transform::Year)
1800                .unwrap()
1801                .build()
1802                .unwrap(),
1803        );
1804
1805        let mut strict_projection = StrictProjection::new(partition_spec.clone());
1806
1807        // test less: (col1 < 2017-01-01) AND (col2 < 2017-01-01) AND (col3 < 2017-01-01) AND (col4 < 2017-01-01) AND (col5 < 2017-01-01)
1808        let predicate = Reference::new("col1")
1809            .less_than(Datum::date_from_str("2017-01-01").unwrap())
1810            .and(Reference::new("col2").less_than(Datum::timestamp_micros(1483228800000000)))
1811            .and(Reference::new("col3").less_than(Datum::timestamptz_micros(1483228800000000)))
1812            .and(Reference::new("col4").less_than(Datum::timestamp_nanos(1483228800000000000)))
1813            .and(Reference::new("col5").less_than(Datum::timestamptz_nanos(1483228800000000000)))
1814            .bind(schema.clone(), false)
1815            .unwrap();
1816        let result = strict_projection.strict_project(&predicate).unwrap();
1817        assert_eq!(
1818            result.to_string(),
1819            "((((pcol1 < 47) AND (pcol2 < 47)) AND (pcol3 < 47)) AND (pcol4 < 47)) AND (pcol5 < 47)"
1820                .to_string()
1821        );
1822
1823        // test less or eq: (col1 <= 2017-01-01) AND (col2 <= 2017-01-01) AND (col3 <= 2017-01-01) AND (col4 <= 2017-01-01) AND (col5 <= 2017-01-01)
1824        let predicate = Reference::new("col1")
1825            .less_than_or_equal_to(Datum::date_from_str("2017-01-01").unwrap())
1826            .and(
1827                Reference::new("col2")
1828                    .less_than_or_equal_to(Datum::timestamp_micros(1483228800000000)),
1829            )
1830            .and(
1831                Reference::new("col3")
1832                    .less_than_or_equal_to(Datum::timestamptz_micros(1483228800000000)),
1833            )
1834            .and(
1835                Reference::new("col4")
1836                    .less_than_or_equal_to(Datum::timestamp_nanos(1483228800000000000)),
1837            )
1838            .and(
1839                Reference::new("col5")
1840                    .less_than_or_equal_to(Datum::timestamptz_nanos(1483228800000000000)),
1841            )
1842            .bind(schema.clone(), false)
1843            .unwrap();
1844        let result = strict_projection.strict_project(&predicate).unwrap();
1845        assert_eq!(
1846            result.to_string(),
1847            "((((pcol1 < 47) AND (pcol2 < 47)) AND (pcol3 < 47)) AND (pcol4 < 47)) AND (pcol5 < 47)"
1848                .to_string()
1849        );
1850
1851        // test greater: (col1 > 2017-01-01) AND (col2 > 2017-01-01) AND (col3 > 2017-01-01) AND (col4 > 2017-01-01) AND (col5 > 2017-01-01)
1852        let predicate = Reference::new("col1")
1853            .greater_than(Datum::date_from_str("2017-01-01").unwrap())
1854            .and(Reference::new("col2").greater_than(Datum::timestamp_micros(1483228800000000)))
1855            .and(Reference::new("col3").greater_than(Datum::timestamptz_micros(1483228800000000)))
1856            .and(Reference::new("col4").greater_than(Datum::timestamp_nanos(1483228800000000000)))
1857            .and(Reference::new("col5").greater_than(Datum::timestamptz_nanos(1483228800000000000)))
1858            .bind(schema.clone(), false)
1859            .unwrap();
1860        let result = strict_projection.strict_project(&predicate).unwrap();
1861        assert_eq!(
1862            result.to_string(),
1863            "((((pcol1 > 47) AND (pcol2 > 47)) AND (pcol3 > 47)) AND (pcol4 > 47)) AND (pcol5 > 47)"
1864                .to_string()
1865        );
1866
1867        // test greater or eq: (col1 >= 2017-01-01) AND (col2 >= 2017-01-01) AND (col3 >= 2017-01-01) AND (col4 >= 2017-01-01) AND (col5 >= 2017-01-01)
1868        let predicate = Reference::new("col1")
1869            .greater_than_or_equal_to(Datum::date_from_str("2017-01-01").unwrap())
1870            .and(
1871                Reference::new("col2")
1872                    .greater_than_or_equal_to(Datum::timestamp_micros(1483228800000000)),
1873            )
1874            .and(
1875                Reference::new("col3")
1876                    .greater_than_or_equal_to(Datum::timestamptz_micros(1483228800000000)),
1877            )
1878            .and(
1879                Reference::new("col4")
1880                    .greater_than_or_equal_to(Datum::timestamp_nanos(1483228800000000000)),
1881            )
1882            .and(
1883                Reference::new("col5")
1884                    .greater_than_or_equal_to(Datum::timestamptz_nanos(1483228800000000000)),
1885            )
1886            .bind(schema.clone(), false)
1887            .unwrap();
1888        let result = strict_projection.strict_project(&predicate).unwrap();
1889        assert_eq!(
1890            result.to_string(),
1891            "((((pcol1 > 46) AND (pcol2 > 46)) AND (pcol3 > 46)) AND (pcol4 > 46)) AND (pcol5 > 46)"
1892                .to_string()
1893        );
1894
1895        // test not eq: (col1 != 2017-01-01) AND (col2 != 2017-01-01) AND (col3 != 2017-01-01) AND (col4 != 2017-01-01) AND (col5 != 2017-01-01)
1896        let predicate = Reference::new("col1")
1897            .not_equal_to(Datum::date_from_str("2017-01-01").unwrap())
1898            .and(Reference::new("col2").not_equal_to(Datum::timestamp_micros(1483228800000000)))
1899            .and(Reference::new("col3").not_equal_to(Datum::timestamptz_micros(1483228800000000)))
1900            .and(Reference::new("col4").not_equal_to(Datum::timestamp_nanos(1483228800000000000)))
1901            .and(Reference::new("col5").not_equal_to(Datum::timestamptz_nanos(1483228800000000000)))
1902            .bind(schema.clone(), false)
1903            .unwrap();
1904        let result = strict_projection.strict_project(&predicate).unwrap();
1905        assert_eq!(result.to_string(), "((((pcol1 != 47) AND (pcol2 != 47)) AND (pcol3 != 47)) AND (pcol4 != 47)) AND (pcol5 != 47)".to_string());
1906
1907        // test not in: (col1 NOT IN (2017-01-01, 2017-12-31)) AND (col2 NOT IN (2017-01-01, 2017-12-31)) AND (col3 NOT IN (2017-01-01, 2017-12-31)) AND (col4 NOT IN (2017-01-01, 2017-12-31)) AND (col5 NOT IN (2017-01-01, 2017-12-31))
1908        let predicate = Reference::new("col1")
1909            .is_not_in(
1910                vec![
1911                    Datum::date_from_str("2017-01-01").unwrap(),
1912                    Datum::date_from_str("2016-12-31").unwrap(),
1913                ]
1914                .into_iter(),
1915            )
1916            .and(
1917                Reference::new("col2").is_not_in(
1918                    vec![
1919                        Datum::timestamp_micros(1483228800000000),
1920                        Datum::timestamp_micros(1483142400000000),
1921                    ]
1922                    .into_iter(),
1923                ),
1924            )
1925            .and(
1926                Reference::new("col3").is_not_in(
1927                    vec![
1928                        Datum::timestamptz_micros(1483228800000000),
1929                        Datum::timestamptz_micros(1483142400000000),
1930                    ]
1931                    .into_iter(),
1932                ),
1933            )
1934            .and(
1935                Reference::new("col4").is_not_in(
1936                    vec![
1937                        Datum::timestamp_nanos(1483228800000000000),
1938                        Datum::timestamp_nanos(1483142400000000000),
1939                    ]
1940                    .into_iter(),
1941                ),
1942            )
1943            .and(
1944                Reference::new("col5").is_not_in(
1945                    vec![
1946                        Datum::timestamptz_nanos(1483228800000000000),
1947                        Datum::timestamptz_nanos(1483142400000000000),
1948                    ]
1949                    .into_iter(),
1950                ),
1951            )
1952            .bind(schema.clone(), false)
1953            .unwrap();
1954        let result = strict_projection.strict_project(&predicate).unwrap();
1955        assert_eq!(result.to_string(), "((((pcol1 NOT IN (47, 46)) AND (pcol2 NOT IN (47, 46))) AND (pcol3 NOT IN (47, 46))) AND (pcol4 NOT IN (47, 46))) AND (pcol5 NOT IN (47, 46))".to_string());
1956
1957        // test in
1958        let predicate = Reference::new("col1")
1959            .is_in(
1960                vec![
1961                    Datum::date_from_str("2017-01-01").unwrap(),
1962                    Datum::date_from_str("2017-12-31").unwrap(),
1963                ]
1964                .into_iter(),
1965            )
1966            .and(
1967                Reference::new("col2").is_in(
1968                    vec![
1969                        Datum::timestamp_micros(1483228800000000),
1970                        Datum::timestamp_micros(1514764799000000),
1971                    ]
1972                    .into_iter(),
1973                ),
1974            )
1975            .bind(schema.clone(), false)
1976            .unwrap();
1977        let result = strict_projection.strict_project(&predicate).unwrap();
1978        assert_eq!(result.to_string(), "FALSE".to_string());
1979    }
1980
1981    #[tokio::test]
1982    async fn test_strict_projection_year_negative_lower_bound() {
1983        let schema = Arc::new(
1984            Schema::builder()
1985                .with_fields(vec![
1986                    Arc::new(NestedField::required(
1987                        1,
1988                        "col1",
1989                        Type::Primitive(PrimitiveType::Date),
1990                    )),
1991                    Arc::new(NestedField::required(
1992                        2,
1993                        "col2",
1994                        Type::Primitive(PrimitiveType::Timestamp),
1995                    )),
1996                    Arc::new(NestedField::required(
1997                        3,
1998                        "col3",
1999                        Type::Primitive(PrimitiveType::Timestamptz),
2000                    )),
2001                    Arc::new(NestedField::required(
2002                        4,
2003                        "col4",
2004                        Type::Primitive(PrimitiveType::TimestampNs),
2005                    )),
2006                    Arc::new(NestedField::required(
2007                        5,
2008                        "col5",
2009                        Type::Primitive(PrimitiveType::TimestamptzNs),
2010                    )),
2011                ])
2012                .build()
2013                .unwrap(),
2014        );
2015
2016        let partition_spec = Arc::new(
2017            PartitionSpec::builder(schema.clone())
2018                .with_spec_id(1)
2019                .add_partition_field("col1", "pcol1", Transform::Year)
2020                .unwrap()
2021                .add_partition_field("col2", "pcol2", Transform::Year)
2022                .unwrap()
2023                .add_partition_field("col3", "pcol3", Transform::Year)
2024                .unwrap()
2025                .add_partition_field("col4", "pcol4", Transform::Year)
2026                .unwrap()
2027                .add_partition_field("col5", "pcol5", Transform::Year)
2028                .unwrap()
2029                .build()
2030                .unwrap(),
2031        );
2032
2033        let mut strict_projection = StrictProjection::new(partition_spec.clone());
2034
2035        // test less: (col1 < 1970-01-01) AND (col2 < 1970-01-01) AND (col3 < 1970-01-01) AND (col4 < 1970-01-01) AND (col5 < 1970-01-01)
2036        let predicate = Reference::new("col1")
2037            .less_than(Datum::date_from_str("1970-01-01").unwrap())
2038            .and(Reference::new("col2").less_than(Datum::timestamp_micros(0)))
2039            .and(Reference::new("col3").less_than(Datum::timestamptz_micros(0)))
2040            .and(Reference::new("col4").less_than(Datum::timestamp_nanos(0)))
2041            .and(Reference::new("col5").less_than(Datum::timestamptz_nanos(0)))
2042            .bind(schema.clone(), false)
2043            .unwrap();
2044        let result = strict_projection.strict_project(&predicate).unwrap();
2045        assert_eq!(
2046            result.to_string(),
2047            "((((pcol1 < 0) AND (pcol2 < 0)) AND (pcol3 < 0)) AND (pcol4 < 0)) AND (pcol5 < 0)"
2048                .to_string()
2049        );
2050
2051        // test less or eq: (col1 <= 1970-01-01) AND (col2 <= 1970-01-01) AND (col3 <= 1970-01-01) AND (col4 <= 1970-01-01) AND (col5 <= 1970-01-01)
2052        let predicate = Reference::new("col1")
2053            .less_than_or_equal_to(Datum::date_from_str("1970-01-01").unwrap())
2054            .and(Reference::new("col2").less_than_or_equal_to(Datum::timestamp_micros(0)))
2055            .and(Reference::new("col3").less_than_or_equal_to(Datum::timestamptz_micros(0)))
2056            .and(Reference::new("col4").less_than_or_equal_to(Datum::timestamp_nanos(0)))
2057            .and(Reference::new("col5").less_than_or_equal_to(Datum::timestamptz_nanos(0)))
2058            .bind(schema.clone(), false)
2059            .unwrap();
2060        let result = strict_projection.strict_project(&predicate).unwrap();
2061        assert_eq!(
2062            result.to_string(),
2063            "((((pcol1 < 0) AND (pcol2 < 0)) AND (pcol3 < 0)) AND (pcol4 < 0)) AND (pcol5 < 0)"
2064                .to_string()
2065        );
2066
2067        // test greater: (col1 > 1970-01-01) AND (col2 > 1970-01-01) AND (col3 > 1970-01-01) AND (col4 > 1970-01-01) AND (col5 > 1970-01-01)
2068        let predicate = Reference::new("col1")
2069            .greater_than(Datum::date_from_str("1970-01-01").unwrap())
2070            .and(Reference::new("col2").greater_than(Datum::timestamp_micros(0)))
2071            .and(Reference::new("col3").greater_than(Datum::timestamptz_micros(0)))
2072            .and(Reference::new("col4").greater_than(Datum::timestamp_nanos(0)))
2073            .and(Reference::new("col5").greater_than(Datum::timestamptz_nanos(0)))
2074            .bind(schema.clone(), false)
2075            .unwrap();
2076        let result = strict_projection.strict_project(&predicate).unwrap();
2077        assert_eq!(
2078            result.to_string(),
2079            "((((pcol1 > 0) AND (pcol2 > 0)) AND (pcol3 > 0)) AND (pcol4 > 0)) AND (pcol5 > 0)"
2080                .to_string()
2081        );
2082
2083        // test greater or eq: (col1 >= 1970-01-01) AND (col2 >= 1970-01-01) AND (col3 >= 1970-01-01) AND (col4 >= 1970-01-01) AND (col5 >= 1970-01-01)
2084        let predicate = Reference::new("col1")
2085            .greater_than_or_equal_to(Datum::date_from_str("1970-01-01").unwrap())
2086            .and(Reference::new("col2").greater_than_or_equal_to(Datum::timestamp_micros(0)))
2087            .and(Reference::new("col3").greater_than_or_equal_to(Datum::timestamptz_micros(0)))
2088            .and(Reference::new("col4").greater_than_or_equal_to(Datum::timestamp_nanos(0)))
2089            .and(Reference::new("col5").greater_than_or_equal_to(Datum::timestamptz_nanos(0)))
2090            .bind(schema.clone(), false)
2091            .unwrap();
2092        let result = strict_projection.strict_project(&predicate).unwrap();
2093        assert_eq!(
2094            result.to_string(),
2095            "((((pcol1 > -1) AND (pcol2 > -1)) AND (pcol3 > -1)) AND (pcol4 > -1)) AND (pcol5 > -1)"
2096                .to_string()
2097        );
2098
2099        // test not eq: (col1 != 1970-01-01) AND (col2 != 1970-01-01) AND (col3 != 1970-01-01) AND (col4 != 1970-01-01) AND (col5 != 1970-01-01)
2100        let predicate = Reference::new("col1")
2101            .not_equal_to(Datum::date_from_str("1970-01-01").unwrap())
2102            .and(Reference::new("col2").not_equal_to(Datum::timestamp_micros(0)))
2103            .and(Reference::new("col3").not_equal_to(Datum::timestamptz_micros(0)))
2104            .and(Reference::new("col4").not_equal_to(Datum::timestamp_nanos(0)))
2105            .and(Reference::new("col5").not_equal_to(Datum::timestamptz_nanos(0)))
2106            .bind(schema.clone(), false)
2107            .unwrap();
2108        let result = strict_projection.strict_project(&predicate).unwrap();
2109        assert_eq!(result.to_string(), "((((pcol1 != 0) AND (pcol2 != 0)) AND (pcol3 != 0)) AND (pcol4 != 0)) AND (pcol5 != 0)".to_string());
2110
2111        // test not in: (col1 NOT IN (1970-01-01, 1970-12-31)) AND (col2 NOT IN (1970-01-01, 1970-12-31)) AND (col3 NOT IN (1970-01-01, 1970-12-31)) AND (col4 NOT IN (1970-01-01, 1970-12-31)) AND (col5 NOT IN (1970-01-01, 1970-12-31))
2112        let predicate = Reference::new("col1")
2113            .is_not_in(
2114                vec![
2115                    Datum::date_from_str("1970-01-01").unwrap(),
2116                    Datum::date_from_str("1970-12-31").unwrap(),
2117                ]
2118                .into_iter(),
2119            )
2120            .and(
2121                Reference::new("col2").is_not_in(
2122                    vec![
2123                        Datum::timestamp_micros(0),
2124                        Datum::timestamp_micros(3153599999000000),
2125                    ]
2126                    .into_iter(),
2127                ),
2128            )
2129            .and(
2130                Reference::new("col3").is_not_in(
2131                    vec![
2132                        Datum::timestamptz_micros(0),
2133                        Datum::timestamptz_micros(3153599999000000),
2134                    ]
2135                    .into_iter(),
2136                ),
2137            )
2138            .and(
2139                Reference::new("col4").is_not_in(
2140                    vec![
2141                        Datum::timestamp_nanos(0),
2142                        Datum::timestamp_nanos(3153599999000000000),
2143                    ]
2144                    .into_iter(),
2145                ),
2146            )
2147            .and(
2148                Reference::new("col5").is_not_in(
2149                    vec![
2150                        Datum::timestamptz_nanos(0),
2151                        Datum::timestamptz_nanos(3153599999000000000),
2152                    ]
2153                    .into_iter(),
2154                ),
2155            )
2156            .bind(schema.clone(), false)
2157            .unwrap();
2158        let result = strict_projection.strict_project(&predicate).unwrap();
2159        assert_eq!(result.to_string(), "((((pcol1 NOT IN (0)) AND (pcol2 NOT IN (0, 99))) AND (pcol3 NOT IN (0, 99))) AND (pcol4 NOT IN (0, 99))) AND (pcol5 NOT IN (0, 99))".to_string());
2160
2161        // test in
2162        let predicate = Reference::new("col1")
2163            .is_in(
2164                vec![
2165                    Datum::date_from_str("1970-01-01").unwrap(),
2166                    Datum::date_from_str("1970-12-31").unwrap(),
2167                ]
2168                .into_iter(),
2169            )
2170            .and(
2171                Reference::new("col2").is_in(
2172                    vec![
2173                        Datum::timestamp_micros(0),
2174                        Datum::timestamp_micros(3153599999000000),
2175                    ]
2176                    .into_iter(),
2177                ),
2178            )
2179            .bind(schema.clone(), false)
2180            .unwrap();
2181        let result = strict_projection.strict_project(&predicate).unwrap();
2182        assert_eq!(result.to_string(), "FALSE".to_string());
2183    }
2184
2185    #[tokio::test]
2186    async fn test_strict_projection_year_upper_bound() {
2187        let schema = Arc::new(
2188            Schema::builder()
2189                .with_fields(vec![
2190                    Arc::new(NestedField::required(
2191                        1,
2192                        "col1",
2193                        Type::Primitive(PrimitiveType::Date),
2194                    )),
2195                    Arc::new(NestedField::required(
2196                        2,
2197                        "col2",
2198                        Type::Primitive(PrimitiveType::Timestamp),
2199                    )),
2200                    Arc::new(NestedField::required(
2201                        3,
2202                        "col3",
2203                        Type::Primitive(PrimitiveType::Timestamptz),
2204                    )),
2205                    Arc::new(NestedField::required(
2206                        4,
2207                        "col4",
2208                        Type::Primitive(PrimitiveType::TimestampNs),
2209                    )),
2210                    Arc::new(NestedField::required(
2211                        5,
2212                        "col5",
2213                        Type::Primitive(PrimitiveType::TimestamptzNs),
2214                    )),
2215                ])
2216                .build()
2217                .unwrap(),
2218        );
2219
2220        let partition_spec = Arc::new(
2221            PartitionSpec::builder(schema.clone())
2222                .with_spec_id(1)
2223                .add_partition_field("col1", "pcol1", Transform::Year)
2224                .unwrap()
2225                .add_partition_field("col2", "pcol2", Transform::Year)
2226                .unwrap()
2227                .add_partition_field("col3", "pcol3", Transform::Year)
2228                .unwrap()
2229                .add_partition_field("col4", "pcol4", Transform::Year)
2230                .unwrap()
2231                .add_partition_field("col5", "pcol5", Transform::Year)
2232                .unwrap()
2233                .build()
2234                .unwrap(),
2235        );
2236
2237        let mut strict_projection = StrictProjection::new(partition_spec.clone());
2238
2239        // test less: (col1 < 2017-12-31) AND (col2 < 2017-12-31) AND (col3 < 2017-12-31) AND (col4 < 2017-12-31) AND (col5 < 2017-12-31)
2240        let predicate = Reference::new("col1")
2241            .less_than(Datum::date_from_str("2017-12-31").unwrap())
2242            .and(Reference::new("col2").less_than(Datum::timestamp_micros(1514764799000000)))
2243            .and(Reference::new("col3").less_than(Datum::timestamptz_micros(1514764799000000)))
2244            .and(Reference::new("col4").less_than(Datum::timestamp_nanos(1514764799000000000)))
2245            .and(Reference::new("col5").less_than(Datum::timestamptz_nanos(1514764799000000000)))
2246            .bind(schema.clone(), false)
2247            .unwrap();
2248        let result = strict_projection.strict_project(&predicate).unwrap();
2249        assert_eq!(
2250            result.to_string(),
2251            "((((pcol1 < 47) AND (pcol2 < 47)) AND (pcol3 < 47)) AND (pcol4 < 47)) AND (pcol5 < 47)"
2252                .to_string()
2253        );
2254
2255        // test less or eq: (col1 <= 2017-12-31) AND (col2 <= 2017-12-31) AND (col3 <= 2017-12-31) AND (col4 <= 2017-12-31) AND (col5 <= 2017-12-31)
2256        let predicate = Reference::new("col1")
2257            .less_than_or_equal_to(Datum::date_from_str("2017-12-31").unwrap())
2258            .and(
2259                Reference::new("col2")
2260                    .less_than_or_equal_to(Datum::timestamp_micros(1514764799000000)),
2261            )
2262            .and(
2263                Reference::new("col3")
2264                    .less_than_or_equal_to(Datum::timestamptz_micros(1514764799000000)),
2265            )
2266            .and(
2267                Reference::new("col4")
2268                    .less_than_or_equal_to(Datum::timestamp_nanos(1514764799000000000)),
2269            )
2270            .and(
2271                Reference::new("col5")
2272                    .less_than_or_equal_to(Datum::timestamptz_nanos(1514764799000000000)),
2273            )
2274            .bind(schema.clone(), false)
2275            .unwrap();
2276        let result = strict_projection.strict_project(&predicate).unwrap();
2277        assert_eq!(
2278            result.to_string(),
2279            "((((pcol1 < 48) AND (pcol2 < 47)) AND (pcol3 < 47)) AND (pcol4 < 47)) AND (pcol5 < 47)"
2280                .to_string()
2281        );
2282
2283        // test greater: (col1 > 2017-12-31) AND (col2 > 2017-12-31) AND (col3 > 2017-12-31) AND (col4 > 2017-12-31) AND (col5 > 2017-12-31)
2284        let predicate = Reference::new("col1")
2285            .greater_than(Datum::date_from_str("2017-12-31").unwrap())
2286            .and(Reference::new("col2").greater_than(Datum::timestamp_micros(1514764799000000)))
2287            .and(Reference::new("col3").greater_than(Datum::timestamptz_micros(1514764799000000)))
2288            .and(Reference::new("col4").greater_than(Datum::timestamp_nanos(1514764799000000000)))
2289            .and(Reference::new("col5").greater_than(Datum::timestamptz_nanos(1514764799000000000)))
2290            .bind(schema.clone(), false)
2291            .unwrap();
2292        let result = strict_projection.strict_project(&predicate).unwrap();
2293        assert_eq!(
2294            result.to_string(),
2295            "((((pcol1 > 47) AND (pcol2 > 47)) AND (pcol3 > 47)) AND (pcol4 > 47)) AND (pcol5 > 47)"
2296                .to_string()
2297        );
2298
2299        // test greater or eq: (col1 >= 2017-12-31) AND (col2 >= 2017-12-31) AND (col3 >= 2017-12-31) AND (col4 >= 2017-12-31) AND (col5 >= 2017-12-31)
2300        let predicate = Reference::new("col1")
2301            .greater_than_or_equal_to(Datum::date_from_str("2017-12-31").unwrap())
2302            .and(
2303                Reference::new("col2")
2304                    .greater_than_or_equal_to(Datum::timestamp_micros(1514764799000000)),
2305            )
2306            .and(
2307                Reference::new("col3")
2308                    .greater_than_or_equal_to(Datum::timestamptz_micros(1514764799000000)),
2309            )
2310            .and(
2311                Reference::new("col4")
2312                    .greater_than_or_equal_to(Datum::timestamp_nanos(1514764799000000000)),
2313            )
2314            .and(
2315                Reference::new("col5")
2316                    .greater_than_or_equal_to(Datum::timestamptz_nanos(1514764799000000000)),
2317            )
2318            .bind(schema.clone(), false)
2319            .unwrap();
2320        let result = strict_projection.strict_project(&predicate).unwrap();
2321        assert_eq!(
2322            result.to_string(),
2323            "((((pcol1 > 47) AND (pcol2 > 47)) AND (pcol3 > 47)) AND (pcol4 > 47)) AND (pcol5 > 47)"
2324                .to_string()
2325        );
2326
2327        // test not eq: (col1 != 2017-12-31) AND (col2 != 2017-12-31) AND (col3 != 2017-12-31) AND (col4 != 2017-12-31) AND (col5 != 2017-12-31)
2328        let predicate = Reference::new("col1")
2329            .not_equal_to(Datum::date_from_str("2017-12-31").unwrap())
2330            .and(Reference::new("col2").not_equal_to(Datum::timestamp_micros(1514764799000000)))
2331            .and(Reference::new("col3").not_equal_to(Datum::timestamptz_micros(1514764799000000)))
2332            .and(Reference::new("col4").not_equal_to(Datum::timestamp_nanos(1514764799000000000)))
2333            .and(Reference::new("col5").not_equal_to(Datum::timestamptz_nanos(1514764799000000000)))
2334            .bind(schema.clone(), false)
2335            .unwrap();
2336        let result = strict_projection.strict_project(&predicate).unwrap();
2337        assert_eq!(result.to_string(), "((((pcol1 != 47) AND (pcol2 != 47)) AND (pcol3 != 47)) AND (pcol4 != 47)) AND (pcol5 != 47)".to_string());
2338
2339        // test not in: (col1 NOT IN (2017-12-31, 2016-01-01)) AND (col2 NOT IN (2017-12-31, 2016-01-01)) AND (col3 NOT IN (2017-12-31, 2016-01-01)) AND (col4 NOT IN (2017-12-31, 2016-01-01)) AND (col5 NOT IN (2017-12-31, 2016-01-01))
2340        let predicate = Reference::new("col1")
2341            .is_not_in(
2342                vec![
2343                    Datum::date_from_str("2017-12-31").unwrap(),
2344                    Datum::date_from_str("2016-01-01").unwrap(),
2345                ]
2346                .into_iter(),
2347            )
2348            .and(
2349                Reference::new("col2").is_not_in(
2350                    vec![
2351                        Datum::timestamp_micros(1514764799000000),
2352                        Datum::timestamp_micros(1451606400000000),
2353                    ]
2354                    .into_iter(),
2355                ),
2356            )
2357            .and(
2358                Reference::new("col3").is_not_in(
2359                    vec![
2360                        Datum::timestamptz_micros(1514764799000000),
2361                        Datum::timestamptz_micros(1451606400000000),
2362                    ]
2363                    .into_iter(),
2364                ),
2365            )
2366            .and(
2367                Reference::new("col4").is_not_in(
2368                    vec![
2369                        Datum::timestamp_nanos(1514764799000000000),
2370                        Datum::timestamp_nanos(1451606400000000000),
2371                    ]
2372                    .into_iter(),
2373                ),
2374            )
2375            .and(
2376                Reference::new("col5").is_not_in(
2377                    vec![
2378                        Datum::timestamptz_nanos(1514764799000000000),
2379                        Datum::timestamptz_nanos(1451606400000000000),
2380                    ]
2381                    .into_iter(),
2382                ),
2383            )
2384            .bind(schema.clone(), false)
2385            .unwrap();
2386        let result = strict_projection.strict_project(&predicate).unwrap();
2387        assert_eq!(result.to_string(), "((((pcol1 NOT IN (47, 46)) AND (pcol2 NOT IN (47, 46))) AND (pcol3 NOT IN (47, 46))) AND (pcol4 NOT IN (47, 46))) AND (pcol5 NOT IN (47, 46))".to_string());
2388
2389        // test in
2390        let predicate = Reference::new("col1")
2391            .is_in(
2392                vec![
2393                    Datum::date_from_str("2017-12-31").unwrap(),
2394                    Datum::date_from_str("2017-01-01").unwrap(),
2395                ]
2396                .into_iter(),
2397            )
2398            .and(
2399                Reference::new("col2").is_in(
2400                    vec![
2401                        Datum::timestamp_micros(1514764799000000),
2402                        Datum::timestamp_micros(1483228800000000),
2403                    ]
2404                    .into_iter(),
2405                ),
2406            )
2407            .bind(schema.clone(), false)
2408            .unwrap();
2409        let result = strict_projection.strict_project(&predicate).unwrap();
2410        assert_eq!(result.to_string(), "FALSE".to_string());
2411    }
2412
2413    #[tokio::test]
2414    async fn test_strict_projection_bucket() {
2415        // int, long, decimal, string, bytes, uuid
2416        let schema = Arc::new(
2417            Schema::builder()
2418                .with_fields(vec![
2419                    Arc::new(NestedField::required(
2420                        1,
2421                        "col1",
2422                        Type::Primitive(PrimitiveType::Int),
2423                    )),
2424                    Arc::new(NestedField::required(
2425                        2,
2426                        "col2",
2427                        Type::Primitive(PrimitiveType::Long),
2428                    )),
2429                    Arc::new(NestedField::required(
2430                        3,
2431                        "col3",
2432                        Type::Primitive(PrimitiveType::Decimal {
2433                            precision: 9,
2434                            scale: 2,
2435                        }),
2436                    )),
2437                    Arc::new(NestedField::required(
2438                        4,
2439                        "col4",
2440                        Type::Primitive(PrimitiveType::String),
2441                    )),
2442                    Arc::new(NestedField::required(
2443                        5,
2444                        "col5",
2445                        Type::Primitive(PrimitiveType::Binary),
2446                    )),
2447                    Arc::new(NestedField::required(
2448                        6,
2449                        "col6",
2450                        Type::Primitive(PrimitiveType::Uuid),
2451                    )),
2452                ])
2453                .build()
2454                .unwrap(),
2455        );
2456
2457        let partition_spec = Arc::new(
2458            PartitionSpec::builder(schema.clone())
2459                .with_spec_id(1)
2460                .add_partition_field("col1", "pcol1", Transform::Bucket(10))
2461                .unwrap()
2462                .add_partition_field("col2", "pcol2", Transform::Bucket(10))
2463                .unwrap()
2464                .add_partition_field("col3", "pcol3", Transform::Bucket(10))
2465                .unwrap()
2466                .add_partition_field("col4", "pcol4", Transform::Bucket(10))
2467                .unwrap()
2468                .add_partition_field("col5", "pcol5", Transform::Bucket(10))
2469                .unwrap()
2470                .add_partition_field("col6", "pcol6", Transform::Bucket(10))
2471                .unwrap()
2472                .build()
2473                .unwrap(),
2474        );
2475
2476        let mut strict_projection = StrictProjection::new(partition_spec.clone());
2477
2478        // test not eq
2479        let predicate = Reference::new("col1")
2480            .not_equal_to(Datum::int(100))
2481            .and(Reference::new("col2").not_equal_to(Datum::long(100)))
2482            .and(Reference::new("col3").not_equal_to(Datum::new(
2483                PrimitiveType::Decimal {
2484                    precision: 9,
2485                    scale: 2,
2486                },
2487                PrimitiveLiteral::Int128(10000),
2488            )))
2489            .and(Reference::new("col4").not_equal_to(Datum::string("abcdefg")))
2490            .and(Reference::new("col5").not_equal_to(Datum::binary("abcdefg".as_bytes().to_vec())))
2491            .and(Reference::new("col6").not_equal_to(Datum::uuid(
2492                Uuid::parse_str("00000000-0000-007b-0000-0000000001c8").unwrap(),
2493            )))
2494            .bind(schema.clone(), false)
2495            .unwrap();
2496        let result = strict_projection.strict_project(&predicate).unwrap();
2497        assert_eq!(result.to_string(), "(((((pcol1 != 6) AND (pcol2 != 6)) AND (pcol3 != 2)) AND (pcol4 != 4)) AND (pcol5 != 4)) AND (pcol6 != 4)".to_string());
2498
2499        // test eq, less, less or eq, greater, greater or eq, in
2500        let predicate = Reference::new("col1")
2501            .equal_to(Datum::int(100))
2502            .and(Reference::new("col2").less_than(Datum::long(100)))
2503            .and(Reference::new("col3").less_than_or_equal_to(Datum::new(
2504                PrimitiveType::Decimal {
2505                    precision: 9,
2506                    scale: 2,
2507                },
2508                PrimitiveLiteral::Int128(10000),
2509            )))
2510            .and(Reference::new("col4").greater_than(Datum::string("abcdefg")))
2511            .and(
2512                Reference::new("col5")
2513                    .greater_than_or_equal_to(Datum::binary("abcdefg".as_bytes().to_vec())),
2514            )
2515            .and(
2516                Reference::new("col6").is_in(
2517                    vec![
2518                        Datum::uuid(
2519                            Uuid::parse_str("00000000-0000-007b-0000-0000000001c8").unwrap(),
2520                        ),
2521                        Datum::uuid(
2522                            Uuid::parse_str("00000000-0000-007b-0000-0000000001c9").unwrap(),
2523                        ),
2524                    ]
2525                    .into_iter(),
2526                ),
2527            )
2528            .bind(schema.clone(), false)
2529            .unwrap();
2530        let result = strict_projection.strict_project(&predicate).unwrap();
2531        assert_eq!(result.to_string(), "FALSE".to_string());
2532
2533        // test not in
2534        let predicate =
2535            Reference::new("col1")
2536                .is_not_in(vec![Datum::int(99), Datum::int(100), Datum::int(101)].into_iter())
2537                .and(Reference::new("col2").is_not_in(
2538                    vec![Datum::long(99), Datum::long(100), Datum::long(101)].into_iter(),
2539                ))
2540                .and(
2541                    Reference::new("col3").is_not_in(
2542                        vec![
2543                            Datum::new(
2544                                PrimitiveType::Decimal {
2545                                    precision: 9,
2546                                    scale: 2,
2547                                },
2548                                PrimitiveLiteral::Int128(9900),
2549                            ),
2550                            Datum::new(
2551                                PrimitiveType::Decimal {
2552                                    precision: 9,
2553                                    scale: 2,
2554                                },
2555                                PrimitiveLiteral::Int128(10000),
2556                            ),
2557                            Datum::new(
2558                                PrimitiveType::Decimal {
2559                                    precision: 9,
2560                                    scale: 2,
2561                                },
2562                                PrimitiveLiteral::Int128(10100),
2563                            ),
2564                        ]
2565                        .into_iter(),
2566                    ),
2567                )
2568                .and(Reference::new("col4").is_not_in(
2569                    vec![Datum::string("abcdefg"), Datum::string("abcdefgabc")].into_iter(),
2570                ))
2571                .and(
2572                    Reference::new("col5").is_not_in(
2573                        vec![
2574                            Datum::binary("abcdefg".as_bytes().to_vec()),
2575                            Datum::binary("abcdehij".as_bytes().to_vec()),
2576                        ]
2577                        .into_iter(),
2578                    ),
2579                )
2580                .and(
2581                    Reference::new("col6").is_not_in(
2582                        vec![
2583                            Datum::uuid(
2584                                Uuid::parse_str("00000000-0000-007b-0000-0000000001c8").unwrap(),
2585                            ),
2586                            Datum::uuid(
2587                                Uuid::parse_str("00000000-0000-01c8-0000-00000000007b").unwrap(),
2588                            ),
2589                        ]
2590                        .into_iter(),
2591                    ),
2592                )
2593                .bind(schema.clone(), false)
2594                .unwrap();
2595        let result = strict_projection.strict_project(&predicate).unwrap();
2596        assert_eq!(result.to_string(), "(((((pcol1 NOT IN (8, 7, 6)) AND (pcol2 NOT IN (8, 7, 6))) AND (pcol3 NOT IN (6, 2))) AND (pcol4 NOT IN (9, 4))) AND (pcol5 NOT IN (4, 6))) AND (pcol6 NOT IN (4, 6))".to_string());
2597    }
2598
2599    #[tokio::test]
2600    async fn test_strict_projection_identity() {
2601        let schema = Arc::new(
2602            Schema::builder()
2603                .with_fields(vec![Arc::new(NestedField::optional(
2604                    1,
2605                    "col1",
2606                    Type::Primitive(PrimitiveType::Long),
2607                ))])
2608                .build()
2609                .unwrap(),
2610        );
2611
2612        let partition_spec = Arc::new(
2613            PartitionSpec::builder(schema.clone())
2614                .with_spec_id(1)
2615                .add_partition_field("col1", "pcol1", Transform::Identity)
2616                .unwrap()
2617                .build()
2618                .unwrap(),
2619        );
2620
2621        let mut strict_projection = StrictProjection::new(partition_spec.clone());
2622
2623        // test not null
2624        let predicate = Reference::new("col1")
2625            .is_not_null()
2626            .bind(schema.clone(), false)
2627            .unwrap();
2628        let result = strict_projection.strict_project(&predicate).unwrap();
2629        assert_eq!(result.to_string(), "pcol1 IS NOT NULL".to_string());
2630
2631        // test is null
2632        let predicate = Reference::new("col1")
2633            .is_null()
2634            .bind(schema.clone(), false)
2635            .unwrap();
2636        let result = strict_projection.strict_project(&predicate).unwrap();
2637        assert_eq!(result.to_string(), "pcol1 IS NULL".to_string());
2638
2639        // test less
2640        let predicate = Reference::new("col1")
2641            .less_than(Datum::long(100))
2642            .bind(schema.clone(), false)
2643            .unwrap();
2644        let result = strict_projection.strict_project(&predicate).unwrap();
2645        assert_eq!(result.to_string(), "pcol1 < 100".to_string());
2646
2647        // test less or eq
2648        let predicate = Reference::new("col1")
2649            .less_than_or_equal_to(Datum::long(100))
2650            .bind(schema.clone(), false)
2651            .unwrap();
2652        let result = strict_projection.strict_project(&predicate).unwrap();
2653        assert_eq!(result.to_string(), "pcol1 <= 100".to_string());
2654
2655        // test greater
2656        let predicate = Reference::new("col1")
2657            .greater_than(Datum::long(100))
2658            .bind(schema.clone(), false)
2659            .unwrap();
2660        let result = strict_projection.strict_project(&predicate).unwrap();
2661        assert_eq!(result.to_string(), "pcol1 > 100".to_string());
2662
2663        // test greater or eq
2664        let predicate = Reference::new("col1")
2665            .greater_than_or_equal_to(Datum::long(100))
2666            .bind(schema.clone(), false)
2667            .unwrap();
2668        let result = strict_projection.strict_project(&predicate).unwrap();
2669        assert_eq!(result.to_string(), "pcol1 >= 100".to_string());
2670
2671        // test eq
2672        let predicate = Reference::new("col1")
2673            .equal_to(Datum::long(100))
2674            .bind(schema.clone(), false)
2675            .unwrap();
2676        let result = strict_projection.strict_project(&predicate).unwrap();
2677        assert_eq!(result.to_string(), "pcol1 = 100".to_string());
2678
2679        // test not eq
2680        let predicate = Reference::new("col1")
2681            .not_equal_to(Datum::long(100))
2682            .bind(schema.clone(), false)
2683            .unwrap();
2684        let result = strict_projection.strict_project(&predicate).unwrap();
2685        assert_eq!(result.to_string(), "pcol1 != 100".to_string());
2686
2687        // test in
2688        let predicate = Reference::new("col1")
2689            .is_in(vec![Datum::long(100), Datum::long(101)].into_iter())
2690            .bind(schema.clone(), false)
2691            .unwrap();
2692        let result = strict_projection.strict_project(&predicate).unwrap();
2693        assert_eq!(result.to_string(), "pcol1 IN (101, 100)".to_string());
2694
2695        // test not in
2696        let predicate = Reference::new("col1")
2697            .is_not_in(vec![Datum::long(100), Datum::long(101)].into_iter())
2698            .bind(schema.clone(), false)
2699            .unwrap();
2700        let result = strict_projection.strict_project(&predicate).unwrap();
2701        assert_eq!(result.to_string(), "pcol1 NOT IN (101, 100)".to_string());
2702    }
2703
2704    #[tokio::test]
2705    async fn test_strict_projection_truncate_lower_bound() {
2706        // int, long, decimal
2707        let schema = Arc::new(
2708            Schema::builder()
2709                .with_fields(vec![
2710                    Arc::new(NestedField::required(
2711                        1,
2712                        "col1",
2713                        Type::Primitive(PrimitiveType::Int),
2714                    )),
2715                    Arc::new(NestedField::required(
2716                        2,
2717                        "col2",
2718                        Type::Primitive(PrimitiveType::Long),
2719                    )),
2720                    Arc::new(NestedField::required(
2721                        3,
2722                        "col3",
2723                        Type::Primitive(PrimitiveType::Decimal {
2724                            precision: 9,
2725                            scale: 2,
2726                        }),
2727                    )),
2728                ])
2729                .build()
2730                .unwrap(),
2731        );
2732
2733        let partition_spec = Arc::new(
2734            PartitionSpec::builder(schema.clone())
2735                .with_spec_id(1)
2736                .add_partition_field("col1", "pcol1", Transform::Truncate(10))
2737                .unwrap()
2738                .add_partition_field("col2", "pcol2", Transform::Truncate(10))
2739                .unwrap()
2740                .add_partition_field("col3", "pcol3", Transform::Truncate(10))
2741                .unwrap()
2742                .build()
2743                .unwrap(),
2744        );
2745
2746        let mut strict_projection = StrictProjection::new(partition_spec.clone());
2747
2748        // test less
2749        let predicate = Reference::new("col1")
2750            .less_than(Datum::int(100))
2751            .and(Reference::new("col2").less_than(Datum::long(100)))
2752            .and(Reference::new("col3").less_than(Datum::new(
2753                PrimitiveType::Decimal {
2754                    precision: 9,
2755                    scale: 2,
2756                },
2757                PrimitiveLiteral::Int128(10000),
2758            )))
2759            .bind(schema.clone(), false)
2760            .unwrap();
2761        let result = strict_projection.strict_project(&predicate).unwrap();
2762        assert_eq!(
2763            result.to_string(),
2764            "((pcol1 < 100) AND (pcol2 < 100)) AND (pcol3 < 10000)".to_string()
2765        );
2766
2767        // test less or eq
2768        let predicate = Reference::new("col1")
2769            .less_than_or_equal_to(Datum::int(100))
2770            .and(Reference::new("col2").less_than_or_equal_to(Datum::long(100)))
2771            .and(Reference::new("col3").less_than_or_equal_to(Datum::new(
2772                PrimitiveType::Decimal {
2773                    precision: 9,
2774                    scale: 2,
2775                },
2776                PrimitiveLiteral::Int128(10000),
2777            )))
2778            .bind(schema.clone(), false)
2779            .unwrap();
2780        let result = strict_projection.strict_project(&predicate).unwrap();
2781        assert_eq!(
2782            result.to_string(),
2783            "((pcol1 < 100) AND (pcol2 < 100)) AND (pcol3 < 10000)".to_string()
2784        );
2785
2786        // test greater
2787        let predicate = Reference::new("col1")
2788            .greater_than(Datum::int(100))
2789            .and(Reference::new("col2").greater_than(Datum::long(100)))
2790            .and(Reference::new("col3").greater_than(Datum::new(
2791                PrimitiveType::Decimal {
2792                    precision: 9,
2793                    scale: 2,
2794                },
2795                PrimitiveLiteral::Int128(10000),
2796            )))
2797            .bind(schema.clone(), false)
2798            .unwrap();
2799        let result = strict_projection.strict_project(&predicate).unwrap();
2800        assert_eq!(
2801            result.to_string(),
2802            "((pcol1 > 100) AND (pcol2 > 100)) AND (pcol3 > 10000)".to_string()
2803        );
2804
2805        // test greater or eq
2806        let predicate = Reference::new("col1")
2807            .greater_than_or_equal_to(Datum::int(100))
2808            .and(Reference::new("col2").greater_than_or_equal_to(Datum::long(100)))
2809            .and(Reference::new("col3").greater_than_or_equal_to(Datum::new(
2810                PrimitiveType::Decimal {
2811                    precision: 9,
2812                    scale: 2,
2813                },
2814                PrimitiveLiteral::Int128(10000),
2815            )))
2816            .bind(schema.clone(), false)
2817            .unwrap();
2818        let result = strict_projection.strict_project(&predicate).unwrap();
2819        assert_eq!(
2820            result.to_string(),
2821            "((pcol1 > 90) AND (pcol2 > 90)) AND (pcol3 > 9990)".to_string()
2822        );
2823
2824        // test not eq
2825        let predicate = Reference::new("col1")
2826            .not_equal_to(Datum::int(100))
2827            .and(Reference::new("col2").not_equal_to(Datum::long(100)))
2828            .and(Reference::new("col3").not_equal_to(Datum::new(
2829                PrimitiveType::Decimal {
2830                    precision: 9,
2831                    scale: 2,
2832                },
2833                PrimitiveLiteral::Int128(10000),
2834            )))
2835            .bind(schema.clone(), false)
2836            .unwrap();
2837        let result = strict_projection.strict_project(&predicate).unwrap();
2838        assert_eq!(
2839            result.to_string(),
2840            "((pcol1 != 100) AND (pcol2 != 100)) AND (pcol3 != 10000)".to_string()
2841        );
2842
2843        // test not in
2844        let predicate =
2845            Reference::new("col1")
2846                .is_not_in(vec![Datum::int(99), Datum::int(100), Datum::int(101)].into_iter())
2847                .and(Reference::new("col2").is_not_in(
2848                    vec![Datum::long(99), Datum::long(100), Datum::long(101)].into_iter(),
2849                ))
2850                .and(
2851                    Reference::new("col3").is_not_in(
2852                        vec![
2853                            Datum::new(
2854                                PrimitiveType::Decimal {
2855                                    precision: 9,
2856                                    scale: 2,
2857                                },
2858                                PrimitiveLiteral::Int128(9900),
2859                            ),
2860                            Datum::new(
2861                                PrimitiveType::Decimal {
2862                                    precision: 9,
2863                                    scale: 2,
2864                                },
2865                                PrimitiveLiteral::Int128(10000),
2866                            ),
2867                            Datum::new(
2868                                PrimitiveType::Decimal {
2869                                    precision: 9,
2870                                    scale: 2,
2871                                },
2872                                PrimitiveLiteral::Int128(10100),
2873                            ),
2874                        ]
2875                        .into_iter(),
2876                    ),
2877                )
2878                .bind(schema.clone(), false)
2879                .unwrap();
2880        let result = strict_projection.strict_project(&predicate).unwrap();
2881        assert_eq!(
2882            result.to_string(),
2883            "((pcol1 NOT IN (100, 90)) AND (pcol2 NOT IN (100, 90))) AND (pcol3 NOT IN (10000, 10100, 9900))"
2884                .to_string()
2885        );
2886
2887        // test in
2888        let predicate = Reference::new("col1")
2889            .is_in(vec![Datum::int(99), Datum::int(100), Datum::int(101)].into_iter())
2890            .and(
2891                Reference::new("col2")
2892                    .is_in(vec![Datum::long(99), Datum::long(100), Datum::long(101)].into_iter()),
2893            )
2894            .and(
2895                Reference::new("col3").is_in(
2896                    vec![
2897                        Datum::new(
2898                            PrimitiveType::Decimal {
2899                                precision: 9,
2900                                scale: 2,
2901                            },
2902                            PrimitiveLiteral::Int128(9900),
2903                        ),
2904                        Datum::new(
2905                            PrimitiveType::Decimal {
2906                                precision: 9,
2907                                scale: 2,
2908                            },
2909                            PrimitiveLiteral::Int128(10000),
2910                        ),
2911                        Datum::new(
2912                            PrimitiveType::Decimal {
2913                                precision: 9,
2914                                scale: 2,
2915                            },
2916                            PrimitiveLiteral::Int128(10100),
2917                        ),
2918                    ]
2919                    .into_iter(),
2920                ),
2921            )
2922            .bind(schema.clone(), false)
2923            .unwrap();
2924        let result = strict_projection.strict_project(&predicate).unwrap();
2925        assert_eq!(result.to_string(), "FALSE".to_string());
2926    }
2927
2928    #[tokio::test]
2929    async fn test_strict_projection_truncate_upper_bound() {
2930        let schema = Arc::new(
2931            Schema::builder()
2932                .with_fields(vec![
2933                    Arc::new(NestedField::required(
2934                        1,
2935                        "col1",
2936                        Type::Primitive(PrimitiveType::Int),
2937                    )),
2938                    Arc::new(NestedField::required(
2939                        2,
2940                        "col2",
2941                        Type::Primitive(PrimitiveType::Long),
2942                    )),
2943                    Arc::new(NestedField::required(
2944                        3,
2945                        "col3",
2946                        Type::Primitive(PrimitiveType::Decimal {
2947                            precision: 9,
2948                            scale: 2,
2949                        }),
2950                    )),
2951                ])
2952                .build()
2953                .unwrap(),
2954        );
2955
2956        let partition_spec = Arc::new(
2957            PartitionSpec::builder(schema.clone())
2958                .with_spec_id(1)
2959                .add_partition_field("col1", "pcol1", Transform::Truncate(10))
2960                .unwrap()
2961                .add_partition_field("col2", "pcol2", Transform::Truncate(10))
2962                .unwrap()
2963                .add_partition_field("col3", "pcol3", Transform::Truncate(10))
2964                .unwrap()
2965                .build()
2966                .unwrap(),
2967        );
2968
2969        let mut strict_projection = StrictProjection::new(partition_spec.clone());
2970
2971        // test less
2972        let predicate = Reference::new("col1")
2973            .less_than(Datum::int(99))
2974            .and(Reference::new("col2").less_than(Datum::long(99)))
2975            .and(Reference::new("col3").less_than(Datum::new(
2976                PrimitiveType::Decimal {
2977                    precision: 9,
2978                    scale: 2,
2979                },
2980                PrimitiveLiteral::Int128(9999),
2981            )))
2982            .bind(schema.clone(), false)
2983            .unwrap();
2984        let result = strict_projection.strict_project(&predicate).unwrap();
2985        assert_eq!(
2986            result.to_string(),
2987            "((pcol1 < 90) AND (pcol2 < 90)) AND (pcol3 < 9990)".to_string()
2988        );
2989
2990        // test less or eq
2991        let predicate = Reference::new("col1")
2992            .less_than_or_equal_to(Datum::int(99))
2993            .and(Reference::new("col2").less_than_or_equal_to(Datum::long(99)))
2994            .and(Reference::new("col3").less_than_or_equal_to(Datum::new(
2995                PrimitiveType::Decimal {
2996                    precision: 9,
2997                    scale: 2,
2998                },
2999                PrimitiveLiteral::Int128(9999),
3000            )))
3001            .bind(schema.clone(), false)
3002            .unwrap();
3003        let result = strict_projection.strict_project(&predicate).unwrap();
3004        assert_eq!(
3005            result.to_string(),
3006            "((pcol1 < 100) AND (pcol2 < 100)) AND (pcol3 < 10000)".to_string()
3007        );
3008
3009        // test greater
3010        let predicate = Reference::new("col1")
3011            .greater_than(Datum::int(99))
3012            .and(Reference::new("col2").greater_than(Datum::long(99)))
3013            .and(Reference::new("col3").greater_than(Datum::new(
3014                PrimitiveType::Decimal {
3015                    precision: 9,
3016                    scale: 2,
3017                },
3018                PrimitiveLiteral::Int128(9999),
3019            )))
3020            .bind(schema.clone(), false)
3021            .unwrap();
3022        let result = strict_projection.strict_project(&predicate).unwrap();
3023        assert_eq!(
3024            result.to_string(),
3025            "((pcol1 > 90) AND (pcol2 > 90)) AND (pcol3 > 9990)".to_string()
3026        );
3027
3028        // test greater or eq
3029        let predicate = Reference::new("col1")
3030            .greater_than_or_equal_to(Datum::int(99))
3031            .and(Reference::new("col2").greater_than_or_equal_to(Datum::long(99)))
3032            .and(Reference::new("col3").greater_than_or_equal_to(Datum::new(
3033                PrimitiveType::Decimal {
3034                    precision: 9,
3035                    scale: 2,
3036                },
3037                PrimitiveLiteral::Int128(9999),
3038            )))
3039            .bind(schema.clone(), false)
3040            .unwrap();
3041        let result = strict_projection.strict_project(&predicate).unwrap();
3042        assert_eq!(
3043            result.to_string(),
3044            "((pcol1 > 90) AND (pcol2 > 90)) AND (pcol3 > 9990)".to_string()
3045        );
3046
3047        // test not eq
3048        let predicate = Reference::new("col1")
3049            .not_equal_to(Datum::int(99))
3050            .and(Reference::new("col2").not_equal_to(Datum::long(99)))
3051            .and(Reference::new("col3").not_equal_to(Datum::new(
3052                PrimitiveType::Decimal {
3053                    precision: 9,
3054                    scale: 2,
3055                },
3056                PrimitiveLiteral::Int128(9999),
3057            )))
3058            .bind(schema.clone(), false)
3059            .unwrap();
3060        let result = strict_projection.strict_project(&predicate).unwrap();
3061        assert_eq!(
3062            result.to_string(),
3063            "((pcol1 != 90) AND (pcol2 != 90)) AND (pcol3 != 9990)".to_string()
3064        );
3065
3066        // test not in
3067        let predicate =
3068            Reference::new("col1")
3069                .is_not_in(vec![Datum::int(99), Datum::int(100), Datum::int(98)].into_iter())
3070                .and(Reference::new("col2").is_not_in(
3071                    vec![Datum::long(99), Datum::long(100), Datum::long(98)].into_iter(),
3072                ))
3073                .and(
3074                    Reference::new("col3").is_not_in(
3075                        vec![
3076                            Datum::new(
3077                                PrimitiveType::Decimal {
3078                                    precision: 9,
3079                                    scale: 2,
3080                                },
3081                                PrimitiveLiteral::Int128(9999),
3082                            ),
3083                            Datum::new(
3084                                PrimitiveType::Decimal {
3085                                    precision: 9,
3086                                    scale: 2,
3087                                },
3088                                PrimitiveLiteral::Int128(9899),
3089                            ),
3090                            Datum::new(
3091                                PrimitiveType::Decimal {
3092                                    precision: 9,
3093                                    scale: 2,
3094                                },
3095                                PrimitiveLiteral::Int128(10099),
3096                            ),
3097                        ]
3098                        .into_iter(),
3099                    ),
3100                )
3101                .bind(schema.clone(), false)
3102                .unwrap();
3103        let result = strict_projection.strict_project(&predicate).unwrap();
3104        assert_eq!(
3105            result.to_string(),
3106            "((pcol1 NOT IN (100, 90)) AND (pcol2 NOT IN (100, 90))) AND (pcol3 NOT IN (9890, 9990, 10090))"
3107                .to_string()
3108        );
3109
3110        // test in
3111        let predicate = Reference::new("col1")
3112            .is_in(vec![Datum::int(99), Datum::int(100), Datum::int(98)].into_iter())
3113            .and(
3114                Reference::new("col2")
3115                    .is_in(vec![Datum::long(99), Datum::long(100), Datum::long(98)].into_iter()),
3116            )
3117            .and(
3118                Reference::new("col3").is_in(
3119                    vec![
3120                        Datum::new(
3121                            PrimitiveType::Decimal {
3122                                precision: 9,
3123                                scale: 2,
3124                            },
3125                            PrimitiveLiteral::Int128(9999),
3126                        ),
3127                        Datum::new(
3128                            PrimitiveType::Decimal {
3129                                precision: 9,
3130                                scale: 2,
3131                            },
3132                            PrimitiveLiteral::Int128(9899),
3133                        ),
3134                        Datum::new(
3135                            PrimitiveType::Decimal {
3136                                precision: 9,
3137                                scale: 2,
3138                            },
3139                            PrimitiveLiteral::Int128(10099),
3140                        ),
3141                    ]
3142                    .into_iter(),
3143                ),
3144            )
3145            .bind(schema.clone(), false)
3146            .unwrap();
3147        let result = strict_projection.strict_project(&predicate).unwrap();
3148        assert_eq!(result.to_string(), "FALSE".to_string());
3149    }
3150
3151    #[tokio::test]
3152    async fn test_strict_projection_truncate_string() {
3153        let schema = Arc::new(
3154            Schema::builder()
3155                .with_fields(vec![Arc::new(NestedField::required(
3156                    1,
3157                    "col1",
3158                    Type::Primitive(PrimitiveType::String),
3159                ))])
3160                .build()
3161                .unwrap(),
3162        );
3163
3164        let partition_spec = Arc::new(
3165            PartitionSpec::builder(schema.clone())
3166                .with_spec_id(1)
3167                .add_partition_field("col1", "pcol1", Transform::Truncate(5))
3168                .unwrap()
3169                .build()
3170                .unwrap(),
3171        );
3172
3173        let mut strict_projection = StrictProjection::new(partition_spec.clone());
3174
3175        // test less
3176        let predicate = Reference::new("col1")
3177            .less_than(Datum::string("abcdefg"))
3178            .bind(schema.clone(), false)
3179            .unwrap();
3180        let result = strict_projection.strict_project(&predicate).unwrap();
3181        assert_eq!(result.to_string(), "pcol1 < \"abcde\"".to_string());
3182
3183        // test less or eq
3184        let predicate = Reference::new("col1")
3185            .less_than_or_equal_to(Datum::string("abcdefg"))
3186            .bind(schema.clone(), false)
3187            .unwrap();
3188        let result = strict_projection.strict_project(&predicate).unwrap();
3189        assert_eq!(result.to_string(), "pcol1 < \"abcde\"".to_string());
3190
3191        // test greater
3192        let predicate = Reference::new("col1")
3193            .greater_than(Datum::string("abcdefg"))
3194            .bind(schema.clone(), false)
3195            .unwrap();
3196        let result = strict_projection.strict_project(&predicate).unwrap();
3197        assert_eq!(result.to_string(), "pcol1 > \"abcde\"".to_string());
3198
3199        // test greater or eq
3200        let predicate = Reference::new("col1")
3201            .greater_than_or_equal_to(Datum::string("abcdefg"))
3202            .bind(schema.clone(), false)
3203            .unwrap();
3204        let result = strict_projection.strict_project(&predicate).unwrap();
3205        assert_eq!(result.to_string(), "pcol1 > \"abcde\"".to_string());
3206
3207        // test not eq
3208        let predicate = Reference::new("col1")
3209            .not_equal_to(Datum::string("abcdefg"))
3210            .bind(schema.clone(), false)
3211            .unwrap();
3212        let result = strict_projection.strict_project(&predicate).unwrap();
3213        assert_eq!(result.to_string(), "pcol1 != \"abcde\"".to_string());
3214
3215        // test not in
3216        let predicate = Reference::new("col1")
3217            .is_not_in(vec![Datum::string("abcdefg"), Datum::string("abcdefgabc")].into_iter())
3218            .bind(schema.clone(), false)
3219            .unwrap();
3220        let result = strict_projection.strict_project(&predicate).unwrap();
3221        assert_eq!(result.to_string(), "pcol1 NOT IN (\"abcde\")".to_string());
3222
3223        // test in
3224        let predicate = Reference::new("col1")
3225            .is_in(vec![Datum::string("abcdefg"), Datum::string("abcdefgabc")].into_iter())
3226            .bind(schema.clone(), false)
3227            .unwrap();
3228        let result = strict_projection.strict_project(&predicate).unwrap();
3229        assert_eq!(result.to_string(), "FALSE".to_string());
3230    }
3231
3232    // # TODO
3233    // test_strict_projection_truncate_binary
3234}