Repository navigation
fix(formatter,ast,parser): no inner semicolons; keep IGNORE NULLS, named args, ON DUPLICATE KEY, OUTPUT, SETTINGS (#602, #600) - #631
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Claude finished @ajitpratap0's task in 26s —— View job PR #631 reviewVerdict: looks good. I have no blocking issues. I read the diff of the non-test source files and did not run the test suite. I'm relying on the validator evidence in the PR description for the race gate. What I checked
Minor observations (non-blocking)
TestsCoverage is good. It has 24 round-trip cases, a corpus invariant, a pool release test, parser error cases and golden updates. The reported fail-on-parent / pass-on-HEAD check is the right way to validate this. The only test I'd add is a round-trip for a set operation inside a CTE inside INSERT...SELECT, so that Security and performanceThere are no security concerns. The parser changes are bounded loops that always advance a token. There is no new allocation on hot paths beyond the CHANGELOG is intentionally deferred to #598, as the PR notes. |
…yle (#602) In readable style (AddSemicolon=true, the FormatString default) every nested FormatStatement call received the caller's options unchanged, so each inner statement appended its own ";". The output contained ";" inside CTE bodies and IN/EXISTS/scalar subqueries, after every set-operation operand and level, and ";;" after INSERT ... SELECT and CREATE [MATERIALIZED] VIEW. None of it re-parsed. Add an unexported nestedOpts helper that clears AddSemicolon and use it at every nested call site (renderInsert, renderCreateView, renderCreateMaterializedView, renderIn, renderExists, renderSubquery, renderWith, renderSetOperation operands). renderSetOperation now writes its ";" once after the whole chain instead of once per level, keeping the linear spine walk. Compact style is unchanged. Tests: table test with exact output, re-parse, idempotency and AST equivalence; compact-style guard; a testdata corpus invariant that the only ";" in readable output is the final terminator; exported FormatExpression check. The set-operation reference oracle now models a single trailing ";". Fixes #602 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve
…LICATE KEY UPDATE, INSERT OUTPUT and ClickHouse SELECT SETTINGS (#600) tree.SQL() and pkg/formatter (compact and readable) dropped five constructs, which silently changed the meaning of the rendered SQL: - FunctionCall.NullTreatment (IGNORE/RESPECT NULLS) is now written after the argument list, before WITHIN GROUP/FILTER/OVER, matching the parser's order. - *ast.NamedArgument had no SQL(), so `f(x => 1)` rendered as `f(x)`. It now renders `name => value`, quoting the name when needed. - InsertStatement.Output (SQL Server OUTPUT) and InsertStatement.OnDuplicateKey (MySQL ON DUPLICATE KEY UPDATE) are now rendered by both InsertStatement.SQL() and the formatter's renderInsert. - ClickHouse SELECT ... SETTINGS was skipped by the parser with no AST field to hold it. It is now parsed as `name = expr [, ...]` and stored on the rightmost SelectStatement (which covers set operations). It is rendered by SelectStatement.SQL() and renderSelect. New additive API (no existing signature changes; legacy ast.Setting is untouched): - type ast.QuerySetting{Name, Value, Pos}: plain data, not a Node, with (*QuerySetting).SQL() - field ast.SelectStatement.Settings []QuerySetting (ClickHouse only) - method (*ast.NamedArgument).SQL() SelectStatement.Children() now exposes setting values. Setting names are not exposed, so they are never reported as columns. releaseSelectStatement releases and zeroes the Settings entries. Behaviour change: ClickHouse SELECT ... SETTINGS parsing is stricter. The old loop swallowed every token up to EOF, ';' or ')'. A malformed list (`SETTINGS`, `SETTINGS x`, `SETTINGS x =`, `SETTINGS = 4`, a trailing comma) or trailing tokens such as INTO OUTFILE now return a syntax error. A trailing `FORMAT <name>` is still accepted, but it is parse-only and not modeled, as before. DDL SETTINGS parsing (ddl.go) is unchanged. The sql_golden_test "function/full" expectation pinned the buggy output without IGNORE NULLS even though NullTreatment was set. It is updated to include it. Tests: round-trip tests for every reproduction in the issue, across the generic, Snowflake, PostgreSQL, MySQL, SQL Server and ClickHouse dialects (SQL(), compact and readable FormatAST, re-parse). Also parser tests for SETTINGS storage and errors, AST rendering/Children tests, and a pool release test. Fixes #600 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve
Accept any word token as the format name in a trailing ClickHouse `SELECT ... SETTINGS ... FORMAT <name>`, using the same rule as setting names. `FORMAT Null` (also NULL/null) tokenizes as a NULL literal and was rejected with E2002 "expected statement, got NULL", although the parent commit accepted it. A format name is now required: a bare `FORMAT` is a syntax error. Tests: FORMAT with JSON, JSONEachRow, TabSeparated, Values, Null, NULL, null and CSV, FORMAT Null followed by another statement, error cases for a missing or quoted format name, and a formatter test that SETTINGS, OUTPUT and ON DUPLICATE KEY UPDATE honour lowercase KeywordCase in compact and readable styles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve
d6049e4 to
9c8640b
Compare
|
Merge train (batch 3): rebased onto main after #630 merged (9c8640b). Conflict resolution: these resolutions match the trial integration of all 12 branches, where the full race gate passed.
Checks after the rebase: the full race gate passes locally, Adversarial pass (Opus): no must-fix findings. One should-fix: ClickHouse |
|
Claude finished @ajitpratap0's task in 18s —— View job PR #631 reviewVerdict: looks good, no blocking issues. I read the non-test source diff ( #602 (semicolons)
#600 (dropped clauses)
Non-blocking observations
Security and performanceThere are no security concerns. The new parser loops always advance a token. The only new allocation is the CHANGELOG is deferred to #598, as the PR notes. |
…d costs - #628: SQLite still rejects count(*) [cnt]; only SQL Server keeps it. - #631: bare FORMAT / FORMAT <name> rules apply after SETTINGS. - Performance: record the measured costs of the batch-3 correctness fixes (Identifier and TableReference growth, quoting in SQL(), lineStarts reallocation, sorted Extract results). - #632: note the lıke, WITH CHECK OPTION and MINUS rendering changes. - Fix the [Unreleased] compare link (v1.14.0...HEAD) and add [1.14.0]. - Keep each section a single tight list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve
…xes; refresh performance baselines (#566) (#598) * docs: consolidate CHANGELOG for the perf audit and batch-2 fixes; refresh performance baselines (#566) CHANGELOG [Unreleased] now covers every PR merged since v1.14.0, with a Behavior changes section for the observable changes in #597, #595, #584, #585, #588, #581, #582/#570, #517/#518 and #511, plus Added, Changed, Fixed, Performance, Security and Internal entries. The two existing entries (#526 and the tokenizer-pool dialect fix) are kept verbatim. performance_baselines.json is re-measured on an Apple M4 with BenchmarkPerformanceBaseline -count=10 -benchmem (median). Baselines are set at about 2.5x the M4 median rather than 1.5x: the CI Benchmark job on ubuntu-latest measures these queries about 1.5-3.2x slower than M4 (last 9 Tests runs on main, 0715bb9..61ca6c8), so 1.5x with the unchanged tolerances would fail CI's slowest runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve * docs(changelog): add batch-3 follow-up fixes (#599-#620) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve * docs(changelog): record #632 render-injection hardening and #624 quote guards Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve * docs(changelog): correct batch-3 entries after review; record measured costs - #628: SQLite still rejects count(*) [cnt]; only SQL Server keeps it. - #631: bare FORMAT / FORMAT <name> rules apply after SETTINGS. - Performance: record the measured costs of the batch-3 correctness fixes (Identifier and TableReference growth, quoting in SQL(), lineStarts reallocation, sorted Extract results). - #632: note the lıke, WITH CHECK OPTION and MINUS rendering changes. - Fix the [Unreleased] compare link (v1.14.0...HEAD) and add [1.14.0]. - Keep each section a single tight list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve * docs(changelog): describe the release as source-compatible with runtime behaviour changes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Summary
#602 formatter: readable style emits
;inside CTE/IN/EXISTS subqueries and set operations, and;;after INSERT ... SELECT / CREATE VIEWIn readable style (AddSemicolon=true), the formatter now ends only the outermost statement with ';'. Before this change, CTE bodies, IN/EXISTS/scalar subqueries and set-operation operands got an inner ';' that broke re-parsing, and INSERT ... SELECT and CREATE [MATERIALIZED] VIEW ended in ';;'. The fix adds an unexported nestedOpts helper that clears AddSemicolon and uses it at the 8 nested render sites. renderSetOperation now writes one ';' after the whole chain, still in a single linear pass over the spine. Compact output is byte-identical to before across 163 corpus statements. In a corpus check over testdata, readable-style re-parse failures fell from 36 to 6 and ';;' occurrences from 13 to 0. The new regression tests (24 round-trip cases, a corpus invariant, and FormatExpression subquery cases) fail on the parent and pass on HEAD; the race suite, vet and golangci-lint are clean. Known issues left for follow-ups: SHOW/DESCRIBE/SEQUENCE/fallback renderers never emit ';' (for example "SHOW TABLES\nSELECT 1;"), NOT EXISTS renders as "NOT (EXISTS (...) NOT )", MATCH...AGAINST and ARRAY-cast rendering are broken, the table name "groups" does not re-parse, nested-subquery indentation, and parenthesization of right-nested set operations.
#600 ast/formatter: SQL() and pkg/formatter drop IGNORE NULLS, named arguments, ON DUPLICATE KEY UPDATE and INSERT OUTPUT; parser discards SELECT ... SETTINGS
Fixes #600. tree.SQL() and pkg/formatter (compact and readable) now keep five constructs they used to drop:
f(x => 1), via the new (*ast.NamedArgument).SQL())The SETTINGS clause is now parsed into the new additive field SelectStatement.Settings ([]ast.QuerySetting). For set operations it is stored on the rightmost SELECT. It is released and zeroed in the pool release path. A trailing
FORMAT <name>is still accepted as parse-only, and a malformed SETTINGS list or a bare FORMAT now returns a syntax error.Every reproduction from the issue round-trips verbatim through SQL(), FormatAST compact and FormatAST readable, and re-parses in its own dialect. The PostgreSQL ON CONFLICT ... RETURNING control case is unchanged. The new regression tests fail on the parent commit and pass on HEAD. The full
-racesuite, go vet and golangci-lint are clean.Deferred (follow-ups):
gosqlx formatstill uses its own renderer, so it keeps dropping these clauses until cli:gosqlx formatsilently corrupts many expressions; switch the CLI to pkg/formatter #599 switches it to pkg/formatter.parse -f jsondoes not serialize Settings.LAST_VALUE(x IGNORE NULLS)is not parsed.Commits (3), one implementation commit per issue plus test-only follow-ups:
;inside CTE/IN/EXISTS subqueries and set operations, and;;after INSERT ... SELECT / CREATE VIEW #602)14 files changed, 957 insertions(+), 35 deletions(-)Test Plan
CGO_ENABLED=1 go test -race -timeout 300s $(go list ./... | grep -v /cbinding)),go vet,golangci-lint, re-ran the reproduction cases on HEAD, and confirmed the new tests fail on the parent commit.benchstat -count=6before/after on the same machine.#602 evidence (validator)
#602 race gate: PASS. Ran
CGO_ENABLED=1 go test -race -timeout 300s $(go list ./... | grep -v /cbinding)on HEAD 1a97287: exit 0, 44 packages ok, no FAIL lines and no flakes (log at scratchpad/b3/602/race.txt).CGO_ENABLED=1 go test -race ./pkg/cbinding/also passed (ok, 1.8s).#600 evidence (validator)
#600 race gate: PASS. Ran
CGO_ENABLED=1 go test -race -timeout 300s $(go list ./... | grep -v /cbinding)at HEAD d6049e4: 44 packages ok, 0 FAIL, exit 0, no flakes. That run includes TestClickHouseSettingsTTLFormat, clickhouse_qa select_settings and select_settings_multi, the new pool release test, and the sql_golden_test.Breaking Changes
;inside CTE/IN/EXISTS subqueries and set operations, and;;after INSERT ... SELECT / CREATE VIEW #602: There are no exported API or signature changes. The only new identifier is the unexported helper nestedOpts. Observable behaviour changes only when AddSemicolon is set (ReadableStyle, FormatString's default): nested statements (CTE bodies; IN, EXISTS and scalar subqueries rendered by the formatter; set-operation operands; and the query of INSERT ... SELECT / CREATE [MATERIALIZED] VIEW) no longer get their own ';'. A set operation chain ends with a single ';'. Compact style is unchanged. The commit/PR should describe this as a correctness fix: the old output did not re-parse, or contained ';;'.ast.QuerySetting{Name string; Value Expression; Pos models.Location}. It is plain data, not a Node, and has a(*QuerySetting).SQL() stringmethod.ast.SelectStatement.Settings []QuerySetting, which the parser populates only for ClickHouse.(*ast.NamedArgument).SQL() string.Observable behaviour changes:
name => valuenamed arguments, INSERT ... OUTPUT, ON DUPLICATE KEY UPDATE and ClickHouse SELECT ... SETTINGS. Before, these were dropped.FORMAT <name>) now return a syntax error instead of being skipped.The commit body will document these. No exported signatures change, and the legacy ast.Setting type is untouched.
Notes
gosqlx formatsilently corrupts many expressions; switch the CLI to pkg/formatter #599-schema/postgres: loader misreports index columns and key flags (column order, INCLUDE, expression and partitioned indexes, read-only roles) #620); later PRs in the train are rebased onto this one after it merges.main.Fixes #602
Fixes #600
Follow-up to #566
🤖 Generated with Claude Code
https://claude.ai/code/session_01UZSk1292jPc8ipDboNxeve