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