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