use std::sync::Arc;
use std::sync::LazyLock;
use vortex_error::VortexExpect;
use vortex_error::VortexResult;
use vortex_error::vortex_panic;
use vortex_utils::iter::ReduceBalancedIterExt;
use crate::aggregate_fn::NumericalAggregateOpts;
use crate::dtype::DType;
use crate::dtype::FieldName;
use crate::dtype::FieldNames;
use crate::dtype::Nullability;
use crate::expr::BoundExpression;
use crate::expr::Expression;
use crate::scalar::Scalar;
use crate::scalar::ScalarValue;
use crate::scalar_fn::EmptyOptions;
use crate::scalar_fn::ScalarFnVTableExt;
use crate::scalar_fn::fns::between::Between;
use crate::scalar_fn::fns::between::BetweenOptions;
use crate::scalar_fn::fns::binary::Binary;
use crate::scalar_fn::fns::byte_length::ByteLength;
use crate::scalar_fn::fns::case_when::CaseWhen;
use crate::scalar_fn::fns::case_when::CaseWhenOptions;
use crate::scalar_fn::fns::cast::Cast;
use crate::scalar_fn::fns::dynamic::DynamicComparison;
use crate::scalar_fn::fns::dynamic::DynamicComparisonExpr;
use crate::scalar_fn::fns::dynamic::Rhs;
use crate::scalar_fn::fns::ext_storage::ExtStorage;
use crate::scalar_fn::fns::fill_null::FillNull;
use crate::scalar_fn::fns::get_item::GetItem;
use crate::scalar_fn::fns::is_not_null::IsNotNull;
use crate::scalar_fn::fns::is_null::IsNull;
use crate::scalar_fn::fns::like::Like;
use crate::scalar_fn::fns::like::LikeOptions;
use crate::scalar_fn::fns::list_contains::ListContains;
use crate::scalar_fn::fns::list_length::ListLength;
use crate::scalar_fn::fns::list_sum::ListSum;
use crate::scalar_fn::fns::literal::Literal;
use crate::scalar_fn::fns::mask::Mask;
use crate::scalar_fn::fns::merge::DuplicateHandling;
use crate::scalar_fn::fns::merge::Merge;
use crate::scalar_fn::fns::not::Not;
use crate::scalar_fn::fns::operators::CompareOperator;
use crate::scalar_fn::fns::operators::Operator;
use crate::scalar_fn::fns::pack::Pack;
use crate::scalar_fn::fns::pack::PackOptions;
use crate::scalar_fn::fns::root::Root;
use crate::scalar_fn::fns::select::FieldSelection;
use crate::scalar_fn::fns::select::Select;
use crate::scalar_fn::fns::variant_get::VariantGet;
use crate::scalar_fn::fns::variant_get::VariantGetOptions;
use crate::scalar_fn::fns::variant_get::VariantPath;
use crate::scalar_fn::fns::zip::Zip;
static ROOT: LazyLock<Expression> = LazyLock::new(|| {
Root.try_new_expr(EmptyOptions, vec![])
.vortex_expect("Creating root() shouldn't fail")
});
pub fn root() -> Expression {
ROOT.clone()
}
pub fn bound_root(dtype: DType) -> BoundExpression {
BoundExpression::new_root(dtype)
}
pub fn is_root(expr: &Expression) -> bool {
(expr.scalar_fn().id() == ROOT.scalar_fn().id()) || expr.is::<Root>()
}
pub fn lit(value: impl Into<Scalar>) -> Expression {
Literal.new_expr(value.into(), [])
}
pub fn bound_lit(value: impl Into<Scalar>) -> BoundExpression {
Literal
.try_new_bound_expr(value.into(), [])
.vortex_expect("literal expressions are always well-typed")
}
pub fn col(field: impl Into<FieldName>) -> Expression {
GetItem.new_expr(field.into(), vec![root()])
}
pub fn bound_col(field: impl Into<FieldName>, scope: DType) -> BoundExpression {
bound_get_item(field, bound_root(scope))
}
pub fn get_item(field: impl Into<FieldName>, child: Expression) -> Expression {
GetItem.new_expr(field.into(), vec![child])
}
pub fn bound_get_item(field: impl Into<FieldName>, child: BoundExpression) -> BoundExpression {
GetItem
.try_new_bound_expr(field.into(), [child])
.vortex_expect("get-item expressions must reference a field in the child dtype")
}
pub fn variant_get(
child: Expression,
path: impl Into<VariantPath>,
dtype: Option<DType>,
) -> Expression {
VariantGet.new_expr(VariantGetOptions::new(path.into(), dtype), vec![child])
}
pub fn bound_variant_get(
child: BoundExpression,
path: impl Into<VariantPath>,
dtype: Option<DType>,
) -> BoundExpression {
VariantGet
.try_new_bound_expr(VariantGetOptions::new(path.into(), dtype), [child])
.vortex_expect("variant-get expressions require a Variant child")
}
pub fn case_when(
condition: Expression,
then_value: Expression,
else_value: Expression,
) -> Expression {
let options = CaseWhenOptions {
num_when_then_pairs: 1,
has_else: true,
};
CaseWhen.new_expr(options, [condition, then_value, else_value])
}
pub fn bound_case_when(
condition: BoundExpression,
then_value: BoundExpression,
else_value: BoundExpression,
) -> BoundExpression {
let options = CaseWhenOptions {
num_when_then_pairs: 1,
has_else: true,
};
CaseWhen
.try_new_bound_expr(options, [condition, then_value, else_value])
.vortex_expect("case expressions must have boolean conditions and matching branch dtypes")
}
pub fn case_when_no_else(condition: Expression, then_value: Expression) -> Expression {
let options = CaseWhenOptions {
num_when_then_pairs: 1,
has_else: false,
};
CaseWhen.new_expr(options, [condition, then_value])
}
pub fn bound_case_when_no_else(
condition: BoundExpression,
then_value: BoundExpression,
) -> BoundExpression {
let options = CaseWhenOptions {
num_when_then_pairs: 1,
has_else: false,
};
CaseWhen
.try_new_bound_expr(options, [condition, then_value])
.vortex_expect("case expressions must have boolean conditions")
}
pub fn nested_case_when(
when_then_pairs: Vec<(Expression, Expression)>,
else_value: Option<Expression>,
) -> Expression {
assert!(
!when_then_pairs.is_empty(),
"nested_case_when requires at least one when/then pair"
);
let has_else = else_value.is_some();
let mut children = Vec::with_capacity(when_then_pairs.len() * 2 + usize::from(has_else));
for (condition, then_value) in &when_then_pairs {
children.push(condition.clone());
children.push(then_value.clone());
}
if let Some(else_expr) = else_value {
children.push(else_expr);
}
let Ok(num_when_then_pairs) = u32::try_from(when_then_pairs.len()) else {
vortex_panic!("nested_case_when has too many when/then pairs");
};
let options = CaseWhenOptions {
num_when_then_pairs,
has_else,
};
CaseWhen.new_expr(options, children)
}
pub fn bound_nested_case_when(
when_then_pairs: Vec<(BoundExpression, BoundExpression)>,
else_value: Option<BoundExpression>,
) -> BoundExpression {
assert!(
!when_then_pairs.is_empty(),
"nested_case_when requires at least one when/then pair"
);
let Ok(num_when_then_pairs) = u32::try_from(when_then_pairs.len()) else {
vortex_panic!("nested_case_when has too many when/then pairs");
};
let has_else = else_value.is_some();
let mut children = Vec::with_capacity(when_then_pairs.len() * 2 + usize::from(has_else));
for (condition, then_value) in when_then_pairs {
children.push(condition);
children.push(then_value);
}
if let Some(else_expr) = else_value {
children.push(else_expr);
}
let options = CaseWhenOptions {
num_when_then_pairs,
has_else,
};
CaseWhen
.try_new_bound_expr(options, children)
.vortex_expect("case expressions must have boolean conditions and matching branch dtypes")
}
pub fn binary(operator: Operator, lhs: Expression, rhs: Expression) -> Expression {
Binary
.try_new_expr(operator, [lhs, rhs])
.vortex_expect("Failed to create binary expression")
}
pub fn bound_binary(
operator: Operator,
lhs: BoundExpression,
rhs: BoundExpression,
) -> BoundExpression {
Binary
.try_new_bound_expr(operator, [lhs, rhs])
.vortex_expect("binary expressions must have compatible operand dtypes")
}
pub fn eq(lhs: Expression, rhs: Expression) -> Expression {
Binary
.try_new_expr(Operator::Eq, [lhs, rhs])
.vortex_expect("Failed to create Eq binary expression")
}
pub fn bound_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
bound_binary(Operator::Eq, lhs, rhs)
}
pub fn not_eq(lhs: Expression, rhs: Expression) -> Expression {
Binary
.try_new_expr(Operator::NotEq, [lhs, rhs])
.vortex_expect("Failed to create NotEq binary expression")
}
pub fn bound_not_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
bound_binary(Operator::NotEq, lhs, rhs)
}
pub fn gt_eq(lhs: Expression, rhs: Expression) -> Expression {
Binary
.try_new_expr(Operator::Gte, [lhs, rhs])
.vortex_expect("Failed to create Gte binary expression")
}
pub fn bound_gt_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
bound_binary(Operator::Gte, lhs, rhs)
}
pub fn gt(lhs: Expression, rhs: Expression) -> Expression {
Binary
.try_new_expr(Operator::Gt, [lhs, rhs])
.vortex_expect("Failed to create Gt binary expression")
}
pub fn bound_gt(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
bound_binary(Operator::Gt, lhs, rhs)
}
pub fn lt_eq(lhs: Expression, rhs: Expression) -> Expression {
Binary
.try_new_expr(Operator::Lte, [lhs, rhs])
.vortex_expect("Failed to create Lte binary expression")
}
pub fn bound_lt_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
bound_binary(Operator::Lte, lhs, rhs)
}
pub fn lt(lhs: Expression, rhs: Expression) -> Expression {
Binary
.try_new_expr(Operator::Lt, [lhs, rhs])
.vortex_expect("Failed to create Lt binary expression")
}
pub fn bound_lt(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
bound_binary(Operator::Lt, lhs, rhs)
}
pub fn or(lhs: Expression, rhs: Expression) -> Expression {
Binary
.try_new_expr(Operator::Or, [lhs, rhs])
.vortex_expect("Failed to create Or binary expression")
}
pub fn bound_or(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
bound_binary(Operator::Or, lhs, rhs)
}
pub fn or_collect<I>(iter: I) -> Option<Expression>
where
I: IntoIterator<Item = Expression>,
{
iter.into_iter().reduce_balanced(or)
}
pub fn bound_or_collect<I>(iter: I) -> Option<BoundExpression>
where
I: IntoIterator<Item = BoundExpression>,
{
iter.into_iter().reduce_balanced(bound_or)
}
pub fn and(lhs: Expression, rhs: Expression) -> Expression {
Binary
.try_new_expr(Operator::And, [lhs, rhs])
.vortex_expect("Failed to create And binary expression")
}
pub fn bound_and(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
bound_binary(Operator::And, lhs, rhs)
}
pub fn and_collect<I>(iter: I) -> Option<Expression>
where
I: IntoIterator<Item = Expression>,
{
iter.into_iter().reduce_balanced(and)
}
pub fn bound_and_collect<I>(iter: I) -> Option<BoundExpression>
where
I: IntoIterator<Item = BoundExpression>,
{
iter.into_iter().reduce_balanced(bound_and)
}
pub fn union_child_validities(expression: &Expression) -> VortexResult<Option<Expression>> {
let child_validities = expression
.children()
.iter()
.map(Expression::validity)
.collect::<VortexResult<Vec<_>>>()?;
Ok(and_collect(child_validities))
}
pub fn checked_add(lhs: Expression, rhs: Expression) -> Expression {
Binary
.try_new_expr(Operator::Add, [lhs, rhs])
.vortex_expect("Failed to create Add binary expression")
}
pub fn bound_checked_add(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
bound_binary(Operator::Add, lhs, rhs)
}
pub fn not(operand: Expression) -> Expression {
Not.new_expr(EmptyOptions, vec![operand])
}
pub fn bound_not(operand: BoundExpression) -> BoundExpression {
Not.try_new_bound_expr(EmptyOptions, [operand])
.vortex_expect("not expressions require a boolean operand")
}
pub fn between(
arr: Expression,
lower: Expression,
upper: Expression,
options: BetweenOptions,
) -> Expression {
Between
.try_new_expr(options, [arr, lower, upper])
.vortex_expect("Failed to create Between expression")
}
pub fn bound_between(
arr: BoundExpression,
lower: BoundExpression,
upper: BoundExpression,
options: BetweenOptions,
) -> BoundExpression {
Between
.try_new_bound_expr(options, [arr, lower, upper])
.vortex_expect("between expressions require compatible operand dtypes")
}
pub fn select(field_names: impl Into<FieldNames>, child: Expression) -> Expression {
Select
.try_new_expr(FieldSelection::Include(field_names.into()), [child])
.vortex_expect("Failed to create Select expression")
}
pub fn bound_select(field_names: impl Into<FieldNames>, child: BoundExpression) -> BoundExpression {
Select
.try_new_bound_expr(FieldSelection::Include(field_names.into()), [child])
.vortex_expect("select expressions require fields from a struct child")
}
pub fn select_exclude(fields: impl Into<FieldNames>, child: Expression) -> Expression {
Select
.try_new_expr(FieldSelection::Exclude(fields.into()), [child])
.vortex_expect("Failed to create Select expression")
}
pub fn bound_select_exclude(
fields: impl Into<FieldNames>,
child: BoundExpression,
) -> BoundExpression {
Select
.try_new_bound_expr(FieldSelection::Exclude(fields.into()), [child])
.vortex_expect("select expressions require fields from a struct child")
}
pub fn pack(
elements: impl IntoIterator<Item = (impl Into<FieldName>, Expression)>,
nullability: Nullability,
) -> Expression {
let (names, values): (Vec<_>, Vec<_>) = elements
.into_iter()
.map(|(name, value)| (name.into(), value))
.unzip();
Pack.new_expr(
PackOptions {
names: names.into(),
nullability,
},
values,
)
}
pub fn bound_pack(
elements: impl IntoIterator<Item = (impl Into<FieldName>, BoundExpression)>,
nullability: Nullability,
) -> BoundExpression {
let (names, values): (Vec<_>, Vec<_>) = elements
.into_iter()
.map(|(name, value)| (name.into(), value))
.unzip();
Pack.try_new_bound_expr(
PackOptions {
names: names.into(),
nullability,
},
values,
)
.vortex_expect("pack expressions must have one name per child")
}
pub fn cast(child: Expression, target: DType) -> Expression {
Cast.try_new_expr(target, [child])
.vortex_expect("Failed to create Cast expression")
}
pub fn bound_cast(child: BoundExpression, target: DType) -> BoundExpression {
Cast.try_new_bound_expr(target, [child])
.vortex_expect("cast expressions require a supported source and target dtype")
}
pub fn fill_null(child: Expression, fill_value: Expression) -> Expression {
FillNull.new_expr(EmptyOptions, [child, fill_value])
}
pub fn bound_fill_null(child: BoundExpression, fill_value: BoundExpression) -> BoundExpression {
FillNull
.try_new_bound_expr(EmptyOptions, [child, fill_value])
.vortex_expect("fill-null expressions require compatible child and fill dtypes")
}
pub fn is_null(child: Expression) -> Expression {
IsNull.new_expr(EmptyOptions, vec![child])
}
pub fn bound_is_null(child: BoundExpression) -> BoundExpression {
IsNull
.try_new_bound_expr(EmptyOptions, [child])
.vortex_expect("is-null expressions are always well-typed")
}
pub fn is_not_null(child: Expression) -> Expression {
IsNotNull.new_expr(EmptyOptions, vec![child])
}
pub fn bound_is_not_null(child: BoundExpression) -> BoundExpression {
IsNotNull
.try_new_bound_expr(EmptyOptions, [child])
.vortex_expect("is-not-null expressions are always well-typed")
}
pub fn like(child: Expression, pattern: Expression) -> Expression {
Like.new_expr(
LikeOptions {
negated: false,
case_insensitive: false,
},
[child, pattern],
)
}
pub fn bound_like(child: BoundExpression, pattern: BoundExpression) -> BoundExpression {
bound_like_with_options(child, pattern, false, false)
}
pub fn ilike(child: Expression, pattern: Expression) -> Expression {
Like.new_expr(
LikeOptions {
negated: false,
case_insensitive: true,
},
[child, pattern],
)
}
pub fn bound_ilike(child: BoundExpression, pattern: BoundExpression) -> BoundExpression {
bound_like_with_options(child, pattern, false, true)
}
pub fn not_like(child: Expression, pattern: Expression) -> Expression {
Like.new_expr(
LikeOptions {
negated: true,
case_insensitive: false,
},
[child, pattern],
)
}
pub fn bound_not_like(child: BoundExpression, pattern: BoundExpression) -> BoundExpression {
bound_like_with_options(child, pattern, true, false)
}
pub fn not_ilike(child: Expression, pattern: Expression) -> Expression {
Like.new_expr(
LikeOptions {
negated: true,
case_insensitive: true,
},
[child, pattern],
)
}
pub fn bound_not_ilike(child: BoundExpression, pattern: BoundExpression) -> BoundExpression {
bound_like_with_options(child, pattern, true, true)
}
fn bound_like_with_options(
child: BoundExpression,
pattern: BoundExpression,
negated: bool,
case_insensitive: bool,
) -> BoundExpression {
Like.try_new_bound_expr(
LikeOptions {
negated,
case_insensitive,
},
[child, pattern],
)
.vortex_expect("like expressions require UTF-8 or binary operands")
}
pub fn mask(array: Expression, mask: Expression) -> Expression {
Mask.new_expr(EmptyOptions, [array, mask])
}
pub fn bound_mask(array: BoundExpression, mask: BoundExpression) -> BoundExpression {
Mask.try_new_bound_expr(EmptyOptions, [array, mask])
.vortex_expect("mask expressions require a boolean mask")
}
pub fn merge(elements: impl IntoIterator<Item = impl Into<Expression>>) -> Expression {
use itertools::Itertools as _;
let values = elements.into_iter().map(|value| value.into()).collect_vec();
Merge.new_expr(DuplicateHandling::default(), values)
}
pub fn bound_merge(elements: impl IntoIterator<Item = BoundExpression>) -> BoundExpression {
bound_merge_opts(elements, DuplicateHandling::default())
}
pub fn merge_opts(
elements: impl IntoIterator<Item = impl Into<Expression>>,
duplicate_handling: DuplicateHandling,
) -> Expression {
use itertools::Itertools as _;
let values = elements.into_iter().map(|value| value.into()).collect_vec();
Merge.new_expr(duplicate_handling, values)
}
pub fn bound_merge_opts(
elements: impl IntoIterator<Item = BoundExpression>,
duplicate_handling: DuplicateHandling,
) -> BoundExpression {
Merge
.try_new_bound_expr(duplicate_handling, elements)
.vortex_expect("merge expressions require non-nullable struct children")
}
pub fn zip_expr(mask: Expression, if_true: Expression, if_false: Expression) -> Expression {
Zip.new_expr(EmptyOptions, [if_true, if_false, mask])
}
pub fn bound_zip_expr(
mask: BoundExpression,
if_true: BoundExpression,
if_false: BoundExpression,
) -> BoundExpression {
Zip.try_new_bound_expr(EmptyOptions, [if_true, if_false, mask])
.vortex_expect("zip expressions require a boolean mask and compatible value dtypes")
}
pub fn dynamic_with_options(options: DynamicComparisonExpr, lhs: Expression) -> Expression {
DynamicComparison.new_expr(options, [lhs])
}
pub fn bound_dynamic_with_options(
options: DynamicComparisonExpr,
lhs: BoundExpression,
) -> BoundExpression {
DynamicComparison
.try_new_bound_expr(options, [lhs])
.vortex_expect("dynamic comparisons require a compatible left-hand dtype")
}
pub fn dynamic(
operator: CompareOperator,
rhs_value: impl Fn() -> Option<ScalarValue> + Send + Sync + 'static,
rhs_dtype: DType,
default: bool,
lhs: Expression,
) -> Expression {
dynamic_with_options(
DynamicComparisonExpr {
operator,
rhs: Arc::new(Rhs {
value: Arc::new(rhs_value),
dtype: rhs_dtype,
}),
default,
},
lhs,
)
}
pub fn bound_dynamic(
operator: CompareOperator,
rhs_value: impl Fn() -> Option<ScalarValue> + Send + Sync + 'static,
rhs_dtype: DType,
default: bool,
lhs: BoundExpression,
) -> BoundExpression {
bound_dynamic_with_options(
DynamicComparisonExpr {
operator,
rhs: Arc::new(Rhs {
value: Arc::new(rhs_value),
dtype: rhs_dtype,
}),
default,
},
lhs,
)
}
pub fn list_contains(list: Expression, value: Expression) -> Expression {
ListContains.new_expr(EmptyOptions, [list, value])
}
pub fn bound_list_contains(list: BoundExpression, value: BoundExpression) -> BoundExpression {
ListContains
.try_new_bound_expr(EmptyOptions, [list, value])
.vortex_expect("list-contains expressions require a compatible list and value dtype")
}
pub fn byte_length(input: Expression) -> Expression {
ByteLength.new_expr(EmptyOptions, [input])
}
pub fn bound_byte_length(input: BoundExpression) -> BoundExpression {
ByteLength
.try_new_bound_expr(EmptyOptions, [input])
.vortex_expect("byte-length expressions require a variable-length binary child")
}
pub fn ext_storage(input: Expression) -> Expression {
ExtStorage.new_expr(EmptyOptions, [input])
}
pub fn bound_ext_storage(input: BoundExpression) -> BoundExpression {
ExtStorage
.try_new_bound_expr(EmptyOptions, [input])
.vortex_expect("extension-storage expressions require an extension child")
}
pub fn list_length(input: Expression) -> Expression {
ListLength.new_expr(EmptyOptions, [input])
}
pub fn bound_list_length(input: BoundExpression) -> BoundExpression {
ListLength
.try_new_bound_expr(EmptyOptions, [input])
.vortex_expect("list-length expressions require a list child")
}
pub fn list_sum(input: Expression) -> Expression {
ListSum.new_expr(NumericalAggregateOpts::default(), [input])
}
pub fn bound_list_sum(input: BoundExpression) -> BoundExpression {
ListSum
.try_new_bound_expr(NumericalAggregateOpts::default(), [input])
.vortex_expect("list-sum expressions require a numeric list child")
}
pub fn list_sum_opts(input: Expression, options: NumericalAggregateOpts) -> Expression {
ListSum.new_expr(options, [input])
}
pub fn bound_list_sum_opts(
input: BoundExpression,
options: NumericalAggregateOpts,
) -> BoundExpression {
ListSum
.try_new_bound_expr(options, [input])
.vortex_expect("list-sum expressions require a numeric list child")
}
pub mod bound {
pub use super::bound_and as and;
pub use super::bound_and_collect as and_collect;
pub use super::bound_between as between;
pub use super::bound_binary as binary;
pub use super::bound_byte_length as byte_length;
pub use super::bound_case_when as case_when;
pub use super::bound_case_when_no_else as case_when_no_else;
pub use super::bound_cast as cast;
pub use super::bound_checked_add as checked_add;
pub use super::bound_col as col;
pub use super::bound_dynamic as dynamic;
pub use super::bound_dynamic_with_options as dynamic_with_options;
pub use super::bound_eq as eq;
pub use super::bound_ext_storage as ext_storage;
pub use super::bound_fill_null as fill_null;
pub use super::bound_get_item as get_item;
pub use super::bound_gt as gt;
pub use super::bound_gt_eq as gt_eq;
pub use super::bound_ilike as ilike;
pub use super::bound_is_not_null as is_not_null;
pub use super::bound_is_null as is_null;
pub use super::bound_like as like;
pub use super::bound_list_contains as list_contains;
pub use super::bound_list_length as list_length;
pub use super::bound_list_sum as list_sum;
pub use super::bound_list_sum_opts as list_sum_opts;
pub use super::bound_lit as lit;
pub use super::bound_lt as lt;
pub use super::bound_lt_eq as lt_eq;
pub use super::bound_mask as mask;
pub use super::bound_merge as merge;
pub use super::bound_merge_opts as merge_opts;
pub use super::bound_nested_case_when as nested_case_when;
pub use super::bound_not as not;
pub use super::bound_not_eq as not_eq;
pub use super::bound_not_ilike as not_ilike;
pub use super::bound_not_like as not_like;
pub use super::bound_or as or;
pub use super::bound_or_collect as or_collect;
pub use super::bound_pack as pack;
pub use super::bound_root as root;
pub use super::bound_select as select;
pub use super::bound_select_exclude as select_exclude;
pub use super::bound_variant_get as variant_get;
pub use super::bound_zip_expr as zip_expr;
}