risingwave_sqlparser/ast/
query.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5//     http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12
13use crate::ast::*;
14
15/// The most complete variant of a `SELECT` query expression, optionally
16/// including `WITH`, `UNION` / other set operations, and `ORDER BY`.
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18pub struct Query {
19    /// WITH (common table expressions, or CTEs)
20    pub with: Option<With>,
21    /// SELECT or UNION / EXCEPT / INTERSECT
22    pub body: SetExpr,
23    /// ORDER BY
24    pub order_by: Vec<OrderByExpr>,
25    /// `LIMIT { <N> | ALL }`
26    pub limit: Option<Expr>,
27    /// `OFFSET <N> [ { ROW | ROWS } ]`
28    ///
29    /// `ROW` and `ROWS` are noise words that don't influence the effect of the clause.
30    /// They are provided for ANSI compatibility.
31    pub offset: Option<String>,
32    /// `FETCH { FIRST | NEXT } <N> [ PERCENT ] { ROW | ROWS } | { ONLY | WITH TIES }`
33    ///
34    /// `ROW` and `ROWS` as well as `FIRST` and `NEXT` are noise words that don't influence the
35    /// effect of the clause. They are provided for ANSI compatibility.
36    pub fetch: Option<Fetch>,
37}
38
39impl Query {
40    /// Simple `VALUES` without other clauses.
41    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    /// `SELECT <expr>` without other clauses.
56    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/// A node in a tree, representing a "query body" expression, roughly:
117/// `SELECT ... [ {UNION|EXCEPT|INTERSECT} SELECT ...]`
118#[allow(clippy::large_enum_variant)]
119#[derive(Debug, Clone, PartialEq, Eq, Hash)]
120pub enum SetExpr {
121    /// Restricted SELECT .. FROM .. HAVING (no ORDER BY or set operations)
122    Select(Box<Select>),
123    /// Parenthesized SELECT subquery, which may include more set operations
124    /// in its body and an optional ORDER BY / LIMIT.
125    Query(Box<Query>),
126    /// UNION/EXCEPT/INTERSECT of two queries
127    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/// `CORRESPONDING [ BY <left paren> <corresponding column list> <right paren> ]`
175#[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/// A restricted variant of `SELECT` (without CTEs/`ORDER BY`), which may
218/// appear either as the only body item of an `SQLQuery`, or as an operand
219/// to a set operation like `UNION`.
220#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
221pub struct Select {
222    pub distinct: Distinct,
223    /// projection expressions
224    pub projection: Vec<SelectItem>,
225    /// FROM
226    pub from: Vec<TableWithJoins>,
227    /// LATERAL VIEWs
228    pub lateral_views: Vec<LateralView>,
229    /// WHERE
230    pub selection: Option<Expr>,
231    /// GROUP BY
232    pub group_by: Vec<Expr>,
233    /// HAVING
234    pub having: Option<Expr>,
235    /// WINDOW
236    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/// An `ALL`, `DISTINCT` or `DISTINCT ON (expr, ...)` after `SELECT`.
268#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
269#[expect(clippy::enum_variant_names)]
270pub enum Distinct {
271    /// An optional parameter that returns all matching rows.
272    #[default]
273    All,
274    /// A parameter that removes duplicates from the result-set.
275    Distinct,
276    /// An optional parameter that eliminates duplicate data based on the expressions.
277    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/// A hive LATERAL VIEW with potential column aliases
303#[derive(Debug, Clone, PartialEq, Eq, Hash)]
304pub struct LateralView {
305    /// LATERAL VIEW
306    pub lateral_view: Expr,
307    /// LATERAL VIEW table name
308    pub lateral_view_name: ObjectName,
309    /// LATERAL VIEW optional column aliases
310    pub lateral_col_alias: Vec<Ident>,
311    /// LATERAL VIEW OUTER
312    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/// A single CTE (used after `WITH`): `alias [(col1, col2, ...)] AS ( query )`
353///
354/// The names in the column list before `AS`, when specified, replace the names
355/// of the columns returned by the query. The parser does not validate that the
356/// number of columns in the query matches the number of columns in the query.
357#[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/// One item of the comma-separated list following `SELECT`
382#[derive(Debug, Clone, PartialEq, Eq, Hash)]
383pub enum SelectItem {
384    /// Any expression, not followed by `[ AS ] alias`
385    UnnamedExpr(Expr),
386    /// Expr is an arbitrary expression, returning either a table or a column.
387    /// Idents are the prefix of `*`, which are consecutive field accesses.
388    /// e.g. `(table.v1).*` or `(table).v1.*`
389    ExprQualifiedWildcard(Expr, Vec<Ident>),
390    /// An expression, followed by `[ AS ] alias`
391    ExprWithAlias { expr: Expr, alias: Ident },
392    /// `alias.*` or even `schema.table.*` followed by optional except
393    QualifiedWildcard(ObjectName, Option<Vec<Expr>>),
394    /// An unqualified `*`, or `* except (exprs)`
395    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/// A table name or a parenthesized subquery with an optional alias
457#[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    /// `<expr>(args)[ AS <alias> ]`
470    ///
471    /// Note that scalar functions can also be used in this way.
472    TableFunction {
473        name: ObjectName,
474        alias: Option<TableAlias>,
475        args: Vec<FunctionArg>,
476        with_ordinality: bool,
477    },
478    /// Represents a parenthesized table factor. The SQL spec only allows a
479    /// join expression (`(foo <JOIN> bar [ <JOIN> baz ... ])`) to be nested,
480    /// possibly several times.
481    ///
482    /// The parser may also accept non-standard nesting of bare tables for some
483    /// dialects, but the information about such nesting is stripped from AST.
484    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/// An `ORDER BY` expression
647#[derive(Debug, Clone, PartialEq, Eq, Hash)]
648pub struct OrderByExpr {
649    pub expr: Expr,
650    /// Optional `ASC` or `DESC`
651    pub asc: Option<bool>,
652    /// Optional `NULLS FIRST` or `NULLS LAST`
653    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    /// SQL semantic equivalent of LIMIT but with same structure as FETCH.
693    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/// A named window definition in the WINDOW clause
727#[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}