Skip to content

fix(syntax): parser gaps batch e (TASK CLONE, CREATE … FROM, quoting, recorded refusals) - #54

Merged
kvlonge merged 11 commits into
mainfrom
fix/parser-gaps-e
Nov 5, 2026
Merged

kvlonge merged 11 commits into
mainfrom
fix/parser-gaps-e

Conversation

@kvlonge

@kvlonge kvlonge commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Parser batch gaps-e. Fixes in the Snowflake parser, each checked against receipted Snowflake verdicts.

  • CREATE TASK … CLONE; CREATE … FROM BACKUP SET / FROM SHARE / AS REPLICA OF / FROM ARCHIVE OF, as a CreateObject node (C confirmed ownership).
  • Quoting kept on SELECT * RENAME aliases, PIVOT aggregate aliases, UNPIVOT columns and COMMENT ON names (auditor withholds).
  • UNDROP STAGE and VARIABLE; CREATE TYPE … AS ; reserved words refused as CREATE TABLE and FUNCTION names; USING TEMPLATE takes a string or a parenthesized query; a hybrid table needs columns, AS or LIKE; a function body is a string or $$ block; BEGIN NAME is a named transaction.
  • Zero-argument aggregates and FLATTEN() parse (Snowflake refuses them only when compiling); PIVOT refuses an aggregate with no arguments; FROM ( f() ) needs TABLE( ); AT, BEFORE and CHANGES need their closing parenthesis, and CHANGES needs a value; IN isn't a function; a type parameter can't be a string.
  • DuckShop driver: Python-UDF callers that the census refuses now report python_udf_not_measured.
  • Exact check: 5 new [[rejection]] entries for CREATE FUNCTION bodies that aren't strings (polyglot AS 1, sqlglot AS RETURN …).

The gate result goes in a comment once it has run on the exact head.

A cloned task has no AS body; the tree keeps the source and the name's quoting.
CREATE OR needs REPLACE or ALTER, and a lone word before CLONE is refused.
…LICA

A database, schema, table or dynamic table restored from a backup set, a
database from a share or as a replica, and a table from an archive are now
one node, CreateObject, with the source's names, identifier, alias, condition
and properties. A lone word before FROM or AS is refused, not kept raw.
…df_not_measured

Census refuses a compiled model's unresolved __udf("stem") call as fn:<udf>
before naming it, so the driver never saw the Python function and reported
the model as refused. It now reads the compiled source too, resolving each
__udf("stem") to its placeholder, so int_payment_attempts,
int_edge_python_values and int_edge_ticket_tokens are not measured rather
than refused by the converter.
SELECT * RENAME v1 AS "v1 Q", a PIVOT aggregate's alias and UNPIVOT's value
and name columns read a quoted name as unquoted, so "v1" and v1 gave one
tree (auditor withholds on sf.query.select.select, pivot and unpivot).
# Conflicts:
#	crates/sqlquack-syntax/src/parser/_helpers/ddl/mod.rs
COMMENT ON TABLE "v1" and COMMENT ON TABLE v1 gave one tree, and
COMMENT ON COLUMN "v1 Q" . v1 lost the table's quoting (auditor withhold on
sf.ddl.comment.is and column-is). Each part now keeps it; a table of several
parts writes its quoted parts with their quotes. Polyglot drops these quotes,
so the exact check lists COMMENT ON as kept quoting.
UNDROP STAGE and VARIABLE; CREATE TYPE ... AS <type> as a type alias;
Snowflake CREATE TABLE and FUNCTION names refuse reserved words; USING
TEMPLATE takes a string or a parenthesized query; a hybrid table needs
columns, AS or LIKE; a function body is a string; BEGIN NAME <name> is a
named transaction; zero-argument aggregates and FLATTEN() parse as calls
with no arguments; PIVOT refuses an aggregate without arguments; FROM ( f() )
needs TABLE( ); AT, BEFORE and CHANGES need their closing parenthesis and
CHANGES needs a value; IN is not a function; a type parameter is not a
string.
Snowflake reads a CREATE FUNCTION body only as a string or a dollar-quoted
block, so polyglot's AS 1 and sqlglot's AS RETURN bodies are now refused
under Snowflake (recorded errno 1003 for AS m1).
@kvlonge
kvlonge merged commit 497ad27 into main Nov 5, 2026

kvlonge commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

Gate (D94): make check-merge RECORDER=0 on the exact head 497ad27 (which contains main 12a1713, i.e. #52 and #53) exited 0: 36 test results ok, 0 failed. Landed as a fast-forward: main = 497ad27.

  • Trees: main tree 79e4a4adb644829b4fb94be76c696d3edd1e6b6e = head tree 79e4a4adb644829b4fb94be76c696d3edd1e6b6e.
  • Coverage diff against main 0c8952e (feat: DuckShop batch 4 (ROUND, framed AVG, SYSTEM$TYPEOF, PARSE_URL, FLATTEN paths) #53 touches no parser, coverage or exact-check file): forms +22 −0, obligations +438 −0 (ddl +17, expressions +2, functions +2, transaction_session +1). That's under the 67-form floor; I gated at the 3-hour coding cap (D122a).
  • Receipted Snowflake-parsed cases now rejected: 0. For information: 89 unreceipted catalogue rows (phase null, missing_receipt) moved from opaque-accepted to rejected. They're omission probes such as CREATE OR API INTEGRATION …, CREATE APPLICATION FROM … and CREATE EXTERNAL LISTING AS 'm' with no name; Snowflake never ran them, so the rule doesn't cover them.
  • Stale reviews: no new ones (70 stale reviews and 18 stale receipts, unchanged from base).
  • Exact check: 8 of 8 pass, with 5 new [[rejection]] entries for CREATE FUNCTION bodies that aren't strings.
  • Local guard set: agree 25,843 ok, 5 bad (the 5 known observe rows).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant