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