1use crate::expr::{
21 AggregateFunction, BinaryExpr, Cast, Exists, GroupingSet, InList, InSubquery,
22 NullTreatment, Placeholder, TryCast, Unnest, WildcardOptions, WindowFunction,
23};
24use crate::function::{
25 AccumulatorArgs, AccumulatorFactoryFunction, PartitionEvaluatorFactory,
26 StateFieldsArgs,
27};
28use crate::ptr_eq::PtrEq;
29use crate::select_expr::SelectExpr;
30use crate::{
31 conditional_expressions::CaseBuilder, expr::Sort, logical_plan::Subquery,
32 AggregateUDF, Expr, LimitEffect, LogicalPlan, Operator, PartitionEvaluator,
33 ScalarFunctionArgs, ScalarFunctionImplementation, ScalarUDF, Signature, Volatility,
34};
35use crate::{
36 AggregateUDFImpl, ColumnarValue, ScalarUDFImpl, WindowFrame, WindowUDF, WindowUDFImpl,
37};
38use arrow::compute::kernels::cast_utils::{
39 parse_interval_day_time, parse_interval_month_day_nano, parse_interval_year_month,
40};
41use arrow::datatypes::{DataType, Field, FieldRef};
42use datafusion_common::{plan_err, Column, Result, ScalarValue, Spans, TableReference};
43use datafusion_functions_window_common::field::WindowUDFFieldArgs;
44use datafusion_functions_window_common::partition::PartitionEvaluatorArgs;
45use datafusion_physical_expr_common::physical_expr::PhysicalExpr;
46use std::any::Any;
47use std::collections::HashMap;
48use std::fmt::Debug;
49use std::hash::Hash;
50use std::ops::Not;
51use std::sync::Arc;
52
53pub fn col(ident: impl Into<Column>) -> Expr {
69 Expr::Column(ident.into())
70}
71
72pub fn out_ref_col(dt: DataType, ident: impl Into<Column>) -> Expr {
77 out_ref_col_with_metadata(dt, HashMap::new(), ident)
78}
79
80pub fn out_ref_col_with_metadata(
82 dt: DataType,
83 metadata: HashMap<String, String>,
84 ident: impl Into<Column>,
85) -> Expr {
86 let column = ident.into();
87 let field: FieldRef =
88 Arc::new(Field::new(column.name(), dt, true).with_metadata(metadata));
89 Expr::OuterReferenceColumn(field, column)
90}
91
92pub fn ident(name: impl Into<String>) -> Expr {
111 Expr::Column(Column::from_name(name))
112}
113
114pub fn placeholder(id: impl Into<String>) -> Expr {
126 Expr::Placeholder(Placeholder {
127 id: id.into(),
128 field: None,
129 })
130}
131
132pub fn wildcard() -> SelectExpr {
142 SelectExpr::Wildcard(WildcardOptions::default())
143}
144
145pub fn wildcard_with_options(options: WildcardOptions) -> SelectExpr {
147 SelectExpr::Wildcard(options)
148}
149
150pub fn qualified_wildcard(qualifier: impl Into<TableReference>) -> SelectExpr {
161 SelectExpr::QualifiedWildcard(qualifier.into(), WildcardOptions::default())
162}
163
164pub fn qualified_wildcard_with_options(
166 qualifier: impl Into<TableReference>,
167 options: WildcardOptions,
168) -> SelectExpr {
169 SelectExpr::QualifiedWildcard(qualifier.into(), options)
170}
171
172pub fn binary_expr(left: Expr, op: Operator, right: Expr) -> Expr {
174 Expr::BinaryExpr(BinaryExpr::new(Box::new(left), op, Box::new(right)))
175}
176
177pub fn and(left: Expr, right: Expr) -> Expr {
179 Expr::BinaryExpr(BinaryExpr::new(
180 Box::new(left),
181 Operator::And,
182 Box::new(right),
183 ))
184}
185
186pub fn or(left: Expr, right: Expr) -> Expr {
188 Expr::BinaryExpr(BinaryExpr::new(
189 Box::new(left),
190 Operator::Or,
191 Box::new(right),
192 ))
193}
194
195pub fn not(expr: Expr) -> Expr {
197 expr.not()
198}
199
200pub fn bitwise_and(left: Expr, right: Expr) -> Expr {
202 Expr::BinaryExpr(BinaryExpr::new(
203 Box::new(left),
204 Operator::BitwiseAnd,
205 Box::new(right),
206 ))
207}
208
209pub fn bitwise_or(left: Expr, right: Expr) -> Expr {
211 Expr::BinaryExpr(BinaryExpr::new(
212 Box::new(left),
213 Operator::BitwiseOr,
214 Box::new(right),
215 ))
216}
217
218pub fn bitwise_xor(left: Expr, right: Expr) -> Expr {
220 Expr::BinaryExpr(BinaryExpr::new(
221 Box::new(left),
222 Operator::BitwiseXor,
223 Box::new(right),
224 ))
225}
226
227pub fn bitwise_shift_right(left: Expr, right: Expr) -> Expr {
229 Expr::BinaryExpr(BinaryExpr::new(
230 Box::new(left),
231 Operator::BitwiseShiftRight,
232 Box::new(right),
233 ))
234}
235
236pub fn bitwise_shift_left(left: Expr, right: Expr) -> Expr {
238 Expr::BinaryExpr(BinaryExpr::new(
239 Box::new(left),
240 Operator::BitwiseShiftLeft,
241 Box::new(right),
242 ))
243}
244
245pub fn in_list(expr: Expr, list: Vec<Expr>, negated: bool) -> Expr {
247 Expr::InList(InList::new(Box::new(expr), list, negated))
248}
249
250pub fn exists(subquery: Arc<LogicalPlan>) -> Expr {
252 let outer_ref_columns = subquery.all_out_ref_exprs();
253 Expr::Exists(Exists {
254 subquery: Subquery {
255 subquery,
256 outer_ref_columns,
257 spans: Spans::new(),
258 },
259 negated: false,
260 })
261}
262
263pub fn not_exists(subquery: Arc<LogicalPlan>) -> Expr {
265 let outer_ref_columns = subquery.all_out_ref_exprs();
266 Expr::Exists(Exists {
267 subquery: Subquery {
268 subquery,
269 outer_ref_columns,
270 spans: Spans::new(),
271 },
272 negated: true,
273 })
274}
275
276pub fn in_subquery(expr: Expr, subquery: Arc<LogicalPlan>) -> Expr {
278 let outer_ref_columns = subquery.all_out_ref_exprs();
279 Expr::InSubquery(InSubquery::new(
280 Box::new(expr),
281 Subquery {
282 subquery,
283 outer_ref_columns,
284 spans: Spans::new(),
285 },
286 false,
287 ))
288}
289
290pub fn not_in_subquery(expr: Expr, subquery: Arc<LogicalPlan>) -> Expr {
292 let outer_ref_columns = subquery.all_out_ref_exprs();
293 Expr::InSubquery(InSubquery::new(
294 Box::new(expr),
295 Subquery {
296 subquery,
297 outer_ref_columns,
298 spans: Spans::new(),
299 },
300 true,
301 ))
302}
303
304pub fn scalar_subquery(subquery: Arc<LogicalPlan>) -> Expr {
306 let outer_ref_columns = subquery.all_out_ref_exprs();
307 Expr::ScalarSubquery(Subquery {
308 subquery,
309 outer_ref_columns,
310 spans: Spans::new(),
311 })
312}
313
314pub fn grouping_set(exprs: Vec<Vec<Expr>>) -> Expr {
316 Expr::GroupingSet(GroupingSet::GroupingSets(exprs))
317}
318
319pub fn cube(exprs: Vec<Expr>) -> Expr {
321 Expr::GroupingSet(GroupingSet::Cube(exprs))
322}
323
324pub fn rollup(exprs: Vec<Expr>) -> Expr {
326 Expr::GroupingSet(GroupingSet::Rollup(exprs))
327}
328
329pub fn cast(expr: Expr, data_type: DataType) -> Expr {
331 Expr::Cast(Cast::new(Box::new(expr), data_type))
332}
333
334pub fn try_cast(expr: Expr, data_type: DataType) -> Expr {
336 Expr::TryCast(TryCast::new(Box::new(expr), data_type))
337}
338
339pub fn is_null(expr: Expr) -> Expr {
341 Expr::IsNull(Box::new(expr))
342}
343
344pub fn is_true(expr: Expr) -> Expr {
346 Expr::IsTrue(Box::new(expr))
347}
348
349pub fn is_not_true(expr: Expr) -> Expr {
351 Expr::IsNotTrue(Box::new(expr))
352}
353
354pub fn is_false(expr: Expr) -> Expr {
356 Expr::IsFalse(Box::new(expr))
357}
358
359pub fn is_not_false(expr: Expr) -> Expr {
361 Expr::IsNotFalse(Box::new(expr))
362}
363
364pub fn is_unknown(expr: Expr) -> Expr {
366 Expr::IsUnknown(Box::new(expr))
367}
368
369pub fn is_not_unknown(expr: Expr) -> Expr {
371 Expr::IsNotUnknown(Box::new(expr))
372}
373
374pub fn case(expr: Expr) -> CaseBuilder {
376 CaseBuilder::new(Some(Box::new(expr)), vec![], vec![], None)
377}
378
379pub fn when(when: Expr, then: Expr) -> CaseBuilder {
381 CaseBuilder::new(None, vec![when], vec![then], None)
382}
383
384pub fn unnest(expr: Expr) -> Expr {
386 Expr::Unnest(Unnest {
387 expr: Box::new(expr),
388 })
389}
390
391pub fn create_udf(
404 name: &str,
405 input_types: Vec<DataType>,
406 return_type: DataType,
407 volatility: Volatility,
408 fun: ScalarFunctionImplementation,
409) -> ScalarUDF {
410 ScalarUDF::from(SimpleScalarUDF::new(
411 name,
412 input_types,
413 return_type,
414 volatility,
415 fun,
416 ))
417}
418
419#[derive(PartialEq, Eq, Hash)]
422pub struct SimpleScalarUDF {
423 name: String,
424 signature: Signature,
425 return_type: DataType,
426 fun: PtrEq<ScalarFunctionImplementation>,
427}
428
429impl Debug for SimpleScalarUDF {
430 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
431 f.debug_struct("SimpleScalarUDF")
432 .field("name", &self.name)
433 .field("signature", &self.signature)
434 .field("return_type", &self.return_type)
435 .field("fun", &"<FUNC>")
436 .finish()
437 }
438}
439
440impl SimpleScalarUDF {
441 pub fn new(
444 name: impl Into<String>,
445 input_types: Vec<DataType>,
446 return_type: DataType,
447 volatility: Volatility,
448 fun: ScalarFunctionImplementation,
449 ) -> Self {
450 Self::new_with_signature(
451 name,
452 Signature::exact(input_types, volatility),
453 return_type,
454 fun,
455 )
456 }
457
458 pub fn new_with_signature(
461 name: impl Into<String>,
462 signature: Signature,
463 return_type: DataType,
464 fun: ScalarFunctionImplementation,
465 ) -> Self {
466 Self {
467 name: name.into(),
468 signature,
469 return_type,
470 fun: fun.into(),
471 }
472 }
473}
474
475impl ScalarUDFImpl for SimpleScalarUDF {
476 fn as_any(&self) -> &dyn Any {
477 self
478 }
479
480 fn name(&self) -> &str {
481 &self.name
482 }
483
484 fn signature(&self) -> &Signature {
485 &self.signature
486 }
487
488 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
489 Ok(self.return_type.clone())
490 }
491
492 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
493 (self.fun)(&args.args)
494 }
495}
496
497pub fn create_udaf(
500 name: &str,
501 input_type: Vec<DataType>,
502 return_type: Arc<DataType>,
503 volatility: Volatility,
504 accumulator: AccumulatorFactoryFunction,
505 state_type: Arc<Vec<DataType>>,
506) -> AggregateUDF {
507 let return_type = Arc::unwrap_or_clone(return_type);
508 let state_type = Arc::unwrap_or_clone(state_type);
509 let state_fields = state_type
510 .into_iter()
511 .enumerate()
512 .map(|(i, t)| Field::new(format!("{i}"), t, true))
513 .map(Arc::new)
514 .collect::<Vec<_>>();
515 AggregateUDF::from(SimpleAggregateUDF::new(
516 name,
517 input_type,
518 return_type,
519 volatility,
520 accumulator,
521 state_fields,
522 ))
523}
524
525#[derive(PartialEq, Eq, Hash)]
528pub struct SimpleAggregateUDF {
529 name: String,
530 signature: Signature,
531 return_type: DataType,
532 accumulator: PtrEq<AccumulatorFactoryFunction>,
533 state_fields: Vec<FieldRef>,
534}
535
536impl Debug for SimpleAggregateUDF {
537 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
538 f.debug_struct("SimpleAggregateUDF")
539 .field("name", &self.name)
540 .field("signature", &self.signature)
541 .field("return_type", &self.return_type)
542 .field("fun", &"<FUNC>")
543 .finish()
544 }
545}
546
547impl SimpleAggregateUDF {
548 pub fn new(
551 name: impl Into<String>,
552 input_type: Vec<DataType>,
553 return_type: DataType,
554 volatility: Volatility,
555 accumulator: AccumulatorFactoryFunction,
556 state_fields: Vec<FieldRef>,
557 ) -> Self {
558 let name = name.into();
559 let signature = Signature::exact(input_type, volatility);
560 Self {
561 name,
562 signature,
563 return_type,
564 accumulator: accumulator.into(),
565 state_fields,
566 }
567 }
568
569 pub fn new_with_signature(
572 name: impl Into<String>,
573 signature: Signature,
574 return_type: DataType,
575 accumulator: AccumulatorFactoryFunction,
576 state_fields: Vec<FieldRef>,
577 ) -> Self {
578 let name = name.into();
579 Self {
580 name,
581 signature,
582 return_type,
583 accumulator: accumulator.into(),
584 state_fields,
585 }
586 }
587}
588
589impl AggregateUDFImpl for SimpleAggregateUDF {
590 fn as_any(&self) -> &dyn Any {
591 self
592 }
593
594 fn name(&self) -> &str {
595 &self.name
596 }
597
598 fn signature(&self) -> &Signature {
599 &self.signature
600 }
601
602 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
603 Ok(self.return_type.clone())
604 }
605
606 fn accumulator(
607 &self,
608 acc_args: AccumulatorArgs,
609 ) -> Result<Box<dyn crate::Accumulator>> {
610 (self.accumulator)(acc_args)
611 }
612
613 fn state_fields(&self, _args: StateFieldsArgs) -> Result<Vec<FieldRef>> {
614 Ok(self.state_fields.clone())
615 }
616}
617
618pub fn create_udwf(
624 name: &str,
625 input_type: DataType,
626 return_type: Arc<DataType>,
627 volatility: Volatility,
628 partition_evaluator_factory: PartitionEvaluatorFactory,
629) -> WindowUDF {
630 let return_type = Arc::unwrap_or_clone(return_type);
631 WindowUDF::from(SimpleWindowUDF::new(
632 name,
633 input_type,
634 return_type,
635 volatility,
636 partition_evaluator_factory,
637 ))
638}
639
640#[derive(PartialEq, Eq, Hash)]
643pub struct SimpleWindowUDF {
644 name: String,
645 signature: Signature,
646 return_type: DataType,
647 partition_evaluator_factory: PtrEq<PartitionEvaluatorFactory>,
648}
649
650impl Debug for SimpleWindowUDF {
651 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
652 f.debug_struct("WindowUDF")
653 .field("name", &self.name)
654 .field("signature", &self.signature)
655 .field("return_type", &"<func>")
656 .field("partition_evaluator_factory", &"<FUNC>")
657 .finish()
658 }
659}
660
661impl SimpleWindowUDF {
662 pub fn new(
665 name: impl Into<String>,
666 input_type: DataType,
667 return_type: DataType,
668 volatility: Volatility,
669 partition_evaluator_factory: PartitionEvaluatorFactory,
670 ) -> Self {
671 let name = name.into();
672 let signature = Signature::exact([input_type].to_vec(), volatility);
673 Self {
674 name,
675 signature,
676 return_type,
677 partition_evaluator_factory: partition_evaluator_factory.into(),
678 }
679 }
680}
681
682impl WindowUDFImpl for SimpleWindowUDF {
683 fn as_any(&self) -> &dyn Any {
684 self
685 }
686
687 fn name(&self) -> &str {
688 &self.name
689 }
690
691 fn signature(&self) -> &Signature {
692 &self.signature
693 }
694
695 fn partition_evaluator(
696 &self,
697 _partition_evaluator_args: PartitionEvaluatorArgs,
698 ) -> Result<Box<dyn PartitionEvaluator>> {
699 (self.partition_evaluator_factory)()
700 }
701
702 fn field(&self, field_args: WindowUDFFieldArgs) -> Result<FieldRef> {
703 Ok(Arc::new(Field::new(
704 field_args.name(),
705 self.return_type.clone(),
706 true,
707 )))
708 }
709
710 fn limit_effect(&self, _args: &[Arc<dyn PhysicalExpr>]) -> LimitEffect {
711 LimitEffect::Unknown
712 }
713}
714
715pub fn interval_year_month_lit(value: &str) -> Expr {
716 let interval = parse_interval_year_month(value).ok();
717 Expr::Literal(ScalarValue::IntervalYearMonth(interval), None)
718}
719
720pub fn interval_datetime_lit(value: &str) -> Expr {
721 let interval = parse_interval_day_time(value).ok();
722 Expr::Literal(ScalarValue::IntervalDayTime(interval), None)
723}
724
725pub fn interval_month_day_nano_lit(value: &str) -> Expr {
726 let interval = parse_interval_month_day_nano(value).ok();
727 Expr::Literal(ScalarValue::IntervalMonthDayNano(interval), None)
728}
729
730pub trait ExprFunctionExt {
772 fn order_by(self, order_by: Vec<Sort>) -> ExprFuncBuilder;
774 fn filter(self, filter: Expr) -> ExprFuncBuilder;
776 fn distinct(self) -> ExprFuncBuilder;
778 fn null_treatment(
780 self,
781 null_treatment: impl Into<Option<NullTreatment>>,
782 ) -> ExprFuncBuilder;
783 fn partition_by(self, partition_by: Vec<Expr>) -> ExprFuncBuilder;
785 fn window_frame(self, window_frame: WindowFrame) -> ExprFuncBuilder;
787}
788
789#[derive(Debug, Clone)]
790pub enum ExprFuncKind {
791 Aggregate(AggregateFunction),
792 Window(Box<WindowFunction>),
793}
794
795#[derive(Debug, Clone)]
799pub struct ExprFuncBuilder {
800 fun: Option<ExprFuncKind>,
801 order_by: Option<Vec<Sort>>,
802 filter: Option<Expr>,
803 distinct: bool,
804 null_treatment: Option<NullTreatment>,
805 partition_by: Option<Vec<Expr>>,
806 window_frame: Option<WindowFrame>,
807}
808
809impl ExprFuncBuilder {
810 fn new(fun: Option<ExprFuncKind>) -> Self {
812 Self {
813 fun,
814 order_by: None,
815 filter: None,
816 distinct: false,
817 null_treatment: None,
818 partition_by: None,
819 window_frame: None,
820 }
821 }
822
823 pub fn build(self) -> Result<Expr> {
830 let Self {
831 fun,
832 order_by,
833 filter,
834 distinct,
835 null_treatment,
836 partition_by,
837 window_frame,
838 } = self;
839
840 let Some(fun) = fun else {
841 return plan_err!(
842 "ExprFunctionExt can only be used with Expr::AggregateFunction or Expr::WindowFunction"
843 );
844 };
845
846 let fun_expr = match fun {
847 ExprFuncKind::Aggregate(mut udaf) => {
848 udaf.params.order_by = order_by.unwrap_or_default();
849 udaf.params.filter = filter.map(Box::new);
850 udaf.params.distinct = distinct;
851 udaf.params.null_treatment = null_treatment;
852 Expr::AggregateFunction(udaf)
853 }
854 ExprFuncKind::Window(mut udwf) => {
855 let has_order_by = order_by.as_ref().map(|o| !o.is_empty());
856 udwf.params.partition_by = partition_by.unwrap_or_default();
857 udwf.params.order_by = order_by.unwrap_or_default();
858 udwf.params.window_frame =
859 window_frame.unwrap_or_else(|| WindowFrame::new(has_order_by));
860 udwf.params.filter = filter.map(Box::new);
861 udwf.params.null_treatment = null_treatment;
862 udwf.params.distinct = distinct;
863 Expr::WindowFunction(udwf)
864 }
865 };
866
867 Ok(fun_expr)
868 }
869}
870
871impl ExprFunctionExt for ExprFuncBuilder {
872 fn order_by(mut self, order_by: Vec<Sort>) -> ExprFuncBuilder {
874 self.order_by = Some(order_by);
875 self
876 }
877
878 fn filter(mut self, filter: Expr) -> ExprFuncBuilder {
880 self.filter = Some(filter);
881 self
882 }
883
884 fn distinct(mut self) -> ExprFuncBuilder {
886 self.distinct = true;
887 self
888 }
889
890 fn null_treatment(
892 mut self,
893 null_treatment: impl Into<Option<NullTreatment>>,
894 ) -> ExprFuncBuilder {
895 self.null_treatment = null_treatment.into();
896 self
897 }
898
899 fn partition_by(mut self, partition_by: Vec<Expr>) -> ExprFuncBuilder {
900 self.partition_by = Some(partition_by);
901 self
902 }
903
904 fn window_frame(mut self, window_frame: WindowFrame) -> ExprFuncBuilder {
905 self.window_frame = Some(window_frame);
906 self
907 }
908}
909
910impl ExprFunctionExt for Expr {
911 fn order_by(self, order_by: Vec<Sort>) -> ExprFuncBuilder {
912 let mut builder = match self {
913 Expr::AggregateFunction(udaf) => {
914 ExprFuncBuilder::new(Some(ExprFuncKind::Aggregate(udaf)))
915 }
916 Expr::WindowFunction(udwf) => {
917 ExprFuncBuilder::new(Some(ExprFuncKind::Window(udwf)))
918 }
919 _ => ExprFuncBuilder::new(None),
920 };
921 if builder.fun.is_some() {
922 builder.order_by = Some(order_by);
923 }
924 builder
925 }
926 fn filter(self, filter: Expr) -> ExprFuncBuilder {
927 match self {
928 Expr::AggregateFunction(udaf) => {
929 let mut builder =
930 ExprFuncBuilder::new(Some(ExprFuncKind::Aggregate(udaf)));
931 builder.filter = Some(filter);
932 builder
933 }
934 _ => ExprFuncBuilder::new(None),
935 }
936 }
937 fn distinct(self) -> ExprFuncBuilder {
938 match self {
939 Expr::AggregateFunction(udaf) => {
940 let mut builder =
941 ExprFuncBuilder::new(Some(ExprFuncKind::Aggregate(udaf)));
942 builder.distinct = true;
943 builder
944 }
945 _ => ExprFuncBuilder::new(None),
946 }
947 }
948 fn null_treatment(
949 self,
950 null_treatment: impl Into<Option<NullTreatment>>,
951 ) -> ExprFuncBuilder {
952 let mut builder = match self {
953 Expr::AggregateFunction(udaf) => {
954 ExprFuncBuilder::new(Some(ExprFuncKind::Aggregate(udaf)))
955 }
956 Expr::WindowFunction(udwf) => {
957 ExprFuncBuilder::new(Some(ExprFuncKind::Window(udwf)))
958 }
959 _ => ExprFuncBuilder::new(None),
960 };
961 if builder.fun.is_some() {
962 builder.null_treatment = null_treatment.into();
963 }
964 builder
965 }
966
967 fn partition_by(self, partition_by: Vec<Expr>) -> ExprFuncBuilder {
968 match self {
969 Expr::WindowFunction(udwf) => {
970 let mut builder = ExprFuncBuilder::new(Some(ExprFuncKind::Window(udwf)));
971 builder.partition_by = Some(partition_by);
972 builder
973 }
974 _ => ExprFuncBuilder::new(None),
975 }
976 }
977
978 fn window_frame(self, window_frame: WindowFrame) -> ExprFuncBuilder {
979 match self {
980 Expr::WindowFunction(udwf) => {
981 let mut builder = ExprFuncBuilder::new(Some(ExprFuncKind::Window(udwf)));
982 builder.window_frame = Some(window_frame);
983 builder
984 }
985 _ => ExprFuncBuilder::new(None),
986 }
987 }
988}
989
990#[cfg(test)]
991mod test {
992 use super::*;
993
994 #[test]
995 fn filter_is_null_and_is_not_null() {
996 let col_null = col("col1");
997 let col_not_null = ident("col2");
998 assert_eq!(format!("{}", col_null.is_null()), "col1 IS NULL");
999 assert_eq!(
1000 format!("{}", col_not_null.is_not_null()),
1001 "col2 IS NOT NULL"
1002 );
1003 }
1004}