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
68impl ComparisonOp {
69 /// Returns the negated comparison operator.
70 pub fn negate(&self) -> Self {
71 match self {
72 ComparisonOp::Equal => ComparisonOp::NotEqual,
73 ComparisonOp::NotEqual => ComparisonOp::Equal,
74 ComparisonOp::LessThan => ComparisonOp::GreaterThanOrEqual,
75 ComparisonOp::LessThanOrEqual => ComparisonOp::GreaterThan,
76 ComparisonOp::GreaterThan => ComparisonOp::LessThanOrEqual,
77 ComparisonOp::GreaterThanOrEqual => ComparisonOp::LessThan,
78 }
79 }
80}
81
82/// A predicate that can be evaluated against row group statistics
83///
84/// Predicates are simplified expressions used for filtering row groups before
85/// decoding. They support basic comparison operations, NULL checks, and logical
86/// combinations (AND, OR, NOT).
87///
88/// # Example
89///
90/// ```rust
91/// use orc_rust::predicate::{Predicate, ComparisonOp, PredicateValue};
92///
93/// // Create a predicate: age >= 18
94/// let predicate = Predicate::gte("age", PredicateValue::Int32(Some(18)));
95///
96/// // Create a compound predicate: age >= 18 AND city = 'NYC'
97/// let predicate = Predicate::and(vec![
98/// Predicate::gte("age", PredicateValue::Int32(Some(18))),
99/// Predicate::eq("city", PredicateValue::Utf8(Some("NYC".to_string()))),
100/// ]);
101/// ```
102#[derive(Debug, Clone, PartialEq)]
103pub enum Predicate {
104 /// Column comparison: column <op> literal
105 Comparison {
106 /// Column name to compare
107 column: String,
108 /// Comparison operator
109 op: ComparisonOp,
110 /// Value to compare against
111 value: ScalarValue,
112 },
113 /// IS NULL check
114 IsNull {
115 /// Column name to check
116 column: String,
117 },
118 /// IS NOT NULL check
119 IsNotNull {
120 /// Column name to check
121 column: String,
122 },
123 /// Logical AND of predicates
124 And(Vec<Predicate>),
125 /// Logical OR of predicates
126 Or(Vec<Predicate>),
127 /// Logical NOT
128 Not(Box<Predicate>),
129}
130
131impl Predicate {
132 /// Create a comparison predicate: column <op> value
133 pub fn comparison(column: &str, op: ComparisonOp, value: ScalarValue) -> Self {
134 Self::Comparison {
135 column: column.to_string(),
136 op,
137 value,
138 }
139 }
140
141 /// Create a predicate for column == value
142 pub fn eq(column: &str, value: ScalarValue) -> Self {
143 Self::comparison(column, ComparisonOp::Equal, value)
144 }
145
146 /// Create a predicate for column != value
147 pub fn ne(column: &str, value: ScalarValue) -> Self {
148 Self::comparison(column, ComparisonOp::NotEqual, value)
149 }
150
151 /// Create a predicate for column < value
152 pub fn lt(column: &str, value: ScalarValue) -> Self {
153 Self::comparison(column, ComparisonOp::LessThan, value)
154 }
155
156 /// Create a predicate for column <= value
157 pub fn lte(column: &str, value: ScalarValue) -> Self {
158 Self::comparison(column, ComparisonOp::LessThanOrEqual, value)
159 }
160
161 /// Create a predicate for column > value
162 pub fn gt(column: &str, value: ScalarValue) -> Self {
163 Self::comparison(column, ComparisonOp::GreaterThan, value)
164 }
165
166 /// Create a predicate for column >= value
167 pub fn gte(column: &str, value: ScalarValue) -> Self {
168 Self::comparison(column, ComparisonOp::GreaterThanOrEqual, value)
169 }
170
171 /// Create a predicate for column IS NULL
172 pub fn is_null(column: &str) -> Self {
173 Self::IsNull {
174 column: column.to_string(),
175 }
176 }
177
178 /// Create a predicate for column IS NOT NULL
179 pub fn is_not_null(column: &str) -> Self {
180 Self::IsNotNull {
181 column: column.to_string(),
182 }
183 }
184
185 /// Combine predicates with AND
186 pub fn and(predicates: Vec<Predicate>) -> Self {
187 Self::And(predicates)
188 }
189
190 /// Combine predicates with OR
191 pub fn or(predicates: Vec<Predicate>) -> Self {
192 Self::Or(predicates)
193 }
194
195 /// Negate a predicate
196 #[allow(clippy::should_implement_trait)]
197 pub fn not(predicate: Predicate) -> Self {
198 Self::Not(Box::new(predicate))
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn test_predicate_creation() {
208 let p1 = Predicate::eq("age", ScalarValue::Int32(Some(18)));
209 assert!(
210 matches!(p1, Predicate::Comparison { ref column, op: ComparisonOp::Equal, .. } if column == "age")
211 );
212
213 let p2 = Predicate::gt("price", ScalarValue::Float64(Some(100.0)));
214 assert!(
215 matches!(p2, Predicate::Comparison { ref column, op: ComparisonOp::GreaterThan, .. } if column == "price")
216 );
217
218 let p3 = Predicate::is_null("description");
219 assert!(matches!(p3, Predicate::IsNull { ref column } if column == "description"));
220
221 let combined = Predicate::and(vec![p1, p2]);
222 assert!(matches!(combined, Predicate::And(_)));
223 }
224}