Skip to content

Commit ae587dc

Browse files
authored
pretty print improvements (#1851)
1 parent 3c59950 commit ae587dc

File tree

6 files changed

+332
-67
lines changed

6 files changed

+332
-67
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,14 @@ keywords, the following should hold true for all SQL:
8989

9090
```rust
9191
// Parse SQL
92+
let sql = "SELECT 'hello'";
9293
let ast = Parser::parse_sql(&GenericDialect, sql).unwrap();
9394

9495
// The original SQL text can be generated from the AST
9596
assert_eq!(ast[0].to_string(), sql);
97+
98+
// The SQL can also be pretty-printed with newlines and indentation
99+
assert_eq!(format!("{:#}", ast[0]), "SELECT\n 'hello'");
96100
```
97101

98102
There are still some cases in this crate where different SQL with seemingly

src/ast/dml.rs

+39-18
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ use serde::{Deserialize, Serialize};
2929
#[cfg(feature = "visitor")]
3030
use sqlparser_derive::{Visit, VisitMut};
3131

32+
use crate::display_utils::{indented_list, Indent, SpaceOrNewline};
33+
3234
pub use super::ddl::{ColumnDef, TableConstraint};
3335

3436
use super::{
@@ -579,28 +581,32 @@ impl Display for Insert {
579581
)?;
580582
}
581583
if !self.columns.is_empty() {
582-
write!(f, "({}) ", display_comma_separated(&self.columns))?;
584+
write!(f, "({})", display_comma_separated(&self.columns))?;
585+
SpaceOrNewline.fmt(f)?;
583586
}
584587
if let Some(ref parts) = self.partitioned {
585588
if !parts.is_empty() {
586-
write!(f, "PARTITION ({}) ", display_comma_separated(parts))?;
589+
write!(f, "PARTITION ({})", display_comma_separated(parts))?;
590+
SpaceOrNewline.fmt(f)?;
587591
}
588592
}
589593
if !self.after_columns.is_empty() {
590-
write!(f, "({}) ", display_comma_separated(&self.after_columns))?;
594+
write!(f, "({})", display_comma_separated(&self.after_columns))?;
595+
SpaceOrNewline.fmt(f)?;
591596
}
592597

593598
if let Some(settings) = &self.settings {
594-
write!(f, "SETTINGS {} ", display_comma_separated(settings))?;
599+
write!(f, "SETTINGS {}", display_comma_separated(settings))?;
600+
SpaceOrNewline.fmt(f)?;
595601
}
596602

597603
if let Some(source) = &self.source {
598-
write!(f, "{source}")?;
604+
source.fmt(f)?;
599605
} else if !self.assignments.is_empty() {
600-
write!(f, "SET ")?;
601-
write!(f, "{}", display_comma_separated(&self.assignments))?;
606+
write!(f, "SET")?;
607+
indented_list(f, &self.assignments)?;
602608
} else if let Some(format_clause) = &self.format_clause {
603-
write!(f, "{format_clause}")?;
609+
format_clause.fmt(f)?;
604610
} else if self.columns.is_empty() {
605611
write!(f, "DEFAULT VALUES")?;
606612
}
@@ -620,7 +626,9 @@ impl Display for Insert {
620626
}
621627

622628
if let Some(returning) = &self.returning {
623-
write!(f, " RETURNING {}", display_comma_separated(returning))?;
629+
SpaceOrNewline.fmt(f)?;
630+
f.write_str("RETURNING")?;
631+
indented_list(f, returning)?;
624632
}
625633
Ok(())
626634
}
@@ -649,32 +657,45 @@ pub struct Delete {
649657

650658
impl Display for Delete {
651659
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
652-
write!(f, "DELETE ")?;
660+
f.write_str("DELETE")?;
653661
if !self.tables.is_empty() {
654-
write!(f, "{} ", display_comma_separated(&self.tables))?;
662+
indented_list(f, &self.tables)?;
655663
}
656664
match &self.from {
657665
FromTable::WithFromKeyword(from) => {
658-
write!(f, "FROM {}", display_comma_separated(from))?;
666+
f.write_str(" FROM")?;
667+
indented_list(f, from)?;
659668
}
660669
FromTable::WithoutKeyword(from) => {
661-
write!(f, "{}", display_comma_separated(from))?;
670+
indented_list(f, from)?;
662671
}
663672
}
664673
if let Some(using) = &self.using {
665-
write!(f, " USING {}", display_comma_separated(using))?;
674+
SpaceOrNewline.fmt(f)?;
675+
f.write_str("USING")?;
676+
indented_list(f, using)?;
666677
}
667678
if let Some(selection) = &self.selection {
668-
write!(f, " WHERE {selection}")?;
679+
SpaceOrNewline.fmt(f)?;
680+
f.write_str("WHERE")?;
681+
SpaceOrNewline.fmt(f)?;
682+
Indent(selection).fmt(f)?;
669683
}
670684
if let Some(returning) = &self.returning {
671-
write!(f, " RETURNING {}", display_comma_separated(returning))?;
685+
SpaceOrNewline.fmt(f)?;
686+
f.write_str("RETURNING")?;
687+
indented_list(f, returning)?;
672688
}
673689
if !self.order_by.is_empty() {
674-
write!(f, " ORDER BY {}", display_comma_separated(&self.order_by))?;
690+
SpaceOrNewline.fmt(f)?;
691+
f.write_str("ORDER BY")?;
692+
indented_list(f, &self.order_by)?;
675693
}
676694
if let Some(limit) = &self.limit {
677-
write!(f, " LIMIT {limit}")?;
695+
SpaceOrNewline.fmt(f)?;
696+
f.write_str("LIMIT")?;
697+
SpaceOrNewline.fmt(f)?;
698+
Indent(limit).fmt(f)?;
678699
}
679700
Ok(())
680701
}

src/ast/mod.rs

+24-12
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use serde::{Deserialize, Serialize};
4141
use sqlparser_derive::{Visit, VisitMut};
4242

4343
use crate::{
44-
display_utils::SpaceOrNewline,
44+
display_utils::{indented_list, SpaceOrNewline},
4545
tokenizer::{Span, Token},
4646
};
4747
use crate::{
@@ -4548,7 +4548,7 @@ impl fmt::Display for Statement {
45484548
}
45494549
Ok(())
45504550
}
4551-
Statement::Insert(insert) => write!(f, "{insert}"),
4551+
Statement::Insert(insert) => insert.fmt(f),
45524552
Statement::Install {
45534553
extension_name: name,
45544554
} => write!(f, "INSTALL {name}"),
@@ -4611,30 +4611,42 @@ impl fmt::Display for Statement {
46114611
returning,
46124612
or,
46134613
} => {
4614-
write!(f, "UPDATE ")?;
4614+
f.write_str("UPDATE ")?;
46154615
if let Some(or) = or {
4616-
write!(f, "{or} ")?;
4616+
or.fmt(f)?;
4617+
f.write_str(" ")?;
46174618
}
4618-
write!(f, "{table}")?;
4619+
table.fmt(f)?;
46194620
if let Some(UpdateTableFromKind::BeforeSet(from)) = from {
4620-
write!(f, " FROM {}", display_comma_separated(from))?;
4621+
SpaceOrNewline.fmt(f)?;
4622+
f.write_str("FROM")?;
4623+
indented_list(f, from)?;
46214624
}
46224625
if !assignments.is_empty() {
4623-
write!(f, " SET {}", display_comma_separated(assignments))?;
4626+
SpaceOrNewline.fmt(f)?;
4627+
f.write_str("SET")?;
4628+
indented_list(f, assignments)?;
46244629
}
46254630
if let Some(UpdateTableFromKind::AfterSet(from)) = from {
4626-
write!(f, " FROM {}", display_comma_separated(from))?;
4631+
SpaceOrNewline.fmt(f)?;
4632+
f.write_str("FROM")?;
4633+
indented_list(f, from)?;
46274634
}
46284635
if let Some(selection) = selection {
4629-
write!(f, " WHERE {selection}")?;
4636+
SpaceOrNewline.fmt(f)?;
4637+
f.write_str("WHERE")?;
4638+
SpaceOrNewline.fmt(f)?;
4639+
Indent(selection).fmt(f)?;
46304640
}
46314641
if let Some(returning) = returning {
4632-
write!(f, " RETURNING {}", display_comma_separated(returning))?;
4642+
SpaceOrNewline.fmt(f)?;
4643+
f.write_str("RETURNING")?;
4644+
indented_list(f, returning)?;
46334645
}
46344646
Ok(())
46354647
}
4636-
Statement::Delete(delete) => write!(f, "{delete}"),
4637-
Statement::Open(open) => write!(f, "{open}"),
4648+
Statement::Delete(delete) => delete.fmt(f),
4649+
Statement::Open(open) => open.fmt(f),
46384650
Statement::Close { cursor } => {
46394651
write!(f, "CLOSE {cursor}")?;
46404652

src/ast/query.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -2888,13 +2888,14 @@ pub struct Values {
28882888

28892889
impl fmt::Display for Values {
28902890
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2891-
write!(f, "VALUES ")?;
2891+
f.write_str("VALUES")?;
28922892
let prefix = if self.explicit_row { "ROW" } else { "" };
28932893
let mut delim = "";
28942894
for row in &self.rows {
2895-
write!(f, "{delim}")?;
2896-
delim = ", ";
2897-
write!(f, "{prefix}({})", display_comma_separated(row))?;
2895+
f.write_str(delim)?;
2896+
delim = ",";
2897+
SpaceOrNewline.fmt(f)?;
2898+
Indent(format_args!("{prefix}({})", display_comma_separated(row))).fmt(f)?;
28982899
}
28992900
Ok(())
29002901
}

src/display_utils.rs

+18-33
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,10 @@ where
3131
T: Write,
3232
{
3333
fn write_str(&mut self, s: &str) -> fmt::Result {
34-
let mut first = true;
35-
for line in s.split('\n') {
36-
if !first {
37-
write!(self.0, "\n{INDENT}")?;
38-
}
39-
self.0.write_str(line)?;
40-
first = false;
34+
self.0.write_str(s)?;
35+
// Our NewLine and SpaceOrNewline utils always print individual newlines as a single-character string.
36+
if s == "\n" {
37+
self.0.write_str(INDENT)?;
4138
}
4239
Ok(())
4340
}
@@ -71,7 +68,7 @@ impl Display for SpaceOrNewline {
7168

7269
/// A value that displays a comma-separated list of values.
7370
/// When pretty-printed (using {:#}), it displays each value on a new line.
74-
pub struct DisplayCommaSeparated<'a, T: fmt::Display>(&'a [T]);
71+
pub(crate) struct DisplayCommaSeparated<'a, T: fmt::Display>(&'a [T]);
7572

7673
impl<T: fmt::Display> fmt::Display for DisplayCommaSeparated<'_, T> {
7774
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -89,45 +86,33 @@ impl<T: fmt::Display> fmt::Display for DisplayCommaSeparated<'_, T> {
8986
}
9087

9188
/// Displays a whitespace, followed by a comma-separated list that is indented when pretty-printed.
92-
pub(crate) fn indented_list<T: fmt::Display>(f: &mut fmt::Formatter, slice: &[T]) -> fmt::Result {
89+
pub(crate) fn indented_list<T: fmt::Display>(f: &mut fmt::Formatter, items: &[T]) -> fmt::Result {
9390
SpaceOrNewline.fmt(f)?;
94-
Indent(DisplayCommaSeparated(slice)).fmt(f)
91+
Indent(DisplayCommaSeparated(items)).fmt(f)
9592
}
9693

9794
#[cfg(test)]
9895
mod tests {
9996
use super::*;
10097

101-
struct DisplayCharByChar<T: Display>(T);
98+
#[test]
99+
fn test_indent() {
100+
struct TwoLines;
102101

103-
impl<T: Display> Display for DisplayCharByChar<T> {
104-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105-
for c in self.0.to_string().chars() {
106-
write!(f, "{}", c)?;
102+
impl Display for TwoLines {
103+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104+
f.write_str("line 1")?;
105+
SpaceOrNewline.fmt(f)?;
106+
f.write_str("line 2")
107107
}
108-
Ok(())
109108
}
110-
}
111109

112-
#[test]
113-
fn test_indent() {
114-
let original = "line 1\nline 2";
115-
let indent = Indent(original);
110+
let indent = Indent(TwoLines);
116111
assert_eq!(
117112
indent.to_string(),
118-
original,
113+
TwoLines.to_string(),
119114
"Only the alternate form should be indented"
120115
);
121-
let expected = " line 1\n line 2";
122-
assert_eq!(format!("{:#}", indent), expected);
123-
let display_char_by_char = DisplayCharByChar(original);
124-
assert_eq!(format!("{:#}", Indent(display_char_by_char)), expected);
125-
}
126-
127-
#[test]
128-
fn test_space_or_newline() {
129-
let space_or_newline = SpaceOrNewline;
130-
assert_eq!(format!("{}", space_or_newline), " ");
131-
assert_eq!(format!("{:#}", space_or_newline), "\n");
116+
assert_eq!(format!("{:#}", indent), " line 1\n line 2");
132117
}
133118
}

0 commit comments

Comments
 (0)