1use crate::ast::*;
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18pub struct Query {
19 pub with: Option<With>,
21 pub body: SetExpr,
23 pub order_by: Vec<OrderByExpr>,
25 pub limit: Option<Expr>,
27 pub offset: Option<String>,
32 pub fetch: Option<Fetch>,
37}
38
39impl Query {
40 pub fn as_simple_values(&self) -> Option<&Values> {
42 match &self {
43 Query {
44 with: None,
45 body: SetExpr::Values(values),
46 order_by,
47 limit: None,
48 offset: None,
49 fetch: None,
50 } if order_by.is_empty() => Some(values),
51 _ => None,
52 }
53 }
54
55 pub fn as_single_select_item(&self) -> Option<&Expr> {
57 match &self {
58 Query {
59 with: None,
60 body: SetExpr::Select(select),
61 order_by,
62 limit: None,
63 offset: None,
64 fetch: None,
65 } if order_by.is_empty() => match select.as_ref() {
66 Select {
67 distinct: Distinct::All,
68 projection,
69 from,
70 lateral_views,
71 selection: None,
72 group_by,
73 having: None,
74 window,
75 } if projection.len() == 1
76 && from.is_empty()
77 && lateral_views.is_empty()
78 && group_by.is_empty()
79 && window.is_empty() =>
80 {
81 match &projection[0] {
82 SelectItem::UnnamedExpr(expr) => Some(expr),
83 SelectItem::ExprWithAlias { expr, .. } => Some(expr),
84 _ => None,
85 }
86 }
87 _ => None,
88 },
89 _ => None,
90 }
91 }
92}
93
94impl fmt::Display for Query {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 if let Some(ref with) = self.with {
97 write!(f, "{} ", with)?;
98 }
99 write!(f, "{}", self.body)?;
100 if !self.order_by.is_empty() {
101 write!(f, " ORDER BY {}", display_comma_separated(&self.order_by))?;
102 }
103 if let Some(ref limit) = self.limit {
104 write!(f, " LIMIT {}", limit)?;
105 }
106 if let Some(ref offset) = self.offset {
107 write!(f, " OFFSET {}", offset)?;
108 }
109 if let Some(ref fetch) = self.fetch {
110 write!(f, " {}", fetch)?;
111 }
112 Ok(())
113 }
114}
115
116#[allow(clippy::large_enum_variant)]
119#[derive(Debug, Clone, PartialEq, Eq, Hash)]
120pub enum SetExpr {
121 Select(Box<Select>),
123 Query(Box<Query>),
126 SetOperation {
128 op: SetOperator,
129 all: bool,
130 corresponding: Corresponding,
131 left: Box<SetExpr>,
132 right: Box<SetExpr>,
133 },
134 Values(Values),
135}
136
137impl fmt::Display for SetExpr {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 match self {
140 SetExpr::Select(s) => write!(f, "{}", s),
141 SetExpr::Query(q) => write!(f, "({})", q),
142 SetExpr::Values(v) => write!(f, "{}", v),
143 SetExpr::SetOperation {
144 left,
145 right,
146 op,
147 all,
148 corresponding,
149 } => {
150 let all_str = if *all { " ALL" } else { "" };
151 write!(f, "{} {}{}{} {}", left, op, all_str, corresponding, right)
152 }
153 }
154 }
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
158pub enum SetOperator {
159 Union,
160 Except,
161 Intersect,
162}
163
164impl fmt::Display for SetOperator {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 f.write_str(match self {
167 SetOperator::Union => "UNION",
168 SetOperator::Except => "EXCEPT",
169 SetOperator::Intersect => "INTERSECT",
170 })
171 }
172}
173
174#[derive(Debug, Clone, PartialEq, Eq, Hash)]
176pub struct Corresponding {
177 pub corresponding: bool,
178 pub column_list: Option<Vec<Ident>>,
179}
180
181impl Corresponding {
182 pub fn with_column_list(column_list: Option<Vec<Ident>>) -> Self {
183 Self {
184 corresponding: true,
185 column_list,
186 }
187 }
188
189 pub fn none() -> Self {
190 Self {
191 corresponding: false,
192 column_list: None,
193 }
194 }
195
196 pub fn is_corresponding(&self) -> bool {
197 self.corresponding
198 }
199
200 pub fn column_list(&self) -> Option<&[Ident]> {
201 self.column_list.as_deref()
202 }
203}
204
205impl fmt::Display for Corresponding {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 if self.corresponding {
208 write!(f, " CORRESPONDING")?;
209 if let Some(column_list) = &self.column_list {
210 write!(f, " BY ({})", display_comma_separated(column_list))?;
211 }
212 }
213 Ok(())
214 }
215}
216
217#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
221pub struct Select {
222 pub distinct: Distinct,
223 pub projection: Vec<SelectItem>,
225 pub from: Vec<TableWithJoins>,
227 pub lateral_views: Vec<LateralView>,
229 pub selection: Option<Expr>,
231 pub group_by: Vec<Expr>,
233 pub having: Option<Expr>,
235 pub window: Vec<NamedWindow>,
237}
238
239impl fmt::Display for Select {
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 write!(f, "SELECT{}", &self.distinct)?;
242 write!(f, " {}", display_comma_separated(&self.projection))?;
243 if !self.from.is_empty() {
244 write!(f, " FROM {}", display_comma_separated(&self.from))?;
245 }
246 if !self.lateral_views.is_empty() {
247 for lv in &self.lateral_views {
248 write!(f, "{}", lv)?;
249 }
250 }
251 if let Some(ref selection) = self.selection {
252 write!(f, " WHERE {}", selection)?;
253 }
254 if !self.group_by.is_empty() {
255 write!(f, " GROUP BY {}", display_comma_separated(&self.group_by))?;
256 }
257 if let Some(ref having) = self.having {
258 write!(f, " HAVING {}", having)?;
259 }
260 if !self.window.is_empty() {
261 write!(f, " WINDOW {}", display_comma_separated(&self.window))?;
262 }
263 Ok(())
264 }
265}
266
267#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
269#[expect(clippy::enum_variant_names)]
270pub enum Distinct {
271 #[default]
273 All,
274 Distinct,
276 DistinctOn(Vec<Expr>),
278}
279
280impl Distinct {
281 pub const fn is_all(&self) -> bool {
282 matches!(self, Distinct::All)
283 }
284
285 pub const fn is_distinct(&self) -> bool {
286 matches!(self, Distinct::Distinct)
287 }
288}
289
290impl fmt::Display for Distinct {
291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292 match self {
293 Distinct::All => write!(f, ""),
294 Distinct::Distinct => write!(f, " DISTINCT"),
295 Distinct::DistinctOn(exprs) => {
296 write!(f, " DISTINCT ON ({})", display_comma_separated(exprs))
297 }
298 }
299 }
300}
301
302#[derive(Debug, Clone, PartialEq, Eq, Hash)]
304pub struct LateralView {
305 pub lateral_view: Expr,
307 pub lateral_view_name: ObjectName,
309 pub lateral_col_alias: Vec<Ident>,
311 pub outer: bool,
313}
314
315impl fmt::Display for LateralView {
316 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317 write!(
318 f,
319 " LATERAL VIEW{outer} {} {}",
320 self.lateral_view,
321 self.lateral_view_name,
322 outer = if self.outer { " OUTER" } else { "" }
323 )?;
324 if !self.lateral_col_alias.is_empty() {
325 write!(
326 f,
327 " AS {}",
328 display_comma_separated(&self.lateral_col_alias)
329 )?;
330 }
331 Ok(())
332 }
333}
334
335#[derive(Debug, Clone, PartialEq, Eq, Hash)]
336pub struct With {
337 pub recursive: bool,
338 pub cte_tables: Vec<Cte>,
339}
340
341impl fmt::Display for With {
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 write!(
344 f,
345 "WITH {}{}",
346 if self.recursive { "RECURSIVE " } else { "" },
347 display_comma_separated(&self.cte_tables)
348 )
349 }
350}
351
352#[derive(Debug, Clone, PartialEq, Eq, Hash)]
358pub struct Cte {
359 pub alias: TableAlias,
360 pub cte_inner: CteInner,
361}
362
363impl fmt::Display for Cte {
364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365 match &self.cte_inner {
366 CteInner::Query(query) => write!(f, "{} AS ({})", self.alias, query)?,
367 CteInner::ChangeLog(obj_name) => {
368 write!(f, "{} AS changelog from {}", self.alias, obj_name)?
369 }
370 }
371 Ok(())
372 }
373}
374
375#[derive(Debug, Clone, PartialEq, Eq, Hash)]
376pub enum CteInner {
377 Query(Box<Query>),
378 ChangeLog(ObjectName),
379}
380
381#[derive(Debug, Clone, PartialEq, Eq, Hash)]
383pub enum SelectItem {
384 UnnamedExpr(Expr),
386 ExprQualifiedWildcard(Expr, Vec<Ident>),
390 ExprWithAlias { expr: Expr, alias: Ident },
392 QualifiedWildcard(ObjectName, Option<Vec<Expr>>),
394 Wildcard(Option<Vec<Expr>>),
396}
397
398impl fmt::Display for SelectItem {
399 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400 match &self {
401 SelectItem::UnnamedExpr(expr) => write!(f, "{}", expr),
402 SelectItem::ExprWithAlias { expr, alias } => write!(f, "{} AS {}", expr, alias),
403 SelectItem::ExprQualifiedWildcard(expr, prefix) => write!(
404 f,
405 "({}){}.*",
406 expr,
407 prefix
408 .iter()
409 .format_with("", |i, f| f(&format_args!(".{i}")))
410 ),
411 SelectItem::QualifiedWildcard(prefix, except) => match except {
412 Some(cols) => write!(
413 f,
414 "{}.* EXCEPT ({})",
415 prefix,
416 cols.iter()
417 .map(|v| v.to_string())
418 .collect::<Vec<String>>()
419 .as_slice()
420 .join(", ")
421 ),
422 None => write!(f, "{}.*", prefix),
423 },
424 SelectItem::Wildcard(except) => match except {
425 Some(cols) => write!(
426 f,
427 "* EXCEPT ({})",
428 cols.iter()
429 .map(|v| v.to_string())
430 .collect::<Vec<String>>()
431 .as_slice()
432 .join(", ")
433 ),
434 None => write!(f, "*"),
435 },
436 }
437 }
438}
439
440#[derive(Debug, Clone, PartialEq, Eq, Hash)]
441pub struct TableWithJoins {
442 pub relation: TableFactor,
443 pub joins: Vec<Join>,
444}
445
446impl fmt::Display for TableWithJoins {
447 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
448 write!(f, "{}", self.relation)?;
449 for join in &self.joins {
450 write!(f, "{}", join)?;
451 }
452 Ok(())
453 }
454}
455
456#[derive(Debug, Clone, PartialEq, Eq, Hash)]
458pub enum TableFactor {
459 Table {
460 name: ObjectName,
461 alias: Option<TableAlias>,
462 as_of: Option<AsOf>,
463 },
464 Derived {
465 lateral: bool,
466 subquery: Box<Query>,
467 alias: Option<TableAlias>,
468 },
469 TableFunction {
473 name: ObjectName,
474 alias: Option<TableAlias>,
475 args: Vec<FunctionArg>,
476 with_ordinality: bool,
477 },
478 NestedJoin(Box<TableWithJoins>),
485}
486
487impl fmt::Display for TableFactor {
488 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
489 match self {
490 TableFactor::Table { name, alias, as_of } => {
491 write!(f, "{}", name)?;
492 if let Some(as_of) = as_of {
493 write!(f, "{}", as_of)?
494 }
495 if let Some(alias) = alias {
496 write!(f, " AS {}", alias)?;
497 }
498 Ok(())
499 }
500 TableFactor::Derived {
501 lateral,
502 subquery,
503 alias,
504 } => {
505 if *lateral {
506 write!(f, "LATERAL ")?;
507 }
508 write!(f, "({})", subquery)?;
509 if let Some(alias) = alias {
510 write!(f, " AS {}", alias)?;
511 }
512 Ok(())
513 }
514 TableFactor::TableFunction {
515 name,
516 alias,
517 args,
518 with_ordinality,
519 } => {
520 write!(f, "{}({})", name, display_comma_separated(args))?;
521 if *with_ordinality {
522 write!(f, " WITH ORDINALITY")?;
523 }
524 if let Some(alias) = alias {
525 write!(f, " AS {}", alias)?;
526 }
527 Ok(())
528 }
529 TableFactor::NestedJoin(table_reference) => write!(f, "({})", table_reference),
530 }
531 }
532}
533
534#[derive(Debug, Clone, PartialEq, Eq, Hash)]
535pub struct TableAlias {
536 pub name: Ident,
537 pub columns: Vec<Ident>,
538}
539
540impl fmt::Display for TableAlias {
541 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
542 write!(f, "{}", self.name)?;
543 if !self.columns.is_empty() {
544 write!(f, " ({})", display_comma_separated(&self.columns))?;
545 }
546 Ok(())
547 }
548}
549
550#[derive(Debug, Clone, PartialEq, Eq, Hash)]
551pub struct Join {
552 pub relation: TableFactor,
553 pub join_operator: JoinOperator,
554}
555
556impl fmt::Display for Join {
557 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
558 fn prefix(constraint: &JoinConstraint) -> &'static str {
559 match constraint {
560 JoinConstraint::Natural => "NATURAL ",
561 _ => "",
562 }
563 }
564 fn suffix(constraint: &'_ JoinConstraint) -> impl fmt::Display + '_ {
565 struct Suffix<'a>(&'a JoinConstraint);
566 impl fmt::Display for Suffix<'_> {
567 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
568 match self.0 {
569 JoinConstraint::On(expr) => write!(f, " ON {}", expr),
570 JoinConstraint::Using(attrs) => {
571 write!(f, " USING({})", display_comma_separated(attrs))
572 }
573 _ => Ok(()),
574 }
575 }
576 }
577 Suffix(constraint)
578 }
579 match &self.join_operator {
580 JoinOperator::Inner(constraint) => write!(
581 f,
582 " {}JOIN {}{}",
583 prefix(constraint),
584 self.relation,
585 suffix(constraint)
586 ),
587 JoinOperator::LeftOuter(constraint) => write!(
588 f,
589 " {}LEFT JOIN {}{}",
590 prefix(constraint),
591 self.relation,
592 suffix(constraint)
593 ),
594 JoinOperator::RightOuter(constraint) => write!(
595 f,
596 " {}RIGHT JOIN {}{}",
597 prefix(constraint),
598 self.relation,
599 suffix(constraint)
600 ),
601 JoinOperator::FullOuter(constraint) => write!(
602 f,
603 " {}FULL JOIN {}{}",
604 prefix(constraint),
605 self.relation,
606 suffix(constraint)
607 ),
608 JoinOperator::CrossJoin => write!(f, " CROSS JOIN {}", self.relation),
609 JoinOperator::AsOfInner(constraint) => write!(
610 f,
611 " {}ASOF JOIN {}{}",
612 prefix(constraint),
613 self.relation,
614 suffix(constraint)
615 ),
616 JoinOperator::AsOfLeft(constraint) => write!(
617 f,
618 " {}ASOF LEFT JOIN {}{}",
619 prefix(constraint),
620 self.relation,
621 suffix(constraint)
622 ),
623 }
624 }
625}
626
627#[derive(Debug, Clone, PartialEq, Eq, Hash)]
628pub enum JoinOperator {
629 Inner(JoinConstraint),
630 LeftOuter(JoinConstraint),
631 RightOuter(JoinConstraint),
632 FullOuter(JoinConstraint),
633 CrossJoin,
634 AsOfInner(JoinConstraint),
635 AsOfLeft(JoinConstraint),
636}
637
638#[derive(Debug, Clone, PartialEq, Eq, Hash)]
639pub enum JoinConstraint {
640 On(Expr),
641 Using(Vec<Ident>),
642 Natural,
643 None,
644}
645
646#[derive(Debug, Clone, PartialEq, Eq, Hash)]
648pub struct OrderByExpr {
649 pub expr: Expr,
650 pub asc: Option<bool>,
652 pub nulls_first: Option<bool>,
654}
655
656impl fmt::Display for OrderByExpr {
657 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
658 write!(f, "{}", self.expr)?;
659 match self.asc {
660 Some(true) => write!(f, " ASC")?,
661 Some(false) => write!(f, " DESC")?,
662 None => (),
663 }
664 match self.nulls_first {
665 Some(true) => write!(f, " NULLS FIRST")?,
666 Some(false) => write!(f, " NULLS LAST")?,
667 None => (),
668 }
669 Ok(())
670 }
671}
672
673#[derive(Debug, Clone, PartialEq, Eq, Hash)]
674pub struct Fetch {
675 pub with_ties: bool,
676 pub quantity: Option<String>,
677}
678
679impl fmt::Display for Fetch {
680 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
681 let extension = if self.with_ties { "WITH TIES" } else { "ONLY" };
682 if let Some(ref quantity) = self.quantity {
683 write!(f, "FETCH FIRST {} ROWS {}", quantity, extension)
684 } else {
685 write!(f, "FETCH FIRST ROWS {}", extension)
686 }
687 }
688}
689
690#[derive(Debug, Clone, PartialEq, Eq, Hash)]
691pub struct Top {
692 pub with_ties: bool,
694 pub percent: bool,
695 pub quantity: Option<Expr>,
696}
697
698impl fmt::Display for Top {
699 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
700 let extension = if self.with_ties { " WITH TIES" } else { "" };
701 if let Some(ref quantity) = self.quantity {
702 let percent = if self.percent { " PERCENT" } else { "" };
703 write!(f, "TOP ({}){}{}", quantity, percent, extension)
704 } else {
705 write!(f, "TOP{}", extension)
706 }
707 }
708}
709
710#[derive(Debug, Clone, PartialEq, Eq, Hash)]
711pub struct Values(pub Vec<Vec<Expr>>);
712
713impl fmt::Display for Values {
714 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
715 write!(f, "VALUES ")?;
716 let mut delim = "";
717 for row in &self.0 {
718 write!(f, "{}", delim)?;
719 delim = ", ";
720 write!(f, "({})", display_comma_separated(row))?;
721 }
722 Ok(())
723 }
724}
725
726#[derive(Debug, Clone, PartialEq, Eq, Hash)]
728pub struct NamedWindow {
729 pub name: Ident,
730 pub window_spec: WindowSpec,
731}
732
733impl fmt::Display for NamedWindow {
734 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
735 write!(f, "{} AS ({})", self.name, self.window_spec)
736 }
737}