Repository navigation
fix(parser,tokenizer): ClickHouse/Snowflake lambda bodies and U& in dialects without & [W0-00a] - #901
Conversation
…t U& where & does not exist - In ClickHouse and Snowflake, -> is now parsed as the lambda operator. Its body is a whole expression, so arrayMap(x -> x * 2, a) is no longer (x -> x) * 2. The new BinaryExpression.Lambda field marks these nodes, and the SQL() and retarget precedence tables treat a lambda as looser than OR in lambda dialects. Identity and ClickHouse<->Snowflake transpiles now keep the lambda unchanged. Transpiles to JSON-arrow dialects keep the body grouped as the target parses it. (#897) - Oracle, Snowflake and ClickHouse have neither U& literals nor an & operator. In these dialects U&'...' and U&"..." now return an E4001 tokenizer error. They used to be read as U & '...' and formatted as BITAND(U, '...') or bitAnd(U, '...'). (#898) Fixes #897 Fixes #898 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
Claude finished @ajitpratap0's task in 20s —— View job Review of PR #901: ClickHouse/Snowflake lambdas and
|
…rrows for ClickHouse/Snowflake
- A lambda (BinaryExpression.Lambda) transpiled to PostgreSQL, MySQL,
MariaDB, SQLite, SQL Server or Oracle is now refused with the
unsupported-operator retarget error. Written as that dialect's -> it
meant JSON extraction (MySQL) or regrouped as (x -> x) * 2 (SQL Server,
Oracle). The generic dialect is unchanged.
- A JSON -> or ->> transpiled to ClickHouse or Snowflake is now refused:
there -> is the lambda operator (and ->> Snowflake's flow operator), so
GoSQLX re-read data -> 'a' = 'x' as the lambda data -> ('a' = 'x').
- Tests now assert the refusal for all six non-lambda targets and for
PostgreSQL/MySQL/MariaDB/SQLite -> ClickHouse/Snowflake; the CHANGELOG
entry describes the refusals instead of the grouped -> output. (#897)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
|
Claude finished @ajitpratap0's task in 18s —— View job Review of PR #901: ClickHouse/Snowflake lambdas and
|
ClickHouse and Snowflake still built a JSON ->> node, which the lambda- dialect JSON-arrow refusal then rejected when formatting or transpiling, even within the same dialect, with an error naming the source dialect as the target. Neither dialect has ->> as an expression operator (Snowflake's ->> is the statement-level flow operator), so parsing it there now returns an E4001 unsupported-feature error. Tests pin the parse error and the same-dialect transpile; the CHANGELOG #897 entry records the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
|
Claude finished @ajitpratap0's task in 14s —— View job Review of PR #901: ClickHouse/Snowflake lambdas and
|
…se it in CHANGELOG ClickHouse/Snowflake -> generic now prints arrayMap(x -> (x * 2), arr) instead of arrayMap(x -> x * 2, arr), which the generic parser read as (x -> x) * 2. Pin all lambda statements through the public Transpile API and list the output change in the #897 CHANGELOG entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
|
Claude finished @ajitpratap0's task in 14s —— View job Review of PR #901: ClickHouse/Snowflake lambdas and
|
|
Claude finished @ajitpratap0's task in 15s —— View job Review of PR #901: ClickHouse/Snowflake lambdas and
|
Summary
This PR fixes the two transpile correctness bugs from perf-audit CRITIQUE item 14 (dive-transpile-bindings §4). They had to be fixed before the W0-02 transpile goldens are captured, so the goldens do not record the wrong output as expected.
Fixes #897
Fixes #898
#897: ClickHouse and Snowflake lambdas. The parser read
->as the JSON arrow, which binds tighter than every other operator.arrayMap(x -> x * 2, a)therefore became(x -> x) * 2in the AST. Formatting for ClickHouse or Snowflake then emittedarrayMap((x -> x) * 2, a). This happened in identity transpiles, in ClickHouse to Snowflake transpiles and inFormatASTChecked.parseJSONExpression): in ClickHouse and Snowflake,->now starts a lambda whose body is a fullparseExpression(). Neither dialect has a JSON->.->>is not an expression operator in either dialect (Snowflake's->>is the statement-level flow operator), so parsing it there now returns an E4001 error,the ->> operator is not supported in <dialect>. Onmainit parsed as a JSON node and formatted unchanged; without this parse-time check the JSON-arrow refusal below would have failed same-dialect formatting and transpiling of such input with an error naming the source dialect as the target.BinaryExpression.Lambda, marks these nodes. It follows theBitwiseXor,XorAtAdditiveandPipesOrpattern.expressionPrecedence(used bySQL()on hand-built trees) gives a lambda the lowest precedence.targetExpressionPrecedence(used byRetargetOperatorsin the formatter and transpiler) does the same for the ClickHouse and Snowflake targets.binaryTargetForm): a lambda sent to a dialect without lambdas (PostgreSQL, MySQL, MariaDB, SQLite, SQL Server, Oracle) is refused with the unsupported-operator retarget error,the lambda operator -> has no equivalent in <target>. Written as that dialect's->, it would mean JSON extraction (MySQL:arrayMap(x -> (x * 2), arr)) or regroup as(x -> x) * 2(SQL Server, Oracle). The generic dialect keeps the lambda but, like the JSON->, groups its body:arrayMap(x -> x * 2, arr)from ClickHouse or Snowflake to generic is nowarrayMap(x -> (x * 2), arr)(it wasarrayMap(x -> x * 2, arr), which the generic parser reads as(x -> x) * 2).->or->>(Lambda == false) sent to ClickHouse or Snowflake is refused withthe JSON operator -> has no equivalent in <target>. There->is the lambda operator and->>is Snowflake's flow operator. Before this, GoSQLX emitteddata -> 'a' = 'x'unchanged, and its own ClickHouse/Snowflake parser now reads that back as the lambdadata -> ('a' = 'x').#898:
U&'...'in Oracle, Snowflake and ClickHouse. None of these dialects has U& literals or an&operator. The tokenizer still splitU&'...'intoU & '...', and formatting emittedBITAND(U, '...')orbitAnd(U, '...').readIdentifier): an adjacentU&'orU&"in these three dialects now returns an E4001 unsupported-feature error at theU&.U&'x'asU & 'x', because they have&.U & '...'is unchanged everywhere.The CHANGELOG has an
[Unreleased]### Fixedentry, as the task requires.Affected paths (before this PR)
arrayMap(x -> x * 2, [1, 2, 3])FormatASTCheckedarrayMap((x -> x) * 2, ...)(x -> x) * 2x -> x * 2(re-parses as(x -> x) * 2)arrayMap(x -> x * 2, ...)x -> x * 2(re-parses as(x -> x) * 2)x -> (x * 2)SELECT data -> 'a' = 'x',data ->> 'a'SELECT a ->> 'b' FROM tFormatWithDialectSELECT a ->> 'b' FROM t->>not supportedSELECT U&'d\0061t'BITAND(U, '...')bitAnd(U, '...')Plain
gosqlx.Format(which parses and prints with the generic dialect, where->is the JSON arrow) is unchanged. The only generic-dialect output change is the ClickHouse/Snowflake -> generic transpile row above. Hot paths: the tokenizer change only adds aswitchinside the existing branch for the rareU&'case. The parser check runs only inside the non-PostgreSQL JSON-operator loop. So no benchmark was run.Test Plan
New behaviour tests. Each one fails on
mainand passes here; I checked this by reverting only the four source files:pkg/sql/parser/lambda_test.go:*,-, AND, OR, LIKE, IN, tuple parameters, nested lambdas) in ClickHouse and Snowflake.->in MySQL, MariaDB, SQLite and PostgreSQL is not a lambda.FormatASTCheckedround-trips across ClickHouse and Snowflake.pkg/sql/ast/lambda_precedence_test.go:SQL()of a hand-built lambda does not group its body.RetargetOperatorskeeps the lambda for ClickHouse, Snowflake and the generic dialect, and refuses it for all 6 dialects without lambdas.RetargetOperatorsrefuses JSON->/->>for ClickHouse and Snowflake and keeps them for the JSON dialects.pkg/transpiler/lambda_unicode_test.go:TestTranspileLambdaToGeneric: the same 11 statements plusarrayMap(x -> x * 2, arr) FROM t, from ClickHouse and Snowflake toDialectGeneric, have pinned outputs with the body parenthesized (x -> (x * 2)).->/->>statements from PostgreSQL, MySQL, MariaDB and SQLite to ClickHouse and Snowflake return the JSON-operator refusal. PostgreSQL to MySQL keeps->.TestLongArrowRejectedForLambdaDialects(parser) and the same-dialect case inTestTranspileJSONArrowRefusedForLambdaDialects:->>in ClickHouse and Snowflake is an E4001 parse error, including for clickhouse->clickhouse and snowflake->snowflake transpiles; MySQL, MariaDB, SQLite and PostgreSQL still parse it. Both fail with the parser check reverted.pkg/sql/tokenizer/unicode_escape_test.go: the E4001 code and the location of the U& rejection. SpacedU & 'x',menu&'x'andU&xare still accepted.TestPrefilterEquivalence: four inputs added, as CRITIQUE item 14 asks.Updated expectations:
TestOtherDialectsKeepUnicodeAmpersandTokenslisted Oracle as a dialect that keeps the threeU & 'x'tokens. Oracle moves to the new rejection test, and MariaDB is added to the list.tokenizer_literal_golden.json: I regenerated it and kept only the changed entries. A structural check confirms that only 111 digests changed, all for oracle, snowflake or clickhouse and all on U& inputs.Gate (AC-STD), run in the worktree:
task check(fmt:check, vet, golangci-lint, test:race): passtask test:cbinding: passgo test -count=1 ./pkg/compatibility/...: passgo run honnef.co/go/tools/cmd/staticcheck@latest ./pkg/sql/ast/ ./pkg/sql/parser/ ./pkg/sql/tokenizer/ ./pkg/transpiler/: no findingstask checkEXIT 0;task test:cbindingok;go test -count=1 ./pkg/compatibility/...ok; staticcheck@latest on./pkg/sql/ast/ ./pkg/transpiler/clean. The pre-commit hook's short run failed once on the timing testTestFormatASTExpressionLinearGrowth(load average 33). It passed on re-run, and intask check.->>parse rejection), re-run in the worktree:task checkEXIT 0;task test:cbindingEXIT 0;go test -count=1 ./pkg/compatibility/...EXIT 0;go run honnef.co/go/tools/cmd/staticcheck@latest ./pkg/sql/parser/... ./pkg/sql/ast/... ./pkg/sql/tokenizer/... ./pkg/transpiler/...EXIT 0, no findings.TestTranspileLambdaToGeneric; test and CHANGELOG only), re-run in the worktree:task checkEXIT 0;task test:cbindingEXIT 0;go test -count=1 ./pkg/compatibility/...EXIT 0;go run honnef.co/go/tools/cmd/staticcheck@latest ./pkg/transpiler/...EXIT 0, no findings.go test -count=1 -run TestPerformanceRegression ./pkg/sql/parser/: failed once in a non-race full run while the shared machine had a load average of about 11. It passed on re-run, about 50% faster than baseline on every case.Breaking Changes
ast.BinaryExpression.Lambda. JSON dumps of the AST gain"Lambda": false|true.->now parses as a lambda, so the AST shape changes:Rightis the whole body.->that meant something else in the target.arrayMap(x -> x * 2, arr)becomesarrayMap(x -> (x * 2), arr)(onmain:arrayMap(x -> x * 2, arr)).->/->>to ClickHouse or Snowflake now returns an error. It used to be emitted unchanged.->>with the ClickHouse or Snowflake dialect (parse, validate,FormatWithDialect, and same-dialect transpile) now fails with an E4001 parse error,the ->> operator is not supported in <dialect>. Onmain,SELECT a ->> 'b' FROM tparsed in these dialects and round-tripped unchanged; neither dialect has->>as an expression operator.U&'...'andU&"..."in Oracle, Snowflake and ClickHouse now fail to tokenize with E4001, where they used to be accepted as a bitwise AND.🤖 Generated with Claude Code
https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2