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