1use std::sync::Arc;
7
8use vortex_error::VortexExpect;
9use vortex_error::VortexResult;
10use vortex_error::vortex_ensure;
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::between::StrictComparison;
28use crate::scalar_fn::fns::binary::Binary;
29use crate::scalar_fn::fns::byte_length::ByteLength;
30use crate::scalar_fn::fns::case_when::CaseWhen;
31use crate::scalar_fn::fns::case_when::CaseWhenOptions;
32use crate::scalar_fn::fns::cast::Cast;
33use crate::scalar_fn::fns::dynamic::DynamicComparison;
34use crate::scalar_fn::fns::dynamic::DynamicComparisonExpr;
35use crate::scalar_fn::fns::dynamic::Rhs;
36use crate::scalar_fn::fns::ext_storage::ExtStorage;
37use crate::scalar_fn::fns::fill_null::FillNull;
38use crate::scalar_fn::fns::get_item::GetItem;
39use crate::scalar_fn::fns::is_nan::IsNan;
40use crate::scalar_fn::fns::is_not_null::IsNotNull;
41use crate::scalar_fn::fns::is_null::IsNull;
42use crate::scalar_fn::fns::like::Like;
43use crate::scalar_fn::fns::like::LikeOptions;
44use crate::scalar_fn::fns::list_contains::ListContains;
45use crate::scalar_fn::fns::list_contains::ListContainsOptions;
46use crate::scalar_fn::fns::list_length::ListLength;
47use crate::scalar_fn::fns::list_sum::ListSum;
48use crate::scalar_fn::fns::literal::Literal;
49use crate::scalar_fn::fns::mask::Mask;
50use crate::scalar_fn::fns::merge::DuplicateHandling;
51use crate::scalar_fn::fns::merge::Merge;
52use crate::scalar_fn::fns::not::Not;
53use crate::scalar_fn::fns::operators::CompareOperator;
54use crate::scalar_fn::fns::operators::Operator;
55use crate::scalar_fn::fns::pack::Pack;
56use crate::scalar_fn::fns::pack::PackOptions;
57use crate::scalar_fn::fns::replace_time_zone::ReplaceTimeZone;
58use crate::scalar_fn::fns::replace_time_zone::ReplaceTimeZoneOptions;
59use crate::scalar_fn::fns::select::FieldSelection;
60use crate::scalar_fn::fns::select::Select;
61use crate::scalar_fn::fns::variant_get::VariantGet;
62use crate::scalar_fn::fns::variant_get::VariantGetOptions;
63use crate::scalar_fn::fns::variant_get::VariantPath;
64use crate::scalar_fn::fns::zip::Zip;
65
66pub fn root() -> Expression {
71 Expression::Root
72}
73
74pub fn bound_root(dtype: DType) -> BoundExpression {
76 BoundExpression::new_root(dtype)
77}
78
79pub fn is_root(expr: &Expression) -> bool {
81 expr.is_root()
82}
83
84pub fn lit(value: impl Into<Scalar>) -> Expression {
104 Literal.new_expr(value.into(), [])
105}
106
107pub fn bound_lit(value: impl Into<Scalar>) -> BoundExpression {
109 Literal
110 .try_new_bound_expr(value.into(), [])
111 .vortex_expect("literal expressions are always well-typed")
112}
113
114pub fn col(field: impl Into<FieldName>) -> Expression {
125 GetItem.new_expr(field.into(), vec![root()])
126}
127
128pub fn bound_col(field: impl Into<FieldName>, scope: DType) -> BoundExpression {
130 bound_get_item(field, bound_root(scope))
131}
132
133pub fn get_item(field: impl Into<FieldName>, child: Expression) -> Expression {
142 GetItem.new_expr(field.into(), vec![child])
143}
144
145pub fn bound_get_item(field: impl Into<FieldName>, child: BoundExpression) -> BoundExpression {
147 GetItem
148 .try_new_bound_expr(field.into(), [child])
149 .vortex_expect("get-item expressions must reference a field in the child dtype")
150}
151
152pub fn variant_get(
159 child: Expression,
160 path: impl Into<VariantPath>,
161 dtype: Option<DType>,
162) -> Expression {
163 VariantGet.new_expr(VariantGetOptions::new(path.into(), dtype), vec![child])
164}
165
166pub fn bound_variant_get(
168 child: BoundExpression,
169 path: impl Into<VariantPath>,
170 dtype: Option<DType>,
171) -> BoundExpression {
172 VariantGet
173 .try_new_bound_expr(VariantGetOptions::new(path.into(), dtype), [child])
174 .vortex_expect("variant-get expressions require a Variant child")
175}
176
177pub fn case_when(
181 condition: Expression,
182 then_value: Expression,
183 else_value: Expression,
184) -> Expression {
185 let options = CaseWhenOptions {
186 num_when_then_pairs: 1,
187 has_else: true,
188 };
189 CaseWhen.new_expr(options, [condition, then_value, else_value])
190}
191
192pub fn bound_case_when(
194 condition: BoundExpression,
195 then_value: BoundExpression,
196 else_value: BoundExpression,
197) -> BoundExpression {
198 let options = CaseWhenOptions {
199 num_when_then_pairs: 1,
200 has_else: true,
201 };
202 CaseWhen
203 .try_new_bound_expr(options, [condition, then_value, else_value])
204 .vortex_expect("case expressions must have boolean conditions and matching branch dtypes")
205}
206
207pub fn case_when_no_else(condition: Expression, then_value: Expression) -> Expression {
209 let options = CaseWhenOptions {
210 num_when_then_pairs: 1,
211 has_else: false,
212 };
213 CaseWhen.new_expr(options, [condition, then_value])
214}
215
216pub fn bound_case_when_no_else(
218 condition: BoundExpression,
219 then_value: BoundExpression,
220) -> BoundExpression {
221 let options = CaseWhenOptions {
222 num_when_then_pairs: 1,
223 has_else: false,
224 };
225 CaseWhen
226 .try_new_bound_expr(options, [condition, then_value])
227 .vortex_expect("case expressions must have boolean conditions")
228}
229
230pub fn nested_case_when(
232 when_then_pairs: Vec<(Expression, Expression)>,
233 else_value: Option<Expression>,
234) -> Expression {
235 assert!(
236 !when_then_pairs.is_empty(),
237 "nested_case_when requires at least one when/then pair"
238 );
239
240 let has_else = else_value.is_some();
241 let mut children = Vec::with_capacity(when_then_pairs.len() * 2 + usize::from(has_else));
242 for (condition, then_value) in &when_then_pairs {
243 children.push(condition.clone());
244 children.push(then_value.clone());
245 }
246 if let Some(else_expr) = else_value {
247 children.push(else_expr);
248 }
249
250 let Ok(num_when_then_pairs) = u32::try_from(when_then_pairs.len()) else {
251 vortex_panic!("nested_case_when has too many when/then pairs");
252 };
253 let options = CaseWhenOptions {
254 num_when_then_pairs,
255 has_else,
256 };
257 CaseWhen.new_expr(options, children)
258}
259
260pub fn bound_nested_case_when(
262 when_then_pairs: Vec<(BoundExpression, BoundExpression)>,
263 else_value: Option<BoundExpression>,
264) -> BoundExpression {
265 assert!(
266 !when_then_pairs.is_empty(),
267 "nested_case_when requires at least one when/then pair"
268 );
269
270 let Ok(num_when_then_pairs) = u32::try_from(when_then_pairs.len()) else {
271 vortex_panic!("nested_case_when has too many when/then pairs");
272 };
273 let has_else = else_value.is_some();
274 let mut children = Vec::with_capacity(when_then_pairs.len() * 2 + usize::from(has_else));
275 for (condition, then_value) in when_then_pairs {
276 children.push(condition);
277 children.push(then_value);
278 }
279 if let Some(else_expr) = else_value {
280 children.push(else_expr);
281 }
282
283 let options = CaseWhenOptions {
284 num_when_then_pairs,
285 has_else,
286 };
287 CaseWhen
288 .try_new_bound_expr(options, children)
289 .vortex_expect("case expressions must have boolean conditions and matching branch dtypes")
290}
291
292pub fn binary(operator: Operator, lhs: Expression, rhs: Expression) -> Expression {
296 Binary
297 .try_new_expr(operator, [lhs, rhs])
298 .vortex_expect("Failed to create binary expression")
299}
300
301pub fn bound_binary(
303 operator: Operator,
304 lhs: BoundExpression,
305 rhs: BoundExpression,
306) -> BoundExpression {
307 Binary
308 .try_new_bound_expr(operator, [lhs, rhs])
309 .vortex_expect("binary expressions must have compatible operand dtypes")
310}
311
312pub fn eq(lhs: Expression, rhs: Expression) -> Expression {
334 Binary
335 .try_new_expr(Operator::Eq, [lhs, rhs])
336 .vortex_expect("Failed to create Eq binary expression")
337}
338
339pub fn bound_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
341 bound_binary(Operator::Eq, lhs, rhs)
342}
343
344pub fn not_eq(lhs: Expression, rhs: Expression) -> Expression {
366 Binary
367 .try_new_expr(Operator::NotEq, [lhs, rhs])
368 .vortex_expect("Failed to create NotEq binary expression")
369}
370
371pub fn bound_not_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
373 bound_binary(Operator::NotEq, lhs, rhs)
374}
375
376pub fn gt_eq(lhs: Expression, rhs: Expression) -> Expression {
398 Binary
399 .try_new_expr(Operator::Gte, [lhs, rhs])
400 .vortex_expect("Failed to create Gte binary expression")
401}
402
403pub fn bound_gt_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
405 bound_binary(Operator::Gte, lhs, rhs)
406}
407
408pub fn gt(lhs: Expression, rhs: Expression) -> Expression {
430 Binary
431 .try_new_expr(Operator::Gt, [lhs, rhs])
432 .vortex_expect("Failed to create Gt binary expression")
433}
434
435pub fn bound_gt(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
437 bound_binary(Operator::Gt, lhs, rhs)
438}
439
440pub fn lt_eq(lhs: Expression, rhs: Expression) -> Expression {
462 Binary
463 .try_new_expr(Operator::Lte, [lhs, rhs])
464 .vortex_expect("Failed to create Lte binary expression")
465}
466
467pub fn bound_lt_eq(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
469 bound_binary(Operator::Lte, lhs, rhs)
470}
471
472pub fn lt(lhs: Expression, rhs: Expression) -> Expression {
494 Binary
495 .try_new_expr(Operator::Lt, [lhs, rhs])
496 .vortex_expect("Failed to create Lt binary expression")
497}
498
499pub fn bound_lt(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
501 bound_binary(Operator::Lt, lhs, rhs)
502}
503
504pub fn or(lhs: Expression, rhs: Expression) -> Expression {
524 Binary
525 .try_new_expr(Operator::Or, [lhs, rhs])
526 .vortex_expect("Failed to create Or binary expression")
527}
528
529pub fn bound_or(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
531 bound_binary(Operator::Or, lhs, rhs)
532}
533
534pub fn or_collect<I>(iter: I) -> Option<Expression>
541where
542 I: IntoIterator<Item = Expression>,
543{
544 iter.into_iter().reduce_balanced(or)
545}
546
547pub fn bound_or_collect<I>(iter: I) -> Option<BoundExpression>
549where
550 I: IntoIterator<Item = BoundExpression>,
551{
552 iter.into_iter().reduce_balanced(bound_or)
553}
554
555pub fn and(lhs: Expression, rhs: Expression) -> Expression {
575 Binary
576 .try_new_expr(Operator::And, [lhs, rhs])
577 .vortex_expect("Failed to create And binary expression")
578}
579
580pub fn bound_and(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
582 bound_binary(Operator::And, lhs, rhs)
583}
584
585pub fn and_collect<I>(iter: I) -> Option<Expression>
592where
593 I: IntoIterator<Item = Expression>,
594{
595 iter.into_iter().reduce_balanced(and)
596}
597
598pub fn bound_and_collect<I>(iter: I) -> Option<BoundExpression>
600where
601 I: IntoIterator<Item = BoundExpression>,
602{
603 iter.into_iter().reduce_balanced(bound_and)
604}
605
606pub fn checked_add(lhs: Expression, rhs: Expression) -> Expression {
625 Binary
626 .try_new_expr(Operator::Add, [lhs, rhs])
627 .vortex_expect("Failed to create Add binary expression")
628}
629
630pub fn bound_checked_add(lhs: BoundExpression, rhs: BoundExpression) -> BoundExpression {
632 bound_binary(Operator::Add, lhs, rhs)
633}
634
635pub fn not(operand: Expression) -> Expression {
646 Not.new_expr(EmptyOptions, vec![operand])
647}
648
649pub fn bound_not(operand: BoundExpression) -> BoundExpression {
651 Not.try_new_bound_expr(EmptyOptions, [operand])
652 .vortex_expect("not expressions require a boolean operand")
653}
654
655pub fn between(
673 arr: Expression,
674 lower: Expression,
675 upper: Expression,
676 options: BetweenOptions,
677) -> Expression {
678 Between
679 .try_new_expr(options, [arr, lower, upper])
680 .vortex_expect("Failed to create Between expression")
681}
682
683pub fn bound_between(
685 arr: BoundExpression,
686 lower: BoundExpression,
687 upper: BoundExpression,
688 options: BetweenOptions,
689) -> BoundExpression {
690 Between
691 .try_new_bound_expr(options, [arr, lower, upper])
692 .vortex_expect("between expressions require compatible operand dtypes")
693}
694
695pub fn select(field_names: impl Into<FieldNames>, child: Expression) -> Expression {
705 Select
706 .try_new_expr(FieldSelection::Include(field_names.into()), [child])
707 .vortex_expect("Failed to create Select expression")
708}
709
710pub fn bound_select(field_names: impl Into<FieldNames>, child: BoundExpression) -> BoundExpression {
712 Select
713 .try_new_bound_expr(FieldSelection::Include(field_names.into()), [child])
714 .vortex_expect("select expressions require fields from a struct child")
715}
716
717pub fn select_exclude(fields: impl Into<FieldNames>, child: Expression) -> Expression {
726 Select
727 .try_new_expr(FieldSelection::Exclude(fields.into()), [child])
728 .vortex_expect("Failed to create Select expression")
729}
730
731pub fn bound_select_exclude(
733 fields: impl Into<FieldNames>,
734 child: BoundExpression,
735) -> BoundExpression {
736 Select
737 .try_new_bound_expr(FieldSelection::Exclude(fields.into()), [child])
738 .vortex_expect("select expressions require fields from a struct child")
739}
740
741pub fn pack(
751 elements: impl IntoIterator<Item = (impl Into<FieldName>, Expression)>,
752 nullability: Nullability,
753) -> Expression {
754 let (names, values): (Vec<_>, Vec<_>) = elements
755 .into_iter()
756 .map(|(name, value)| (name.into(), value))
757 .unzip();
758 Pack.new_expr(
759 PackOptions {
760 names: names.into(),
761 nullability,
762 },
763 values,
764 )
765}
766
767pub fn bound_pack(
769 elements: impl IntoIterator<Item = (impl Into<FieldName>, BoundExpression)>,
770 nullability: Nullability,
771) -> BoundExpression {
772 let (names, values): (Vec<_>, Vec<_>) = elements
773 .into_iter()
774 .map(|(name, value)| (name.into(), value))
775 .unzip();
776 Pack.try_new_bound_expr(
777 PackOptions {
778 names: names.into(),
779 nullability,
780 },
781 values,
782 )
783 .vortex_expect("pack expressions must have one name per child")
784}
785
786pub fn cast(child: Expression, target: DType) -> Expression {
798 Cast.try_new_expr(target, [child])
799 .vortex_expect("Failed to create Cast expression")
800}
801
802pub fn bound_cast(child: BoundExpression, target: DType) -> BoundExpression {
804 Cast.try_new_bound_expr(target, [child])
805 .vortex_expect("cast expressions require a supported source and target dtype")
806}
807
808pub fn fill_null(child: Expression, fill_value: Expression) -> Expression {
817 FillNull.new_expr(EmptyOptions, [child, fill_value])
818}
819
820pub fn bound_fill_null(child: BoundExpression, fill_value: BoundExpression) -> BoundExpression {
822 FillNull
823 .try_new_bound_expr(EmptyOptions, [child, fill_value])
824 .vortex_expect("fill-null expressions require compatible child and fill dtypes")
825}
826
827pub fn is_null(child: Expression) -> Expression {
838 IsNull.new_expr(EmptyOptions, vec![child])
839}
840
841pub fn bound_is_null(child: BoundExpression) -> BoundExpression {
843 IsNull
844 .try_new_bound_expr(EmptyOptions, [child])
845 .vortex_expect("is-null expressions are always well-typed")
846}
847
848pub fn is_nan(child: Expression) -> Expression {
860 IsNan.new_expr(EmptyOptions, vec![child])
861}
862
863pub fn bound_is_nan(child: BoundExpression) -> BoundExpression {
865 IsNan
866 .try_new_bound_expr(EmptyOptions, [child])
867 .vortex_expect("is-nan expressions are always well-typed")
868}
869
870pub fn is_not_null(child: Expression) -> Expression {
881 IsNotNull.new_expr(EmptyOptions, vec![child])
882}
883
884pub fn bound_is_not_null(child: BoundExpression) -> BoundExpression {
886 IsNotNull
887 .try_new_bound_expr(EmptyOptions, [child])
888 .vortex_expect("is-not-null expressions are always well-typed")
889}
890
891pub fn like(child: Expression, pattern: Expression) -> Expression {
895 Like.new_expr(
896 LikeOptions {
897 negated: false,
898 case_insensitive: false,
899 },
900 [child, pattern],
901 )
902}
903
904pub fn bound_like(child: BoundExpression, pattern: BoundExpression) -> BoundExpression {
906 bound_like_with_options(child, pattern, false, false)
907}
908
909pub fn ilike(child: Expression, pattern: Expression) -> Expression {
911 Like.new_expr(
912 LikeOptions {
913 negated: false,
914 case_insensitive: true,
915 },
916 [child, pattern],
917 )
918}
919
920pub fn bound_ilike(child: BoundExpression, pattern: BoundExpression) -> BoundExpression {
922 bound_like_with_options(child, pattern, false, true)
923}
924
925pub fn not_like(child: Expression, pattern: Expression) -> Expression {
927 Like.new_expr(
928 LikeOptions {
929 negated: true,
930 case_insensitive: false,
931 },
932 [child, pattern],
933 )
934}
935
936pub fn bound_not_like(child: BoundExpression, pattern: BoundExpression) -> BoundExpression {
938 bound_like_with_options(child, pattern, true, false)
939}
940
941pub fn not_ilike(child: Expression, pattern: Expression) -> Expression {
943 Like.new_expr(
944 LikeOptions {
945 negated: true,
946 case_insensitive: true,
947 },
948 [child, pattern],
949 )
950}
951
952pub fn bound_not_ilike(child: BoundExpression, pattern: BoundExpression) -> BoundExpression {
954 bound_like_with_options(child, pattern, true, true)
955}
956
957fn bound_like_with_options(
958 child: BoundExpression,
959 pattern: BoundExpression,
960 negated: bool,
961 case_insensitive: bool,
962) -> BoundExpression {
963 Like.try_new_bound_expr(
964 LikeOptions {
965 negated,
966 case_insensitive,
967 },
968 [child, pattern],
969 )
970 .vortex_expect("like expressions require UTF-8 or binary operands")
971}
972
973pub fn mask(array: Expression, mask: Expression) -> Expression {
977 Mask.new_expr(EmptyOptions, [array, mask])
978}
979
980pub fn bound_mask(array: BoundExpression, mask: BoundExpression) -> BoundExpression {
982 Mask.try_new_bound_expr(EmptyOptions, [array, mask])
983 .vortex_expect("mask expressions require a boolean mask")
984}
985
986pub fn merge(elements: impl IntoIterator<Item = impl Into<Expression>>) -> Expression {
999 use itertools::Itertools as _;
1000 let values = elements.into_iter().map(|value| value.into()).collect_vec();
1001 Merge.new_expr(DuplicateHandling::default(), values)
1002}
1003
1004pub fn bound_merge(elements: impl IntoIterator<Item = BoundExpression>) -> BoundExpression {
1006 bound_merge_opts(elements, DuplicateHandling::default())
1007}
1008
1009pub fn merge_opts(
1011 elements: impl IntoIterator<Item = impl Into<Expression>>,
1012 duplicate_handling: DuplicateHandling,
1013) -> Expression {
1014 use itertools::Itertools as _;
1015 let values = elements.into_iter().map(|value| value.into()).collect_vec();
1016 Merge.new_expr(duplicate_handling, values)
1017}
1018
1019pub fn bound_merge_opts(
1021 elements: impl IntoIterator<Item = BoundExpression>,
1022 duplicate_handling: DuplicateHandling,
1023) -> BoundExpression {
1024 Merge
1025 .try_new_bound_expr(duplicate_handling, elements)
1026 .vortex_expect("merge expressions require non-nullable struct children")
1027}
1028
1029pub fn zip_expr(mask: Expression, if_true: Expression, if_false: Expression) -> Expression {
1038 Zip.new_expr(EmptyOptions, [if_true, if_false, mask])
1039}
1040
1041pub fn bound_zip_expr(
1043 mask: BoundExpression,
1044 if_true: BoundExpression,
1045 if_false: BoundExpression,
1046) -> BoundExpression {
1047 Zip.try_new_bound_expr(EmptyOptions, [if_true, if_false, mask])
1048 .vortex_expect("zip expressions require a boolean mask and compatible value dtypes")
1049}
1050
1051pub fn dynamic_with_options(options: DynamicComparisonExpr, lhs: Expression) -> Expression {
1055 DynamicComparison.new_expr(options, [lhs])
1056}
1057
1058pub fn bound_dynamic_with_options(
1060 options: DynamicComparisonExpr,
1061 lhs: BoundExpression,
1062) -> BoundExpression {
1063 DynamicComparison
1064 .try_new_bound_expr(options, [lhs])
1065 .vortex_expect("dynamic comparisons require a compatible left-hand dtype")
1066}
1067
1068pub fn dynamic(
1070 operator: CompareOperator,
1071 rhs_value: impl Fn() -> Option<ScalarValue> + Send + Sync + 'static,
1072 rhs_dtype: DType,
1073 default: bool,
1074 lhs: Expression,
1075) -> Expression {
1076 dynamic_with_options(
1077 DynamicComparisonExpr {
1078 operator,
1079 rhs: Arc::new(Rhs {
1080 value: Arc::new(rhs_value),
1081 dtype: rhs_dtype,
1082 }),
1083 default,
1084 },
1085 lhs,
1086 )
1087}
1088
1089pub fn bound_dynamic(
1091 operator: CompareOperator,
1092 rhs_value: impl Fn() -> Option<ScalarValue> + Send + Sync + 'static,
1093 rhs_dtype: DType,
1094 default: bool,
1095 lhs: BoundExpression,
1096) -> BoundExpression {
1097 bound_dynamic_with_options(
1098 DynamicComparisonExpr {
1099 operator,
1100 rhs: Arc::new(Rhs {
1101 value: Arc::new(rhs_value),
1102 dtype: rhs_dtype,
1103 }),
1104 default,
1105 },
1106 lhs,
1107 )
1108}
1109
1110pub fn list_contains(list: Expression, value: Expression) -> Expression {
1122 list_contains_opts(list, value, ListContainsOptions::default())
1123}
1124
1125pub fn list_contains_opts(
1128 list: Expression,
1129 value: Expression,
1130 options: ListContainsOptions,
1131) -> Expression {
1132 ListContains.new_expr(options, [list, value])
1133}
1134
1135pub fn in_list(needle: Expression, list: Expression) -> Expression {
1144 list_contains_opts(
1145 list,
1146 needle,
1147 ListContainsOptions {
1148 sql_null_semantics: true,
1149 },
1150 )
1151}
1152
1153pub fn in_list_of(
1176 needle: Expression,
1177 element_dtype: DType,
1178 elements: Vec<Scalar>,
1179) -> VortexResult<Expression> {
1180 let element_dtype = if elements.iter().any(Scalar::is_null) {
1181 element_dtype.as_nullable()
1182 } else {
1183 element_dtype.as_nonnullable()
1184 };
1185 let mut elements = elements
1186 .iter()
1187 .map(|element| {
1188 vortex_ensure!(
1189 element.dtype() == &DType::Null
1190 || element.dtype().eq_ignore_nullability(&element_dtype),
1191 "IN list element {element} is not of type {element_dtype}"
1192 );
1193 element.cast(&element_dtype)
1194 })
1195 .collect::<VortexResult<Vec<_>>>()?;
1196
1197 if let Some((low, high)) = contiguous_integer_bounds(&elements) {
1198 let options = BetweenOptions {
1199 lower_strict: StrictComparison::NonStrict,
1200 upper_strict: StrictComparison::NonStrict,
1201 };
1202 return Ok(between(
1203 needle,
1204 lit(low.clone()),
1205 lit(high.clone()),
1206 options,
1207 ));
1208 }
1209
1210 if elements
1212 .windows(2)
1213 .all(|pair| pair[0].partial_cmp(&pair[1]).is_some())
1214 {
1215 elements.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1216 }
1217 let list = Scalar::list(Arc::new(element_dtype), elements, Nullability::NonNullable);
1218 Ok(in_list(needle, lit(list)))
1219}
1220
1221fn contiguous_integer_bounds(scalars: &[Scalar]) -> Option<(&Scalar, &Scalar)> {
1224 let mut values = scalars
1225 .iter()
1226 .map(|scalar| {
1227 let primitive = scalar.as_primitive_opt()?;
1228 if !primitive.ptype().is_int() {
1229 return None;
1230 }
1231 let value = if primitive.ptype().is_unsigned_int() {
1232 primitive.as_opt::<u64>()??.into()
1233 } else {
1234 i128::from(primitive.as_opt::<i64>()??)
1235 };
1236 Some((value, scalar))
1237 })
1238 .collect::<Option<Vec<(i128, &Scalar)>>>()?;
1239 values.sort_unstable_by_key(|(value, _)| *value);
1240 values.dedup_by_key(|(value, _)| *value);
1241 let (low, low_scalar) = values.first()?;
1242 let (high, high_scalar) = values.last()?;
1243 (high - low + 1 == i128::try_from(values.len()).ok()?).then_some((*low_scalar, *high_scalar))
1244}
1245
1246pub fn bound_list_contains(list: BoundExpression, value: BoundExpression) -> BoundExpression {
1248 ListContains
1249 .try_new_bound_expr(ListContainsOptions::default(), [list, value])
1250 .vortex_expect("list-contains expressions require a list child")
1251}
1252
1253pub fn bound_in_list(needle: BoundExpression, list: BoundExpression) -> BoundExpression {
1255 ListContains
1256 .try_new_bound_expr(
1257 ListContainsOptions {
1258 sql_null_semantics: true,
1259 },
1260 [list, needle],
1261 )
1262 .vortex_expect("list-contains expressions require a list child")
1263}
1264
1265pub fn byte_length(input: Expression) -> Expression {
1275 ByteLength.new_expr(EmptyOptions, [input])
1276}
1277
1278pub fn bound_byte_length(input: BoundExpression) -> BoundExpression {
1280 ByteLength
1281 .try_new_bound_expr(EmptyOptions, [input])
1282 .vortex_expect("byte-length expressions require a variable-length binary child")
1283}
1284
1285pub fn ext_storage(input: Expression) -> Expression {
1294 ExtStorage.new_expr(EmptyOptions, [input])
1295}
1296
1297pub fn bound_ext_storage(input: BoundExpression) -> BoundExpression {
1299 ExtStorage
1300 .try_new_bound_expr(EmptyOptions, [input])
1301 .vortex_expect("extension-storage expressions require an extension child")
1302}
1303
1304pub fn list_length(input: Expression) -> Expression {
1315 ListLength.new_expr(EmptyOptions, [input])
1316}
1317
1318pub fn bound_list_length(input: BoundExpression) -> BoundExpression {
1320 ListLength
1321 .try_new_bound_expr(EmptyOptions, [input])
1322 .vortex_expect("list-length expressions require a list child")
1323}
1324
1325pub fn list_sum(input: Expression) -> Expression {
1340 ListSum.new_expr(NumericalAggregateOpts::default(), [input])
1341}
1342
1343pub fn bound_list_sum(input: BoundExpression) -> BoundExpression {
1345 ListSum
1346 .try_new_bound_expr(NumericalAggregateOpts::default(), [input])
1347 .vortex_expect("list-sum expressions require a numeric list child")
1348}
1349
1350pub fn list_sum_opts(input: Expression, options: NumericalAggregateOpts) -> Expression {
1353 ListSum.new_expr(options, [input])
1354}
1355
1356pub fn bound_list_sum_opts(
1358 input: BoundExpression,
1359 options: NumericalAggregateOpts,
1360) -> BoundExpression {
1361 ListSum
1362 .try_new_bound_expr(options, [input])
1363 .vortex_expect("list-sum expressions require a numeric list child")
1364}
1365
1366pub fn replace_time_zone(
1371 input: Expression,
1372 ambiguous: Expression,
1373 options: ReplaceTimeZoneOptions,
1374) -> Expression {
1375 ReplaceTimeZone.new_expr(options, [input, ambiguous])
1376}
1377
1378pub fn bound_replace_time_zone(
1380 input: BoundExpression,
1381 ambiguous: BoundExpression,
1382 options: ReplaceTimeZoneOptions,
1383) -> BoundExpression {
1384 ReplaceTimeZone
1385 .try_new_bound_expr(options, [input, ambiguous])
1386 .vortex_expect("timezone replacement requires a timestamp and a UTF-8 ambiguity policy")
1387}
1388
1389pub mod bound {
1395 pub use super::bound_and as and;
1396 pub use super::bound_and_collect as and_collect;
1397 pub use super::bound_between as between;
1398 pub use super::bound_binary as binary;
1399 pub use super::bound_byte_length as byte_length;
1400 pub use super::bound_case_when as case_when;
1401 pub use super::bound_case_when_no_else as case_when_no_else;
1402 pub use super::bound_cast as cast;
1403 pub use super::bound_checked_add as checked_add;
1404 pub use super::bound_col as col;
1405 pub use super::bound_dynamic as dynamic;
1406 pub use super::bound_dynamic_with_options as dynamic_with_options;
1407 pub use super::bound_eq as eq;
1408 pub use super::bound_ext_storage as ext_storage;
1409 pub use super::bound_fill_null as fill_null;
1410 pub use super::bound_get_item as get_item;
1411 pub use super::bound_gt as gt;
1412 pub use super::bound_gt_eq as gt_eq;
1413 pub use super::bound_ilike as ilike;
1414 pub use super::bound_in_list as in_list;
1415 pub use super::bound_is_nan as is_nan;
1416 pub use super::bound_is_not_null as is_not_null;
1417 pub use super::bound_is_null as is_null;
1418 pub use super::bound_like as like;
1419 pub use super::bound_list_contains as list_contains;
1420 pub use super::bound_list_length as list_length;
1421 pub use super::bound_list_sum as list_sum;
1422 pub use super::bound_list_sum_opts as list_sum_opts;
1423 pub use super::bound_lit as lit;
1424 pub use super::bound_lt as lt;
1425 pub use super::bound_lt_eq as lt_eq;
1426 pub use super::bound_mask as mask;
1427 pub use super::bound_merge as merge;
1428 pub use super::bound_merge_opts as merge_opts;
1429 pub use super::bound_nested_case_when as nested_case_when;
1430 pub use super::bound_not as not;
1431 pub use super::bound_not_eq as not_eq;
1432 pub use super::bound_not_ilike as not_ilike;
1433 pub use super::bound_not_like as not_like;
1434 pub use super::bound_or as or;
1435 pub use super::bound_or_collect as or_collect;
1436 pub use super::bound_pack as pack;
1437 pub use super::bound_replace_time_zone as replace_time_zone;
1438 pub use super::bound_root as root;
1439 pub use super::bound_select as select;
1440 pub use super::bound_select_exclude as select_exclude;
1441 pub use super::bound_variant_get as variant_get;
1442 pub use super::bound_zip_expr as zip_expr;
1443}