1use 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 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 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 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}