Expand description
An Ast written back out as SQL, the way DuckDB writes one.
duckdb_views().sql is a deparse of the body rather than the text somebody typed, which was
measured: a view created with odd spacing, lower case type names and a comment in the middle
comes back normalised and without the comment. So the column needs a writer, and the writer has
to agree with the pin character for character or the column is a divergence on every view a
harness looks at.
§This is not a pretty printer and it is not the printer in transform’s tests
Two things are going on in the pin’s output and only one of them is printing. count(*) comes
back as count_star(), x IS TRUE as (CAST(x AS BOOLEAN) IS NOT DISTINCT FROM true),
s LIKE 'a' as (s ~~ 'a'), [1, 2] as list_value(1, 2), x IN (SELECT ...) as
(x = ANY(SELECT ...)) and a simple CASE x WHEN 1 as a searched CASE with an ELSE NULL
nobody wrote. Those are rewrites DuckDB’s transformer does on the way in, and what gets printed
is the rewritten tree. rudb’s AST keeps the written form, deliberately, because an error message
should say what was written. So the rewrites happen here, at the point of printing, and every one
of them is a line in this file with the measurement it came from next to it.
transform’s tests have a printer of their own and it stays. It answers a different question:
what shape did the transform produce. Printing IS TRUE as a cast and a distinct test would hide
exactly the bug those tests are there to catch.
§The parentheses
Every binary operation is parenthesised, whatever the precedence, so x + y * 2 - 1 is
((x + (y * 2)) - 1). Every unary one parenthesises its operand instead, so -x is -(x). That
is upstream’s rule and it is also the only rule that is safe without a precedence table, since a
printer that leaves parentheses out has to be right about precedence in both directions.
A few of the quirks that follow from printing this way are upstream’s rather than anybody’s
design, and they are reproduced because the column is a comparison. CASE is followed by two
spaces, because the slot for the operand of a simple CASE is filled in unconditionally and a
searched one leaves it empty. A FROM list has a space before each comma. A chain of three set
operations loses the space before the second operator.
§What does not agree yet
Three things, and none of them is a printing question. Each one is a place where rudb’s transform
threw away something the pin kept, so the answer is in transform and not here, and each has an
issue of its own. Two hundred and sixty two view bodies were measured against the pin and these
four lines are what is left over.
^ and ** are one BinaryOp::Power here and two operators there, and the pin keeps whichever
was written all the way down to the function it resolves: [1] ^ [2] and [1] ** [2] fail with
different names in the message. A view written with one comes back with the other.
LIMIT ALL is dropped by the transform, since it means no limit, and the pin writes it back as
LIMIT NULL.
A subscript is rewritten by the transform into the call it stands for, so [1, 2][1] is
array_extract(list_value(1, 2), 1) here and list_value(1, 2)[1] there. The pin does the same
rewrite at bind time and prints the subscript, so this one is a matter of doing it later.
§What the parser cannot reach yet
A body the parser refuses never gets here, so none of the following is a divergence today. They
were measured anyway, at the same time as the rest, because the measurement is the expensive part
and whoever adds the syntax will need the answer. A window is printed with its clause spelled out
and a named one is inlined, so OVER w with WINDOW w AS (ORDER BY x) is OVER (ORDER BY x). A
FILTER keeps its own parentheses and parenthesises the condition inside them. EXISTS is
written without a space before the parenthesis. x IN (SELECT ...) is (x = ANY(SELECT ...)) and
x > ALL (SELECT ...) is (NOT (x <= ANY(SELECT ...))). A WITH loses the space after the last
bracket, so it reads WITH a AS (SELECT 1 AS n)SELECT n FROM a, and a recursive one writes the
column list as (n) with a space. LATERAL goes. TABLESAMPLE 10 PERCENT is TABLESAMPLE System(10.0 PERCENT). CUBE (x, y) and ROLLUP (x, y) are both written out as the
GROUPING SETS they stand for. {'a': 1} is struct_pack(a := 1) and MAP {'a': 1} is
"map"(list_value('a'), list_value(1)). A list comprehension is expanded into the three nested
lambdas it is made of.
Functions§
- create_
view - A
CREATE VIEWwritten back out, which is whatduckdb_views()reports assql. - query
- One query written back out.