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