Skip to main content

Module deparse

Module deparse 

Source
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 VIEW written back out, which is what duckdb_views() reports as sql.
query
One query written back out.