Skip to main content

lutra_sql/
query.rs

1#[cfg(not(feature = "std"))]
2use alloc::{boxed::Box, vec::Vec};
3
4use super::display_utils::{SpaceOrNewline, indented_list};
5use crate::dml::{MergeClause, OutputClause, Update};
6use crate::*;
7
8/// The most complete variant of a `SELECT` query expression, optionally
9/// including `WITH`, `UNION` / other set operations, and `ORDER BY`.
10#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
11pub struct Query {
12    /// WITH (common table expressions, or CTEs)
13    pub with: Option<With>,
14    /// SELECT or UNION / EXCEPT / INTERSECT
15    pub body: Box<SetExpr>,
16    /// ORDER BY
17    pub order_by: Option<OrderBy>,
18    /// LIMIT
19    pub limit: Option<Expr>,
20    /// OFFSET
21    pub offset: Option<Expr>,
22}
23
24impl fmt::Display for Query {
25    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26        if let Some(ref with) = self.with {
27            with.fmt(f)?;
28            SpaceOrNewline.fmt(f)?;
29        }
30        self.body.fmt(f)?;
31        if let Some(ref order_by) = self.order_by {
32            SpaceOrNewline.fmt(f)?;
33            order_by.fmt(f)?;
34        }
35
36        if let Some(ref offset) = self.offset {
37            SpaceOrNewline.fmt(f)?;
38            f.write_str("OFFSET ")?;
39            offset.fmt(f)?;
40        }
41        if let Some(ref limit) = self.limit {
42            SpaceOrNewline.fmt(f)?;
43            f.write_str("LIMIT ")?;
44            limit.fmt(f)?;
45        }
46        Ok(())
47    }
48}
49
50/// A node in a tree, representing a "query body" expression, roughly:
51/// `SELECT ... [ {UNION|EXCEPT|INTERSECT} SELECT ...]`
52#[allow(clippy::large_enum_variant)]
53#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
54pub enum SetExpr {
55    /// Restricted SELECT .. FROM .. HAVING (no ORDER BY or set operations)
56    Select(Box<Select>),
57    /// Parenthesized SELECT subquery, which may include more set operations
58    /// in its body and an optional ORDER BY / LIMIT.
59    Query(Box<Query>),
60    /// UNION/EXCEPT/INTERSECT of two queries
61    SetOperation {
62        op: SetOperator,
63        set_quantifier: SetQuantifier,
64        left: Box<SetExpr>,
65        right: Box<SetExpr>,
66    },
67    Values(Values),
68    Insert(Insert),
69    Update(Update),
70    Delete(Delete),
71    Merge {
72        /// optional INTO keyword
73        into: bool,
74        /// Specifies the table to merge
75        table: RelNamed,
76        /// Specifies the table or subquery to join with the target table
77        source: RelNamed,
78        /// Specifies the expression on which to join the target table and source
79        on: Box<Expr>,
80        /// Specifies the actions to perform when values match or do not match.
81        clauses: Vec<MergeClause>,
82        // Specifies the output to save changes in MSSQL
83        output: Option<OutputClause>,
84    },
85    Source(String),
86    Copy(Box<Copy>),
87}
88
89impl SetExpr {
90    /// If this `SetExpr` is a `SELECT`, returns the [`Select`].
91    pub fn as_select(&self) -> Option<&Select> {
92        if let Self::Select(select) = self {
93            Some(&**select)
94        } else {
95            None
96        }
97    }
98}
99
100impl fmt::Display for SetExpr {
101    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102        match self {
103            SetExpr::Select(s) => s.fmt(f),
104            SetExpr::Query(q) => {
105                f.write_str("(")?;
106                q.fmt(f)?;
107                f.write_str(")")
108            }
109            SetExpr::Values(v) => v.fmt(f),
110            SetExpr::Insert(v) => v.fmt(f),
111            SetExpr::Update(v) => v.fmt(f),
112            SetExpr::Delete(v) => v.fmt(f),
113            SetExpr::Merge {
114                into,
115                table,
116                source,
117                on,
118                clauses,
119                output,
120            } => {
121                write!(
122                    f,
123                    "MERGE{int} {table} USING {source} ",
124                    int = if *into { " INTO" } else { "" }
125                )?;
126                write!(f, "ON {on} ")?;
127                write!(f, "{}", display_separated(clauses, " "))?;
128                if let Some(output) = output {
129                    write!(f, " {output}")?;
130                }
131                Ok(())
132            }
133            SetExpr::SetOperation {
134                left,
135                right,
136                op,
137                set_quantifier,
138            } => {
139                Indent(left).fmt(f)?;
140                SpaceOrNewline.fmt(f)?;
141
142                op.fmt(f)?;
143                f.write_str(" ")?;
144                set_quantifier.fmt(f)?;
145
146                SpaceOrNewline.fmt(f)?;
147                Indent(right).fmt(f)?;
148                Ok(())
149            }
150            SetExpr::Source(s) => f.write_str(s),
151            SetExpr::Copy(c) => c.fmt(f),
152        }
153    }
154}
155
156#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
157pub struct Copy {
158    pub source: SetExpr,
159    pub target: Expr,
160    pub options: String,
161}
162
163impl fmt::Display for Copy {
164    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165        f.write_str("COPY(")?;
166        self.source.fmt(f)?;
167        f.write_str(") TO ")?;
168        self.target.fmt(f)?;
169        f.write_str(" (")?;
170        f.write_str(&self.options)?;
171        f.write_str(")")?;
172        Ok(())
173    }
174}
175
176#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
177pub enum SetOperator {
178    Union,
179    Except,
180    Intersect,
181    Minus,
182}
183
184impl fmt::Display for SetOperator {
185    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
186        f.write_str(match self {
187            SetOperator::Union => "UNION",
188            SetOperator::Except => "EXCEPT",
189            SetOperator::Intersect => "INTERSECT",
190            SetOperator::Minus => "MINUS",
191        })
192    }
193}
194
195/// A quantifier for [SetOperator].
196#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
197pub enum SetQuantifier {
198    All,
199    Distinct,
200    ByName,
201    AllByName,
202    DistinctByName,
203}
204
205impl fmt::Display for SetQuantifier {
206    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207        match self {
208            SetQuantifier::All => write!(f, "ALL"),
209            SetQuantifier::Distinct => write!(f, "DISTINCT"),
210            SetQuantifier::ByName => write!(f, "BY NAME"),
211            SetQuantifier::AllByName => write!(f, "ALL BY NAME"),
212            SetQuantifier::DistinctByName => write!(f, "DISTINCT BY NAME"),
213        }
214    }
215}
216
217/// A restricted variant of `SELECT` (without CTEs/`ORDER BY`), which may
218/// appear either as the only body item of a `Query`, or as an operand
219/// to a set operation like `UNION`.
220#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
221pub struct Select {
222    /// FROM
223    pub from: Vec<RelNamed>,
224    /// WHERE
225    pub selection: Option<Expr>,
226    /// GROUP BY
227    pub group_by: Vec<Expr>,
228    /// `SELECT [DISTINCT] ...`
229    pub distinct: Option<Distinct>,
230    /// projection expressions
231    pub projection: Vec<SelectItem>,
232    /// HAVING
233    pub having: Option<Expr>,
234}
235
236impl fmt::Display for Select {
237    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
238        let mut is_first = true;
239        fn space_or_nl(f: &mut fmt::Formatter<'_>, is_first: &mut bool) -> fmt::Result {
240            if *is_first {
241                *is_first = false;
242                return Ok(());
243            }
244            SpaceOrNewline.fmt(f)
245        }
246
247        if !f.alternate() {
248            space_or_nl(f, &mut is_first)?;
249            f.write_str("SELECT")?;
250            if let Some(ref distinct) = self.distinct {
251                f.write_str(" ")?;
252                distinct.fmt(f)?;
253            }
254            if !self.projection.is_empty() {
255                indented_list(f, &self.projection)?;
256            }
257        }
258
259        if !self.from.is_empty() {
260            space_or_nl(f, &mut is_first)?;
261            f.write_str("FROM")?;
262            indented_list(f, &self.from)?;
263        }
264        if let Some(ref selection) = self.selection {
265            space_or_nl(f, &mut is_first)?;
266            f.write_str("WHERE ")?;
267            selection.fmt(f)?;
268        }
269
270        if !self.group_by.is_empty() {
271            space_or_nl(f, &mut is_first)?;
272            f.write_str("GROUP BY ")?;
273            display_comma_separated(&self.group_by).fmt(f)?;
274        }
275
276        if f.alternate() {
277            space_or_nl(f, &mut is_first)?;
278            write!(f, "SELECT")?;
279            if let Some(ref distinct) = self.distinct {
280                f.write_str(" ")?;
281                distinct.fmt(f)?;
282            }
283            if !self.projection.is_empty() {
284                indented_list(f, &self.projection)?;
285            }
286        }
287        if let Some(ref having) = self.having {
288            space_or_nl(f, &mut is_first)?;
289            f.write_str("HAVING")?;
290            Indent(having).fmt(f)?;
291        }
292        Ok(())
293    }
294}
295
296/// A hive LATERAL VIEW with potential column aliases
297#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
298pub struct LateralView {
299    /// LATERAL VIEW
300    pub lateral_view: Expr,
301    /// LATERAL VIEW table name
302    pub lateral_view_name: ObjectName,
303    /// LATERAL VIEW optional column aliases
304    pub lateral_col_alias: Vec<Ident>,
305    /// LATERAL VIEW OUTER
306    pub outer: bool,
307}
308
309impl fmt::Display for LateralView {
310    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
311        write!(
312            f,
313            " LATERAL VIEW{outer} {} {}",
314            self.lateral_view,
315            self.lateral_view_name,
316            outer = if self.outer { " OUTER" } else { "" }
317        )?;
318        if !self.lateral_col_alias.is_empty() {
319            write!(
320                f,
321                " AS {}",
322                display_comma_separated(&self.lateral_col_alias)
323            )?;
324        }
325        Ok(())
326    }
327}
328
329#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
330pub struct With {
331    /// Token for the "WITH" keyword
332    pub recursive: bool,
333    pub cte_tables: Vec<Cte>,
334}
335
336impl fmt::Display for With {
337    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
338        f.write_str("WITH ")?;
339        if self.recursive {
340            f.write_str("RECURSIVE ")?;
341        }
342        display_comma_separated(&self.cte_tables).fmt(f)?;
343        Ok(())
344    }
345}
346
347#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
348pub enum CteAsMaterialized {
349    /// The `WITH` statement specifies `AS MATERIALIZED` behavior
350    Materialized,
351    /// The `WITH` statement specifies `AS NOT MATERIALIZED` behavior
352    NotMaterialized,
353}
354
355impl fmt::Display for CteAsMaterialized {
356    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
357        match *self {
358            CteAsMaterialized::Materialized => {
359                write!(f, "MATERIALIZED")?;
360            }
361            CteAsMaterialized::NotMaterialized => {
362                write!(f, "NOT MATERIALIZED")?;
363            }
364        };
365        Ok(())
366    }
367}
368
369/// A single CTE (used after `WITH`): `<alias> [(col1, col2, ...)] AS <materialized> ( <query> )`
370/// The names in the column list before `AS`, when specified, replace the names
371/// of the columns returned by the query. The parser does not validate that the
372/// number of columns in the query matches the number of columns in the query.
373#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
374pub struct Cte {
375    pub alias: TableAlias,
376    pub query: Box<Query>,
377    pub materialized: Option<CteAsMaterialized>,
378}
379
380impl fmt::Display for Cte {
381    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
382        self.alias.fmt(f)?;
383        f.write_str(" AS")?;
384        if let Some(materialized) = self.materialized.as_ref() {
385            f.write_str(" ")?;
386            materialized.fmt(f)?;
387        };
388        f.write_str(" (")?;
389        NewLine.fmt(f)?;
390        Indent(&self.query).fmt(f)?;
391        NewLine.fmt(f)?;
392        f.write_str(")")?;
393        Ok(())
394    }
395}
396
397/// One item of the comma-separated list following `SELECT`
398#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
399pub struct SelectItem {
400    pub expr: Expr,
401    pub alias: Option<Ident>,
402}
403
404impl SelectItem {
405    pub fn unnamed(expr: Expr) -> Self {
406        SelectItem { expr, alias: None }
407    }
408}
409
410impl fmt::Display for SelectItem {
411    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
412        if f.alternate()
413            && let Some(alias) = &self.alias
414        {
415            write!(f, "{:05} = ", &alias.value)?;
416        }
417
418        self.expr.fmt(f)?;
419
420        if !f.alternate()
421            && let Some(alias) = &self.alias
422        {
423            f.write_str(" AS ")?;
424            alias.fmt(f)?;
425        }
426        Ok(())
427    }
428}
429
430/// An expression optionally followed by an alias.
431///
432/// Example:
433/// ```sql
434/// 42 AS myint
435/// ```
436#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
437pub struct ExprWithAlias {
438    pub expr: Expr,
439    pub alias: Option<Ident>,
440}
441
442impl fmt::Display for ExprWithAlias {
443    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
444        let ExprWithAlias { expr, alias } = self;
445        write!(f, "{expr}")?;
446        if let Some(alias) = alias {
447            write!(f, " AS {alias}")?;
448        }
449        Ok(())
450    }
451}
452
453/// A table name or a parenthesized subquery with an optional alias
454#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
455pub struct RelNamed {
456    pub lateral: bool,
457    pub alias: Option<TableAlias>,
458    pub expr: RelExpr,
459}
460
461impl RelNamed {
462    pub fn unnamed(expr: RelExpr) -> Self {
463        Self {
464            expr,
465            alias: None,
466            lateral: false,
467        }
468    }
469}
470
471impl fmt::Display for RelNamed {
472    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
473        if self.lateral && !matches!(self.expr, RelExpr::Table(..)) {
474            write!(f, "LATERAL ")?;
475        }
476        if f.alternate()
477            && let Some(alias) = &self.alias
478        {
479            write!(f, "{alias} = ")?;
480        }
481        self.expr.fmt(f)?;
482        if !f.alternate()
483            && let Some(alias) = &self.alias
484        {
485            write!(f, " AS {alias}")?;
486        }
487        Ok(())
488    }
489}
490
491#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
492pub enum RelExpr {
493    Table(ObjectName),
494    Subquery(Box<Query>),
495    Function {
496        name: ObjectName,
497        args: Vec<Expr>,
498        ordinality: bool,
499    },
500}
501
502impl RelExpr {
503    pub fn unnamed(self) -> RelNamed {
504        RelNamed::unnamed(self)
505    }
506
507    pub fn alias(self, alias: Ident) -> RelNamed {
508        RelNamed {
509            expr: self,
510            alias: Some(TableAlias::simple(alias)),
511            lateral: false,
512        }
513    }
514
515    pub fn alias_cols(self, alias: Ident, cols: Vec<Ident>) -> RelNamed {
516        RelNamed {
517            expr: self,
518            alias: Some(TableAlias::new(alias, cols)),
519            lateral: false,
520        }
521    }
522
523    pub fn subquery(query: Query) -> Self {
524        RelExpr::Subquery(Box::new(query))
525    }
526
527    pub fn function(name: Ident, args: Vec<Expr>) -> Self {
528        RelExpr::Function {
529            name: ObjectName(vec![name]),
530            args: args.into_iter().collect(),
531            ordinality: false,
532        }
533    }
534}
535
536impl fmt::Display for RelExpr {
537    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
538        match self {
539            RelExpr::Table(name) => {
540                name.fmt(f)?;
541            }
542            RelExpr::Subquery(subquery) => {
543                f.write_str("(")?;
544                NewLine.fmt(f)?;
545                Indent(subquery).fmt(f)?;
546                NewLine.fmt(f)?;
547                f.write_str(")")?;
548            }
549            RelExpr::Function {
550                name,
551                args,
552                ordinality,
553            } => {
554                write!(f, "{name}({})", display_comma_separated(args))?;
555                if *ordinality {
556                    write!(f, " WITH ORDINALITY")?;
557                }
558            }
559        }
560        Ok(())
561    }
562}
563
564#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
565pub struct TableAlias {
566    pub name: Ident,
567    pub columns: Vec<Ident>,
568}
569
570impl TableAlias {
571    fn new(name: Ident, columns: Vec<Ident>) -> Self {
572        Self { name, columns }
573    }
574
575    pub fn simple(name: Ident) -> Self {
576        Self::new(name, vec![])
577    }
578}
579
580impl fmt::Display for TableAlias {
581    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
582        write!(f, "{}", self.name)?;
583        if !self.columns.is_empty() {
584            write!(f, " ({})", display_comma_separated(&self.columns))?;
585        }
586        Ok(())
587    }
588}
589
590#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
591pub enum TableVersion {
592    /// When the table version is defined using `FOR SYSTEM_TIME AS OF`.
593    /// For example: `SELECT * FROM tbl FOR SYSTEM_TIME AS OF TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR)`
594    ForSystemTimeAsOf(Expr),
595    /// When the table version is defined using a function.
596    /// For example: `SELECT * FROM tbl AT(TIMESTAMP => '2020-08-14 09:30:00')`
597    Function(Expr),
598}
599
600impl Display for TableVersion {
601    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
602        match self {
603            TableVersion::ForSystemTimeAsOf(e) => write!(f, "FOR SYSTEM_TIME AS OF {e}")?,
604            TableVersion::Function(func) => write!(f, "{func}")?,
605        }
606        Ok(())
607    }
608}
609
610#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
611pub struct Join {
612    pub relation: RelNamed,
613    pub join_operator: JoinOperator,
614}
615
616impl fmt::Display for Join {
617    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
618        fn prefix(constraint: &JoinConstraint) -> &'static str {
619            match constraint {
620                JoinConstraint::Natural => "NATURAL ",
621                _ => "",
622            }
623        }
624        fn suffix(constraint: &'_ JoinConstraint) -> impl fmt::Display + '_ {
625            struct Suffix<'a>(&'a JoinConstraint);
626            impl fmt::Display for Suffix<'_> {
627                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
628                    match self.0 {
629                        JoinConstraint::On(expr) => write!(f, " ON {expr}"),
630                        JoinConstraint::Using(attrs) => {
631                            write!(f, " USING({})", display_comma_separated(attrs))
632                        }
633                        _ => Ok(()),
634                    }
635                }
636            }
637            Suffix(constraint)
638        }
639
640        match &self.join_operator {
641            JoinOperator::Join(constraint) => f.write_fmt(format_args!(
642                "{}JOIN {}{}",
643                prefix(constraint),
644                self.relation,
645                suffix(constraint)
646            )),
647            JoinOperator::Inner(constraint) => f.write_fmt(format_args!(
648                "{}INNER JOIN {}{}",
649                prefix(constraint),
650                self.relation,
651                suffix(constraint)
652            )),
653            JoinOperator::Left(constraint) => f.write_fmt(format_args!(
654                "{}LEFT JOIN {}{}",
655                prefix(constraint),
656                self.relation,
657                suffix(constraint)
658            )),
659            JoinOperator::LeftOuter(constraint) => f.write_fmt(format_args!(
660                "{}LEFT OUTER JOIN {}{}",
661                prefix(constraint),
662                self.relation,
663                suffix(constraint)
664            )),
665            JoinOperator::Right(constraint) => f.write_fmt(format_args!(
666                "{}RIGHT JOIN {}{}",
667                prefix(constraint),
668                self.relation,
669                suffix(constraint)
670            )),
671            JoinOperator::RightOuter(constraint) => f.write_fmt(format_args!(
672                "{}RIGHT OUTER JOIN {}{}",
673                prefix(constraint),
674                self.relation,
675                suffix(constraint)
676            )),
677            JoinOperator::FullOuter(constraint) => f.write_fmt(format_args!(
678                "{}FULL JOIN {}{}",
679                prefix(constraint),
680                self.relation,
681                suffix(constraint)
682            )),
683            JoinOperator::CrossJoin => f.write_fmt(format_args!("CROSS JOIN {}", self.relation)),
684            JoinOperator::Semi(constraint) => f.write_fmt(format_args!(
685                "{}SEMI JOIN {}{}",
686                prefix(constraint),
687                self.relation,
688                suffix(constraint)
689            )),
690            JoinOperator::LeftSemi(constraint) => f.write_fmt(format_args!(
691                "{}LEFT SEMI JOIN {}{}",
692                prefix(constraint),
693                self.relation,
694                suffix(constraint)
695            )),
696            JoinOperator::RightSemi(constraint) => f.write_fmt(format_args!(
697                "{}RIGHT SEMI JOIN {}{}",
698                prefix(constraint),
699                self.relation,
700                suffix(constraint)
701            )),
702            JoinOperator::Anti(constraint) => f.write_fmt(format_args!(
703                "{}ANTI JOIN {}{}",
704                prefix(constraint),
705                self.relation,
706                suffix(constraint)
707            )),
708            JoinOperator::LeftAnti(constraint) => f.write_fmt(format_args!(
709                "{}LEFT ANTI JOIN {}{}",
710                prefix(constraint),
711                self.relation,
712                suffix(constraint)
713            )),
714            JoinOperator::RightAnti(constraint) => f.write_fmt(format_args!(
715                "{}RIGHT ANTI JOIN {}{}",
716                prefix(constraint),
717                self.relation,
718                suffix(constraint)
719            )),
720            JoinOperator::CrossApply => f.write_fmt(format_args!("CROSS APPLY {}", self.relation)),
721            JoinOperator::OuterApply => f.write_fmt(format_args!("OUTER APPLY {}", self.relation)),
722            JoinOperator::AsOf {
723                match_condition,
724                constraint,
725            } => f.write_fmt(format_args!(
726                "ASOF JOIN {} MATCH_CONDITION ({match_condition}){}",
727                self.relation,
728                suffix(constraint)
729            )),
730            JoinOperator::StraightJoin(constraint) => f.write_fmt(format_args!(
731                "STRAIGHT_JOIN {}{}",
732                self.relation,
733                suffix(constraint)
734            )),
735        }
736    }
737}
738
739#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
740pub enum JoinOperator {
741    Join(JoinConstraint),
742    Inner(JoinConstraint),
743    Left(JoinConstraint),
744    LeftOuter(JoinConstraint),
745    Right(JoinConstraint),
746    RightOuter(JoinConstraint),
747    FullOuter(JoinConstraint),
748    CrossJoin,
749    /// SEMI (non-standard)
750    Semi(JoinConstraint),
751    /// LEFT SEMI (non-standard)
752    LeftSemi(JoinConstraint),
753    /// RIGHT SEMI (non-standard)
754    RightSemi(JoinConstraint),
755    /// ANTI (non-standard)
756    Anti(JoinConstraint),
757    /// LEFT ANTI (non-standard)
758    LeftAnti(JoinConstraint),
759    /// RIGHT ANTI (non-standard)
760    RightAnti(JoinConstraint),
761    /// CROSS APPLY (non-standard)
762    CrossApply,
763    /// OUTER APPLY (non-standard)
764    OuterApply,
765    /// `ASOF` joins are used for joining tables containing time-series data
766    /// whose timestamp columns do not match exactly.
767    ///
768    /// See <https://docs.snowflake.com/en/sql-reference/constructs/asof-join>.
769    AsOf {
770        match_condition: Expr,
771        constraint: JoinConstraint,
772    },
773    /// STRAIGHT_JOIN (non-standard)
774    ///
775    /// See <https://dev.mysql.com/doc/refman/8.4/en/join.html>.
776    StraightJoin(JoinConstraint),
777}
778
779#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
780pub enum JoinConstraint {
781    On(Expr),
782    Using(Vec<ObjectName>),
783    Natural,
784    None,
785}
786
787#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
788pub enum OrderByKind {
789    /// ALL syntax of [DuckDB] and [ClickHouse].
790    ///
791    /// [DuckDB]:  <https://duckdb.org/docs/sql/query_syntax/orderby>
792    /// [ClickHouse]: <https://clickhouse.com/docs/en/sql-reference/statements/select/order-by>
793    All(OrderByOptions),
794
795    /// Expressions
796    Expressions(Vec<OrderByExpr>),
797}
798
799#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
800pub struct OrderBy {
801    pub exprs: Vec<OrderByExpr>,
802}
803
804impl fmt::Display for OrderBy {
805    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
806        write!(f, "ORDER BY")?;
807        write!(f, " {}", display_comma_separated(&self.exprs))?;
808
809        Ok(())
810    }
811}
812
813/// An `ORDER BY` expression
814#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
815pub struct OrderByExpr {
816    pub expr: Expr,
817    pub options: OrderByOptions,
818}
819
820impl fmt::Display for OrderByExpr {
821    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
822        write!(f, "{}{}", self.expr, self.options)?;
823        Ok(())
824    }
825}
826
827#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
828pub struct OrderByOptions {
829    /// Optional `ASC` or `DESC`
830    pub asc: Option<bool>,
831    /// Optional `NULLS FIRST` or `NULLS LAST`
832    pub nulls_first: Option<bool>,
833}
834
835impl fmt::Display for OrderByOptions {
836    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
837        match self.asc {
838            Some(true) => write!(f, " ASC")?,
839            Some(false) => write!(f, " DESC")?,
840            None => (),
841        }
842        match self.nulls_first {
843            Some(true) => write!(f, " NULLS FIRST")?,
844            Some(false) => write!(f, " NULLS LAST")?,
845            None => (),
846        }
847        Ok(())
848    }
849}
850
851#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
852pub enum Distinct {
853    /// DISTINCT
854    Distinct,
855
856    /// DISTINCT ON({column names})
857    On(Vec<Expr>),
858}
859
860impl fmt::Display for Distinct {
861    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
862        match self {
863            Distinct::Distinct => write!(f, "DISTINCT"),
864            Distinct::On(col_names) => {
865                let col_names = display_comma_separated(col_names);
866                write!(f, "DISTINCT ON ({col_names})")
867            }
868        }
869    }
870}
871
872#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
873pub struct Values {
874    /// Was there an explicit ROWs keyword (MySQL)?
875    /// <https://dev.mysql.com/doc/refman/8.0/en/values.html>
876    pub explicit_row: bool,
877    pub rows: Vec<Vec<Expr>>,
878}
879
880impl fmt::Display for Values {
881    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
882        f.write_str("VALUES")?;
883        let prefix = if self.explicit_row { "ROW" } else { "" };
884        let mut delim = "";
885        for row in &self.rows {
886            f.write_str(delim)?;
887            delim = ",";
888            SpaceOrNewline.fmt(f)?;
889            Indent(format_args!("{prefix}({})", display_comma_separated(row))).fmt(f)?;
890        }
891        Ok(())
892    }
893}
894
895#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
896pub struct SelectInto {
897    pub temporary: bool,
898    pub unlogged: bool,
899    pub table: bool,
900    pub name: ObjectName,
901}
902
903impl fmt::Display for SelectInto {
904    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
905        let temporary = if self.temporary { " TEMPORARY" } else { "" };
906        let unlogged = if self.unlogged { " UNLOGGED" } else { "" };
907        let table = if self.table { " TABLE" } else { "" };
908
909        write!(f, "INTO{}{}{} {}", temporary, unlogged, table, self.name)
910    }
911}