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(cluster) = &v.in_cluster {
267            docs.push(RcDoc::text(format!(
268                "IN CLUSTER {}",
269                cluster.to_ast_string_simple()
270            )));
271        }
272        if !v.with_options.is_empty() {
273            docs.push(bracket(
274                "WITH (",
275                comma_separate(|wo| self.doc_display_pass(wo), &v.with_options),
276                ")",
277            ));
278        }
279        docs.push(nest_title("AS", self.doc_query(&v.query)));
280        intersperse_line_nest(docs)
281    }
282
283    fn doc_view_definition<'a, T: AstInfo>(&'a self, v: &'a ViewDefinition<T>) -> RcDoc<'a> {
284        let mut docs = vec![RcDoc::text(v.name.to_string())];
285        if !v.columns.is_empty() {
286            docs.push(bracket(
287                "(",
288                comma_separate(|c| self.doc_display_pass(c), &v.columns),
289                ")",
290            ));
291        }
292        docs.push(nest_title("AS", self.doc_query(&v.query)));
293        RcDoc::intersperse(docs, Doc::line()).group()
294    }
295
296    pub(crate) fn doc_insert<'a, T: AstInfo>(&'a self, v: &'a InsertStatement<T>) -> RcDoc<'a> {
297        let mut first = vec![RcDoc::text(format!(
298            "INSERT INTO {}",
299            v.table_name.to_ast_string_simple()
300        ))];
301        if !v.columns.is_empty() {
302            first.push(bracket(
303                "(",
304                comma_separate(|c| self.doc_display_pass(c), &v.columns),
305                ")",
306            ));
307        }
308        let sources = match &v.source {
309            InsertSource::Query(query) => self.doc_query(query),
310            _ => self.doc_display(&v.source, "insert source"),
311        };
312        let mut doc = intersperse_line_nest([intersperse_line_nest(first), sources]);
313        if !v.returning.is_empty() {
314            doc = nest(
315                doc,
316                nest_title(
317                    "RETURNING",
318                    comma_separate(|r| self.doc_display_pass(r), &v.returning),
319                ),
320            )
321        }
322        doc
323    }
324
325    pub(crate) fn doc_select_statement<'a, T: AstInfo>(
326        &'a self,
327        v: &'a SelectStatement<T>,
328    ) -> RcDoc<'a> {
329        let mut doc = self.doc_query(&v.query);
330        if let Some(as_of) = &v.as_of {
331            doc = intersperse_line_nest([doc, self.doc_as_of(as_of)]);
332        }
333        doc.group()
334    }
335
336    fn doc_order_by<'a, T: AstInfo>(&'a self, v: &'a [OrderByExpr<T>]) -> RcDoc<'a> {
337        title_comma_separate("ORDER BY", |o| self.doc_order_by_expr(o), v)
338    }
339
340    fn doc_order_by_expr<'a, T: AstInfo>(&'a self, v: &'a OrderByExpr<T>) -> RcDoc<'a> {
341        let doc = self.doc_expr(&v.expr);
342        let doc = match v.asc {
343            Some(true) => nest(doc, RcDoc::text("ASC")),
344            Some(false) => nest(doc, RcDoc::text("DESC")),
345            None => doc,
346        };
347        match v.nulls_last {
348            Some(true) => nest(doc, RcDoc::text("NULLS LAST")),
349            Some(false) => nest(doc, RcDoc::text("NULLS FIRST")),
350            None => doc,
351        }
352    }
353
354    fn doc_query<'a, T: AstInfo>(&'a self, v: &'a Query<T>) -> RcDoc<'a> {
355        let mut docs = vec![];
356        if !v.ctes.is_empty() {
357            match &v.ctes {
358                CteBlock::Simple(ctes) => {
359                    docs.push(title_comma_separate("WITH", |cte| self.doc_cte(cte), ctes))
360                }
361                CteBlock::MutuallyRecursive(mutrec) => {
362                    let mut doc = RcDoc::text("WITH MUTUALLY RECURSIVE");
363                    if !mutrec.options.is_empty() {
364                        doc = nest(
365                            doc,
366                            bracket(
367                                "(",
368                                comma_separate(|o| self.doc_display_pass(o), &mutrec.options),
369                                ")",
370                            ),
371                        );
372                    }
373                    docs.push(nest(
374                        doc,
375                        comma_separate(|c| self.doc_mutually_recursive(c), &mutrec.ctes),
376                    ));
377                }
378            }
379        }
380        docs.push(self.doc_set_expr(&v.body));
381        if !v.order_by.is_empty() {
382            docs.push(self.doc_order_by(&v.order_by));
383        }
384
385        let offset = if let Some(offset) = &v.offset {
386            vec![RcDoc::concat([nest_title("OFFSET", self.doc_expr(offset))])]
387        } else {
388            vec![]
389        };
390
391        if let Some(limit) = &v.limit {
392            if limit.with_ties {
393                docs.extend(offset);
394                docs.push(RcDoc::concat([
395                    RcDoc::text("FETCH FIRST "),
396                    self.doc_expr(&limit.quantity),
397                    RcDoc::text(" ROWS WITH TIES"),
398                ]));
399            } else {
400                docs.push(nest_title("LIMIT", self.doc_expr(&limit.quantity)));
401                docs.extend(offset);
402            }
403        } else {
404            docs.extend(offset);
405        }
406
407        RcDoc::intersperse(docs, Doc::line()).group()
408    }
409
410    fn doc_cte<'a, T: AstInfo>(&'a self, v: &'a Cte<T>) -> RcDoc<'a> {
411        RcDoc::concat([
412            RcDoc::text(format!("{} AS", v.alias)),
413            RcDoc::line(),
414            bracket("(", self.doc_query(&v.query), ")"),
415        ])
416    }
417
418    fn doc_mutually_recursive<'a, T: AstInfo>(&'a self, v: &'a CteMutRec<T>) -> RcDoc<'a> {
419        let mut docs = Vec::new();
420        if !v.columns.is_empty() {
421            docs.push(bracket(
422                "(",
423                comma_separate(|c| self.doc_display_pass(c), &v.columns),
424                ")",
425            ));
426        }
427        docs.push(bracket("AS (", self.doc_query(&v.query), ")"));
428        nest(
429            self.doc_display_pass(&v.name),
430            RcDoc::intersperse(docs, Doc::line()).group(),
431        )
432    }
433
434    fn doc_set_expr<'a, T: AstInfo>(&'a self, v: &'a SetExpr<T>) -> RcDoc<'a> {
435        match v {
436            SetExpr::Select(v) => self.doc_select(v),
437            SetExpr::Query(v) => bracket("(", self.doc_query(v), ")"),
438            SetExpr::SetOperation {
439                op,
440                all,
441                left,
442                right,
443            } => {
444                let all_str = if *all { " ALL" } else { "" };
445                RcDoc::concat([
446                    self.doc_set_expr(left),
447                    RcDoc::line(),
448                    RcDoc::concat([
449                        RcDoc::text(format!("{}{}", op, all_str)),
450                        RcDoc::line(),
451                        self.doc_set_expr(right),
452                    ])
453                    .nest(TAB)
454                    .group(),
455                ])
456            }
457            SetExpr::Values(v) => self.doc_values(v),
458            SetExpr::Show(v) => self.doc_display(v, "SHOW"),
459            SetExpr::Table(v) => nest(RcDoc::text("TABLE"), self.doc_display_pass(v)),
460        }
461        .group()
462    }
463
464    fn doc_values<'a, T: AstInfo>(&'a self, v: &'a Values<T>) -> RcDoc<'a> {
465        let rows =
466            v.0.iter()
467                .map(|row| bracket("(", comma_separate(|v| self.doc_expr(v), row), ")"));
468        RcDoc::concat([RcDoc::text("VALUES"), RcDoc::line(), comma_separated(rows)])
469            .nest(TAB)
470            .group()
471    }
472
473    fn doc_table_with_joins<'a, T: AstInfo>(&'a self, v: &'a TableWithJoins<T>) -> RcDoc<'a> {
474        let mut docs = vec![self.doc_table_factor(&v.relation)];
475        for j in &v.joins {
476            docs.push(self.doc_join(j));
477        }
478        intersperse_line_nest(docs)
479    }
480
481    fn doc_join<'a, T: AstInfo>(&'a self, v: &'a Join<T>) -> RcDoc<'a> {
482        let (constraint, name) = match &v.join_operator {
483            JoinOperator::Inner(constraint) => (constraint, "JOIN"),
484            JoinOperator::FullOuter(constraint) => (constraint, "FULL JOIN"),
485            JoinOperator::LeftOuter(constraint) => (constraint, "LEFT JOIN"),
486            JoinOperator::RightOuter(constraint) => (constraint, "RIGHT JOIN"),
487            _ => return self.doc_display(v, "join operator"),
488        };
489        let constraint = match constraint {
490            JoinConstraint::On(expr) => nest_title("ON", self.doc_expr(expr)),
491            JoinConstraint::Using { columns, alias } => {
492                let mut doc = bracket(
493                    "USING(",
494                    comma_separate(|c| self.doc_display_pass(c), columns),
495                    ")",
496                );
497                if let Some(alias) = alias {
498                    doc = nest(doc, nest_title("AS", self.doc_display_pass(alias)));
499                }
500                doc
501            }
502            _ => return self.doc_display(v, "join constraint"),
503        };
504        intersperse_line_nest([
505            RcDoc::text(name),
506            self.doc_table_factor(&v.relation),
507            constraint,
508        ])
509    }
510
511    fn doc_table_factor<'a, T: AstInfo>(&'a self, v: &'a TableFactor<T>) -> RcDoc<'a> {
512        match v {
513            TableFactor::Derived {
514                lateral,
515                subquery,
516                alias,
517            } => {
518                let prefix = if *lateral { "LATERAL (" } else { "(" };
519                let mut docs = vec![bracket(prefix, self.doc_query(subquery), ")")];
520                if let Some(alias) = alias {
521                    docs.push(RcDoc::text(format!("AS {}", alias)));
522                }
523                intersperse_line_nest(docs)
524            }
525            TableFactor::NestedJoin { join, alias } => {
526                let mut doc = bracket("(", self.doc_table_with_joins(join), ")");
527                if let Some(alias) = alias {
528                    doc = nest(doc, RcDoc::text(format!("AS {}", alias)));
529                }
530                doc
531            }
532            TableFactor::Table { name, alias } => {
533                let mut doc = self.doc_display_pass(name);
534                if let Some(alias) = alias {
535                    doc = nest(doc, RcDoc::text(format!("AS {}", alias)));
536                }
537                doc
538            }
539            _ => self.doc_display(v, "table factor variant"),
540        }
541    }
542
543    fn doc_distinct<'a, T: AstInfo>(&'a self, v: &'a Distinct<T>) -> RcDoc<'a> {
544        match v {
545            Distinct::EntireRow => RcDoc::text("DISTINCT"),
546            Distinct::On(cols) => bracket(
547                "DISTINCT ON (",
548                comma_separate(|c| self.doc_expr(c), cols),
549                ")",
550            ),
551        }
552    }
553
554    fn doc_select<'a, T: AstInfo>(&'a self, v: &'a Select<T>) -> RcDoc<'a> {
555        let mut docs = vec![];
556        let mut select = RcDoc::text("SELECT");
557        if let Some(distinct) = &v.distinct {
558            select = nest(select, self.doc_distinct(distinct));
559        }
560        docs.push(nest_comma_separate(
561            select,
562            |s| self.doc_select_item(s),
563            &v.projection,
564        ));
565        if !v.from.is_empty() {
566            docs.push(title_comma_separate(
567                "FROM",
568                |t| self.doc_table_with_joins(t),
569                &v.from,
570            ));
571        }
572        if let Some(selection) = &v.selection {
573            docs.push(nest_title("WHERE", self.doc_expr(selection)));
574        }
575        if !v.group_by.is_empty() {
576            docs.push(title_comma_separate(
577                "GROUP BY",
578                |e| self.doc_expr(e),
579                &v.group_by,
580            ));
581        }
582        if let Some(having) = &v.having {
583            docs.push(nest_title("HAVING", self.doc_expr(having)));
584        }
585        if let Some(qualify) = &v.qualify {
586            docs.push(nest_title("QUALIFY", self.doc_expr(qualify)));
587        }
588        if !v.options.is_empty() {
589            docs.push(bracket(
590                "OPTIONS (",
591                comma_separate(|o| self.doc_display_pass(o), &v.options),
592                ")",
593            ));
594        }
595        RcDoc::intersperse(docs, Doc::line()).group()
596    }
597
598    fn doc_select_item<'a, T: AstInfo>(&'a self, v: &'a SelectItem<T>) -> RcDoc<'a> {
599        match v {
600            SelectItem::Expr { expr, alias } => {
601                let mut doc = self.doc_expr(expr);
602                if let Some(alias) = alias {
603                    doc = nest(
604                        doc,
605                        RcDoc::concat([RcDoc::text("AS "), self.doc_display_pass(alias)]),
606                    );
607                }
608                doc
609            }
610            SelectItem::Wildcard => self.doc_display_pass(v),
611        }
612    }
613
614    pub fn doc_expr<'a, T: AstInfo>(&'a self, v: &'a Expr<T>) -> RcDoc<'a> {
615        match v {
616            Expr::Op { op, expr1, expr2 } => {
617                if let Some(expr2) = expr2 {
618                    RcDoc::concat([
619                        self.doc_expr(expr1),
620                        RcDoc::line(),
621                        RcDoc::text(format!("{} ", op)),
622                        self.doc_expr(expr2).nest(TAB),
623                    ])
624                } else {
625                    RcDoc::concat([RcDoc::text(format!("{} ", op)), self.doc_expr(expr1)])
626                }
627            }
628            Expr::Case {
629                operand,
630                conditions,
631                results,
632                else_result,
633            } => {
634                let mut docs = Vec::new();
635                if let Some(operand) = operand {
636                    docs.push(self.doc_expr(operand));
637                }
638                for (c, r) in conditions.iter().zip_eq(results) {
639                    let when = nest_title("WHEN", self.doc_expr(c));
640                    let then = nest_title("THEN", self.doc_expr(r));
641                    docs.push(nest(when, then));
642                }
643                if let Some(else_result) = else_result {
644                    docs.push(nest_title("ELSE", self.doc_expr(else_result)));
645                }
646                let doc = intersperse_line_nest(docs);
647                bracket_doc(RcDoc::text("CASE"), doc, RcDoc::text("END"), RcDoc::line())
648            }
649            Expr::Cast { expr, data_type } => {
650                let doc = self.doc_expr(expr);
651                RcDoc::concat([
652                    doc,
653                    RcDoc::text(format!("::{}", data_type.to_ast_string_simple())),
654                ])
655            }
656            Expr::Nested(ast) => bracket("(", self.doc_expr(ast), ")"),
657            Expr::Function(fun) => self.doc_function(fun),
658            Expr::Subquery(ast) => bracket("(", self.doc_query(ast), ")"),
659            Expr::Identifier(_)
660            | Expr::Value(_)
661            | Expr::QualifiedWildcard(_)
662            | Expr::WildcardAccess(_)
663            | Expr::FieldAccess { .. } => self.doc_display_pass(v),
664            Expr::And { left, right } => bracket_doc(
665                self.doc_expr(left),
666                RcDoc::text("AND"),
667                self.doc_expr(right),
668                RcDoc::line(),
669            ),
670            Expr::Or { left, right } => bracket_doc(
671                self.doc_expr(left),
672                RcDoc::text("OR"),
673                self.doc_expr(right),
674                RcDoc::line(),
675            ),
676            Expr::Exists(s) => bracket("EXISTS (", self.doc_query(s), ")"),
677            Expr::IsExpr {
678                expr,
679                negated,
680                construct,
681            } => bracket_doc(
682                self.doc_expr(expr),
683                RcDoc::text(if *negated { "IS NOT" } else { "IS" }),
684                self.doc_display_pass(construct),
685                RcDoc::line(),
686            ),
687            Expr::Not { expr } => {
688                RcDoc::concat([RcDoc::text("NOT"), RcDoc::line(), self.doc_expr(expr)])
689            }
690            Expr::Between {
691                expr,
692                negated,
693                low,
694                high,
695            } => RcDoc::intersperse(
696                [
697                    self.doc_expr(expr),
698                    RcDoc::text(if *negated { "NOT BETWEEN" } else { "BETWEEN" }),
699                    RcDoc::intersperse(
700                        [self.doc_expr(low), RcDoc::text("AND"), self.doc_expr(high)],
701                        RcDoc::line(),
702                    )
703                    .group(),
704                ],
705                RcDoc::line(),
706            ),
707            Expr::InSubquery {
708                expr,
709                subquery,
710                negated,
711            } => RcDoc::intersperse(
712                [
713                    self.doc_expr(expr),
714                    RcDoc::text(if *negated { "NOT IN (" } else { "IN (" }),
715                    self.doc_query(subquery),
716                    RcDoc::text(")"),
717                ],
718                RcDoc::line(),
719            ),
720            Expr::InList {
721                expr,
722                list,
723                negated,
724            } => RcDoc::intersperse(
725                [
726                    self.doc_expr(expr),
727                    RcDoc::text(if *negated { "NOT IN (" } else { "IN (" }),
728                    comma_separate(|e| self.doc_expr(e), list),
729                    RcDoc::text(")"),
730                ],
731                RcDoc::line(),
732            ),
733            Expr::Row { exprs } => {
734                bracket("ROW(", comma_separate(|e| self.doc_expr(e), exprs), ")")
735            }
736            Expr::NullIf { l_expr, r_expr } => bracket(
737                "NULLIF (",
738                comma_separate(|e| self.doc_expr(e), [&**l_expr, &**r_expr]),
739                ")",
740            ),
741            Expr::HomogenizingFunction { function, exprs } => bracket(
742                format!("{function}("),
743                comma_separate(|e| self.doc_expr(e), exprs),
744                ")",
745            ),
746            Expr::ArraySubquery(s) => bracket("ARRAY(", self.doc_query(s), ")"),
747            Expr::ListSubquery(s) => bracket("LIST(", self.doc_query(s), ")"),
748            Expr::Array(exprs) => {
749                bracket("ARRAY[", comma_separate(|e| self.doc_expr(e), exprs), "]")
750            }
751            Expr::List(exprs) => bracket("LIST[", comma_separate(|e| self.doc_expr(e), exprs), "]"),
752            _ => self.doc_display(v, "expr variant"),
753        }
754        .group()
755    }
756
757    fn doc_function<'a, T: AstInfo>(&'a self, v: &'a Function<T>) -> RcDoc<'a> {
758        match &v.args {
759            FunctionArgs::Star => self.doc_display_pass(v),
760            FunctionArgs::Args { args, order_by } => {
761                if args.is_empty() {
762                    // Nullary, don't allow newline between parens, so just delegate.
763                    return self.doc_display_pass(v);
764                }
765                if v.filter.is_some() || v.over.is_some() || !order_by.is_empty() {
766                    return self.doc_display(v, "function filter or over or order by");
767                }
768                let special = match v.name.to_ast_string_stable().as_str() {
769                    r#""extract""# if v.args.len() == Some(2) => true,
770                    r#""position""# if v.args.len() == Some(2) => true,
771                    _ => false,
772                };
773                if special {
774                    return self.doc_display(v, "special function");
775                }
776                let name = format!(
777                    "{}({}",
778                    v.name.to_ast_string_simple(),
779                    if v.distinct { "DISTINCT " } else { "" }
780                );
781                bracket(name, comma_separate(|e| self.doc_expr(e), args), ")")
782            }
783        }
784    }
785}