1use std::hash::Hash;
43use std::hash::Hasher;
44use std::sync::Arc;
45
46use vortex_error::VortexExpect;
47use vortex_utils::aliases::hash_set::HashSet;
48
49use crate::dtype::FieldName;
50use crate::expr::traversal::NodeExt;
51use crate::expr::traversal::ReferenceCollector;
52use crate::scalar_fn::fns::binary::Binary;
53use crate::scalar_fn::fns::operators::Operator;
54
55pub mod aliases;
56pub mod analysis;
57#[cfg(feature = "arbitrary")]
58pub mod arbitrary;
59pub mod display;
60pub(crate) mod expression;
61mod exprs;
62pub(crate) mod field;
63pub mod forms;
64mod optimize;
65pub mod proto;
66pub mod stats;
67pub mod transform;
68pub mod traversal;
69
70pub use analysis::*;
71pub use expression::*;
72pub use exprs::*;
73
74pub trait VortexExprExt {
75 fn field_references(&self) -> HashSet<FieldName>;
77}
78
79impl VortexExprExt for Expression {
80 fn field_references(&self) -> HashSet<FieldName> {
81 let mut collector = ReferenceCollector::new();
82 self.accept(&mut collector)
84 .vortex_expect("reference collector should never fail");
85 collector.into_fields()
86 }
87}
88
89pub fn split_conjunction(expr: &Expression) -> Vec<Expression> {
91 let mut conjunctions = vec![];
92 split_inner(expr, &mut conjunctions);
93 conjunctions
94}
95
96fn split_inner(expr: &Expression, exprs: &mut Vec<Expression>) {
97 match expr.as_opt::<Binary>() {
98 Some(operator) if *operator == Operator::And => {
99 split_inner(expr.child(0), exprs);
100 split_inner(expr.child(1), exprs);
101 }
102 Some(_) | None => {
103 exprs.push(expr.clone());
104 }
105 }
106}
107
108#[derive(Clone, Debug)]
110pub struct ExactExpr(pub Expression);
111impl PartialEq for ExactExpr {
112 fn eq(&self, other: &Self) -> bool {
113 self.0.scalar_fn() == other.0.scalar_fn()
114 && Arc::ptr_eq(self.0.children(), other.0.children())
115 }
116}
117impl Eq for ExactExpr {}
118
119impl Hash for ExactExpr {
120 fn hash<H: Hasher>(&self, state: &mut H) {
121 self.0.scalar_fn().hash(state);
122 Arc::as_ptr(self.0.children()).hash(state);
123 }
124}
125
126#[cfg(feature = "_test-harness")]
127pub mod test_harness {
128 use crate::dtype::DType;
129 use crate::dtype::Nullability;
130 use crate::dtype::PType;
131 use crate::dtype::StructFields;
132
133 pub fn struct_dtype() -> DType {
134 DType::Struct(
135 StructFields::new(
136 ["a", "col1", "col2", "bool1", "bool2"].into(),
137 vec![
138 DType::Primitive(PType::I32, Nullability::NonNullable),
139 DType::Primitive(PType::U16, Nullability::Nullable),
140 DType::Primitive(PType::U16, Nullability::Nullable),
141 DType::Bool(Nullability::NonNullable),
142 DType::Bool(Nullability::NonNullable),
143 ],
144 ),
145 Nullability::NonNullable,
146 )
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use std::collections::hash_map::RandomState;
153 use std::hash::BuildHasher;
154
155 use super::*;
156 use crate::dtype::DType;
157 use crate::dtype::FieldNames;
158 use crate::dtype::Nullability;
159 use crate::dtype::PType;
160 use crate::dtype::StructFields;
161 use crate::expr::and;
162 use crate::expr::col;
163 use crate::expr::eq;
164 use crate::expr::get_item;
165 use crate::expr::gt;
166 use crate::expr::gt_eq;
167 use crate::expr::lit;
168 use crate::expr::lt;
169 use crate::expr::lt_eq;
170 use crate::expr::not;
171 use crate::expr::not_eq;
172 use crate::expr::or;
173 use crate::expr::root;
174 use crate::expr::select;
175 use crate::expr::select_exclude;
176 use crate::scalar::Scalar;
177
178 #[test]
179 fn basic_expr_split_test() {
180 let lhs = get_item("col1", root());
181 let rhs = lit(1);
182 let expr = eq(lhs, rhs);
183 let conjunction = split_conjunction(&expr);
184 assert_eq!(conjunction.len(), 1);
185 }
186
187 #[test]
188 fn basic_conjunction_split_test() {
189 let lhs = get_item("col1", root());
190 let rhs = lit(1);
191 let expr = and(lhs, rhs);
192 let conjunction = split_conjunction(&expr);
193 assert_eq!(conjunction.len(), 2, "Conjunction is {conjunction:?}");
194 }
195
196 #[test]
197 fn exact_expr_hash_consistent_with_eq() {
198 let state = RandomState::new();
199 let expr = eq(get_item("col1", root()), lit(1));
200
201 let a = ExactExpr(expr.clone());
203 let b = ExactExpr(expr);
204 assert_eq!(a, b);
205 assert_eq!(state.hash_one(&a), state.hash_one(&b));
206
207 let rebuilt = ExactExpr(eq(get_item("col1", root()), lit(1)));
209 assert_ne!(a, rebuilt);
210 }
211
212 #[test]
213 fn expr_display() {
214 assert_eq!(col("a").to_string(), "$.a");
215 assert_eq!(root().to_string(), "$");
216
217 let col1: Expression = col("col1");
218 let col2: Expression = col("col2");
219 assert_eq!(
220 and(col1.clone(), col2.clone()).to_string(),
221 "($.col1 and $.col2)"
222 );
223 assert_eq!(
224 or(col1.clone(), col2.clone()).to_string(),
225 "($.col1 or $.col2)"
226 );
227 assert_eq!(
228 eq(col1.clone(), col2.clone()).to_string(),
229 "($.col1 = $.col2)"
230 );
231 assert_eq!(
232 not_eq(col1.clone(), col2.clone()).to_string(),
233 "($.col1 != $.col2)"
234 );
235 assert_eq!(
236 gt(col1.clone(), col2.clone()).to_string(),
237 "($.col1 > $.col2)"
238 );
239 assert_eq!(
240 gt_eq(col1.clone(), col2.clone()).to_string(),
241 "($.col1 >= $.col2)"
242 );
243 assert_eq!(
244 lt(col1.clone(), col2.clone()).to_string(),
245 "($.col1 < $.col2)"
246 );
247 assert_eq!(
248 lt_eq(col1.clone(), col2.clone()).to_string(),
249 "($.col1 <= $.col2)"
250 );
251
252 assert_eq!(
253 or(lt(col1.clone(), col2.clone()), not_eq(col1.clone(), col2),).to_string(),
254 "(($.col1 < $.col2) or ($.col1 != $.col2))"
255 );
256
257 assert_eq!(not(col1).to_string(), "vortex.not($.col1)");
258
259 assert_eq!(
260 select(vec![FieldName::from("col1")], root()).to_string(),
261 "${col1}"
262 );
263 assert_eq!(
264 select(
265 vec![FieldName::from("col1"), FieldName::from("col2")],
266 root()
267 )
268 .to_string(),
269 "${col1, col2}"
270 );
271 assert_eq!(
272 select_exclude(
273 vec![FieldName::from("col1"), FieldName::from("col2")],
274 root()
275 )
276 .to_string(),
277 "${~ col1, col2}"
278 );
279
280 assert_eq!(lit(Scalar::from(0u8)).to_string(), "0u8");
281 assert_eq!(lit(Scalar::from(0.0f32)).to_string(), "0f32");
282 assert_eq!(
283 lit(Scalar::from(i64::MAX)).to_string(),
284 "9223372036854775807i64"
285 );
286 assert_eq!(lit(Scalar::from(true)).to_string(), "true");
287 assert_eq!(
288 lit(Scalar::null(DType::Bool(Nullability::Nullable))).to_string(),
289 "null"
290 );
291
292 assert_eq!(
293 lit(Scalar::struct_(
294 DType::Struct(
295 StructFields::new(
296 FieldNames::from(["dog", "cat"]),
297 vec![
298 DType::Primitive(PType::U32, Nullability::NonNullable),
299 DType::Utf8(Nullability::NonNullable)
300 ],
301 ),
302 Nullability::NonNullable
303 ),
304 vec![Scalar::from(32_u32), Scalar::from("rufus".to_string())]
305 ))
306 .to_string(),
307 "{dog: 32u32, cat: \"rufus\"}"
308 );
309 }
310}