Repository navigation
fix(cli,transpiler,lsp): close re-verification follow-ups - #887
Conversation
PostgreSQL, Redshift and SQLite VACUUM / ANALYZE and PostgreSQL LISTEN, UNLISTEN and NOTIFY were kept verbatim, so trailing same-line tokens (VACUUM t junk) were swallowed into the statement text. They now follow the GRANT / COPY pattern: a small per-dialect grammar finds the end and utilityTailError rejects anything after it. Generic mode accepts the union of the dialect forms; MySQL / MariaDB / Oracle ANALYZE TABLE keeps its verbatim path. Adds regression tests for the already-fixed CREATE TRIGGER / PREPARE / GRANT / COPY boundaries and for the splitter's MySQL backslash escapes and PostgreSQL CREATE RULE ... DO (a; b) command lists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
Scan(tree) only inspected top-level SELECT WHERE/HAVING/columns/FROM, INSERT VALUES rows, UPDATE/DELETE WHERE and set-operation leaves, so time-based, out-of-band and tautology patterns inside CTEs, subqueries, derived tables, CASE/CAST/IN/BETWEEN, ORDER/GROUP BY, JOIN ON, window specs, FILTER, LIMIT, LATERAL/APPLY, PREWHERE, QUALIFY, CONNECT BY, RETURNING, ON CONFLICT, INSERT ... SELECT, MERGE, CREATE TABLE AS, CREATE [MATERIALIZED] VIEW and EXPLAIN went undetected while ScanSQL caught them. Scan now uses ast.Inspect: every FunctionCall and table function is checked once, tautologies are checked in every condition position, UNION checks run on each outermost set operation (including those nested in CTEs and subqueries), and text-bodied nodes keep the text path. JOIN Left references are skipped to avoid double counting, and qualified identifiers such as a.id = b.id are no longer reported as tautologies. T-SQL IF ... WAITFOR and standalone VALUES were already covered on main (#871) and gain regression tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
…TE HISTORY SQL Server: a delimited [top] / "top" is now an ordinary name (the select list no longer reads it as the TOP clause) and keeps its delimiters in SQL() and the formatter. Bare TOP is rejected where a table name, DML/MERGE target, table alias or select-item alias is expected, matching T-SQL, instead of being accepted in FROM/INSERT/DELETE but rejected in UPDATE. MariaDB: DELETE HISTORY FROM t [PARTITION (p, ...)] [BEFORE SYSTEM_TIME [TIMESTAMP | TRANSACTION] expr] parses any point expression and round-trips the partition list and unit keyword. The new fields live in DMLTargetFields.History (DeleteHistoryOptions) so DeleteStatement keeps its size class. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
…comments - utility grammar cursor no longer stores the -1 groupEnd/bracketEnd sentinel before failing (GRANT ... TO [ / [AppUser / CURRENT_USER(, MariaDB IDENTIFIED VIA ... USING PASSWORD(, COPY (); Parser.seek clamps negative indexes to EOF. These inputs now return E2002 instead of panicking. - With the MySQL or MariaDB dialect, '#' starts a line comment in the tokenizer (recorded in Comments, preserved by the formatter), the statement splitter and its procedural lookahead, highlight error recovery and the dropped-word checker. Other dialects keep their '#' meaning. - Regression tests over all 8 dialects, fuzz seeds, realworld corpus cases; literal golden regenerated (only mysql/mariadb digests change for inputs containing '#'). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
- initialize now sends the LSP 3.17 key textDocumentSync instead of the non-standard textDocumentSyncOptions, so clients send open/change/close. - readMessage rejects a non-positive Content-Length instead of panicking in makeslice, and drains oversized bodies so message framing survives. - didChange ranges are clamped to the document, read as UTF-16 code units and respect CRLF line ends; negative positions are rejected and reversed ranges are swapped, so slicing cannot go out of bounds. - A panic in a message handler is logged and answered with InternalError, and background diagnostics recover too, so one bad input cannot end the server process. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
…rvive long operator chains - A LIMIT that belongs to a UNION/INTERSECT/EXCEPT was pushed into the last branch as TOP when targeting SQL Server, silently changing results. It is now emitted on the set operation as ORDER BY ... OFFSET n ROWS FETCH NEXT m ROWS ONLY; without ORDER BY, Transpile returns an error. A TOP branch moved to a LIMIT dialect keeps the set-level ORDER BY. - operatorRetargeter.walk and forEachLiteral now use explicit stacks, so a 490,000-term OR/AND/+ chain transpiles instead of crashing the process with an unrecoverable stack overflow. Rewrites that would nest output more than 512 levels deep return an error, and || flattening into CONCAT stays linear. - Near-ceiling chain tests skip under -race to stay within the package timeout; a 50k-term case runs everywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
…e once in Extract - formatSQL's comment-preserving path cleared the layout terminator but only the plain path added ';' to statements rendered as raw text, so with AddSemicolon and any comment, PostgreSQL DO $$...$$, CREATE FUNCTION bodies and Oracle BEGIN ... END blocks lost their ';' and the output failed to parse or merged two statements. Both paths now use formatter.EnsureStatementTerminator. - metadataCollector visited WITH clauses, CTE bodies, set-operation operands, INSERT queries and UPDATE assignments explicitly and again through Children(), doubling the work at each level: nested CTEs at depth 10 took about 59 s and long UNION ALL chains never finished. Children() already covers them, so the explicit visits are gone and every node is visited once. Results are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
…ion and inline comments - Dollar-quote stripping ran inside quoted strings, so SELECT '$x$'; DROP TABLE users; SELECT '$x$' scanned clean. A $tag$ is now stripped only when every quoting interpretation (backslash or doubled quote escapes, PostgreSQL E-strings, "..." strings, [bracket] identifiers, nested comments, # comments) agrees it is outside strings and comments. - Tautology and comment checks only looked at the first 10,000 bytes. They now cover the whole input; inputs above 10 MiB get a CRITICAL INPUT_TOO_LARGE finding regardless of the severity filter. - Comments between keywords (UNION/**/SELECT, SLEEP/**/(5), ;--\nDROP, # comments, /*/, MySQL 1--1) evaded matching. Patterns now also run over comment-free views of the text for each quoting convention; views only add findings. /*! */ and /*M! */ bodies are scanned. - The AST UNION NULL-padding check now matches the parser's null literal. - The tautology check compares operands without allocating per candidate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
- analyze --security and lint's security pass merge ScanSQL findings with the AST scan, so stacked DROP statements are no longer reported clean; inline SQL containing '/' is no longer treated as a missing file. - SQL Server: LIMIT/OFFSET and bare OFFSET without ORDER BY get ORDER BY (SELECT NULL); FETCH FIRST gets OFFSET 0 ROWS. - Row limits in derived tables, JOIN operands and scalar subqueries are rendered for the target dialect; a cheap text pre-check keeps the common select list on the old path with no extra allocations. - QueryTailFrom keeps WITH TIES; targets that cannot express it error. - LSP unknown methods return MethodNotFound (-32601). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
# Conflicts: # CHANGELOG.md # pkg/sql/security/wp_p_bypass_test.go # pkg/transpiler/row_limits.go # pkg/transpiler/row_limits_test.go
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.
|
|
Claude finished @ajitpratap0's task in 27s —— View job Review of PR #887I read the non-test diff only. I did not build the code or run the tests. Overall: The changes are focused and the tests cover each fix. The notes below are mostly edge cases and maintainability. Findings
PerformanceThe pre-check keeps the old path for most queries, which matches the allocation numbers in the description. The nested scalar subquery copy is O(depth²), which is acceptable at the parser's depth limit. Test coverageCoverage is good for the listed scenarios. The gaps to consider are the "select" heuristic edge cases, the No blocking issues found. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
|
Claude finished @ajitpratap0's task in 16s —— View job Review of PR #887I read the diff for the CLI, LSP and input-detection changes and checked it against the earlier review notes. I did not build the code or run the tests. Overall: The changes are focused and each fix has a test. I found no blocking issues. Verified
Suggestions (non-blocking)
Performance and securityThe cheap pre-check keeps the common path unchanged, which matches the allocation numbers in the PR description. The nested scalar-subquery copy is O(depth²) and acceptable at the parser's depth limit. Running |
Summary
Fixes the issues the 2026-10-28 re-verification found after #878–#885:
gosqlx analyze --securityandgosqlx lint's security pass used onlyScan(AST), soSELECT 1; DROP TABLE usersreported "No security issues detected". Both now also runScanSQLon the raw text and merge the findings, deduplicated by pattern, severity and description. Inline SQL containing/(UNION/**/SELECT,4 / 2) is no longer mistaken for a missing file path; real paths still read files.LIMIT n OFFSET mand bareOFFSETwithout ORDER BY now getORDER BY (SELECT NULL), andFETCH FIRSTgets the requiredOFFSET 0 ROWS. Set operations without ORDER BY still return an error, because SQL Server requires UNION ORDER BY items to appear in the select list.LIMITthere.WITH TIES:ast.QueryTailFromdropped the flag. It now survives set-operation tails, and targets that cannot express it return an error.-32601(MethodNotFound) instead of-32603.Test Plan
analyze_security_regression_test.go/**/input, existing filescmdutil/input_test.gorow_limits_test.gopkg/transpiler/wpq_nested_test.go,pkg/formatter/wpq_nested_test.goquery_tail_test.goWITH TIESround tripsmethod_not_found_test.gotask check(55 packages, golangci-lint 0 issues) andtask test:cbindingpass locally.Format, 26 for realworld dialect formatting). Realworld dialect formatting is about 4% slower from the new derived-table and set-operation rewrites. Nested scalar subqueries in the select list are copied per level; at the parser's maximum depth that costs low milliseconds.Breaking Changes
analyze --securityandlintmay report more findings, namely thoseScanSQLcatches.WITH TIESnow returns an error instead of silently dropping it.🤖 Generated with Claude Code
https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2