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::vortex_panic;
10use vortex_utils::iter::ReduceBalancedIterExt;
11
12use crate::aggregate_fn::NumericalAggregateOpts;
13use crate::dtype::DType;
14use crate::dtype::FieldName;
15use crate::dtype::FieldNames;
16use crate::dtype::Nullability;
17use crate::expr::BoundExpression;
18use crate::expr::Expression;
19use crate::scalar::Scalar;
20use crate::scalar::ScalarValue;
21use crate::scalar_fn::EmptyOptions;
22use crate::scalar_fn::ScalarFnVTableExt;
23use crate::scalar_fn::fns::between::Between;
24use crate::scalar_fn::fns::between::BetweenOptions;
25use crate::scalar_fn::fns::binary::Binary;
26use crate::scalar_fn::fns::byte_length::ByteLength;
27use crate::scalar_fn::fns::case_when::CaseWhen;
28use crate::scalar_fn::fns::case_when::CaseWhenOptions;
29use crate::scalar_fn::fns::cast::Cast;
30use crate::scalar_fn::fns::dynamic::DynamicComparison;
31use crate::scalar_fn::fns::dynamic::DynamicComparisonExpr;
32use crate::scalar_fn::fns::dynamic::Rhs;
33use crate::scalar_fn::fns::ext_storage::ExtStorage;
34use crate::scalar_fn::fns::fill_null::FillNull;
35use crate::scalar_fn::fns::get_item::GetItem;
36use crate::scalar_fn::fns::is_nan::IsNan;
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_contains::ListContainsOptions;
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::replace_time_zone::ReplaceTimeZone;
55use crate::scalar_fn::fns::replace_time_zone::ReplaceTimeZoneOptions;
56use crate::scalar_fn::fns::select::FieldSelection;
57use crate::scalar_fn::fns::select::Select;
58use crate::scalar_fn::fns::variant_get::VariantGet;
59use crate::scalar_fn::fns::variant_get::VariantGetOptions;
60use crate::scalar_fn::fns::variant_get::VariantPath;
61use crate::scalar_fn::fns::zip::Zip;
62
63/// Creates an expression that references the root scope.
64///
65/// Returns the entire input array as passed to the expression evaluator.
66/// This is commonly used as the starting point for field access and other operations.
67pub fn root() -> Expression {
68    Expression::Root
69}
70
71/// Creates a bound expression that references a root scope with the given dtype.
72pub fn bound_root(dtype: DType) -> BoundExpression {
73    BoundExpression::new_root(dtype)
74}
75
76/// Return whether the expression is a root expression.
77pub fn is_root(expr: &Expression) -> bool {
78    expr.is_root()
79}
80
81// ---- Literal ----
82
83/// Create a new `Literal` expression from a type that coerces to `Scalar`.
84///
85///
86/// ## Example usage
87///
88/// ```
89/// use vortex_array::arrays::PrimitiveArray;
90/// use vortex_array::dtype::Nullability;
91/// use vortex_array::expr::lit;
92/// use vortex_array::scalar_fn::fns::literal::Literal;
93/// use vortex_array::scalar::Scalar;
94///
95/// let number = lit(34i32);
96///
97/// let scalar = number.as_::<Literal>();
98/// assert_eq!(scalar, &Scalar::primitive(34i32, Nullability::NonNullable));
99/// ```
100pub fn lit(value: impl Into<Scalar>) -> Expression {
101    Literal.new_expr(value.into(), [])
102}
103
104/// Creates a bound literal expression.
105pub fn bound_lit(value: impl Into<Scalar>) -> BoundExpression {
106    Literal
107        .try_new_bound_expr(value.into(), [])
108        .vortex_expect("literal expressions are always well-typed")
109}
110
111// ---- GetItem / Col ----
112
113/// Creates an expression that accesses a field from the root array.
114///
115/// Equivalent to `get_item(field, root())` - extracts a named field from the input array.
116///
117/// ```rust
118/// # use vortex_array::expr::col;
119/// let expr = col("name");
120/// ```
121pub fn col(field: impl Into<FieldName>) -> Expression {
122    GetItem.new_expr(field.into(), vec![root()])
123}
124
125/// Creates a bound expression that accesses a field from a root scope with the given dtype.
126pub fn bound_col(field: impl Into<FieldName>, scope: DType) -> BoundExpression {
127    bound_get_item(field, bound_root(scope))
128}
129
130/// Creates an expression that extracts a named field from a struct expression.
131///
132/// Accesses the specified field from the result of the child expression.
133///
134/// ```rust
135/// # use vortex_array::expr::{get_item, root};
136/// let expr = get_item("user_id", root());
137/// ```
138pub fn get_item(field: impl Into<FieldName>, child: Expression) -> Expression {
139    GetItem.new_expr(field.into(), vec![child])
140}
141
142/// Creates a bound expression that extracts a named field from a struct expression.
143pub fn bound_get_item(field: impl Into<FieldName>, child: BoundExpression) -> BoundExpression {
144    GetItem
145        .try_new_bound_expr(field.into(), [child])
146        .vortex_expect("get-item expressions must reference a field in the child dtype")
147}
148
149// ---- VariantGet ----
150
151/// Creates an expression that extracts a path from a Variant expression.
152///
153/// Missing paths, traversal mismatches, and failed casts return null. When `dtype` is `None`,
154/// results are nullable Variant values; otherwise results are nullable values of `dtype`.
155pub fn variant_get(
156    child: Expression,
157    path: impl Into<VariantPath>,
158    dtype: Option<DType>,
159) -> Expression {
160    VariantGet.new_expr(VariantGetOptions::new(path.into(), dtype), vec![child])
161}
162
163/// Creates a bound expression that extracts a path from a Variant expression.
164pub fn bound_variant_get(
165    child: BoundExpression,
166    path: impl Into<VariantPath>,
167    dtype: Option<DType>,
168) -> BoundExpression {
169    VariantGet
170        .try_new_bound_expr(VariantGetOptions::new(path.into(), dtype), [child])
171        .vortex_expect("variant-get expressions require a Variant child")
172}
173
174// ---- CaseWhen ----
175
176/// Creates a CASE WHEN expression with one WHEN/THEN pair and an ELSE value.
177pub fn case_when(
178    condition: Expression,
179    then_value: Expression,
180    else_value: Expression,
181) -> Expression {
182    let options = CaseWhenOptions {
183        num_when_then_pairs: 1,
184        has_else: true,
185    };
186    CaseWhen.new_expr(options, [condition, then_value, else_value])
187}
188
189/// Creates a bound CASE WHEN expression with one WHEN/THEN pair and an ELSE value.
190pub fn bound_case_when(
191    condition: BoundExpression,
192    then_value: BoundExpression,
193    else_value: BoundExpression,
194) -> BoundExpression {
195    let options = CaseWhenOptions {
196        num_when_then_pairs: 1,
197        has_else: true,
198    };
199    CaseWhen
200        .try_new_bound_expr(options, [condition, then_value, else_value])
201        .vortex_expect("case expressions must have boolean conditions and matching branch dtypes")
202}
203
204/// Creates a CASE WHEN expression with one WHEN/THEN pair and no ELSE value.
205pub fn case_when_no_else(condition: Expression, then_value: Expression) -> Expression {
206    let options = CaseWhenOptions {
207        num_when_then_pairs: 1,
208        has_else: false,
209    };
210    CaseWhen.new_expr(options, [condition, then_value])
211}
212
213/// Creates a bound CASE WHEN expression with one WHEN/THEN pair and no ELSE value.
214pub fn bound_case_when_no_else(
215    condition: BoundExpression,
216    then_value: BoundExpression,
217) -> BoundExpression {
218    let options = CaseWhenOptions {
219        num_when_then_pairs: 1,
220        has_else: false,
221    };
222    CaseWhen
223        .try_new_bound_expr(options, [condition, then_value])
224        .vortex_expect("case expressions must have boolean conditions")
225}
226
227/// Creates an n-ary CASE WHEN expression from WHEN/THEN pairs and an optional ELSE value.
228pub fn nested_case_when(
229    when_then_pairs: Vec<(Expression, Expression)>,
230    else_value: Option<Expression>,
231) -> Expression {
232    assert!(
233        !when_then_pairs.is_empty(),
234        "nested_case_when requires at least one when/then pair"
235    );
236
237    let has_else = else_value.is_some();
238    let mut children = Vec::with_capacity(when_then_pairs.len() * 2 + usize::from(has_else));
239    for (condition, then_value) in &when_then_pairs {
240        children.push(condition.clone());
241        children.push(then_value.clone());
242    }
243    if let Some(else_expr) = else_value {
244        children.push(else_expr);
245    }
246
247    let Ok(num_when_then_pairs) = u32::try_from(when_then_pairs.len()) else {
248        vortex_panic!("nested_case_when has too many when/then pairs");
249    };
250    let options = CaseWhenOptions {
251        num_when_then_pairs,
252        has_else,
253    };
254    CaseWhen.new_expr(options, children)
255}
256
257/// Creates a bound n-ary CASE WHEN expression from WHEN/THEN pairs and an optional ELSE value.
258pub fn bound_nested_case_when(
259    when_then_pairs: Vec<(BoundExpression, BoundExpression)>,
260    else_value: Option<BoundExpression>,
261) -> BoundExpression {
262    assert!(
263        !when_then_pairs.is_empty(),
264        "nested_case_when requires at least one when/then pair"
265    );
266
267    let Ok(num_when_then_pairs) = u32::try_from(when_then_pairs.len()) else {
268        vortex_panic!("nested_case_when has too many when/then pairs");
269    };
270    let has_else = else_value.is_some();
271    let mut children = Vec::with_capacity(when_then_pairs.len() * 2 + usize::from(has_else));
272    for (condition, then_value) in when_then_pairs {
273        children.push(condition);
274        children.push(then_value);
275    }
276    if let Some(else_expr) = else_value {
277        children.push(else_expr);
278    }
279
280    let options = CaseWhenOptions {
281        num_when_then_pairs,
282        has_else,
283    };
284    CaseWhen
285        .try_new_bound_expr(options, children)
286        .vortex_expect("case expressions must have boolean conditions and matching branch dtypes")
287}
288
289// ---- Binary operators ----
290
291/// Creates a binary expression with the given operator.
292pub fn binary(operator: Operator, lhs: Expression, rhs: Expression) -> Expression {
293    Binary
294        .try_new_expr(operator, [lhs, rhs])
295        .vortex_expect("Failed to create binary expression")
296}
297
298/// Creates a bound binary expression with the given operator.
299pub fn bound_binary(
300    operator: Operator,
301    lhs: BoundExpression,
302    rhs: BoundExpression,
303) -> BoundExpression {
304    Binary
305        .try_new_bound_expr(operator, [lhs, rhs])
306        .vortex_expect("binary expressions must have compatible operand dtypes")
307}
308
309/// Create a new [`Binary`] using the [`Eq`](Operator::Eq) operator.
310///
311/// ## Example usage
312///
313/// ```
314/// # use vortex_array::arrays::{BoolArray, PrimitiveArray};
315/// # use vortex_array::arrays::bool::BoolArrayExt;
316/// # use vortex_array::IntoArray;
317/// # use vortex_array::{VortexSessionExecute, array_session};
318/// # use vortex_array::validity::Validity;
319/// # use vortex_buffer::buffer;
320/// # use vortex_array::expr::{eq, root, lit};
321/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
322/// let result = xs.into_array().apply(&eq(root(), lit(3))).unwrap();
323/// let mut ctx = array_session().create_execution_ctx();
324///
325/// assert_eq!(
326///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
327///     BoolArray::from_iter(vec![false, false, true]).to_bit_buffer(),
328/// );
329/// ```
330pub fn eq(lhs: Expression, rhs: Expression) -> Expression {
331    Binary
332        .try_new_expr(Operator::Eq, [lhs, rhs])
333        .vortex_expect("Failed to create Eq binary expression")
334}
335
336/// Creates a bound equality expression.
337pub fn bound_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
338    bound_binary(Operator::Eq, lhs, rhs)
339}
340
341/// Create a new [`Binary`] using the [`NotEq`](Operator::NotEq) operator.
342///
343/// ## Example usage
344///
345/// ```
346/// # use vortex_array::arrays::{BoolArray, PrimitiveArray};
347/// # use vortex_array::arrays::bool::BoolArrayExt;
348/// # use vortex_array::IntoArray;
349/// # use vortex_array::{VortexSessionExecute, array_session};
350/// # use vortex_array::validity::Validity;
351/// # use vortex_buffer::buffer;
352/// # use vortex_array::expr::{root, lit, not_eq};
353/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
354/// let result = xs.into_array().apply(&not_eq(root(), lit(3))).unwrap();
355/// let mut ctx = array_session().create_execution_ctx();
356///
357/// assert_eq!(
358///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
359///     BoolArray::from_iter(vec![true, true, false]).to_bit_buffer(),
360/// );
361/// ```
362pub fn not_eq(lhs: Expression, rhs: Expression) -> Expression {
363    Binary
364        .try_new_expr(Operator::NotEq, [lhs, rhs])
365        .vortex_expect("Failed to create NotEq binary expression")
366}
367
368/// Creates a bound inequality expression.
369pub fn bound_not_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
370    bound_binary(Operator::NotEq, lhs, rhs)
371}
372
373/// Create a new [`Binary`] using the [`Gte`](Operator::Gte) operator.
374///
375/// ## Example usage
376///
377/// ```
378/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
379/// # use vortex_array::arrays::bool::BoolArrayExt;
380/// # use vortex_array::IntoArray;
381/// # use vortex_array::{VortexSessionExecute, array_session};
382/// # use vortex_array::validity::Validity;
383/// # use vortex_buffer::buffer;
384/// # use vortex_array::expr::{gt_eq, root, lit};
385/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
386/// let result = xs.into_array().apply(&gt_eq(root(), lit(3))).unwrap();
387/// let mut ctx = array_session().create_execution_ctx();
388///
389/// assert_eq!(
390///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
391///     BoolArray::from_iter(vec![false, false, true]).to_bit_buffer(),
392/// );
393/// ```
394pub fn gt_eq(lhs: Expression, rhs: Expression) -> Expression {
395    Binary
396        .try_new_expr(Operator::Gte, [lhs, rhs])
397        .vortex_expect("Failed to create Gte binary expression")
398}
399
400/// Creates a bound greater-than-or-equal expression.
401pub fn bound_gt_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
402    bound_binary(Operator::Gte, lhs, rhs)
403}
404
405/// Create a new [`Binary`] using the [`Gt`](Operator::Gt) operator.
406///
407/// ## Example usage
408///
409/// ```
410/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
411/// # use vortex_array::arrays::bool::BoolArrayExt;
412/// # use vortex_array::IntoArray;
413/// # use vortex_array::{VortexSessionExecute, array_session};
414/// # use vortex_array::validity::Validity;
415/// # use vortex_buffer::buffer;
416/// # use vortex_array::expr::{gt, root, lit};
417/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
418/// let result = xs.into_array().apply(&gt(root(), lit(2))).unwrap();
419/// let mut ctx = array_session().create_execution_ctx();
420///
421/// assert_eq!(
422///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
423///     BoolArray::from_iter(vec![false, false, true]).to_bit_buffer(),
424/// );
425/// ```
426pub fn gt(lhs: Expression, rhs: Expression) -> Expression {
427    Binary
428        .try_new_expr(Operator::Gt, [lhs, rhs])
429        .vortex_expect("Failed to create Gt binary expression")
430}
431
432/// Creates a bound greater-than expression.
433pub fn bound_gt(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
434    bound_binary(Operator::Gt, lhs, rhs)
435}
436
437/// Create a new [`Binary`] using the [`Lte`](Operator::Lte) operator.
438///
439/// ## Example usage
440///
441/// ```
442/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
443/// # use vortex_array::arrays::bool::BoolArrayExt;
444/// # use vortex_array::IntoArray;
445/// # use vortex_array::{VortexSessionExecute, array_session};
446/// # use vortex_array::validity::Validity;
447/// # use vortex_buffer::buffer;
448/// # use vortex_array::expr::{root, lit, lt_eq};
449/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
450/// let result = xs.into_array().apply(&lt_eq(root(), lit(2))).unwrap();
451/// let mut ctx = array_session().create_execution_ctx();
452///
453/// assert_eq!(
454///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
455///     BoolArray::from_iter(vec![true, true, false]).to_bit_buffer(),
456/// );
457/// ```
458pub fn lt_eq(lhs: Expression, rhs: Expression) -> Expression {
459    Binary
460        .try_new_expr(Operator::Lte, [lhs, rhs])
461        .vortex_expect("Failed to create Lte binary expression")
462}
463
464/// Creates a bound less-than-or-equal expression.
465pub fn bound_lt_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
466    bound_binary(Operator::Lte, lhs, rhs)
467}
468
469/// Create a new [`Binary`] using the [`Lt`](Operator::Lt) operator.
470///
471/// ## Example usage
472///
473/// ```
474/// # use vortex_array::arrays::{BoolArray, PrimitiveArray };
475/// # use vortex_array::arrays::bool::BoolArrayExt;
476/// # use vortex_array::IntoArray;
477/// # use vortex_array::{VortexSessionExecute, array_session};
478/// # use vortex_array::validity::Validity;
479/// # use vortex_buffer::buffer;
480/// # use vortex_array::expr::{root, lit, lt};
481/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
482/// let result = xs.into_array().apply(&lt(root(), lit(3))).unwrap();
483/// let mut ctx = array_session().create_execution_ctx();
484///
485/// assert_eq!(
486///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
487///     BoolArray::from_iter(vec![true, true, false]).to_bit_buffer(),
488/// );
489/// ```
490pub fn lt(lhs: Expression, rhs: Expression) -> Expression {
491    Binary
492        .try_new_expr(Operator::Lt, [lhs, rhs])
493        .vortex_expect("Failed to create Lt binary expression")
494}
495
496/// Creates a bound less-than expression.
497pub fn bound_lt(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
498    bound_binary(Operator::Lt, lhs, rhs)
499}
500
501/// Create a new [`Binary`] using the [`Or`](Operator::Or) operator.
502///
503/// ## Example usage
504///
505/// ```
506/// # use vortex_array::arrays::BoolArray;
507/// # use vortex_array::arrays::bool::BoolArrayExt;
508/// # use vortex_array::IntoArray;
509/// # use vortex_array::{VortexSessionExecute, array_session};
510/// # use vortex_array::expr::{root, lit, or};
511/// let xs = BoolArray::from_iter(vec![true, false, true]);
512/// let result = xs.into_array().apply(&or(root(), lit(false))).unwrap();
513/// let mut ctx = array_session().create_execution_ctx();
514///
515/// assert_eq!(
516///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
517///     BoolArray::from_iter(vec![true, false, true]).to_bit_buffer(),
518/// );
519/// ```
520pub fn or(lhs: Expression, rhs: Expression) -> Expression {
521    Binary
522        .try_new_expr(Operator::Or, [lhs, rhs])
523        .vortex_expect("Failed to create Or binary expression")
524}
525
526/// Creates a bound boolean OR expression.
527pub fn bound_or(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
528    bound_binary(Operator::Or, lhs, rhs)
529}
530
531/// Collects a list of `or`ed values into a single expression using a balanced tree.
532///
533/// This creates a balanced binary tree to avoid deep nesting that could cause
534/// stack overflow during drop or evaluation.
535///
536/// [a, b, c, d] => or(or(a, b), or(c, d))
537pub fn or_collect<I>(iter: I) -> Option<Expression>
538where
539    I: IntoIterator<Item = Expression>,
540{
541    iter.into_iter().reduce_balanced(or)
542}
543
544/// Collects bound expressions into a balanced tree of boolean OR expressions.
545pub fn bound_or_collect<I>(iter: I) -> Option<BoundExpression>
546where
547    I: IntoIterator<Item = BoundExpression>,
548{
549    iter.into_iter().reduce_balanced(bound_or)
550}
551
552/// Create a new [`Binary`] using the [`And`](Operator::And) operator.
553///
554/// ## Example usage
555///
556/// ```
557/// # use vortex_array::arrays::BoolArray;
558/// # use vortex_array::arrays::bool::BoolArrayExt;
559/// # use vortex_array::IntoArray;
560/// # use vortex_array::{VortexSessionExecute, array_session};
561/// # use vortex_array::expr::{and, root, lit};
562/// let xs = BoolArray::from_iter(vec![true, false, true]).into_array();
563/// let result = xs.apply(&and(root(), lit(true))).unwrap();
564/// let mut ctx = array_session().create_execution_ctx();
565///
566/// assert_eq!(
567///     result.execute::<BoolArray>(&mut ctx).unwrap().to_bit_buffer(),
568///     BoolArray::from_iter(vec![true, false, true]).to_bit_buffer(),
569/// );
570/// ```
571pub fn and(lhs: Expression, rhs: Expression) -> Expression {
572    Binary
573        .try_new_expr(Operator::And, [lhs, rhs])
574        .vortex_expect("Failed to create And binary expression")
575}
576
577/// Creates a bound boolean AND expression.
578pub fn bound_and(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
579    bound_binary(Operator::And, lhs, rhs)
580}
581
582/// Collects a list of `and`ed values into a single expression using a balanced tree.
583///
584/// This creates a balanced binary tree to avoid deep nesting that could cause
585/// stack overflow during drop or evaluation.
586///
587/// [a, b, c, d] => and(and(a, b), and(c, d))
588pub fn and_collect<I>(iter: I) -> Option<Expression>
589where
590    I: IntoIterator<Item = Expression>,
591{
592    iter.into_iter().reduce_balanced(and)
593}
594
595/// Collects bound expressions into a balanced tree of boolean AND expressions.
596pub fn bound_and_collect<I>(iter: I) -> Option<BoundExpression>
597where
598    I: IntoIterator<Item = BoundExpression>,
599{
600    iter.into_iter().reduce_balanced(bound_and)
601}
602
603/// Create a new [`Binary`] using the [`Add`](Operator::Add) operator.
604///
605/// ## Example usage
606///
607/// ```
608/// # use vortex_array::IntoArray;
609/// # use vortex_array::arrays::PrimitiveArray;
610/// # use vortex_array::builtins::ArrayBuiltins;
611/// # use vortex_array::{VortexSessionExecute, array_session};
612/// # use vortex_buffer::buffer;
613/// # use vortex_array::expr::{checked_add, lit, root};
614/// let xs = buffer![1, 2, 3].into_array();
615/// let result = xs.apply(&checked_add(root(), lit(5))).unwrap();
616///
617/// let mut ctx = array_session().create_execution_ctx();
618/// let result = result.execute::<PrimitiveArray>(&mut ctx).unwrap();
619/// assert_eq!(result.as_slice::<i32>(), [6, 7, 8]);
620/// ```
621pub fn checked_add(lhs: Expression, rhs: Expression) -> Expression {
622    Binary
623        .try_new_expr(Operator::Add, [lhs, rhs])
624        .vortex_expect("Failed to create Add binary expression")
625}
626
627/// Creates a bound checked-add expression.
628pub fn bound_checked_add(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
629    bound_binary(Operator::Add, lhs, rhs)
630}
631
632// ---- Not ----
633
634/// Creates an expression that logically inverts boolean values.
635///
636/// Returns the logical negation of the input boolean expression.
637///
638/// ```rust
639/// # use vortex_array::expr::{not, root};
640/// let expr = not(root());
641/// ```
642pub fn not(operand: Expression) -> Expression {
643    Not.new_expr(EmptyOptions, vec![operand])
644}
645
646/// Creates a bound expression that logically inverts boolean values.
647pub fn bound_not(operand: BoundExpression) -> BoundExpression {
648    Not.try_new_bound_expr(EmptyOptions, [operand])
649        .vortex_expect("not expressions require a boolean operand")
650}
651
652// ---- Between ----
653
654/// Creates an expression that checks if values are between two bounds.
655///
656/// Returns a boolean array indicating which values fall within the specified range.
657/// The comparison strictness is controlled by the options parameter.
658///
659/// ```rust
660/// # use vortex_array::scalar_fn::fns::between::BetweenOptions;
661/// # use vortex_array::scalar_fn::fns::between::StrictComparison;
662/// # use vortex_array::expr::{between, lit, root};
663/// let opts = BetweenOptions {
664///     lower_strict: StrictComparison::NonStrict,
665///     upper_strict: StrictComparison::NonStrict,
666/// };
667/// let expr = between(root(), lit(10), lit(20), opts);
668/// ```
669pub fn between(
670    arr: Expression,
671    lower: Expression,
672    upper: Expression,
673    options: BetweenOptions,
674) -> Expression {
675    Between
676        .try_new_expr(options, [arr, lower, upper])
677        .vortex_expect("Failed to create Between expression")
678}
679
680/// Creates a bound expression that checks if values are between two bounds.
681pub fn bound_between(
682    arr: BoundExpression,
683    lower: BoundExpression,
684    upper: BoundExpression,
685    options: BetweenOptions,
686) -> BoundExpression {
687    Between
688        .try_new_bound_expr(options, [arr, lower, upper])
689        .vortex_expect("between expressions require compatible operand dtypes")
690}
691
692// ---- Select ----
693
694/// Creates an expression that selects (includes) specific fields from an array.
695///
696/// Projects only the specified fields from the child expression, which must be of DType struct.
697/// ```rust
698/// # use vortex_array::expr::{select, root};
699/// let expr = select(["name", "age"], root());
700/// ```
701pub fn select(field_names: impl Into<FieldNames>, child: Expression) -> Expression {
702    Select
703        .try_new_expr(FieldSelection::Include(field_names.into()), [child])
704        .vortex_expect("Failed to create Select expression")
705}
706
707/// Creates a bound expression that selects specific fields from a struct expression.
708pub fn bound_select(field_names: impl Into<FieldNames>, child: BoundExpression) -> BoundExpression {
709    Select
710        .try_new_bound_expr(FieldSelection::Include(field_names.into()), [child])
711        .vortex_expect("select expressions require fields from a struct child")
712}
713
714/// Creates an expression that excludes specific fields from an array.
715///
716/// Projects all fields except the specified ones from the input struct expression.
717///
718/// ```rust
719/// # use vortex_array::expr::{select_exclude, root};
720/// let expr = select_exclude(["internal_id", "metadata"], root());
721/// ```
722pub fn select_exclude(fields: impl Into<FieldNames>, child: Expression) -> Expression {
723    Select
724        .try_new_expr(FieldSelection::Exclude(fields.into()), [child])
725        .vortex_expect("Failed to create Select expression")
726}
727
728/// Creates a bound expression that excludes specific fields from a struct expression.
729pub fn bound_select_exclude(
730    fields: impl Into<FieldNames>,
731    child: BoundExpression,
732) -> BoundExpression {
733    Select
734        .try_new_bound_expr(FieldSelection::Exclude(fields.into()), [child])
735        .vortex_expect("select expressions require fields from a struct child")
736}
737
738// ---- Pack ----
739
740/// Creates an expression that packs values into a struct with named fields.
741///
742/// ```rust
743/// # use vortex_array::dtype::Nullability;
744/// # use vortex_array::expr::{pack, col, lit};
745/// let expr = pack([("id", col("user_id")), ("constant", lit(42))], Nullability::NonNullable);
746/// ```
747pub fn pack(
748    elements: impl IntoIterator<Item = (impl Into<FieldName>, Expression)>,
749    nullability: Nullability,
750) -> Expression {
751    let (names, values): (Vec<_>, Vec<_>) = elements
752        .into_iter()
753        .map(|(name, value)| (name.into(), value))
754        .unzip();
755    Pack.new_expr(
756        PackOptions {
757            names: names.into(),
758            nullability,
759        },
760        values,
761    )
762}
763
764/// Creates a bound expression that packs values into a struct with named fields.
765pub fn bound_pack(
766    elements: impl IntoIterator<Item = (impl Into<FieldName>, BoundExpression)>,
767    nullability: Nullability,
768) -> BoundExpression {
769    let (names, values): (Vec<_>, Vec<_>) = elements
770        .into_iter()
771        .map(|(name, value)| (name.into(), value))
772        .unzip();
773    Pack.try_new_bound_expr(
774        PackOptions {
775            names: names.into(),
776            nullability,
777        },
778        values,
779    )
780    .vortex_expect("pack expressions must have one name per child")
781}
782
783// ---- Cast ----
784
785/// Creates an expression that casts values to a target data type.
786///
787/// Converts the input expression's values to the specified target type.
788///
789/// ```rust
790/// # use vortex_array::dtype::{DType, Nullability, PType};
791/// # use vortex_array::expr::{cast, root};
792/// let expr = cast(root(), DType::Primitive(PType::I64, Nullability::NonNullable));
793/// ```
794pub fn cast(child: Expression, target: DType) -> Expression {
795    Cast.try_new_expr(target, [child])
796        .vortex_expect("Failed to create Cast expression")
797}
798
799/// Creates a bound expression that casts values to a target dtype.
800pub fn bound_cast(child: BoundExpression, target: DType) -> BoundExpression {
801    Cast.try_new_bound_expr(target, [child])
802        .vortex_expect("cast expressions require a supported source and target dtype")
803}
804
805// ---- FillNull ----
806
807/// Creates an expression that replaces null values with a fill value.
808///
809/// ```rust
810/// # use vortex_array::expr::{fill_null, root, lit};
811/// let expr = fill_null(root(), lit(0i32));
812/// ```
813pub fn fill_null(child: Expression, fill_value: Expression) -> Expression {
814    FillNull.new_expr(EmptyOptions, [child, fill_value])
815}
816
817/// Creates a bound expression that replaces null values with a fill value.
818pub fn bound_fill_null(child: BoundExpression, fill_value: BoundExpression) -> BoundExpression {
819    FillNull
820        .try_new_bound_expr(EmptyOptions, [child, fill_value])
821        .vortex_expect("fill-null expressions require compatible child and fill dtypes")
822}
823
824// ---- IsNull ----
825
826/// Creates an expression that checks for null values.
827///
828/// Returns a boolean array indicating which positions contain null values.
829///
830/// ```rust
831/// # use vortex_array::expr::{is_null, root};
832/// let expr = is_null(root());
833/// ```
834pub fn is_null(child: Expression) -> Expression {
835    IsNull.new_expr(EmptyOptions, vec![child])
836}
837
838/// Creates a bound expression that checks for null values.
839pub fn bound_is_null(child: BoundExpression) -> BoundExpression {
840    IsNull
841        .try_new_bound_expr(EmptyOptions, [child])
842        .vortex_expect("is-null expressions are always well-typed")
843}
844
845// ---- IsNan ----
846
847/// Creates an expression that checks for NaN values.
848///
849/// The expression is strict: null inputs produce null outputs, so the output nullability
850/// follows the input. Only primitive float inputs are supported.
851///
852/// ```rust
853/// # use vortex_array::expr::{is_nan, root};
854/// let expr = is_nan(root());
855/// ```
856pub fn is_nan(child: Expression) -> Expression {
857    IsNan.new_expr(EmptyOptions, vec![child])
858}
859
860/// Creates a bound expression that checks for NaN values.
861pub fn bound_is_nan(child: BoundExpression) -> BoundExpression {
862    IsNan
863        .try_new_bound_expr(EmptyOptions, [child])
864        .vortex_expect("is-nan 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 whether a list contains a value.
1110///
1111/// A null element never matches anything: a needle that matches no element yields `false`. For
1112/// SQL's three-valued `IN`, where a null element makes that `null`, use [`in_list`].
1113///
1114/// ```rust
1115/// # use vortex_array::expr::{list_contains, lit, root};
1116/// let expr = list_contains(root(), lit(42));
1117/// ```
1118pub fn list_contains(list: Expression, value: Expression) -> Expression {
1119    list_contains_opts(list, value, ListContainsOptions::default())
1120}
1121
1122/// Creates an expression that checks whether a list contains a value, with explicit
1123/// [`ListContainsOptions`].
1124pub fn list_contains_opts(
1125    list: Expression,
1126    value: Expression,
1127    options: ListContainsOptions,
1128) -> Expression {
1129    ListContains.new_expr(options, [list, value])
1130}
1131
1132/// Creates `needle IN (list)` with SQL null semantics: a null element is an unknown value, so a
1133/// needle that matches no element yields `null` rather than `false` when the list holds one, and
1134/// `not(in_list(..))` never admits such a row.
1135///
1136/// ```rust
1137/// # use vortex_array::expr::{in_list, lit, root};
1138/// let expr = in_list(root(), lit(vec![1, 2, 3]));
1139/// ```
1140pub fn in_list(needle: Expression, list: Expression) -> Expression {
1141    list_contains_opts(
1142        list,
1143        needle,
1144        ListContainsOptions {
1145            sql_null_semantics: true,
1146        },
1147    )
1148}
1149
1150/// Creates a bound expression that checks whether a list contains a value.
1151pub fn bound_list_contains(list: BoundExpression, value: BoundExpression) -> BoundExpression {
1152    ListContains
1153        .try_new_bound_expr(ListContainsOptions::default(), [list, value])
1154        .vortex_expect("list-contains expressions require a list child")
1155}
1156
1157/// Creates a bound `needle IN (list)` with SQL null semantics. See [`in_list`].
1158pub fn bound_in_list(needle: BoundExpression, list: BoundExpression) -> BoundExpression {
1159    ListContains
1160        .try_new_bound_expr(
1161            ListContainsOptions {
1162                sql_null_semantics: true,
1163            },
1164            [list, needle],
1165        )
1166        .vortex_expect("list-contains expressions require a list child")
1167}
1168
1169// ---- ByteLength ----
1170
1171/// Creates an expression that computes the byte length of each element.
1172/// This is akin to ANSI SQL OCTET_LENGTH(), or DuckDB's strlen().
1173///
1174/// ```rust
1175/// # use vortex_array::expr::{byte_length, root};
1176/// let expr = byte_length(root());
1177/// ```
1178pub fn byte_length(input: Expression) -> Expression {
1179    ByteLength.new_expr(EmptyOptions, [input])
1180}
1181
1182/// Creates a bound expression that computes each element's byte length.
1183pub fn bound_byte_length(input: BoundExpression) -> BoundExpression {
1184    ByteLength
1185        .try_new_bound_expr(EmptyOptions, [input])
1186        .vortex_expect("byte-length expressions require a variable-length binary child")
1187}
1188
1189// ---- ExtStorage ----
1190
1191/// Creates an expression that extracts the storage values from an extension array.
1192///
1193/// ```rust
1194/// # use vortex_array::expr::{ext_storage, root};
1195/// let expr = ext_storage(root());
1196/// ```
1197pub fn ext_storage(input: Expression) -> Expression {
1198    ExtStorage.new_expr(EmptyOptions, [input])
1199}
1200
1201/// Creates a bound expression that extracts an extension array's storage values.
1202pub fn bound_ext_storage(input: BoundExpression) -> BoundExpression {
1203    ExtStorage
1204        .try_new_bound_expr(EmptyOptions, [input])
1205        .vortex_expect("extension-storage expressions require an extension child")
1206}
1207
1208// ---- ListLength ----
1209
1210/// Creates an expression that computes the number of elements in each list
1211/// for `List` and `FixedSizeList` inputs. This is akin to ANSI SQL `CARDINALITY()`,
1212/// or DuckDB's `len()`/`array_length()`.
1213///
1214/// ```rust
1215/// # use vortex_array::expr::{list_length, root};
1216/// let expr = list_length(root());
1217/// ```
1218pub fn list_length(input: Expression) -> Expression {
1219    ListLength.new_expr(EmptyOptions, [input])
1220}
1221
1222/// Creates a bound expression that computes the number of elements in each list.
1223pub fn bound_list_length(input: BoundExpression) -> BoundExpression {
1224    ListLength
1225        .try_new_bound_expr(EmptyOptions, [input])
1226        .vortex_expect("list-length expressions require a list child")
1227}
1228
1229// ---- ListSum ----
1230
1231/// Creates an expression that sums the elements of each list for `List` and
1232/// `FixedSizeList` inputs, akin to DuckDB's `list_sum()`.
1233///
1234/// Follows SQL `SUM` semantics per list: null lists, empty lists, and lists whose elements are
1235/// all null yield null; null elements are skipped; integer and decimal overflow yields a null
1236/// value. The result dtype follows `sum`'s widening rules and is always nullable. NaN float
1237/// elements are skipped by default; see [`list_sum_opts`] for the NaN-including variant.
1238///
1239/// ```rust
1240/// # use vortex_array::expr::{list_sum, root};
1241/// let expr = list_sum(root());
1242/// ```
1243pub fn list_sum(input: Expression) -> Expression {
1244    ListSum.new_expr(NumericalAggregateOpts::default(), [input])
1245}
1246
1247/// Creates a bound expression that sums the elements of each list.
1248pub fn bound_list_sum(input: BoundExpression) -> BoundExpression {
1249    ListSum
1250        .try_new_bound_expr(NumericalAggregateOpts::default(), [input])
1251        .vortex_expect("list-sum expressions require a numeric list child")
1252}
1253
1254/// Creates a [`list_sum`] expression with explicit [`NumericalAggregateOpts`], controlling
1255/// whether NaN float elements are skipped (the default) or poison the list's sum to NaN.
1256pub fn list_sum_opts(input: Expression, options: NumericalAggregateOpts) -> Expression {
1257    ListSum.new_expr(options, [input])
1258}
1259
1260/// Creates a bound list-sum expression with explicit aggregate options.
1261pub fn bound_list_sum_opts(
1262    input: BoundExpression,
1263    options: NumericalAggregateOpts,
1264) -> BoundExpression {
1265    ListSum
1266        .try_new_bound_expr(options, [input])
1267        .vortex_expect("list-sum expressions require a numeric list child")
1268}
1269
1270/// Reinterpret timestamp wall times in `options.time_zone`, preserving the time unit.
1271///
1272/// `ambiguous` is a UTF-8 expression containing `raise`, `earliest`, `latest`, or `null`.
1273/// Removing the timezone preserves wall time in a timezone-naive timestamp.
1274pub fn replace_time_zone(
1275    input: Expression,
1276    ambiguous: Expression,
1277    options: ReplaceTimeZoneOptions,
1278) -> Expression {
1279    ReplaceTimeZone.new_expr(options, [input, ambiguous])
1280}
1281
1282/// Create a bound timezone replacement expression.
1283pub fn bound_replace_time_zone(
1284    input: BoundExpression,
1285    ambiguous: BoundExpression,
1286    options: ReplaceTimeZoneOptions,
1287) -> BoundExpression {
1288    ReplaceTimeZone
1289        .try_new_bound_expr(options, [input, ambiguous])
1290        .vortex_expect("timezone replacement requires a timestamp and a UTF-8 ambiguity policy")
1291}
1292
1293/// Constructors for expressions whose children have already been bound and type-checked.
1294///
1295/// These mirror the constructors in [`crate::expr`] and panic when the supplied children do not
1296/// form a well-typed expression. Use [`BoundExpression::try_new`] when construction must be
1297/// fallible.
1298pub mod bound {
1299    pub use super::bound_and as and;
1300    pub use super::bound_and_collect as and_collect;
1301    pub use super::bound_between as between;
1302    pub use super::bound_binary as binary;
1303    pub use super::bound_byte_length as byte_length;
1304    pub use super::bound_case_when as case_when;
1305    pub use super::bound_case_when_no_else as case_when_no_else;
1306    pub use super::bound_cast as cast;
1307    pub use super::bound_checked_add as checked_add;
1308    pub use super::bound_col as col;
1309    pub use super::bound_dynamic as dynamic;
1310    pub use super::bound_dynamic_with_options as dynamic_with_options;
1311    pub use super::bound_eq as eq;
1312    pub use super::bound_ext_storage as ext_storage;
1313    pub use super::bound_fill_null as fill_null;
1314    pub use super::bound_get_item as get_item;
1315    pub use super::bound_gt as gt;
1316    pub use super::bound_gt_eq as gt_eq;
1317    pub use super::bound_ilike as ilike;
1318    pub use super::bound_in_list as in_list;
1319    pub use super::bound_is_nan as is_nan;
1320    pub use super::bound_is_not_null as is_not_null;
1321    pub use super::bound_is_null as is_null;
1322    pub use super::bound_like as like;
1323    pub use super::bound_list_contains as list_contains;
1324    pub use super::bound_list_length as list_length;
1325    pub use super::bound_list_sum as list_sum;
1326    pub use super::bound_list_sum_opts as list_sum_opts;
1327    pub use super::bound_lit as lit;
1328    pub use super::bound_lt as lt;
1329    pub use super::bound_lt_eq as lt_eq;
1330    pub use super::bound_mask as mask;
1331    pub use super::bound_merge as merge;
1332    pub use super::bound_merge_opts as merge_opts;
1333    pub use super::bound_nested_case_when as nested_case_when;
1334    pub use super::bound_not as not;
1335    pub use super::bound_not_eq as not_eq;
1336    pub use super::bound_not_ilike as not_ilike;
1337    pub use super::bound_not_like as not_like;
1338    pub use super::bound_or as or;
1339    pub use super::bound_or_collect as or_collect;
1340    pub use super::bound_pack as pack;
1341    pub use super::bound_replace_time_zone as replace_time_zone;
1342    pub use super::bound_root as root;
1343    pub use super::bound_select as select;
1344    pub use super::bound_select_exclude as select_exclude;
1345    pub use super::bound_variant_get as variant_get;
1346    pub use super::bound_zip_expr as zip_expr;
1347}