Skip to main content

rudb_parse/
deparse.rs

1//! An [`Ast`] written back out as SQL, the way DuckDB writes one.
2//!
3//! `duckdb_views().sql` is a deparse of the body rather than the text somebody typed, which was
4//! measured: a view created with odd spacing, lower case type names and a comment in the middle
5//! comes back normalised and without the comment. So the column needs a writer, and the writer has
6//! to agree with the pin character for character or the column is a divergence on every view a
7//! harness looks at.
8//!
9//! # This is not a pretty printer and it is not the printer in `transform`'s tests
10//!
11//! Two things are going on in the pin's output and only one of them is printing. `count(*)` comes
12//! back as `count_star()`, `x IS TRUE` as `(CAST(x AS BOOLEAN) IS NOT DISTINCT FROM true)`,
13//! `s LIKE 'a'` as `(s ~~ 'a')`, `[1, 2]` as `list_value(1, 2)`, `x IN (SELECT ...)` as
14//! `(x = ANY(SELECT ...))` and a simple `CASE x WHEN 1` as a searched `CASE` with an `ELSE NULL`
15//! nobody wrote. Those are rewrites DuckDB's transformer does on the way in, and what gets printed
16//! is the rewritten tree. rudb's AST keeps the written form, deliberately, because an error message
17//! should say what was written. So the rewrites happen here, at the point of printing, and every one
18//! of them is a line in this file with the measurement it came from next to it.
19//!
20//! `transform`'s tests have a printer of their own and it stays. It answers a different question:
21//! what shape did the transform produce. Printing `IS TRUE` as a cast and a distinct test would hide
22//! exactly the bug those tests are there to catch.
23//!
24//! # The parentheses
25//!
26//! Every binary operation is parenthesised, whatever the precedence, so `x + y * 2 - 1` is
27//! `((x + (y * 2)) - 1)`. Every unary one parenthesises its operand instead, so `-x` is `-(x)`. That
28//! is upstream's rule and it is also the only rule that is safe without a precedence table, since a
29//! printer that leaves parentheses out has to be right about precedence in both directions.
30//!
31//! A few of the quirks that follow from printing this way are upstream's rather than anybody's
32//! design, and they are reproduced because the column is a comparison. `CASE` is followed by two
33//! spaces, because the slot for the operand of a simple `CASE` is filled in unconditionally and a
34//! searched one leaves it empty. A `FROM` list has a space before each comma. A chain of three set
35//! operations loses the space before the second operator.
36//!
37//! # What does not agree yet
38//!
39//! Three things, and none of them is a printing question. Each one is a place where rudb's transform
40//! threw away something the pin kept, so the answer is in `transform` and not here, and each has an
41//! issue of its own. Two hundred and sixty two view bodies were measured against the pin and these
42//! four lines are what is left over.
43//!
44//! `^` and `**` are one [`BinaryOp::Power`] here and two operators there, and the pin keeps whichever
45//! was written all the way down to the function it resolves: `[1] ^ [2]` and `[1] ** [2]` fail with
46//! different names in the message. A view written with one comes back with the other.
47//!
48//! `LIMIT ALL` is dropped by the transform, since it means no limit, and the pin writes it back as
49//! `LIMIT NULL`.
50//!
51//! A subscript is rewritten by the transform into the call it stands for, so `[1, 2][1]` is
52//! `array_extract(list_value(1, 2), 1)` here and `list_value(1, 2)[1]` there. The pin does the same
53//! rewrite at bind time and prints the subscript, so this one is a matter of doing it later.
54//!
55//! # What the parser cannot reach yet
56//!
57//! A body the parser refuses never gets here, so none of the following is a divergence today. They
58//! were measured anyway, at the same time as the rest, because the measurement is the expensive part
59//! and whoever adds the syntax will need the answer. A window is printed with its clause spelled out
60//! and a named one is inlined, so `OVER w` with `WINDOW w AS (ORDER BY x)` is `OVER (ORDER BY x)`. A
61//! `FILTER` keeps its own parentheses and parenthesises the condition inside them. `EXISTS` is
62//! written without a space before the parenthesis. `x IN (SELECT ...)` is `(x = ANY(SELECT ...))` and
63//! `x > ALL (SELECT ...)` is `(NOT (x <= ANY(SELECT ...)))`. A `WITH` loses the space after the last
64//! bracket, so it reads `WITH a AS (SELECT 1 AS n)SELECT n FROM a`, and a recursive one writes the
65//! column list as ` (n)` with a space. `LATERAL` goes. `TABLESAMPLE 10 PERCENT` is `TABLESAMPLE
66//! System(10.0 PERCENT)`. `CUBE (x, y)` and `ROLLUP (x, y)` are both written out as the
67//! `GROUPING SETS` they stand for. `{'a': 1}` is `struct_pack(a := 1)` and `MAP {'a': 1}` is
68//! `"map"(list_value('a'), list_value(1))`. A list comprehension is expanded into the three nested
69//! lambdas it is made of.
70
71use crate::ast::{
72    Ast, BinaryOp, CaseArm, CreateViewRef, Distinct, Expr, ExprRef, JoinKind, LiteralKind, Nulls,
73    Order, OrderItem, Quantifier, QueryBody, QueryRef, SelectRef, SetOp, Slice, Source, SourceRef,
74    StrRef, Target, UnaryOp,
75};
76use crate::matcher::NONE;
77use crate::tokenize::quoted;
78
79/// A `CREATE VIEW` written back out, which is what `duckdb_views()` reports as `sql`.
80///
81/// The name loses its qualification, which was measured: `CREATE VIEW main.v AS ...` comes back as
82/// `CREATE VIEW v AS ...`. So does `OR REPLACE` and so does `IF NOT EXISTS`, since what the column
83/// answers is what this view is and not what the statement that made it asked for.
84#[must_use]
85pub fn create_view(ast: &Ast, index: CreateViewRef) -> String {
86    let written = ast.create_view(index);
87    let name = ast.name(written.name).last().unwrap_or_default();
88    let temporary = if written.temporary { "TEMP " } else { "" };
89    let mut out = format!("CREATE {temporary}VIEW {}", quoted(name));
90    if !written.columns.is_empty() {
91        // A space before the parenthesis, where `CREATE TABLE t(x INTEGER)` has none. Both were
92        // measured and they really do differ.
93        out += &format!(" ({})", names(ast, written.columns));
94    }
95    out + &format!(" AS {};", query(ast, written.query))
96}
97
98/// One query written back out.
99#[must_use]
100pub fn query(ast: &Ast, index: QueryRef) -> String {
101    let held = ast.query(index);
102    let mut out = match held.body {
103        QueryBody::Select(select) => selection(ast, select),
104        QueryBody::SetOp { op, quantifier, by_name, left, right } => {
105            setop(ast, op, quantifier, by_name, left, right)
106        }
107        // A `VALUES` on its own becomes a select over it, named the way upstream names it. The name
108        // is not a choice here: `CREATE VIEW v AS VALUES (1)` comes back with `AS valueslist` on it.
109        QueryBody::Values(rows) => format!("SELECT * FROM ({}) AS valueslist", values(ast, rows)),
110        QueryBody::Describe(inner) => format!("DESCRIBE ({})", query(ast, inner)),
111    };
112    if held.order_by_all {
113        // `ORDER BY ALL` is a star over the columns by the time it is printed.
114        out += " ORDER BY COLUMNS(*)";
115    } else if !held.order_by.is_empty() {
116        let items: Vec<String> =
117            ast.order_list(held.order_by).iter().map(|item| order(ast, item)).collect();
118        out += &format!(" ORDER BY {}", items.join(", "));
119    }
120    if held.limit != NONE {
121        // `LIMIT 10 PERCENT` comes back as `LIMIT (10) %`, which is upstream writing the percent
122        // sign into the slot an operator goes in and getting the spacing wrong. Reproduced.
123        if held.limit_percent {
124            out += &format!(" LIMIT ({}) %", expr(ast, held.limit));
125        } else {
126            out += &format!(" LIMIT {}", expr(ast, held.limit));
127        }
128    }
129    if held.offset != NONE {
130        out += &format!(" OFFSET {}", expr(ast, held.offset));
131    }
132    out
133}
134
135/// A set operation, with the spacing bug upstream has in it.
136///
137/// Each side is wrapped in parentheses unless it is itself a set operation, in which case it is
138/// written bare. The bare case also loses the space that would follow it, which is why
139/// `a UNION b UNION c` comes back as `(a) UNION (b)UNION (c)` and not with a space there. That is
140/// the pin's output and it is a comparison, so it is what this writes.
141fn setop(
142    ast: &Ast,
143    op: SetOp,
144    quantifier: Quantifier,
145    by_name: bool,
146    left: QueryRef,
147    right: QueryRef,
148) -> String {
149    let word = match op {
150        SetOp::Union => "UNION",
151        SetOp::Except => "EXCEPT",
152        SetOp::Intersect => "INTERSECT",
153    };
154    // `UNION DISTINCT` comes back as `UNION`, since distinct is what the operator does anyway.
155    let all = if matches!(quantifier, Quantifier::All) { " ALL" } else { "" };
156    let named = if by_name { " BY NAME" } else { "" };
157    format!("{}{word}{all}{named} {}", branch(ast, left, true), branch(ast, right, false))
158}
159
160/// One side of a set operation, parenthesised unless it is a set operation itself.
161fn branch(ast: &Ast, index: QueryRef, left: bool) -> String {
162    let text = query(ast, index);
163    if matches!(ast.query(index).body, QueryBody::SetOp { .. }) {
164        return text;
165    }
166    if left { format!("({text}) ") } else { format!("({text})") }
167}
168
169/// One select block, without the modifiers that hang off the query around it.
170fn selection(ast: &Ast, index: SelectRef) -> String {
171    let held = ast.select(index);
172    let mut out = "SELECT".to_string();
173    match held.distinct {
174        Distinct::No => {}
175        Distinct::Yes => out += " DISTINCT",
176        Distinct::On(list) => out += &format!(" DISTINCT ON ({})", exprs(ast, list)),
177    }
178    let targets: Vec<String> =
179        ast.target_list(held.targets).iter().map(|target| aliased(ast, target)).collect();
180    out += &format!(" {}", targets.join(", "));
181    if !held.from.is_empty() {
182        // A space before the comma, which is upstream's and was measured on `FROM t t1, t t2`.
183        let sources: Vec<String> =
184            ast.source_list(held.from).iter().map(|&index| source(ast, index)).collect();
185        out += &format!(" FROM {}", sources.join(" , "));
186    }
187    if held.filter != NONE {
188        out += &format!(" WHERE {}", expr(ast, held.filter));
189    }
190    if held.group_by_all {
191        out += " GROUP BY ALL";
192    } else if !held.group_by.is_empty() {
193        out += &format!(" GROUP BY {}", exprs(ast, held.group_by));
194    }
195    if held.having != NONE {
196        out += &format!(" HAVING {}", expr(ast, held.having));
197    }
198    out
199}
200
201/// One entry of a target list, with its alias if it was given one.
202fn aliased(ast: &Ast, target: &Target) -> String {
203    let written = expr(ast, target.expr);
204    if target.alias == NONE {
205        return written;
206    }
207    format!("{written} AS {}", quoted(ast.string(target.alias)))
208}
209
210/// One entry of an order by list.
211fn order(ast: &Ast, item: &OrderItem) -> String {
212    let mut out = expr(ast, item.expr);
213    match item.order {
214        Order::Unstated => {}
215        Order::Ascending => out += " ASC",
216        Order::Descending => out += " DESC",
217    }
218    match item.nulls {
219        Nulls::Unstated => {}
220        Nulls::First => out += " NULLS FIRST",
221        Nulls::Last => out += " NULLS LAST",
222    }
223    out
224}
225
226/// One entry of a `FROM` clause.
227fn source(ast: &Ast, index: SourceRef) -> String {
228    match ast.source(index) {
229        Source::Table { name, alias, columns } => label(ast, parts(ast, name), alias, columns),
230        Source::Subquery { query: inner, alias, columns } => {
231            label(ast, format!("({})", query(ast, inner)), alias, columns)
232        }
233        Source::Function { name, args, alias, columns } => {
234            let written: Vec<String> =
235                ast.target_list(args).iter().map(|arg| argument(ast, arg)).collect();
236            let call = format!("{}({})", parts(ast, name), written.join(", "));
237            label(ast, call, alias, columns)
238        }
239        // A `VALUES` in a `FROM` clause is wrapped in a select of its own, named `valueslist`, and
240        // then given whatever alias was written. Measured, including the name.
241        Source::Values { rows, alias, columns } => {
242            let inner = format!("(SELECT * FROM ({}) AS valueslist)", values(ast, rows));
243            label(ast, inner, alias, columns)
244        }
245        Source::Join { left, right, kind, natural, on, using } => {
246            let word = match kind {
247                JoinKind::Inner => "INNER",
248                JoinKind::Left => "LEFT",
249                JoinKind::Right => "RIGHT",
250                // `FULL OUTER JOIN` loses the `OUTER`, and a `NATURAL JOIN` gains an `INNER`.
251                JoinKind::Full => "FULL",
252                JoinKind::Semi => "SEMI",
253                JoinKind::Anti => "ANTI",
254                JoinKind::Cross => "CROSS",
255                JoinKind::Positional => "POSITIONAL",
256            };
257            let natural = if natural { "NATURAL " } else { "" };
258            let mut out =
259                format!("({} {natural}{word} JOIN {}", source(ast, left), source(ast, right));
260            if on != NONE {
261                // A second pair of parentheses around a condition that has its own, so an equality
262                // comes out as `ON ((a.x = b.y))`.
263                out += &format!(" ON ({})", expr(ast, on));
264            }
265            if !using.is_empty() {
266                out += &format!(" USING ({})", names(ast, using));
267            }
268            out + ")"
269        }
270    }
271}
272
273/// One argument of a table function, which is an expression or a name and an expression.
274///
275/// A named one is parenthesised and written with `=`, so `read_csv(f, header = true)` comes back as
276/// `read_csv(f, ("header" = true))`. The name goes through the quoting rule like any other
277/// identifier, which is why `header` gains quotes there.
278fn argument(ast: &Ast, arg: &Target) -> String {
279    if arg.alias == NONE {
280        return expr(ast, arg.expr);
281    }
282    format!("({} = {})", quoted(ast.string(arg.alias)), expr(ast, arg.expr))
283}
284
285/// A from item with its alias and column list, if it was given either.
286fn label(ast: &Ast, written: String, alias: StrRef, columns: Slice) -> String {
287    let mut out = written;
288    if alias != NONE {
289        out += &format!(" AS {}", quoted(ast.string(alias)));
290    }
291    if !columns.is_empty() {
292        out += &format!("({})", names(ast, columns));
293    }
294    out
295}
296
297/// The rows of a `VALUES`, with the keyword in front of them.
298fn values(ast: &Ast, rows: Slice) -> String {
299    let written: Vec<String> =
300        ast.rows(rows).iter().map(|&row| format!("({})", exprs(ast, row))).collect();
301    format!("VALUES {}", written.join(", "))
302}
303
304/// One expression.
305fn expr(ast: &Ast, index: ExprRef) -> String {
306    match ast.expr(index) {
307        Expr::Star { qualifier, replacements } => star(ast, qualifier, replacements),
308        Expr::Column { name } => parts(ast, name),
309        Expr::Literal { kind, text } => literal(ast, kind, text),
310        Expr::Unary { op, operand } => unary(ast, op, operand),
311        Expr::Binary { op, left, right } => binary(ast, op, left, right),
312        Expr::Function { name, args, distinct } => call(ast, name, args, distinct),
313        Expr::Cast { operand, ty, try_cast } => {
314            let word = if try_cast { "TRY_CAST" } else { "CAST" };
315            format!("{word}({} AS {})", expr(ast, operand), typename(ast.string(ty)))
316        }
317        Expr::Case { operand, arms, otherwise } => case(ast, operand, arms, otherwise),
318        Expr::Between { operand, low, high, negated } => {
319            let written = format!(
320                "({} BETWEEN {} AND {})",
321                expr(ast, operand),
322                expr(ast, low),
323                expr(ast, high)
324            );
325            if negated { format!("(NOT {written})") } else { written }
326        }
327        Expr::In { operand, list, negated } => {
328            let written = format!("({} IN ({}))", expr(ast, operand), exprs(ast, list));
329            if negated { format!("(NOT {written})") } else { written }
330        }
331        Expr::Parameter { name } => format!("${}", ast.string(name)),
332        // A bracketed list is a call to `list_value`, including when it is empty.
333        Expr::List { items } => format!("list_value({})", exprs(ast, items)),
334        // And a parenthesised list is a call to `row`, which needs its quotes because it is a
335        // keyword.
336        Expr::Row { items } => format!("\"row\"({})", exprs(ast, items)),
337        Expr::Subquery { query: inner } => format!("({})", query(ast, inner)),
338    }
339}
340
341/// A star, with the qualifier and the replace list it may have been written with.
342fn star(ast: &Ast, qualifier: Slice, replacements: Slice) -> String {
343    let mut out =
344        if qualifier.is_empty() { "*".to_string() } else { format!("{}.*", parts(ast, qualifier)) };
345    if !replacements.is_empty() {
346        let written: Vec<String> =
347            ast.target_list(replacements).iter().map(|target| aliased(ast, target)).collect();
348        out += &format!(" REPLACE ({})", written.join(", "));
349    }
350    out
351}
352
353/// One literal.
354fn literal(ast: &Ast, kind: LiteralKind, text: StrRef) -> String {
355    match kind {
356        // `true` and `false` in lower case, which is the pin's spelling whichever way they were
357        // written.
358        LiteralKind::Null => "NULL".to_string(),
359        LiteralKind::True => "true".to_string(),
360        LiteralKind::False => "false".to_string(),
361        LiteralKind::Number => number(ast.string(text)),
362        LiteralKind::String => string(ast.string(text)),
363        // A blob prints as a string of its escaped form cast to `BLOB`, so `X'ab'` comes back as
364        // `'\xAB'::BLOB`.
365        LiteralKind::Blob => format!("{}::BLOB", string(ast.string(text))),
366    }
367}
368
369/// A numeric literal, written back as the value it was read as rather than as the text.
370///
371/// The value is what upstream prints, so the shape of the literal decides the shape of the answer.
372/// A literal with an exponent in it is a DOUBLE and comes back in whatever form a double prints in.
373/// One with a point in it is a DECIMAL of the width and scale that were written, so the digits after
374/// the point survive exactly, trailing zeros and all, and only the digits in front of it are tidied.
375/// One with neither is an integer. The underscores a long number can be written with are a way of
376/// writing it and not part of it, so `1_000` is `1000` in all three.
377fn number(written: &str) -> String {
378    let text = written.replace('_', "");
379    if text.contains(['e', 'E']) {
380        return double(&text);
381    }
382    let Some((whole, fraction)) = text.split_once('.') else {
383        return leading(&text).to_string();
384    };
385    // `1.` is a decimal of scale zero, which prints without the point, and `.5` keeps the empty
386    // side it was written with rather than growing a zero. Both were measured.
387    if fraction.is_empty() {
388        return leading(whole).to_string();
389    }
390    format!("{}.{fraction}", if whole.is_empty() { "" } else { leading(whole) })
391}
392
393/// A run of digits with the zeros in front of it dropped, down to one digit.
394fn leading(digits: &str) -> &str {
395    let trimmed = digits.trim_start_matches('0');
396    if trimmed.is_empty() { &digits[digits.len().saturating_sub(1)..] } else { trimmed }
397}
398
399/// A double, in the form the formatting library upstream uses prints one in.
400///
401/// Plain digits while the decimal exponent is between minus four and fifteen, and the exponent form
402/// outside that, with at least two digits of exponent and a sign that is written even when it is a
403/// plus. So `1e3` is `1000.0`, `1e15` is `1000000000000000.0`, `1e16` is `1e+16`, `5e-4` is `0.0005`
404/// and `5e-5` is `5e-05`. A plain one always has a point in it, which is what tells a double from an
405/// integer when it is read back.
406fn double(text: &str) -> String {
407    let Ok(value) = text.parse::<f64>() else {
408        return text.to_string();
409    };
410    // The shortest digits that read back as this value, which is what `{:e}` is, and the exponent
411    // that goes with them. Rust writes that form as `2.5e-5`, so the exponent is the tail.
412    let shortest = format!("{value:e}");
413    let (mantissa, exponent) = shortest.split_once('e').unwrap_or((shortest.as_str(), "0"));
414    let exponent: i32 = exponent.parse().unwrap_or(0);
415    if (-4..=15).contains(&exponent) {
416        let plain = format!("{value}");
417        return if plain.contains('.') { plain } else { plain + ".0" };
418    }
419    let sign = if exponent < 0 { '-' } else { '+' };
420    format!("{mantissa}e{sign}{:02}", exponent.abs())
421}
422
423/// A string literal, with the one character that has to be escaped escaped.
424///
425/// Only the quote. A newline written as `e'\n'` comes back as a real newline inside the quotes,
426/// which was measured, so everything else goes out as the byte it is.
427fn string(text: &str) -> String {
428    format!("'{}'", text.replace('\'', "''"))
429}
430
431/// A prefix or postfix operator.
432fn unary(ast: &Ast, op: UnaryOp, operand: ExprRef) -> String {
433    // `-1` is a number and not a negation of one, so a minus in front of a numeric constant folds
434    // into it and `- -3` folds twice and comes back as `3`. A plus does not fold, which is why
435    // `+3` comes back as `+(3)`.
436    if matches!(op, UnaryOp::Negate) {
437        if let Some(number) = negated(ast, operand) {
438            return number;
439        }
440    }
441    let written = expr(ast, operand);
442    match op {
443        UnaryOp::Not => format!("(NOT {written})"),
444        UnaryOp::Negate => format!("-({written})"),
445        UnaryOp::Plus => format!("+({written})"),
446        UnaryOp::BitNot => format!("~({written})"),
447        // `x!` is a call to `factorial` by the time it is printed.
448        UnaryOp::Factorial => format!("factorial({written})"),
449        UnaryOp::IsNull => format!("({written} IS NULL)"),
450        UnaryOp::IsNotNull => format!("({written} IS NOT NULL)"),
451        // `IS UNKNOWN` is `IS NULL` and nothing else, so it prints as the thing it means.
452        UnaryOp::IsUnknown => format!("({written} IS NULL)"),
453        UnaryOp::IsNotUnknown => format!("({written} IS NOT NULL)"),
454        // And the four tests against a boolean are a cast and a distinct test, which is what they
455        // are defined to be: `x IS TRUE` is false rather than null for a null `x`, and a plain
456        // `x = true` would not be.
457        UnaryOp::IsTrue => distinct(&written, "true", true),
458        UnaryOp::IsNotTrue => distinct(&written, "true", false),
459        UnaryOp::IsFalse => distinct(&written, "false", true),
460        UnaryOp::IsNotFalse => distinct(&written, "false", false),
461    }
462}
463
464/// What `IS TRUE` and its three relatives are written as.
465fn distinct(operand: &str, against: &str, same: bool) -> String {
466    let word = if same { "IS NOT DISTINCT FROM" } else { "IS DISTINCT FROM" };
467    format!("(CAST({operand} AS BOOLEAN) {word} {against})")
468}
469
470/// The text of a numeric constant with a minus applied to it, and `None` for anything else.
471///
472/// Recursive, because the fold happens on the way in and applies again to what it produced. A minus
473/// in front of a minus in front of `3` is the constant `3`.
474fn negated(ast: &Ast, index: ExprRef) -> Option<String> {
475    match ast.expr(index) {
476        Expr::Literal { kind: LiteralKind::Number, text } => {
477            Some(format!("-{}", number(ast.string(text))))
478        }
479        Expr::Unary { op: UnaryOp::Negate, operand } => {
480            let inner = negated(ast, operand)?;
481            Some(inner.strip_prefix('-').unwrap_or(&inner).to_string())
482        }
483        _ => None,
484    }
485}
486
487/// An infix operator, parenthesised.
488fn binary(ast: &Ast, op: BinaryOp, left: ExprRef, right: ExprRef) -> String {
489    let (left, right) = (expr(ast, left), expr(ast, right));
490    // The three that are not written as an operator at all.
491    match op {
492        BinaryOp::SimilarTo => return format!("regexp_full_match({left}, {right})"),
493        BinaryOp::NotSimilarTo => return format!("(NOT regexp_full_match({left}, {right}))"),
494        // The arguments swap, so `ts AT TIME ZONE 'UTC'` is `timezone('UTC', ts)`.
495        BinaryOp::AtTimeZone => return format!("timezone({right}, {left})"),
496        // And the one that is written as an operator and is not parenthesised.
497        BinaryOp::Collate => return format!("{left} COLLATE {right}"),
498        _ => {}
499    }
500    let word = match op {
501        BinaryOp::Or => "OR",
502        BinaryOp::And => "AND",
503        BinaryOp::Eq => "=",
504        BinaryOp::NotEq => "!=",
505        BinaryOp::Lt => "<",
506        BinaryOp::Gt => ">",
507        BinaryOp::LtEq => "<=",
508        BinaryOp::GtEq => ">=",
509        BinaryOp::IsDistinctFrom => "IS DISTINCT FROM",
510        BinaryOp::IsNotDistinctFrom => "IS NOT DISTINCT FROM",
511        BinaryOp::Add => "+",
512        BinaryOp::Subtract => "-",
513        BinaryOp::Multiply => "*",
514        BinaryOp::Divide => "/",
515        BinaryOp::IntegerDivide => "//",
516        BinaryOp::Modulo => "%",
517        // Whichever of `^` and `**` was written is what the pin prints, and both arrive here as one
518        // operator, so one spelling has to stand for both. See the module doc.
519        BinaryOp::Power => "**",
520        BinaryOp::BitAnd => "&",
521        BinaryOp::BitOr => "|",
522        BinaryOp::ShiftLeft => "<<",
523        BinaryOp::ShiftRight => ">>",
524        BinaryOp::Concat => "||",
525        // The four pattern operators have a word spelling and a symbol spelling, and the symbol is
526        // what comes back whichever was written.
527        BinaryOp::Like => "~~",
528        BinaryOp::NotLike => "!~~",
529        BinaryOp::ILike => "~~*",
530        BinaryOp::NotILike => "!~~*",
531        BinaryOp::Glob => "~~~",
532        BinaryOp::Regex => "~",
533        BinaryOp::RegexInsensitive => "~*",
534        BinaryOp::NotRegexInsensitive => "!~*",
535        BinaryOp::Arrow => "->",
536        BinaryOp::LongArrow => "->>",
537        BinaryOp::Contains => "@>",
538        BinaryOp::ContainedBy => "<@",
539        BinaryOp::Overlaps => "&&",
540        BinaryOp::StartsWith => "^@",
541        BinaryOp::InetContainedByOrEq => "<<=",
542        BinaryOp::InetContainsOrEq => ">>=",
543        BinaryOp::Named(name) => ast.string(name),
544        BinaryOp::SimilarTo | BinaryOp::NotSimilarTo | BinaryOp::AtTimeZone | BinaryOp::Collate => {
545            unreachable!("the four that return above")
546        }
547    };
548    format!("({left} {word} {right})")
549}
550
551/// A function call.
552fn call(ast: &Ast, name: Slice, args: Slice, distinct: bool) -> String {
553    let written = parts(ast, name);
554    let list = ast.expr_list(args);
555    // `count(*)` is a different function from `count`, and the star is how it is spelled rather than
556    // an argument it takes, so it prints under the name it really has.
557    if list.len() == 1
558        && matches!(ast.expr(list[0]), Expr::Star { qualifier, replacements }
559            if qualifier.is_empty() && replacements.is_empty())
560        && written.eq_ignore_ascii_case("count")
561    {
562        return "count_star()".to_string();
563    }
564    let word = if distinct { "DISTINCT " } else { "" };
565    format!("{}({word}{})", operator(ast, name, &written), exprs(ast, args))
566}
567
568/// The name a call is written back under, which is the name it was written with for all but two.
569///
570/// `coalesce` and `ifnull` are grammar rules rather than function names, so they come back as the
571/// one thing the rule stands for, upper case and unquoted. That holds for the one argument form as
572/// well: `coalesce(x)` is `COALESCE(x)` and not `x`. No other name does this, which was measured,
573/// and `nullif` is the one to check against because it looks like it should and does not.
574fn operator(ast: &Ast, name: Slice, written: &str) -> String {
575    let one = ast.name(name).next().unwrap_or_default();
576    let alone = ast.name(name).count() == 1;
577    if alone && (one.eq_ignore_ascii_case("coalesce") || one.eq_ignore_ascii_case("ifnull")) {
578        return "COALESCE".to_string();
579    }
580    written.to_string()
581}
582
583/// A `CASE`, always searched and always with an `ELSE`.
584///
585/// A simple `CASE x WHEN 1 THEN 'a'` is rewritten into `CASE WHEN x = 1 THEN 'a' ELSE NULL END` on
586/// the way in, so both forms print the same way. The two spaces after `CASE` are upstream leaving
587/// the operand slot empty and writing the space around it anyway.
588fn case(ast: &Ast, operand: ExprRef, arms: Slice, otherwise: ExprRef) -> String {
589    let mut out = "CASE ".to_string();
590    for arm in ast.arm_list(arms) {
591        let when = when(ast, operand, arm);
592        out += &format!(" WHEN ({when}) THEN ({})", expr(ast, arm.then));
593    }
594    let last = if otherwise == NONE { "NULL".to_string() } else { expr(ast, otherwise) };
595    out + &format!(" ELSE {last} END")
596}
597
598/// The condition of one arm, which is the arm's own for a searched `CASE` and an equality for a
599/// simple one.
600fn when(ast: &Ast, operand: ExprRef, arm: &CaseArm) -> String {
601    if operand == NONE {
602        return expr(ast, arm.when);
603    }
604    format!("({} = {})", expr(ast, operand), expr(ast, arm.when))
605}
606
607/// A type as upstream writes one, which is two rules and not one.
608///
609/// A name the SQL standard spells is resolved and written back under the one name its type has, so
610/// `int` is `INTEGER`, `numeric(5)` is `DECIMAL(5)`, `character varying` is `VARCHAR` and `real` is
611/// `FLOAT`. Every other name is written back exactly as somebody typed it, case and all and without
612/// quotes, so `text` stays `text`, `TEXT` stays `TEXT` and `int4` stays `int4`. All of that was
613/// measured a name at a time, and the split is not arbitrary: the standard names are the ones the
614/// grammar has rules for, and everything else is a name the parser hands to the catalog to look up
615/// later, so the text is all it has.
616///
617/// This walks the text rather than going through the type system, because the type system throws
618/// away what has to survive here. `DECIMAL(5)` and `DECIMAL` both become a width and a scale, and
619/// `VARCHAR(10)` becomes `VARCHAR`, but upstream prints back the length that was written.
620fn typename(text: &str) -> String {
621    let text = text.trim();
622    // A trailing `[]` or `[3]` is a list or an array of whatever is in front of it, and the element
623    // is resolved the same way: `int[]` is `INTEGER[]` while `int4[]` stays `int4[]`.
624    if let Some(open) = suffix(text) {
625        return typename(&text[..open]) + &text[open..];
626    }
627    let (base, arguments) = arguments(text);
628    let Some(name) = standard(base) else {
629        let base = unquote(base);
630        return match arguments {
631            Some(arguments) => format!("{}({arguments})", catalogued(&base)),
632            None => catalogued(&base),
633        };
634    };
635    match (name, arguments) {
636        // `STRUCT(a bool)` and `UNION(a int)` are a name and a type each, and the name keeps the
637        // case it was written in while the type goes round again.
638        ("STRUCT" | "UNION", Some(inside)) => {
639            let written: Vec<String> = pieces(inside).iter().map(|piece| field(piece)).collect();
640            format!("{name}({})", written.join(", "))
641        }
642        ("MAP", Some(inside)) => {
643            let written: Vec<String> = pieces(inside).iter().map(|piece| typename(piece)).collect();
644            format!("{name}({})", written.join(", "))
645        }
646        // The width and the scale of a decimal and the length of a string survive, because upstream
647        // prints the modifiers it was given rather than the ones the type ended up with.
648        ("DECIMAL" | "VARCHAR", Some(inside)) => {
649            format!("{name}({})", pieces(inside).join(", "))
650        }
651        // And everything else drops them, because they chose the type rather than sitting on it.
652        // `float(10)` is a `FLOAT` and there is nothing left of the ten.
653        _ => name.to_string(),
654    }
655}
656
657/// A type name the grammar has no rule for, which is a name for the catalog to look up later.
658///
659/// Written back as it stands, with the case it was written in and with no quotes, because all the
660/// parser has is the text. `bool`, `TEXT`, `int4`, `timestamptz` and `Mixed` all come back exactly
661/// as they went in, which was measured a name at a time.
662///
663/// `json` is the one exception in the whole list and it comes back quoted. That is not a rule about
664/// json, it is what happens to a name the parser resolves on its own rather than leaving for the
665/// catalog: the type it lands on carries the written name as its label, and a label is written back
666/// through the identifier rule, which quotes a keyword. `json` is the only name that is both a
667/// keyword and one of those, so it is the only one where the difference shows. The case that was
668/// written survives it, so `JSON` is `"JSON"` and `json` is `"json"`.
669fn catalogued(base: &str) -> String {
670    if base.eq_ignore_ascii_case("json") { quoted(base) } else { base.to_string() }
671}
672
673/// A name with its quotes taken off, if it had any.
674fn unquote(base: &str) -> String {
675    match base.strip_prefix('"').and_then(|rest| rest.strip_suffix('"')) {
676        Some(inside) => inside.replace("\"\"", "\""),
677        None => base.to_string(),
678    }
679}
680
681/// Where the trailing `[]` or `[3]` of a list or an array type starts, if there is one.
682fn suffix(text: &str) -> Option<usize> {
683    let rest = text.strip_suffix(']')?;
684    let open = rest.rfind('[')?;
685    rest[open + 1..].bytes().all(|byte| byte.is_ascii_digit()).then_some(open)
686}
687
688/// A type split into the name and whatever was in the parentheses after it.
689fn arguments(text: &str) -> (&str, Option<&str>) {
690    let Some(rest) = text.strip_suffix(')') else {
691        return (text, None);
692    };
693    let mut depth = 0usize;
694    for (at, byte) in rest.bytes().enumerate() {
695        match byte {
696            b'(' if depth == 0 => depth = 1,
697            b'(' => depth += 1,
698            b')' => depth -= 1,
699            _ => continue,
700        }
701        if depth == 1 && byte == b'(' {
702            return (rest[..at].trim(), Some(rest[at + 1..].trim()));
703        }
704    }
705    (text, None)
706}
707
708/// The entries of an argument list, split on the commas that are not inside anything.
709fn pieces(inside: &str) -> Vec<&str> {
710    let mut found = Vec::new();
711    let (mut depth, mut quoted, mut start) = (0usize, false, 0usize);
712    for (at, byte) in inside.bytes().enumerate() {
713        match byte {
714            b'"' => quoted = !quoted,
715            b'(' | b'[' if !quoted => depth += 1,
716            b')' | b']' if !quoted => depth = depth.saturating_sub(1),
717            b',' if !quoted && depth == 0 => {
718                found.push(inside[start..at].trim());
719                start = at + 1;
720            }
721            _ => {}
722        }
723    }
724    found.push(inside[start..].trim());
725    found
726}
727
728/// One field of a `STRUCT` or a `UNION`, which is a name and then a type.
729fn field(piece: &str) -> String {
730    let mut quoting = false;
731    for (at, byte) in piece.bytes().enumerate() {
732        match byte {
733            b'"' => quoting = !quoting,
734            byte if byte.is_ascii_whitespace() && !quoting => {
735                let name = piece[..at].trim();
736                let name =
737                    if name.starts_with('"') { quoted(&unquote(name)) } else { name.to_string() };
738                return format!("{name} {}", typename(&piece[at + 1..]));
739            }
740            _ => {}
741        }
742    }
743    piece.to_string()
744}
745
746/// The one name a type the SQL standard spells is written back under, and nothing for any other.
747///
748/// Several words for the ones the standard writes with several. The list is short because it is the
749/// standard's list and not DuckDB's: `HUGEINT`, `TEXT`, `BLOB` and the rest of the names DuckDB adds
750/// are not in here, and they are the ones that come back exactly as they were written.
751fn standard(base: &str) -> Option<&'static str> {
752    const NAMES: &[(&str, &str)] = &[
753        ("BOOLEAN", "BOOLEAN"),
754        ("INT", "INTEGER"),
755        ("INTEGER", "INTEGER"),
756        ("SMALLINT", "SMALLINT"),
757        ("BIGINT", "BIGINT"),
758        ("DEC", "DECIMAL"),
759        ("DECIMAL", "DECIMAL"),
760        ("NUMERIC", "DECIMAL"),
761        ("REAL", "FLOAT"),
762        ("FLOAT", "FLOAT"),
763        ("DOUBLE PRECISION", "DOUBLE"),
764        ("CHAR", "VARCHAR"),
765        ("CHARACTER", "VARCHAR"),
766        ("CHARACTER VARYING", "VARCHAR"),
767        ("NATIONAL CHARACTER", "VARCHAR"),
768        ("NATIONAL CHARACTER VARYING", "VARCHAR"),
769        ("VARCHAR", "VARCHAR"),
770        ("BIT", "BIT"),
771        ("DATE", "DATE"),
772        ("TIME", "TIME"),
773        ("TIME WITH TIME ZONE", "TIME WITH TIME ZONE"),
774        ("TIME WITHOUT TIME ZONE", "TIME"),
775        ("TIMESTAMP", "TIMESTAMP"),
776        ("TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH TIME ZONE"),
777        ("TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMP"),
778        ("INTERVAL", "INTERVAL"),
779        ("STRUCT", "STRUCT"),
780        ("UNION", "UNION"),
781        ("MAP", "MAP"),
782    ];
783    let written: Vec<&str> = base.split_whitespace().collect();
784    let written = written.join(" ");
785    NAMES
786        .iter()
787        .find(|(spelling, _)| spelling.eq_ignore_ascii_case(&written))
788        .map(|(_, name)| *name)
789}
790
791/// A run of expressions, comma separated.
792fn exprs(ast: &Ast, list: Slice) -> String {
793    let written: Vec<String> = ast.expr_list(list).iter().map(|&item| expr(ast, item)).collect();
794    written.join(", ")
795}
796
797/// A run of identifiers, comma separated, each quoted if it has to be.
798fn names(ast: &Ast, list: Slice) -> String {
799    ast.name(list).map(quoted).collect::<Vec<_>>().join(", ")
800}
801
802/// A dotted name, each part quoted if it has to be.
803fn parts(ast: &Ast, list: Slice) -> String {
804    ast.name(list).map(quoted).collect::<Vec<_>>().join(".")
805}
806
807#[cfg(test)]
808mod tests {
809    use super::create_view;
810    use crate::ast::Statement;
811    use crate::transform::parse_ast;
812
813    /// The whole statement, deparsed.
814    fn whole(sql: &str) -> String {
815        let ast = parse_ast(sql).unwrap_or_else(|error| panic!("{sql} should parse: {error}"));
816        let Statement::CreateView(index) = ast.statements[0] else {
817            panic!("that was not a create view");
818        };
819        create_view(&ast, index)
820    }
821
822    /// Just the body, which is what most of these are about.
823    fn body(query: &str) -> String {
824        let written = whole(&format!("CREATE VIEW v AS {query}"));
825        written
826            .strip_prefix("CREATE VIEW v AS ")
827            .and_then(|rest| rest.strip_suffix(';'))
828            .expect("the statement wrapper is there")
829            .to_string()
830    }
831
832    #[test]
833    fn a_statement_loses_its_qualification_and_its_or_replace() {
834        assert_eq!(whole("CREATE VIEW main.v AS SELECT 1"), "CREATE VIEW v AS SELECT 1;");
835        assert_eq!(whole("CREATE OR REPLACE VIEW v AS SELECT 1"), "CREATE VIEW v AS SELECT 1;");
836        assert_eq!(whole("CREATE VIEW IF NOT EXISTS v AS SELECT 1"), "CREATE VIEW v AS SELECT 1;");
837        assert_eq!(whole("CREATE TEMP VIEW v AS SELECT 1"), "CREATE TEMP VIEW v AS SELECT 1;");
838    }
839
840    /// A space before the parenthesis here, and none in a `CREATE TABLE`. Both measured.
841    #[test]
842    fn an_alias_list_is_written_with_a_space_in_front_of_it() {
843        assert_eq!(
844            whole(r#"CREATE VIEW v ("Weird Name", "x y") AS SELECT 1, 2"#),
845            r#"CREATE VIEW v ("Weird Name", "x y") AS SELECT 1, 2;"#
846        );
847    }
848
849    #[test]
850    fn comments_and_spacing_go_and_the_case_of_a_name_stays() {
851        assert_eq!(
852            whole("CREATE VIEW v AS SELECT  X /* a note */ FROM   T"),
853            "CREATE VIEW v AS SELECT X FROM T;"
854        );
855    }
856
857    #[test]
858    fn every_binary_operation_is_parenthesised_and_every_unary_one_parenthesises_its_operand() {
859        assert_eq!(body("SELECT x + y * 2 - 1 FROM t"), "SELECT ((x + (y * 2)) - 1) FROM t");
860        assert_eq!(
861            body("SELECT x > 1 AND y < 2 OR b FROM t"),
862            "SELECT (((x > 1) AND (y < 2)) OR b) FROM t"
863        );
864        assert_eq!(body("SELECT NOT b FROM t"), "SELECT (NOT b) FROM t");
865        assert_eq!(body("SELECT ~x FROM t"), "SELECT ~(x) FROM t");
866        assert_eq!(body("SELECT +x FROM t"), "SELECT +(x) FROM t");
867        assert_eq!(body("SELECT -x FROM t"), "SELECT -(x) FROM t");
868    }
869
870    /// A minus in front of a number is part of the number, and it folds as many times as it is
871    /// written. A plus is not part of one and does not fold.
872    #[test]
873    fn a_minus_in_front_of_a_constant_folds_into_it() {
874        assert_eq!(body("SELECT -1"), "SELECT -1");
875        assert_eq!(body("SELECT - -3"), "SELECT 3");
876        assert_eq!(body("SELECT +3"), "SELECT +(3)");
877    }
878
879    #[test]
880    fn the_null_tests_and_the_boolean_tests() {
881        assert_eq!(body("SELECT x IS NULL FROM t"), "SELECT (x IS NULL) FROM t");
882        assert_eq!(body("SELECT x ISNULL FROM t"), "SELECT (x IS NULL) FROM t");
883        assert_eq!(body("SELECT x NOTNULL FROM t"), "SELECT (x IS NOT NULL) FROM t");
884        assert_eq!(
885            body("SELECT b IS TRUE FROM t"),
886            "SELECT (CAST(b AS BOOLEAN) IS NOT DISTINCT FROM true) FROM t"
887        );
888        assert_eq!(
889            body("SELECT b IS NOT TRUE FROM t"),
890            "SELECT (CAST(b AS BOOLEAN) IS DISTINCT FROM true) FROM t"
891        );
892        assert_eq!(
893            body("SELECT b IS FALSE FROM t"),
894            "SELECT (CAST(b AS BOOLEAN) IS NOT DISTINCT FROM false) FROM t"
895        );
896        assert_eq!(body("SELECT b IS UNKNOWN FROM t"), "SELECT (b IS NULL) FROM t");
897        assert_eq!(body("SELECT b IS NOT UNKNOWN FROM t"), "SELECT (b IS NOT NULL) FROM t");
898        assert_eq!(
899            body("SELECT x IS DISTINCT FROM y FROM t"),
900            "SELECT (x IS DISTINCT FROM y) FROM t"
901        );
902    }
903
904    #[test]
905    fn a_negated_between_or_in_is_a_not_around_the_plain_one() {
906        assert_eq!(body("SELECT x BETWEEN 1 AND 10 FROM t"), "SELECT (x BETWEEN 1 AND 10) FROM t");
907        assert_eq!(
908            body("SELECT x NOT BETWEEN 1 AND 2 FROM t"),
909            "SELECT (NOT (x BETWEEN 1 AND 2)) FROM t"
910        );
911        assert_eq!(body("SELECT x IN (1, 2, 3) FROM t"), "SELECT (x IN (1, 2, 3)) FROM t");
912        assert_eq!(body("SELECT x NOT IN (1, 2) FROM t"), "SELECT (NOT (x IN (1, 2))) FROM t");
913    }
914
915    /// The four pattern operators have a word spelling and a symbol spelling, and the symbol is
916    /// what comes back either way.
917    #[test]
918    fn the_pattern_operators_come_back_as_symbols() {
919        assert_eq!(body("SELECT s LIKE 'a' FROM t"), "SELECT (s ~~ 'a') FROM t");
920        assert_eq!(body("SELECT s NOT LIKE 'a' FROM t"), "SELECT (s !~~ 'a') FROM t");
921        assert_eq!(body("SELECT s ILIKE 'a' FROM t"), "SELECT (s ~~* 'a') FROM t");
922        assert_eq!(body("SELECT s NOT ILIKE 'a' FROM t"), "SELECT (s !~~* 'a') FROM t");
923        assert_eq!(body("SELECT s GLOB 'a' FROM t"), "SELECT (s ~~~ 'a') FROM t");
924    }
925
926    #[test]
927    fn collate_has_no_parentheses_and_the_rest_of_the_operators_keep_their_spelling() {
928        assert_eq!(body("SELECT s COLLATE NOCASE FROM t"), "SELECT s COLLATE NOCASE FROM t");
929        assert_eq!(body("SELECT x // y FROM t"), "SELECT (x // y) FROM t");
930        assert_eq!(body("SELECT x || y FROM t"), "SELECT (x || y) FROM t");
931        assert_eq!(body("SELECT x @> y FROM t"), "SELECT (x @> y) FROM t");
932        assert_eq!(body("SELECT x <=> y FROM t"), "SELECT (x <=> y) FROM t");
933    }
934
935    /// Always searched, always with an `ELSE`, and two spaces after the keyword.
936    #[test]
937    fn a_case_is_written_the_long_way_round() {
938        assert_eq!(
939            body("SELECT CASE WHEN x > 0 THEN 'a' WHEN x < 0 THEN 'b' ELSE 'c' END FROM t"),
940            "SELECT CASE  WHEN ((x > 0)) THEN ('a') WHEN ((x < 0)) THEN ('b') ELSE 'c' END FROM t"
941        );
942        assert_eq!(
943            body("SELECT CASE x WHEN 1 THEN 'a' END FROM t"),
944            "SELECT CASE  WHEN ((x = 1)) THEN ('a') ELSE NULL END FROM t"
945        );
946    }
947
948    #[test]
949    fn a_cast_writes_its_type_in_upper_case_with_a_space_after_the_comma() {
950        assert_eq!(body("SELECT x::varchar FROM t"), "SELECT CAST(x AS VARCHAR) FROM t");
951        assert_eq!(
952            body("SELECT cast(x as decimal(4,1)) FROM t"),
953            "SELECT CAST(x AS DECIMAL(4, 1)) FROM t"
954        );
955        assert_eq!(
956            body("SELECT TRY_CAST(s AS INTEGER) FROM t"),
957            "SELECT TRY_CAST(s AS INTEGER) FROM t"
958        );
959    }
960
961    /// The names the grammar has a rule for, which come back under the one name the type has.
962    #[test]
963    fn a_standard_type_name_is_resolved_and_the_modifiers_it_was_written_with_survive() {
964        let cast = |written: &str| body(&format!("SELECT CAST(x AS {written})"));
965        assert_eq!(cast("int"), "SELECT CAST(x AS INTEGER)");
966        assert_eq!(cast("numeric(5)"), "SELECT CAST(x AS DECIMAL(5))");
967        assert_eq!(cast("decimal"), "SELECT CAST(x AS DECIMAL)");
968        assert_eq!(cast("varchar(10)"), "SELECT CAST(x AS VARCHAR(10))");
969        assert_eq!(cast("national character(2)"), "SELECT CAST(x AS VARCHAR(2))");
970        // The argument of a float chose the type rather than sitting on it, so there is nothing
971        // left of the ten by the time it is written back.
972        assert_eq!(cast("float(10)"), "SELECT CAST(x AS FLOAT)");
973        assert_eq!(cast("real"), "SELECT CAST(x AS FLOAT)");
974        assert_eq!(cast("double precision"), "SELECT CAST(x AS DOUBLE)");
975        assert_eq!(cast("time with time zone"), "SELECT CAST(x AS TIME WITH TIME ZONE)");
976        assert_eq!(cast("int[]"), "SELECT CAST(x AS INTEGER[])");
977        assert_eq!(cast("int[2][3]"), "SELECT CAST(x AS INTEGER[2][3])");
978        assert_eq!(cast("map(int, varchar)"), "SELECT CAST(x AS MAP(INTEGER, VARCHAR))");
979        assert_eq!(cast("union(a int)"), "SELECT CAST(x AS UNION(a INTEGER))");
980    }
981
982    /// A struct keeps the case of the field name and resolves the field type.
983    #[test]
984    fn a_struct_field_keeps_its_name_and_its_type_goes_round_again() {
985        assert_eq!(body("SELECT CAST(x AS struct(a bool))"), "SELECT CAST(x AS STRUCT(a bool))");
986        assert_eq!(
987            body("SELECT CAST(x AS struct(\"A b\" int))"),
988            "SELECT CAST(x AS STRUCT(\"A b\" INTEGER))"
989        );
990    }
991
992    /// And every other name is the catalog's business, so the text is written back as it stands.
993    #[test]
994    fn a_type_name_the_grammar_has_no_rule_for_keeps_the_case_it_was_written_in() {
995        let cast = |written: &str| body(&format!("SELECT CAST(x AS {written})"));
996        assert_eq!(cast("text"), "SELECT CAST(x AS text)");
997        assert_eq!(cast("TEXT"), "SELECT CAST(x AS TEXT)");
998        assert_eq!(cast("DOUBLE"), "SELECT CAST(x AS DOUBLE)");
999        assert_eq!(cast("bool"), "SELECT CAST(x AS bool)");
1000        assert_eq!(cast("\"bool\""), "SELECT CAST(x AS bool)");
1001        assert_eq!(cast("int4[]"), "SELECT CAST(x AS int4[])");
1002        assert_eq!(cast("TIMESTAMPTZ"), "SELECT CAST(x AS TIMESTAMPTZ)");
1003        // The one name that comes back quoted, for the reason written on `catalogued`.
1004        assert_eq!(cast("JSON"), "SELECT CAST(x AS \"JSON\")");
1005        assert_eq!(cast("json"), "SELECT CAST(x AS \"json\")");
1006        assert_eq!(cast("json[]"), "SELECT CAST(x AS \"json\"[])");
1007        assert_eq!(cast("struct(a json)"), "SELECT CAST(x AS STRUCT(a \"json\"))");
1008    }
1009
1010    #[test]
1011    fn a_star_count_is_a_function_of_its_own_and_a_list_is_a_call() {
1012        assert_eq!(body("SELECT count(*) FROM t"), "SELECT count_star() FROM t");
1013        assert_eq!(body("SELECT count(DISTINCT x) FROM t"), "SELECT count(DISTINCT x) FROM t");
1014        assert_eq!(body("SELECT [1, 2, 3]"), "SELECT list_value(1, 2, 3)");
1015        assert_eq!(body("SELECT []"), "SELECT list_value()");
1016    }
1017
1018    /// A function name goes through the same quoting rule an identifier does, so the ones that are
1019    /// keywords in a class come back quoted.
1020    #[test]
1021    fn a_function_name_is_quoted_when_it_is_a_keyword() {
1022        assert_eq!(body("SELECT nullif(x, 1) FROM t"), "SELECT \"nullif\"(x, 1) FROM t");
1023        assert_eq!(body("SELECT length(s) FROM t"), "SELECT length(s) FROM t");
1024    }
1025
1026    #[test]
1027    fn the_literals() {
1028        assert_eq!(body("SELECT NULL, TRUE, FALSE"), "SELECT NULL, true, false");
1029        assert_eq!(body("SELECT 1.50, .5, 1_000"), "SELECT 1.50, .5, 1000");
1030        assert_eq!(body("SELECT 'it''s'"), "SELECT 'it''s'");
1031    }
1032
1033    /// A number comes back as the value it was read as, which is three rules and not one.
1034    #[test]
1035    fn a_number_is_written_back_as_the_value_the_shape_of_it_made() {
1036        assert_eq!(body("SELECT 007, 1_000"), "SELECT 7, 1000");
1037        assert_eq!(body("SELECT 1.50, 00.5, 1., 0.0"), "SELECT 1.50, 0.5, 1, 0.0");
1038        assert_eq!(body("SELECT 1e3, 1.5e2, 1e-3, 5e-4"), "SELECT 1000.0, 150.0, 0.001, 0.0005");
1039        assert_eq!(body("SELECT 5e-5, 2.5e-5, 1e-10"), "SELECT 5e-05, 2.5e-05, 1e-10");
1040        assert_eq!(body("SELECT 1e15, 1e16, 1e100"), "SELECT 1000000000000000.0, 1e+16, 1e+100");
1041    }
1042
1043    /// The part of an `EXTRACT` is a keyword and a keyword has one spelling.
1044    #[test]
1045    fn an_extract_is_a_date_part_call_and_the_keyword_it_named_has_one_spelling() {
1046        assert_eq!(body("SELECT extract(year FROM d)"), "SELECT date_part('YEAR', d)");
1047        assert_eq!(body("SELECT extract(years FROM d)"), "SELECT date_part('YEAR', d)");
1048        assert_eq!(body("SELECT extract(seconds FROM d)"), "SELECT date_part('SECOND', d)");
1049        // Two of the thirteen are written back plural, which is a list and not a rule.
1050        assert_eq!(
1051            body("SELECT extract(millisecond FROM d)"),
1052            "SELECT date_part('MILLISECONDS', d)"
1053        );
1054        assert_eq!(
1055            body("SELECT extract(microseconds FROM d)"),
1056            "SELECT date_part('MICROSECONDS', d)"
1057        );
1058        assert_eq!(body("SELECT extract(millennia FROM d)"), "SELECT date_part('MILLENNIUM', d)");
1059        // A word the grammar does not name as a keyword is an identifier and keeps its case.
1060        assert_eq!(body("SELECT extract(epoch FROM d)"), "SELECT date_part('epoch', d)");
1061        assert_eq!(body("SELECT extract(dow FROM d)"), "SELECT date_part('dow', d)");
1062    }
1063
1064    /// The two spellings of the one operator, which is not a function name however much it looks it.
1065    #[test]
1066    fn coalesce_and_ifnull_are_one_operator_and_it_is_written_in_upper_case() {
1067        assert_eq!(body("SELECT coalesce(x, y)"), "SELECT COALESCE(x, y)");
1068        assert_eq!(body("SELECT IfNull(x, y)"), "SELECT COALESCE(x, y)");
1069        // Including the one argument form, which is not folded away.
1070        assert_eq!(body("SELECT coalesce(x)"), "SELECT COALESCE(x)");
1071        // And no other name does this, which `nullif` is the one to check against.
1072        assert_eq!(body("SELECT nullif(x, y)"), "SELECT \"nullif\"(x, y)");
1073        assert_eq!(body("SELECT greatest(x, y)"), "SELECT greatest(x, y)");
1074    }
1075
1076    #[test]
1077    fn the_modifiers_hang_off_the_query_and_not_off_the_select() {
1078        assert_eq!(body("SELECT x FROM t LIMIT 5 OFFSET 2"), "SELECT x FROM t LIMIT 5 OFFSET 2");
1079        assert_eq!(body("SELECT x FROM t LIMIT 10 PERCENT"), "SELECT x FROM t LIMIT (10) %");
1080        assert_eq!(
1081            body("SELECT x FROM t ORDER BY x ASC, y NULLS LAST"),
1082            "SELECT x FROM t ORDER BY x ASC, y NULLS LAST"
1083        );
1084        assert_eq!(body("SELECT x FROM t ORDER BY ALL"), "SELECT x FROM t ORDER BY COLUMNS(*)");
1085        assert_eq!(body("SELECT x FROM t GROUP BY ALL"), "SELECT x FROM t GROUP BY ALL");
1086        assert_eq!(
1087            body("SELECT x FROM t GROUP BY x HAVING x > 0"),
1088            "SELECT x FROM t GROUP BY x HAVING (x > 0)"
1089        );
1090        assert_eq!(
1091            body("SELECT DISTINCT ON (x) x, y FROM t"),
1092            "SELECT DISTINCT ON (x) x, y FROM t"
1093        );
1094    }
1095
1096    /// A branch that is a set operation of its own is written bare, and a bare left branch loses the
1097    /// space that would follow it. Upstream's, and reproduced because the column is a comparison.
1098    #[test]
1099    fn a_chain_of_set_operations_loses_a_space_in_the_middle() {
1100        assert_eq!(
1101            body("SELECT x FROM t UNION ALL SELECT y FROM t"),
1102            "(SELECT x FROM t) UNION ALL (SELECT y FROM t)"
1103        );
1104        assert_eq!(
1105            body("SELECT x FROM t UNION SELECT y FROM t UNION SELECT 1"),
1106            "(SELECT x FROM t) UNION (SELECT y FROM t)UNION (SELECT 1)"
1107        );
1108        assert_eq!(
1109            body("SELECT x FROM t UNION DISTINCT SELECT y FROM t"),
1110            "(SELECT x FROM t) UNION (SELECT y FROM t)"
1111        );
1112    }
1113
1114    #[test]
1115    fn a_values_body_is_wrapped_in_a_select_that_names_it() {
1116        assert_eq!(
1117            body("VALUES (1, 'a'), (2, 'b')"),
1118            "SELECT * FROM (VALUES (1, 'a'), (2, 'b')) AS valueslist"
1119        );
1120    }
1121
1122    /// A space before each comma, which is upstream's and is not a typo here.
1123    #[test]
1124    fn a_from_list_has_a_space_before_the_comma() {
1125        assert_eq!(body("SELECT 1 FROM t AS t1, t AS t2"), "SELECT 1 FROM t AS t1 , t AS t2");
1126    }
1127
1128    #[test]
1129    fn a_from_item_and_its_aliases() {
1130        assert_eq!(body("SELECT 1 FROM t AS r(n)"), "SELECT 1 FROM t AS r(n)");
1131        assert_eq!(body("SELECT 1 FROM main.t"), "SELECT 1 FROM main.t");
1132        assert_eq!(
1133            body("SELECT 1 FROM (SELECT x FROM t) AS sub"),
1134            "SELECT 1 FROM (SELECT x FROM t) AS sub"
1135        );
1136        assert_eq!(body("SELECT 1 FROM range(10)"), "SELECT 1 FROM \"range\"(10)");
1137    }
1138
1139    /// Joins are parenthesised, `FULL OUTER` loses a word, `NATURAL` gains one, and an `ON` gets a
1140    /// second pair of parentheses on top of the ones the condition already has.
1141    #[test]
1142    fn a_join_is_parenthesised_and_so_is_its_condition_twice() {
1143        assert_eq!(
1144            body("SELECT 1 FROM t AS a JOIN t AS b ON a.x = b.y"),
1145            "SELECT 1 FROM (t AS a INNER JOIN t AS b ON ((a.x = b.y)))"
1146        );
1147        assert_eq!(
1148            body("SELECT 1 FROM t LEFT JOIN t AS u USING (x)"),
1149            "SELECT 1 FROM (t LEFT JOIN t AS u USING (x))"
1150        );
1151        assert_eq!(
1152            body("SELECT 1 FROM t CROSS JOIN t AS u"),
1153            "SELECT 1 FROM (t CROSS JOIN t AS u)"
1154        );
1155        assert_eq!(
1156            body("SELECT 1 FROM t FULL OUTER JOIN t AS u ON t.x = u.x"),
1157            "SELECT 1 FROM (t FULL JOIN t AS u ON ((t.x = u.x)))"
1158        );
1159        assert_eq!(
1160            body("SELECT 1 FROM t NATURAL JOIN t AS u"),
1161            "SELECT 1 FROM (t NATURAL INNER JOIN t AS u)"
1162        );
1163        assert_eq!(
1164            body("SELECT 1 FROM t POSITIONAL JOIN t AS u"),
1165            "SELECT 1 FROM (t POSITIONAL JOIN t AS u)"
1166        );
1167    }
1168
1169    #[test]
1170    fn a_target_keeps_its_alias_and_a_star_keeps_its_replace_list() {
1171        assert_eq!(body("SELECT 1 + 2 AS \"quoted alias\""), "SELECT (1 + 2) AS \"quoted alias\"");
1172        assert_eq!(body("SELECT x AS \"select\" FROM t"), "SELECT x AS \"select\" FROM t");
1173        assert_eq!(body("SELECT t.* FROM t"), "SELECT t.* FROM t");
1174        assert_eq!(
1175            body("SELECT * REPLACE (x + 1 AS x) FROM t"),
1176            "SELECT * REPLACE ((x + 1) AS x) FROM t"
1177        );
1178    }
1179
1180    #[test]
1181    fn a_describe_gets_parentheses_round_what_it_describes() {
1182        assert_eq!(body("DESCRIBE SELECT 1"), "DESCRIBE (SELECT 1)");
1183    }
1184}