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