Skip to main content

vortex_array/expr/
exprs.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Factory functions for creating [`Expression`]s from scalar function vtables.
5
6use std::sync::Arc;
7
8use vortex_error::VortexExpect;
9use vortex_error::VortexResult;
10use vortex_error::vortex_panic;
11use vortex_utils::iter::ReduceBalancedIterExt;
12
13use crate::aggregate_fn::NumericalAggregateOpts;
14use crate::dtype::DType;
15use crate::dtype::FieldName;
16use crate::dtype::FieldNames;
17use crate::dtype::Nullability;
18use crate::expr::BoundExpression;
19use crate::expr::Expression;
20use crate::scalar::Scalar;
21use crate::scalar::ScalarValue;
22use crate::scalar_fn::EmptyOptions;
23use crate::scalar_fn::ScalarFnVTableExt;
24use crate::scalar_fn::fns::between::Between;
25use crate::scalar_fn::fns::between::BetweenOptions;
26use crate::scalar_fn::fns::binary::Binary;
27use crate::scalar_fn::fns::byte_length::ByteLength;
28use crate::scalar_fn::fns::case_when::CaseWhen;
29use crate::scalar_fn::fns::case_when::CaseWhenOptions;
30use crate::scalar_fn::fns::cast::Cast;
31use crate::scalar_fn::fns::dynamic::DynamicComparison;
32use crate::scalar_fn::fns::dynamic::DynamicComparisonExpr;
33use crate::scalar_fn::fns::dynamic::Rhs;
34use crate::scalar_fn::fns::ext_storage::ExtStorage;
35use crate::scalar_fn::fns::fill_null::FillNull;
36use crate::scalar_fn::fns::get_item::GetItem;
37use crate::scalar_fn::fns::is_not_null::IsNotNull;
38use crate::scalar_fn::fns::is_null::IsNull;
39use crate::scalar_fn::fns::like::Like;
40use crate::scalar_fn::fns::like::LikeOptions;
41use crate::scalar_fn::fns::list_contains::ListContains;
42use crate::scalar_fn::fns::list_length::ListLength;
43use crate::scalar_fn::fns::list_sum::ListSum;
44use crate::scalar_fn::fns::literal::Literal;
45use crate::scalar_fn::fns::mask::Mask;
46use crate::scalar_fn::fns::merge::DuplicateHandling;
47use crate::scalar_fn::fns::merge::Merge;
48use crate::scalar_fn::fns::not::Not;
49use crate::scalar_fn::fns::operators::CompareOperator;
50use crate::scalar_fn::fns::operators::Operator;
51use crate::scalar_fn::fns::pack::Pack;
52use crate::scalar_fn::fns::pack::PackOptions;
53use crate::scalar_fn::fns::select::FieldSelection;
54use crate::scalar_fn::fns::select::Select;
55use crate::scalar_fn::fns::variant_get::VariantGet;
56use crate::scalar_fn::fns::variant_get::VariantGetOptions;
57use crate::scalar_fn::fns::variant_get::VariantPath;
58use crate::scalar_fn::fns::zip::Zip;
59
60/// Creates an expression that references the root scope.
61///
62/// Returns the entire input array as passed to the expression evaluator.
63/// This is commonly used as the starting point for field access and other operations.
64pub fn root() -> Expression {
65    Expression::Root
66}
67
68/// Creates a bound expression that references a root scope with the given dtype.
69pub fn bound_root(dtype: DType) -> BoundExpression {
70    BoundExpression::new_root(dtype)
71}
72
73/// Return whether the expression is a root expression.
74pub fn is_root(expr: &Expression) -> bool {
75    expr.is_root()
76}
77
78// ---- Literal ----
79
80/// Create a new `Literal` expression from a type that coerces to `Scalar`.
81///
82///
83/// ## Example usage
84///
85/// ```
86/// use vortex_array::arrays::PrimitiveArray;
87/// use vortex_array::dtype::Nullability;
88/// use vortex_array::expr::lit;
89/// use vortex_array::scalar_fn::fns::literal::Literal;
90/// use vortex_array::scalar::Scalar;
91///
92/// let number = lit(34i32);
93///
94/// let scalar = number.as_::<Literal>();
95/// assert_eq!(scalar, &Scalar::primitive(34i32, Nullability::NonNullable));
96/// ```
97pub fn lit(value: impl Into<Scalar>) -> Expression {
98    Literal.new_expr(value.into(), [])
99}
100
101/// Creates a bound literal expression.
102pub fn bound_lit(value: impl Into<Scalar>) -> BoundExpression {
103    Literal
104        .try_new_bound_expr(value.into(), [])
105        .vortex_expect("literal expressions are always well-typed")
106}
107
108// ---- GetItem / Col ----
109
110/// Creates an expression that accesses a field from the root array.
111///
112/// Equivalent to `get_item(field, root())` - extracts a named field from the input array.
113///
114/// ```rust
115/// # use vortex_array::expr::col;
116/// let expr = col("name");
117/// ```
118pub fn col(field: impl Into<FieldName>) -> Expression {
119    GetItem.new_expr(field.into(), vec![root()])
120}
121
122/// Creates a bound expression that accesses a field from a root scope with the given dtype.
123pub fn bound_col(field: impl Into<FieldName>, scope: DType) -> BoundExpression {
124    bound_get_item(field, bound_root(scope))
125}
126
127/// Creates an expression that extracts a named field from a struct expression.
128///
129/// Accesses the specified field from the result of the child expression.
130///
131/// ```rust
132/// # use vortex_array::expr::{get_item, root};
133/// let expr = get_item("user_id", root());
134/// ```
135pub fn get_item(field: impl Into<FieldName>, child: Expression) -> Expression {
136    GetItem.new_expr(field.into(), vec![child])
137}
138
139/// Creates a bound expression that extracts a named field from a struct expression.
140pub fn bound_get_item(field: impl Into<FieldName>, child: BoundExpression) -> BoundExpression {
141    GetItem
142        .try_new_bound_expr(field.into(), [child])
143        .vortex_expect("get-item expressions must reference a field in the child dtype")
144}
145
146// ---- VariantGet ----
147
148/// Creates an expression that extracts a path from a Variant expression.
149///
150/// Missing paths, traversal mismatches, and failed casts return null. When `dtype` is `None`,
151/// results are nullable Variant values; otherwise results are nullable values of `dtype`.
152pub fn variant_get(
153    child: Expression,
154    path: impl Into<VariantPath>,
155    dtype: Option<DType>,
156) -> Expression {
157    VariantGet.new_expr(VariantGetOptions::new(path.into(), dtype), vec![child])
158}
159
160/// Creates a bound expression that extracts a path from a Variant expression.
161pub fn bound_variant_get(
162    child: BoundExpression,
163    path: impl Into<VariantPath>,
164    dtype: Option<DType>,
165) -> BoundExpression {
166    VariantGet
167        .try_new_bound_expr(VariantGetOptions::new(path.into(), dtype), [child])
168        .vortex_expect("variant-get expressions require a Variant child")
169}
170
171// ---- CaseWhen ----
172
173/// Creates a CASE WHEN expression with one WHEN/THEN pair and an ELSE value.
174pub fn case_when(
175    condition: Expression,
176    then_value: Expression,
177    else_value: Expression,
178) -> Expression {
179    let options = CaseWhenOptions {
180        num_when_then_pairs: 1,
181        has_else: true,
182    };
183    CaseWhen.new_expr(options, [condition, then_value, else_value])
184}
185
186/// Creates a bound CASE WHEN expression with one WHEN/THEN pair and an ELSE value.
187pub fn bound_case_when(
188    condition: BoundExpression,
189    then_value: BoundExpression,
190    else_value: BoundExpression,
191) -> BoundExpression {
192    let options = CaseWhenOptions {
193        num_when_then_pairs: 1,
194        has_else: true,
195    };
196    CaseWhen
197        .try_new_bound_expr(options, [condition, then_value, else_value])
198        .vortex_expect("case expressions must have boolean conditions and matching branch dtypes")
199}
200
201/// Creates a CASE WHEN expression with one WHEN/THEN pair and no ELSE value.
202pub fn case_when_no_else(condition: Expression, then_value: Expression) -> Expression {
203    let options = CaseWhenOptions {
204        num_when_then_pairs: 1,
205        has_else: false,
206    };
207    CaseWhen.new_expr(options, [condition, then_value])
208}
209
210/// Creates a bound CASE WHEN expression with one WHEN/THEN pair and no ELSE value.
211pub fn bound_case_when_no_else(
212    condition: BoundExpression,
213    then_value: BoundExpression,
214) -> BoundExpression {
215    let options = CaseWhenOptions {
216        num_when_then_pairs: 1,
217        has_else: false,
218    };
219    CaseWhen
220        .try_new_bound_expr(options, [condition, then_value])
221        .vortex_expect("case expressions must have boolean conditions")
222}
223
224/// Creates an n-ary CASE WHEN expression from WHEN/THEN pairs and an optional ELSE value.
225pub fn nested_case_when(
226    when_then_pairs: Vec<(Expression, Expression)>,
227    else_value: Option<Expression>,
228) -> Expression {
229    assert!(
230        !when_then_pairs.is_empty(),
231        "nested_case_when requires at least one when/then pair"
232    );
233
234    let has_else = else_value.is_some();
235    let mut children = Vec::with_capacity(when_then_pairs.len() * 2 + usize::from(has_else));
236    for (condition, then_value) in &when_then_pairs {
237        children.push(condition.clone());
238        children.push(then_value.clone());
239    }
240    if let Some(else_expr) = else_value {
241        children.push(else_expr);
242    }
243
244    let Ok(num_when_then_pairs) = u32::try_from(when_then_pairs.len()) else {
245        vortex_panic!("nested_case_when has too many when/then pairs");
246    };
247    let options = CaseWhenOptions {
248        num_when_then_pairs,
249        has_else,
250    };
251    CaseWhen.new_expr(options, children)
252}
253
254/// Creates a bound n-ary CASE WHEN expression from WHEN/THEN pairs and an optional ELSE value.
255pub fn bound_nested_case_when(
256    when_then_pairs: Vec<(BoundExpression, BoundExpression)>,
257    else_value: Option<BoundExpression>,
258) -> BoundExpression {
259    assert!(
260        !when_then_pairs.is_empty(),
261        "nested_case_when requires at least one when/then pair"
262    );
263
264    let Ok(num_when_then_pairs) = u32::try_from(when_then_pairs.len()) else {
265        vortex_panic!("nested_case_when has too many when/then pairs");
266    };
267    let has_else = else_value.is_some();
268    let mut children = Vec::with_capacity(when_then_pairs.len() * 2 + usize::from(has_else));
269    for (condition, then_value) in when_then_pairs {
270        children.push(condition);
271        children.push(then_value);
272    }
273    if let Some(else_expr) = else_value {
274        children.push(else_expr);
275    }
276
277    let options = CaseWhenOptions {
278        num_when_then_pairs,
279        has_else,
280    };
281    CaseWhen
282        .try_new_bound_expr(options, children)
283        .vortex_expect("case expressions must have boolean conditions and matching branch dtypes")
284}
285
286// ---- Binary operators ----
287
288/// Creates a binary expression with the given operator.
289pub fn binary(operator: Operator, lhs: Expression, rhs: Expression) -> Expression {
290    Binary
291        .try_new_expr(operator, [lhs, rhs])
292        .vortex_expect("Failed to create binary expression")
293}
294
295/// Creates a bound binary expression with the given operator.
296pub fn bound_binary(
297    operator: Operator,
298    lhs: BoundExpression,
299    rhs: BoundExpression,
300) -> BoundExpression {
301    Binary
302        .try_new_bound_expr(operator, [lhs, rhs])
303        .vortex_expect("binary expressions must have compatible operand dtypes")
304}
305
306/// Create a new [`Binary`] using the [`Eq`](Operator::Eq) operator.
307///
308/// ## Example usage
309///
310/// ```
311/// # use vortex_array::arrays::{BoolArray, PrimitiveArray};
312/// # use vortex_array::arrays::bool::BoolArrayExt;
313/// # use vortex_array::IntoArray;
314/// # use vortex_array::{VortexSessionExecute, array_session};
315/// # use vortex_array::validity::Validity;
316/// # use vortex_buffer::buffer;
317/// # use vortex_array::expr::{eq, root, lit};
318/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
319/// let result = xs.into_array().apply(&eq(root(), lit(3))).unwrap();
320/// let mut ctx = array_session().create_execution_ctx();
321///
322/// assert_eq!(
323///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
324///     BoolArray::from_iter(vec![false, false, true]).to_bit_buffer(),
325/// );
326/// ```
327pub fn eq(lhs: Expression, rhs: Expression) -> Expression {
328    Binary
329        .try_new_expr(Operator::Eq, [lhs, rhs])
330        .vortex_expect("Failed to create Eq binary expression")
331}
332
333/// Creates a bound equality expression.
334pub fn bound_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
335    bound_binary(Operator::Eq, lhs, rhs)
336}
337
338/// Create a new [`Binary`] using the [`NotEq`](Operator::NotEq) operator.
339///
340/// ## Example usage
341///
342/// ```
343/// # use vortex_array::arrays::{BoolArray, PrimitiveArray};
344/// # use vortex_array::arrays::bool::BoolArrayExt;
345/// # use vortex_array::IntoArray;
346/// # use vortex_array::{VortexSessionExecute, array_session};
347/// # use vortex_array::validity::Validity;
348/// # use vortex_buffer::buffer;
349/// # use vortex_array::expr::{root, lit, not_eq};
350/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
351/// let result = xs.into_array().apply(&not_eq(root(), lit(3))).unwrap();
352/// let mut ctx = array_session().create_execution_ctx();
353///
354/// assert_eq!(
355///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
356///     BoolArray::from_iter(vec![true, true, false]).to_bit_buffer(),
357/// );
358/// ```
359pub fn not_eq(lhs: Expression, rhs: Expression) -> Expression {
360    Binary
361        .try_new_expr(Operator::NotEq, [lhs, rhs])
362        .vortex_expect("Failed to create NotEq binary expression")
363}
364
365/// Creates a bound inequality expression.
366pub fn bound_not_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
367    bound_binary(Operator::NotEq, lhs, rhs)
368}
369
370/// Create a new [`Binary`] using the [`Gte`](Operator::Gte) operator.
371///
372/// ## Example usage
373///
374/// ```
375/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
376/// # use vortex_array::arrays::bool::BoolArrayExt;
377/// # use vortex_array::IntoArray;
378/// # use vortex_array::{VortexSessionExecute, array_session};
379/// # use vortex_array::validity::Validity;
380/// # use vortex_buffer::buffer;
381/// # use vortex_array::expr::{gt_eq, root, lit};
382/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
383/// let result = xs.into_array().apply(&gt_eq(root(), lit(3))).unwrap();
384/// let mut ctx = array_session().create_execution_ctx();
385///
386/// assert_eq!(
387///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
388///     BoolArray::from_iter(vec![false, false, true]).to_bit_buffer(),
389/// );
390/// ```
391pub fn gt_eq(lhs: Expression, rhs: Expression) -> Expression {
392    Binary
393        .try_new_expr(Operator::Gte, [lhs, rhs])
394        .vortex_expect("Failed to create Gte binary expression")
395}
396
397/// Creates a bound greater-than-or-equal expression.
398pub fn bound_gt_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
399    bound_binary(Operator::Gte, lhs, rhs)
400}
401
402/// Create a new [`Binary`] using the [`Gt`](Operator::Gt) operator.
403///
404/// ## Example usage
405///
406/// ```
407/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
408/// # use vortex_array::arrays::bool::BoolArrayExt;
409/// # use vortex_array::IntoArray;
410/// # use vortex_array::{VortexSessionExecute, array_session};
411/// # use vortex_array::validity::Validity;
412/// # use vortex_buffer::buffer;
413/// # use vortex_array::expr::{gt, root, lit};
414/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
415/// let result = xs.into_array().apply(&gt(root(), lit(2))).unwrap();
416/// let mut ctx = array_session().create_execution_ctx();
417///
418/// assert_eq!(
419///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
420///     BoolArray::from_iter(vec![false, false, true]).to_bit_buffer(),
421/// );
422/// ```
423pub fn gt(lhs: Expression, rhs: Expression) -> Expression {
424    Binary
425        .try_new_expr(Operator::Gt, [lhs, rhs])
426        .vortex_expect("Failed to create Gt binary expression")
427}
428
429/// Creates a bound greater-than expression.
430pub fn bound_gt(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
431    bound_binary(Operator::Gt, lhs, rhs)
432}
433
434/// Create a new [`Binary`] using the [`Lte`](Operator::Lte) operator.
435///
436/// ## Example usage
437///
438/// ```
439/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
440/// # use vortex_array::arrays::bool::BoolArrayExt;
441/// # use vortex_array::IntoArray;
442/// # use vortex_array::{VortexSessionExecute, array_session};
443/// # use vortex_array::validity::Validity;
444/// # use vortex_buffer::buffer;
445/// # use vortex_array::expr::{root, lit, lt_eq};
446/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
447/// let result = xs.into_array().apply(&lt_eq(root(), lit(2))).unwrap();
448/// let mut ctx = array_session().create_execution_ctx();
449///
450/// assert_eq!(
451///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
452///     BoolArray::from_iter(vec![true, true, false]).to_bit_buffer(),
453/// );
454/// ```
455pub fn lt_eq(lhs: Expression, rhs: Expression) -> Expression {
456    Binary
457        .try_new_expr(Operator::Lte, [lhs, rhs])
458        .vortex_expect("Failed to create Lte binary expression")
459}
460
461/// Creates a bound less-than-or-equal expression.
462pub fn bound_lt_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
463    bound_binary(Operator::Lte, lhs, rhs)
464}
465
466/// Create a new [`Binary`] using the [`Lt`](Operator::Lt) operator.
467///
468/// ## Example usage
469///
470/// ```
471/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
472/// # use vortex_array::arrays::bool::BoolArrayExt;
473/// # use vortex_array::IntoArray;
474/// # use vortex_array::{VortexSessionExecute, array_session};
475/// # use vortex_array::validity::Validity;
476/// # use vortex_buffer::buffer;
477/// # use vortex_array::expr::{root, lit, lt};
478/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
479/// let result = xs.into_array().apply(&lt(root(), lit(3))).unwrap();
480/// let mut ctx = array_session().create_execution_ctx();
481///
482/// assert_eq!(
483///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
484///     BoolArray::from_iter(vec![true, true, false]).to_bit_buffer(),
485/// );
486/// ```
487pub fn lt(lhs: Expression, rhs: Expression) -> Expression {
488    Binary
489        .try_new_expr(Operator::Lt, [lhs, rhs])
490        .vortex_expect("Failed to create Lt binary expression")
491}
492
493/// Creates a bound less-than expression.
494pub fn bound_lt(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
495    bound_binary(Operator::Lt, lhs, rhs)
496}
497
498/// Create a new [`Binary`] using the [`Or`](Operator::Or) operator.
499///
500/// ## Example usage
501///
502/// ```
503/// # use vortex_array::arrays::BoolArray;
504/// # use vortex_array::arrays::bool::BoolArrayExt;
505/// # use vortex_array::IntoArray;
506/// # use vortex_array::{VortexSessionExecute, array_session};
507/// # use vortex_array::expr::{root, lit, or};
508/// let xs = BoolArray::from_iter(vec![true, false, true]);
509/// let result = xs.into_array().apply(&or(root(), lit(false))).unwrap();
510/// let mut ctx = array_session().create_execution_ctx();
511///
512/// assert_eq!(
513///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
514///     BoolArray::from_iter(vec![true, false, true]).to_bit_buffer(),
515/// );
516/// ```
517pub fn or(lhs: Expression, rhs: Expression) -> Expression {
518    Binary
519        .try_new_expr(Operator::Or, [lhs, rhs])
520        .vortex_expect("Failed to create Or binary expression")
521}
522
523/// Creates a bound boolean OR expression.
524pub fn bound_or(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
525    bound_binary(Operator::Or, lhs, rhs)
526}
527
528/// Collects a list of `or`ed values into a single expression using a balanced tree.
529///
530/// This creates a balanced binary tree to avoid deep nesting that could cause
531/// stack overflow during drop or evaluation.
532///
533/// [a, b, c, d] => or(or(a, b), or(c, d))
534pub fn or_collect<I>(iter: I) -> Option<Expression>
535where
536    I: IntoIterator<Item = Expression>,
537{
538    iter.into_iter().reduce_balanced(or)
539}
540
541/// Collects bound expressions into a balanced tree of boolean OR expressions.
542pub fn bound_or_collect<I>(iter: I) -> Option<BoundExpression>
543where
544    I: IntoIterator<Item = BoundExpression>,
545{
546    iter.into_iter().reduce_balanced(bound_or)
547}
548
549/// Create a new [`Binary`] using the [`And`](Operator::And) operator.
550///
551/// ## Example usage
552///
553/// ```
554/// # use vortex_array::arrays::BoolArray;
555/// # use vortex_array::arrays::bool::BoolArrayExt;
556/// # use vortex_array::IntoArray;
557/// # use vortex_array::{VortexSessionExecute, array_session};
558/// # use vortex_array::expr::{and, root, lit};
559/// let xs = BoolArray::from_iter(vec![true, false, true]).into_array();
560/// let result = xs.apply(&and(root(), lit(true))).unwrap();
561/// let mut ctx = array_session().create_execution_ctx();
562///
563/// assert_eq!(
564///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
565///     BoolArray::from_iter(vec![true, false, true]).to_bit_buffer(),
566/// );
567/// ```
568pub fn and(lhs: Expression, rhs: Expression) -> Expression {
569    Binary
570        .try_new_expr(Operator::And, [lhs, rhs])
571        .vortex_expect("Failed to create And binary expression")
572}
573
574/// Creates a bound boolean AND expression.
575pub fn bound_and(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
576    bound_binary(Operator::And, lhs, rhs)
577}
578
579/// Collects a list of `and`ed values into a single expression using a balanced tree.
580///
581/// This creates a balanced binary tree to avoid deep nesting that could cause
582/// stack overflow during drop or evaluation.
583///
584/// [a, b, c, d] => and(and(a, b), and(c, d))
585pub fn and_collect<I>(iter: I) -> Option<Expression>
586where
587    I: IntoIterator<Item = Expression>,
588{
589    iter.into_iter().reduce_balanced(and)
590}
591
592/// Collects bound expressions into a balanced tree of boolean AND expressions.
593pub fn bound_and_collect<I>(iter: I) -> Option<BoundExpression>
594where
595    I: IntoIterator<Item = BoundExpression>,
596{
597    iter.into_iter().reduce_balanced(bound_and)
598}
599
600/// The conjunction of an expression's child validities — i.e. the validity of a scalar function
601/// whose result is null exactly when any operand is null.
602///
603/// This is the `ScalarFnVTable::validity` for kernels that propagate nulls and never produce a
604/// null from non-null inputs (comparisons, arithmetic, most spatial and tensor operations). Returning it lets
605/// the planner derive the output's null mask without executing the kernel. Yields `None` when the
606/// expression has no children.
607pub fn union_child_validities(expression: &Expression) -> VortexResult<Option<Expression>> {
608    let child_validities = expression
609        .children()
610        .iter()
611        .map(Expression::validity)
612        .collect::<VortexResult<Vec<_>>>()?;
613    Ok(and_collect(child_validities))
614}
615
616/// Create a new [`Binary`] using the [`Add`](Operator::Add) operator.
617///
618/// ## Example usage
619///
620/// ```
621/// # use vortex_array::IntoArray;
622/// # use vortex_array::arrays::PrimitiveArray;
623/// # use vortex_array::builtins::ArrayBuiltins;
624/// # use vortex_array::{VortexSessionExecute, array_session};
625/// # use vortex_buffer::buffer;
626/// # use vortex_array::expr::{checked_add, lit, root};
627/// let xs = buffer![1, 2, 3].into_array();
628/// let result = xs.apply(&checked_add(root(), lit(5))).unwrap();
629///
630/// let mut ctx = array_session().create_execution_ctx();
631/// let result = result.execute::<PrimitiveArray>(&mut ctx).unwrap();
632/// assert_eq!(result.as_slice::<i32>(), [6, 7, 8]);
633/// ```
634pub fn checked_add(lhs: Expression, rhs: Expression) -> Expression {
635    Binary
636        .try_new_expr(Operator::Add, [lhs, rhs])
637        .vortex_expect("Failed to create Add binary expression")
638}
639
640/// Creates a bound checked-add expression.
641pub fn bound_checked_add(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
642    bound_binary(Operator::Add, lhs, rhs)
643}
644
645// ---- Not ----
646
647/// Creates an expression that logically inverts boolean values.
648///
649/// Returns the logical negation of the input boolean expression.
650///
651/// ```rust
652/// # use vortex_array::expr::{not, root};
653/// let expr = not(root());
654/// ```
655pub fn not(operand: Expression) -> Expression {
656    Not.new_expr(EmptyOptions, vec![operand])
657}
658
659/// Creates a bound expression that logically inverts boolean values.
660pub fn bound_not(operand: BoundExpression) -> BoundExpression {
661    Not.try_new_bound_expr(EmptyOptions, [operand])
662        .vortex_expect("not expressions require a boolean operand")
663}
664
665// ---- Between ----
666
667/// Creates an expression that checks if values are between two bounds.
668///
669/// Returns a boolean array indicating which values fall within the specified range.
670/// The comparison strictness is controlled by the options parameter.
671///
672/// ```rust
673/// # use vortex_array::scalar_fn::fns::between::BetweenOptions;
674/// # use vortex_array::scalar_fn::fns::between::StrictComparison;
675/// # use vortex_array::expr::{between, lit, root};
676/// let opts = BetweenOptions {
677///     lower_strict: StrictComparison::NonStrict,
678///     upper_strict: StrictComparison::NonStrict,
679/// };
680/// let expr = between(root(), lit(10), lit(20), opts);
681/// ```
682pub fn between(
683    arr: Expression,
684    lower: Expression,
685    upper: Expression,
686    options: BetweenOptions,
687) -> Expression {
688    Between
689        .try_new_expr(options, [arr, lower, upper])
690        .vortex_expect("Failed to create Between expression")
691}
692
693/// Creates a bound expression that checks if values are between two bounds.
694pub fn bound_between(
695    arr: BoundExpression,
696    lower: BoundExpression,
697    upper: BoundExpression,
698    options: BetweenOptions,
699) -> BoundExpression {
700    Between
701        .try_new_bound_expr(options, [arr, lower, upper])
702        .vortex_expect("between expressions require compatible operand dtypes")
703}
704
705// ---- Select ----
706
707/// Creates an expression that selects (includes) specific fields from an array.
708///
709/// Projects only the specified fields from the child expression, which must be of DType struct.
710/// ```rust
711/// # use vortex_array::expr::{select, root};
712/// let expr = select(["name", "age"], root());
713/// ```
714pub fn select(field_names: impl Into<FieldNames>, child: Expression) -> Expression {
715    Select
716        .try_new_expr(FieldSelection::Include(field_names.into()), [child])
717        .vortex_expect("Failed to create Select expression")
718}
719
720/// Creates a bound expression that selects specific fields from a struct expression.
721pub fn bound_select(field_names: impl Into<FieldNames>, child: BoundExpression) -> BoundExpression {
722    Select
723        .try_new_bound_expr(FieldSelection::Include(field_names.into()), [child])
724        .vortex_expect("select expressions require fields from a struct child")
725}
726
727/// Creates an expression that excludes specific fields from an array.
728///
729/// Projects all fields except the specified ones from the input struct expression.
730///
731/// ```rust
732/// # use vortex_array::expr::{select_exclude, root};
733/// let expr = select_exclude(["internal_id", "metadata"], root());
734/// ```
735pub fn select_exclude(fields: impl Into<FieldNames>, child: Expression) -> Expression {
736    Select
737        .try_new_expr(FieldSelection::Exclude(fields.into()), [child])
738        .vortex_expect("Failed to create Select expression")
739}
740
741/// Creates a bound expression that excludes specific fields from a struct expression.
742pub fn bound_select_exclude(
743    fields: impl Into<FieldNames>,
744    child: BoundExpression,
745) -> BoundExpression {
746    Select
747        .try_new_bound_expr(FieldSelection::Exclude(fields.into()), [child])
748        .vortex_expect("select expressions require fields from a struct child")
749}
750
751// ---- Pack ----
752
753/// Creates an expression that packs values into a struct with named fields.
754///
755/// ```rust
756/// # use vortex_array::dtype::Nullability;
757/// # use vortex_array::expr::{pack, col, lit};
758/// let expr = pack([("id", col("user_id")), ("constant", lit(42))], Nullability::NonNullable);
759/// ```
760pub fn pack(
761    elements: impl IntoIterator<Item = (impl Into<FieldName>, Expression)>,
762    nullability: Nullability,
763) -> Expression {
764    let (names, values): (Vec<_>, Vec<_>) = elements
765        .into_iter()
766        .map(|(name, value)| (name.into(), value))
767        .unzip();
768    Pack.new_expr(
769        PackOptions {
770            names: names.into(),
771            nullability,
772        },
773        values,
774    )
775}
776
777/// Creates a bound expression that packs values into a struct with named fields.
778pub fn bound_pack(
779    elements: impl IntoIterator<Item = (impl Into<FieldName>, BoundExpression)>,
780    nullability: Nullability,
781) -> BoundExpression {
782    let (names, values): (Vec<_>, Vec<_>) = elements
783        .into_iter()
784        .map(|(name, value)| (name.into(), value))
785        .unzip();
786    Pack.try_new_bound_expr(
787        PackOptions {
788            names: names.into(),
789            nullability,
790        },
791        values,
792    )
793    .vortex_expect("pack expressions must have one name per child")
794}
795
796// ---- Cast ----
797
798/// Creates an expression that casts values to a target data type.
799///
800/// Converts the input expression's values to the specified target type.
801///
802/// ```rust
803/// # use vortex_array::dtype::{DType, Nullability, PType};
804/// # use vortex_array::expr::{cast, root};
805/// let expr = cast(root(), DType::Primitive(PType::I64, Nullability::NonNullable));
806/// ```
807pub fn cast(child: Expression, target: DType) -> Expression {
808    Cast.try_new_expr(target, [child])
809        .vortex_expect("Failed to create Cast expression")
810}
811
812/// Creates a bound expression that casts values to a target dtype.
813pub fn bound_cast(child: BoundExpression, target: DType) -> BoundExpression {
814    Cast.try_new_bound_expr(target, [child])
815        .vortex_expect("cast expressions require a supported source and target dtype")
816}
817
818// ---- FillNull ----
819
820/// Creates an expression that replaces null values with a fill value.
821///
822/// ```rust
823/// # use vortex_array::expr::{fill_null, root, lit};
824/// let expr = fill_null(root(), lit(0i32));
825/// ```
826pub fn fill_null(child: Expression, fill_value: Expression) -> Expression {
827    FillNull.new_expr(EmptyOptions, [child, fill_value])
828}
829
830/// Creates a bound expression that replaces null values with a fill value.
831pub fn bound_fill_null(child: BoundExpression, fill_value: BoundExpression) -> BoundExpression {
832    FillNull
833        .try_new_bound_expr(EmptyOptions, [child, fill_value])
834        .vortex_expect("fill-null expressions require compatible child and fill dtypes")
835}
836
837// ---- IsNull ----
838
839/// Creates an expression that checks for null values.
840///
841/// Returns a boolean array indicating which positions contain null values.
842///
843/// ```rust
844/// # use vortex_array::expr::{is_null, root};
845/// let expr = is_null(root());
846/// ```
847pub fn is_null(child: Expression) -> Expression {
848    IsNull.new_expr(EmptyOptions, vec![child])
849}
850
851/// Creates a bound expression that checks for null values.
852pub fn bound_is_null(child: BoundExpression) -> BoundExpression {
853    IsNull
854        .try_new_bound_expr(EmptyOptions, [child])
855        .vortex_expect("is-null expressions are always well-typed")
856}
857
858// ---- IsNotNull ----
859
860/// Creates an expression that checks for non-null values.
861///
862/// Returns a boolean array indicating which positions contain non-null values.
863///
864/// ```rust
865/// # use vortex_array::expr::{is_not_null, root};
866/// let expr = is_not_null(root());
867/// ```
868pub fn is_not_null(child: Expression) -> Expression {
869    IsNotNull.new_expr(EmptyOptions, vec![child])
870}
871
872/// Creates a bound expression that checks for non-null values.
873pub fn bound_is_not_null(child: BoundExpression) -> BoundExpression {
874    IsNotNull
875        .try_new_bound_expr(EmptyOptions, [child])
876        .vortex_expect("is-not-null expressions are always well-typed")
877}
878
879// ---- Like ----
880
881/// Creates a SQL LIKE expression.
882pub fn like(child: Expression, pattern: Expression) -> Expression {
883    Like.new_expr(
884        LikeOptions {
885            negated: false,
886            case_insensitive: false,
887        },
888        [child, pattern],
889    )
890}
891
892/// Creates a bound SQL LIKE expression.
893pub fn bound_like(child: BoundExpression, pattern: BoundExpression) -> BoundExpression {
894    bound_like_with_options(child, pattern, false, false)
895}
896
897/// Creates a case-insensitive SQL ILIKE expression.
898pub fn ilike(child: Expression, pattern: Expression) -> Expression {
899    Like.new_expr(
900        LikeOptions {
901            negated: false,
902            case_insensitive: true,
903        },
904        [child, pattern],
905    )
906}
907
908/// Creates a bound case-insensitive SQL ILIKE expression.
909pub fn bound_ilike(child: BoundExpression, pattern: BoundExpression) -> BoundExpression {
910    bound_like_with_options(child, pattern, false, true)
911}
912
913/// Creates a negated SQL NOT LIKE expression.
914pub fn not_like(child: Expression, pattern: Expression) -> Expression {
915    Like.new_expr(
916        LikeOptions {
917            negated: true,
918            case_insensitive: false,
919        },
920        [child, pattern],
921    )
922}
923
924/// Creates a bound negated SQL NOT LIKE expression.
925pub fn bound_not_like(child: BoundExpression, pattern: BoundExpression) -> BoundExpression {
926    bound_like_with_options(child, pattern, true, false)
927}
928
929/// Creates a negated case-insensitive SQL NOT ILIKE expression.
930pub fn not_ilike(child: Expression, pattern: Expression) -> Expression {
931    Like.new_expr(
932        LikeOptions {
933            negated: true,
934            case_insensitive: true,
935        },
936        [child, pattern],
937    )
938}
939
940/// Creates a bound negated case-insensitive SQL NOT ILIKE expression.
941pub fn bound_not_ilike(child: BoundExpression, pattern: BoundExpression) -> BoundExpression {
942    bound_like_with_options(child, pattern, true, true)
943}
944
945fn bound_like_with_options(
946    child: BoundExpression,
947    pattern: BoundExpression,
948    negated: bool,
949    case_insensitive: bool,
950) -> BoundExpression {
951    Like.try_new_bound_expr(
952        LikeOptions {
953            negated,
954            case_insensitive,
955        },
956        [child, pattern],
957    )
958    .vortex_expect("like expressions require UTF-8 or binary operands")
959}
960
961// ---- Mask ----
962
963/// Creates a mask expression that applies the given boolean mask to the input array.
964pub fn mask(array: Expression, mask: Expression) -> Expression {
965    Mask.new_expr(EmptyOptions, [array, mask])
966}
967
968/// Creates a bound mask expression.
969pub fn bound_mask(array: BoundExpression, mask: BoundExpression) -> BoundExpression {
970    Mask.try_new_bound_expr(EmptyOptions, [array, mask])
971        .vortex_expect("mask expressions require a boolean mask")
972}
973
974// ---- Merge ----
975
976/// Creates an expression that merges struct expressions into a single struct.
977///
978/// Combines fields from all input expressions. If field names are duplicated,
979/// later expressions win. Fields are not recursively merged.
980///
981/// ```rust
982/// # use vortex_array::dtype::Nullability;
983/// # use vortex_array::expr::{merge, get_item, root};
984/// let expr = merge([get_item("a", root()), get_item("b", root())]);
985/// ```
986pub fn merge(elements: impl IntoIterator<Item = impl Into<Expression>>) -> Expression {
987    use itertools::Itertools as _;
988    let values = elements.into_iter().map(|value| value.into()).collect_vec();
989    Merge.new_expr(DuplicateHandling::default(), values)
990}
991
992/// Creates a bound expression that merges struct expressions.
993pub fn bound_merge(elements: impl IntoIterator<Item = BoundExpression>) -> BoundExpression {
994    bound_merge_opts(elements, DuplicateHandling::default())
995}
996
997/// Creates a merge expression with explicit duplicate handling.
998pub fn merge_opts(
999    elements: impl IntoIterator<Item = impl Into<Expression>>,
1000    duplicate_handling: DuplicateHandling,
1001) -> Expression {
1002    use itertools::Itertools as _;
1003    let values = elements.into_iter().map(|value| value.into()).collect_vec();
1004    Merge.new_expr(duplicate_handling, values)
1005}
1006
1007/// Creates a bound merge expression with explicit duplicate handling.
1008pub fn bound_merge_opts(
1009    elements: impl IntoIterator<Item = BoundExpression>,
1010    duplicate_handling: DuplicateHandling,
1011) -> BoundExpression {
1012    Merge
1013        .try_new_bound_expr(duplicate_handling, elements)
1014        .vortex_expect("merge expressions require non-nullable struct children")
1015}
1016
1017// ---- Zip ----
1018
1019/// Creates a zip expression that conditionally selects between two arrays.
1020///
1021/// ```rust
1022/// # use vortex_array::expr::{zip_expr, root, lit};
1023/// let expr = zip_expr(lit(true), root(), lit(0i32));
1024/// ```
1025pub fn zip_expr(mask: Expression, if_true: Expression, if_false: Expression) -> Expression {
1026    Zip.new_expr(EmptyOptions, [if_true, if_false, mask])
1027}
1028
1029/// Creates a bound zip expression that conditionally selects between two arrays.
1030pub fn bound_zip_expr(
1031    mask: BoundExpression,
1032    if_true: BoundExpression,
1033    if_false: BoundExpression,
1034) -> BoundExpression {
1035    Zip.try_new_bound_expr(EmptyOptions, [if_true, if_false, mask])
1036        .vortex_expect("zip expressions require a boolean mask and compatible value dtypes")
1037}
1038
1039// ---- Dynamic ----
1040
1041/// Creates a dynamic comparison expression from its complete options.
1042pub fn dynamic_with_options(options: DynamicComparisonExpr, lhs: Expression) -> Expression {
1043    DynamicComparison.new_expr(options, [lhs])
1044}
1045
1046/// Creates a bound dynamic comparison expression from its complete options.
1047pub fn bound_dynamic_with_options(
1048    options: DynamicComparisonExpr,
1049    lhs: BoundExpression,
1050) -> BoundExpression {
1051    DynamicComparison
1052        .try_new_bound_expr(options, [lhs])
1053        .vortex_expect("dynamic comparisons require a compatible left-hand dtype")
1054}
1055
1056/// Creates a dynamic comparison expression.
1057pub fn dynamic(
1058    operator: CompareOperator,
1059    rhs_value: impl Fn() -> Option<ScalarValue> + Send + Sync + 'static,
1060    rhs_dtype: DType,
1061    default: bool,
1062    lhs: Expression,
1063) -> Expression {
1064    dynamic_with_options(
1065        DynamicComparisonExpr {
1066            operator,
1067            rhs: Arc::new(Rhs {
1068                value: Arc::new(rhs_value),
1069                dtype: rhs_dtype,
1070            }),
1071            default,
1072        },
1073        lhs,
1074    )
1075}
1076
1077/// Creates a bound dynamic comparison expression.
1078pub fn bound_dynamic(
1079    operator: CompareOperator,
1080    rhs_value: impl Fn() -> Option<ScalarValue> + Send + Sync + 'static,
1081    rhs_dtype: DType,
1082    default: bool,
1083    lhs: BoundExpression,
1084) -> BoundExpression {
1085    bound_dynamic_with_options(
1086        DynamicComparisonExpr {
1087            operator,
1088            rhs: Arc::new(Rhs {
1089                value: Arc::new(rhs_value),
1090                dtype: rhs_dtype,
1091            }),
1092            default,
1093        },
1094        lhs,
1095    )
1096}
1097
1098// ---- ListContains ----
1099
1100/// Creates an expression that checks if a value is contained in a list.
1101///
1102/// Returns a boolean array indicating whether the value appears in each list.
1103///
1104/// ```rust
1105/// # use vortex_array::expr::{list_contains, lit, root};
1106/// let expr = list_contains(root(), lit(42));
1107/// ```
1108pub fn list_contains(list: Expression, value: Expression) -> Expression {
1109    ListContains.new_expr(EmptyOptions, [list, value])
1110}
1111
1112/// Creates a bound expression that checks if a value is contained in a list.
1113pub fn bound_list_contains(list: BoundExpression, value: BoundExpression) -> BoundExpression {
1114    ListContains
1115        .try_new_bound_expr(EmptyOptions, [list, value])
1116        .vortex_expect("list-contains expressions require a compatible list and value dtype")
1117}
1118
1119// ---- ByteLength ----
1120
1121/// Creates an expression that computes the byte length of each element.
1122/// This is akin to ANSI SQL OCTET_LENGTH(), or DuckDB's strlen().
1123///
1124/// ```rust
1125/// # use vortex_array::expr::{byte_length, root};
1126/// let expr = byte_length(root());
1127/// ```
1128pub fn byte_length(input: Expression) -> Expression {
1129    ByteLength.new_expr(EmptyOptions, [input])
1130}
1131
1132/// Creates a bound expression that computes each element's byte length.
1133pub fn bound_byte_length(input: BoundExpression) -> BoundExpression {
1134    ByteLength
1135        .try_new_bound_expr(EmptyOptions, [input])
1136        .vortex_expect("byte-length expressions require a variable-length binary child")
1137}
1138
1139// ---- ExtStorage ----
1140
1141/// Creates an expression that extracts the storage values from an extension array.
1142///
1143/// ```rust
1144/// # use vortex_array::expr::{ext_storage, root};
1145/// let expr = ext_storage(root());
1146/// ```
1147pub fn ext_storage(input: Expression) -> Expression {
1148    ExtStorage.new_expr(EmptyOptions, [input])
1149}
1150
1151/// Creates a bound expression that extracts an extension array's storage values.
1152pub fn bound_ext_storage(input: BoundExpression) -> BoundExpression {
1153    ExtStorage
1154        .try_new_bound_expr(EmptyOptions, [input])
1155        .vortex_expect("extension-storage expressions require an extension child")
1156}
1157
1158// ---- ListLength ----
1159
1160/// Creates an expression that computes the number of elements in each list
1161/// for `List` and `FixedSizeList` inputs. This is akin to ANSI SQL `CARDINALITY()`,
1162/// or DuckDB's `len()`/`array_length()`.
1163///
1164/// ```rust
1165/// # use vortex_array::expr::{list_length, root};
1166/// let expr = list_length(root());
1167/// ```
1168pub fn list_length(input: Expression) -> Expression {
1169    ListLength.new_expr(EmptyOptions, [input])
1170}
1171
1172/// Creates a bound expression that computes the number of elements in each list.
1173pub fn bound_list_length(input: BoundExpression) -> BoundExpression {
1174    ListLength
1175        .try_new_bound_expr(EmptyOptions, [input])
1176        .vortex_expect("list-length expressions require a list child")
1177}
1178
1179// ---- ListSum ----
1180
1181/// Creates an expression that sums the elements of each list for `List` and
1182/// `FixedSizeList` inputs, akin to DuckDB's `list_sum()`.
1183///
1184/// Follows SQL `SUM` semantics per list: null lists, empty lists, and lists whose elements are
1185/// all null yield null; null elements are skipped; integer and decimal overflow yields a null
1186/// value. The result dtype follows `sum`'s widening rules and is always nullable. NaN float
1187/// elements are skipped by default; see [`list_sum_opts`] for the NaN-including variant.
1188///
1189/// ```rust
1190/// # use vortex_array::expr::{list_sum, root};
1191/// let expr = list_sum(root());
1192/// ```
1193pub fn list_sum(input: Expression) -> Expression {
1194    ListSum.new_expr(NumericalAggregateOpts::default(), [input])
1195}
1196
1197/// Creates a bound expression that sums the elements of each list.
1198pub fn bound_list_sum(input: BoundExpression) -> BoundExpression {
1199    ListSum
1200        .try_new_bound_expr(NumericalAggregateOpts::default(), [input])
1201        .vortex_expect("list-sum expressions require a numeric list child")
1202}
1203
1204/// Creates a [`list_sum`] expression with explicit [`NumericalAggregateOpts`], controlling
1205/// whether NaN float elements are skipped (the default) or poison the list's sum to NaN.
1206pub fn list_sum_opts(input: Expression, options: NumericalAggregateOpts) -> Expression {
1207    ListSum.new_expr(options, [input])
1208}
1209
1210/// Creates a bound list-sum expression with explicit aggregate options.
1211pub fn bound_list_sum_opts(
1212    input: BoundExpression,
1213    options: NumericalAggregateOpts,
1214) -> BoundExpression {
1215    ListSum
1216        .try_new_bound_expr(options, [input])
1217        .vortex_expect("list-sum expressions require a numeric list child")
1218}
1219
1220/// Constructors for expressions whose children have already been bound and type-checked.
1221///
1222/// These mirror the constructors in [`crate::expr`] and panic when the supplied children do not
1223/// form a well-typed expression. Use [`BoundExpression::try_new`] when construction must be
1224/// fallible.
1225pub mod bound {
1226    pub use super::bound_and as and;
1227    pub use super::bound_and_collect as and_collect;
1228    pub use super::bound_between as between;
1229    pub use super::bound_binary as binary;
1230    pub use super::bound_byte_length as byte_length;
1231    pub use super::bound_case_when as case_when;
1232    pub use super::bound_case_when_no_else as case_when_no_else;
1233    pub use super::bound_cast as cast;
1234    pub use super::bound_checked_add as checked_add;
1235    pub use super::bound_col as col;
1236    pub use super::bound_dynamic as dynamic;
1237    pub use super::bound_dynamic_with_options as dynamic_with_options;
1238    pub use super::bound_eq as eq;
1239    pub use super::bound_ext_storage as ext_storage;
1240    pub use super::bound_fill_null as fill_null;
1241    pub use super::bound_get_item as get_item;
1242    pub use super::bound_gt as gt;
1243    pub use super::bound_gt_eq as gt_eq;
1244    pub use super::bound_ilike as ilike;
1245    pub use super::bound_is_not_null as is_not_null;
1246    pub use super::bound_is_null as is_null;
1247    pub use super::bound_like as like;
1248    pub use super::bound_list_contains as list_contains;
1249    pub use super::bound_list_length as list_length;
1250    pub use super::bound_list_sum as list_sum;
1251    pub use super::bound_list_sum_opts as list_sum_opts;
1252    pub use super::bound_lit as lit;
1253    pub use super::bound_lt as lt;
1254    pub use super::bound_lt_eq as lt_eq;
1255    pub use super::bound_mask as mask;
1256    pub use super::bound_merge as merge;
1257    pub use super::bound_merge_opts as merge_opts;
1258    pub use super::bound_nested_case_when as nested_case_when;
1259    pub use super::bound_not as not;
1260    pub use super::bound_not_eq as not_eq;
1261    pub use super::bound_not_ilike as not_ilike;
1262    pub use super::bound_not_like as not_like;
1263    pub use super::bound_or as or;
1264    pub use super::bound_or_collect as or_collect;
1265    pub use super::bound_pack as pack;
1266    pub use super::bound_root as root;
1267    pub use super::bound_select as select;
1268    pub use super::bound_select_exclude as select_exclude;
1269    pub use super::bound_variant_get as variant_get;
1270    pub use super::bound_zip_expr as zip_expr;
1271}