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