mz_sql_pretty/
doc.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Functions that convert SQL AST nodes to pretty Docs.
11
12use itertools::Itertools;
13use mz_sql_parser::ast::display::AstDisplay;
14use mz_sql_parser::ast::*;
15use pretty::{Doc, RcDoc};
16
17use crate::util::{
18    bracket, bracket_doc, comma_separate, comma_separated, intersperse_line_nest, nest,
19    nest_comma_separate, nest_title, title_comma_separate,
20};
21use crate::{Pretty, TAB};
22
23impl Pretty {
24    // Use when we don't know what to do.
25    pub(crate) fn doc_display<'a, T: AstDisplay>(&self, v: &T, _debug: &str) -> RcDoc<'a, ()> {
26        #[cfg(test)]
27        eprintln!(
28            "UNKNOWN PRETTY TYPE in {}: {}, {}",
29            _debug,
30            std::any::type_name::<T>(),
31            v.to_ast_string_simple()
32        );
33        self.doc_display_pass(v)
34    }
35
36    // Use when the AstDisplay trait is what we want.
37    fn doc_display_pass<'a, T: AstDisplay>(&self, v: &T) -> RcDoc<'a, ()> {
38        RcDoc::text(v.to_ast_string(self.config.format_mode))
39    }
40
41    pub(crate) fn doc_create_source<'a, T: AstInfo>(
42        &'a self,
43        v: &'a CreateSourceStatement<T>,
44    ) -> RcDoc<'a> {
45        let mut docs = Vec::new();
46        let title = format!(
47            "CREATE SOURCE{}",
48            if v.if_not_exists {
49                " IF NOT EXISTS"
50            } else {
51                ""
52            }
53        );
54        let mut doc = self.doc_display_pass(&v.name);
55        let mut names = Vec::new();
56        names.extend(v.col_names.iter().map(|name| self.doc_display_pass(name)));
57        names.extend(v.key_constraint.iter().map(|kc| self.doc_display_pass(kc)));
58        if !names.is_empty() {
59            doc = nest(doc, bracket("(", comma_separated(names), ")"));
60        }
61        docs.push(nest_title(title, doc));
62        if let Some(cluster) = &v.in_cluster {
63            docs.push(nest_title("IN CLUSTER", self.doc_display_pass(cluster)));
64        }
65        docs.push(nest_title("FROM", self.doc_display_pass(&v.connection)));
66        if let Some(format) = &v.format {
67            docs.push(self.doc_format_specifier(format));
68        }
69        if !v.include_metadata.is_empty() {
70            docs.push(nest_title(
71                "INCLUDE",
72                comma_separate(|im| self.doc_display_pass(im), &v.include_metadata),
73            ));
74        }
75        if let Some(envelope) = &v.envelope {
76            docs.push(nest_title("ENVELOPE", self.doc_display_pass(envelope)));
77        }
78        if let Some(references) = &v.external_references {
79            docs.push(self.doc_external_references(references));
80        }
81        if let Some(progress) = &v.progress_subsource {
82            docs.push(nest_title(
83                "EXPOSE PROGRESS AS",
84                self.doc_display_pass(progress),
85            ));
86        }
87        if !v.with_options.is_empty() {
88            docs.push(bracket(
89                "WITH (",
90                comma_separate(|wo| self.doc_display_pass(wo), &v.with_options),
91                ")",
92            ));
93        }
94        RcDoc::intersperse(docs, Doc::line()).group()
95    }
96
97    fn doc_format_specifier<T: AstInfo>(&self, v: &FormatSpecifier<T>) -> RcDoc<'_> {
98        match v {
99            FormatSpecifier::Bare(format) => nest_title("FORMAT", self.doc_display_pass(format)),
100            FormatSpecifier::KeyValue { key, value } => {
101                let docs = vec![
102                    nest_title("KEY FORMAT", self.doc_display_pass(key)),
103                    nest_title("VALUE FORMAT", self.doc_display_pass(value)),
104                ];
105                RcDoc::intersperse(docs, Doc::line()).group()
106            }
107        }
108    }
109
110    fn doc_external_references<'a>(&'a self, v: &'a ExternalReferences) -> RcDoc<'a> {
111        match v {
112            ExternalReferences::SubsetTables(subsources) => bracket(
113                "FOR TABLES (",
114                comma_separate(|s| self.doc_display_pass(s), subsources),
115                ")",
116            ),
117            ExternalReferences::SubsetSchemas(schemas) => bracket(
118                "FOR SCHEMAS (",
119                comma_separate(|s| self.doc_display_pass(s), schemas),
120                ")",
121            ),
122            ExternalReferences::All => RcDoc::text("FOR ALL TABLES"),
123        }
124    }
125
126    pub(crate) fn doc_copy<'a, T: AstInfo>(&'a self, v: &'a CopyStatement<T>) -> RcDoc<'a> {
127        let relation = match &v.relation {
128            CopyRelation::Named { name, columns } => {
129                let mut relation = self.doc_display_pass(name);
130                if !columns.is_empty() {
131                    relation = bracket_doc(
132                        nest(relation, RcDoc::text("(")),
133                        comma_separate(|c| self.doc_display_pass(c), columns),
134                        RcDoc::text(")"),
135                        RcDoc::line_(),
136                    );
137                }
138                RcDoc::concat([RcDoc::text("COPY "), relation])
139            }
140            CopyRelation::Select(query) => bracket("COPY (", self.doc_select_statement(query), ")"),
141            CopyRelation::Subscribe(query) => bracket("COPY (", self.doc_subscribe(query), ")"),
142        };
143        let mut docs = vec![
144            relation,
145            RcDoc::concat([
146                self.doc_display_pass(&v.direction),
147                RcDoc::text(" "),
148                self.doc_display_pass(&v.target),
149            ]),
150        ];
151        if !v.options.is_empty() {
152            docs.push(bracket(
153                "WITH (",
154                comma_separate(|o| self.doc_display_pass(o), &v.options),
155                ")",
156            ));
157        }
158        RcDoc::intersperse(docs, Doc::line()).group()
159    }
160
161    pub(crate) fn doc_subscribe<'a, T: AstInfo>(
162        &'a self,
163        v: &'a SubscribeStatement<T>,
164    ) -> RcDoc<'a> {
165        let doc = match &v.relation {
166            SubscribeRelation::Name(name) => nest_title("SUBSCRIBE", self.doc_display_pass(name)),
167            SubscribeRelation::Query(query) => bracket("SUBSCRIBE (", self.doc_query(query), ")"),
168        };
169        let mut docs = vec![doc];
170        if !v.options.is_empty() {
171            docs.push(bracket(
172                "WITH (",
173                comma_separate(|o| self.doc_display_pass(o), &v.options),
174                ")",
175            ));
176        }
177        if let Some(as_of) = &v.as_of {
178            docs.push(self.doc_as_of(as_of));
179        }
180        if let Some(up_to) = &v.up_to {
181            docs.push(nest_title("UP TO", self.doc_expr(up_to)));
182        }
183        match &v.output {
184            SubscribeOutput::Diffs => {}
185            SubscribeOutput::WithinTimestampOrderBy { order_by } => {
186                docs.push(nest_title(
187                    "WITHIN TIMESTAMP ORDER BY ",
188                    comma_separate(|o| self.doc_order_by_expr(o), order_by),
189                ));
190            }
191            SubscribeOutput::EnvelopeUpsert { key_columns } => {
192                docs.push(bracket(
193                    "ENVELOPE UPSERT (KEY (",
194                    comma_separate(|kc| self.doc_display_pass(kc), key_columns),
195                    "))",
196                ));
197            }
198            SubscribeOutput::EnvelopeDebezium { key_columns } => {
199                docs.push(bracket(
200                    "ENVELOPE DEBEZIUM (KEY (",
201                    comma_separate(|kc| self.doc_display_pass(kc), key_columns),
202                    "))",
203                ));
204            }
205        }
206        RcDoc::intersperse(docs, Doc::line()).group()
207    }
208
209    fn doc_as_of<'a, T: AstInfo>(&'a self, v: &'a AsOf<T>) -> RcDoc<'a> {
210        let (title, expr) = match v {
211            AsOf::At(expr) => ("AS OF", expr),
212            AsOf::AtLeast(expr) => ("AS OF AT LEAST", expr),
213        };
214        nest_title(title, self.doc_expr(expr))
215    }
216
217    pub(crate) fn doc_create_view<'a, T: AstInfo>(
218        &'a self,
219        v: &'a CreateViewStatement<T>,
220    ) -> RcDoc<'a> {
221        let mut docs = vec![];
222        docs.push(RcDoc::text(format!(
223            "CREATE{}{} VIEW{}",
224            if v.if_exists == IfExistsBehavior::Replace {
225                " OR REPLACE"
226            } else {
227                ""
228            },
229            if v.temporary { " TEMPORARY" } else { "" },
230            if v.if_exists == IfExistsBehavior::Skip {
231                " IF NOT EXISTS"
232            } else {
233                ""
234            },
235        )));
236        docs.push(self.doc_view_definition(&v.definition));
237        intersperse_line_nest(docs)
238    }
239
240    pub(crate) fn doc_create_materialized_view<'a, T: AstInfo>(
241        &'a self,
242        v: &'a CreateMaterializedViewStatement<T>,
243    ) -> RcDoc<'a> {
244        let mut docs = vec![];
245        docs.push(RcDoc::text(format!(
246            "CREATE{} MATERIALIZED VIEW{} {}",
247            if v.if_exists == IfExistsBehavior::Replace {
248                " OR REPLACE"
249            } else {
250                ""
251            },
252            if v.if_exists == IfExistsBehavior::Skip {
253                " IF NOT EXISTS"
254            } else {
255                ""
256            },
257            v.name,
258        )));
259        if !v.columns.is_empty() {
260            docs.push(bracket(
261                "(",
262                comma_separate(|c| self.doc_display_pass(c), &v.columns),
263                ")",
264            ));
265        }
266        if let Some(target) = &v.replacing {
267            docs.push(RcDoc::text(format!(
268                "REPLACING {}",
269                target.to_ast_string_simple()
270            )));
271        }
272        if let Some(cluster) = &v.in_cluster {
273            docs.push(RcDoc::text(format!(
274                "IN CLUSTER {}",
275                cluster.to_ast_string_simple()
276            )));
277        }
278        if !v.with_options.is_empty() {
279            docs.push(bracket(
280                "WITH (",
281                comma_separate(|wo| self.doc_display_pass(wo), &v.with_options),
282                ")",
283            ));
284        }
285        docs.push(nest_title("AS", self.doc_query(&v.query)));
286        intersperse_line_nest(docs)
287    }
288
289    fn doc_view_definition<'a, T: AstInfo>(&'a self, v: &'a ViewDefinition<T>) -> RcDoc<'a> {
290        let mut docs = vec![RcDoc::text(v.name.to_string())];
291        if !v.columns.is_empty() {
292            docs.push(bracket(
293                "(",
294                comma_separate(|c| self.doc_display_pass(c), &v.columns),
295                ")",
296            ));
297        }
298        docs.push(nest_title("AS", self.doc_query(&v.query)));
299        RcDoc::intersperse(docs, Doc::line()).group()
300    }
301
302    pub(crate) fn doc_insert<'a, T: AstInfo>(&'a self, v: &'a InsertStatement<T>) -> RcDoc<'a> {
303        let mut first = vec![RcDoc::text(format!(
304            "INSERT INTO {}",
305            v.table_name.to_ast_string_simple()
306        ))];
307        if !v.columns.is_empty() {
308            first.push(bracket(
309                "(",
310                comma_separate(|c| self.doc_display_pass(c), &v.columns),
311                ")",
312            ));
313        }
314        let sources = match &v.source {
315            InsertSource::Query(query) => self.doc_query(query),
316            _ => self.doc_display(&v.source, "insert source"),
317        };
318        let mut doc = intersperse_line_nest([intersperse_line_nest(first), sources]);
319        if !v.returning.is_empty() {
320            doc = nest(
321                doc,
322                nest_title(
323                    "RETURNING",
324                    comma_separate(|r| self.doc_display_pass(r), &v.returning),
325                ),
326            )
327        }
328        doc
329    }
330
331    pub(crate) fn doc_select_statement<'a, T: AstInfo>(
332        &'a self,
333        v: &'a SelectStatement<T>,
334    ) -> RcDoc<'a> {
335        let mut doc = self.doc_query(&v.query);
336        if let Some(as_of) = &v.as_of {
337            doc = intersperse_line_nest([doc, self.doc_as_of(as_of)]);
338        }
339        doc.group()
340    }
341
342    fn doc_order_by<'a, T: AstInfo>(&'a self, v: &'a [OrderByExpr<T>]) -> RcDoc<'a> {
343        title_comma_separate("ORDER BY", |o| self.doc_order_by_expr(o), v)
344    }
345
346    fn doc_order_by_expr<'a, T: AstInfo>(&'a self, v: &'a OrderByExpr<T>) -> RcDoc<'a> {
347        let doc = self.doc_expr(&v.expr);
348        let doc = match v.asc {
349            Some(true) => nest(doc, RcDoc::text("ASC")),
350            Some(false) => nest(doc, RcDoc::text("DESC")),
351            None => doc,
352        };
353        match v.nulls_last {
354            Some(true) => nest(doc, RcDoc::text("NULLS LAST")),
355            Some(false) => nest(doc, RcDoc::text("NULLS FIRST")),
356            None => doc,
357        }
358    }
359
360    fn doc_query<'a, T: AstInfo>(&'a self, v: &'a Query<T>) -> RcDoc<'a> {
361        let mut docs = vec![];
362        if !v.ctes.is_empty() {
363            match &v.ctes {
364                CteBlock::Simple(ctes) => {
365                    docs.push(title_comma_separate("WITH", |cte| self.doc_cte(cte), ctes))
366                }
367                CteBlock::MutuallyRecursive(mutrec) => {
368                    let mut doc = RcDoc::text("WITH MUTUALLY RECURSIVE");
369                    if !mutrec.options.is_empty() {
370                        doc = nest(
371                            doc,
372                            bracket(
373                                "(",
374                                comma_separate(|o| self.doc_display_pass(o), &mutrec.options),
375                                ")",
376                            ),
377                        );
378                    }
379                    docs.push(nest(
380                        doc,
381                        comma_separate(|c| self.doc_mutually_recursive(c), &mutrec.ctes),
382                    ));
383                }
384            }
385        }
386        docs.push(self.doc_set_expr(&v.body));
387        if !v.order_by.is_empty() {
388            docs.push(self.doc_order_by(&v.order_by));
389        }
390
391        let offset = if let Some(offset) = &v.offset {
392            vec![RcDoc::concat([nest_title("OFFSET", self.doc_expr(offset))])]
393        } else {
394            vec![]
395        };
396
397        if let Some(limit) = &v.limit {
398            if limit.with_ties {
399                docs.extend(offset);
400                docs.push(RcDoc::concat([
401                    RcDoc::text("FETCH FIRST "),
402                    self.doc_expr(&limit.quantity),
403                    RcDoc::text(" ROWS WITH TIES"),
404                ]));
405            } else {
406                docs.push(nest_title("LIMIT", self.doc_expr(&limit.quantity)));
407                docs.extend(offset);
408            }
409        } else {
410            docs.extend(offset);
411        }
412
413        RcDoc::intersperse(docs, Doc::line()).group()
414    }
415
416    fn doc_cte<'a, T: AstInfo>(&'a self, v: &'a Cte<T>) -> RcDoc<'a> {
417        RcDoc::concat([
418            RcDoc::text(format!("{} AS", v.alias)),
419            RcDoc::line(),
420            bracket("(", self.doc_query(&v.query), ")"),
421        ])
422    }
423
424    fn doc_mutually_recursive<'a, T: AstInfo>(&'a self, v: &'a CteMutRec<T>) -> RcDoc<'a> {
425        let mut docs = Vec::new();
426        if !v.columns.is_empty() {
427            docs.push(bracket(
428                "(",
429                comma_separate(|c| self.doc_display_pass(c), &v.columns),
430                ")",
431            ));
432        }
433        docs.push(bracket("AS (", self.doc_query(&v.query), ")"));
434        nest(
435            self.doc_display_pass(&v.name),
436            RcDoc::intersperse(docs, Doc::line()).group(),
437        )
438    }
439
440    fn doc_set_expr<'a, T: AstInfo>(&'a self, v: &'a SetExpr<T>) -> RcDoc<'a> {
441        match v {
442            SetExpr::Select(v) => self.doc_select(v),
443            SetExpr::Query(v) => bracket("(", self.doc_query(v), ")"),
444            SetExpr::SetOperation {
445                op,
446                all,
447                left,
448                right,
449            } => {
450                let all_str = if *all { " ALL" } else { "" };
451                RcDoc::concat([
452                    self.doc_set_expr(left),
453                    RcDoc::line(),
454                    RcDoc::concat([
455                        RcDoc::text(format!("{}{}", op, all_str)),
456                        RcDoc::line(),
457                        self.doc_set_expr(right),
458                    ])
459                    .nest(TAB)
460                    .group(),
461                ])
462            }
463            SetExpr::Values(v) => self.doc_values(v),
464            SetExpr::Show(v) => self.doc_display(v, "SHOW"),
465            SetExpr::Table(v) => nest(RcDoc::text("TABLE"), self.doc_display_pass(v)),
466        }
467        .group()
468    }
469
470    fn doc_values<'a, T: AstInfo>(&'a self, v: &'a Values<T>) -> RcDoc<'a> {
471        let rows =
472            v.0.iter()
473                .map(|row| bracket("(", comma_separate(|v| self.doc_expr(v), row), ")"));
474        RcDoc::concat([RcDoc::text("VALUES"), RcDoc::line(), comma_separated(rows)])
475            .nest(TAB)
476            .group()
477    }
478
479    fn doc_table_with_joins<'a, T: AstInfo>(&'a self, v: &'a TableWithJoins<T>) -> RcDoc<'a> {
480        let mut docs = vec![self.doc_table_factor(&v.relation)];
481        for j in &v.joins {
482            docs.push(self.doc_join(j));
483        }
484        intersperse_line_nest(docs)
485    }
486
487    fn doc_join<'a, T: AstInfo>(&'a self, v: &'a Join<T>) -> RcDoc<'a> {
488        let (constraint, name) = match &v.join_operator {
489            JoinOperator::Inner(constraint) => (constraint, "JOIN"),
490            JoinOperator::FullOuter(constraint) => (constraint, "FULL JOIN"),
491            JoinOperator::LeftOuter(constraint) => (constraint, "LEFT JOIN"),
492            JoinOperator::RightOuter(constraint) => (constraint, "RIGHT JOIN"),
493            _ => return self.doc_display(v, "join operator"),
494        };
495        let constraint = match constraint {
496            JoinConstraint::On(expr) => nest_title("ON", self.doc_expr(expr)),
497            JoinConstraint::Using { columns, alias } => {
498                let mut doc = bracket(
499                    "USING(",
500                    comma_separate(|c| self.doc_display_pass(c), columns),
501                    ")",
502                );
503                if let Some(alias) = alias {
504                    doc = nest(doc, nest_title("AS", self.doc_display_pass(alias)));
505                }
506                doc
507            }
508            _ => return self.doc_display(v, "join constraint"),
509        };
510        intersperse_line_nest([
511            RcDoc::text(name),
512            self.doc_table_factor(&v.relation),
513            constraint,
514        ])
515    }
516
517    fn doc_table_factor<'a, T: AstInfo>(&'a self, v: &'a TableFactor<T>) -> RcDoc<'a> {
518        match v {
519            TableFactor::Derived {
520                lateral,
521                subquery,
522                alias,
523            } => {
524                let prefix = if *lateral { "LATERAL (" } else { "(" };
525                let mut docs = vec![bracket(prefix, self.doc_query(subquery), ")")];
526                if let Some(alias) = alias {
527                    docs.push(RcDoc::text(format!("AS {}", alias)));
528                }
529                intersperse_line_nest(docs)
530            }
531            TableFactor::NestedJoin { join, alias } => {
532                let mut doc = bracket("(", self.doc_table_with_joins(join), ")");
533                if let Some(alias) = alias {
534                    doc = nest(doc, RcDoc::text(format!("AS {}", alias)));
535                }
536                doc
537            }
538            TableFactor::Table { name, alias } => {
539                let mut doc = self.doc_display_pass(name);
540                if let Some(alias) = alias {
541                    doc = nest(doc, RcDoc::text(format!("AS {}", alias)));
542                }
543                doc
544            }
545            _ => self.doc_display(v, "table factor variant"),
546        }
547    }
548
549    fn doc_distinct<'a, T: AstInfo>(&'a self, v: &'a Distinct<T>) -> RcDoc<'a> {
550        match v {
551            Distinct::EntireRow => RcDoc::text("DISTINCT"),
552            Distinct::On(cols) => bracket(
553                "DISTINCT ON (",
554                comma_separate(|c| self.doc_expr(c), cols),
555                ")",
556            ),
557        }
558    }
559
560    fn doc_select<'a, T: AstInfo>(&'a self, v: &'a Select<T>) -> RcDoc<'a> {
561        let mut docs = vec![];
562        let mut select = RcDoc::text("SELECT");
563        if let Some(distinct) = &v.distinct {
564            select = nest(select, self.doc_distinct(distinct));
565        }
566        docs.push(nest_comma_separate(
567            select,
568            |s| self.doc_select_item(s),
569            &v.projection,
570        ));
571        if !v.from.is_empty() {
572            docs.push(title_comma_separate(
573                "FROM",
574                |t| self.doc_table_with_joins(t),
575                &v.from,
576            ));
577        }
578        if let Some(selection) = &v.selection {
579            docs.push(nest_title("WHERE", self.doc_expr(selection)));
580        }
581        if !v.group_by.is_empty() {
582            docs.push(title_comma_separate(
583                "GROUP BY",
584                |e| self.doc_expr(e),
585                &v.group_by,
586            ));
587        }
588        if let Some(having) = &v.having {
589            docs.push(nest_title("HAVING", self.doc_expr(having)));
590        }
591        if let Some(qualify) = &v.qualify {
592            docs.push(nest_title("QUALIFY", self.doc_expr(qualify)));
593        }
594        if !v.options.is_empty() {
595            docs.push(bracket(
596                "OPTIONS (",
597                comma_separate(|o| self.doc_display_pass(o), &v.options),
598                ")",
599            ));
600        }
601        RcDoc::intersperse(docs, Doc::line()).group()
602    }
603
604    fn doc_select_item<'a, T: AstInfo>(&'a self, v: &'a SelectItem<T>) -> RcDoc<'a> {
605        match v {
606            SelectItem::Expr { expr, alias } => {
607                let mut doc = self.doc_expr(expr);
608                if let Some(alias) = alias {
609                    doc = nest(
610                        doc,
611                        RcDoc::concat([RcDoc::text("AS "), self.doc_display_pass(alias)]),
612                    );
613                }
614                doc
615            }
616            SelectItem::Wildcard => self.doc_display_pass(v),
617        }
618    }
619
620    pub fn doc_expr<'a, T: AstInfo>(&'a self, v: &'a Expr<T>) -> RcDoc<'a> {
621        match v {
622            Expr::Op { op, expr1, expr2 } => {
623                if let Some(expr2) = expr2 {
624                    RcDoc::concat([
625                        self.doc_expr(expr1),
626                        RcDoc::line(),
627                        RcDoc::text(format!("{} ", op)),
628                        self.doc_expr(expr2).nest(TAB),
629                    ])
630                } else {
631                    RcDoc::concat([RcDoc::text(format!("{} ", op)), self.doc_expr(expr1)])
632                }
633            }
634            Expr::Case {
635                operand,
636                conditions,
637                results,
638                else_result,
639            } => {
640                let mut docs = Vec::new();
641                if let Some(operand) = operand {
642                    docs.push(self.doc_expr(operand));
643                }
644                for (c, r) in conditions.iter().zip_eq(results) {
645                    let when = nest_title("WHEN", self.doc_expr(c));
646                    let then = nest_title("THEN", self.doc_expr(r));
647                    docs.push(nest(when, then));
648                }
649                if let Some(else_result) = else_result {
650                    docs.push(nest_title("ELSE", self.doc_expr(else_result)));
651                }
652                let doc = intersperse_line_nest(docs);
653                bracket_doc(RcDoc::text("CASE"), doc, RcDoc::text("END"), RcDoc::line())
654            }
655            Expr::Cast { expr, data_type } => {
656                let doc = self.doc_expr(expr);
657                RcDoc::concat([
658                    doc,
659                    RcDoc::text(format!("::{}", data_type.to_ast_string_simple())),
660                ])
661            }
662            Expr::Nested(ast) => bracket("(", self.doc_expr(ast), ")"),
663            Expr::Function(fun) => self.doc_function(fun),
664            Expr::Subquery(ast) => bracket("(", self.doc_query(ast), ")"),
665            Expr::Identifier(_)
666            | Expr::Value(_)
667            | Expr::QualifiedWildcard(_)
668            | Expr::WildcardAccess(_)
669            | Expr::FieldAccess { .. } => self.doc_display_pass(v),
670            Expr::And { left, right } => bracket_doc(
671                self.doc_expr(left),
672                RcDoc::text("AND"),
673                self.doc_expr(right),
674                RcDoc::line(),
675            ),
676            Expr::Or { left, right } => bracket_doc(
677                self.doc_expr(left),
678                RcDoc::text("OR"),
679                self.doc_expr(right),
680                RcDoc::line(),
681            ),
682            Expr::Exists(s) => bracket("EXISTS (", self.doc_query(s), ")"),
683            Expr::IsExpr {
684                expr,
685                negated,
686                construct,
687            } => bracket_doc(
688                self.doc_expr(expr),
689                RcDoc::text(if *negated { "IS NOT" } else { "IS" }),
690                self.doc_display_pass(construct),
691                RcDoc::line(),
692            ),
693            Expr::Not { expr } => {
694                RcDoc::concat([RcDoc::text("NOT"), RcDoc::line(), self.doc_expr(expr)])
695            }
696            Expr::Between {
697                expr,
698                negated,
699                low,
700                high,
701            } => RcDoc::intersperse(
702                [
703                    self.doc_expr(expr),
704                    RcDoc::text(if *negated { "NOT BETWEEN" } else { "BETWEEN" }),
705                    RcDoc::intersperse(
706                        [self.doc_expr(low), RcDoc::text("AND"), self.doc_expr(high)],
707                        RcDoc::line(),
708                    )
709                    .group(),
710                ],
711                RcDoc::line(),
712            ),
713            Expr::InSubquery {
714                expr,
715                subquery,
716                negated,
717            } => RcDoc::intersperse(
718                [
719                    self.doc_expr(expr),
720                    RcDoc::text(if *negated { "NOT IN (" } else { "IN (" }),
721                    self.doc_query(subquery),
722                    RcDoc::text(")"),
723                ],
724                RcDoc::line(),
725            ),
726            Expr::InList {
727                expr,
728                list,
729                negated,
730            } => RcDoc::intersperse(
731                [
732                    self.doc_expr(expr),
733                    RcDoc::text(if *negated { "NOT IN (" } else { "IN (" }),
734                    comma_separate(|e| self.doc_expr(e), list),
735                    RcDoc::text(")"),
736                ],
737                RcDoc::line(),
738            ),
739            Expr::Row { exprs } => {
740                bracket("ROW(", comma_separate(|e| self.doc_expr(e), exprs), ")")
741            }
742            Expr::NullIf { l_expr, r_expr } => bracket(
743                "NULLIF (",
744                comma_separate(|e| self.doc_expr(e), [&**l_expr, &**r_expr]),
745                ")",
746            ),
747            Expr::HomogenizingFunction { function, exprs } => bracket(
748                format!("{function}("),
749                comma_separate(|e| self.doc_expr(e), exprs),
750                ")",
751            ),
752            Expr::ArraySubquery(s) => bracket("ARRAY(", self.doc_query(s), ")"),
753            Expr::ListSubquery(s) => bracket("LIST(", self.doc_query(s), ")"),
754            Expr::Array(exprs) => {
755                bracket("ARRAY[", comma_separate(|e| self.doc_expr(e), exprs), "]")
756            }
757            Expr::List(exprs) => bracket("LIST[", comma_separate(|e| self.doc_expr(e), exprs), "]"),
758            _ => self.doc_display(v, "expr variant"),
759        }
760        .group()
761    }
762
763    fn doc_function<'a, T: AstInfo>(&'a self, v: &'a Function<T>) -> RcDoc<'a> {
764        match &v.args {
765            FunctionArgs::Star => self.doc_display_pass(v),
766            FunctionArgs::Args { args, order_by } => {
767                if args.is_empty() {
768                    // Nullary, don't allow newline between parens, so just delegate.
769                    return self.doc_display_pass(v);
770                }
771                if v.filter.is_some() || v.over.is_some() || !order_by.is_empty() {
772                    return self.doc_display(v, "function filter or over or order by");
773                }
774                let special = match v.name.to_ast_string_stable().as_str() {
775                    r#""extract""# if v.args.len() == Some(2) => true,
776                    r#""position""# if v.args.len() == Some(2) => true,
777                    _ => false,
778                };
779                if special {
780                    return self.doc_display(v, "special function");
781                }
782                let name = format!(
783                    "{}({}",
784                    v.name.to_ast_string_simple(),
785                    if v.distinct { "DISTINCT " } else { "" }
786                );
787                bracket(name, comma_separate(|e| self.doc_expr(e), args), ")")
788            }
789        }
790    }
791}