1use std::fmt::{self, Debug};
22use std::hash::Hash;
23use std::mem;
24
25use crate::ast::display::{self, AstDisplay, AstFormatter, WithOptionName};
26use crate::ast::{AstInfo, Expr, Function, Ident, ShowStatement, WithOptionValue};
27
28#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
31pub struct Query<T: AstInfo> {
32 pub ctes: CteBlock<T>,
34 pub body: SetExpr<T>,
36 pub order_by: Vec<OrderByExpr<T>>,
38 pub limit: Option<Limit<T>>,
41 pub offset: Option<Expr<T>>,
43}
44
45impl<T: AstInfo> AstDisplay for Query<T> {
46 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
47 f.write_node(&self.ctes);
48 f.write_node(&self.body);
49 if !self.order_by.is_empty() {
50 f.write_str(" ORDER BY ");
51 f.write_node(&display::comma_separated(&self.order_by));
52 }
53
54 let write_offset = |f: &mut AstFormatter<W>| {
55 if let Some(offset) = &self.offset {
56 f.write_str(" OFFSET ");
57 f.write_node(offset);
58 }
59 };
60
61 if let Some(limit) = &self.limit {
62 if limit.with_ties {
63 write_offset(f);
64 f.write_str(" FETCH FIRST ");
65 f.write_node(&limit.quantity);
66 f.write_str(" ROWS WITH TIES");
67 } else {
68 f.write_str(" LIMIT ");
69 f.write_node(&limit.quantity);
70 write_offset(f);
71 }
72 } else {
73 write_offset(f);
74 }
75 }
76}
77impl_display_t!(Query);
78
79impl<T: AstInfo> Query<T> {
80 pub fn select(select: Select<T>) -> Query<T> {
81 Query {
82 ctes: CteBlock::empty(),
83 body: SetExpr::Select(Box::new(select)),
84 order_by: vec![],
85 limit: None,
86 offset: None,
87 }
88 }
89
90 pub fn query(query: Query<T>) -> Query<T> {
91 Query {
92 ctes: CteBlock::empty(),
93 body: SetExpr::Query(Box::new(query)),
94 order_by: vec![],
95 limit: None,
96 offset: None,
97 }
98 }
99
100 pub fn take(&mut self) -> Query<T> {
101 mem::replace(
102 self,
103 Query::<T> {
104 ctes: CteBlock::empty(),
105 order_by: vec![],
106 body: SetExpr::Values(Values(vec![])),
107 limit: None,
108 offset: None,
109 },
110 )
111 }
112}
113
114#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
117pub enum SetExpr<T: AstInfo> {
118 Select(Box<Select<T>>),
120 Query(Box<Query<T>>),
123 SetOperation {
125 op: SetOperator,
126 all: bool,
127 left: Box<SetExpr<T>>,
128 right: Box<SetExpr<T>>,
129 },
130 Values(Values<T>),
131 Show(ShowStatement<T>),
132 Table(T::ItemName),
133}
134
135impl<T: AstInfo> SetExpr<T> {
136 pub fn starts_with_show(&self) -> bool {
141 match self {
142 SetExpr::Show(_) => true,
143 SetExpr::SetOperation { left, .. } => left.starts_with_show(),
144 _ => false,
145 }
146 }
147}
148
149impl<T: AstInfo> AstDisplay for SetExpr<T> {
150 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
151 match self {
152 SetExpr::Select(s) => f.write_node(s),
153 SetExpr::Query(q) => {
154 f.write_str("(");
155 f.write_node(q);
156 f.write_str(")")
157 }
158 SetExpr::Values(v) => f.write_node(v),
159 SetExpr::Show(v) => f.write_node(v),
160 SetExpr::Table(t) => {
161 f.write_str("TABLE ");
162 f.write_node(t)
163 }
164 SetExpr::SetOperation {
165 left,
166 right,
167 op,
168 all,
169 } => {
170 f.write_node(left);
171 f.write_str(" ");
172 f.write_node(op);
173 f.write_str(" ");
174 if *all {
175 f.write_str("ALL ");
176 }
177 f.write_node(right);
178 }
179 }
180 }
181}
182impl_display_t!(SetExpr);
183
184#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
185pub enum SetOperator {
186 Union,
187 Except,
188 Intersect,
189}
190
191impl AstDisplay for SetOperator {
192 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
193 f.write_str(match self {
194 SetOperator::Union => "UNION",
195 SetOperator::Except => "EXCEPT",
196 SetOperator::Intersect => "INTERSECT",
197 })
198 }
199}
200impl_display!(SetOperator);
201
202#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
203pub enum SelectOptionName {
204 ExpectedGroupSize,
205 AggregateInputGroupSize,
206 DistinctOnInputGroupSize,
207 LimitInputGroupSize,
208}
209
210impl AstDisplay for SelectOptionName {
211 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
212 f.write_str(match self {
213 SelectOptionName::ExpectedGroupSize => "EXPECTED GROUP SIZE",
214 SelectOptionName::AggregateInputGroupSize => "AGGREGATE INPUT GROUP SIZE",
215 SelectOptionName::DistinctOnInputGroupSize => "DISTINCT ON INPUT GROUP SIZE",
216 SelectOptionName::LimitInputGroupSize => "LIMIT INPUT GROUP SIZE",
217 })
218 }
219}
220impl_display!(SelectOptionName);
221
222impl WithOptionName for SelectOptionName {
223 fn redact_value(&self) -> bool {
229 match self {
230 SelectOptionName::ExpectedGroupSize
231 | SelectOptionName::AggregateInputGroupSize
232 | SelectOptionName::DistinctOnInputGroupSize
233 | SelectOptionName::LimitInputGroupSize => false,
234 }
235 }
236}
237
238#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
239pub struct SelectOption<T: AstInfo> {
240 pub name: SelectOptionName,
241 pub value: Option<WithOptionValue<T>>,
242}
243impl_display_for_with_option!(SelectOption);
244
245#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
249pub struct Select<T: AstInfo> {
250 pub distinct: Option<Distinct<T>>,
251 pub projection: Vec<SelectItem<T>>,
253 pub from: Vec<TableWithJoins<T>>,
255 pub selection: Option<Expr<T>>,
257 pub group_by: Vec<Expr<T>>,
259 pub having: Option<Expr<T>>,
261 pub qualify: Option<Expr<T>>,
263 pub options: Vec<SelectOption<T>>,
265}
266
267impl<T: AstInfo> AstDisplay for Select<T> {
268 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
269 f.write_str("SELECT");
270 if let Some(distinct) = &self.distinct {
271 f.write_str(" ");
272 f.write_node(distinct);
273 }
274 if !self.projection.is_empty() {
275 f.write_str(" ");
276 f.write_node(&display::comma_separated(&self.projection));
277 }
278 if !self.from.is_empty() {
279 f.write_str(" FROM ");
280 f.write_node(&display::comma_separated(&self.from));
281 }
282 if let Some(ref selection) = self.selection {
283 f.write_str(" WHERE ");
284 f.write_node(selection);
285 }
286 if !self.group_by.is_empty() {
287 f.write_str(" GROUP BY ");
288 f.write_node(&display::comma_separated(&self.group_by));
289 }
290 if let Some(ref having) = self.having {
291 f.write_str(" HAVING ");
292 f.write_node(having);
293 }
294 if let Some(ref qualify) = self.qualify {
295 f.write_str(" QUALIFY ");
296 f.write_node(qualify);
297 }
298 if !self.options.is_empty() {
299 f.write_str(" OPTIONS (");
300 f.write_node(&display::comma_separated(&self.options));
301 f.write_str(")");
302 }
303 }
304}
305impl_display_t!(Select);
306
307impl<T: AstInfo> Select<T> {
308 pub fn from(mut self, twj: TableWithJoins<T>) -> Select<T> {
309 self.from.push(twj);
310 self
311 }
312
313 pub fn project(mut self, select_item: SelectItem<T>) -> Select<T> {
314 self.projection.push(select_item);
315 self
316 }
317
318 pub fn selection(mut self, selection: Option<Expr<T>>) -> Select<T> {
319 self.selection = selection;
320 self
321 }
322}
323
324#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
325pub enum Distinct<T: AstInfo> {
326 EntireRow,
327 On(Vec<Expr<T>>),
328}
329impl_display_t!(Distinct);
330
331impl<T: AstInfo> AstDisplay for Distinct<T> {
332 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
333 match self {
334 Distinct::EntireRow => f.write_str("DISTINCT"),
335 Distinct::On(cols) => {
336 f.write_str("DISTINCT ON (");
337 f.write_node(&display::comma_separated(cols));
338 f.write_str(")");
339 }
340 }
341 }
342}
343
344#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
350pub enum CteBlock<T: AstInfo> {
351 Simple(Vec<Cte<T>>),
352 MutuallyRecursive(MutRecBlock<T>),
353}
354
355#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
356pub struct MutRecBlock<T: AstInfo> {
357 pub options: Vec<MutRecBlockOption<T>>,
358 pub ctes: Vec<CteMutRec<T>>,
359}
360
361impl<T: AstInfo> CteBlock<T> {
362 pub fn empty() -> Self {
364 CteBlock::Simple(Vec::new())
365 }
366 pub fn is_empty(&self) -> bool {
368 match self {
369 CteBlock::Simple(list) => list.is_empty(),
370 CteBlock::MutuallyRecursive(list) => list.ctes.is_empty(),
371 }
372 }
373 pub fn bound_identifiers(&self) -> impl Iterator<Item = &Ident> {
375 let mut names = Vec::new();
376 match self {
377 CteBlock::Simple(list) => {
378 for cte in list.iter() {
379 names.push(&cte.alias.name);
380 }
381 }
382 CteBlock::MutuallyRecursive(MutRecBlock { options: _, ctes }) => {
383 for cte in ctes.iter() {
384 names.push(&cte.name);
385 }
386 }
387 }
388 names.into_iter()
389 }
390}
391
392impl<T: AstInfo> AstDisplay for CteBlock<T> {
393 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
394 if !self.is_empty() {
395 match self {
396 CteBlock::Simple(list) => {
397 f.write_str("WITH ");
398 f.write_node(&display::comma_separated(list));
399 }
400 CteBlock::MutuallyRecursive(MutRecBlock { options, ctes }) => {
401 f.write_str("WITH MUTUALLY RECURSIVE ");
402 if !options.is_empty() {
403 f.write_str("(");
404 f.write_node(&display::comma_separated(options));
405 f.write_str(") ");
406 }
407 f.write_node(&display::comma_separated(ctes));
408 }
409 }
410 f.write_str(" ");
411 }
412 }
413}
414
415#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
420pub struct Cte<T: AstInfo> {
421 pub alias: TableAlias,
422 pub id: T::CteId,
423 pub query: Query<T>,
424}
425
426impl<T: AstInfo> AstDisplay for Cte<T> {
427 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
428 f.write_node(&self.alias);
429 f.write_str(" AS (");
430 f.write_node(&self.query);
431 f.write_str(")");
432 }
433}
434impl_display_t!(Cte);
435
436#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
437pub struct CteMutRec<T: AstInfo> {
438 pub name: Ident,
439 pub columns: Vec<CteMutRecColumnDef<T>>,
440 pub id: T::CteId,
441 pub query: Query<T>,
442}
443
444impl<T: AstInfo> AstDisplay for CteMutRec<T> {
445 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
446 f.write_node(&self.name);
447 if !self.columns.is_empty() {
448 f.write_str(" (");
449 f.write_node(&display::comma_separated(&self.columns));
450 f.write_str(")");
451 }
452 f.write_str(" AS (");
453 f.write_node(&self.query);
454 f.write_str(")");
455 }
456}
457impl_display_t!(CteMutRec);
458
459#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
461pub struct CteMutRecColumnDef<T: AstInfo> {
462 pub name: Ident,
463 pub data_type: T::DataType,
464}
465
466impl<T: AstInfo> AstDisplay for CteMutRecColumnDef<T> {
467 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
468 f.write_node(&self.name);
469 f.write_str(" ");
470 f.write_node(&self.data_type);
471 }
472}
473impl_display_t!(CteMutRecColumnDef);
474
475#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
476pub enum MutRecBlockOptionName {
477 RecursionLimit,
478 ErrorAtRecursionLimit,
479 ReturnAtRecursionLimit,
480}
481
482impl AstDisplay for MutRecBlockOptionName {
483 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
484 f.write_str(match self {
485 MutRecBlockOptionName::RecursionLimit => "RECURSION LIMIT",
486 MutRecBlockOptionName::ErrorAtRecursionLimit => "ERROR AT RECURSION LIMIT",
487 MutRecBlockOptionName::ReturnAtRecursionLimit => "RETURN AT RECURSION LIMIT",
488 })
489 }
490}
491impl_display!(MutRecBlockOptionName);
492
493impl WithOptionName for MutRecBlockOptionName {
494 fn redact_value(&self) -> bool {
500 match self {
501 MutRecBlockOptionName::RecursionLimit
502 | MutRecBlockOptionName::ErrorAtRecursionLimit
503 | MutRecBlockOptionName::ReturnAtRecursionLimit => false,
504 }
505 }
506}
507
508#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
509pub struct MutRecBlockOption<T: AstInfo> {
510 pub name: MutRecBlockOptionName,
511 pub value: Option<WithOptionValue<T>>,
512}
513impl_display_for_with_option!(MutRecBlockOption);
514
515#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
517pub enum SelectItem<T: AstInfo> {
518 Expr { expr: Expr<T>, alias: Option<Ident> },
520 Wildcard,
522}
523
524impl<T: AstInfo> AstDisplay for SelectItem<T> {
525 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
526 match &self {
527 SelectItem::Expr { expr, alias } => {
528 f.write_node(expr);
529 if let Some(alias) = alias {
530 f.write_str(" AS ");
531 f.write_node(alias);
532 }
533 }
534 SelectItem::Wildcard => f.write_str("*"),
535 }
536 }
537}
538impl_display_t!(SelectItem);
539
540#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
541pub struct TableWithJoins<T: AstInfo> {
542 pub relation: TableFactor<T>,
543 pub joins: Vec<Join<T>>,
544}
545
546impl<T: AstInfo> AstDisplay for TableWithJoins<T> {
547 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
548 f.write_node(&self.relation);
549 for join in &self.joins {
550 f.write_node(join)
551 }
552 }
553}
554impl_display_t!(TableWithJoins);
555
556impl<T: AstInfo> TableWithJoins<T> {
557 pub fn subquery(query: Query<T>, alias: TableAlias) -> TableWithJoins<T> {
558 TableWithJoins {
559 relation: TableFactor::Derived {
560 lateral: false,
561 subquery: Box::new(query),
562 alias: Some(alias),
563 },
564 joins: vec![],
565 }
566 }
567}
568
569#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
571pub enum TableFactor<T: AstInfo> {
572 Table {
573 name: T::ItemName,
574 alias: Option<TableAlias>,
575 },
576 Function {
577 function: Function<T>,
578 alias: Option<TableAlias>,
579 with_ordinality: bool,
580 },
581 RowsFrom {
582 functions: Vec<Function<T>>,
583 alias: Option<TableAlias>,
584 with_ordinality: bool,
585 },
586 Derived {
587 lateral: bool,
588 subquery: Box<Query<T>>,
589 alias: Option<TableAlias>,
590 },
591 NestedJoin {
596 join: Box<TableWithJoins<T>>,
597 alias: Option<TableAlias>,
598 },
599}
600
601impl<T: AstInfo> AstDisplay for TableFactor<T> {
602 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
603 match self {
604 TableFactor::Table { name, alias } => {
605 f.write_node(name);
606 if let Some(alias) = alias {
607 f.write_str(" AS ");
608 f.write_node(alias);
609 }
610 }
611 TableFactor::Function {
612 function,
613 alias,
614 with_ordinality,
615 } => {
616 function.fmt_table_call(f);
617 if *with_ordinality {
618 f.write_str(" WITH ORDINALITY");
619 }
620 if let Some(alias) = &alias {
621 f.write_str(" AS ");
622 f.write_node(alias);
623 }
624 }
625 TableFactor::RowsFrom {
626 functions,
627 alias,
628 with_ordinality,
629 } => {
630 f.write_str("ROWS FROM (");
631 for (i, function) in functions.iter().enumerate() {
632 if i > 0 {
633 f.write_str(", ");
634 }
635 function.fmt_table_call(f);
636 }
637 f.write_str(")");
638 if *with_ordinality {
639 f.write_str(" WITH ORDINALITY");
640 }
641 if let Some(alias) = alias {
642 f.write_str(" AS ");
643 f.write_node(alias);
644 }
645 }
646 TableFactor::Derived {
647 lateral,
648 subquery,
649 alias,
650 } => {
651 if *lateral {
652 f.write_str("LATERAL ");
653 }
654 f.write_str("(");
655 f.write_node(subquery);
656 f.write_str(")");
657 if let Some(alias) = alias {
658 f.write_str(" AS ");
659 f.write_node(alias);
660 }
661 }
662 TableFactor::NestedJoin { join, alias } => {
663 f.write_str("(");
664 f.write_node(join);
665 f.write_str(")");
666 if let Some(alias) = alias {
667 f.write_str(" AS ");
668 f.write_node(alias);
669 }
670 }
671 }
672 }
673}
674impl_display_t!(TableFactor);
675
676#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
677pub struct TableAlias {
678 pub name: Ident,
679 pub columns: Vec<Ident>,
680 pub strict: bool,
686}
687
688impl AstDisplay for TableAlias {
689 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
690 f.write_node(&self.name);
691 if !self.columns.is_empty() {
692 f.write_str(" (");
693 f.write_node(&display::comma_separated(&self.columns));
694 f.write_str(")");
695 }
696 }
697}
698impl_display!(TableAlias);
699
700#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
701pub struct Join<T: AstInfo> {
702 pub relation: TableFactor<T>,
703 pub join_operator: JoinOperator<T>,
704}
705
706impl<T: AstInfo> AstDisplay for Join<T> {
707 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
708 fn prefix<T: AstInfo>(constraint: &JoinConstraint<T>) -> &'static str {
709 match constraint {
710 JoinConstraint::Natural => "NATURAL ",
711 _ => "",
712 }
713 }
714 fn suffix<'a, T: AstInfo>(constraint: &'a JoinConstraint<T>) -> impl AstDisplay + 'a {
715 struct Suffix<'a, T: AstInfo>(&'a JoinConstraint<T>);
716 impl<'a, T: AstInfo> AstDisplay for Suffix<'a, T> {
717 fn fmt<W>(&self, f: &mut AstFormatter<W>)
718 where
719 W: fmt::Write,
720 {
721 match self.0 {
722 JoinConstraint::On(expr) => {
723 f.write_str(" ON ");
724 f.write_node(expr);
725 }
726 JoinConstraint::Using { columns, alias } => {
727 f.write_str(" USING (");
728 f.write_node(&display::comma_separated(columns));
729 f.write_str(")");
730
731 if let Some(join_using_alias) = alias {
732 f.write_str(" AS ");
733 f.write_node(join_using_alias);
734 }
735 }
736 JoinConstraint::Natural => {}
737 }
738 }
739 }
740 Suffix(constraint)
741 }
742 match &self.join_operator {
743 JoinOperator::Inner(constraint) => {
744 f.write_str(" ");
745 f.write_str(prefix(constraint));
746 f.write_str("JOIN ");
747 f.write_node(&self.relation);
748 f.write_node(&suffix(constraint));
749 }
750 JoinOperator::LeftOuter(constraint) => {
751 f.write_str(" ");
752 f.write_str(prefix(constraint));
753 f.write_str("LEFT JOIN ");
754 f.write_node(&self.relation);
755 f.write_node(&suffix(constraint));
756 }
757 JoinOperator::RightOuter(constraint) => {
758 f.write_str(" ");
759 f.write_str(prefix(constraint));
760 f.write_str("RIGHT JOIN ");
761 f.write_node(&self.relation);
762 f.write_node(&suffix(constraint));
763 }
764 JoinOperator::FullOuter(constraint) => {
765 f.write_str(" ");
766 f.write_str(prefix(constraint));
767 f.write_str("FULL JOIN ");
768 f.write_node(&self.relation);
769 f.write_node(&suffix(constraint));
770 }
771 JoinOperator::CrossJoin => {
772 f.write_str(" CROSS JOIN ");
773 f.write_node(&self.relation);
774 }
775 }
776 }
777}
778impl_display_t!(Join);
779
780#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
781pub enum JoinOperator<T: AstInfo> {
782 Inner(JoinConstraint<T>),
783 LeftOuter(JoinConstraint<T>),
784 RightOuter(JoinConstraint<T>),
785 FullOuter(JoinConstraint<T>),
786 CrossJoin,
787}
788
789#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
790pub enum JoinConstraint<T: AstInfo> {
791 On(Expr<T>),
792 Using {
793 columns: Vec<Ident>,
794 alias: Option<Ident>,
795 },
796 Natural,
797}
798
799#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
801pub struct OrderByExpr<T: AstInfo> {
802 pub expr: Expr<T>,
803 pub asc: Option<bool>,
804 pub nulls_last: Option<bool>,
805}
806
807impl<T: AstInfo> AstDisplay for OrderByExpr<T> {
808 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
809 f.write_node(&self.expr);
810 match self.asc {
811 Some(true) => f.write_str(" ASC"),
812 Some(false) => f.write_str(" DESC"),
813 None => {}
814 }
815 match self.nulls_last {
816 Some(true) => f.write_str(" NULLS LAST"),
817 Some(false) => f.write_str(" NULLS FIRST"),
818 None => {}
819 }
820 }
821}
822impl_display_t!(OrderByExpr);
823
824#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
825pub struct Limit<T: AstInfo> {
826 pub with_ties: bool,
827 pub quantity: Expr<T>,
828}
829
830#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
831pub struct Values<T: AstInfo>(pub Vec<Vec<Expr<T>>>);
832
833impl<T: AstInfo> AstDisplay for Values<T> {
834 fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
835 f.write_str("VALUES ");
836 let mut delim = "";
837
838 for (i, row) in self.0.iter().enumerate() {
839 if f.redacted() && i == 20 {
840 f.write_str("/* ");
841 f.write_str(&(self.0.len().saturating_sub(20)).to_string());
842 f.write_str(" more rows */");
843 break;
844 }
845
846 f.write_str(delim);
847 delim = ", ";
848 f.write_str("(");
849 f.write_node(&display::comma_separated(row));
850 f.write_str(")");
851 }
852 }
853}
854impl_display_t!(Values);