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}