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