1use itertools::Itertools;
13use mz_sql_parser::ast::display::{AstDisplay, FormatMode, escape_single_quote_string};
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 pub(crate) fn doc_create_webhook_source<'a, T: AstInfo>(
98 &'a self,
99 v: &'a CreateWebhookSourceStatement<T>,
100 ) -> RcDoc<'a> {
101 let mut docs = Vec::new();
102
103 let mut title = "CREATE ".to_string();
104 if v.is_table {
105 title.push_str("TABLE");
106 } else {
107 title.push_str("SOURCE");
108 }
109 if v.if_not_exists {
110 title.push_str(" IF NOT EXISTS");
111 }
112 docs.push(nest_title(title, self.doc_display_pass(&v.name)));
113
114 if !v.is_table {
116 if let Some(cluster) = &v.in_cluster {
117 docs.push(nest_title("IN CLUSTER", self.doc_display_pass(cluster)));
118 }
119 }
120
121 docs.push(RcDoc::text("FROM WEBHOOK"));
122 docs.push(nest_title(
123 "BODY FORMAT",
124 self.doc_display_pass(&v.body_format),
125 ));
126
127 if !v.include_headers.mappings.is_empty() || v.include_headers.column.is_some() {
128 let mut header_docs = Vec::new();
129
130 for mapping in &v.include_headers.mappings {
132 header_docs.push(self.doc_display_pass(mapping));
133 }
134
135 if let Some(filters) = &v.include_headers.column {
137 if filters.is_empty() {
138 header_docs.push(RcDoc::text("INCLUDE HEADERS"));
139 } else {
140 header_docs.push(bracket(
141 "INCLUDE HEADERS (",
142 comma_separate(|f| self.doc_display_pass(f), filters),
143 ")",
144 ));
145 }
146 }
147
148 if !header_docs.is_empty() {
149 docs.extend(header_docs);
150 }
151 }
152
153 if let Some(check) = &v.validate_using {
154 docs.push(self.doc_webhook_check(check));
155 }
156
157 RcDoc::intersperse(docs, Doc::line()).group()
158 }
159
160 fn doc_webhook_check<'a, T: AstInfo>(
161 &'a self,
162 v: &'a CreateWebhookSourceCheck<T>,
163 ) -> RcDoc<'a> {
164 let mut inner = Vec::new();
165
166 if let Some(options) = &v.options {
167 let mut with_items = Vec::new();
168
169 for header in &options.headers {
170 with_items.push(self.doc_display_pass(header));
171 }
172 for body in &options.bodies {
173 with_items.push(self.doc_display_pass(body));
174 }
175 for secret in &options.secrets {
176 with_items.push(self.doc_display_pass(secret));
177 }
178
179 if !with_items.is_empty() {
180 inner.push(bracket("WITH (", comma_separated(with_items), ")"));
181 inner.push(RcDoc::line());
182 }
183 }
184
185 inner.push(self.doc_display_pass(&v.using));
186
187 bracket_doc(
188 RcDoc::text("CHECK ("),
189 RcDoc::concat(inner),
190 RcDoc::text(")"),
191 RcDoc::line(),
192 )
193 }
194
195 pub(crate) fn doc_create_table<'a, T: AstInfo>(
196 &'a self,
197 v: &'a CreateTableStatement<T>,
198 ) -> RcDoc<'a> {
199 let mut docs = Vec::new();
200
201 let mut title = "CREATE ".to_string();
203 if v.temporary {
204 title.push_str("TEMPORARY ");
205 }
206 title.push_str("TABLE");
207 if v.if_not_exists {
208 title.push_str(" IF NOT EXISTS");
209 }
210
211 let mut col_items = Vec::new();
213 col_items.extend(v.columns.iter().map(|c| self.doc_display_pass(c)));
214 col_items.extend(v.constraints.iter().map(|c| self.doc_display_pass(c)));
215
216 let table_def = nest(
217 self.doc_display_pass(&v.name),
218 bracket("(", comma_separated(col_items), ")"),
219 );
220 docs.push(nest_title(title, table_def));
221
222 if !v.with_options.is_empty() {
224 docs.push(bracket(
225 "WITH (",
226 comma_separate(|wo| self.doc_display_pass(wo), &v.with_options),
227 ")",
228 ));
229 }
230
231 RcDoc::intersperse(docs, Doc::line()).group()
232 }
233
234 pub(crate) fn doc_create_table_from_source<'a, T: AstInfo>(
235 &'a self,
236 v: &'a CreateTableFromSourceStatement<T>,
237 ) -> RcDoc<'a> {
238 let mut docs = Vec::new();
239
240 let mut title = "CREATE TABLE".to_string();
242 if v.if_not_exists {
243 title.push_str(" IF NOT EXISTS");
244 }
245
246 let mut table_def = self.doc_display_pass(&v.name);
247
248 let has_columns_or_constraints = match &v.columns {
249 TableFromSourceColumns::NotSpecified => false,
250 _ => true,
251 } || !v.constraints.is_empty();
252
253 if has_columns_or_constraints {
254 let mut items = Vec::new();
255
256 match &v.columns {
257 TableFromSourceColumns::NotSpecified => {}
258 TableFromSourceColumns::Named(cols) => {
259 items.extend(cols.iter().map(|c| self.doc_display_pass(c)));
260 }
261 TableFromSourceColumns::Defined(cols) => {
262 items.extend(cols.iter().map(|c| self.doc_display_pass(c)));
263 }
264 }
265
266 items.extend(v.constraints.iter().map(|c| self.doc_display_pass(c)));
267
268 if !items.is_empty() {
269 table_def = nest(table_def, bracket("(", comma_separated(items), ")"));
270 }
271 }
272
273 docs.push(nest_title(title, table_def));
274
275 let mut from_source = nest_title("FROM SOURCE", self.doc_display_pass(&v.source));
277 if let Some(reference) = &v.external_reference {
278 from_source = nest(
279 from_source,
280 bracket("(REFERENCE = ", self.doc_display_pass(reference), ")"),
281 );
282 }
283 docs.push(from_source);
284
285 if let Some(format) = &v.format {
286 docs.push(self.doc_format_specifier(format));
287 }
288
289 if !v.include_metadata.is_empty() {
290 docs.push(nest_title(
291 "INCLUDE",
292 comma_separate(|im| self.doc_display_pass(im), &v.include_metadata),
293 ));
294 }
295
296 if let Some(envelope) = &v.envelope {
297 docs.push(nest_title("ENVELOPE", self.doc_display_pass(envelope)));
298 }
299
300 if !v.with_options.is_empty() {
301 docs.push(bracket(
302 "WITH (",
303 comma_separate(|wo| self.doc_display_pass(wo), &v.with_options),
304 ")",
305 ));
306 }
307
308 RcDoc::intersperse(docs, Doc::line()).group()
309 }
310
311 pub(crate) fn doc_create_connection<'a, T: AstInfo>(
312 &'a self,
313 v: &'a CreateConnectionStatement<T>,
314 ) -> RcDoc<'a> {
315 let mut docs = Vec::new();
316
317 let mut title = "CREATE CONNECTION".to_string();
318 if v.if_not_exists {
319 title.push_str(" IF NOT EXISTS");
320 }
321 docs.push(nest_title(title, self.doc_display_pass(&v.name)));
322
323 let connection_with_values = nest(
324 RcDoc::concat([
325 RcDoc::text("TO "),
326 self.doc_display_pass(&v.connection_type),
327 ]),
328 bracket(
329 "(",
330 comma_separate(|val| self.doc_display_pass(val), &v.values),
331 ")",
332 ),
333 );
334 docs.push(connection_with_values);
335
336 if !v.with_options.is_empty() {
337 docs.push(bracket(
338 "WITH (",
339 comma_separate(|wo| self.doc_display_pass(wo), &v.with_options),
340 ")",
341 ));
342 }
343
344 RcDoc::intersperse(docs, Doc::line()).group()
345 }
346
347 pub(crate) fn doc_create_sink<'a, T: AstInfo>(
348 &'a self,
349 v: &'a CreateSinkStatement<T>,
350 ) -> RcDoc<'a> {
351 let mut docs = Vec::new();
352
353 let mut title = "CREATE SINK".to_string();
355 if v.if_not_exists {
356 title.push_str(" IF NOT EXISTS");
357 }
358
359 if let Some(name) = &v.name {
360 docs.push(nest_title(title, self.doc_display_pass(name)));
361 } else {
362 docs.push(RcDoc::text(title));
363 }
364
365 if let Some(cluster) = &v.in_cluster {
366 docs.push(nest_title("IN CLUSTER", self.doc_display_pass(cluster)));
367 }
368
369 docs.push(nest_title("FROM", self.doc_display_pass(&v.from)));
370 docs.push(nest_title("INTO", self.doc_display_pass(&v.connection)));
371
372 if let Some(format) = &v.format {
373 docs.push(self.doc_format_specifier(format));
374 }
375
376 if let Some(envelope) = &v.envelope {
377 docs.push(nest_title("ENVELOPE", self.doc_display_pass(envelope)));
378 }
379
380 if let Some(mode) = &v.mode {
381 docs.push(nest_title("MODE", self.doc_display_pass(mode)));
382 }
383
384 if !v.with_options.is_empty() {
385 docs.push(bracket(
386 "WITH (",
387 comma_separate(|wo| self.doc_display_pass(wo), &v.with_options),
388 ")",
389 ));
390 }
391
392 RcDoc::intersperse(docs, Doc::line()).group()
393 }
394
395 pub(crate) fn doc_create_subsource<'a, T: AstInfo>(
396 &'a self,
397 v: &'a CreateSubsourceStatement<T>,
398 ) -> RcDoc<'a> {
399 let mut docs = Vec::new();
400
401 let mut title = "CREATE SUBSOURCE".to_string();
403 if v.if_not_exists {
404 title.push_str(" IF NOT EXISTS");
405 }
406
407 let mut col_items = Vec::new();
409 col_items.extend(v.columns.iter().map(|c| self.doc_display_pass(c)));
410 col_items.extend(v.constraints.iter().map(|c| self.doc_display_pass(c)));
411
412 let table_def = nest(
413 self.doc_display_pass(&v.name),
414 bracket("(", comma_separated(col_items), ")"),
415 );
416 docs.push(nest_title(title, table_def));
417
418 if let Some(of_source) = &v.of_source {
420 docs.push(nest_title("OF SOURCE", self.doc_display_pass(of_source)));
421 }
422
423 if !v.with_options.is_empty() {
425 docs.push(bracket(
426 "WITH (",
427 comma_separate(|wo| self.doc_display_pass(wo), &v.with_options),
428 ")",
429 ));
430 }
431
432 RcDoc::intersperse(docs, Doc::line()).group()
433 }
434
435 pub(crate) fn doc_create_cluster<'a, T: AstInfo>(
436 &'a self,
437 v: &'a CreateClusterStatement<T>,
438 ) -> RcDoc<'a> {
439 let mut docs = Vec::new();
440
441 docs.push(nest_title("CREATE CLUSTER", self.doc_display_pass(&v.name)));
443
444 if !v.options.is_empty() {
446 docs.push(bracket(
447 "(",
448 comma_separate(|o| self.doc_display_pass(o), &v.options),
449 ")",
450 ));
451 }
452
453 if !v.features.is_empty() {
455 docs.push(bracket(
456 "FEATURES (",
457 comma_separate(|f| self.doc_display_pass(f), &v.features),
458 ")",
459 ));
460 }
461
462 RcDoc::intersperse(docs, Doc::line()).group()
463 }
464
465 pub(crate) fn doc_create_cluster_replica<'a, T: AstInfo>(
466 &'a self,
467 v: &'a CreateClusterReplicaStatement<T>,
468 ) -> RcDoc<'a> {
469 let mut docs = Vec::new();
470
471 let replica_name = RcDoc::concat([
473 self.doc_display_pass(&v.of_cluster),
474 RcDoc::text("."),
475 self.doc_display_pass(&v.definition.name),
476 ]);
477 docs.push(nest_title("CREATE CLUSTER REPLICA", replica_name));
478
479 docs.push(bracket(
481 "(",
482 comma_separate(|o| self.doc_display_pass(o), &v.definition.options),
483 ")",
484 ));
485
486 RcDoc::intersperse(docs, Doc::line()).group()
487 }
488
489 pub(crate) fn doc_create_network_policy<'a, T: AstInfo>(
490 &'a self,
491 v: &'a CreateNetworkPolicyStatement<T>,
492 ) -> RcDoc<'a> {
493 let docs = vec![
494 nest_title("CREATE NETWORK POLICY", self.doc_display_pass(&v.name)),
496 bracket(
498 "(",
499 comma_separate(|o| self.doc_display_pass(o), &v.options),
500 ")",
501 ),
502 ];
503
504 RcDoc::intersperse(docs, Doc::line()).group()
505 }
506
507 pub(crate) fn doc_create_index<'a, T: AstInfo>(
508 &'a self,
509 v: &'a CreateIndexStatement<T>,
510 ) -> RcDoc<'a> {
511 let mut docs = Vec::new();
512
513 let mut title = "CREATE".to_string();
515 if v.key_parts.is_none() {
516 title.push_str(" DEFAULT");
517 }
518 title.push_str(" INDEX");
519 if v.if_not_exists {
520 title.push_str(" IF NOT EXISTS");
521 }
522
523 if let Some(name) = &v.name {
524 let name_doc = if name.as_str().eq_ignore_ascii_case("in") {
529 RcDoc::text(format!("\"{}\"", name.as_str()))
530 } else {
531 self.doc_display_pass(name)
532 };
533 docs.push(nest_title(title, name_doc));
534 } else {
535 docs.push(RcDoc::text(title));
536 }
537
538 if let Some(cluster) = &v.in_cluster {
540 docs.push(nest_title("IN CLUSTER", self.doc_display_pass(cluster)));
541 }
542
543 let on_clause = if let Some(key_parts) = &v.key_parts {
545 nest(
546 self.doc_display_pass(&v.on_name),
547 bracket(
548 "(",
549 comma_separate(|k| self.doc_display_pass(k), key_parts),
550 ")",
551 ),
552 )
553 } else {
554 self.doc_display_pass(&v.on_name)
555 };
556 docs.push(nest_title("ON", on_clause));
557
558 if !v.with_options.is_empty() {
560 docs.push(bracket(
561 "WITH (",
562 comma_separate(|wo| self.doc_display_pass(wo), &v.with_options),
563 ")",
564 ));
565 }
566
567 RcDoc::intersperse(docs, Doc::line()).group()
568 }
569
570 fn doc_format_specifier<T: AstInfo>(&self, v: &FormatSpecifier<T>) -> RcDoc<'_> {
571 match v {
572 FormatSpecifier::Bare(format) => nest_title("FORMAT", self.doc_display_pass(format)),
573 FormatSpecifier::KeyValue { key, value } => {
574 let docs = vec![
575 nest_title("KEY FORMAT", self.doc_display_pass(key)),
576 nest_title("VALUE FORMAT", self.doc_display_pass(value)),
577 ];
578 RcDoc::intersperse(docs, Doc::line()).group()
579 }
580 }
581 }
582
583 fn doc_external_references<'a>(&'a self, v: &'a ExternalReferences) -> RcDoc<'a> {
584 match v {
585 ExternalReferences::SubsetTables(subsources) => bracket(
586 "FOR TABLES (",
587 comma_separate(|s| self.doc_display_pass(s), subsources),
588 ")",
589 ),
590 ExternalReferences::SubsetSchemas(schemas) => bracket(
591 "FOR SCHEMAS (",
592 comma_separate(|s| self.doc_display_pass(s), schemas),
593 ")",
594 ),
595 ExternalReferences::All => RcDoc::text("FOR ALL TABLES"),
596 }
597 }
598
599 pub(crate) fn doc_copy<'a, T: AstInfo>(&'a self, v: &'a CopyStatement<T>) -> RcDoc<'a> {
600 let relation = match &v.relation {
601 CopyRelation::Named { name, columns } => {
602 let mut relation = self.doc_display_pass(name);
603 if !columns.is_empty() {
604 relation = bracket_doc(
605 nest(relation, RcDoc::text("(")),
606 comma_separate(|c| self.doc_display_pass(c), columns),
607 RcDoc::text(")"),
608 RcDoc::line_(),
609 );
610 }
611 RcDoc::concat([RcDoc::text("COPY "), relation])
612 }
613 CopyRelation::Select(query) => bracket("COPY (", self.doc_select_statement(query), ")"),
614 CopyRelation::Subscribe(query) => bracket("COPY (", self.doc_subscribe(query), ")"),
615 };
616 let mut docs = vec![
617 relation,
618 RcDoc::concat([
619 self.doc_display_pass(&v.direction),
620 RcDoc::text(" "),
621 self.doc_display_pass(&v.target),
622 ]),
623 ];
624 if !v.options.is_empty() {
625 docs.push(bracket(
626 "WITH (",
627 comma_separate(|o| self.doc_display_pass(o), &v.options),
628 ")",
629 ));
630 }
631 RcDoc::intersperse(docs, Doc::line()).group()
632 }
633
634 pub(crate) fn doc_subscribe<'a, T: AstInfo>(
635 &'a self,
636 v: &'a SubscribeStatement<T>,
637 ) -> RcDoc<'a> {
638 let doc = match &v.relation {
639 SubscribeRelation::Name(name) => {
640 let title = if v.relation.needs_explicit_to(matches!(
641 self.config.format_mode,
642 FormatMode::Simple | FormatMode::SimpleRedacted
643 )) {
644 "SUBSCRIBE TO"
645 } else {
646 "SUBSCRIBE"
647 };
648 nest_title(title, self.doc_display_pass(name))
649 }
650 SubscribeRelation::Query(query) => bracket("SUBSCRIBE (", self.doc_query(query), ")"),
651 };
652 let mut docs = vec![doc];
653 if !v.options.is_empty() {
654 docs.push(bracket(
655 "WITH (",
656 comma_separate(|o| self.doc_display_pass(o), &v.options),
657 ")",
658 ));
659 }
660 if let Some(as_of) = &v.as_of {
661 docs.push(self.doc_as_of(as_of));
662 }
663 if let Some(up_to) = &v.up_to {
664 docs.push(nest_title("UP TO", self.doc_expr(up_to)));
665 }
666 match &v.output {
667 SubscribeOutput::Diffs => {}
668 SubscribeOutput::WithinTimestampOrderBy { order_by } => {
669 docs.push(nest_title(
670 "WITHIN TIMESTAMP ORDER BY ",
671 comma_separate(|o| self.doc_order_by_expr(o), order_by),
672 ));
673 }
674 SubscribeOutput::EnvelopeUpsert { key_columns } => {
675 docs.push(bracket(
676 "ENVELOPE UPSERT (KEY (",
677 comma_separate(|kc| self.doc_display_pass(kc), key_columns),
678 "))",
679 ));
680 }
681 SubscribeOutput::EnvelopeDebezium { key_columns } => {
682 docs.push(bracket(
683 "ENVELOPE DEBEZIUM (KEY (",
684 comma_separate(|kc| self.doc_display_pass(kc), key_columns),
685 "))",
686 ));
687 }
688 }
689 RcDoc::intersperse(docs, Doc::line()).group()
690 }
691
692 fn doc_as_of<'a, T: AstInfo>(&'a self, v: &'a AsOf<T>) -> RcDoc<'a> {
693 let (title, expr) = match v {
694 AsOf::At(expr) => ("AS OF", expr),
695 AsOf::AtLeast(expr) => ("AS OF AT LEAST", expr),
696 };
697 nest_title(title, self.doc_expr(expr))
698 }
699
700 pub(crate) fn doc_create_view<'a, T: AstInfo>(
701 &'a self,
702 v: &'a CreateViewStatement<T>,
703 ) -> RcDoc<'a> {
704 let mut docs = vec![];
705 docs.push(RcDoc::text(format!(
706 "CREATE{}{} VIEW{}",
707 if v.if_exists == IfExistsBehavior::Replace {
708 " OR REPLACE"
709 } else {
710 ""
711 },
712 if v.temporary { " TEMPORARY" } else { "" },
713 if v.if_exists == IfExistsBehavior::Skip {
714 " IF NOT EXISTS"
715 } else {
716 ""
717 },
718 )));
719 docs.push(self.doc_view_definition(&v.definition));
720 intersperse_line_nest(docs)
721 }
722
723 pub(crate) fn doc_create_materialized_view<'a, T: AstInfo>(
724 &'a self,
725 v: &'a CreateMaterializedViewStatement<T>,
726 ) -> RcDoc<'a> {
727 let mut docs = vec![];
728 docs.push(RcDoc::text(format!(
729 "CREATE{}{} MATERIALIZED VIEW{} {}",
730 if v.if_exists == IfExistsBehavior::Replace {
731 " OR REPLACE"
732 } else {
733 ""
734 },
735 if v.replacement_for.is_some() {
736 " REPLACEMENT"
737 } else {
738 ""
739 },
740 if v.if_exists == IfExistsBehavior::Skip {
741 " IF NOT EXISTS"
742 } else {
743 ""
744 },
745 v.name,
746 )));
747 if !v.columns.is_empty() {
748 docs.push(bracket(
749 "(",
750 comma_separate(|c| self.doc_display_pass(c), &v.columns),
751 ")",
752 ));
753 }
754 if let Some(target) = &v.replacement_for {
755 docs.push(RcDoc::text(format!(
756 "FOR {}",
757 target.to_ast_string_simple()
758 )));
759 }
760 match (&v.in_cluster, &v.in_cluster_replica) {
761 (Some(cluster), Some(replica)) => {
762 docs.push(RcDoc::text(format!(
763 "IN CLUSTER {} REPLICA {}",
764 cluster.to_ast_string_simple(),
765 replica.to_ast_string_simple(),
766 )));
767 }
768 (Some(cluster), None) => {
769 docs.push(RcDoc::text(format!(
770 "IN CLUSTER {}",
771 cluster.to_ast_string_simple(),
772 )));
773 }
774 (None, Some(replica)) => {
775 docs.push(RcDoc::text(format!(
776 "IN REPLICA {}",
777 replica.to_ast_string_simple(),
778 )));
779 }
780 (None, None) => {}
781 }
782 if !v.with_options.is_empty() {
783 docs.push(bracket(
784 "WITH (",
785 comma_separate(|wo| self.doc_display_pass(wo), &v.with_options),
786 ")",
787 ));
788 }
789 docs.push(nest_title("AS", self.doc_query(&v.query)));
790 if let Some(time) = &v.as_of {
793 docs.push(RcDoc::text(format!("AS OF {time}")));
794 }
795 intersperse_line_nest(docs)
796 }
797
798 pub(crate) fn doc_create_role<'a>(&'a self, v: &'a CreateRoleStatement) -> RcDoc<'a> {
799 let mut docs = vec![RcDoc::text(format!(
800 "CREATE ROLE {}",
801 v.name.to_ast_string_simple()
802 ))];
803 for option in &v.options {
804 docs.push(self.doc_role_attribute(option));
805 }
806 intersperse_line_nest(docs)
807 }
808
809 pub(crate) fn doc_alter_role<'a, T: AstInfo>(
810 &'a self,
811 v: &'a AlterRoleStatement<T>,
812 ) -> RcDoc<'a> {
813 let mut docs = vec![RcDoc::text(format!(
814 "ALTER ROLE {}",
815 v.name.to_ast_string_simple()
816 ))];
817 match &v.option {
818 AlterRoleOption::Attributes(attrs) => {
819 for attr in attrs {
820 docs.push(self.doc_role_attribute(attr));
821 }
822 }
823 AlterRoleOption::Variable(var) => docs.push(self.doc_display_pass(var)),
826 }
827 intersperse_line_nest(docs)
828 }
829
830 fn doc_role_attribute<'a>(&'a self, attr: &'a RoleAttribute) -> RcDoc<'a> {
834 match attr {
835 RoleAttribute::Password(Some(password)) => RcDoc::text(format!(
836 "PASSWORD '{}'",
837 escape_single_quote_string(password)
838 )),
839 RoleAttribute::Password(None) => RcDoc::text("PASSWORD NULL"),
840 other => self.doc_display_pass(other),
841 }
842 }
843
844 pub(crate) fn doc_declare<'a, T: AstInfo<NestedStatement = Statement<Raw>>>(
849 &'a self,
850 v: &'a DeclareStatement<T>,
851 ) -> RcDoc<'a> {
852 RcDoc::text(format!(
853 "DECLARE {} CURSOR FOR ",
854 v.name.to_ast_string_simple()
855 ))
856 .append(self.to_doc(&v.stmt))
857 }
858
859 pub(crate) fn doc_prepare<'a, T: AstInfo<NestedStatement = Statement<Raw>>>(
862 &'a self,
863 v: &'a PrepareStatement<T>,
864 ) -> RcDoc<'a> {
865 RcDoc::text(format!("PREPARE {} AS ", v.name.to_ast_string_simple()))
866 .append(self.to_doc(&v.stmt))
867 }
868
869 fn doc_view_definition<'a, T: AstInfo>(&'a self, v: &'a ViewDefinition<T>) -> RcDoc<'a> {
870 let mut docs = vec![RcDoc::text(v.name.to_string())];
871 if !v.columns.is_empty() {
872 docs.push(bracket(
873 "(",
874 comma_separate(|c| self.doc_display_pass(c), &v.columns),
875 ")",
876 ));
877 }
878 docs.push(nest_title("AS", self.doc_query(&v.query)));
879 RcDoc::intersperse(docs, Doc::line()).group()
880 }
881
882 pub(crate) fn doc_insert<'a, T: AstInfo>(&'a self, v: &'a InsertStatement<T>) -> RcDoc<'a> {
883 let mut first = vec![RcDoc::text(format!(
884 "INSERT INTO {}",
885 v.table_name.to_ast_string_simple()
886 ))];
887 if !v.columns.is_empty() {
888 first.push(bracket(
889 "(",
890 comma_separate(|c| self.doc_display_pass(c), &v.columns),
891 ")",
892 ));
893 }
894 let sources = match &v.source {
895 InsertSource::Query(query) => self.doc_query(query),
896 InsertSource::DefaultValues => self.doc_display(&v.source, "insert source"),
897 };
898 let mut doc = intersperse_line_nest([intersperse_line_nest(first), sources]);
899 if !v.returning.is_empty() {
900 doc = nest(
901 doc,
902 nest_title(
903 "RETURNING",
904 comma_separate(|r| self.doc_display_pass(r), &v.returning),
905 ),
906 )
907 }
908 doc
909 }
910
911 pub(crate) fn doc_select_statement<'a, T: AstInfo>(
912 &'a self,
913 v: &'a SelectStatement<T>,
914 ) -> RcDoc<'a> {
915 let query = self.doc_query(&v.query);
916 let mut doc = if v.query.body.starts_with_show() {
921 bracket("(", query, ")")
922 } else {
923 query
924 };
925 if let Some(as_of) = &v.as_of {
926 doc = intersperse_line_nest([doc, self.doc_as_of(as_of)]);
927 }
928 doc.group()
929 }
930
931 fn doc_order_by<'a, T: AstInfo>(&'a self, v: &'a [OrderByExpr<T>]) -> RcDoc<'a> {
932 title_comma_separate("ORDER BY", |o| self.doc_order_by_expr(o), v)
933 }
934
935 fn doc_order_by_expr<'a, T: AstInfo>(&'a self, v: &'a OrderByExpr<T>) -> RcDoc<'a> {
936 let doc = self.doc_expr(&v.expr);
937 let doc = match v.asc {
938 Some(true) => nest(doc, RcDoc::text("ASC")),
939 Some(false) => nest(doc, RcDoc::text("DESC")),
940 None => doc,
941 };
942 match v.nulls_last {
943 Some(true) => nest(doc, RcDoc::text("NULLS LAST")),
944 Some(false) => nest(doc, RcDoc::text("NULLS FIRST")),
945 None => doc,
946 }
947 }
948
949 fn doc_query<'a, T: AstInfo>(&'a self, v: &'a Query<T>) -> RcDoc<'a> {
950 let mut docs = vec![];
951 if !v.ctes.is_empty() {
952 match &v.ctes {
953 CteBlock::Simple(ctes) => {
954 docs.push(title_comma_separate("WITH", |cte| self.doc_cte(cte), ctes))
955 }
956 CteBlock::MutuallyRecursive(mutrec) => {
957 let mut doc = RcDoc::text("WITH MUTUALLY RECURSIVE");
958 if !mutrec.options.is_empty() {
959 doc = nest(
960 doc,
961 bracket(
962 "(",
963 comma_separate(|o| self.doc_display_pass(o), &mutrec.options),
964 ")",
965 ),
966 );
967 }
968 docs.push(nest(
969 doc,
970 comma_separate(|c| self.doc_mutually_recursive(c), &mutrec.ctes),
971 ));
972 }
973 }
974 }
975 docs.push(self.doc_set_expr(&v.body));
976 if !v.order_by.is_empty() {
977 docs.push(self.doc_order_by(&v.order_by));
978 }
979
980 let offset = if let Some(offset) = &v.offset {
981 vec![RcDoc::concat([nest_title("OFFSET", self.doc_expr(offset))])]
982 } else {
983 vec![]
984 };
985
986 if let Some(limit) = &v.limit {
987 if limit.with_ties {
988 docs.extend(offset);
989 docs.push(RcDoc::concat([
990 RcDoc::text("FETCH FIRST "),
991 self.doc_expr(&limit.quantity),
992 RcDoc::text(" ROWS WITH TIES"),
993 ]));
994 } else {
995 docs.push(nest_title("LIMIT", self.doc_expr(&limit.quantity)));
996 docs.extend(offset);
997 }
998 } else {
999 docs.extend(offset);
1000 }
1001
1002 RcDoc::intersperse(docs, Doc::line()).group()
1003 }
1004
1005 fn doc_cte<'a, T: AstInfo>(&'a self, v: &'a Cte<T>) -> RcDoc<'a> {
1006 RcDoc::concat([
1007 RcDoc::text(format!("{} AS", v.alias)),
1008 RcDoc::line(),
1009 bracket("(", self.doc_query(&v.query), ")"),
1010 ])
1011 }
1012
1013 fn doc_mutually_recursive<'a, T: AstInfo>(&'a self, v: &'a CteMutRec<T>) -> RcDoc<'a> {
1014 let mut docs = Vec::new();
1015 if !v.columns.is_empty() {
1016 docs.push(bracket(
1017 "(",
1018 comma_separate(|c| self.doc_display_pass(c), &v.columns),
1019 ")",
1020 ));
1021 }
1022 docs.push(bracket("AS (", self.doc_query(&v.query), ")"));
1023 nest(
1024 self.doc_display_pass(&v.name),
1025 RcDoc::intersperse(docs, Doc::line()).group(),
1026 )
1027 }
1028
1029 fn doc_set_expr<'a, T: AstInfo>(&'a self, v: &'a SetExpr<T>) -> RcDoc<'a> {
1030 match v {
1031 SetExpr::Select(v) => self.doc_select(v),
1032 SetExpr::Query(v) => bracket("(", self.doc_query(v), ")"),
1033 SetExpr::SetOperation {
1034 op,
1035 all,
1036 left,
1037 right,
1038 } => {
1039 let all_str = if *all { " ALL" } else { "" };
1040 RcDoc::concat([
1041 self.doc_set_expr(left),
1042 RcDoc::line(),
1043 RcDoc::concat([
1044 RcDoc::text(format!("{}{}", op, all_str)),
1045 RcDoc::line(),
1046 self.doc_set_expr(right),
1047 ])
1048 .nest(TAB)
1049 .group(),
1050 ])
1051 }
1052 SetExpr::Values(v) => self.doc_values(v),
1053 SetExpr::Show(v) => self.doc_display(v, "SHOW"),
1054 SetExpr::Table(v) => nest(RcDoc::text("TABLE"), self.doc_display_pass(v)),
1055 }
1056 .group()
1057 }
1058
1059 fn doc_values<'a, T: AstInfo>(&'a self, v: &'a Values<T>) -> RcDoc<'a> {
1060 let rows =
1061 v.0.iter()
1062 .map(|row| bracket("(", comma_separate(|v| self.doc_expr(v), row), ")"));
1063 RcDoc::concat([RcDoc::text("VALUES"), RcDoc::line(), comma_separated(rows)])
1064 .nest(TAB)
1065 .group()
1066 }
1067
1068 fn doc_table_with_joins<'a, T: AstInfo>(&'a self, v: &'a TableWithJoins<T>) -> RcDoc<'a> {
1069 let mut docs = vec![self.doc_table_factor(&v.relation)];
1070 for j in &v.joins {
1071 docs.push(self.doc_join(j));
1072 }
1073 intersperse_line_nest(docs)
1074 }
1075
1076 fn doc_join<'a, T: AstInfo>(&'a self, v: &'a Join<T>) -> RcDoc<'a> {
1077 let (constraint, name) = match &v.join_operator {
1078 JoinOperator::Inner(constraint) => (constraint, "JOIN"),
1079 JoinOperator::FullOuter(constraint) => (constraint, "FULL JOIN"),
1080 JoinOperator::LeftOuter(constraint) => (constraint, "LEFT JOIN"),
1081 JoinOperator::RightOuter(constraint) => (constraint, "RIGHT JOIN"),
1082 JoinOperator::CrossJoin => return self.doc_display(v, "join operator"),
1083 };
1084 let constraint = match constraint {
1085 JoinConstraint::On(expr) => nest_title("ON", self.doc_expr(expr)),
1086 JoinConstraint::Using { columns, alias } => {
1087 let mut doc = bracket(
1088 "USING(",
1089 comma_separate(|c| self.doc_display_pass(c), columns),
1090 ")",
1091 );
1092 if let Some(alias) = alias {
1093 doc = nest(doc, nest_title("AS", self.doc_display_pass(alias)));
1094 }
1095 doc
1096 }
1097 JoinConstraint::Natural => return self.doc_display(v, "join constraint"),
1098 };
1099 intersperse_line_nest([
1100 RcDoc::text(name),
1101 self.doc_table_factor(&v.relation),
1102 constraint,
1103 ])
1104 }
1105
1106 fn doc_table_factor<'a, T: AstInfo>(&'a self, v: &'a TableFactor<T>) -> RcDoc<'a> {
1107 match v {
1108 TableFactor::Derived {
1109 lateral,
1110 subquery,
1111 alias,
1112 } => {
1113 let prefix = if *lateral { "LATERAL (" } else { "(" };
1114 let mut docs = vec![bracket(prefix, self.doc_query(subquery), ")")];
1115 if let Some(alias) = alias {
1116 docs.push(RcDoc::text(format!("AS {}", alias)));
1117 }
1118 intersperse_line_nest(docs)
1119 }
1120 TableFactor::NestedJoin { join, alias } => {
1121 let mut doc = bracket("(", self.doc_table_with_joins(join), ")");
1122 if let Some(alias) = alias {
1123 doc = nest(doc, RcDoc::text(format!("AS {}", alias)));
1124 }
1125 doc
1126 }
1127 TableFactor::Table { name, alias } => {
1128 let mut doc = self.doc_display_pass(name);
1129 if let Some(alias) = alias {
1130 doc = nest(doc, RcDoc::text(format!("AS {}", alias)));
1131 }
1132 doc
1133 }
1134 _ => self.doc_display(v, "table factor variant"),
1135 }
1136 }
1137
1138 fn doc_distinct<'a, T: AstInfo>(&'a self, v: &'a Distinct<T>) -> RcDoc<'a> {
1139 match v {
1140 Distinct::EntireRow => RcDoc::text("DISTINCT"),
1141 Distinct::On(cols) => bracket(
1142 "DISTINCT ON (",
1143 comma_separate(|c| self.doc_expr(c), cols),
1144 ")",
1145 ),
1146 }
1147 }
1148
1149 fn doc_select<'a, T: AstInfo>(&'a self, v: &'a Select<T>) -> RcDoc<'a> {
1150 let mut docs = vec![];
1151 let mut select = RcDoc::text("SELECT");
1152 if let Some(distinct) = &v.distinct {
1153 select = nest(select, self.doc_distinct(distinct));
1154 }
1155 docs.push(nest_comma_separate(
1156 select,
1157 |s| self.doc_select_item(s),
1158 &v.projection,
1159 ));
1160 if !v.from.is_empty() {
1161 docs.push(title_comma_separate(
1162 "FROM",
1163 |t| self.doc_table_with_joins(t),
1164 &v.from,
1165 ));
1166 }
1167 if let Some(selection) = &v.selection {
1168 docs.push(nest_title("WHERE", self.doc_expr(selection)));
1169 }
1170 if !v.group_by.is_empty() {
1171 docs.push(title_comma_separate(
1172 "GROUP BY",
1173 |e| self.doc_expr(e),
1174 &v.group_by,
1175 ));
1176 }
1177 if let Some(having) = &v.having {
1178 docs.push(nest_title("HAVING", self.doc_expr(having)));
1179 }
1180 if let Some(qualify) = &v.qualify {
1181 docs.push(nest_title("QUALIFY", self.doc_expr(qualify)));
1182 }
1183 if !v.options.is_empty() {
1184 docs.push(bracket(
1185 "OPTIONS (",
1186 comma_separate(|o| self.doc_display_pass(o), &v.options),
1187 ")",
1188 ));
1189 }
1190 RcDoc::intersperse(docs, Doc::line()).group()
1191 }
1192
1193 fn doc_select_item<'a, T: AstInfo>(&'a self, v: &'a SelectItem<T>) -> RcDoc<'a> {
1194 match v {
1195 SelectItem::Expr { expr, alias } => {
1196 let mut doc = self.doc_expr(expr);
1197 if let Some(alias) = alias {
1198 doc = nest(
1199 doc,
1200 RcDoc::concat([RcDoc::text("AS "), self.doc_display_pass(alias)]),
1201 );
1202 }
1203 doc
1204 }
1205 SelectItem::Wildcard => self.doc_display_pass(v),
1206 }
1207 }
1208
1209 pub fn doc_expr<'a, T: AstInfo>(&'a self, v: &'a Expr<T>) -> RcDoc<'a> {
1210 match v {
1211 Expr::Op { op, expr1, expr2 } => {
1212 if let Some(expr2) = expr2 {
1213 RcDoc::concat([
1214 self.doc_expr(expr1),
1215 RcDoc::line(),
1216 RcDoc::text(format!("{} ", op)),
1217 self.doc_expr(expr2).nest(TAB),
1218 ])
1219 } else {
1220 let needs_parens = {
1227 let mut e = expr1.as_ref();
1228 let mut saw_postfix = false;
1229 loop {
1230 match e {
1231 Expr::Cast { expr, .. } | Expr::Subscript { expr, .. } => {
1232 saw_postfix = true;
1233 e = expr.as_ref();
1234 }
1235 Expr::Value(Value::Number(_)) => break saw_postfix,
1236 Expr::Op { expr2: None, .. } | Expr::Not { .. } => break false,
1240 Expr::Value(_)
1241 | Expr::Identifier(_)
1242 | Expr::QualifiedWildcard(_)
1243 | Expr::Parameter(_)
1244 | Expr::Function(_)
1245 | Expr::HomogenizingFunction { .. }
1246 | Expr::NullIf { .. }
1247 | Expr::Subquery(_)
1248 | Expr::Exists(_)
1249 | Expr::Nested(_)
1250 | Expr::Array(_)
1251 | Expr::ArraySubquery(_)
1252 | Expr::List(_)
1253 | Expr::ListSubquery(_)
1254 | Expr::Map(_)
1255 | Expr::MapSubquery(_)
1256 | Expr::Case { .. }
1257 | Expr::Row { .. } => break false,
1258 _ => break true,
1259 }
1260 }
1261 };
1262 let operand = if needs_parens {
1263 bracket("(", self.doc_expr(expr1), ")")
1264 } else {
1265 self.doc_expr(expr1)
1266 };
1267 RcDoc::concat([RcDoc::text(format!("{} ", op)), operand])
1268 }
1269 }
1270 Expr::Case {
1271 operand,
1272 conditions,
1273 results,
1274 else_result,
1275 } => {
1276 let mut docs = Vec::new();
1277 if let Some(operand) = operand {
1278 docs.push(self.doc_expr(operand));
1279 }
1280 for (c, r) in conditions.iter().zip_eq(results) {
1281 let when = nest_title("WHEN", self.doc_expr(c));
1282 let then = nest_title("THEN", self.doc_expr(r));
1283 docs.push(nest(when, then));
1284 }
1285 if let Some(else_result) = else_result {
1286 docs.push(nest_title("ELSE", self.doc_expr(else_result)));
1287 }
1288 let doc = intersperse_line_nest(docs);
1289 bracket_doc(RcDoc::text("CASE"), doc, RcDoc::text("END"), RcDoc::line())
1290 }
1291 Expr::Cast { expr, data_type } => {
1292 let doc = self.doc_expr(expr);
1293 RcDoc::concat([
1294 doc,
1295 RcDoc::text(format!("::{}", data_type.to_ast_string_simple())),
1296 ])
1297 }
1298 Expr::Nested(ast) => bracket("(", self.doc_expr(ast), ")"),
1299 Expr::Function(fun) => self.doc_function(fun),
1300 Expr::Subquery(ast) => bracket("(", self.doc_query(ast), ")"),
1301 Expr::Identifier(_)
1302 | Expr::Value(_)
1303 | Expr::QualifiedWildcard(_)
1304 | Expr::WildcardAccess(_)
1305 | Expr::FieldAccess { .. } => self.doc_display_pass(v),
1306 Expr::And { left, right } => bracket_doc(
1307 self.doc_expr(left),
1308 RcDoc::text("AND"),
1309 self.doc_expr(right),
1310 RcDoc::line(),
1311 ),
1312 Expr::Or { left, right } => bracket_doc(
1313 self.doc_expr(left),
1314 RcDoc::text("OR"),
1315 self.doc_expr(right),
1316 RcDoc::line(),
1317 ),
1318 Expr::Exists(s) => bracket("EXISTS (", self.doc_query(s), ")"),
1319 Expr::IsExpr {
1320 expr,
1321 negated,
1322 construct,
1323 } => bracket_doc(
1324 self.doc_expr(expr),
1325 RcDoc::text(if *negated { "IS NOT" } else { "IS" }),
1326 self.doc_display_pass(construct),
1327 RcDoc::line(),
1328 ),
1329 Expr::Not { expr } => {
1330 RcDoc::concat([RcDoc::text("NOT"), RcDoc::line(), self.doc_expr(expr)])
1331 }
1332 Expr::Between {
1333 expr,
1334 negated,
1335 low,
1336 high,
1337 } => RcDoc::intersperse(
1338 [
1339 self.doc_expr(expr),
1340 RcDoc::text(if *negated { "NOT BETWEEN" } else { "BETWEEN" }),
1341 RcDoc::intersperse(
1342 [self.doc_expr(low), RcDoc::text("AND"), self.doc_expr(high)],
1343 RcDoc::line(),
1344 )
1345 .group(),
1346 ],
1347 RcDoc::line(),
1348 ),
1349 Expr::InSubquery {
1350 expr,
1351 subquery,
1352 negated,
1353 } => RcDoc::intersperse(
1354 [
1355 self.doc_expr(expr),
1356 RcDoc::text(if *negated { "NOT IN (" } else { "IN (" }),
1357 self.doc_query(subquery),
1358 RcDoc::text(")"),
1359 ],
1360 RcDoc::line(),
1361 ),
1362 Expr::InList {
1363 expr,
1364 list,
1365 negated,
1366 } => RcDoc::intersperse(
1367 [
1368 self.doc_expr(expr),
1369 RcDoc::text(if *negated { "NOT IN (" } else { "IN (" }),
1370 comma_separate(|e| self.doc_expr(e), list),
1371 RcDoc::text(")"),
1372 ],
1373 RcDoc::line(),
1374 ),
1375 Expr::Row { exprs } => {
1376 bracket("ROW(", comma_separate(|e| self.doc_expr(e), exprs), ")")
1377 }
1378 Expr::NullIf { l_expr, r_expr } => bracket(
1379 "NULLIF (",
1380 comma_separate(|e| self.doc_expr(e), [&**l_expr, &**r_expr]),
1381 ")",
1382 ),
1383 Expr::HomogenizingFunction { function, exprs } => bracket(
1384 format!("{function}("),
1385 comma_separate(|e| self.doc_expr(e), exprs),
1386 ")",
1387 ),
1388 Expr::ArraySubquery(s) => bracket("ARRAY(", self.doc_query(s), ")"),
1389 Expr::ListSubquery(s) => bracket("LIST(", self.doc_query(s), ")"),
1390 Expr::Array(exprs) => {
1391 bracket("ARRAY[", comma_separate(|e| self.doc_expr(e), exprs), "]")
1392 }
1393 Expr::List(exprs) => bracket("LIST[", comma_separate(|e| self.doc_expr(e), exprs), "]"),
1394 _ => self.doc_display(v, "expr variant"),
1395 }
1396 .group()
1397 }
1398
1399 fn doc_function<'a, T: AstInfo>(&'a self, v: &'a Function<T>) -> RcDoc<'a> {
1400 match &v.args {
1401 FunctionArgs::Star => self.doc_display_pass(v),
1402 FunctionArgs::Args { args, order_by } => {
1403 if args.is_empty() {
1404 return self.doc_display_pass(v);
1406 }
1407 if v.filter.is_some() || v.over.is_some() || !order_by.is_empty() {
1408 return self.doc_display(v, "function filter or over or order by");
1409 }
1410 let name_stable = v.name.to_ast_string_stable();
1411 let special = match name_stable.as_str() {
1412 r#""extract""# if v.args.len() == Some(2) => true,
1413 r#""position""# if v.args.len() == Some(2) => true,
1414 _ => false,
1415 };
1416 if special {
1417 return self.doc_display(v, "special function");
1418 }
1419 let needs_quote = matches!(
1428 name_stable.as_str(),
1429 r#""array""#
1430 | r#""coalesce""#
1431 | r#""exists""#
1432 | r#""extract""#
1433 | r#""greatest""#
1434 | r#""least""#
1435 | r#""list""#
1436 | r#""map""#
1437 | r#""normalize""#
1438 | r#""nullif""#
1439 | r#""position""#
1440 | r#""row""#
1441 | r#""substring""#
1442 | r#""trim""#
1443 );
1444 let printed_name = if needs_quote {
1445 name_stable
1446 } else {
1447 v.name.to_ast_string_simple()
1448 };
1449 let name = format!(
1450 "{}({}",
1451 printed_name,
1452 if v.distinct { "DISTINCT " } else { "" }
1453 );
1454 bracket(name, comma_separate(|e| self.doc_expr(e), args), ")")
1455 }
1456 }
1457 }
1458}