1use crate::expr::{
21 AggregateFunction, BinaryExpr, Cast, Exists, GroupingSet, InList, InSubquery,
22 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 sqlparser::ast::NullTreatment;
47use std::any::Any;
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 {
75 Expr::OuterReferenceColumn(dt, ident.into())
76}
77
78pub fn ident(name: impl Into<String>) -> Expr {
97 Expr::Column(Column::from_name(name))
98}
99
100pub fn placeholder(id: impl Into<String>) -> Expr {
112 Expr::Placeholder(Placeholder {
113 id: id.into(),
114 data_type: None,
115 })
116}
117
118pub fn wildcard() -> SelectExpr {
128 SelectExpr::Wildcard(WildcardOptions::default())
129}
130
131pub fn wildcard_with_options(options: WildcardOptions) -> SelectExpr {
133 SelectExpr::Wildcard(options)
134}
135
136pub fn qualified_wildcard(qualifier: impl Into<TableReference>) -> SelectExpr {
147 SelectExpr::QualifiedWildcard(qualifier.into(), WildcardOptions::default())
148}
149
150pub fn qualified_wildcard_with_options(
152 qualifier: impl Into<TableReference>,
153 options: WildcardOptions,
154) -> SelectExpr {
155 SelectExpr::QualifiedWildcard(qualifier.into(), options)
156}
157
158pub fn binary_expr(left: Expr, op: Operator, right: Expr) -> Expr {
160 Expr::BinaryExpr(BinaryExpr::new(Box::new(left), op, Box::new(right)))
161}
162
163pub fn and(left: Expr, right: Expr) -> Expr {
165 Expr::BinaryExpr(BinaryExpr::new(
166 Box::new(left),
167 Operator::And,
168 Box::new(right),
169 ))
170}
171
172pub fn or(left: Expr, right: Expr) -> Expr {
174 Expr::BinaryExpr(BinaryExpr::new(
175 Box::new(left),
176 Operator::Or,
177 Box::new(right),
178 ))
179}
180
181pub fn not(expr: Expr) -> Expr {
183 expr.not()
184}
185
186pub fn bitwise_and(left: Expr, right: Expr) -> Expr {
188 Expr::BinaryExpr(BinaryExpr::new(
189 Box::new(left),
190 Operator::BitwiseAnd,
191 Box::new(right),
192 ))
193}
194
195pub fn bitwise_or(left: Expr, right: Expr) -> Expr {
197 Expr::BinaryExpr(BinaryExpr::new(
198 Box::new(left),
199 Operator::BitwiseOr,
200 Box::new(right),
201 ))
202}
203
204pub fn bitwise_xor(left: Expr, right: Expr) -> Expr {
206 Expr::BinaryExpr(BinaryExpr::new(
207 Box::new(left),
208 Operator::BitwiseXor,
209 Box::new(right),
210 ))
211}
212
213pub fn bitwise_shift_right(left: Expr, right: Expr) -> Expr {
215 Expr::BinaryExpr(BinaryExpr::new(
216 Box::new(left),
217 Operator::BitwiseShiftRight,
218 Box::new(right),
219 ))
220}
221
222pub fn bitwise_shift_left(left: Expr, right: Expr) -> Expr {
224 Expr::BinaryExpr(BinaryExpr::new(
225 Box::new(left),
226 Operator::BitwiseShiftLeft,
227 Box::new(right),
228 ))
229}
230
231pub fn in_list(expr: Expr, list: Vec<Expr>, negated: bool) -> Expr {
233 Expr::InList(InList::new(Box::new(expr), list, negated))
234}
235
236pub fn exists(subquery: Arc<LogicalPlan>) -> Expr {
238 let outer_ref_columns = subquery.all_out_ref_exprs();
239 Expr::Exists(Exists {
240 subquery: Subquery {
241 subquery,
242 outer_ref_columns,
243 spans: Spans::new(),
244 },
245 negated: false,
246 })
247}
248
249pub fn not_exists(subquery: Arc<LogicalPlan>) -> Expr {
251 let outer_ref_columns = subquery.all_out_ref_exprs();
252 Expr::Exists(Exists {
253 subquery: Subquery {
254 subquery,
255 outer_ref_columns,
256 spans: Spans::new(),
257 },
258 negated: true,
259 })
260}
261
262pub fn in_subquery(expr: Expr, subquery: Arc<LogicalPlan>) -> Expr {
264 let outer_ref_columns = subquery.all_out_ref_exprs();
265 Expr::InSubquery(InSubquery::new(
266 Box::new(expr),
267 Subquery {
268 subquery,
269 outer_ref_columns,
270 spans: Spans::new(),
271 },
272 false,
273 ))
274}
275
276pub fn not_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 true,
287 ))
288}
289
290pub fn scalar_subquery(subquery: Arc<LogicalPlan>) -> Expr {
292 let outer_ref_columns = subquery.all_out_ref_exprs();
293 Expr::ScalarSubquery(Subquery {
294 subquery,
295 outer_ref_columns,
296 spans: Spans::new(),
297 })
298}
299
300pub fn grouping_set(exprs: Vec<Vec<Expr>>) -> Expr {
302 Expr::GroupingSet(GroupingSet::GroupingSets(exprs))
303}
304
305pub fn cube(exprs: Vec<Expr>) -> Expr {
307 Expr::GroupingSet(GroupingSet::Cube(exprs))
308}
309
310pub fn rollup(exprs: Vec<Expr>) -> Expr {
312 Expr::GroupingSet(GroupingSet::Rollup(exprs))
313}
314
315pub fn cast(expr: Expr, data_type: DataType) -> Expr {
317 Expr::Cast(Cast::new(Box::new(expr), data_type))
318}
319
320pub fn try_cast(expr: Expr, data_type: DataType) -> Expr {
322 Expr::TryCast(TryCast::new(Box::new(expr), data_type))
323}
324
325pub fn is_null(expr: Expr) -> Expr {
327 Expr::IsNull(Box::new(expr))
328}
329
330pub fn is_true(expr: Expr) -> Expr {
332 Expr::IsTrue(Box::new(expr))
333}
334
335pub fn is_not_true(expr: Expr) -> Expr {
337 Expr::IsNotTrue(Box::new(expr))
338}
339
340pub fn is_false(expr: Expr) -> Expr {
342 Expr::IsFalse(Box::new(expr))
343}
344
345pub fn is_not_false(expr: Expr) -> Expr {
347 Expr::IsNotFalse(Box::new(expr))
348}
349
350pub fn is_unknown(expr: Expr) -> Expr {
352 Expr::IsUnknown(Box::new(expr))
353}
354
355pub fn is_not_unknown(expr: Expr) -> Expr {
357 Expr::IsNotUnknown(Box::new(expr))
358}
359
360pub fn case(expr: Expr) -> CaseBuilder {
362 CaseBuilder::new(Some(Box::new(expr)), vec![], vec![], None)
363}
364
365pub fn when(when: Expr, then: Expr) -> CaseBuilder {
367 CaseBuilder::new(None, vec![when], vec![then], None)
368}
369
370pub fn unnest(expr: Expr) -> Expr {
372 Expr::Unnest(Unnest {
373 expr: Box::new(expr),
374 })
375}
376
377pub fn create_udf(
390 name: &str,
391 input_types: Vec<DataType>,
392 return_type: DataType,
393 volatility: Volatility,
394 fun: ScalarFunctionImplementation,
395) -> ScalarUDF {
396 ScalarUDF::from(SimpleScalarUDF::new(
397 name,
398 input_types,
399 return_type,
400 volatility,
401 fun,
402 ))
403}
404
405#[derive(PartialEq, Eq, Hash)]
408pub struct SimpleScalarUDF {
409 name: String,
410 signature: Signature,
411 return_type: DataType,
412 fun: PtrEq<ScalarFunctionImplementation>,
413}
414
415impl Debug for SimpleScalarUDF {
416 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
417 f.debug_struct("SimpleScalarUDF")
418 .field("name", &self.name)
419 .field("signature", &self.signature)
420 .field("return_type", &self.return_type)
421 .field("fun", &"<FUNC>")
422 .finish()
423 }
424}
425
426impl SimpleScalarUDF {
427 pub fn new(
430 name: impl Into<String>,
431 input_types: Vec<DataType>,
432 return_type: DataType,
433 volatility: Volatility,
434 fun: ScalarFunctionImplementation,
435 ) -> Self {
436 Self::new_with_signature(
437 name,
438 Signature::exact(input_types, volatility),
439 return_type,
440 fun,
441 )
442 }
443
444 pub fn new_with_signature(
447 name: impl Into<String>,
448 signature: Signature,
449 return_type: DataType,
450 fun: ScalarFunctionImplementation,
451 ) -> Self {
452 Self {
453 name: name.into(),
454 signature,
455 return_type,
456 fun: fun.into(),
457 }
458 }
459}
460
461impl ScalarUDFImpl for SimpleScalarUDF {
462 fn as_any(&self) -> &dyn Any {
463 self
464 }
465
466 fn name(&self) -> &str {
467 &self.name
468 }
469
470 fn signature(&self) -> &Signature {
471 &self.signature
472 }
473
474 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
475 Ok(self.return_type.clone())
476 }
477
478 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
479 (self.fun)(&args.args)
480 }
481}
482
483pub fn create_udaf(
486 name: &str,
487 input_type: Vec<DataType>,
488 return_type: Arc<DataType>,
489 volatility: Volatility,
490 accumulator: AccumulatorFactoryFunction,
491 state_type: Arc<Vec<DataType>>,
492) -> AggregateUDF {
493 let return_type = Arc::unwrap_or_clone(return_type);
494 let state_type = Arc::unwrap_or_clone(state_type);
495 let state_fields = state_type
496 .into_iter()
497 .enumerate()
498 .map(|(i, t)| Field::new(format!("{i}"), t, true))
499 .map(Arc::new)
500 .collect::<Vec<_>>();
501 AggregateUDF::from(SimpleAggregateUDF::new(
502 name,
503 input_type,
504 return_type,
505 volatility,
506 accumulator,
507 state_fields,
508 ))
509}
510
511#[derive(PartialEq, Eq, Hash)]
514pub struct SimpleAggregateUDF {
515 name: String,
516 signature: Signature,
517 return_type: DataType,
518 accumulator: PtrEq<AccumulatorFactoryFunction>,
519 state_fields: Vec<FieldRef>,
520}
521
522impl Debug for SimpleAggregateUDF {
523 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
524 f.debug_struct("SimpleAggregateUDF")
525 .field("name", &self.name)
526 .field("signature", &self.signature)
527 .field("return_type", &self.return_type)
528 .field("fun", &"<FUNC>")
529 .finish()
530 }
531}
532
533impl SimpleAggregateUDF {
534 pub fn new(
537 name: impl Into<String>,
538 input_type: Vec<DataType>,
539 return_type: DataType,
540 volatility: Volatility,
541 accumulator: AccumulatorFactoryFunction,
542 state_fields: Vec<FieldRef>,
543 ) -> Self {
544 let name = name.into();
545 let signature = Signature::exact(input_type, volatility);
546 Self {
547 name,
548 signature,
549 return_type,
550 accumulator: accumulator.into(),
551 state_fields,
552 }
553 }
554
555 pub fn new_with_signature(
558 name: impl Into<String>,
559 signature: Signature,
560 return_type: DataType,
561 accumulator: AccumulatorFactoryFunction,
562 state_fields: Vec<FieldRef>,
563 ) -> Self {
564 let name = name.into();
565 Self {
566 name,
567 signature,
568 return_type,
569 accumulator: accumulator.into(),
570 state_fields,
571 }
572 }
573}
574
575impl AggregateUDFImpl for SimpleAggregateUDF {
576 fn as_any(&self) -> &dyn Any {
577 self
578 }
579
580 fn name(&self) -> &str {
581 &self.name
582 }
583
584 fn signature(&self) -> &Signature {
585 &self.signature
586 }
587
588 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
589 Ok(self.return_type.clone())
590 }
591
592 fn accumulator(
593 &self,
594 acc_args: AccumulatorArgs,
595 ) -> Result<Box<dyn crate::Accumulator>> {
596 (self.accumulator)(acc_args)
597 }
598
599 fn state_fields(&self, _args: StateFieldsArgs) -> Result<Vec<FieldRef>> {
600 Ok(self.state_fields.clone())
601 }
602}
603
604pub fn create_udwf(
610 name: &str,
611 input_type: DataType,
612 return_type: Arc<DataType>,
613 volatility: Volatility,
614 partition_evaluator_factory: PartitionEvaluatorFactory,
615) -> WindowUDF {
616 let return_type = Arc::unwrap_or_clone(return_type);
617 WindowUDF::from(SimpleWindowUDF::new(
618 name,
619 input_type,
620 return_type,
621 volatility,
622 partition_evaluator_factory,
623 ))
624}
625
626#[derive(PartialEq, Eq, Hash)]
629pub struct SimpleWindowUDF {
630 name: String,
631 signature: Signature,
632 return_type: DataType,
633 partition_evaluator_factory: PtrEq<PartitionEvaluatorFactory>,
634}
635
636impl Debug for SimpleWindowUDF {
637 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
638 f.debug_struct("WindowUDF")
639 .field("name", &self.name)
640 .field("signature", &self.signature)
641 .field("return_type", &"<func>")
642 .field("partition_evaluator_factory", &"<FUNC>")
643 .finish()
644 }
645}
646
647impl SimpleWindowUDF {
648 pub fn new(
651 name: impl Into<String>,
652 input_type: DataType,
653 return_type: DataType,
654 volatility: Volatility,
655 partition_evaluator_factory: PartitionEvaluatorFactory,
656 ) -> Self {
657 let name = name.into();
658 let signature = Signature::exact([input_type].to_vec(), volatility);
659 Self {
660 name,
661 signature,
662 return_type,
663 partition_evaluator_factory: partition_evaluator_factory.into(),
664 }
665 }
666}
667
668impl WindowUDFImpl for SimpleWindowUDF {
669 fn as_any(&self) -> &dyn Any {
670 self
671 }
672
673 fn name(&self) -> &str {
674 &self.name
675 }
676
677 fn signature(&self) -> &Signature {
678 &self.signature
679 }
680
681 fn partition_evaluator(
682 &self,
683 _partition_evaluator_args: PartitionEvaluatorArgs,
684 ) -> Result<Box<dyn PartitionEvaluator>> {
685 (self.partition_evaluator_factory)()
686 }
687
688 fn field(&self, field_args: WindowUDFFieldArgs) -> Result<FieldRef> {
689 Ok(Arc::new(Field::new(
690 field_args.name(),
691 self.return_type.clone(),
692 true,
693 )))
694 }
695
696 fn limit_effect(&self, _args: &[Arc<dyn PhysicalExpr>]) -> LimitEffect {
697 LimitEffect::Unknown
698 }
699}
700
701pub fn interval_year_month_lit(value: &str) -> Expr {
702 let interval = parse_interval_year_month(value).ok();
703 Expr::Literal(ScalarValue::IntervalYearMonth(interval), None)
704}
705
706pub fn interval_datetime_lit(value: &str) -> Expr {
707 let interval = parse_interval_day_time(value).ok();
708 Expr::Literal(ScalarValue::IntervalDayTime(interval), None)
709}
710
711pub fn interval_month_day_nano_lit(value: &str) -> Expr {
712 let interval = parse_interval_month_day_nano(value).ok();
713 Expr::Literal(ScalarValue::IntervalMonthDayNano(interval), None)
714}
715
716pub trait ExprFunctionExt {
758 fn order_by(self, order_by: Vec<Sort>) -> ExprFuncBuilder;
760 fn filter(self, filter: Expr) -> ExprFuncBuilder;
762 fn distinct(self) -> ExprFuncBuilder;
764 fn null_treatment(
766 self,
767 null_treatment: impl Into<Option<NullTreatment>>,
768 ) -> ExprFuncBuilder;
769 fn partition_by(self, partition_by: Vec<Expr>) -> ExprFuncBuilder;
771 fn window_frame(self, window_frame: WindowFrame) -> ExprFuncBuilder;
773}
774
775#[derive(Debug, Clone)]
776pub enum ExprFuncKind {
777 Aggregate(AggregateFunction),
778 Window(Box<WindowFunction>),
779}
780
781#[derive(Debug, Clone)]
785pub struct ExprFuncBuilder {
786 fun: Option<ExprFuncKind>,
787 order_by: Option<Vec<Sort>>,
788 filter: Option<Expr>,
789 distinct: bool,
790 null_treatment: Option<NullTreatment>,
791 partition_by: Option<Vec<Expr>>,
792 window_frame: Option<WindowFrame>,
793}
794
795impl ExprFuncBuilder {
796 fn new(fun: Option<ExprFuncKind>) -> Self {
798 Self {
799 fun,
800 order_by: None,
801 filter: None,
802 distinct: false,
803 null_treatment: None,
804 partition_by: None,
805 window_frame: None,
806 }
807 }
808
809 pub fn build(self) -> Result<Expr> {
816 let Self {
817 fun,
818 order_by,
819 filter,
820 distinct,
821 null_treatment,
822 partition_by,
823 window_frame,
824 } = self;
825
826 let Some(fun) = fun else {
827 return plan_err!(
828 "ExprFunctionExt can only be used with Expr::AggregateFunction or Expr::WindowFunction"
829 );
830 };
831
832 let fun_expr = match fun {
833 ExprFuncKind::Aggregate(mut udaf) => {
834 udaf.params.order_by = order_by.unwrap_or_default();
835 udaf.params.filter = filter.map(Box::new);
836 udaf.params.distinct = distinct;
837 udaf.params.null_treatment = null_treatment;
838 Expr::AggregateFunction(udaf)
839 }
840 ExprFuncKind::Window(mut udwf) => {
841 let has_order_by = order_by.as_ref().map(|o| !o.is_empty());
842 udwf.params.partition_by = partition_by.unwrap_or_default();
843 udwf.params.order_by = order_by.unwrap_or_default();
844 udwf.params.window_frame =
845 window_frame.unwrap_or_else(|| WindowFrame::new(has_order_by));
846 udwf.params.filter = filter.map(Box::new);
847 udwf.params.null_treatment = null_treatment;
848 udwf.params.distinct = distinct;
849 Expr::WindowFunction(udwf)
850 }
851 };
852
853 Ok(fun_expr)
854 }
855}
856
857impl ExprFunctionExt for ExprFuncBuilder {
858 fn order_by(mut self, order_by: Vec<Sort>) -> ExprFuncBuilder {
860 self.order_by = Some(order_by);
861 self
862 }
863
864 fn filter(mut self, filter: Expr) -> ExprFuncBuilder {
866 self.filter = Some(filter);
867 self
868 }
869
870 fn distinct(mut self) -> ExprFuncBuilder {
872 self.distinct = true;
873 self
874 }
875
876 fn null_treatment(
878 mut self,
879 null_treatment: impl Into<Option<NullTreatment>>,
880 ) -> ExprFuncBuilder {
881 self.null_treatment = null_treatment.into();
882 self
883 }
884
885 fn partition_by(mut self, partition_by: Vec<Expr>) -> ExprFuncBuilder {
886 self.partition_by = Some(partition_by);
887 self
888 }
889
890 fn window_frame(mut self, window_frame: WindowFrame) -> ExprFuncBuilder {
891 self.window_frame = Some(window_frame);
892 self
893 }
894}
895
896impl ExprFunctionExt for Expr {
897 fn order_by(self, order_by: Vec<Sort>) -> ExprFuncBuilder {
898 let mut builder = match self {
899 Expr::AggregateFunction(udaf) => {
900 ExprFuncBuilder::new(Some(ExprFuncKind::Aggregate(udaf)))
901 }
902 Expr::WindowFunction(udwf) => {
903 ExprFuncBuilder::new(Some(ExprFuncKind::Window(udwf)))
904 }
905 _ => ExprFuncBuilder::new(None),
906 };
907 if builder.fun.is_some() {
908 builder.order_by = Some(order_by);
909 }
910 builder
911 }
912 fn filter(self, filter: Expr) -> ExprFuncBuilder {
913 match self {
914 Expr::AggregateFunction(udaf) => {
915 let mut builder =
916 ExprFuncBuilder::new(Some(ExprFuncKind::Aggregate(udaf)));
917 builder.filter = Some(filter);
918 builder
919 }
920 _ => ExprFuncBuilder::new(None),
921 }
922 }
923 fn distinct(self) -> ExprFuncBuilder {
924 match self {
925 Expr::AggregateFunction(udaf) => {
926 let mut builder =
927 ExprFuncBuilder::new(Some(ExprFuncKind::Aggregate(udaf)));
928 builder.distinct = true;
929 builder
930 }
931 _ => ExprFuncBuilder::new(None),
932 }
933 }
934 fn null_treatment(
935 self,
936 null_treatment: impl Into<Option<NullTreatment>>,
937 ) -> ExprFuncBuilder {
938 let mut builder = match self {
939 Expr::AggregateFunction(udaf) => {
940 ExprFuncBuilder::new(Some(ExprFuncKind::Aggregate(udaf)))
941 }
942 Expr::WindowFunction(udwf) => {
943 ExprFuncBuilder::new(Some(ExprFuncKind::Window(udwf)))
944 }
945 _ => ExprFuncBuilder::new(None),
946 };
947 if builder.fun.is_some() {
948 builder.null_treatment = null_treatment.into();
949 }
950 builder
951 }
952
953 fn partition_by(self, partition_by: Vec<Expr>) -> ExprFuncBuilder {
954 match self {
955 Expr::WindowFunction(udwf) => {
956 let mut builder = ExprFuncBuilder::new(Some(ExprFuncKind::Window(udwf)));
957 builder.partition_by = Some(partition_by);
958 builder
959 }
960 _ => ExprFuncBuilder::new(None),
961 }
962 }
963
964 fn window_frame(self, window_frame: WindowFrame) -> ExprFuncBuilder {
965 match self {
966 Expr::WindowFunction(udwf) => {
967 let mut builder = ExprFuncBuilder::new(Some(ExprFuncKind::Window(udwf)));
968 builder.window_frame = Some(window_frame);
969 builder
970 }
971 _ => ExprFuncBuilder::new(None),
972 }
973 }
974}
975
976#[cfg(test)]
977mod test {
978 use super::*;
979
980 #[test]
981 fn filter_is_null_and_is_not_null() {
982 let col_null = col("col1");
983 let col_not_null = ident("col2");
984 assert_eq!(format!("{}", col_null.is_null()), "col1 IS NULL");
985 assert_eq!(
986 format!("{}", col_not_null.is_not_null()),
987 "col2 IS NOT NULL"
988 );
989 }
990}