orc_rust/
predicate.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
18//! Predicate types for row group filtering
19//!
20//! This module provides simplified predicate expressions that can be evaluated
21//! against row group statistics to filter out row groups before decoding.
22
23/// A simplified value type for predicates
24///
25/// This is a simplified representation of scalar values for predicate evaluation.
26/// In the future, this could be replaced with arrow's ScalarValue if available.
27#[derive(Debug, Clone, PartialEq)]
28pub enum PredicateValue {
29    /// Boolean value
30    Boolean(Option<bool>),
31    /// 8-bit signed integer
32    Int8(Option<i8>),
33    /// 16-bit signed integer
34    Int16(Option<i16>),
35    /// 32-bit signed integer
36    Int32(Option<i32>),
37    /// 64-bit signed integer
38    Int64(Option<i64>),
39    /// 32-bit floating point
40    Float32(Option<f32>),
41    /// 64-bit floating point
42    Float64(Option<f64>),
43    /// UTF-8 string
44    Utf8(Option<String>),
45}
46
47// For backward compatibility, we'll use PredicateValue as the value type
48// Users can convert from arrow types if needed
49pub type ScalarValue = PredicateValue;
50
51/// Comparison operator for predicates
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum ComparisonOp {
54    /// Equal to
55    Equal,
56    /// Not equal to
57    NotEqual,
58    /// Less than
59    LessThan,
60    /// Less than or equal to
61    LessThanOrEqual,
62    /// Greater than
63    GreaterThan,
64    /// Greater than or equal to
65    GreaterThanOrEqual,
66}
67
68/// A predicate that can be evaluated against row group statistics
69///
70/// Predicates are simplified expressions used for filtering row groups before
71/// decoding. They support basic comparison operations, NULL checks, and logical
72/// combinations (AND, OR, NOT).
73///
74/// # Example
75///
76/// ```rust
77/// use orc_rust::predicate::{Predicate, ComparisonOp, PredicateValue};
78///
79/// // Create a predicate: age >= 18
80/// let predicate = Predicate::gte("age", PredicateValue::Int32(Some(18)));
81///
82/// // Create a compound predicate: age >= 18 AND city = 'NYC'
83/// let predicate = Predicate::and(vec![
84///     Predicate::gte("age", PredicateValue::Int32(Some(18))),
85///     Predicate::eq("city", PredicateValue::Utf8(Some("NYC".to_string()))),
86/// ]);
87/// ```
88#[derive(Debug, Clone, PartialEq)]
89pub enum Predicate {
90    /// Column comparison: column <op> literal
91    Comparison {
92        /// Column name to compare
93        column: String,
94        /// Comparison operator
95        op: ComparisonOp,
96        /// Value to compare against
97        value: ScalarValue,
98    },
99    /// IS NULL check
100    IsNull {
101        /// Column name to check
102        column: String,
103    },
104    /// IS NOT NULL check
105    IsNotNull {
106        /// Column name to check
107        column: String,
108    },
109    /// Logical AND of predicates
110    And(Vec<Predicate>),
111    /// Logical OR of predicates
112    Or(Vec<Predicate>),
113    /// Logical NOT
114    Not(Box<Predicate>),
115}
116
117impl Predicate {
118    /// Create a comparison predicate: column <op> value
119    pub fn comparison(column: &str, op: ComparisonOp, value: ScalarValue) -> Self {
120        Self::Comparison {
121            column: column.to_string(),
122            op,
123            value,
124        }
125    }
126
127    /// Create a predicate for column == value
128    pub fn eq(column: &str, value: ScalarValue) -> Self {
129        Self::comparison(column, ComparisonOp::Equal, value)
130    }
131
132    /// Create a predicate for column != value
133    pub fn ne(column: &str, value: ScalarValue) -> Self {
134        Self::comparison(column, ComparisonOp::NotEqual, value)
135    }
136
137    /// Create a predicate for column < value
138    pub fn lt(column: &str, value: ScalarValue) -> Self {
139        Self::comparison(column, ComparisonOp::LessThan, value)
140    }
141
142    /// Create a predicate for column <= value
143    pub fn lte(column: &str, value: ScalarValue) -> Self {
144        Self::comparison(column, ComparisonOp::LessThanOrEqual, value)
145    }
146
147    /// Create a predicate for column > value
148    pub fn gt(column: &str, value: ScalarValue) -> Self {
149        Self::comparison(column, ComparisonOp::GreaterThan, value)
150    }
151
152    /// Create a predicate for column >= value
153    pub fn gte(column: &str, value: ScalarValue) -> Self {
154        Self::comparison(column, ComparisonOp::GreaterThanOrEqual, value)
155    }
156
157    /// Create a predicate for column IS NULL
158    pub fn is_null(column: &str) -> Self {
159        Self::IsNull {
160            column: column.to_string(),
161        }
162    }
163
164    /// Create a predicate for column IS NOT NULL
165    pub fn is_not_null(column: &str) -> Self {
166        Self::IsNotNull {
167            column: column.to_string(),
168        }
169    }
170
171    /// Combine predicates with AND
172    pub fn and(predicates: Vec<Predicate>) -> Self {
173        Self::And(predicates)
174    }
175
176    /// Combine predicates with OR
177    pub fn or(predicates: Vec<Predicate>) -> Self {
178        Self::Or(predicates)
179    }
180
181    /// Negate a predicate
182    #[allow(clippy::should_implement_trait)]
183    pub fn not(predicate: Predicate) -> Self {
184        Self::Not(Box::new(predicate))
185    }
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191
192    #[test]
193    fn test_predicate_creation() {
194        let p1 = Predicate::eq("age", ScalarValue::Int32(Some(18)));
195        assert!(
196            matches!(p1, Predicate::Comparison { ref column, op: ComparisonOp::Equal, .. } if column == "age")
197        );
198
199        let p2 = Predicate::gt("price", ScalarValue::Float64(Some(100.0)));
200        assert!(
201            matches!(p2, Predicate::Comparison { ref column, op: ComparisonOp::GreaterThan, .. } if column == "price")
202        );
203
204        let p3 = Predicate::is_null("description");
205        assert!(matches!(p3, Predicate::IsNull { ref column } if column == "description"));
206
207        let combined = Predicate::and(vec![p1, p2]);
208        assert!(matches!(combined, Predicate::And(_)));
209    }
210}