Commit c50661f
* feat(parser): add SQL Server PIVOT/UNPIVOT clause parsing (#456)
Add support for SQL Server and Oracle PIVOT/UNPIVOT operators in FROM
clauses. PIVOT transforms rows to columns via an aggregate function,
while UNPIVOT performs the reverse column-to-row transformation.
- Add PivotClause and UnpivotClause AST node types
- Add Pivot/Unpivot fields to TableReference struct
- Implement parsePivotClause/parseUnpivotClause in new pivot.go
- Wire parsing into parseFromTableReference and parseJoinedTableRef
- Add PIVOT/UNPIVOT to tokenizer keyword map for correct token typing
- Update formatter to render PIVOT/UNPIVOT clauses
- Enable testdata/mssql/11_pivot.sql and 12_unpivot.sql
- Add 4 dedicated tests covering subquery+alias, plain table, AS alias
Closes #456
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* security: add CVE-2026-32285 to .trivyignore
CVE-2026-32285 affects github.com/buger/jsonparser v1.1.1, which is a
transitive dependency via mark3labs/mcp-go → invopop/jsonschema →
wk8/go-ordered-map → buger/jsonparser.
No fixed version is available upstream. The package is not called
directly by any GoSQLX code and risk is scoped to MCP JSON schema
generation. Added to .trivyignore until a patched version is released.
Fixes Trivy Repository Scan CI failures in PR #475 and #477.
* fix(parser): scope PIVOT/UNPIVOT to SQL Server/Oracle dialects
PIVOT and UNPIVOT were registered in the global tokenizer keyword map,
which made them reserved words across every dialect. Queries like
`SELECT pivot FROM users` then failed in PostgreSQL/MySQL/SQLite/
ClickHouse where these identifiers are perfectly legal.
Changes:
- Remove PIVOT/UNPIVOT from tokenizer keywordTokenTypes (they are now
tokenized as identifiers in all dialects).
- Gate isPivotKeyword/isUnpivotKeyword on the parser dialect (TSQL/
Oracle only) and accept identifier-typed tokens by value match.
- Skip alias consumption in parseFromTableReference / parseJoinedTableRef
when the upcoming identifier is a contextual PIVOT/UNPIVOT keyword,
so the pivot-clause parser can claim it.
- Fix unsafe single-value type assertion in TestTSQL_PivotWithASAlias
to comply with the project's mandatory two-value form.
- Add TestPivotIdentifierInNonTSQLDialects regression covering pivot/
unpivot as identifiers in PostgreSQL, MySQL, and SQLite.
All parser/tokenizer/formatter tests pass with -race.
* fix(parser): polish PIVOT/UNPIVOT round-trip and validation
- Formatter emits AS before PIVOT/UNPIVOT aliases for clean round-trip.
- Tokenizer records Quote='[' on SQL Server bracket-quoted identifiers;
pivot parser uses renderQuotedIdent to preserve [North] etc. in
PivotClause.InValues and UnpivotClause.InColumns.
- Reject empty IN lists for both PIVOT and UNPIVOT.
- Extract parsePivotAlias helper, collapsing four duplicated alias
blocks in select_subquery.go.
- Add TestPivotNegativeCases (missing parens, missing FOR/IN, empty IN)
and TestPivotBracketedInValuesPreserved.
Full test suite passes with -race.
* fix(parser): escape embedded delimiters and fix empty-IN error order
- renderQuotedIdent now doubles embedded `]`, `"`, and `` ` ``
per dialect convention so identifiers like [foo]bar] round-trip
unambiguously.
- Empty PIVOT/UNPIVOT IN list check now runs before the closing-`)`
check so the user-facing error is "at least one value/column..."
instead of the misleading ") to close ... IN list".
- Clarify renderQuotedIdent comment to reference Token.Quote and
Word.QuoteStyle as the actual sources.
* refactor(formatter): thread nodeFormatter through tableRefSQL and joinSQL
Previously these package-level renderers hardcoded keyword literals
(PIVOT, UNPIVOT, FOR, IN, LATERAL, JOIN, ON) which bypassed the
caller's case policy (f.kw). Thread *nodeFormatter into both
functions and route every keyword through f.kw so uppercase/lowercase
options apply uniformly across FROM, JOIN, MERGE, DELETE USING, and
UPDATE FROM paths.
Addresses claude-review feedback on PR #477. All formatter, parser,
and tokenizer tests pass with -race.
* fix(parser): allow TABLE/PARTITION/TABLES as identifiers in ClickHouse (#480)
ClickHouse system tables (system.replicas, system.parts, system.tables) expose
columns named `table` and `partition`, and queries commonly reference
`system.tables`. The parser previously rejected these because TABLE, PARTITION,
and TABLES tokenize as keywords and the non-reserved-keyword-as-identifier path
was gated to SQL Server only.
- Extend the gate in parsePrimaryExpression to also enable for ClickHouse.
- Add TokenTypePartition and the "TABLES" keyword value to isNonReservedKeyword
so they can serve as identifiers in expression position and after qualifiers.
Closes #480
* fix(parser): allow ROWS as identifier in ClickHouse (#480, #482)
Extend the non-reserved-keyword allowlist to also accept ROWS as a column
identifier. ClickHouse exposes a `rows` column in system.tables and other
system tables; ROWS is otherwise reserved for window-frame syntax
(ROWS BETWEEN ...) but is unambiguous in expression position.
Part of #482 (ClickHouse parser gaps from QA sweep).
---------
Co-authored-by: Ajit Pratap Singh <ajitpratapsingh@Ajits-Mac-mini-2655.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent daea668 commit c50661f
File tree
3 files changed
+124
-4
lines changed- pkg/sql/parser
3 files changed
+124
-4
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
103 | 103 | | |
104 | 104 | | |
105 | 105 | | |
106 | | - | |
| 106 | + | |
107 | 107 | | |
108 | 108 | | |
109 | 109 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
958 | 958 | | |
959 | 959 | | |
960 | 960 | | |
961 | | - | |
| 961 | + | |
| 962 | + | |
962 | 963 | | |
963 | | - | |
| 964 | + | |
| 965 | + | |
| 966 | + | |
964 | 967 | | |
965 | 968 | | |
966 | 969 | | |
967 | 970 | | |
968 | | - | |
| 971 | + | |
| 972 | + | |
969 | 973 | | |
970 | 974 | | |
971 | 975 | | |
| |||
0 commit comments