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
60//! `FILTER` keeps its own parentheses and parenthesises the condition inside them. `EXISTS` is
61//! written without a space before the parenthesis. `x IN (SELECT ...)` is `(x = ANY(SELECT ...))` and
62//! `x > ALL (SELECT ...)` is `(NOT (x <= ANY(SELECT ...)))`. A `WITH` loses the space after the last
63//! bracket, so it reads `WITH a AS (SELECT 1 AS n)SELECT n FROM a`, and a recursive one writes the
64//! column list as ` (n)` with a space. `LATERAL` goes. `TABLESAMPLE 10 PERCENT` is `TABLESAMPLE
65//! System(10.0 PERCENT)`. `CUBE (x, y)` and `ROLLUP (x, y)` are both written out as the
66//! `GROUPING SETS` they stand for. `{'a': 1}` is `struct_pack(a := 1)` and `MAP {'a': 1}` is
67//! `"map"(list_value('a'), list_value(1))`. A list comprehension is expanded into the three nested
68//! lambdas it is made of.
69
70use crate::ast::{
71    Ast, BinaryOp, CaseArm, CreateViewRef, Distinct, Expr, ExprRef, JoinKind, LiteralKind, Nulls,
72    Order, OrderItem, Quantifier, QueryBody, QueryRef, SelectRef, SetOp, Slice, Source, SourceRef,
73    StrRef, Target, UnaryOp, WindowBound, WindowExclude, WindowUnit,
74};
75use crate::matcher::NONE;
76use crate::tokenize::quoted;
77
78/// A `CREATE VIEW` written back out, which is what `duckdb_views()` reports as `sql`.
79///
80/// The name loses its qualification, which was measured: `CREATE VIEW main.v AS ...` comes back as
81/// `CREATE VIEW v AS ...`. So does `OR REPLACE` and so does `IF NOT EXISTS`, since what the column
82/// answers is what this view is and not what the statement that made it asked for.
83#[must_use]
84pub fn create_view(ast: &Ast, index: CreateViewRef) -> String {
85    let written = ast.create_view(index);
86    let name = ast.name(written.name).last().unwrap_or_default();
87    let temporary = if written.temporary { "TEMP " } else { "" };
88    let mut out = format!("CREATE {temporary}VIEW {}", quoted(name));
89    if !written.columns.is_empty() {
90        // A space before the parenthesis, where `CREATE TABLE t(x INTEGER)` has none. Both were
91        // measured and they really do differ.
92        out += &format!(" ({})", names(ast, written.columns));
93    }
94    out + &format!(" AS {};", query(ast, written.query))
95}
96
97/// One query written back out.
98#[must_use]
99pub fn query(ast: &Ast, index: QueryRef) -> String {
100    let held = ast.query(index);
101    let mut out = with(ast, held.ctes);
102    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/// The materialised `WITH` definitions in front of a query, or nothing when there are none.
137///
138/// The last bracket is not followed by a space, which is upstream's spacing and was measured: a
139/// view defined with one comes back as `WITH c AS MATERIALIZED (SELECT 1 AS x)SELECT * FROM c`. A
140/// column list is written with a space in front of it for the same reason.
141fn with(ast: &Ast, ctes: Slice) -> String {
142    if ctes.is_empty() {
143        return String::new();
144    }
145    let written: Vec<String> = ast
146        .cte_list(ctes)
147        .iter()
148        .map(|&index| {
149            let held = ast.cte(index);
150            let columns = if held.columns.is_empty() {
151                String::new()
152            } else {
153                format!(" ({})", names(ast, held.columns))
154            };
155            format!(
156                "{}{columns} AS MATERIALIZED ({})",
157                quoted(ast.string(held.name)),
158                query(ast, held.query)
159            )
160        })
161        .collect();
162    format!("WITH {}", written.join(", "))
163}
164
165/// A set operation, with the spacing bug upstream has in it.
166///
167/// Each side is wrapped in parentheses unless it is itself a set operation, in which case it is
168/// written bare. The bare case also loses the space that would follow it, which is why
169/// `a UNION b UNION c` comes back as `(a) UNION (b)UNION (c)` and not with a space there. That is
170/// the pin's output and it is a comparison, so it is what this writes.
171fn setop(
172    ast: &Ast,
173    op: SetOp,
174    quantifier: Quantifier,
175    by_name: bool,
176    left: QueryRef,
177    right: QueryRef,
178) -> String {
179    let word = match op {
180        SetOp::Union => "UNION",
181        SetOp::Except => "EXCEPT",
182        SetOp::Intersect => "INTERSECT",
183    };
184    // `UNION DISTINCT` comes back as `UNION`, since distinct is what the operator does anyway.
185    let all = if matches!(quantifier, Quantifier::All) { " ALL" } else { "" };
186    let named = if by_name { " BY NAME" } else { "" };
187    format!("{}{word}{all}{named} {}", branch(ast, left, true), branch(ast, right, false))
188}
189
190/// One side of a set operation, parenthesised unless it is a set operation itself.
191fn branch(ast: &Ast, index: QueryRef, left: bool) -> String {
192    let text = query(ast, index);
193    if matches!(ast.query(index).body, QueryBody::SetOp { .. }) {
194        return text;
195    }
196    if left { format!("({text}) ") } else { format!("({text})") }
197}
198
199/// One select block, without the modifiers that hang off the query around it.
200fn selection(ast: &Ast, index: SelectRef) -> String {
201    let held = ast.select(index);
202    let mut out = "SELECT".to_string();
203    match held.distinct {
204        Distinct::No => {}
205        Distinct::Yes => out += " DISTINCT",
206        Distinct::On(list) => out += &format!(" DISTINCT ON ({})", exprs(ast, list)),
207    }
208    let targets: Vec<String> =
209        ast.target_list(held.targets).iter().map(|target| aliased(ast, target)).collect();
210    out += &format!(" {}", targets.join(", "));
211    if !held.from.is_empty() {
212        // A space before the comma, which is upstream's and was measured on `FROM t t1, t t2`.
213        let sources: Vec<String> =
214            ast.source_list(held.from).iter().map(|&index| source(ast, index)).collect();
215        out += &format!(" FROM {}", sources.join(" , "));
216    }
217    if held.filter != NONE {
218        out += &format!(" WHERE {}", expr(ast, held.filter));
219    }
220    if held.group_by_all {
221        out += " GROUP BY ALL";
222    } else if !held.group_by.is_empty() {
223        out += &format!(" GROUP BY {}", exprs(ast, held.group_by));
224    }
225    if held.having != NONE {
226        out += &format!(" HAVING {}", expr(ast, held.having));
227    }
228    out
229}
230
231/// One entry of a target list, with its alias if it was given one.
232fn aliased(ast: &Ast, target: &Target) -> String {
233    let written = expr(ast, target.expr);
234    if target.alias == NONE {
235        return written;
236    }
237    format!("{written} AS {}", quoted(ast.string(target.alias)))
238}
239
240/// One entry of an order by list.
241fn order(ast: &Ast, item: &OrderItem) -> String {
242    let mut out = expr(ast, item.expr);
243    match item.order {
244        Order::Unstated => {}
245        Order::Ascending => out += " ASC",
246        Order::Descending => out += " DESC",
247    }
248    match item.nulls {
249        Nulls::Unstated => {}
250        Nulls::First => out += " NULLS FIRST",
251        Nulls::Last => out += " NULLS LAST",
252    }
253    out
254}
255
256/// One entry of a `FROM` clause.
257fn source(ast: &Ast, index: SourceRef) -> String {
258    match ast.source(index) {
259        Source::Table { name, alias, columns } => label(ast, parts(ast, name), alias, columns),
260        // The name it was written with, since the definition is somewhere else in the tree and a
261        // reference to it is a name where a table goes.
262        Source::Cte { cte, alias, columns } => {
263            label(ast, quoted(ast.string(ast.cte(cte).name)), alias, columns)
264        }
265        Source::Subquery { query: inner, alias, columns } => {
266            label(ast, format!("({})", query(ast, inner)), alias, columns)
267        }
268        Source::Function { name, args, alias, columns, .. } => {
269            let written: Vec<String> =
270                ast.target_list(args).iter().map(|arg| argument(ast, arg)).collect();
271            let call = format!("{}({})", parts(ast, name), written.join(", "));
272            label(ast, call, alias, columns)
273        }
274        // A `VALUES` in a `FROM` clause is wrapped in a select of its own, named `valueslist`, and
275        // then given whatever alias was written. Measured, including the name.
276        Source::Values { rows, alias, columns } => {
277            let inner = format!("(SELECT * FROM ({}) AS valueslist)", values(ast, rows));
278            label(ast, inner, alias, columns)
279        }
280        Source::Join { left, right, kind, natural, on, using } => {
281            let word = match kind {
282                JoinKind::Inner => "INNER",
283                JoinKind::Left => "LEFT",
284                JoinKind::Right => "RIGHT",
285                // `FULL OUTER JOIN` loses the `OUTER`, and a `NATURAL JOIN` gains an `INNER`.
286                JoinKind::Full => "FULL",
287                JoinKind::Semi => "SEMI",
288                JoinKind::Anti => "ANTI",
289                JoinKind::Cross => "CROSS",
290                JoinKind::Positional => "POSITIONAL",
291            };
292            let natural = if natural { "NATURAL " } else { "" };
293            let mut out =
294                format!("({} {natural}{word} JOIN {}", source(ast, left), source(ast, right));
295            if on != NONE {
296                // A second pair of parentheses around a condition that has its own, so an equality
297                // comes out as `ON ((a.x = b.y))`.
298                out += &format!(" ON ({})", expr(ast, on));
299            }
300            if !using.is_empty() {
301                out += &format!(" USING ({})", names(ast, using));
302            }
303            out + ")"
304        }
305    }
306}
307
308/// One argument of a table function, which is an expression or a name and an expression.
309///
310/// A named one is parenthesised and written with `=`, so `read_csv(f, header = true)` comes back as
311/// `read_csv(f, ("header" = true))`. The name goes through the quoting rule like any other
312/// identifier, which is why `header` gains quotes there.
313fn argument(ast: &Ast, arg: &Target) -> String {
314    if arg.alias == NONE {
315        return expr(ast, arg.expr);
316    }
317    format!("({} = {})", quoted(ast.string(arg.alias)), expr(ast, arg.expr))
318}
319
320/// A from item with its alias and column list, if it was given either.
321fn label(ast: &Ast, written: String, alias: StrRef, columns: Slice) -> String {
322    let mut out = written;
323    if alias != NONE {
324        out += &format!(" AS {}", quoted(ast.string(alias)));
325    }
326    if !columns.is_empty() {
327        out += &format!("({})", names(ast, columns));
328    }
329    out
330}
331
332/// The rows of a `VALUES`, with the keyword in front of them.
333fn values(ast: &Ast, rows: Slice) -> String {
334    let written: Vec<String> =
335        ast.rows(rows).iter().map(|&row| format!("({})", exprs(ast, row))).collect();
336    format!("VALUES {}", written.join(", "))
337}
338
339/// One expression, written back out.
340///
341/// Public because the binder names an unaliased target after the text that produced it, and for a
342/// window call that text is this one: `SELECT sum(x) OVER (ORDER BY x)` has a column called
343/// `sum(x) OVER (ORDER BY x)` and getting there any other way would be a second deparser.
344pub fn expression(ast: &Ast, index: ExprRef) -> String {
345    expr(ast, index)
346}
347
348/// One expression.
349fn expr(ast: &Ast, index: ExprRef) -> String {
350    match ast.expr(index) {
351        Expr::Star { qualifier, replacements } => star(ast, qualifier, replacements),
352        Expr::Column { name } => parts(ast, name),
353        Expr::Literal { kind, text } => literal(ast, kind, text),
354        Expr::Unary { op, operand } => unary(ast, op, operand),
355        Expr::Binary { op, left, right } => binary(ast, op, left, right),
356        Expr::Function { name, args, distinct, filter } => call(ast, name, args, distinct, filter),
357        held @ Expr::Window { .. } => window(ast, held),
358        Expr::Cast { operand, ty, try_cast } => {
359            let word = if try_cast { "TRY_CAST" } else { "CAST" };
360            format!("{word}({} AS {})", expr(ast, operand), typename(ast.string(ty)))
361        }
362        Expr::Case { operand, arms, otherwise } => case(ast, operand, arms, otherwise),
363        Expr::Between { operand, low, high, negated } => {
364            let written = format!(
365                "({} BETWEEN {} AND {})",
366                expr(ast, operand),
367                expr(ast, low),
368                expr(ast, high)
369            );
370            if negated { format!("(NOT {written})") } else { written }
371        }
372        Expr::In { operand, list, negated } => {
373            let written = format!("({} IN ({}))", expr(ast, operand), exprs(ast, list));
374            if negated { format!("(NOT {written})") } else { written }
375        }
376        Expr::InSubquery { operand, query: inner, negated } => {
377            let any = format!("({} = ANY({}))", expr(ast, operand), query(ast, inner));
378            if negated { format!("(NOT {any})") } else { any }
379        }
380        Expr::QuantifiedSubquery { operand, op, query: inner, all } => {
381            let (op, negate) = if all { (negated_comparison(op), true) } else { (op, false) };
382            let word = comparison_word(op);
383            let any = format!("({} {word} ANY({}))", expr(ast, operand), query(ast, inner));
384            if negate { format!("(NOT {any})") } else { any }
385        }
386        Expr::Parameter { name } => format!("${}", ast.string(name)),
387        // A bracketed list is a call to `list_value`, including when it is empty.
388        Expr::List { items } => format!("list_value({})", exprs(ast, items)),
389        // And a parenthesised list is a call to `row`, which needs its quotes because it is a
390        // keyword.
391        Expr::Row { items } => format!("\"row\"({})", exprs(ast, items)),
392        // Upstream brackets a lambda whole, and names the parameters the way it names columns.
393        Expr::Lambda { params, body } => {
394            let params: Vec<String> = ast.name(params).map(quoted).collect();
395            format!("(lambda {}: {})", params.join(", "), expr(ast, body))
396        }
397        Expr::Subquery { query: inner } => format!("({})", query(ast, inner)),
398        Expr::Exists { query: inner, negated } => {
399            let exists = format!("EXISTS({})", query(ast, inner));
400            if negated { format!("(NOT {exists})") } else { exists }
401        }
402    }
403}
404
405fn comparison_word(op: BinaryOp) -> &'static str {
406    match op {
407        BinaryOp::Eq => "=",
408        BinaryOp::NotEq => "!=",
409        BinaryOp::Lt => "<",
410        BinaryOp::Gt => ">",
411        BinaryOp::LtEq => "<=",
412        BinaryOp::GtEq => ">=",
413        _ => unreachable!("the grammar permits only a comparison before ANY or ALL"),
414    }
415}
416
417fn negated_comparison(op: BinaryOp) -> BinaryOp {
418    match op {
419        BinaryOp::Eq => BinaryOp::NotEq,
420        BinaryOp::NotEq => BinaryOp::Eq,
421        BinaryOp::Lt => BinaryOp::GtEq,
422        BinaryOp::Gt => BinaryOp::LtEq,
423        BinaryOp::LtEq => BinaryOp::Gt,
424        BinaryOp::GtEq => BinaryOp::Lt,
425        _ => unreachable!("the grammar permits only a comparison before ANY or ALL"),
426    }
427}
428
429/// A star, with the qualifier and the replace list it may have been written with.
430fn star(ast: &Ast, qualifier: Slice, replacements: Slice) -> String {
431    let mut out =
432        if qualifier.is_empty() { "*".to_string() } else { format!("{}.*", parts(ast, qualifier)) };
433    if !replacements.is_empty() {
434        let written: Vec<String> =
435            ast.target_list(replacements).iter().map(|target| aliased(ast, target)).collect();
436        out += &format!(" REPLACE ({})", written.join(", "));
437    }
438    out
439}
440
441/// One literal.
442fn literal(ast: &Ast, kind: LiteralKind, text: StrRef) -> String {
443    match kind {
444        // `true` and `false` in lower case, which is the pin's spelling whichever way they were
445        // written.
446        LiteralKind::Null => "NULL".to_string(),
447        LiteralKind::True => "true".to_string(),
448        LiteralKind::False => "false".to_string(),
449        LiteralKind::Number => number(ast.string(text)),
450        LiteralKind::String => string(ast.string(text)),
451        // A blob prints as a string of its escaped form cast to `BLOB`, so `X'ab'` comes back as
452        // `'\xAB'::BLOB`.
453        LiteralKind::Blob => format!("{}::BLOB", string(ast.string(text))),
454    }
455}
456
457/// A numeric literal, written back as the value it was read as rather than as the text.
458///
459/// The value is what upstream prints, so the shape of the literal decides the shape of the answer.
460/// A literal with an exponent in it is a DOUBLE and comes back in whatever form a double prints in.
461/// One with a point in it is a DECIMAL of the width and scale that were written, so the digits after
462/// the point survive exactly, trailing zeros and all, and only the digits in front of it are tidied.
463/// One with neither is an integer. The underscores a long number can be written with are a way of
464/// writing it and not part of it, so `1_000` is `1000` in all three.
465fn number(written: &str) -> String {
466    let text = written.replace('_', "");
467    if text.contains(['e', 'E']) {
468        return double(&text);
469    }
470    let Some((whole, fraction)) = text.split_once('.') else {
471        return leading(&text).to_string();
472    };
473    // `1.` is a decimal of scale zero, which prints without the point, and `.5` keeps the empty
474    // side it was written with rather than growing a zero. Both were measured.
475    if fraction.is_empty() {
476        return leading(whole).to_string();
477    }
478    format!("{}.{fraction}", if whole.is_empty() { "" } else { leading(whole) })
479}
480
481/// A run of digits with the zeros in front of it dropped, down to one digit.
482fn leading(digits: &str) -> &str {
483    let trimmed = digits.trim_start_matches('0');
484    if trimmed.is_empty() { &digits[digits.len().saturating_sub(1)..] } else { trimmed }
485}
486
487/// A double, in the form the formatting library upstream uses prints one in.
488///
489/// Plain digits while the decimal exponent is between minus four and fifteen, and the exponent form
490/// outside that, with at least two digits of exponent and a sign that is written even when it is a
491/// plus. So `1e3` is `1000.0`, `1e15` is `1000000000000000.0`, `1e16` is `1e+16`, `5e-4` is `0.0005`
492/// and `5e-5` is `5e-05`. A plain one always has a point in it, which is what tells a double from an
493/// integer when it is read back.
494fn double(text: &str) -> String {
495    let Ok(value) = text.parse::<f64>() else {
496        return text.to_string();
497    };
498    // The shortest digits that read back as this value, which is what `{:e}` is, and the exponent
499    // that goes with them. Rust writes that form as `2.5e-5`, so the exponent is the tail.
500    let shortest = format!("{value:e}");
501    let (mantissa, exponent) = shortest.split_once('e').unwrap_or((shortest.as_str(), "0"));
502    let exponent: i32 = exponent.parse().unwrap_or(0);
503    if (-4..=15).contains(&exponent) {
504        let plain = format!("{value}");
505        return if plain.contains('.') { plain } else { plain + ".0" };
506    }
507    let sign = if exponent < 0 { '-' } else { '+' };
508    format!("{mantissa}e{sign}{:02}", exponent.abs())
509}
510
511/// A string literal, with the one character that has to be escaped escaped.
512///
513/// Only the quote. A newline written as `e'\n'` comes back as a real newline inside the quotes,
514/// which was measured, so everything else goes out as the byte it is.
515fn string(text: &str) -> String {
516    format!("'{}'", text.replace('\'', "''"))
517}
518
519/// A prefix or postfix operator.
520fn unary(ast: &Ast, op: UnaryOp, operand: ExprRef) -> String {
521    // `-1` is a number and not a negation of one, so a minus in front of a numeric constant folds
522    // into it and `- -3` folds twice and comes back as `3`. A plus does not fold, which is why
523    // `+3` comes back as `+(3)`.
524    if matches!(op, UnaryOp::Negate) {
525        if let Some(number) = negated(ast, operand) {
526            return number;
527        }
528    }
529    let written = expr(ast, operand);
530    match op {
531        UnaryOp::Not => format!("(NOT {written})"),
532        UnaryOp::Negate => format!("-({written})"),
533        UnaryOp::Plus => format!("+({written})"),
534        UnaryOp::BitNot => format!("~({written})"),
535        // `x!` is a call to `factorial` by the time it is printed.
536        UnaryOp::Factorial => format!("factorial({written})"),
537        UnaryOp::IsNull => format!("({written} IS NULL)"),
538        UnaryOp::IsNotNull => format!("({written} IS NOT NULL)"),
539        // `IS UNKNOWN` is `IS NULL` and nothing else, so it prints as the thing it means.
540        UnaryOp::IsUnknown => format!("({written} IS NULL)"),
541        UnaryOp::IsNotUnknown => format!("({written} IS NOT NULL)"),
542        // And the four tests against a boolean are a cast and a distinct test, which is what they
543        // are defined to be: `x IS TRUE` is false rather than null for a null `x`, and a plain
544        // `x = true` would not be.
545        UnaryOp::IsTrue => distinct(&written, "true", true),
546        UnaryOp::IsNotTrue => distinct(&written, "true", false),
547        UnaryOp::IsFalse => distinct(&written, "false", true),
548        UnaryOp::IsNotFalse => distinct(&written, "false", false),
549    }
550}
551
552/// What `IS TRUE` and its three relatives are written as.
553fn distinct(operand: &str, against: &str, same: bool) -> String {
554    let word = if same { "IS NOT DISTINCT FROM" } else { "IS DISTINCT FROM" };
555    format!("(CAST({operand} AS BOOLEAN) {word} {against})")
556}
557
558/// The text of a numeric constant with a minus applied to it, and `None` for anything else.
559///
560/// Recursive, because the fold happens on the way in and applies again to what it produced. A minus
561/// in front of a minus in front of `3` is the constant `3`.
562fn negated(ast: &Ast, index: ExprRef) -> Option<String> {
563    match ast.expr(index) {
564        Expr::Literal { kind: LiteralKind::Number, text } => {
565            Some(format!("-{}", number(ast.string(text))))
566        }
567        Expr::Unary { op: UnaryOp::Negate, operand } => {
568            let inner = negated(ast, operand)?;
569            Some(inner.strip_prefix('-').unwrap_or(&inner).to_string())
570        }
571        _ => None,
572    }
573}
574
575/// An infix operator, parenthesised.
576fn binary(ast: &Ast, op: BinaryOp, left: ExprRef, right: ExprRef) -> String {
577    let (left, right) = (expr(ast, left), expr(ast, right));
578    // The three that are not written as an operator at all.
579    match op {
580        BinaryOp::SimilarTo => return format!("regexp_full_match({left}, {right})"),
581        BinaryOp::NotSimilarTo => return format!("(NOT regexp_full_match({left}, {right}))"),
582        // The arguments swap, so `ts AT TIME ZONE 'UTC'` is `timezone('UTC', ts)`.
583        BinaryOp::AtTimeZone => return format!("timezone({right}, {left})"),
584        // And the one that is written as an operator and is not parenthesised.
585        BinaryOp::Collate => return format!("{left} COLLATE {right}"),
586        _ => {}
587    }
588    let word = match op {
589        BinaryOp::Or => "OR",
590        BinaryOp::And => "AND",
591        BinaryOp::Eq => "=",
592        BinaryOp::NotEq => "!=",
593        BinaryOp::Lt => "<",
594        BinaryOp::Gt => ">",
595        BinaryOp::LtEq => "<=",
596        BinaryOp::GtEq => ">=",
597        BinaryOp::IsDistinctFrom => "IS DISTINCT FROM",
598        BinaryOp::IsNotDistinctFrom => "IS NOT DISTINCT FROM",
599        BinaryOp::Add => "+",
600        BinaryOp::Subtract => "-",
601        BinaryOp::Multiply => "*",
602        BinaryOp::Divide => "/",
603        BinaryOp::IntegerDivide => "//",
604        BinaryOp::Modulo => "%",
605        // Whichever of `^` and `**` was written is what the pin prints, and both arrive here as one
606        // operator, so one spelling has to stand for both. See the module doc.
607        BinaryOp::Power => "**",
608        BinaryOp::BitAnd => "&",
609        BinaryOp::BitOr => "|",
610        BinaryOp::ShiftLeft => "<<",
611        BinaryOp::ShiftRight => ">>",
612        BinaryOp::Concat => "||",
613        // The four pattern operators have a word spelling and a symbol spelling, and the symbol is
614        // what comes back whichever was written.
615        BinaryOp::Like => "~~",
616        BinaryOp::NotLike => "!~~",
617        BinaryOp::ILike => "~~*",
618        BinaryOp::NotILike => "!~~*",
619        BinaryOp::Glob => "~~~",
620        BinaryOp::Regex => "~",
621        BinaryOp::NotRegex => "!~",
622        BinaryOp::RegexInsensitive => "~*",
623        BinaryOp::NotRegexInsensitive => "!~*",
624        BinaryOp::Arrow => "->",
625        BinaryOp::LongArrow => "->>",
626        BinaryOp::Contains => "@>",
627        BinaryOp::ContainedBy => "<@",
628        BinaryOp::Overlaps => "&&",
629        BinaryOp::StartsWith => "^@",
630        BinaryOp::InetContainedByOrEq => "<<=",
631        BinaryOp::InetContainsOrEq => ">>=",
632        BinaryOp::Named(name) => ast.string(name),
633        BinaryOp::SimilarTo | BinaryOp::NotSimilarTo | BinaryOp::AtTimeZone | BinaryOp::Collate => {
634            unreachable!("the four that return above")
635        }
636    };
637    format!("({left} {word} {right})")
638}
639
640/// A function call.
641fn call(ast: &Ast, name: Slice, args: Slice, distinct: bool, filter: ExprRef) -> String {
642    let written = parts(ast, name);
643    let list = ast.expr_list(args);
644    // `count(*)` is a different function from `count`, and the star is how it is spelled rather than
645    // an argument it takes, so it prints under the name it really has. `count()` with nothing in it
646    // is the third spelling of the same function and prints under that name too.
647    if written.eq_ignore_ascii_case("count") {
648        let starred = list.len() == 1
649            && matches!(ast.expr(list[0]), Expr::Star { qualifier, replacements }
650                if qualifier.is_empty() && replacements.is_empty());
651        if starred || list.is_empty() {
652            return format!("count_star(){}", filtered(ast, filter));
653        }
654    }
655    let word = if distinct { "DISTINCT " } else { "" };
656    format!(
657        "{}({word}{}){}",
658        operator(ast, name, &written),
659        exprs(ast, args),
660        filtered(ast, filter)
661    )
662}
663
664/// The `FILTER` a call was written with, or nothing at all when it was written without one.
665///
666/// The word `WHERE` is always printed even when it was not written, because upstream prints it: a
667/// view defined with `FILTER (x > 1)` comes back with `FILTER (WHERE (x > 1))`.
668fn filtered(ast: &Ast, filter: ExprRef) -> String {
669    if filter == NONE { String::new() } else { format!(" FILTER (WHERE {})", expr(ast, filter)) }
670}
671
672/// A call with its window, which is the form a window target with no alias is named after.
673///
674/// The frame is printed only when it is not the default one, which is what upstream does and which
675/// is why `sum(x) OVER (ORDER BY x RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)` comes back
676/// as `sum(x) OVER (ORDER BY x)`. A single bound is printed as the pair it stands for, so
677/// `ROWS UNBOUNDED PRECEDING` comes back as `ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW`.
678///
679/// The whole expression is taken rather than its parts, because there are eight of them now and a
680/// call with eight arguments is one the reader has to count along to read.
681fn window(ast: &Ast, held: Expr) -> String {
682    let Expr::Window { name, args, distinct, filter, ignore_nulls, order: sorted, spec } = held
683    else {
684        return String::new();
685    };
686    let word = if distinct { "DISTINCT " } else { "" };
687    // The `ORDER BY` inside the brackets goes back where it was written, after the arguments and
688    // before `IGNORE NULLS`, which is the order the grammar has them in.
689    let sorted = if sorted.is_empty() {
690        String::new()
691    } else {
692        let items: Vec<String> =
693            ast.order_list(sorted).iter().map(|item| order(ast, item)).collect();
694        format!(" ORDER BY {}", items.join(", "))
695    };
696    // `RESPECT NULLS` is the default and upstream drops it, so only the other one is written.
697    let nulls = if ignore_nulls { " IGNORE NULLS" } else { "" };
698    let written = parts(ast, name);
699    // `count(*) OVER ()` comes back as `count() OVER ()`, where the same call without a window
700    // comes back as `count_star()`. The star goes and the name stays, which is upstream's answer
701    // and not the one the ordinary call path gives.
702    let list = ast.expr_list(args);
703    let bare = list.len() == 1
704        && matches!(ast.expr(list[0]), Expr::Star { qualifier, replacements }
705            if qualifier.is_empty() && replacements.is_empty());
706    let inner = if bare { String::new() } else { exprs(ast, args) };
707    let call = format!(
708        "{}({word}{inner}{sorted}{nulls}){}",
709        operator(ast, name, &written),
710        filtered(ast, filter)
711    );
712    let held = ast.window(spec);
713    let mut inside: Vec<String> = Vec::new();
714    if !held.partition.is_empty() {
715        inside.push(format!("PARTITION BY {}", exprs(ast, held.partition)));
716    }
717    if !held.order.is_empty() {
718        let items: Vec<String> =
719            ast.order_list(held.order).iter().map(|item| order(ast, item)).collect();
720        inside.push(format!("ORDER BY {}", items.join(", ")));
721    }
722    if !held.frame_is_default() {
723        let unit = match held.unit {
724            WindowUnit::Rows => "ROWS",
725            WindowUnit::Range => "RANGE",
726            WindowUnit::Groups => "GROUPS",
727        };
728        let mut frame =
729            format!("{unit} BETWEEN {} AND {}", bound(ast, held.start), bound(ast, held.end));
730        frame += match held.exclude {
731            WindowExclude::NoOthers => "",
732            WindowExclude::CurrentRow => " EXCLUDE CURRENT ROW",
733            WindowExclude::Group => " EXCLUDE GROUP",
734            WindowExclude::Ties => " EXCLUDE TIES",
735        };
736        inside.push(frame);
737    }
738    format!("{call} OVER ({})", inside.join(" "))
739}
740
741/// One end of a window frame.
742fn bound(ast: &Ast, end: WindowBound) -> String {
743    match end {
744        WindowBound::UnboundedPreceding => "UNBOUNDED PRECEDING".to_string(),
745        WindowBound::Preceding(offset) => format!("{} PRECEDING", expr(ast, offset)),
746        WindowBound::CurrentRow => "CURRENT ROW".to_string(),
747        WindowBound::Following(offset) => format!("{} FOLLOWING", expr(ast, offset)),
748        WindowBound::UnboundedFollowing => "UNBOUNDED FOLLOWING".to_string(),
749    }
750}
751
752/// The name a call is written back under, which is the name it was written with for all but two.
753///
754/// `coalesce` and `ifnull` are grammar rules rather than function names, so they come back as the
755/// one thing the rule stands for, upper case and unquoted. That holds for the one argument form as
756/// well: `coalesce(x)` is `COALESCE(x)` and not `x`. No other name does this, which was measured,
757/// and `nullif` is the one to check against because it looks like it should and does not.
758fn operator(ast: &Ast, name: Slice, written: &str) -> String {
759    let one = ast.name(name).next().unwrap_or_default();
760    let alone = ast.name(name).count() == 1;
761    if alone && (one.eq_ignore_ascii_case("coalesce") || one.eq_ignore_ascii_case("ifnull")) {
762        return "COALESCE".to_string();
763    }
764    written.to_string()
765}
766
767/// A `CASE`, always searched and always with an `ELSE`.
768///
769/// A simple `CASE x WHEN 1 THEN 'a'` is rewritten into `CASE WHEN x = 1 THEN 'a' ELSE NULL END` on
770/// the way in, so both forms print the same way. The two spaces after `CASE` are upstream leaving
771/// the operand slot empty and writing the space around it anyway.
772fn case(ast: &Ast, operand: ExprRef, arms: Slice, otherwise: ExprRef) -> String {
773    let mut out = "CASE ".to_string();
774    for arm in ast.arm_list(arms) {
775        let when = when(ast, operand, arm);
776        out += &format!(" WHEN ({when}) THEN ({})", expr(ast, arm.then));
777    }
778    let last = if otherwise == NONE { "NULL".to_string() } else { expr(ast, otherwise) };
779    out + &format!(" ELSE {last} END")
780}
781
782/// The condition of one arm, which is the arm's own for a searched `CASE` and an equality for a
783/// simple one.
784fn when(ast: &Ast, operand: ExprRef, arm: &CaseArm) -> String {
785    if operand == NONE {
786        return expr(ast, arm.when);
787    }
788    format!("({} = {})", expr(ast, operand), expr(ast, arm.when))
789}
790
791/// A type as upstream writes one, which is two rules and not one.
792///
793/// A name the SQL standard spells is resolved and written back under the one name its type has, so
794/// `int` is `INTEGER`, `numeric(5)` is `DECIMAL(5)`, `character varying` is `VARCHAR` and `real` is
795/// `FLOAT`. Every other name is written back exactly as somebody typed it, case and all and without
796/// quotes, so `text` stays `text`, `TEXT` stays `TEXT` and `int4` stays `int4`. All of that was
797/// measured a name at a time, and the split is not arbitrary: the standard names are the ones the
798/// grammar has rules for, and everything else is a name the parser hands to the catalog to look up
799/// later, so the text is all it has.
800///
801/// This walks the text rather than going through the type system, because the type system throws
802/// away what has to survive here. `DECIMAL(5)` and `DECIMAL` both become a width and a scale, and
803/// `VARCHAR(10)` becomes `VARCHAR`, but upstream prints back the length that was written.
804fn typename(text: &str) -> String {
805    let text = text.trim();
806    // A trailing `[]` or `[3]` is a list or an array of whatever is in front of it, and the element
807    // is resolved the same way: `int[]` is `INTEGER[]` while `int4[]` stays `int4[]`.
808    if let Some(open) = suffix(text) {
809        return typename(&text[..open]) + &text[open..];
810    }
811    let (base, arguments) = arguments(text);
812    let Some(name) = standard(base) else {
813        let base = unquote(base);
814        return match arguments {
815            Some(arguments) => format!("{}({arguments})", catalogued(&base)),
816            None => catalogued(&base),
817        };
818    };
819    match (name, arguments) {
820        // `STRUCT(a bool)` and `UNION(a int)` are a name and a type each, and the name keeps the
821        // case it was written in while the type goes round again.
822        ("STRUCT" | "UNION", Some(inside)) => {
823            let written: Vec<String> = pieces(inside).iter().map(|piece| field(piece)).collect();
824            format!("{name}({})", written.join(", "))
825        }
826        ("MAP", Some(inside)) => {
827            let written: Vec<String> = pieces(inside).iter().map(|piece| typename(piece)).collect();
828            format!("{name}({})", written.join(", "))
829        }
830        // The width and the scale of a decimal and the length of a string survive, because upstream
831        // prints the modifiers it was given rather than the ones the type ended up with.
832        ("DECIMAL" | "VARCHAR", Some(inside)) => {
833            format!("{name}({})", pieces(inside).join(", "))
834        }
835        // And everything else drops them, because they chose the type rather than sitting on it.
836        // `float(10)` is a `FLOAT` and there is nothing left of the ten.
837        _ => name.to_string(),
838    }
839}
840
841/// A type name the grammar has no rule for, which is a name for the catalog to look up later.
842///
843/// Written back as it stands, with the case it was written in and with no quotes, because all the
844/// parser has is the text. `bool`, `TEXT`, `int4`, `timestamptz` and `Mixed` all come back exactly
845/// as they went in, which was measured a name at a time.
846///
847/// `json` is the one exception in the whole list and it comes back quoted. That is not a rule about
848/// json, it is what happens to a name the parser resolves on its own rather than leaving for the
849/// catalog: the type it lands on carries the written name as its label, and a label is written back
850/// through the identifier rule, which quotes a keyword. `json` is the only name that is both a
851/// keyword and one of those, so it is the only one where the difference shows. The case that was
852/// written survives it, so `JSON` is `"JSON"` and `json` is `"json"`.
853fn catalogued(base: &str) -> String {
854    if base.eq_ignore_ascii_case("json") { quoted(base) } else { base.to_string() }
855}
856
857/// A name with its quotes taken off, if it had any.
858fn unquote(base: &str) -> String {
859    match base.strip_prefix('"').and_then(|rest| rest.strip_suffix('"')) {
860        Some(inside) => inside.replace("\"\"", "\""),
861        None => base.to_string(),
862    }
863}
864
865/// Where the trailing `[]` or `[3]` of a list or an array type starts, if there is one.
866fn suffix(text: &str) -> Option<usize> {
867    let rest = text.strip_suffix(']')?;
868    let open = rest.rfind('[')?;
869    rest[open + 1..].bytes().all(|byte| byte.is_ascii_digit()).then_some(open)
870}
871
872/// A type split into the name and whatever was in the parentheses after it.
873fn arguments(text: &str) -> (&str, Option<&str>) {
874    let Some(rest) = text.strip_suffix(')') else {
875        return (text, None);
876    };
877    let mut depth = 0usize;
878    for (at, byte) in rest.bytes().enumerate() {
879        match byte {
880            b'(' if depth == 0 => depth = 1,
881            b'(' => depth += 1,
882            b')' => depth -= 1,
883            _ => continue,
884        }
885        if depth == 1 && byte == b'(' {
886            return (rest[..at].trim(), Some(rest[at + 1..].trim()));
887        }
888    }
889    (text, None)
890}
891
892/// The entries of an argument list, split on the commas that are not inside anything.
893fn pieces(inside: &str) -> Vec<&str> {
894    let mut found = Vec::new();
895    let (mut depth, mut quoted, mut start) = (0usize, false, 0usize);
896    for (at, byte) in inside.bytes().enumerate() {
897        match byte {
898            b'"' => quoted = !quoted,
899            b'(' | b'[' if !quoted => depth += 1,
900            b')' | b']' if !quoted => depth = depth.saturating_sub(1),
901            b',' if !quoted && depth == 0 => {
902                found.push(inside[start..at].trim());
903                start = at + 1;
904            }
905            _ => {}
906        }
907    }
908    found.push(inside[start..].trim());
909    found
910}
911
912/// One field of a `STRUCT` or a `UNION`, which is a name and then a type.
913fn field(piece: &str) -> String {
914    let mut quoting = false;
915    for (at, byte) in piece.bytes().enumerate() {
916        match byte {
917            b'"' => quoting = !quoting,
918            byte if byte.is_ascii_whitespace() && !quoting => {
919                let name = piece[..at].trim();
920                let name =
921                    if name.starts_with('"') { quoted(&unquote(name)) } else { name.to_string() };
922                return format!("{name} {}", typename(&piece[at + 1..]));
923            }
924            _ => {}
925        }
926    }
927    piece.to_string()
928}
929
930/// The one name a type the SQL standard spells is written back under, and nothing for any other.
931///
932/// Several words for the ones the standard writes with several. The list is short because it is the
933/// standard's list and not DuckDB's: `HUGEINT`, `TEXT`, `BLOB` and the rest of the names DuckDB adds
934/// are not in here, and they are the ones that come back exactly as they were written.
935fn standard(base: &str) -> Option<&'static str> {
936    const NAMES: &[(&str, &str)] = &[
937        ("BOOLEAN", "BOOLEAN"),
938        ("INT", "INTEGER"),
939        ("INTEGER", "INTEGER"),
940        ("SMALLINT", "SMALLINT"),
941        ("BIGINT", "BIGINT"),
942        ("DEC", "DECIMAL"),
943        ("DECIMAL", "DECIMAL"),
944        ("NUMERIC", "DECIMAL"),
945        ("REAL", "FLOAT"),
946        ("FLOAT", "FLOAT"),
947        ("DOUBLE PRECISION", "DOUBLE"),
948        ("CHAR", "VARCHAR"),
949        ("CHARACTER", "VARCHAR"),
950        ("CHARACTER VARYING", "VARCHAR"),
951        ("NATIONAL CHARACTER", "VARCHAR"),
952        ("NATIONAL CHARACTER VARYING", "VARCHAR"),
953        ("VARCHAR", "VARCHAR"),
954        ("BIT", "BIT"),
955        ("DATE", "DATE"),
956        ("TIME", "TIME"),
957        ("TIME WITH TIME ZONE", "TIME WITH TIME ZONE"),
958        ("TIME WITHOUT TIME ZONE", "TIME"),
959        ("TIMESTAMP", "TIMESTAMP"),
960        ("TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH TIME ZONE"),
961        ("TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMP"),
962        ("INTERVAL", "INTERVAL"),
963        ("STRUCT", "STRUCT"),
964        ("UNION", "UNION"),
965        ("MAP", "MAP"),
966    ];
967    let written: Vec<&str> = base.split_whitespace().collect();
968    let written = written.join(" ");
969    NAMES
970        .iter()
971        .find(|(spelling, _)| spelling.eq_ignore_ascii_case(&written))
972        .map(|(_, name)| *name)
973}
974
975/// A run of expressions, comma separated.
976fn exprs(ast: &Ast, list: Slice) -> String {
977    let written: Vec<String> = ast.expr_list(list).iter().map(|&item| expr(ast, item)).collect();
978    written.join(", ")
979}
980
981/// A run of identifiers, comma separated, each quoted if it has to be.
982fn names(ast: &Ast, list: Slice) -> String {
983    ast.name(list).map(quoted).collect::<Vec<_>>().join(", ")
984}
985
986/// A dotted name, each part quoted if it has to be.
987fn parts(ast: &Ast, list: Slice) -> String {
988    ast.name(list).map(quoted).collect::<Vec<_>>().join(".")
989}
990
991#[cfg(test)]
992mod tests {
993    use super::create_view;
994    use crate::ast::Statement;
995    use crate::transform::parse_ast;
996
997    /// The whole statement, deparsed.
998    fn whole(sql: &str) -> String {
999        let ast = parse_ast(sql).unwrap_or_else(|error| panic!("{sql} should parse: {error}"));
1000        let Statement::CreateView(index) = ast.statements[0] else {
1001            panic!("that was not a create view");
1002        };
1003        create_view(&ast, index)
1004    }
1005
1006    /// Just the body, which is what most of these are about.
1007    fn body(query: &str) -> String {
1008        let written = whole(&format!("CREATE VIEW v AS {query}"));
1009        written
1010            .strip_prefix("CREATE VIEW v AS ")
1011            .and_then(|rest| rest.strip_suffix(';'))
1012            .expect("the statement wrapper is there")
1013            .to_string()
1014    }
1015
1016    #[test]
1017    fn a_statement_loses_its_qualification_and_its_or_replace() {
1018        assert_eq!(whole("CREATE VIEW main.v AS SELECT 1"), "CREATE VIEW v AS SELECT 1;");
1019        assert_eq!(whole("CREATE OR REPLACE VIEW v AS SELECT 1"), "CREATE VIEW v AS SELECT 1;");
1020        assert_eq!(whole("CREATE VIEW IF NOT EXISTS v AS SELECT 1"), "CREATE VIEW v AS SELECT 1;");
1021        assert_eq!(whole("CREATE TEMP VIEW v AS SELECT 1"), "CREATE TEMP VIEW v AS SELECT 1;");
1022    }
1023
1024    /// A space before the parenthesis here, and none in a `CREATE TABLE`. Both measured.
1025    #[test]
1026    fn an_alias_list_is_written_with_a_space_in_front_of_it() {
1027        assert_eq!(
1028            whole(r#"CREATE VIEW v ("Weird Name", "x y") AS SELECT 1, 2"#),
1029            r#"CREATE VIEW v ("Weird Name", "x y") AS SELECT 1, 2;"#
1030        );
1031    }
1032
1033    #[test]
1034    fn comments_and_spacing_go_and_the_case_of_a_name_stays() {
1035        assert_eq!(
1036            whole("CREATE VIEW v AS SELECT  X /* a note */ FROM   T"),
1037            "CREATE VIEW v AS SELECT X FROM T;"
1038        );
1039    }
1040
1041    #[test]
1042    fn every_binary_operation_is_parenthesised_and_every_unary_one_parenthesises_its_operand() {
1043        assert_eq!(body("SELECT x + y * 2 - 1 FROM t"), "SELECT ((x + (y * 2)) - 1) FROM t");
1044        assert_eq!(
1045            body("SELECT x > 1 AND y < 2 OR b FROM t"),
1046            "SELECT (((x > 1) AND (y < 2)) OR b) FROM t"
1047        );
1048        assert_eq!(body("SELECT NOT b FROM t"), "SELECT (NOT b) FROM t");
1049        assert_eq!(body("SELECT ~x FROM t"), "SELECT ~(x) FROM t");
1050        assert_eq!(body("SELECT +x FROM t"), "SELECT +(x) FROM t");
1051        assert_eq!(body("SELECT -x FROM t"), "SELECT -(x) FROM t");
1052    }
1053
1054    /// A minus in front of a number is part of the number, and it folds as many times as it is
1055    /// written. A plus is not part of one and does not fold.
1056    #[test]
1057    fn a_minus_in_front_of_a_constant_folds_into_it() {
1058        assert_eq!(body("SELECT -1"), "SELECT -1");
1059        assert_eq!(body("SELECT - -3"), "SELECT 3");
1060        assert_eq!(body("SELECT +3"), "SELECT +(3)");
1061    }
1062
1063    #[test]
1064    fn the_null_tests_and_the_boolean_tests() {
1065        assert_eq!(body("SELECT x IS NULL FROM t"), "SELECT (x IS NULL) FROM t");
1066        assert_eq!(body("SELECT x ISNULL FROM t"), "SELECT (x IS NULL) FROM t");
1067        assert_eq!(body("SELECT x NOTNULL FROM t"), "SELECT (x IS NOT NULL) FROM t");
1068        assert_eq!(
1069            body("SELECT b IS TRUE FROM t"),
1070            "SELECT (CAST(b AS BOOLEAN) IS NOT DISTINCT FROM true) FROM t"
1071        );
1072        assert_eq!(
1073            body("SELECT b IS NOT TRUE FROM t"),
1074            "SELECT (CAST(b AS BOOLEAN) IS DISTINCT FROM true) FROM t"
1075        );
1076        assert_eq!(
1077            body("SELECT b IS FALSE FROM t"),
1078            "SELECT (CAST(b AS BOOLEAN) IS NOT DISTINCT FROM false) FROM t"
1079        );
1080        assert_eq!(body("SELECT b IS UNKNOWN FROM t"), "SELECT (b IS NULL) FROM t");
1081        assert_eq!(body("SELECT b IS NOT UNKNOWN FROM t"), "SELECT (b IS NOT NULL) FROM t");
1082        assert_eq!(
1083            body("SELECT x IS DISTINCT FROM y FROM t"),
1084            "SELECT (x IS DISTINCT FROM y) FROM t"
1085        );
1086    }
1087
1088    #[test]
1089    fn a_negated_between_or_in_is_a_not_around_the_plain_one() {
1090        assert_eq!(body("SELECT x BETWEEN 1 AND 10 FROM t"), "SELECT (x BETWEEN 1 AND 10) FROM t");
1091        assert_eq!(
1092            body("SELECT x NOT BETWEEN 1 AND 2 FROM t"),
1093            "SELECT (NOT (x BETWEEN 1 AND 2)) FROM t"
1094        );
1095        assert_eq!(body("SELECT x IN (1, 2, 3) FROM t"), "SELECT (x IN (1, 2, 3)) FROM t");
1096        assert_eq!(body("SELECT x NOT IN (1, 2) FROM t"), "SELECT (NOT (x IN (1, 2))) FROM t");
1097        assert_eq!(body("SELECT x IN (SELECT y FROM t)"), "SELECT (x = ANY(SELECT y FROM t))");
1098        assert_eq!(
1099            body("SELECT x NOT IN (SELECT y FROM t)"),
1100            "SELECT (NOT (x = ANY(SELECT y FROM t)))"
1101        );
1102        assert_eq!(body("SELECT x = ANY (SELECT y FROM t)"), "SELECT (x = ANY(SELECT y FROM t))");
1103        assert_eq!(
1104            body("SELECT x > ALL (SELECT y FROM t)"),
1105            "SELECT (NOT (x <= ANY(SELECT y FROM t)))"
1106        );
1107    }
1108
1109    /// The four pattern operators have a word spelling and a symbol spelling, and the symbol is
1110    /// what comes back either way.
1111    #[test]
1112    fn the_pattern_operators_come_back_as_symbols() {
1113        assert_eq!(body("SELECT s LIKE 'a' FROM t"), "SELECT (s ~~ 'a') FROM t");
1114        assert_eq!(body("SELECT s NOT LIKE 'a' FROM t"), "SELECT (s !~~ 'a') FROM t");
1115        assert_eq!(body("SELECT s ILIKE 'a' FROM t"), "SELECT (s ~~* 'a') FROM t");
1116        assert_eq!(body("SELECT s NOT ILIKE 'a' FROM t"), "SELECT (s !~~* 'a') FROM t");
1117        assert_eq!(body("SELECT s GLOB 'a' FROM t"), "SELECT (s ~~~ 'a') FROM t");
1118        assert_eq!(body("SELECT s !~ 'a' FROM t"), "SELECT (s !~ 'a') FROM t");
1119        assert_eq!(
1120            body("SELECT s NOT SIMILAR TO 'a' FROM t"),
1121            "SELECT (NOT regexp_full_match(s, 'a')) FROM t"
1122        );
1123    }
1124
1125    #[test]
1126    fn collate_has_no_parentheses_and_the_rest_of_the_operators_keep_their_spelling() {
1127        assert_eq!(body("SELECT s COLLATE NOCASE FROM t"), "SELECT s COLLATE NOCASE FROM t");
1128        assert_eq!(body("SELECT x // y FROM t"), "SELECT (x // y) FROM t");
1129        assert_eq!(body("SELECT x || y FROM t"), "SELECT (x || y) FROM t");
1130        assert_eq!(body("SELECT x @> y FROM t"), "SELECT (x @> y) FROM t");
1131        assert_eq!(body("SELECT x <=> y FROM t"), "SELECT (x <=> y) FROM t");
1132    }
1133
1134    /// Always searched, always with an `ELSE`, and two spaces after the keyword.
1135    #[test]
1136    fn a_case_is_written_the_long_way_round() {
1137        assert_eq!(
1138            body("SELECT CASE WHEN x > 0 THEN 'a' WHEN x < 0 THEN 'b' ELSE 'c' END FROM t"),
1139            "SELECT CASE  WHEN ((x > 0)) THEN ('a') WHEN ((x < 0)) THEN ('b') ELSE 'c' END FROM t"
1140        );
1141        assert_eq!(
1142            body("SELECT CASE x WHEN 1 THEN 'a' END FROM t"),
1143            "SELECT CASE  WHEN ((x = 1)) THEN ('a') ELSE NULL END FROM t"
1144        );
1145    }
1146
1147    #[test]
1148    fn a_cast_writes_its_type_in_upper_case_with_a_space_after_the_comma() {
1149        assert_eq!(body("SELECT x::varchar FROM t"), "SELECT CAST(x AS VARCHAR) FROM t");
1150        assert_eq!(
1151            body("SELECT cast(x as decimal(4,1)) FROM t"),
1152            "SELECT CAST(x AS DECIMAL(4, 1)) FROM t"
1153        );
1154        assert_eq!(
1155            body("SELECT TRY_CAST(s AS INTEGER) FROM t"),
1156            "SELECT TRY_CAST(s AS INTEGER) FROM t"
1157        );
1158    }
1159
1160    /// The names the grammar has a rule for, which come back under the one name the type has.
1161    #[test]
1162    fn a_standard_type_name_is_resolved_and_the_modifiers_it_was_written_with_survive() {
1163        let cast = |written: &str| body(&format!("SELECT CAST(x AS {written})"));
1164        assert_eq!(cast("int"), "SELECT CAST(x AS INTEGER)");
1165        assert_eq!(cast("numeric(5)"), "SELECT CAST(x AS DECIMAL(5))");
1166        assert_eq!(cast("decimal"), "SELECT CAST(x AS DECIMAL)");
1167        assert_eq!(cast("varchar(10)"), "SELECT CAST(x AS VARCHAR(10))");
1168        assert_eq!(cast("national character(2)"), "SELECT CAST(x AS VARCHAR(2))");
1169        // The argument of a float chose the type rather than sitting on it, so there is nothing
1170        // left of the ten by the time it is written back.
1171        assert_eq!(cast("float(10)"), "SELECT CAST(x AS FLOAT)");
1172        assert_eq!(cast("real"), "SELECT CAST(x AS FLOAT)");
1173        assert_eq!(cast("double precision"), "SELECT CAST(x AS DOUBLE)");
1174        assert_eq!(cast("time with time zone"), "SELECT CAST(x AS TIME WITH TIME ZONE)");
1175        assert_eq!(cast("int[]"), "SELECT CAST(x AS INTEGER[])");
1176        assert_eq!(cast("int[2][3]"), "SELECT CAST(x AS INTEGER[2][3])");
1177        assert_eq!(cast("map(int, varchar)"), "SELECT CAST(x AS MAP(INTEGER, VARCHAR))");
1178        assert_eq!(cast("union(a int)"), "SELECT CAST(x AS UNION(a INTEGER))");
1179    }
1180
1181    /// A struct keeps the case of the field name and resolves the field type.
1182    #[test]
1183    fn a_struct_field_keeps_its_name_and_its_type_goes_round_again() {
1184        assert_eq!(body("SELECT CAST(x AS struct(a bool))"), "SELECT CAST(x AS STRUCT(a bool))");
1185        assert_eq!(
1186            body("SELECT CAST(x AS struct(\"A b\" int))"),
1187            "SELECT CAST(x AS STRUCT(\"A b\" INTEGER))"
1188        );
1189    }
1190
1191    /// And every other name is the catalog's business, so the text is written back as it stands.
1192    #[test]
1193    fn a_type_name_the_grammar_has_no_rule_for_keeps_the_case_it_was_written_in() {
1194        let cast = |written: &str| body(&format!("SELECT CAST(x AS {written})"));
1195        assert_eq!(cast("text"), "SELECT CAST(x AS text)");
1196        assert_eq!(cast("TEXT"), "SELECT CAST(x AS TEXT)");
1197        assert_eq!(cast("DOUBLE"), "SELECT CAST(x AS DOUBLE)");
1198        assert_eq!(cast("bool"), "SELECT CAST(x AS bool)");
1199        assert_eq!(cast("\"bool\""), "SELECT CAST(x AS bool)");
1200        assert_eq!(cast("int4[]"), "SELECT CAST(x AS int4[])");
1201        assert_eq!(cast("TIMESTAMPTZ"), "SELECT CAST(x AS TIMESTAMPTZ)");
1202        // The one name that comes back quoted, for the reason written on `catalogued`.
1203        assert_eq!(cast("JSON"), "SELECT CAST(x AS \"JSON\")");
1204        assert_eq!(cast("json"), "SELECT CAST(x AS \"json\")");
1205        assert_eq!(cast("json[]"), "SELECT CAST(x AS \"json\"[])");
1206        assert_eq!(cast("struct(a json)"), "SELECT CAST(x AS STRUCT(a \"json\"))");
1207    }
1208
1209    #[test]
1210    fn a_star_count_is_a_function_of_its_own_and_a_list_is_a_call() {
1211        assert_eq!(body("SELECT count(*) FROM t"), "SELECT count_star() FROM t");
1212        assert_eq!(body("SELECT count() FROM t"), "SELECT count_star() FROM t");
1213        assert_eq!(body("SELECT count(DISTINCT x) FROM t"), "SELECT count(DISTINCT x) FROM t");
1214        assert_eq!(body("SELECT [1, 2, 3]"), "SELECT list_value(1, 2, 3)");
1215        assert_eq!(body("SELECT []"), "SELECT list_value()");
1216    }
1217
1218    /// A function name goes through the same quoting rule an identifier does, so the ones that are
1219    /// keywords in a class come back quoted.
1220    #[test]
1221    fn a_function_name_is_quoted_when_it_is_a_keyword() {
1222        assert_eq!(body("SELECT nullif(x, 1) FROM t"), "SELECT \"nullif\"(x, 1) FROM t");
1223        assert_eq!(body("SELECT length(s) FROM t"), "SELECT length(s) FROM t");
1224    }
1225
1226    #[test]
1227    fn the_literals() {
1228        assert_eq!(body("SELECT NULL, TRUE, FALSE"), "SELECT NULL, true, false");
1229        assert_eq!(body("SELECT 1.50, .5, 1_000"), "SELECT 1.50, .5, 1000");
1230        assert_eq!(body("SELECT 'it''s'"), "SELECT 'it''s'");
1231    }
1232
1233    /// A number comes back as the value it was read as, which is three rules and not one.
1234    #[test]
1235    fn a_number_is_written_back_as_the_value_the_shape_of_it_made() {
1236        assert_eq!(body("SELECT 007, 1_000"), "SELECT 7, 1000");
1237        assert_eq!(body("SELECT 1.50, 00.5, 1., 0.0"), "SELECT 1.50, 0.5, 1, 0.0");
1238        assert_eq!(body("SELECT 1e3, 1.5e2, 1e-3, 5e-4"), "SELECT 1000.0, 150.0, 0.001, 0.0005");
1239        assert_eq!(body("SELECT 5e-5, 2.5e-5, 1e-10"), "SELECT 5e-05, 2.5e-05, 1e-10");
1240        assert_eq!(body("SELECT 1e15, 1e16, 1e100"), "SELECT 1000000000000000.0, 1e+16, 1e+100");
1241    }
1242
1243    /// The part of an `EXTRACT` is a keyword and a keyword has one spelling.
1244    #[test]
1245    fn an_extract_is_a_date_part_call_and_the_keyword_it_named_has_one_spelling() {
1246        assert_eq!(body("SELECT extract(year FROM d)"), "SELECT date_part('YEAR', d)");
1247        assert_eq!(body("SELECT extract(years FROM d)"), "SELECT date_part('YEAR', d)");
1248        assert_eq!(body("SELECT extract(seconds FROM d)"), "SELECT date_part('SECOND', d)");
1249        // Two of the thirteen are written back plural, which is a list and not a rule.
1250        assert_eq!(
1251            body("SELECT extract(millisecond FROM d)"),
1252            "SELECT date_part('MILLISECONDS', d)"
1253        );
1254        assert_eq!(
1255            body("SELECT extract(microseconds FROM d)"),
1256            "SELECT date_part('MICROSECONDS', d)"
1257        );
1258        assert_eq!(body("SELECT extract(millennia FROM d)"), "SELECT date_part('MILLENNIUM', d)");
1259        // A word the grammar does not name as a keyword is an identifier and keeps its case.
1260        assert_eq!(body("SELECT extract(epoch FROM d)"), "SELECT date_part('epoch', d)");
1261        assert_eq!(body("SELECT extract(dow FROM d)"), "SELECT date_part('dow', d)");
1262    }
1263
1264    /// The two spellings of the one operator, which is not a function name however much it looks it.
1265    #[test]
1266    fn coalesce_and_ifnull_are_one_operator_and_it_is_written_in_upper_case() {
1267        assert_eq!(body("SELECT coalesce(x, y)"), "SELECT COALESCE(x, y)");
1268        assert_eq!(body("SELECT IfNull(x, y)"), "SELECT COALESCE(x, y)");
1269        // Including the one argument form, which is not folded away.
1270        assert_eq!(body("SELECT coalesce(x)"), "SELECT COALESCE(x)");
1271        // And no other name does this, which `nullif` is the one to check against.
1272        assert_eq!(body("SELECT nullif(x, y)"), "SELECT \"nullif\"(x, y)");
1273        assert_eq!(body("SELECT greatest(x, y)"), "SELECT greatest(x, y)");
1274    }
1275
1276    #[test]
1277    fn the_modifiers_hang_off_the_query_and_not_off_the_select() {
1278        assert_eq!(body("SELECT x FROM t LIMIT 5 OFFSET 2"), "SELECT x FROM t LIMIT 5 OFFSET 2");
1279        assert_eq!(body("SELECT x FROM t LIMIT 10 PERCENT"), "SELECT x FROM t LIMIT (10) %");
1280        assert_eq!(
1281            body("SELECT x FROM t ORDER BY x ASC, y NULLS LAST"),
1282            "SELECT x FROM t ORDER BY x ASC, y NULLS LAST"
1283        );
1284        assert_eq!(body("SELECT x FROM t ORDER BY ALL"), "SELECT x FROM t ORDER BY COLUMNS(*)");
1285        assert_eq!(body("SELECT x FROM t GROUP BY ALL"), "SELECT x FROM t GROUP BY ALL");
1286        assert_eq!(
1287            body("SELECT x FROM t GROUP BY x HAVING x > 0"),
1288            "SELECT x FROM t GROUP BY x HAVING (x > 0)"
1289        );
1290        assert_eq!(
1291            body("SELECT DISTINCT ON (x) x, y FROM t"),
1292            "SELECT DISTINCT ON (x) x, y FROM t"
1293        );
1294    }
1295
1296    /// A branch that is a set operation of its own is written bare, and a bare left branch loses the
1297    /// space that would follow it. Upstream's, and reproduced because the column is a comparison.
1298    #[test]
1299    fn a_chain_of_set_operations_loses_a_space_in_the_middle() {
1300        assert_eq!(
1301            body("SELECT x FROM t UNION ALL SELECT y FROM t"),
1302            "(SELECT x FROM t) UNION ALL (SELECT y FROM t)"
1303        );
1304        assert_eq!(
1305            body("SELECT x FROM t UNION SELECT y FROM t UNION SELECT 1"),
1306            "(SELECT x FROM t) UNION (SELECT y FROM t)UNION (SELECT 1)"
1307        );
1308        assert_eq!(
1309            body("SELECT x FROM t UNION DISTINCT SELECT y FROM t"),
1310            "(SELECT x FROM t) UNION (SELECT y FROM t)"
1311        );
1312    }
1313
1314    #[test]
1315    fn a_values_body_is_wrapped_in_a_select_that_names_it() {
1316        assert_eq!(
1317            body("VALUES (1, 'a'), (2, 'b')"),
1318            "SELECT * FROM (VALUES (1, 'a'), (2, 'b')) AS valueslist"
1319        );
1320    }
1321
1322    /// A space before each comma, which is upstream's and is not a typo here.
1323    #[test]
1324    fn a_from_list_has_a_space_before_the_comma() {
1325        assert_eq!(body("SELECT 1 FROM t AS t1, t AS t2"), "SELECT 1 FROM t AS t1 , t AS t2");
1326    }
1327
1328    #[test]
1329    fn a_from_item_and_its_aliases() {
1330        assert_eq!(body("SELECT 1 FROM t AS r(n)"), "SELECT 1 FROM t AS r(n)");
1331        assert_eq!(body("SELECT 1 FROM main.t"), "SELECT 1 FROM main.t");
1332        assert_eq!(
1333            body("SELECT 1 FROM (SELECT x FROM t) AS sub"),
1334            "SELECT 1 FROM (SELECT x FROM t) AS sub"
1335        );
1336        assert_eq!(body("SELECT 1 FROM range(10)"), "SELECT 1 FROM \"range\"(10)");
1337    }
1338
1339    /// Joins are parenthesised, `FULL OUTER` loses a word, `NATURAL` gains one, and an `ON` gets a
1340    /// second pair of parentheses on top of the ones the condition already has.
1341    #[test]
1342    fn a_join_is_parenthesised_and_so_is_its_condition_twice() {
1343        assert_eq!(
1344            body("SELECT 1 FROM t AS a JOIN t AS b ON a.x = b.y"),
1345            "SELECT 1 FROM (t AS a INNER JOIN t AS b ON ((a.x = b.y)))"
1346        );
1347        assert_eq!(
1348            body("SELECT 1 FROM t LEFT JOIN t AS u USING (x)"),
1349            "SELECT 1 FROM (t LEFT JOIN t AS u USING (x))"
1350        );
1351        assert_eq!(
1352            body("SELECT 1 FROM t CROSS JOIN t AS u"),
1353            "SELECT 1 FROM (t CROSS JOIN t AS u)"
1354        );
1355        assert_eq!(
1356            body("SELECT 1 FROM t FULL OUTER JOIN t AS u ON t.x = u.x"),
1357            "SELECT 1 FROM (t FULL JOIN t AS u ON ((t.x = u.x)))"
1358        );
1359        assert_eq!(
1360            body("SELECT 1 FROM t NATURAL JOIN t AS u"),
1361            "SELECT 1 FROM (t NATURAL INNER JOIN t AS u)"
1362        );
1363        assert_eq!(
1364            body("SELECT 1 FROM t POSITIONAL JOIN t AS u"),
1365            "SELECT 1 FROM (t POSITIONAL JOIN t AS u)"
1366        );
1367    }
1368
1369    #[test]
1370    fn a_target_keeps_its_alias_and_a_star_keeps_its_replace_list() {
1371        assert_eq!(body("SELECT 1 + 2 AS \"quoted alias\""), "SELECT (1 + 2) AS \"quoted alias\"");
1372        assert_eq!(body("SELECT x AS \"select\" FROM t"), "SELECT x AS \"select\" FROM t");
1373        assert_eq!(body("SELECT t.* FROM t"), "SELECT t.* FROM t");
1374        assert_eq!(
1375            body("SELECT * REPLACE (x + 1 AS x) FROM t"),
1376            "SELECT * REPLACE ((x + 1) AS x) FROM t"
1377        );
1378    }
1379
1380    #[test]
1381    fn a_describe_gets_parentheses_round_what_it_describes() {
1382        assert_eq!(body("DESCRIBE SELECT 1"), "DESCRIBE (SELECT 1)");
1383    }
1384
1385    /// Every string on the right was read out of `duckdb_views()` on the pin for the query on the
1386    /// left, which is also the column name the same window gets when the target has no alias.
1387    #[test]
1388    fn a_window_is_written_with_the_parts_that_were_written_in_it() {
1389        assert_eq!(
1390            body("SELECT row_number() OVER () AS n FROM t"),
1391            "SELECT row_number() OVER () AS n FROM t"
1392        );
1393        assert_eq!(
1394            body(
1395                "SELECT row_number() OVER (PARTITION BY a ORDER BY b DESC NULLS FIRST) AS n FROM t"
1396            ),
1397            "SELECT row_number() OVER (PARTITION BY a ORDER BY b DESC NULLS FIRST) AS n FROM t"
1398        );
1399        assert_eq!(
1400            body(
1401                "SELECT sum(i) OVER (PARTITION BY i, i+1 ORDER BY i ASC NULLS LAST, i DESC) AS n FROM t"
1402            ),
1403            "SELECT sum(i) OVER (PARTITION BY i, (i + 1) ORDER BY i ASC NULLS LAST, i DESC) AS n FROM t"
1404        );
1405        assert_eq!(
1406            body("SELECT sum(DISTINCT i) OVER (ORDER BY i) AS n FROM t"),
1407            "SELECT sum(DISTINCT i) OVER (ORDER BY i) AS n FROM t"
1408        );
1409        assert_eq!(
1410            body("SELECT first_value(i IGNORE NULLS) OVER (ORDER BY i) AS n FROM t"),
1411            "SELECT first_value(i IGNORE NULLS) OVER (ORDER BY i) AS n FROM t"
1412        );
1413        assert_eq!(
1414            body("SELECT first_value(i RESPECT NULLS) OVER (ORDER BY i) AS n FROM t"),
1415            "SELECT first_value(i) OVER (ORDER BY i) AS n FROM t"
1416        );
1417        assert_eq!(
1418            body("SELECT count(*) OVER () AS n FROM t"),
1419            "SELECT count() OVER () AS n FROM t"
1420        );
1421        assert_eq!(
1422            body("SELECT main.sum(i) OVER (ORDER BY i) AS n FROM t"),
1423            "SELECT main.sum(i) OVER (ORDER BY i) AS n FROM t"
1424        );
1425    }
1426
1427    /// Every string on the right was read out of `duckdb_views()` on the pin. The word `WHERE` is
1428    /// written back whether or not it was written, because the pin writes it back either way.
1429    #[test]
1430    fn a_filter_is_written_after_the_call_and_before_the_over() {
1431        assert_eq!(
1432            body("SELECT sum(x) FILTER (WHERE y > 1) FROM t"),
1433            "SELECT sum(x) FILTER (WHERE (y > 1)) FROM t"
1434        );
1435        assert_eq!(
1436            body("SELECT sum(x) FILTER (y > 1) FROM t"),
1437            "SELECT sum(x) FILTER (WHERE (y > 1)) FROM t"
1438        );
1439        assert_eq!(
1440            body("SELECT count(*) FILTER (WHERE b) FROM t"),
1441            "SELECT count_star() FILTER (WHERE b) FROM t"
1442        );
1443        assert_eq!(
1444            body("SELECT count() FILTER (WHERE b) FROM t"),
1445            "SELECT count_star() FILTER (WHERE b) FROM t"
1446        );
1447        assert_eq!(
1448            body("SELECT sum(x) FILTER (WHERE y > 1) OVER (ORDER BY x) FROM t"),
1449            "SELECT sum(x) FILTER (WHERE (y > 1)) OVER (ORDER BY x) FROM t"
1450        );
1451        assert_eq!(
1452            body("SELECT sum(DISTINCT x) FILTER (WHERE b) OVER () FROM t"),
1453            "SELECT sum(DISTINCT x) FILTER (WHERE b) OVER () FROM t"
1454        );
1455        assert_eq!(
1456            body("SELECT count(*) FILTER (WHERE b) OVER () FROM t"),
1457            "SELECT count() FILTER (WHERE b) OVER () FROM t"
1458        );
1459    }
1460
1461    /// The default frame is not written, and neither is the `RANGE` that was written in its place.
1462    #[test]
1463    fn a_frame_is_written_only_when_it_is_not_the_one_that_was_assumed() {
1464        assert_eq!(
1465            body(
1466                "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS n FROM t"
1467            ),
1468            "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1469        );
1470        assert_eq!(
1471            body("SELECT sum(i) OVER (ORDER BY i RANGE UNBOUNDED PRECEDING) AS n FROM t"),
1472            "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1473        );
1474        assert_eq!(
1475            body(
1476                "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE NO OTHERS) AS n FROM t"
1477            ),
1478            "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS n FROM t"
1479        );
1480        assert_eq!(
1481            body("SELECT sum(i) OVER (ORDER BY i ROWS CURRENT ROW) AS n FROM t"),
1482            "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND CURRENT ROW) AS n FROM t"
1483        );
1484        assert_eq!(
1485            body(
1486                "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN (1+1) PRECEDING AND CURRENT ROW) AS n FROM t"
1487            ),
1488            "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN (1 + 1) PRECEDING AND CURRENT ROW) AS n FROM t"
1489        );
1490        assert_eq!(
1491            body(
1492                "SELECT sum(i) OVER (ORDER BY i GROUPS BETWEEN CURRENT ROW AND 2 FOLLOWING EXCLUDE CURRENT ROW) AS n FROM t"
1493            ),
1494            "SELECT sum(i) OVER (ORDER BY i GROUPS BETWEEN CURRENT ROW AND 2 FOLLOWING EXCLUDE CURRENT ROW) AS n FROM t"
1495        );
1496        assert_eq!(
1497            body(
1498                "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE GROUP) AS n FROM t"
1499            ),
1500            "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE GROUP) AS n FROM t"
1501        );
1502        assert_eq!(
1503            body(
1504                "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS n FROM t"
1505            ),
1506            "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS n FROM t"
1507        );
1508    }
1509
1510    /// A frame over the whole partition measures the same however it says it does, and the three
1511    /// spellings come back as the one upstream picks.
1512    #[test]
1513    fn a_frame_that_covers_the_partition_is_written_as_a_row_count() {
1514        for unit in ["ROWS", "RANGE", "GROUPS"] {
1515            assert_eq!(
1516                body(&format!(
1517                    "SELECT sum(i) OVER (ORDER BY i {unit} BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS n FROM t"
1518                )),
1519                "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS n FROM t"
1520            );
1521        }
1522        assert_eq!(
1523            body(
1524                "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE TIES) AS n FROM t"
1525            ),
1526            "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE TIES) AS n FROM t"
1527        );
1528    }
1529
1530    /// A named window is gone by the time anything is written back out, which is upstream's answer
1531    /// as well: the `WINDOW` clause does not survive a round trip through the catalog there.
1532    #[test]
1533    fn a_named_window_is_written_out_where_it_was_used() {
1534        assert_eq!(
1535            body("SELECT sum(i) OVER w AS n FROM t WINDOW w AS (PARTITION BY i ORDER BY i)"),
1536            "SELECT sum(i) OVER (PARTITION BY i ORDER BY i) AS n FROM t"
1537        );
1538        assert_eq!(
1539            body("SELECT sum(i) OVER (w) AS n FROM t WINDOW w AS (ORDER BY i)"),
1540            "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1541        );
1542        assert_eq!(
1543            body(
1544                "SELECT sum(i) OVER (w ROWS UNBOUNDED PRECEDING) AS n FROM t WINDOW w AS (PARTITION BY i)"
1545            ),
1546            "SELECT sum(i) OVER (PARTITION BY i ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS n FROM t"
1547        );
1548        assert_eq!(
1549            body("SELECT sum(i) OVER (w PARTITION BY i) AS n FROM t WINDOW w AS (ORDER BY i)"),
1550            "SELECT sum(i) OVER (PARTITION BY i ORDER BY i) AS n FROM t"
1551        );
1552        assert_eq!(
1553            body("SELECT sum(i) OVER v AS n FROM t WINDOW w AS (ORDER BY i), v AS (w)"),
1554            "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1555        );
1556    }
1557}