1use std::{fmt, mem};
22
23use itertools::Itertools;
24use mz_ore::soft_assert_eq_or_log;
25use mz_sql_lexer::keywords::*;
26
27use crate::ast::display::{self, AstDisplay, AstFormatter};
28use crate::ast::{AstInfo, Ident, OrderByExpr, Query, UnresolvedItemName, Value};
29
30#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
36pub enum Expr<T: AstInfo> {
37 Identifier(Vec<Ident>),
40 QualifiedWildcard(Vec<Ident>),
42 FieldAccess {
44 expr: Box<Expr<T>>,
45 field: Ident,
46 },
47 WildcardAccess(Box<Expr<T>>),
54 Parameter(usize),
56 Not {
58 expr: Box<Expr<T>>,
59 },
60 And {
62 left: Box<Expr<T>>,
63 right: Box<Expr<T>>,
64 },
65 Or {
67 left: Box<Expr<T>>,
68 right: Box<Expr<T>>,
69 },
70 IsExpr {
72 expr: Box<Expr<T>>,
73 construct: IsExprConstruct<T>,
74 negated: bool,
75 },
76 InList {
78 expr: Box<Expr<T>>,
79 list: Vec<Expr<T>>,
80 negated: bool,
81 },
82 InSubquery {
84 expr: Box<Expr<T>>,
85 subquery: Box<Query<T>>,
86 negated: bool,
87 },
88 Like {
90 expr: Box<Expr<T>>,
91 pattern: Box<Expr<T>>,
92 escape: Option<Box<Expr<T>>>,
93 case_insensitive: bool,
94 negated: bool,
95 },
96 Between {
98 expr: Box<Expr<T>>,
99 negated: bool,
100 low: Box<Expr<T>>,
101 high: Box<Expr<T>>,
102 },
103 Op {
105 op: Op,
106 expr1: Box<Expr<T>>,
107 expr2: Option<Box<Expr<T>>>,
108 },
109 Cast {
111 expr: Box<Expr<T>>,
112 data_type: T::DataType,
113 },
114 Collate {
116 expr: Box<Expr<T>>,
117 collation: UnresolvedItemName,
118 },
119 HomogenizingFunction {
125 function: HomogenizingFunction,
126 exprs: Vec<Expr<T>>,
127 },
128 NullIf {
133 l_expr: Box<Expr<T>>,
134 r_expr: Box<Expr<T>>,
135 },
136 Nested(Box<Expr<T>>),
138 Row {
140 exprs: Vec<Expr<T>>,
141 },
142 Value(Value),
144 Function(Function<T>),
146 Case {
152 operand: Option<Box<Expr<T>>>,
153 conditions: Vec<Expr<T>>,
154 results: Vec<Expr<T>>,
155 else_result: Option<Box<Expr<T>>>,
156 },
157 Exists(Box<Query<T>>),
160 Subquery(Box<Query<T>>),
163 AnySubquery {
165 left: Box<Expr<T>>,
166 op: Op,
167 right: Box<Query<T>>,
168 },
169 AnyExpr {
171 left: Box<Expr<T>>,
172 op: Op,
173 right: Box<Expr<T>>,
174 },
175 AllSubquery {
177 left: Box<Expr<T>>,
178 op: Op,
179 right: Box<Query<T>>,
180 },
181 AllExpr {
183 left: Box<Expr<T>>,
184 op: Op,
185 right: Box<Expr<T>>,
186 },
187 Array(Vec<Expr<T>>),
189 ArraySubquery(Box<Query<T>>),
190 List(Vec<Expr<T>>),
192 ListSubquery(Box<Query<T>>),
193 Map(Vec<MapEntry<T>>),
195 MapSubquery(Box<Query<T>>),
196 Subscript {
198 expr: Box<Expr<T>>,
199 positions: Vec<SubscriptPosition<T>>,
200 },
201}
202
203impl<T: AstInfo> AstDisplay for Expr<T> {
204 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
205 match self {
206 Expr::Identifier(s) => f.write_node(&display::separated(s, ".")),
207 Expr::QualifiedWildcard(q) => {
208 f.write_node(&display::separated(q, "."));
209 f.write_str(".*");
210 }
211 Expr::FieldAccess { expr, field } => {
212 f.write_node(expr);
213 f.write_str(".");
214 f.write_node(field);
215 }
216 Expr::WildcardAccess(expr) => {
217 f.write_node(expr);
218 f.write_str(".*");
219 }
220 Expr::Parameter(n) => f.write_str(&format!("${}", n)),
221 Expr::Not { expr } => {
222 f.write_str("NOT ");
223 f.write_node(expr);
224 }
225 Expr::And { left, right } => {
226 f.write_node(left);
227 f.write_str(" AND ");
228 f.write_node(right);
229 }
230 Expr::Or { left, right } => {
231 f.write_node(left);
232 f.write_str(" OR ");
233 f.write_node(right);
234 }
235 Expr::IsExpr {
236 expr,
237 negated,
238 construct,
239 } => {
240 f.write_node(&expr);
241 f.write_str(" IS ");
242 if *negated {
243 f.write_str("NOT ");
244 }
245 f.write_node(construct);
246 }
247 Expr::InList {
248 expr,
249 list,
250 negated,
251 } => {
252 f.write_node(&expr);
253 f.write_str(" ");
254 if *negated {
255 f.write_str("NOT ");
256 }
257 f.write_str("IN (");
258 f.write_node(&display::comma_separated(list));
259 f.write_str(")");
260 }
261 Expr::InSubquery {
262 expr,
263 subquery,
264 negated,
265 } => {
266 f.write_node(&expr);
267 f.write_str(" ");
268 if *negated {
269 f.write_str("NOT ");
270 }
271 f.write_str("IN (");
272 f.write_node(&subquery);
273 f.write_str(")");
274 }
275 Expr::Like {
276 expr,
277 pattern,
278 escape,
279 case_insensitive,
280 negated,
281 } => {
282 f.write_node(&expr);
283 f.write_str(" ");
284 if *negated {
285 f.write_str("NOT ");
286 }
287 if *case_insensitive {
288 f.write_str("I");
289 }
290 f.write_str("LIKE ");
291 f.write_node(&pattern);
292 if let Some(escape) = escape {
293 f.write_str(" ESCAPE ");
294 f.write_node(escape);
295 }
296 }
297 Expr::Between {
298 expr,
299 negated,
300 low,
301 high,
302 } => {
303 f.write_node(&expr);
304 if *negated {
305 f.write_str(" NOT");
306 }
307 f.write_str(" BETWEEN ");
308 f.write_node(&low);
309 f.write_str(" AND ");
310 f.write_node(&high);
311 }
312 Expr::Op { op, expr1, expr2 } => {
313 if let Some(expr2) = expr2 {
314 f.write_node(&expr1);
315 f.write_str(" ");
316 f.write_str(op);
317 f.write_str(" ");
318 f.write_node(&expr2);
319 } else {
320 f.write_str(op);
321 f.write_str(" ");
322 f.write_node(&expr1);
323 }
324 }
325 Expr::Cast { expr, data_type } => {
326 f.write_node(&expr);
327 f.write_str("::");
328 f.write_node(data_type);
329 }
330 Expr::Collate { expr, collation } => {
331 f.write_node(&expr);
332 f.write_str(" COLLATE ");
333 f.write_node(&collation);
334 }
335 Expr::HomogenizingFunction { function, exprs } => {
336 f.write_node(function);
337 f.write_str("(");
338 f.write_node(&display::comma_separated(exprs));
339 f.write_str(")");
340 }
341 Expr::NullIf { l_expr, r_expr } => {
342 f.write_str("NULLIF(");
343 f.write_node(&display::comma_separated(&[l_expr, r_expr]));
344 f.write_str(")");
345 }
346 Expr::Nested(ast) => {
347 f.write_str("(");
348 f.write_node(&ast);
349 f.write_str(")");
350 }
351 Expr::Row { exprs } => {
352 f.write_str("ROW(");
353 f.write_node(&display::comma_separated(exprs));
354 f.write_str(")");
355 }
356 Expr::Value(v) => {
357 f.write_node(v);
358 }
359 Expr::Function(fun) => {
360 f.write_node(fun);
361 }
362 Expr::Case {
363 operand,
364 conditions,
365 results,
366 else_result,
367 } => {
368 f.write_str("CASE");
369 if let Some(operand) = operand {
370 f.write_str(" ");
371 f.write_node(&operand);
372 }
373 for (c, r) in conditions.iter().zip_eq(results) {
374 f.write_str(" WHEN ");
375 f.write_node(c);
376 f.write_str(" THEN ");
377 f.write_node(r);
378 }
379
380 if let Some(else_result) = else_result {
381 f.write_str(" ELSE ");
382 f.write_node(&else_result);
383 }
384 f.write_str(" END")
385 }
386 Expr::Exists(s) => {
387 f.write_str("EXISTS (");
388 f.write_node(&s);
389 f.write_str(")");
390 }
391 Expr::Subquery(s) => {
392 f.write_str("(");
393 f.write_node(&s);
394 f.write_str(")");
395 }
396 Expr::AnySubquery { left, op, right } => {
397 f.write_node(&left);
398 f.write_str(" ");
399 f.write_str(op);
400 f.write_str(" ANY (");
401 f.write_node(&right);
402 f.write_str(")");
403 }
404 Expr::AnyExpr { left, op, right } => {
405 f.write_node(&left);
406 f.write_str(" ");
407 f.write_str(op);
408 f.write_str(" ANY (");
409 f.write_node(&right);
410 f.write_str(")");
411 }
412 Expr::AllSubquery { left, op, right } => {
413 f.write_node(&left);
414 f.write_str(" ");
415 f.write_str(op);
416 f.write_str(" ALL (");
417 f.write_node(&right);
418 f.write_str(")");
419 }
420 Expr::AllExpr { left, op, right } => {
421 f.write_node(&left);
422 f.write_str(" ");
423 f.write_str(op);
424 f.write_str(" ALL (");
425 f.write_node(&right);
426 f.write_str(")");
427 }
428 Expr::Array(exprs) => {
429 f.write_str("ARRAY[");
430 f.write_node(&display::comma_separated(exprs));
431 f.write_str("]");
432 }
433 Expr::ArraySubquery(s) => {
434 f.write_str("ARRAY(");
435 f.write_node(&s);
436 f.write_str(")");
437 }
438 Expr::List(exprs) => {
439 f.write_str("LIST[");
440 f.write_node(&display::comma_separated(exprs));
441 f.write_str("]");
442 }
443 Expr::ListSubquery(s) => {
444 f.write_str("LIST(");
445 f.write_node(&s);
446 f.write_str(")");
447 }
448 Expr::Map(exprs) => {
449 f.write_str("MAP[");
450 f.write_node(&display::comma_separated(exprs));
451 f.write_str("]");
452 }
453 Expr::MapSubquery(s) => {
454 f.write_str("MAP(");
455 f.write_node(&s);
456 f.write_str(")");
457 }
458 Expr::Subscript { expr, positions } => {
459 f.write_node(&expr);
460 f.write_str("[");
461
462 let mut first = true;
463
464 for p in positions {
465 if first {
466 first = false
467 } else {
468 f.write_str("][");
469 }
470 f.write_node(p);
471 }
472
473 f.write_str("]");
474 }
475 }
476 }
477}
478impl_display_t!(Expr);
479
480impl<T: AstInfo> Expr<T> {
481 pub fn null() -> Expr<T> {
482 Expr::Value(Value::Null)
483 }
484
485 pub fn number<S>(n: S) -> Expr<T>
486 where
487 S: Into<String>,
488 {
489 Expr::Value(Value::Number(n.into()))
490 }
491
492 pub fn negate(self) -> Expr<T> {
493 Expr::Not {
494 expr: Box::new(self),
495 }
496 }
497
498 pub fn and(self, right: Expr<T>) -> Expr<T> {
499 Expr::And {
500 left: Box::new(self),
501 right: Box::new(right),
502 }
503 }
504
505 pub fn or(self, right: Expr<T>) -> Expr<T> {
506 Expr::Or {
507 left: Box::new(self),
508 right: Box::new(right),
509 }
510 }
511
512 pub fn binop(self, op: Op, right: Expr<T>) -> Expr<T> {
513 Expr::Op {
514 op,
515 expr1: Box::new(self),
516 expr2: Some(Box::new(right)),
517 }
518 }
519
520 pub fn lt(self, right: Expr<T>) -> Expr<T> {
521 self.binop(Op::bare("<"), right)
522 }
523
524 pub fn lt_eq(self, right: Expr<T>) -> Expr<T> {
525 self.binop(Op::bare("<="), right)
526 }
527
528 pub fn gt(self, right: Expr<T>) -> Expr<T> {
529 self.binop(Op::bare(">"), right)
530 }
531
532 pub fn gt_eq(self, right: Expr<T>) -> Expr<T> {
533 self.binop(Op::bare(">="), right)
534 }
535
536 pub fn equals(self, right: Expr<T>) -> Expr<T> {
537 self.binop(Op::bare("="), right)
538 }
539
540 pub fn not_equals(self, right: Expr<T>) -> Expr<T> {
541 self.binop(Op::bare("<>"), right)
542 }
543
544 pub fn minus(self, right: Expr<T>) -> Expr<T> {
545 self.binop(Op::bare("-"), right)
546 }
547
548 pub fn multiply(self, right: Expr<T>) -> Expr<T> {
549 self.binop(Op::bare("*"), right)
550 }
551
552 pub fn modulo(self, right: Expr<T>) -> Expr<T> {
553 self.binop(Op::bare("%"), right)
554 }
555
556 pub fn divide(self, right: Expr<T>) -> Expr<T> {
557 self.binop(Op::bare("/"), right)
558 }
559
560 pub fn cast(self, data_type: T::DataType) -> Expr<T> {
561 Expr::Cast {
562 expr: Box::new(self),
563 data_type,
564 }
565 }
566
567 pub fn call(name: T::ItemName, args: Vec<Expr<T>>) -> Expr<T> {
568 Expr::Function(Function {
569 name,
570 args: FunctionArgs::args(args),
571 filter: None,
572 over: None,
573 distinct: false,
574 })
575 }
576
577 pub fn call_nullary(name: T::ItemName) -> Expr<T> {
578 Expr::call(name, vec![])
579 }
580
581 pub fn call_unary(self, name: T::ItemName) -> Expr<T> {
582 Expr::call(name, vec![self])
583 }
584
585 pub fn take(&mut self) -> Expr<T> {
586 mem::replace(self, Expr::Identifier(vec![]))
587 }
588}
589
590#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
592pub struct Op {
593 pub namespace: Option<Vec<Ident>>,
595 pub op: String,
597}
598
599impl AstDisplay for Op {
600 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
601 if let Some(namespace) = &self.namespace {
602 f.write_str("OPERATOR(");
603 for name in namespace {
604 f.write_node(name);
605 f.write_str(".");
606 }
607 f.write_str(&self.op);
608 f.write_str(")");
609 } else {
610 f.write_str(&self.op)
611 }
612 }
613}
614impl_display!(Op);
615
616impl Op {
617 pub fn bare<S>(op: S) -> Op
619 where
620 S: Into<String>,
621 {
622 Op {
623 namespace: None,
624 op: op.into(),
625 }
626 }
627}
628
629#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
630pub enum HomogenizingFunction {
631 Coalesce,
632 Greatest,
633 Least,
634}
635
636impl AstDisplay for HomogenizingFunction {
637 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
638 match self {
639 HomogenizingFunction::Coalesce => f.write_str("COALESCE"),
640 HomogenizingFunction::Greatest => f.write_str("GREATEST"),
641 HomogenizingFunction::Least => f.write_str("LEAST"),
642 }
643 }
644}
645impl_display!(HomogenizingFunction);
646
647#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
648pub struct MapEntry<T: AstInfo> {
649 pub key: Expr<T>,
650 pub value: Expr<T>,
651}
652
653impl<T: AstInfo> AstDisplay for MapEntry<T> {
654 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
655 f.write_node(&self.key);
656 f.write_str(" => ");
657 f.write_node(&self.value);
658 }
659}
660impl_display_t!(MapEntry);
661
662#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
663pub struct SubscriptPosition<T: AstInfo> {
664 pub start: Option<Expr<T>>,
665 pub end: Option<Expr<T>>,
666 pub explicit_slice: bool,
668}
669
670impl<T: AstInfo> AstDisplay for SubscriptPosition<T> {
671 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
672 if let Some(start) = &self.start {
673 f.write_node(start);
674 }
675 if self.explicit_slice {
676 f.write_str(":");
677 if let Some(end) = &self.end {
678 f.write_node(end);
679 }
680 }
681 }
682}
683impl_display_t!(SubscriptPosition);
684
685#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
688pub struct WindowSpec<T: AstInfo> {
689 pub partition_by: Vec<Expr<T>>,
690 pub order_by: Vec<OrderByExpr<T>>,
691 pub window_frame: Option<WindowFrame>,
692 pub ignore_nulls: bool,
695 pub respect_nulls: bool,
696}
697
698impl<T: AstInfo> AstDisplay for WindowSpec<T> {
699 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
700 if self.ignore_nulls {
701 f.write_str(" IGNORE NULLS");
702 }
703 if self.respect_nulls {
704 f.write_str(" RESPECT NULLS");
705 }
706 f.write_str(" OVER (");
707 let mut delim = "";
708 if !self.partition_by.is_empty() {
709 delim = " ";
710 f.write_str("PARTITION BY ");
711 f.write_node(&display::comma_separated(&self.partition_by));
712 }
713 if !self.order_by.is_empty() {
714 f.write_str(delim);
715 delim = " ";
716 f.write_str("ORDER BY ");
717 f.write_node(&display::comma_separated(&self.order_by));
718 }
719 if let Some(window_frame) = &self.window_frame {
720 if let Some(end_bound) = &window_frame.end_bound {
721 f.write_str(delim);
722 f.write_node(&window_frame.units);
723 f.write_str(" BETWEEN ");
724 f.write_node(&window_frame.start_bound);
725 f.write_str(" AND ");
726 f.write_node(&*end_bound);
727 } else {
728 f.write_str(delim);
729 f.write_node(&window_frame.units);
730 f.write_str(" ");
731 f.write_node(&window_frame.start_bound);
732 }
733 }
734 f.write_str(")");
735 }
736}
737impl_display_t!(WindowSpec);
738
739#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
745pub struct WindowFrame {
746 pub units: WindowFrameUnits,
747 pub start_bound: WindowFrameBound,
748 pub end_bound: Option<WindowFrameBound>,
752 }
754
755#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
756pub enum WindowFrameUnits {
757 Rows,
758 Range,
759 Groups,
760}
761
762impl AstDisplay for WindowFrameUnits {
763 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
764 f.write_str(match self {
765 WindowFrameUnits::Rows => "ROWS",
766 WindowFrameUnits::Range => "RANGE",
767 WindowFrameUnits::Groups => "GROUPS",
768 })
769 }
770}
771impl_display!(WindowFrameUnits);
772
773#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
775pub enum WindowFrameBound {
776 CurrentRow,
778 Preceding(Option<u64>),
780 Following(Option<u64>),
782}
783
784impl AstDisplay for WindowFrameBound {
785 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
786 match self {
787 WindowFrameBound::CurrentRow => f.write_str("CURRENT ROW"),
788 WindowFrameBound::Preceding(None) => f.write_str("UNBOUNDED PRECEDING"),
789 WindowFrameBound::Following(None) => f.write_str("UNBOUNDED FOLLOWING"),
790 WindowFrameBound::Preceding(Some(n)) => {
791 f.write_str(n);
792 f.write_str(" PRECEDING");
793 }
794 WindowFrameBound::Following(Some(n)) => {
795 f.write_str(n);
796 f.write_str(" FOLLOWING");
797 }
798 }
799 }
800}
801impl_display!(WindowFrameBound);
802
803#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
805pub struct Function<T: AstInfo> {
806 pub name: T::ItemName,
807 pub args: FunctionArgs<T>,
808 pub filter: Option<Box<Expr<T>>>,
810 pub over: Option<WindowSpec<T>>,
811 pub distinct: bool,
813}
814
815impl<T: AstInfo> AstDisplay for Function<T> {
816 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
817 if !f.stable() {
821 let special: Option<(&str, &[Option<Keyword>])> =
822 match self.name.to_ast_string_stable().as_str() {
823 r#""extract""# if self.args.len() == Some(2) => {
824 Some(("extract", &[None, Some(FROM)]))
825 }
826 r#""position""# if self.args.len() == Some(2) => {
827 Some(("position", &[None, Some(IN)]))
828 }
829
830 _ => None,
834 };
835 if let Some((name, kws)) = special {
836 f.write_str(name);
837 f.write_str("(");
838 self.args.intersperse_function_argument_keywords(f, kws);
839 f.write_str(")");
840 return;
841 }
842 }
843
844 f.write_node(&self.name);
845 f.write_str("(");
846 if self.distinct {
847 f.write_str("DISTINCT ")
848 }
849 f.write_node(&self.args);
850 f.write_str(")");
851 if let Some(filter) = &self.filter {
852 f.write_str(" FILTER (WHERE ");
853 f.write_node(&filter);
854 f.write_str(")");
855 }
856 if let Some(o) = &self.over {
857 f.write_node(o);
858 }
859 }
860}
861impl_display_t!(Function);
862
863#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
865pub enum FunctionArgs<T: AstInfo> {
866 Star,
868 Args {
870 args: Vec<Expr<T>>,
871 order_by: Vec<OrderByExpr<T>>,
872 },
873}
874
875impl<T: AstInfo> FunctionArgs<T> {
876 pub fn args(args: Vec<Expr<T>>) -> Self {
877 Self::Args {
878 args,
879 order_by: vec![],
880 }
881 }
882
883 pub fn len(&self) -> Option<usize> {
885 match self {
886 FunctionArgs::Star => None,
887 FunctionArgs::Args { args, .. } => Some(args.len()),
888 }
889 }
890
891 fn intersperse_function_argument_keywords<W: fmt::Write>(
893 &self,
894 f: &mut AstFormatter<W>,
895 kws: &[Option<Keyword>],
896 ) {
897 let args = match self {
898 FunctionArgs::Star => unreachable!(),
899 FunctionArgs::Args { args, .. } => args,
900 };
901 soft_assert_eq_or_log!(args.len(), kws.len());
902 let mut delim = "";
903 for (arg, kw) in args.iter().zip_eq(kws) {
904 if let Some(kw) = kw {
905 f.write_str(delim);
906 f.write_str(kw.as_str());
907 delim = " ";
908 }
909 f.write_str(delim);
910 f.write_node(arg);
911 delim = " ";
912 }
913 }
914}
915
916impl<T: AstInfo> AstDisplay for FunctionArgs<T> {
917 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
918 match self {
919 FunctionArgs::Star => f.write_str("*"),
920 FunctionArgs::Args { args, order_by } => {
921 f.write_node(&display::comma_separated(args));
922 if !order_by.is_empty() {
923 f.write_str(" ORDER BY ");
924 f.write_node(&display::comma_separated(order_by));
925 }
926 }
927 }
928 }
929}
930impl_display_t!(FunctionArgs);
931
932#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
933pub enum IsExprConstruct<T: AstInfo> {
934 Null,
935 True,
936 False,
937 Unknown,
938 DistinctFrom(Box<Expr<T>>),
939}
940
941impl<T: AstInfo> AstDisplay for IsExprConstruct<T> {
942 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
943 match self {
944 IsExprConstruct::Null => f.write_str("NULL"),
945 IsExprConstruct::True => f.write_str("TRUE"),
946 IsExprConstruct::False => f.write_str("FALSE"),
947 IsExprConstruct::Unknown => f.write_str("UNKNOWN"),
948 IsExprConstruct::DistinctFrom(e) => {
949 f.write_str("DISTINCT FROM ");
950 e.fmt(f);
951 }
952 }
953 }
954}
955impl_display_t!(IsExprConstruct);