From 66b6d67098f849e1fe713d78f8f9e499121876c2 Mon Sep 17 00:00:00 2001 From: Toshiki Iga Date: Wed, 28 Jan 2026 08:28:20 +0900 Subject: [PATCH 1/4] Tiga0128d (#6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SQLパーサー強化 + ブラウザデモ追加 + ドキュメント整備 概要 SQL Formatter 実装に向けた前段のパーサーを強化し、ブラウザで SQL→AST を確認できるデモを追加。テスト拡充とドキュメント整備も実施。 変更点 パーサー機能強化 lexer 実用化(コメント/文字列エスケープ/指数/引用識別子) 構文厳密化(FROM/HAVING/JOIN/Window frame/ORDER+FETCH 制約) MATCH_RECOGNIZE をテーブル修飾として解釈 NATURAL を句キーワードとして扱い別名誤認を防止 ブラウザ利用向け window.CalciteLexer/Parser の公開 テスト拡充 回帰ケース(MATCH_RECOGNIZE / PIVOT / WINDOW のバリエーション) lexer ケース(コメント/引用識別子/指数/先頭ドット数値) DDL/DML ケース(SET/RESET/ALTER/EXPLAIN/DESCRIBE/CALL) 異常系テスト追加(HAVING without GROUP, NATURAL JOIN + ON 等) ブラウザデモ追加 [index.html](https://file+.vscode-resource.vscode-cdn.net/Users/igapyon/.vscode/extensions/openai.chatgpt-0.4.67-darwin-arm64/webview/#) に Tailwind CSS ベースの SQL→AST Playground を追加 ドキュメント整備 README 追加(目的/構成/テスト手順/デモ) スクリーンショット埋め込み AGENTS.md 追加 TODO 更新 スクリーンショット --- AGENTS.md | 26 +++++ README.md | 33 ++++++ TODO.md | 27 ++--- index.html | 122 ++++++++++++++++++++ screenshot.png | Bin 0 -> 252927 bytes spec/apache-calcite-Parser-test.js | 75 +++++++++++- spec/apache-calcite-Parser.js | 179 ++++++++++++++++++++++------- 7 files changed, 406 insertions(+), 56 deletions(-) create mode 100644 AGENTS.md create mode 100644 README.md create mode 100644 index.html create mode 100644 screenshot.png diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..1f27c07 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,26 @@ +# AGENTS.md + +このリポジトリで作業するエージェント向けの最小ガイドです。 + +## 目的 +- 最終目標: SQL Formatter の実装 +- 現状: SQL パーサーの実装とテストの整備 + +## 主要ファイル +- `spec/apache-calcite-Parser.js`: lexer + parser 本体 +- `spec/apache-calcite-Parser-test.js`: 回帰/異常系テスト +- `spec/apache-calcite-Parser.md`: 仕様(EBNFベース) +- `index.html`: SQL → AST 出力の簡易デモ + +## 作業の基本方針 +- `.md` と `.js` の production 名は一致させる +- 変更後は `node spec/apache-calcite-Parser-test.js` を実行 +- 破壊的変更は `TODO.md` に記録 + +## 進捗の記録 +- 直近のタスクは `TODO.md` を参照 + +## 実行コマンド +```bash +node spec/apache-calcite-Parser-test.js +``` diff --git a/README.md b/README.md new file mode 100644 index 0000000..782a69e --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# SQL Formatter (WIP) + +このリポジトリは、最終的に **SQL Formatter** を作るためのプロジェクトです。 +現在は前段として **SQL パーサー** を実装しています。 + +![Playground screenshot](screenshot.png) + +## 目的 +- 最終目標: SQL を整形するフォーマッタの実装 +- 現状: SQL パーサーを自作し、AST を生成する段階 + +## 現在の構成 +- `spec/apache-calcite-Parser.js`: パーサー本体(lexer + parser) +- `spec/apache-calcite-Parser-test.js`: 回帰/異常系テスト +- `spec/apache-calcite-Parser.md`: 仕様(EBNFベース) +- `spec/apache-calcite-Parser.jj`: 参照文法 + +## 使い方(開発時) +テストを実行してパーサーの動作確認ができます。 + +```bash +node spec/apache-calcite-Parser-test.js +``` + +## 直近の作業予定 +- **index.html から SQL を入力すると AST がテキストで出力される** 簡易デモを作成 + - ブラウザで SQL を入力 → JS パーサーで AST を生成 → テキスト表示 +- AST 仕様の整理と固定化 +- 構文の厳密化(句の順序/排他の追加チェック) +- 最終的に formatter へ接続 + +## ライセンス +`LICENSE` を参照してください。 diff --git a/TODO.md b/TODO.md index 5de8024..05e2ef7 100644 --- a/TODO.md +++ b/TODO.md @@ -4,22 +4,19 @@ - Stage 1: lexer + 全 production skeleton 済み。 - 入口/Select/From/Join/式/関数/型/JSON/日付系の主要骨格は実装済み。 - md⇔js の production 名・順序は一致済み(ヘルパー除外で一致)。 +- 回帰テスト/異常系テストを追加済み(全 46 件通過)。 +- lexer 実用化(コメント/文字列エスケープ/指数/引用識別子)対応済み。 +- 構文厳密化(FROM 必須/HAVING 制約/JOIN 条件/フレーム条件/ORDER+FETCH など)対応済み。 ## 残り作業(優先順の目安・更新) -1. SetOp/Query 周り - - AddSetOpQuery / BinaryQueryOperator / AddSetOpQueryOrExpr / Query / SqlQueryEof - - PartitionedQueryOrQueryOrExpr / OrderByOfSetSemanticsTable -2. テーブル参照の拡張 - - Snapshot / Tablesample / Pivot / Unpivot / MatchRecognize / TableOverOpt / Over / ExtendedTableRef -3. LIMIT/OFFSET/FETCH と ORDER BY 制約 - - LimitClause / OffsetClause / FetchClause -4. Add* 系の実装(実用上の肝) - - AddSelectItem / AddOrderItem / AddGroupingElement / AddWindowSpec / AddWithItem など -5. DDL/DML の細部 - - SqlSetOption/SqlAlter/SqlExplain などの分岐網羅(現状は最小実装) -6. 低優先ユーティリティ - - ReservedFunctionName / NonReserved* / CollectionsTypeName / CollateClause など -7. 厳格な突合(最終) +1. AST 仕様の整理 + - ノード型の命名とフィールドを最終定義し、テストで固定化 +2. 構文厳密化の追加候補 + - 句の出現順序/必須/排他の追加チェック(ORDER/LIMIT/OFFSET/FETCH/QUALIFY など) + - 省略可能要素の優先順位を整理(誤って別名に吸われる余地の洗い出し) +3. テスト拡張(必要なら) + - SETOP/ORDER/LIMIT の境界・エラー例を追加 +4. 厳格な突合(最終) - apache-calcite-Parser.md を正とし、apache-calcite-Parser.js の整合を最終チェック。 - 手順(最小): 1) .md から production 名一覧を抽出(順序付き) @@ -33,7 +30,7 @@ - 差分一覧(テキスト)と、反映後の .js ## 実装方針メモ -- lexer は最小: IDENT/NUMBER/STRING/SYMBOL/EOF のみ。予約語判定は isKeyword で大文字比較。 +- lexer は実用化済み(コメント/エスケープ/指数/引用識別子対応)。予約語判定は isKeyword で大文字比較。 - AST 形式は自由だが、既存の軽量ノード(type + 必要最小限のフィールド)で統一。 - 失敗時は notImplemented を残して段階的に埋める。 diff --git a/index.html b/index.html new file mode 100644 index 0000000..489cac3 --- /dev/null +++ b/index.html @@ -0,0 +1,122 @@ + + + + + + SQL Parser Playground + + + + + + + +
+
+
+

SQL Parser Playground

+

SQL を入力して AST をテキストとして出力します。

+
+ +
+
+
+

SQL Input

+ +
+ +
+ + + +
+
+ +
+
+

AST Output

+ idle +
+

+        
+
+ +
+

Parser: spec/apache-calcite-Parser.js をブラウザで実行

+
+
+
+ + + + + diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..361df77425de98828e27f5d3dfb0718cf6fdec40 GIT binary patch literal 252927 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�v#v=2 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 Uof@=ZHLL*uMx5BX=UXb!k@=Hfq{fSco_q2W& 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 0)', fn: 'SqlStmtList' }, + { name: 'match-recognize-partition-order', sql: 'SELECT * FROM t MATCH_RECOGNIZE (PARTITION BY a ORDER BY b MEASURES CLASSIFIER() AS c PATTERN (A B) DEFINE A AS a > 0, B AS b > 0)', fn: 'SqlStmtList' }, + { name: 'match-recognize-after-skip', sql: 'SELECT * FROM t MATCH_RECOGNIZE (AFTER MATCH SKIP TO NEXT ROW PATTERN (A) DEFINE A AS a > 0)', fn: 'SqlStmtList' }, { name: 'pivot', sql: 'SELECT * FROM t PIVOT (SUM(x) FOR y IN (1))', fn: 'SqlStmtList' }, + { name: 'pivot-multi-values', sql: 'SELECT * FROM t PIVOT (SUM(x) FOR y IN (1 AS one, 2 AS two))', fn: 'SqlStmtList' }, + { name: 'pivot-multi-aggs', sql: 'SELECT * FROM t PIVOT (SUM(x) AS sx, COUNT(*) FOR y IN (1))', fn: 'SqlStmtList' }, { name: 'unpivot', sql: 'SELECT * FROM t UNPIVOT (v FOR c IN (a))', fn: 'SqlStmtList' }, + { name: 'window-order-frame', sql: 'SELECT a FROM t WINDOW w AS (PARTITION BY a ORDER BY b ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)', fn: 'SqlStmtList' }, + { name: 'window-range', sql: 'SELECT a FROM t WINDOW w AS (ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)', fn: 'SqlStmtList' }, + { name: 'window-allow-partial', sql: 'SELECT a FROM t WINDOW w AS (ORDER BY b ALLOW PARTIAL)', fn: 'SqlStmtList' }, + { name: 'lexer-line-comment', sql: 'SELECT 1 -- trailing comment', fn: 'SqlStmtList' }, + { name: 'lexer-block-comment', sql: 'SELECT /* block */ 1', fn: 'SqlStmtList' }, + { name: 'lexer-quoted-ident-double', sql: 'SELECT "Select" FROM "From"', fn: 'SqlStmtList' }, + { name: 'lexer-quoted-ident-backtick', sql: 'SELECT `a` FROM `b`', fn: 'SqlStmtList' }, + { name: 'lexer-string-escape', sql: "SELECT 'a''b' FROM t", fn: 'SqlStmtList' }, + { name: 'lexer-number-exponent', sql: 'SELECT 1.2e-3 FROM t', fn: 'SqlStmtList' }, + { name: 'lexer-number-leading-dot', sql: 'SELECT .5 FROM t', fn: 'SqlStmtList' }, + { name: 'ddl-set', sql: 'SET foo = 1', fn: 'SqlStmtList' }, + { name: 'ddl-reset', sql: 'RESET foo', fn: 'SqlStmtList' }, + { name: 'ddl-reset-all', sql: 'RESET ALL', fn: 'SqlStmtList' }, + { name: 'ddl-alter-system-set', sql: 'ALTER SYSTEM SET foo = ON', fn: 'SqlStmtList' }, + { name: 'ddl-alter-session-reset', sql: 'ALTER SESSION RESET foo', fn: 'SqlStmtList' }, + { name: 'ddl-explain', sql: 'EXPLAIN PLAN INCLUDING ATTRIBUTES WITH TYPE AS JSON FOR SELECT 1', fn: 'SqlStmtList' }, + { name: 'ddl-describe-table', sql: 'DESCRIBE TABLE t', fn: 'SqlStmtList' }, + { name: 'ddl-describe-database', sql: 'DESCRIBE DATABASE db', fn: 'SqlStmtList' }, + { name: 'ddl-describe-statement', sql: 'DESCRIBE STATEMENT SELECT 1', fn: 'SqlStmtList' }, + { name: 'ddl-call', sql: 'CALL foo(1)', fn: 'SqlStmtList' }, ]; +const negativeCases = [ + { name: 'neg-having-without-group', sql: 'SELECT a FROM t HAVING a > 0', fn: 'SqlStmtList' }, + { name: 'neg-natural-join-on', sql: 'SELECT * FROM a NATURAL JOIN b ON a.id = b.id', fn: 'SqlStmtList' }, + { name: 'neg-join-no-condition', sql: 'SELECT * FROM a JOIN b', fn: 'SqlStmtList' }, + { name: 'neg-window-frame-without-order', sql: 'SELECT a FROM t WINDOW w AS (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)', fn: 'SqlStmtList' }, + { name: 'neg-fetch-without-order', sql: 'SELECT * FROM t FETCH FIRST 1 ROW ONLY', fn: 'SqlStmtList' }, + { name: 'neg-limit-and-fetch', sql: 'SELECT * FROM t ORDER BY a LIMIT 1 FETCH FIRST 1 ROW ONLY', fn: 'SqlStmtList' }, + { name: 'neg-where-without-from', sql: 'SELECT a WHERE a > 0', fn: 'SqlStmtList' }, +]; + +if (require.main === module) { + const samples = [ + "SELECT 1", + "SELECT /*+ index(t) */ a AS x FROM t WHERE a IS NOT DISTINCT FROM b", + "WITH t AS (SELECT 1) SELECT * FROM t", + "EXPLAIN PLAN FOR SELECT 1", + "INSERT INTO t(a) VALUES (1)", + "UPDATE t SET a = 1 WHERE b = 2", + "DELETE FROM t WHERE a IN (1,2,3)", + "MERGE INTO t USING u ON t.id = u.id WHEN MATCHED THEN UPDATE SET a = 1", + "SELECT ARRAY_AGG(x) FROM t", + "SELECT JSON_VALUE(doc, '$.a' RETURNING VARCHAR) FROM t", + "SELECT DATE_DIFF(d1, d2, DAY) FROM t", + ]; + for (const src of samples) { + try { + runCase({ name: 'sample', sql: src, fn: 'SqlStmtList' }); + console.log(`OK: ${src}`); + } catch (e) { + console.error(`NG: ${src} -> ${e.message}`); + } + } +} + function runCase({ name, sql, fn }) { const lexer = new CalciteLexer(sql); const tokens = lexer.tokenize(); @@ -42,9 +100,24 @@ for (const c of cases) { } } +for (const c of negativeCases) { + let ok = false; + try { + runCase(c); + } catch (_err) { + ok = true; + } + if (ok) { + console.log(`OK ${c.name} (rejected)`); + } else { + failed++; + console.error(`NG ${c.name}: expected rejection`); + } +} + if (failed > 0) { console.error(`\nFAILED: ${failed}`); process.exit(1); } -console.log(`\nALL OK: ${cases.length}`); +console.log(`\nALL OK: ${cases.length + negativeCases.length}`); diff --git a/spec/apache-calcite-Parser.js b/spec/apache-calcite-Parser.js index 0ea31f7..3bc38b2 100644 --- a/spec/apache-calcite-Parser.js +++ b/spec/apache-calcite-Parser.js @@ -20,23 +20,101 @@ class CalciteLexer { this.pos++; continue; } + // line comment + if (ch === "-" && s[this.pos + 1] === "-") { + this.pos += 2; + while (this.pos < s.length && s[this.pos] !== "\n") this.pos++; + continue; + } + // block comment + if (ch === "/" && s[this.pos + 1] === "*") { + this.pos += 2; + while (this.pos < s.length && !(s[this.pos] === "*" && s[this.pos + 1] === "/")) { + this.pos++; + } + if (this.pos < s.length) this.pos += 2; + continue; + } // strings (single-quoted, no escape handling) if (ch === "'") { let value = ""; this.pos++; - while (this.pos < s.length && s[this.pos] !== "'") { + while (this.pos < s.length) { + if (s[this.pos] === "'") { + if (s[this.pos + 1] === "'") { + value += "'"; + this.pos += 2; + continue; + } + break; + } + if (s[this.pos] === "\\" && this.pos + 1 < s.length) { + value += s[this.pos + 1]; + this.pos += 2; + continue; + } value += s[this.pos++]; } - this.pos++; + if (s[this.pos] === "'") this.pos++; this.tokens.push({ type: "STRING", value }); continue; } - // numbers - if (/[0-9]/.test(ch)) { + // quoted identifiers + if (ch === '"' || ch === "`") { + const quote = ch; let value = ""; - while (this.pos < s.length && /[0-9\.]/.test(s[this.pos])) { + this.pos++; + while (this.pos < s.length) { + if (s[this.pos] === quote) { + if (s[this.pos + 1] === quote) { + value += quote; + this.pos += 2; + continue; + } + break; + } value += s[this.pos++]; } + if (s[this.pos] === quote) this.pos++; + this.tokens.push({ type: "IDENT", value }); + continue; + } + // numbers (including leading dot and exponent) + if (/[0-9]/.test(ch) || (ch === "." && /[0-9]/.test(s[this.pos + 1]))) { + let value = ""; + if (ch === ".") { + value += "."; + this.pos++; + while (this.pos < s.length && /[0-9]/.test(s[this.pos])) { + value += s[this.pos++]; + } + } else { + while (this.pos < s.length && /[0-9]/.test(s[this.pos])) { + value += s[this.pos++]; + } + if (s[this.pos] === ".") { + value += "."; + this.pos++; + while (this.pos < s.length && /[0-9]/.test(s[this.pos])) { + value += s[this.pos++]; + } + } + } + if (/[eE]/.test(s[this.pos])) { + const e = s[this.pos]; + const sign = s[this.pos + 1]; + if (/[0-9\+\-]/.test(sign) && /[0-9]/.test(s[this.pos + 2] || "")) { + value += e; + this.pos++; + if (sign === "+" || sign === "-") { + value += sign; + this.pos++; + } + while (this.pos < s.length && /[0-9]/.test(s[this.pos])) { + value += s[this.pos++]; + } + } + } this.tokens.push({ type: "NUMBER", value }); continue; } @@ -180,8 +258,9 @@ class CalciteParser { const keywords = new Set([ "FROM", "WHERE", "GROUP", "HAVING", "WINDOW", "QUALIFY", "ORDER", "LIMIT", "OFFSET", "FETCH", "UNION", "INTERSECT", "EXCEPT", - "JOIN", "INNER", "LEFT", "RIGHT", "FULL", "CROSS", "ASOF", - "SET", "USING", "ON", "WHEN", + "JOIN", "INNER", "LEFT", "RIGHT", "FULL", "CROSS", "ASOF", "NATURAL", + "SET", "USING", "ON", "WHEN", "FOR", + "MATCH_RECOGNIZE", ]); return keywords.has(value); } @@ -580,6 +659,12 @@ class CalciteParser { } else if (this.isKeyword("FETCH")) { fetch = this.FetchClause(); } + if (fetch && !orderBy) { + throw new Error("FETCH requires ORDER BY"); + } + if (limit && fetch) { + throw new Error("LIMIT and FETCH cannot be combined"); + } return { type: "OrderByLimitOpt", orderBy, limit, offset, fetch }; } @@ -671,6 +756,13 @@ class CalciteParser { while (this.acceptSymbol(",")) { selectItems.push(this.AddSelectItem()); } + if (!this.isKeyword("FROM")) { + if (this.isKeyword("WHERE") || this.isKeyword("GROUP") || this.isKeyword("HAVING") || + this.isKeyword("WINDOW") || this.isKeyword("QUALIFY")) { + const t = this.peek(); + throw new Error(`Expected FROM before ${String(t.value).toUpperCase()}`); + } + } let from = null; let where = null; let groupBy = null; @@ -685,6 +777,9 @@ class CalciteParser { if (this.isKeyword("WINDOW")) window = this.Window(); if (this.isKeyword("QUALIFY")) qualify = this.Qualify(); } + if (having && !groupBy) { + throw new Error("HAVING requires GROUP BY"); + } return { type: "SqlSelect", hints, @@ -718,7 +813,17 @@ class CalciteParser { WindowSpecification() { this.expectSymbol("("); let name = null; - if (this.peek().type === "IDENT") { + if ( + this.peek().type === "IDENT" && + !this.isKeyword("PARTITION") && + !this.isKeyword("ORDER") && + !this.isKeyword("ROWS") && + !this.isKeyword("RANGE") && + !this.isKeyword("GROUPS") && + !this.isKeyword("EXCLUDE") && + !this.isKeyword("ALLOW") && + !this.isKeyword("DISALLOW") + ) { name = this.SimpleIdentifier(); } let partitionBy = null; @@ -745,6 +850,9 @@ class CalciteParser { const exclusion = this.WindowExclusion(); frame.exclusion = exclusion; } + if (frame && !orderBy) { + throw new Error("Window frame requires ORDER BY"); + } let partial = null; if (this.acceptKeyword("ALLOW") || this.acceptKeyword("DISALLOW")) { const mode = String(this.tokens[this.pos - 1].value).toUpperCase(); @@ -913,6 +1021,12 @@ class CalciteParser { } else if (this.acceptKeyword("USING")) { condition = { type: "Using", columns: this.ParenthesizedSimpleIdentifierList() }; } + if (!natural && joinType !== "CROSS JOIN" && !condition) { + throw new Error("JOIN requires ON or USING"); + } + if (natural && condition) { + throw new Error("NATURAL JOIN cannot use ON or USING"); + } return { type: "JoinTable", natural, joinType, table, condition }; } @@ -983,6 +1097,8 @@ class CalciteParser { if (this.isKeyword("PIVOT")) pivot = this.Pivot(); let unpivot = null; if (this.isKeyword("UNPIVOT")) unpivot = this.Unpivot(); + let matchRecognize = null; + if (this.isKeyword("MATCH_RECOGNIZE")) matchRecognize = this.MatchRecognize(); // alias let alias = null; let columns = null; @@ -991,7 +1107,10 @@ class CalciteParser { if (this.isSymbol("(")) { columns = this.ParenthesizedSimpleIdentifierList(); } - } else if (this.peek().type === "IDENT") { + } else if ( + this.peek().type === "IDENT" && + !this.isClauseKeyword(String(this.peek().value).toUpperCase()) + ) { alias = this.SimpleIdentifier(); if (this.isSymbol("(")) { columns = this.ParenthesizedSimpleIdentifierList(); @@ -1001,7 +1120,7 @@ class CalciteParser { if (this.isKeyword("TABLESAMPLE")) { tablesample = this.Tablesample(); } - return { type: "TableRef", base, pivot, unpivot, alias, columns, tablesample }; + return { type: "TableRef", base, pivot, unpivot, matchRecognize, alias, columns, tablesample }; } Snapshot() { @@ -3359,6 +3478,7 @@ class CalciteParser { PatternFactor() { const primary = this.PatternPrimary(); + if (!primary) return null; let quantifier = null; if (this.acceptSymbol("*")) quantifier = { kind: "*" }; else if (this.acceptSymbol("+")) quantifier = { kind: "+" }; @@ -3944,34 +4064,13 @@ class CalciteParser { } -module.exports = { - CalciteLexer, - CalciteParser, -}; - -if (require.main === module) { - const samples = [ - "SELECT 1", - "SELECT /*+ index(t) */ a AS x FROM t WHERE a IS NOT DISTINCT FROM b", - "WITH t AS (SELECT 1) SELECT * FROM t", - "EXPLAIN PLAN FOR SELECT 1", - "INSERT INTO t(a) VALUES (1)", - "UPDATE t SET a = 1 WHERE b = 2", - "DELETE FROM t WHERE a IN (1,2,3)", - "MERGE INTO t USING u ON t.id = u.id WHEN MATCHED THEN UPDATE SET a = 1", - "SELECT ARRAY_AGG(x) FROM t", - "SELECT JSON_VALUE(doc, '$.a' RETURNING VARCHAR) FROM t", - "SELECT DATE_DIFF(d1, d2, DAY) FROM t", - ]; - for (const src of samples) { - const lexer = new CalciteLexer(src); - const tokens = lexer.tokenize(); - const parser = new CalciteParser(tokens); - try { - parser.SqlStmtList(); - console.log(`OK: ${src}`); - } catch (e) { - console.error(`NG: ${src} -> ${e.message}`); - } - } +if (typeof module !== "undefined" && module.exports) { + module.exports = { + CalciteLexer, + CalciteParser, + }; +} +if (typeof window !== "undefined") { + window.CalciteLexer = CalciteLexer; + window.CalciteParser = CalciteParser; } From e75a380231e2d77e8272fe29636506e55b2355b3 Mon Sep 17 00:00:00 2001 From: Toshiki Iga Date: Wed, 28 Jan 2026 12:27:02 +0900 Subject: [PATCH 2/4] =?UTF-8?q?SQL=20Formatter=20WIP=E3=81=AE=E3=83=87?= =?UTF-8?q?=E3=83=A2=E5=AE=9F=E8=A3=85=E3=81=A8=E3=83=91=E3=83=BC=E3=82=B5?= =?UTF-8?q?=E5=BC=B7=E5=8C=96/=E3=83=86=E3=82=B9=E3=83=88=E6=8B=A1?= =?UTF-8?q?=E5=85=85=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SQL Formatter WIPのデモ実装とパーサ強化/テスト拡充 概要 SQL Formatter の基盤整備として、AST/フォーマットの UI を追加し、パーサの厳密化とテスト拡充を実施。フォーマッタの初期骨格とルール定義、ドキュメントも更新。 主な変更点 フォーマッタ/UIを SQL Formatter WIP UI として新規作成 入力 SQL → AST → 整形の最小パイプラインを搭載 WHERE/QUALIFY/AND/OR/ORDER/GROUP/JOIN の整形ルールを実装 CTE (WITH) と基本的な関数/OVER を出力 コメント挿入(簡易)を追加 lexer の実用化(コメント/エスケープ/指数/引用識別子) 構文厳密化(FROM/HAVING/JOIN/FRAME/FETCH/OFFSET/SETOP など) トークンに start/end を付与 --- AGENTS.md | 2 +- AST.md | 142 +++++++++ FORMATTER_RULES.md | 57 ++++ README.md | 13 +- formatter.js | 134 ++++++++ index-parser.html | 190 +++++++++++ index.html | 486 ++++++++++++++++++++++++++--- spec/apache-calcite-Parser-test.js | 125 ++++++++ spec/apache-calcite-Parser.js | 54 +++- 9 files changed, 1154 insertions(+), 49 deletions(-) create mode 100644 AST.md create mode 100644 FORMATTER_RULES.md create mode 100644 formatter.js create mode 100644 index-parser.html diff --git a/AGENTS.md b/AGENTS.md index 1f27c07..23c3d03 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,7 +10,7 @@ - `spec/apache-calcite-Parser.js`: lexer + parser 本体 - `spec/apache-calcite-Parser-test.js`: 回帰/異常系テスト - `spec/apache-calcite-Parser.md`: 仕様(EBNFベース) -- `index.html`: SQL → AST 出力の簡易デモ +- `index-parser.html`: SQL → AST 出力の簡易デモ ## 作業の基本方針 - `.md` と `.js` の production 名は一致させる diff --git a/AST.md b/AST.md new file mode 100644 index 0000000..ae5193a --- /dev/null +++ b/AST.md @@ -0,0 +1,142 @@ +# AST 仕様(暫定) + +このドキュメントは、`spec/apache-calcite-Parser.js` が出力する AST の最小仕様をまとめたものです。 +現状は実装の読みやすさ優先の軽量ノード構成です。 + +## 共通ルール +- すべてのノードは `type` フィールドを持つ +- 省略可能要素は `null` または未設定 +- リストは配列で表現 + +## ルート +- `SqlStmtList` + - `statements`: ステートメント配列 +- `SqlStmtEof` + - `stmt` +- `SqlExpressionEof` + - `expr` + +## クエリ +- `SqlSelect` + - `hints`: `TableHints | null` + - `stream`: `boolean` + - `setQuantifier`: `"ALL" | "DISTINCT" | null` + - `selectItems`: `AddSelectItem[]` + - `from`: `FromClause | null` + - `where`: `Where | null` + - `groupBy`: `GroupBy | null` + - `having`: `Having | null` + - `window`: `Window | null` + - `qualify`: `Qualify | null` +- `QueryOrExpr` + - `withList`, `leaf`, `setOps` +- `OrderedQueryOrExpr` + - `query`, `orderByLimitOpt` + +## 句 +- `FromClause` + - `first`: `TableRef` + - `joins`: (`JoinTable` | `CommaJoin` | `ApplyJoin`)[] +- `JoinTable` + - `natural`: `boolean` + - `joinType`: `string` (e.g. `"JOIN"`, `"LEFT JOIN"`) + - `table`: `TableRef` + - `condition`: `On | Using | null` +- `CommaJoin` + - `table` +- `Where` + - `expr` +- `GroupBy` + - `set`: `"DISTINCT" | "ALL" | null` + - `list`: `GroupingElementList` +- `Having` + - `expr` +- `Window` + - `items`: `AddWindowSpec[]` +- `Qualify` + - `expr` +- `OrderBy` + - `list`: `OrderItemList` +- `LimitClause` + - `value`, `offset` +- `OffsetClause` + - `value`, `rows` +- `FetchClause` + - `mode`, `value`, `rows` + +## テーブル参照 +- `TableRef` + - `base`, `pivot`, `unpivot`, `matchRecognize`, `alias`, `columns`, `tablesample` +- `TableName` + - `name`: `CompoundTableIdentifier` +- `Subquery` + - `query`: `OrderedQueryOrExpr` +- `Unnest` + - `items`: `Expression[]` + - `withOrdinality`: `boolean` +- `TableFunctionCall` + - `call`: `NamedRoutineCall` +- `Pivot` + - `aggs`: `AddPivotAgg[]` + - `axis`: `SimpleIdentifierOrList` + - `values`: `AddPivotValue[]` +- `Unpivot` + - `nulls`: `"INCLUDE" | "EXCLUDE" | null` + - `columns`: `SimpleIdentifierOrList` + - `axis`: `SimpleIdentifierOrList` + - `values`: `AddUnpivotValue[]` +- `MatchRecognize` + - `partitionBy`, `orderBy`, `measures`, `rowsPerMatch`, `afterMatchSkip` + - `pattern`, `anchorStart`, `anchorEnd`, `within`, `subsets`, `define` + +## 式 +- `Expression2b`(内部表現) + - `prefixes`, `base`, `extensions` +- `BinaryExpression` + - `operator`, `left`, `right` +- `Literal` + - `value` +- `StringLiteral` + - `value` +- `NumericLiteral` + - `value` +- `Identifier` + - `name` +- `CompoundIdentifier` + - `parts` + +## 関数 +- `NamedFunctionCall` + - `name`, `args`, `orderBy`, `withinGroup`, `nullTreatment`, `filter`, `over` +- `BuiltinFunctionCall` + - `kind` ごとにフィールドが異なる(各関数ノード参照) +- `MatchRecognizeFunctionCall` + +## DML/DDL +- `SqlInsert` / `SqlDelete` / `SqlUpdate` / `SqlMerge` +- `SqlSetOption` / `SqlAlter` / `SqlExplain` / `SqlDescribe` + +## 例 +```json +{ + "type": "SqlStmtList", + "statements": [ + { + "type": "SqlSelect", + "selectItems": [ + { "type": "AddSelectItem", "expr": { "type": "SelectExpression", "star": true } } + ], + "from": { + "type": "FromClause", + "first": { "type": "TableRef", "base": { "type": "TableName", "name": { "type": "CompoundTableIdentifier" } } }, + "joins": [] + } + } + ] +} +``` + +## TODO +- `Expression*` 系の正規化 +- `BuiltinFunctionCall` の各関数ノードの仕様を追加 +- テストで AST 形を固定 diff --git a/FORMATTER_RULES.md b/FORMATTER_RULES.md new file mode 100644 index 0000000..415b1d2 --- /dev/null +++ b/FORMATTER_RULES.md @@ -0,0 +1,57 @@ +# SQL Formatter ルール(暫定) + +本ドキュメントは SQL Formatter の出力スタイルを定義するルール表です。 + +## 基本スタイル +- インデント: **4 スペース** +- キーワード: **大文字 (UPPER)** +- 末尾セミコロン: **なし** +- カンマ位置: **先頭** + +## SELECT 句 +- カラムは **1 行 1 カラム** +- 例: + ```sql + SELECT + col_a + , col_b + , SUM(col_c) + FROM table_name + ``` + +## 主要句の改行方針 +- 以下の句は **必ず改行** + - `FROM` + - `JOIN` + - `WHERE` + - `GROUP BY` + - `HAVING` + - `ORDER BY` + +## JOIN の整形 +- JOIN 句は 1 行 1 句 +- `ON` / `USING` は次行にインデント + +例: +```sql +FROM table_a a +JOIN table_b b + ON a.id = b.id +``` + +## WHERE / GROUP BY / HAVING / ORDER BY +- 各句は 1 行開始 +- `AND` / `OR` は **次行** に出す + +例: +```sql +WHERE a = 1 + AND b = 2 +``` + +## TODO +- サブクエリ・括弧のインデント方針 +- 関数引数の改行ルール +- CASE 式の整形ルール +- SETOP(UNION/INTERSECT/EXCEPT)の改行規則 +- WINDOW 句/OVER 句の改行規則 diff --git a/README.md b/README.md index 782a69e..4c008be 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ - `spec/apache-calcite-Parser-test.js`: 回帰/異常系テスト - `spec/apache-calcite-Parser.md`: 仕様(EBNFベース) - `spec/apache-calcite-Parser.jj`: 参照文法 +- `AST.md`: AST 仕様(暫定) +- `FORMATTER_RULES.md`: SQL Formatter の出力ルール +- `index-parser.html`: SQL → AST 出力の簡易デモ +- `index.html`: SQL Formatter の UI(WIP) ## 使い方(開発時) テストを実行してパーサーの動作確認ができます。 @@ -23,11 +27,16 @@ node spec/apache-calcite-Parser-test.js ``` ## 直近の作業予定 -- **index.html から SQL を入力すると AST がテキストで出力される** 簡易デモを作成 - - ブラウザで SQL を入力 → JS パーサーで AST を生成 → テキスト表示 +- **index-parser.html**: SQL → AST を出力する簡易デモ(実装済み) +- **index.html**: 将来的に SQL Formatter の UI を実装予定 - AST 仕様の整理と固定化 - 構文の厳密化(句の順序/排他の追加チェック) - 最終的に formatter へ接続 +## デモ機能 +- AST の JSON 出力 +- トークン一覧の表示 +- エラートークンのハイライト表示 + ## ライセンス `LICENSE` を参照してください。 diff --git a/formatter.js b/formatter.js new file mode 100644 index 0000000..e4b4b5c --- /dev/null +++ b/formatter.js @@ -0,0 +1,134 @@ +'use strict'; + +const { CalciteLexer, CalciteParser } = require('./spec/apache-calcite-Parser'); + +function formatSql(sql) { + const lexer = new CalciteLexer(sql); + const tokens = lexer.tokenize(); + const parser = new CalciteParser(tokens); + const ast = parser.SqlStmtList(); + return renderNode(ast, { indent: 0 }).trim(); +} + +function renderNode(node, ctx) { + if (!node) return ''; + switch (node.type) { + case 'SqlStmtList': + return node.statements.map(stmt => renderNode(stmt, ctx)).join('\n'); + case 'SqlSelect': + return renderSelect(node, ctx); + case 'AddSelectItem': + return renderSelectItem(node, ctx); + case 'SelectExpression': + if (node.star) return '*'; + return renderNode(node.expr, ctx); + case 'TableRef': + return renderTableRef(node, ctx); + case 'FromClause': + return renderFrom(node, ctx); + case 'Where': + return `WHERE ${renderNode(node.expr, ctx)}`; + case 'GroupBy': + return renderGroupBy(node, ctx); + case 'OrderBy': + return renderOrderBy(node, ctx); + case 'OrderItemList': + return node.items.map(it => renderNode(it, ctx)).join('\n'); + case 'AddOrderItem': + return renderOrderItem(node, ctx); + case 'StringLiteral': + return `'${node.value}'`; + case 'NumericLiteral': + return String(node.value); + case 'Literal': + return String(node.value); + case 'Identifier': + return node.name; + case 'CompoundIdentifier': + return node.parts.map(p => (p.type === 'Identifier' ? p.name : '*')).join('.'); + case 'Expression2b': + return renderExpression2b(node, ctx); + case 'BinaryExpression': + return `${renderNode(node.left, ctx)} ${renderBinaryOp(node.operator)} ${renderNode(node.right, ctx)}`; + default: + return `[${node.type}]`; + } +} + +function renderSelect(node, ctx) { + const lines = []; + lines.push('SELECT'); + const items = node.selectItems || []; + items.forEach((item, idx) => { + const prefix = idx === 0 ? indent(ctx, 1) : `${indent(ctx, 1)}, `; + lines.push(prefix + renderNode(item, { ...ctx, indent: ctx.indent + 1 })); + }); + if (node.from) { + lines.push(`${indent(ctx)}FROM ${renderNode(node.from, { ...ctx, indent: ctx.indent + 1 })}`); + } + if (node.where) { + lines.push(`${indent(ctx)}${renderNode(node.where, { ...ctx, indent: ctx.indent + 1 })}`); + } + if (node.groupBy) { + lines.push(`${indent(ctx)}${renderNode(node.groupBy, { ...ctx, indent: ctx.indent + 1 })}`); + } + if (node.having) { + lines.push(`${indent(ctx)}${renderNode(node.having, { ...ctx, indent: ctx.indent + 1 })}`); + } + if (node.orderBy) { + lines.push(`${indent(ctx)}${renderNode(node.orderBy, { ...ctx, indent: ctx.indent + 1 })}`); + } + return lines.join('\n'); +} + +function renderSelectItem(node, ctx) { + const expr = renderNode(node.expr, ctx); + if (node.alias) return `${expr} AS ${node.alias.name || node.alias}`; + return expr; +} + +function renderFrom(node, ctx) { + const base = renderNode(node.first, ctx); + const joins = (node.joins || []).map(j => renderNode(j, ctx)); + return [base, ...joins].join('\n' + indent(ctx)); +} + +function renderTableRef(node, ctx) { + if (node.base) { + return renderNode(node.base, ctx); + } + return '[TableRef]'; +} + +function renderGroupBy(node, ctx) { + const items = renderNode(node.list, ctx); + return `GROUP BY ${items}`; +} + +function renderOrderBy(node, ctx) { + const items = renderNode(node.list, ctx); + return `ORDER BY ${items}`; +} + +function renderOrderItem(node, ctx) { + const expr = renderNode(node.expr, ctx); + const dir = node.direction ? ` ${node.direction}` : ''; + return `${expr}${dir}`; +} + +function renderExpression2b(node, ctx) { + const base = renderNode(node.base, ctx); + return base; +} + +function renderBinaryOp(op) { + if (!op) return ''; + if (typeof op === 'string') return op; + return op.op || ''; +} + +function indent(ctx, extra = 0) { + return ' '.repeat((ctx.indent + extra) * 4); +} + +module.exports = { formatSql }; diff --git a/index-parser.html b/index-parser.html new file mode 100644 index 0000000..64fceb3 --- /dev/null +++ b/index-parser.html @@ -0,0 +1,190 @@ + + + + + + SQL Parser Playground + + + + + + + +
+
+
+

SQL Parser Playground

+

SQL を入力して AST をテキストとして出力します。

+
+ +
+
+
+

SQL Input

+ +
+ +

+          
+ + + +
+
+ +
+
+

AST Output

+ idle +
+

+        
+
+ +
+
+

Tokens

+ 0 tokens +
+

+      
+ +
+

Parser: spec/apache-calcite-Parser.js をブラウザで実行

+
+
+
+ + + + + diff --git a/index.html b/index.html index 489cac3..495e927 100644 --- a/index.html +++ b/index.html @@ -3,17 +3,16 @@ - SQL Parser Playground + SQL Formatter (WIP)
-

SQL Parser Playground

-

SQL を入力して AST をテキストとして出力します。

+

SQL Formatter (WIP)

+

+ ここは SQL Formatter の UI を実装予定のページです。現在はプレースホルダー。 +

SQL Input

- -
- -
- - - +
+ + +
+
-

AST Output

+

Formatted Output

idle
-

+          
// formatted output will appear here
-

Parser: spec/apache-calcite-Parser.js をブラウザで実行

+

AST デモは index-parser.html を参照してください。

- diff --git a/spec/apache-calcite-Parser-test.js b/spec/apache-calcite-Parser-test.js index e4c9c1a..483a145 100644 --- a/spec/apache-calcite-Parser-test.js +++ b/spec/apache-calcite-Parser-test.js @@ -3,18 +3,22 @@ const { CalciteLexer, CalciteParser } = require('./apache-calcite-Parser'); const cases = [ + // basic { name: 'select-basic', sql: 'SELECT 1', fn: 'SqlStmtList' }, { name: 'select-with-from', sql: 'SELECT a FROM t', fn: 'SqlStmtList' }, { name: 'select-where', sql: 'SELECT a FROM t WHERE b = 1', fn: 'SqlStmtList' }, { name: 'select-group-by', sql: 'SELECT a, COUNT(*) FROM t GROUP BY a', fn: 'SqlStmtList' }, { name: 'select-window', sql: 'SELECT a FROM t WINDOW w AS (PARTITION BY a)', fn: 'SqlStmtList' }, + // CTE / DML core { name: 'cte', sql: 'WITH t AS (SELECT 1) SELECT * FROM t', fn: 'SqlStmtList' }, { name: 'insert-values', sql: 'INSERT INTO t(a) VALUES (1)', fn: 'SqlStmtList' }, { name: 'update', sql: 'UPDATE t SET a = 1 WHERE b = 2', fn: 'SqlStmtList' }, { name: 'delete', sql: 'DELETE FROM t WHERE a IN (1,2,3)', fn: 'SqlStmtList' }, { name: 'merge', sql: 'MERGE INTO t USING u ON t.id = u.id WHEN MATCHED THEN UPDATE SET a = 1', fn: 'SqlStmtList' }, + // functions / expressions { name: 'json-value', sql: "SELECT JSON_VALUE(doc, '$.a' RETURNING VARCHAR) FROM t", fn: 'SqlStmtList' }, { name: 'date-diff', sql: 'SELECT DATE_DIFF(d1, d2, DAY) FROM t', fn: 'SqlStmtList' }, + // MATCH_RECOGNIZE / PIVOT / UNPIVOT { name: 'match-recognize', sql: 'SELECT * FROM t MATCH_RECOGNIZE (PATTERN (A) DEFINE A AS a > 0)', fn: 'SqlStmtList' }, { name: 'match-recognize-partition-order', sql: 'SELECT * FROM t MATCH_RECOGNIZE (PARTITION BY a ORDER BY b MEASURES CLASSIFIER() AS c PATTERN (A B) DEFINE A AS a > 0, B AS b > 0)', fn: 'SqlStmtList' }, { name: 'match-recognize-after-skip', sql: 'SELECT * FROM t MATCH_RECOGNIZE (AFTER MATCH SKIP TO NEXT ROW PATTERN (A) DEFINE A AS a > 0)', fn: 'SqlStmtList' }, @@ -22,9 +26,11 @@ const cases = [ { name: 'pivot-multi-values', sql: 'SELECT * FROM t PIVOT (SUM(x) FOR y IN (1 AS one, 2 AS two))', fn: 'SqlStmtList' }, { name: 'pivot-multi-aggs', sql: 'SELECT * FROM t PIVOT (SUM(x) AS sx, COUNT(*) FOR y IN (1))', fn: 'SqlStmtList' }, { name: 'unpivot', sql: 'SELECT * FROM t UNPIVOT (v FOR c IN (a))', fn: 'SqlStmtList' }, + // window clause { name: 'window-order-frame', sql: 'SELECT a FROM t WINDOW w AS (PARTITION BY a ORDER BY b ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)', fn: 'SqlStmtList' }, { name: 'window-range', sql: 'SELECT a FROM t WINDOW w AS (ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)', fn: 'SqlStmtList' }, { name: 'window-allow-partial', sql: 'SELECT a FROM t WINDOW w AS (ORDER BY b ALLOW PARTIAL)', fn: 'SqlStmtList' }, + // lexer coverage { name: 'lexer-line-comment', sql: 'SELECT 1 -- trailing comment', fn: 'SqlStmtList' }, { name: 'lexer-block-comment', sql: 'SELECT /* block */ 1', fn: 'SqlStmtList' }, { name: 'lexer-quoted-ident-double', sql: 'SELECT "Select" FROM "From"', fn: 'SqlStmtList' }, @@ -32,6 +38,7 @@ const cases = [ { name: 'lexer-string-escape', sql: "SELECT 'a''b' FROM t", fn: 'SqlStmtList' }, { name: 'lexer-number-exponent', sql: 'SELECT 1.2e-3 FROM t', fn: 'SqlStmtList' }, { name: 'lexer-number-leading-dot', sql: 'SELECT .5 FROM t', fn: 'SqlStmtList' }, + // DDL / DCL { name: 'ddl-set', sql: 'SET foo = 1', fn: 'SqlStmtList' }, { name: 'ddl-reset', sql: 'RESET foo', fn: 'SqlStmtList' }, { name: 'ddl-reset-all', sql: 'RESET ALL', fn: 'SqlStmtList' }, @@ -42,9 +49,108 @@ const cases = [ { name: 'ddl-describe-database', sql: 'DESCRIBE DATABASE db', fn: 'SqlStmtList' }, { name: 'ddl-describe-statement', sql: 'DESCRIBE STATEMENT SELECT 1', fn: 'SqlStmtList' }, { name: 'ddl-call', sql: 'CALL foo(1)', fn: 'SqlStmtList' }, + // joins / set ops / order & limit + { name: 'select-join-inner', sql: 'SELECT * FROM a JOIN b ON a.id = b.id', fn: 'SqlStmtList' }, + { name: 'select-join-left', sql: 'SELECT * FROM a LEFT JOIN b ON a.id = b.id', fn: 'SqlStmtList' }, + { name: 'select-join-using', sql: 'SELECT * FROM a JOIN b USING (id)', fn: 'SqlStmtList' }, + { name: 'select-join-natural', sql: 'SELECT * FROM a NATURAL JOIN b', fn: 'SqlStmtList' }, + { name: 'select-join-cross', sql: 'SELECT * FROM a CROSS JOIN b', fn: 'SqlStmtList' }, + { name: 'select-join-comma', sql: 'SELECT * FROM a, b', fn: 'SqlStmtList' }, + { name: 'select-setop-union', sql: 'SELECT a FROM t UNION SELECT a FROM u', fn: 'SqlStmtList' }, + { name: 'select-setop-intersect', sql: 'SELECT a FROM t INTERSECT SELECT a FROM u', fn: 'SqlStmtList' }, + { name: 'select-setop-except', sql: 'SELECT a FROM t EXCEPT SELECT a FROM u', fn: 'SqlStmtList' }, + { name: 'select-order-limit', sql: 'SELECT a FROM t ORDER BY a LIMIT 10', fn: 'SqlStmtList' }, + { name: 'select-offset-limit', sql: 'SELECT a FROM t ORDER BY a OFFSET 5 LIMIT 10', fn: 'SqlStmtList' }, + { name: 'select-fetch', sql: 'SELECT a FROM t ORDER BY a FETCH FIRST 3 ROWS ONLY', fn: 'SqlStmtList' }, + { name: 'select-values', sql: 'VALUES (1), (2)', fn: 'SqlStmtList' }, + { name: 'select-table', sql: 'TABLE t', fn: 'SqlStmtList' }, + // expression / function variants + { name: 'select-case', sql: 'SELECT CASE WHEN a > 0 THEN 1 ELSE 0 END FROM t', fn: 'SqlStmtList' }, + { name: 'select-cast', sql: 'SELECT CAST(a AS INTEGER) FROM t', fn: 'SqlStmtList' }, + { name: 'select-coalesce', sql: 'SELECT COALESCE(a, 0) FROM t', fn: 'SqlStmtList' }, + { name: 'select-json-exists', sql: "SELECT JSON_EXISTS(doc, '$.a') FROM t", fn: 'SqlStmtList' }, + { name: 'select-interval', sql: "SELECT INTERVAL '1' DAY FROM t", fn: 'SqlStmtList' }, + // window functions + { name: 'select-window-over-basic', sql: 'SELECT SUM(a) OVER (PARTITION BY b ORDER BY c) FROM t', fn: 'SqlStmtList' }, + { name: 'select-window-over-frame-rows', sql: 'SELECT SUM(a) OVER (ORDER BY c ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) FROM t', fn: 'SqlStmtList' }, + { name: 'select-window-over-frame-range', sql: 'SELECT SUM(a) OVER (ORDER BY c RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM t', fn: 'SqlStmtList' }, + { name: 'select-window-qualify', sql: 'SELECT a FROM t QUALIFY ROW_NUMBER() OVER (ORDER BY a) = 1', fn: 'SqlStmtList' }, + { name: 'select-window-within-group', sql: 'SELECT LISTAGG(a) WITHIN GROUP (ORDER BY a) FROM t', fn: 'SqlStmtList' }, + { name: 'select-window-null-treatment', sql: 'SELECT LAST_VALUE(a) RESPECT NULLS OVER (ORDER BY a) FROM t', fn: 'SqlStmtList' }, + { name: 'select-window-ignore-nulls', sql: 'SELECT FIRST_VALUE(a) IGNORE NULLS OVER (ORDER BY a) FROM t', fn: 'SqlStmtList' }, + { name: 'select-window-filter', sql: 'SELECT COUNT(*) FILTER (WHERE a > 0) FROM t', fn: 'SqlStmtList' }, + { name: 'select-window-ordered-set', sql: 'SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY a) FROM t', fn: 'SqlStmtList' }, + { name: 'select-window-over-name', sql: 'SELECT SUM(a) OVER w FROM t WINDOW w AS (PARTITION BY b)', fn: 'SqlStmtList' }, + // JSON / text / datetime functions + { name: 'select-json-query', sql: "SELECT JSON_QUERY(doc, '$.a') FROM t", fn: 'SqlStmtList' }, + { name: 'select-json-object', sql: "SELECT JSON_OBJECT('a' VALUE 1) FROM t", fn: 'SqlStmtList' }, + { name: 'select-json-array', sql: "SELECT JSON_ARRAY(1, 2, 3) FROM t", fn: 'SqlStmtList' }, + { name: 'select-json-objectagg', sql: "SELECT JSON_OBJECTAGG(k VALUE v) FROM t", fn: 'SqlStmtList' }, + { name: 'select-json-arrayagg', sql: "SELECT JSON_ARRAYAGG(a) FROM t", fn: 'SqlStmtList' }, + { name: 'select-trim', sql: "SELECT TRIM(BOTH 'x' FROM a) FROM t", fn: 'SqlStmtList' }, + { name: 'select-substring', sql: 'SELECT SUBSTRING(a FROM 2 FOR 3) FROM t', fn: 'SqlStmtList' }, + { name: 'select-position', sql: "SELECT POSITION('a' IN b) FROM t", fn: 'SqlStmtList' }, + { name: 'select-translate', sql: "SELECT TRANSLATE(a, 'abc', 'xyz') FROM t", fn: 'SqlStmtList' }, + { name: 'select-overlay', sql: "SELECT OVERLAY(a PLACING 'x' FROM 2) FROM t", fn: 'SqlStmtList' }, + { name: 'select-date-trunc', sql: "SELECT DATE_TRUNC('DAY', d) FROM t", fn: 'SqlStmtList' }, + { name: 'select-timestamp-diff', sql: 'SELECT TIMESTAMP_DIFF(t1, t2, DAY) FROM t', fn: 'SqlStmtList' }, + { name: 'select-time-trunc', sql: "SELECT TIME_TRUNC(t, HOUR) FROM t", fn: 'SqlStmtList' }, + { name: 'select-convert', sql: 'SELECT CONVERT(a, INTEGER) FROM t', fn: 'SqlStmtList' }, + { name: 'select-extract', sql: 'SELECT EXTRACT(YEAR FROM d) FROM t', fn: 'SqlStmtList' }, + { name: 'select-contains-substr', sql: "SELECT CONTAINS_SUBSTR(a, 'x') FROM t", fn: 'SqlStmtList' }, + { name: 'select-contains-substr-with-scope', sql: "SELECT CONTAINS_SUBSTR(a, 'x', JSON_SCOPE := 2) FROM t", fn: 'SqlStmtList' }, + // typed literals / collections / cast + { name: 'select-typed-date', sql: "SELECT DATE '2020-01-01' FROM t", fn: 'SqlStmtList' }, + { name: 'select-typed-time', sql: "SELECT TIME '12:34:56' FROM t", fn: 'SqlStmtList' }, + { name: 'select-typed-timestamp', sql: "SELECT TIMESTAMP '2020-01-01 12:34:56' FROM t", fn: 'SqlStmtList' }, + { name: 'select-uuid', sql: "SELECT UUID '123e4567-e89b-12d3-a456-426614174000' FROM t", fn: 'SqlStmtList' }, + { name: 'select-interval-year', sql: "SELECT INTERVAL '2' YEAR FROM t", fn: 'SqlStmtList' }, + { name: 'select-interval-day-to-second', sql: "SELECT INTERVAL '1 02:03:04' DAY TO SECOND FROM t", fn: 'SqlStmtList' }, + { name: 'select-row-type', sql: 'SELECT ROW(1, 2) FROM t', fn: 'SqlStmtList' }, + { name: 'select-array-constructor', sql: 'SELECT ARRAY[1, 2, 3] FROM t', fn: 'SqlStmtList' }, + { name: 'select-map-constructor', sql: 'SELECT MAP[1, 2] FROM t', fn: 'SqlStmtList' }, + { name: 'select-multiset', sql: 'SELECT MULTISET[1, 2] FROM t', fn: 'SqlStmtList' }, + { name: 'select-cast-typed', sql: 'SELECT CAST(a AS DECIMAL(10,2)) FROM t', fn: 'SqlStmtList' }, + { name: 'select-cast-with-timezone', sql: 'SELECT CAST(a AS TIMESTAMP WITH TIME ZONE) FROM t', fn: 'SqlStmtList' }, + { name: 'select-period', sql: "SELECT PERIOD (DATE '2020-01-01', DATE '2020-12-31') FROM t", fn: 'SqlStmtList' }, + // DML variants + { name: 'insert-select', sql: 'INSERT INTO t(a) SELECT a FROM u', fn: 'SqlStmtList' }, + { name: 'insert-default-values', sql: 'INSERT INTO t VALUES (DEFAULT)', fn: 'SqlStmtList' }, + { name: 'update-multi-set', sql: 'UPDATE t SET a = 1, b = 2 WHERE c = 3', fn: 'SqlStmtList' }, + { name: 'delete-simple', sql: 'DELETE FROM t', fn: 'SqlStmtList' }, + { name: 'merge-update-insert', sql: 'MERGE INTO t USING u ON t.id = u.id WHEN MATCHED THEN UPDATE SET a = 1 WHEN NOT MATCHED THEN INSERT (id) VALUES (u.id)', fn: 'SqlStmtList' }, + { name: 'explain-without-impl', sql: 'EXPLAIN PLAN WITHOUT IMPLEMENTATION FOR SELECT 1', fn: 'SqlStmtList' }, + { name: 'describe-catalog', sql: 'DESCRIBE CATALOG cat', fn: 'SqlStmtList' }, + { name: 'describe-schema', sql: 'DESCRIBE SCHEMA sch', fn: 'SqlStmtList' }, + { name: 'set-string', sql: "SET foo = 'bar'", fn: 'SqlStmtList' }, + { name: 'set-null', sql: 'SET foo = NULL', fn: 'SqlStmtList' }, + // setop variants + { name: 'setop-union-all', sql: 'SELECT a FROM t UNION ALL SELECT a FROM u', fn: 'SqlStmtList' }, + { name: 'setop-union-distinct', sql: 'SELECT a FROM t UNION DISTINCT SELECT a FROM u', fn: 'SqlStmtList' }, + { name: 'setop-except-all', sql: 'SELECT a FROM t EXCEPT ALL SELECT a FROM u', fn: 'SqlStmtList' }, + { name: 'setop-intersect-distinct', sql: 'SELECT a FROM t INTERSECT DISTINCT SELECT a FROM u', fn: 'SqlStmtList' }, + { name: 'setop-chained', sql: 'SELECT a FROM t UNION SELECT a FROM u INTERSECT SELECT a FROM v', fn: 'SqlStmtList' }, + { name: 'setop-order-limit', sql: 'SELECT a FROM t UNION SELECT a FROM u ORDER BY a LIMIT 5', fn: 'SqlStmtList' }, + { name: 'setop-order-fetch', sql: 'SELECT a FROM t UNION SELECT a FROM u ORDER BY a FETCH FIRST 3 ROWS ONLY', fn: 'SqlStmtList' }, + { name: 'setop-order-offset-limit', sql: 'SELECT a FROM t UNION SELECT a FROM u ORDER BY a OFFSET 2 LIMIT 4', fn: 'SqlStmtList' }, + { name: 'setop-table-values', sql: 'VALUES (1) UNION SELECT 2', fn: 'SqlStmtList' }, + { name: 'setop-table-table', sql: 'TABLE t UNION TABLE u', fn: 'SqlStmtList' }, + // DML/DDL extras + { name: 'insert-upsert', sql: 'UPSERT INTO t(a) VALUES (1)', fn: 'SqlStmtList' }, + { name: 'insert-hints', sql: 'INSERT INTO t /*+ hint */ (a) VALUES (1)', fn: 'SqlStmtList' }, + { name: 'update-with-alias', sql: 'UPDATE t AS x SET a = 1', fn: 'SqlStmtList' }, + { name: 'update-with-extend', sql: 'UPDATE t EXTEND (a INTEGER) SET a = 1', fn: 'SqlStmtList' }, + { name: 'delete-with-alias', sql: 'DELETE FROM t AS x', fn: 'SqlStmtList' }, + { name: 'delete-with-extend', sql: 'DELETE FROM t EXTEND (a INTEGER)', fn: 'SqlStmtList' }, + { name: 'merge-with-extend', sql: 'MERGE INTO t EXTEND (a INTEGER) USING u ON t.id = u.id WHEN MATCHED THEN UPDATE SET a = 1', fn: 'SqlStmtList' }, + { name: 'explain-including-all', sql: 'EXPLAIN PLAN INCLUDING ALL ATTRIBUTES FOR SELECT 1', fn: 'SqlStmtList' }, + { name: 'explain-as-xml', sql: 'EXPLAIN PLAN AS XML FOR SELECT 1', fn: 'SqlStmtList' }, + { name: 'describe-table-extra', sql: 'DESCRIBE TABLE t EXTENDED', fn: 'SqlStmtList' }, + { name: 'set-interval', sql: "SET foo = INTERVAL '1' DAY", fn: 'SqlStmtList' }, ]; const negativeCases = [ + // clause order / required parts { name: 'neg-having-without-group', sql: 'SELECT a FROM t HAVING a > 0', fn: 'SqlStmtList' }, { name: 'neg-natural-join-on', sql: 'SELECT * FROM a NATURAL JOIN b ON a.id = b.id', fn: 'SqlStmtList' }, { name: 'neg-join-no-condition', sql: 'SELECT * FROM a JOIN b', fn: 'SqlStmtList' }, @@ -52,6 +158,25 @@ const negativeCases = [ { name: 'neg-fetch-without-order', sql: 'SELECT * FROM t FETCH FIRST 1 ROW ONLY', fn: 'SqlStmtList' }, { name: 'neg-limit-and-fetch', sql: 'SELECT * FROM t ORDER BY a LIMIT 1 FETCH FIRST 1 ROW ONLY', fn: 'SqlStmtList' }, { name: 'neg-where-without-from', sql: 'SELECT a WHERE a > 0', fn: 'SqlStmtList' }, + { name: 'neg-offset-without-order', sql: 'SELECT * FROM t OFFSET 1', fn: 'SqlStmtList' }, + { name: 'neg-setop-nonquery-left', sql: '1 UNION SELECT 1', fn: 'SqlStmtList' }, + // syntax shape errors + { name: 'neg-offset-without-order-2', sql: 'SELECT * FROM t OFFSET 1 ROW', fn: 'SqlStmtList' }, + { name: 'neg-join-using-nonlist', sql: 'SELECT * FROM a JOIN b USING id', fn: 'SqlStmtList' }, + { name: 'neg-having-before-group', sql: 'SELECT a FROM t HAVING a > 0 GROUP BY a', fn: 'SqlStmtList' }, + { name: 'neg-window-before-group', sql: 'SELECT a FROM t WINDOW w AS (PARTITION BY a) GROUP BY a', fn: 'SqlStmtList' }, + { name: 'neg-fetch-without-only', sql: 'SELECT * FROM t ORDER BY a FETCH FIRST 1 ROW', fn: 'SqlStmtList' }, + { name: 'neg-limit-comma', sql: 'SELECT * FROM t LIMIT , 10', fn: 'SqlStmtList' }, + { name: 'neg-explain-missing-for', sql: 'EXPLAIN PLAN SELECT 1', fn: 'SqlStmtList' }, + { name: 'neg-describe-missing-target', sql: 'DESCRIBE', fn: 'SqlStmtList' }, + { name: 'neg-qualify-without-from', sql: 'SELECT a QUALIFY ROW_NUMBER() OVER (ORDER BY a) = 1', fn: 'SqlStmtList' }, + { name: 'neg-order-without-by', sql: 'SELECT * FROM t ORDER a', fn: 'SqlStmtList' }, + { name: 'neg-group-without-by', sql: 'SELECT a FROM t GROUP a', fn: 'SqlStmtList' }, + { name: 'neg-fetch-invalid-mode', sql: 'SELECT * FROM t ORDER BY a FETCH MIDDLE 1 ROW ONLY', fn: 'SqlStmtList' }, + { name: 'neg-update-missing-set', sql: 'UPDATE t WHERE a = 1', fn: 'SqlStmtList' }, + { name: 'neg-delete-missing-from', sql: 'DELETE t', fn: 'SqlStmtList' }, + { name: 'neg-merge-missing-into', sql: 'MERGE t USING u ON t.id = u.id WHEN MATCHED THEN UPDATE SET a = 1', fn: 'SqlStmtList' }, + { name: 'neg-insert-missing-into', sql: 'INSERT t(a) VALUES (1)', fn: 'SqlStmtList' }, ]; if (require.main === module) { diff --git a/spec/apache-calcite-Parser.js b/spec/apache-calcite-Parser.js index 3bc38b2..5ca3942 100644 --- a/spec/apache-calcite-Parser.js +++ b/spec/apache-calcite-Parser.js @@ -35,8 +35,9 @@ class CalciteLexer { if (this.pos < s.length) this.pos += 2; continue; } - // strings (single-quoted, no escape handling) + // strings (single-quoted, with simple escape handling) if (ch === "'") { + const start = this.pos; let value = ""; this.pos++; while (this.pos < s.length) { @@ -56,11 +57,12 @@ class CalciteLexer { value += s[this.pos++]; } if (s[this.pos] === "'") this.pos++; - this.tokens.push({ type: "STRING", value }); + this.tokens.push({ type: "STRING", value, start, end: this.pos }); continue; } // quoted identifiers if (ch === '"' || ch === "`") { + const start = this.pos; const quote = ch; let value = ""; this.pos++; @@ -76,11 +78,12 @@ class CalciteLexer { value += s[this.pos++]; } if (s[this.pos] === quote) this.pos++; - this.tokens.push({ type: "IDENT", value }); + this.tokens.push({ type: "IDENT", value, start, end: this.pos }); continue; } // numbers (including leading dot and exponent) if (/[0-9]/.test(ch) || (ch === "." && /[0-9]/.test(s[this.pos + 1]))) { + const start = this.pos; let value = ""; if (ch === ".") { value += "."; @@ -115,30 +118,31 @@ class CalciteLexer { } } } - this.tokens.push({ type: "NUMBER", value }); + this.tokens.push({ type: "NUMBER", value, start, end: this.pos }); continue; } // identifiers if (/[A-Za-z_]/.test(ch)) { + const start = this.pos; let value = ""; while (this.pos < s.length && /[A-Za-z0-9_]/.test(s[this.pos])) { value += s[this.pos++]; } - this.tokens.push({ type: "IDENT", value }); + this.tokens.push({ type: "IDENT", value, start, end: this.pos }); continue; } // symbols (two-char first) const two = s.slice(this.pos, this.pos + 2); const twoOps = ["<=", ">=", "<>", "!=", "||", "::", "->", ":="]; if (twoOps.includes(two)) { - this.tokens.push({ type: "SYMBOL", value: two }); + this.tokens.push({ type: "SYMBOL", value: two, start: this.pos, end: this.pos + 2 }); this.pos += 2; continue; } - this.tokens.push({ type: "SYMBOL", value: ch }); + this.tokens.push({ type: "SYMBOL", value: ch, start: this.pos, end: this.pos + 1 }); this.pos++; } - this.tokens.push({ type: "EOF", value: null }); + this.tokens.push({ type: "EOF", value: null, start: this.pos, end: this.pos }); return this.tokens; } } @@ -476,6 +480,22 @@ class CalciteParser { if (this.isSymbol("(")) { columns = this.ParenthesizedCompoundIdentifierList(); } + const isSourceStart = + this.isKeyword("WITH") || + this.isKeyword("SELECT") || + this.isKeyword("VALUES") || + this.isKeyword("VALUE") || + this.isKeyword("TABLE") || + (this.isSymbol("(") && ( + this.isKeywordAt("WITH", 1) || + this.isKeywordAt("SELECT", 1) || + this.isKeywordAt("VALUES", 1) || + this.isKeywordAt("VALUE", 1) || + this.isKeywordAt("TABLE", 1) + )); + if (!isSourceStart) { + throw new Error("Invalid INSERT source"); + } const source = this.OrderedQueryOrExpr(); return { type: "SqlInsert", mode, keywords, table, hints, extend, columns, source }; } @@ -636,6 +656,16 @@ class CalciteParser { while (this.isKeyword("UNION") || this.isKeyword("INTERSECT") || this.isKeyword("EXCEPT")) { setOps.push(this.AddSetOpQuery()); } + if (setOps.length > 0) { + const isQueryLeaf = + leaf && + (leaf.type === "SqlSelect" || + leaf.type === "TableConstructor" || + leaf.type === "ExplicitTable"); + if (!isQueryLeaf) { + throw new Error("SETOP requires query operands"); + } + } return { type: "QueryOrExpr", withList, leaf, setOps }; } @@ -659,6 +689,9 @@ class CalciteParser { } else if (this.isKeyword("FETCH")) { fetch = this.FetchClause(); } + if (offset && !orderBy) { + throw new Error("OFFSET requires ORDER BY"); + } if (fetch && !orderBy) { throw new Error("FETCH requires ORDER BY"); } @@ -780,6 +813,9 @@ class CalciteParser { if (having && !groupBy) { throw new Error("HAVING requires GROUP BY"); } + if (qualify && !from) { + throw new Error("QUALIFY requires FROM"); + } return { type: "SqlSelect", hints, @@ -2447,7 +2483,7 @@ class CalciteParser { AddSetOpQuery() { const op = this.BinaryQueryOperator(); - const right = this.LeafQueryOrExpr(); + const right = this.LeafQuery(); return { type: "AddSetOpQuery", op, right }; } From 4a05a0ad9f3675bb3185c1ba77b54d953e8df0e8 Mon Sep 17 00:00:00 2001 From: Toshiki Iga Date: Thu, 29 Jan 2026 00:54:41 +0900 Subject: [PATCH 3/4] Improve parser/formatter coverage, add comment preservation and formatter tests (#8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve parser/formatter coverage, add comment preservation and formatter tests 概要: パーサ/lexer の拡張(Unicode識別子、コメント保持、TableHints、ASOF MATCH_CONDITION など) formatter(index.html と sql-formatter.js)の整形改善とコピーUI追加 テスト拡充(パーサ/フォーマッタ) 変更点: Parser/Lexer: Unicode識別子・Unicodeエスケープの実解釈と妥当性検証 -- コメントの保持(先頭/SELECT行) TableHints (/*+ ... */) をトークン化して保持 TableRef 修飾子反映、ASOF JOIN の MATCH_CONDITION 対応 BRACKET/BigQuery/プレフィックス文字列など識別子/文字列拡張 数値リテラルの種別判定強化 Formatter (index.html): FROM/ORDER BY 改行・インデント改善 サブクエリの描画修正 SELECT コメント保持 出力コピー用アイコンボタン追加 --- README.md | 4 + TODO.md | 14 +- formatter.js | 134 --------- index.html | 75 ++++- spec/apache-calcite-Parser-test.js | 60 +++- spec/apache-calcite-Parser.js | 431 +++++++++++++++++++++++++---- sql-formatter-test.js | 73 +++++ sql-formatter.js | 230 +++++++++++++++ 8 files changed, 810 insertions(+), 211 deletions(-) delete mode 100644 formatter.js create mode 100644 sql-formatter-test.js create mode 100644 sql-formatter.js diff --git a/README.md b/README.md index 4c008be..dece042 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,10 @@ node spec/apache-calcite-Parser-test.js - 構文の厳密化(句の順序/排他の追加チェック) - 最終的に formatter へ接続 +## 仕様情報の流れ(開発過程) +- `spec/apache-calcite-Parser.jj` の文法を元に `spec/apache-calcite-Parser.md`(EBNF仕様)を作成 +- `spec/apache-calcite-Parser.md` を元に `spec/apache-calcite-Parser.js`(実装)を生成 + ## デモ機能 - AST の JSON 出力 - トークン一覧の表示 diff --git a/TODO.md b/TODO.md index 05e2ef7..860d39c 100644 --- a/TODO.md +++ b/TODO.md @@ -4,8 +4,8 @@ - Stage 1: lexer + 全 production skeleton 済み。 - 入口/Select/From/Join/式/関数/型/JSON/日付系の主要骨格は実装済み。 - md⇔js の production 名・順序は一致済み(ヘルパー除外で一致)。 -- 回帰テスト/異常系テストを追加済み(全 46 件通過)。 -- lexer 実用化(コメント/文字列エスケープ/指数/引用識別子)対応済み。 +- 回帰テスト/異常系テストを追加済み(全 187 件通過)。 +- lexer 実用化(コメント/文字列エスケープ/指数/引用識別子/Unicode/BigQuery/ヒント/数値種別/Unicodeエスケープ検証)対応済み。 - 構文厳密化(FROM 必須/HAVING 制約/JOIN 条件/フレーム条件/ORDER+FETCH など)対応済み。 ## 残り作業(優先順の目安・更新) @@ -16,7 +16,15 @@ - 省略可能要素の優先順位を整理(誤って別名に吸われる余地の洗い出し) 3. テスト拡張(必要なら) - SETOP/ORDER/LIMIT の境界・エラー例を追加 -4. 厳格な突合(最終) +4. 仕様(.md)と実装(.js)の差分反映 + - TableRef 修飾子(TableHints/ExtendTable/TableOverOpt/Snapshot)の対応を整理 ✅ + - ASOF JOIN の MATCH_CONDITION を仕様に合わせて反映 ✅ + - lexer の識別子/文字列/数値トークン差分を解消 ✅ + - HYPHENATED/BRACKET/BIG_QUERY 系識別子 ✅ + - PREFIXED/UNICODE/BINARY/UESCAPE/連結文字列 ✅ + - DECIMAL/APPROX 数値リテラル ✅ + - TableHints がコメントとして破棄されないよう lexer と整合 ✅ +5. 厳格な突合(最終) - apache-calcite-Parser.md を正とし、apache-calcite-Parser.js の整合を最終チェック。 - 手順(最小): 1) .md から production 名一覧を抽出(順序付き) diff --git a/formatter.js b/formatter.js deleted file mode 100644 index e4b4b5c..0000000 --- a/formatter.js +++ /dev/null @@ -1,134 +0,0 @@ -'use strict'; - -const { CalciteLexer, CalciteParser } = require('./spec/apache-calcite-Parser'); - -function formatSql(sql) { - const lexer = new CalciteLexer(sql); - const tokens = lexer.tokenize(); - const parser = new CalciteParser(tokens); - const ast = parser.SqlStmtList(); - return renderNode(ast, { indent: 0 }).trim(); -} - -function renderNode(node, ctx) { - if (!node) return ''; - switch (node.type) { - case 'SqlStmtList': - return node.statements.map(stmt => renderNode(stmt, ctx)).join('\n'); - case 'SqlSelect': - return renderSelect(node, ctx); - case 'AddSelectItem': - return renderSelectItem(node, ctx); - case 'SelectExpression': - if (node.star) return '*'; - return renderNode(node.expr, ctx); - case 'TableRef': - return renderTableRef(node, ctx); - case 'FromClause': - return renderFrom(node, ctx); - case 'Where': - return `WHERE ${renderNode(node.expr, ctx)}`; - case 'GroupBy': - return renderGroupBy(node, ctx); - case 'OrderBy': - return renderOrderBy(node, ctx); - case 'OrderItemList': - return node.items.map(it => renderNode(it, ctx)).join('\n'); - case 'AddOrderItem': - return renderOrderItem(node, ctx); - case 'StringLiteral': - return `'${node.value}'`; - case 'NumericLiteral': - return String(node.value); - case 'Literal': - return String(node.value); - case 'Identifier': - return node.name; - case 'CompoundIdentifier': - return node.parts.map(p => (p.type === 'Identifier' ? p.name : '*')).join('.'); - case 'Expression2b': - return renderExpression2b(node, ctx); - case 'BinaryExpression': - return `${renderNode(node.left, ctx)} ${renderBinaryOp(node.operator)} ${renderNode(node.right, ctx)}`; - default: - return `[${node.type}]`; - } -} - -function renderSelect(node, ctx) { - const lines = []; - lines.push('SELECT'); - const items = node.selectItems || []; - items.forEach((item, idx) => { - const prefix = idx === 0 ? indent(ctx, 1) : `${indent(ctx, 1)}, `; - lines.push(prefix + renderNode(item, { ...ctx, indent: ctx.indent + 1 })); - }); - if (node.from) { - lines.push(`${indent(ctx)}FROM ${renderNode(node.from, { ...ctx, indent: ctx.indent + 1 })}`); - } - if (node.where) { - lines.push(`${indent(ctx)}${renderNode(node.where, { ...ctx, indent: ctx.indent + 1 })}`); - } - if (node.groupBy) { - lines.push(`${indent(ctx)}${renderNode(node.groupBy, { ...ctx, indent: ctx.indent + 1 })}`); - } - if (node.having) { - lines.push(`${indent(ctx)}${renderNode(node.having, { ...ctx, indent: ctx.indent + 1 })}`); - } - if (node.orderBy) { - lines.push(`${indent(ctx)}${renderNode(node.orderBy, { ...ctx, indent: ctx.indent + 1 })}`); - } - return lines.join('\n'); -} - -function renderSelectItem(node, ctx) { - const expr = renderNode(node.expr, ctx); - if (node.alias) return `${expr} AS ${node.alias.name || node.alias}`; - return expr; -} - -function renderFrom(node, ctx) { - const base = renderNode(node.first, ctx); - const joins = (node.joins || []).map(j => renderNode(j, ctx)); - return [base, ...joins].join('\n' + indent(ctx)); -} - -function renderTableRef(node, ctx) { - if (node.base) { - return renderNode(node.base, ctx); - } - return '[TableRef]'; -} - -function renderGroupBy(node, ctx) { - const items = renderNode(node.list, ctx); - return `GROUP BY ${items}`; -} - -function renderOrderBy(node, ctx) { - const items = renderNode(node.list, ctx); - return `ORDER BY ${items}`; -} - -function renderOrderItem(node, ctx) { - const expr = renderNode(node.expr, ctx); - const dir = node.direction ? ` ${node.direction}` : ''; - return `${expr}${dir}`; -} - -function renderExpression2b(node, ctx) { - const base = renderNode(node.base, ctx); - return base; -} - -function renderBinaryOp(op) { - if (!op) return ''; - if (typeof op === 'string') return op; - return op.op || ''; -} - -function indent(ctx, extra = 0) { - return ' '.repeat((ctx.indent + extra) * 4); -} - -module.exports = { formatSql }; diff --git a/index.html b/index.html index 495e927..de51066 100644 --- a/index.html +++ b/index.html @@ -52,7 +52,15 @@

SQL Input

Formatted Output

- idle +
+ + idle +
// formatted output will appear here
@@ -70,6 +78,7 @@

Formatted Output

const status = document.getElementById('status'); const parseBtn = document.getElementById('parseBtn'); const formatBtn = document.getElementById('formatBtn'); + const copyBtn = document.getElementById('copyBtn'); const defaultSql = "SELECT a FROM t WHERE b = 1"; input.value = defaultSql; @@ -78,6 +87,30 @@

Formatted Output

output.textContent = text; } + async function copyOutput() { + const text = output.textContent || ''; + if (!text.trim()) { + status.textContent = 'empty'; + return; + } + try { + if (navigator.clipboard && navigator.clipboard.writeText) { + await navigator.clipboard.writeText(text); + } else { + const range = document.createRange(); + range.selectNodeContents(output); + const selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange(range); + document.execCommand('copy'); + selection.removeAllRanges(); + } + status.textContent = 'copied'; + } catch (err) { + status.textContent = 'copy failed'; + } + } + function parseSql() { const sql = input.value.trim(); if (!sql) { @@ -133,6 +166,7 @@

Formatted Output

parseBtn.addEventListener('click', parseSql); formatBtn.addEventListener('click', formatSql); + copyBtn.addEventListener('click', copyOutput); input.addEventListener('keydown', (e) => { if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') parseSql(); }); @@ -270,9 +304,10 @@

Formatted Output

function renderOrderBy(node, ctx) { const items = node.list.items.map(it => renderOrderItem(it, ctx)); if (items.length === 0) return 'ORDER BY'; - const lines = [`ORDER BY ${items[0]}`]; + const lines = ['ORDER BY']; + lines.push(`${indent(ctx, 1)}${items[0]}`); for (let i = 1; i < items.length; i++) { - lines.push(`${indent(ctx)}, ${items[i]}`); + lines.push(`${indent(ctx, 1)}, ${items[i]}`); } return lines.join('\n'); } @@ -325,21 +360,26 @@

Formatted Output

function renderSelect(node, ctx) { const lines = []; - lines.push('SELECT'); + if (node.selectComments && node.selectComments.length > 0) { + lines.push(`SELECT -- ${node.selectComments.join(' ')}`); + } else { + lines.push('SELECT'); + } const items = node.selectItems || []; items.forEach((item, idx) => { const prefix = idx === 0 ? indent(ctx, 1) : `${indent(ctx, 1)}, `; lines.push(prefix + renderSelectItem(item, { ...ctx, indent: ctx.indent + 1 })); }); if (node.from) { - lines.push(`${indent(ctx)}FROM ${renderFrom(node.from, { ...ctx, indent: ctx.indent + 1 })}`); + lines.push(`${indent(ctx)}FROM`); + lines.push(`${indent(ctx, 1)}${renderFrom(node.from, { ...ctx, indent: ctx.indent + 1 })}`); } if (node.where) { lines.push(`${indent(ctx)}WHERE`); lines.push(`${indent(ctx, 1)}${formatNode(node.where.expr, { ...ctx, indent: ctx.indent + 1 })}`); } if (node.groupBy) { - lines.push(`${indent(ctx)}${renderGroupBy(node.groupBy, { ...ctx, indent: ctx.indent + 1 })}`); + lines.push(`${indent(ctx)}${renderGroupBy(node.groupBy, ctx)}`); } if (node.having) { lines.push(`${indent(ctx)}HAVING ${formatNode(node.having.expr, { ...ctx, indent: ctx.indent + 1 })}`); @@ -349,7 +389,7 @@

Formatted Output

lines.push(`${indent(ctx, 1)}${formatNode(node.qualify.expr, { ...ctx, indent: ctx.indent + 1 })}`); } if (node.orderBy) { - lines.push(`${indent(ctx)}${renderOrderBy(node.orderBy, { ...ctx, indent: ctx.indent + 1 })}`); + lines.push(`${indent(ctx)}${renderOrderBy(node.orderBy, ctx)}`); } return lines.join('\n'); } @@ -401,7 +441,14 @@

Formatted Output

if (!node) return ''; switch (node.type) { case 'SqlStmtList': - return node.statements.map(stmt => formatNode(stmt, ctx)).join('\n'); + { + const lines = []; + if (node.leadingComments && node.leadingComments.length) { + node.leadingComments.forEach(c => lines.push(`-- ${c}`)); + } + lines.push(...node.statements.map(stmt => formatNode(stmt, ctx))); + return lines.filter(Boolean).join('\n'); + } case 'OrderedQueryOrExpr': { const withList = node.query.withList ? renderWithList(node.query.withList, ctx) : null; const base = renderQueryOrExpr(node.query, ctx); @@ -426,6 +473,18 @@

Formatted Output

return formatNode(node.expr, ctx); case 'TableRef': return renderTableRef(node, ctx); + case 'Subquery': { + const inner = formatNode(node.query, { ...ctx, indent: 0 }); + const pad = indent(ctx, 1); + const body = inner ? inner.split('\n').map(line => pad + line).join('\n') : pad; + return `(\n${body}\n${indent(ctx)})`; + } + case 'LateralSubquery': { + const inner = formatNode(node.query, { ...ctx, indent: 0 }); + const pad = indent(ctx, 1); + const body = inner ? inner.split('\n').map(line => pad + line).join('\n') : pad; + return `LATERAL (\n${body}\n${indent(ctx)})`; + } case 'TableName': return formatNode(node.name, ctx); case 'CompoundTableIdentifier': diff --git a/spec/apache-calcite-Parser-test.js b/spec/apache-calcite-Parser-test.js index 483a145..590155e 100644 --- a/spec/apache-calcite-Parser-test.js +++ b/spec/apache-calcite-Parser-test.js @@ -2,22 +2,34 @@ const { CalciteLexer, CalciteParser } = require('./apache-calcite-Parser'); +const stmt = (name, sql) => ({ name, sql, fn: 'SqlStmtList' }); +const selectList = (items) => `SELECT ${items.join(', ')}`; +const fromTable = (table) => `FROM ${table}`; +const joinSubquery = (left, right, onExpr) => + `SELECT ${left}.id FROM ${left} JOIN (SELECT id FROM ${right}) AS ${right} ON ${onExpr}`; + const cases = [ // basic { name: 'select-basic', sql: 'SELECT 1', fn: 'SqlStmtList' }, { name: 'select-with-from', sql: 'SELECT a FROM t', fn: 'SqlStmtList' }, { name: 'select-where', sql: 'SELECT a FROM t WHERE b = 1', fn: 'SqlStmtList' }, { name: 'select-group-by', sql: 'SELECT a, COUNT(*) FROM t GROUP BY a', fn: 'SqlStmtList' }, + // group by variants + { name: 'group-by-distinct', sql: 'SELECT a FROM t GROUP BY DISTINCT a', fn: 'SqlStmtList' }, + { name: 'group-by-all', sql: 'SELECT a FROM t GROUP BY ALL a', fn: 'SqlStmtList' }, + { name: 'group-by-grouping-sets', sql: 'SELECT a, b FROM t GROUP BY GROUPING SETS (a, b)', fn: 'SqlStmtList' }, + { name: 'group-by-rollup', sql: 'SELECT a, b FROM t GROUP BY ROLLUP (a, b)', fn: 'SqlStmtList' }, + { name: 'group-by-cube', sql: 'SELECT a, b FROM t GROUP BY CUBE (a, b)', fn: 'SqlStmtList' }, { name: 'select-window', sql: 'SELECT a FROM t WINDOW w AS (PARTITION BY a)', fn: 'SqlStmtList' }, // CTE / DML core - { name: 'cte', sql: 'WITH t AS (SELECT 1) SELECT * FROM t', fn: 'SqlStmtList' }, + { name: 'with-cte', sql: 'WITH t AS (SELECT 1) SELECT * FROM t', fn: 'SqlStmtList' }, { name: 'insert-values', sql: 'INSERT INTO t(a) VALUES (1)', fn: 'SqlStmtList' }, - { name: 'update', sql: 'UPDATE t SET a = 1 WHERE b = 2', fn: 'SqlStmtList' }, - { name: 'delete', sql: 'DELETE FROM t WHERE a IN (1,2,3)', fn: 'SqlStmtList' }, - { name: 'merge', sql: 'MERGE INTO t USING u ON t.id = u.id WHEN MATCHED THEN UPDATE SET a = 1', fn: 'SqlStmtList' }, + { name: 'update-basic', sql: 'UPDATE t SET a = 1 WHERE b = 2', fn: 'SqlStmtList' }, + { name: 'delete-basic', sql: 'DELETE FROM t WHERE a IN (1,2,3)', fn: 'SqlStmtList' }, + { name: 'merge-basic', sql: 'MERGE INTO t USING u ON t.id = u.id WHEN MATCHED THEN UPDATE SET a = 1', fn: 'SqlStmtList' }, // functions / expressions - { name: 'json-value', sql: "SELECT JSON_VALUE(doc, '$.a' RETURNING VARCHAR) FROM t", fn: 'SqlStmtList' }, - { name: 'date-diff', sql: 'SELECT DATE_DIFF(d1, d2, DAY) FROM t', fn: 'SqlStmtList' }, + { name: 'select-json-value', sql: "SELECT JSON_VALUE(doc, '$.a' RETURNING VARCHAR) FROM t", fn: 'SqlStmtList' }, + { name: 'select-date-diff', sql: 'SELECT DATE_DIFF(d1, d2, DAY) FROM t', fn: 'SqlStmtList' }, // MATCH_RECOGNIZE / PIVOT / UNPIVOT { name: 'match-recognize', sql: 'SELECT * FROM t MATCH_RECOGNIZE (PATTERN (A) DEFINE A AS a > 0)', fn: 'SqlStmtList' }, { name: 'match-recognize-partition-order', sql: 'SELECT * FROM t MATCH_RECOGNIZE (PARTITION BY a ORDER BY b MEASURES CLASSIFIER() AS c PATTERN (A B) DEFINE A AS a > 0, B AS b > 0)', fn: 'SqlStmtList' }, @@ -35,9 +47,21 @@ const cases = [ { name: 'lexer-block-comment', sql: 'SELECT /* block */ 1', fn: 'SqlStmtList' }, { name: 'lexer-quoted-ident-double', sql: 'SELECT "Select" FROM "From"', fn: 'SqlStmtList' }, { name: 'lexer-quoted-ident-backtick', sql: 'SELECT `a` FROM `b`', fn: 'SqlStmtList' }, + { name: 'lexer-quoted-ident-bracket', sql: 'SELECT [Select] FROM [From]', fn: 'SqlStmtList' }, + { name: 'lexer-quoted-ident-bracket-wide', sql: 'SELECT [a b,c] FROM t', fn: 'SqlStmtList' }, + { name: 'lexer-hyphenated-ident', sql: 'SELECT * FROM a-b', fn: 'SqlStmtList' }, + { name: 'lexer-unicode-ident', sql: 'SELECT ユーザーID, レシピID FROM recipes', fn: 'SqlStmtList' }, + { name: 'lexer-bigquery-double-quoted-string', sql: 'SELECT "a\\\"b" FROM t', fn: 'SqlStmtList' }, + { name: 'lexer-unicode-quoted-ident-uescape', sql: 'SELECT * FROM U&"A\\\\0042" UESCAPE \'\\\'', fn: 'SqlStmtList' }, { name: 'lexer-string-escape', sql: "SELECT 'a''b' FROM t", fn: 'SqlStmtList' }, + { name: 'lexer-string-prefixed-n', sql: "SELECT N'abc' FROM t", fn: 'SqlStmtList' }, + { name: 'lexer-string-prefixed-e', sql: "SELECT E'\\n' FROM t", fn: 'SqlStmtList' }, + { name: 'lexer-string-prefixed-x', sql: "SELECT X'0A' FROM t", fn: 'SqlStmtList' }, + { name: 'lexer-string-unicode', sql: "SELECT U&'d\\\\0061' UESCAPE '\\' FROM t", fn: 'SqlStmtList' }, { name: 'lexer-number-exponent', sql: 'SELECT 1.2e-3 FROM t', fn: 'SqlStmtList' }, { name: 'lexer-number-leading-dot', sql: 'SELECT .5 FROM t', fn: 'SqlStmtList' }, + { name: 'lexer-number-approx', sql: 'SELECT 1E+10 FROM t', fn: 'SqlStmtList' }, + { name: 'lexer-number-decimal-dot', sql: 'SELECT 1. FROM t', fn: 'SqlStmtList' }, // DDL / DCL { name: 'ddl-set', sql: 'SET foo = 1', fn: 'SqlStmtList' }, { name: 'ddl-reset', sql: 'RESET foo', fn: 'SqlStmtList' }, @@ -56,14 +80,34 @@ const cases = [ { name: 'select-join-natural', sql: 'SELECT * FROM a NATURAL JOIN b', fn: 'SqlStmtList' }, { name: 'select-join-cross', sql: 'SELECT * FROM a CROSS JOIN b', fn: 'SqlStmtList' }, { name: 'select-join-comma', sql: 'SELECT * FROM a, b', fn: 'SqlStmtList' }, + { name: 'select-join-asof-match-condition', sql: 'SELECT * FROM a ASOF JOIN b MATCH_CONDITION a.ts <= b.ts ON a.id = b.id', fn: 'SqlStmtList' }, + // table ref variants + { name: 'from-lateral-subquery', sql: 'SELECT * FROM LATERAL (SELECT 1) AS x', fn: 'SqlStmtList' }, + { name: 'from-unnest', sql: 'SELECT * FROM UNNEST(arr)', fn: 'SqlStmtList' }, + { name: 'from-unnest-ordinality', sql: 'SELECT * FROM UNNEST(arr) WITH ORDINALITY', fn: 'SqlStmtList' }, + { name: 'from-table-function', sql: 'SELECT * FROM TABLE(foo(1))', fn: 'SqlStmtList' }, + { name: 'from-tablesample', sql: 'SELECT * FROM t TABLESAMPLE SYSTEM (10) REPEATABLE (1)', fn: 'SqlStmtList' }, + { name: 'from-snapshot', sql: "SELECT * FROM t FOR SYSTEM_TIME AS OF TIMESTAMP '2020-01-01 00:00:00'", fn: 'SqlStmtList' }, + stmt('join-subquery', joinSubquery('a', 'b', 'a.id = b.id')), { name: 'select-setop-union', sql: 'SELECT a FROM t UNION SELECT a FROM u', fn: 'SqlStmtList' }, { name: 'select-setop-intersect', sql: 'SELECT a FROM t INTERSECT SELECT a FROM u', fn: 'SqlStmtList' }, { name: 'select-setop-except', sql: 'SELECT a FROM t EXCEPT SELECT a FROM u', fn: 'SqlStmtList' }, { name: 'select-order-limit', sql: 'SELECT a FROM t ORDER BY a LIMIT 10', fn: 'SqlStmtList' }, { name: 'select-offset-limit', sql: 'SELECT a FROM t ORDER BY a OFFSET 5 LIMIT 10', fn: 'SqlStmtList' }, { name: 'select-fetch', sql: 'SELECT a FROM t ORDER BY a FETCH FIRST 3 ROWS ONLY', fn: 'SqlStmtList' }, + { name: 'order-by-nulls-first', sql: 'SELECT a FROM t ORDER BY a NULLS FIRST', fn: 'SqlStmtList' }, + { name: 'order-by-nulls-last', sql: 'SELECT a FROM t ORDER BY a DESC NULLS LAST', fn: 'SqlStmtList' }, + { name: 'select-limit-all', sql: 'SELECT a FROM t LIMIT ALL', fn: 'SqlStmtList' }, + { name: 'select-limit-offset-comma', sql: 'SELECT a FROM t LIMIT 3, 10', fn: 'SqlStmtList' }, + { name: 'select-offset-rows', sql: 'SELECT a FROM t ORDER BY a OFFSET 5 ROWS', fn: 'SqlStmtList' }, + { name: 'select-fetch-next', sql: 'SELECT a FROM t ORDER BY a FETCH NEXT 3 ROWS ONLY', fn: 'SqlStmtList' }, { name: 'select-values', sql: 'VALUES (1), (2)', fn: 'SqlStmtList' }, { name: 'select-table', sql: 'TABLE t', fn: 'SqlStmtList' }, + // japanese identifiers + stmt( + 'select-japanese-idents', + `${selectList(['顧客.会員ID', 'COUNT(注文ID) AS 注文数'])} ${fromTable('顧客')} GROUP BY 会員ID` + ), // expression / function variants { name: 'select-case', sql: 'SELECT CASE WHEN a > 0 THEN 1 ELSE 0 END FROM t', fn: 'SqlStmtList' }, { name: 'select-cast', sql: 'SELECT CAST(a AS INTEGER) FROM t', fn: 'SqlStmtList' }, @@ -138,6 +182,7 @@ const cases = [ // DML/DDL extras { name: 'insert-upsert', sql: 'UPSERT INTO t(a) VALUES (1)', fn: 'SqlStmtList' }, { name: 'insert-hints', sql: 'INSERT INTO t /*+ hint */ (a) VALUES (1)', fn: 'SqlStmtList' }, + { name: 'select-hints-multi', sql: 'SELECT /*+ hint1, hint2 */ * FROM t', fn: 'SqlStmtList' }, { name: 'update-with-alias', sql: 'UPDATE t AS x SET a = 1', fn: 'SqlStmtList' }, { name: 'update-with-extend', sql: 'UPDATE t EXTEND (a INTEGER) SET a = 1', fn: 'SqlStmtList' }, { name: 'delete-with-alias', sql: 'DELETE FROM t AS x', fn: 'SqlStmtList' }, @@ -154,6 +199,7 @@ const negativeCases = [ { name: 'neg-having-without-group', sql: 'SELECT a FROM t HAVING a > 0', fn: 'SqlStmtList' }, { name: 'neg-natural-join-on', sql: 'SELECT * FROM a NATURAL JOIN b ON a.id = b.id', fn: 'SqlStmtList' }, { name: 'neg-join-no-condition', sql: 'SELECT * FROM a JOIN b', fn: 'SqlStmtList' }, + { name: 'neg-join-match-condition-nonasof', sql: 'SELECT * FROM a JOIN b MATCH_CONDITION a.ts <= b.ts ON a.id = b.id', fn: 'SqlStmtList' }, { name: 'neg-window-frame-without-order', sql: 'SELECT a FROM t WINDOW w AS (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)', fn: 'SqlStmtList' }, { name: 'neg-fetch-without-order', sql: 'SELECT * FROM t FETCH FIRST 1 ROW ONLY', fn: 'SqlStmtList' }, { name: 'neg-limit-and-fetch', sql: 'SELECT * FROM t ORDER BY a LIMIT 1 FETCH FIRST 1 ROW ONLY', fn: 'SqlStmtList' }, @@ -177,6 +223,8 @@ const negativeCases = [ { name: 'neg-delete-missing-from', sql: 'DELETE t', fn: 'SqlStmtList' }, { name: 'neg-merge-missing-into', sql: 'MERGE t USING u ON t.id = u.id WHEN MATCHED THEN UPDATE SET a = 1', fn: 'SqlStmtList' }, { name: 'neg-insert-missing-into', sql: 'INSERT t(a) VALUES (1)', fn: 'SqlStmtList' }, + { name: 'neg-unicode-escape-surrogate', sql: "SELECT U&'\\D800' FROM t", fn: 'SqlStmtList' }, + { name: 'neg-unicode-escape-out-of-range', sql: "SELECT U&'\\+110000' FROM t", fn: 'SqlStmtList' }, ]; if (require.main === module) { diff --git a/spec/apache-calcite-Parser.js b/spec/apache-calcite-Parser.js index 5ca3942..3c5ff56 100644 --- a/spec/apache-calcite-Parser.js +++ b/spec/apache-calcite-Parser.js @@ -14,6 +14,152 @@ class CalciteLexer { tokenize() { const s = this.input; + const len = s.length; + const skipWhitespace = (p) => { + while (p < len && /\s/.test(s[p])) p++; + return p; + }; + const isIdentStart = (ch) => { + return ch === "_" || ch === "$" || /\p{L}/u.test(ch); + }; + const isIdentPart = (ch) => { + return ch === "_" || ch === "$" || /\p{L}|\p{Nd}/u.test(ch); + }; + const startsWithKeywordAt = (p, keyword) => { + const slice = s.slice(p, p + keyword.length); + if (slice.toUpperCase() !== keyword) return false; + const next = s[p + keyword.length]; + return !(next && /[A-Za-z0-9_]/.test(next)); + }; + const readQuotedString = (allowBackslashEscape = true) => { + const start = this.pos; + let value = ""; + this.pos++; + while (this.pos < len) { + if (s[this.pos] === "'") { + if (s[this.pos + 1] === "'") { + value += "'"; + this.pos += 2; + continue; + } + break; + } + if (allowBackslashEscape && s[this.pos] === "\\" && this.pos + 1 < len) { + value += s[this.pos + 1]; + this.pos += 2; + continue; + } + value += s[this.pos++]; + } + if (s[this.pos] === "'") this.pos++; + return { value, start, end: this.pos }; + }; + const readQuotedIdentifier = (quote) => { + const start = this.pos; + let value = ""; + this.pos++; + while (this.pos < len) { + if (s[this.pos] === quote) { + if (s[this.pos + 1] === quote) { + value += quote; + this.pos += 2; + continue; + } + break; + } + value += s[this.pos++]; + } + if (s[this.pos] === quote) this.pos++; + return { value, start, end: this.pos }; + }; + const readDoubleQuotedMaybeString = () => { + const start = this.pos; + let value = ""; + let isString = false; + this.pos++; + while (this.pos < len) { + if (s[this.pos] === "\\") { + if (this.pos + 1 < len) { + value += s[this.pos + 1]; + this.pos += 2; + isString = true; + continue; + } + } + if (s[this.pos] === "\"") { + if (s[this.pos + 1] === "\"") { + value += "\""; + this.pos += 2; + continue; + } + break; + } + value += s[this.pos++]; + } + if (s[this.pos] === "\"") this.pos++; + return { value, start, end: this.pos, isString }; + }; + const tryReadBracketIdentifier = () => { + let p = this.pos + 1; + let value = ""; + while (p < len) { + if (s[p] === "]") { + if (s[p + 1] === "]") { + value += "]"; + p += 2; + continue; + } + break; + } + if (s[p] === "\n" || s[p] === "\r") return null; + value += s[p++]; + } + if (s[p] !== "]") return null; + if (!value || !/^[A-Za-z_]/.test(value)) return null; + const start = this.pos; + this.pos = p + 1; + return { value, start, end: this.pos }; + }; + const decodeUnicodeEscapes = (raw, escapeChar) => { + let out = ""; + for (let i = 0; i < raw.length; i++) { + const ch = raw[i]; + if (ch !== escapeChar) { + out += ch; + continue; + } + const next = raw[i + 1]; + if (next === escapeChar) { + out += escapeChar; + i++; + continue; + } + if (next === "+") { + const hex = raw.slice(i + 2, i + 8); + if (!/^[0-9A-Fa-f]{6}$/.test(hex)) { + throw new Error("Invalid Unicode escape sequence"); + } + const code = parseInt(hex, 16); + if (code > 0x10FFFF || (code >= 0xD800 && code <= 0xDFFF)) { + throw new Error("Invalid Unicode code point"); + } + out += String.fromCodePoint(code); + i += 7; + continue; + } + const hex = raw.slice(i + 1, i + 5); + if (!/^[0-9A-Fa-f]{4}$/.test(hex)) { + throw new Error("Invalid Unicode escape sequence"); + } + const code = parseInt(hex, 16); + if (code > 0x10FFFF || (code >= 0xD800 && code <= 0xDFFF)) { + throw new Error("Invalid Unicode code point"); + } + out += String.fromCodePoint(code); + i += 4; + } + return out; + }; while (this.pos < s.length) { const ch = s[this.pos]; if (/\s/.test(ch)) { @@ -22,12 +168,17 @@ class CalciteLexer { } // line comment if (ch === "-" && s[this.pos + 1] === "-") { + const start = this.pos; this.pos += 2; - while (this.pos < s.length && s[this.pos] !== "\n") this.pos++; + let value = ""; + while (this.pos < s.length && s[this.pos] !== "\n") { + value += s[this.pos++]; + } + this.tokens.push({ type: "COMMENT_LINE", value: value.trim(), start, end: this.pos }); continue; } // block comment - if (ch === "/" && s[this.pos + 1] === "*") { + if (ch === "/" && s[this.pos + 1] === "*" && s[this.pos + 2] !== "+") { this.pos += 2; while (this.pos < s.length && !(s[this.pos] === "*" && s[this.pos + 1] === "/")) { this.pos++; @@ -35,52 +186,113 @@ class CalciteLexer { if (this.pos < s.length) this.pos += 2; continue; } - // strings (single-quoted, with simple escape handling) - if (ch === "'") { + // table hints (/*+ ... */) + if (ch === "/" && s[this.pos + 1] === "*" && s[this.pos + 2] === "+") { const start = this.pos; + this.pos += 3; let value = ""; - this.pos++; - while (this.pos < s.length) { - if (s[this.pos] === "'") { - if (s[this.pos + 1] === "'") { - value += "'"; - this.pos += 2; - continue; + while (this.pos < s.length && !(s[this.pos] === "*" && s[this.pos + 1] === "/")) { + value += s[this.pos++]; + } + if (this.pos < s.length) this.pos += 2; + this.tokens.push({ type: "HINT", value: value.trim(), start, end: this.pos }); + continue; + } + // strings (single-quoted, with prefix/unicode/binary support) + if (ch === "'" || ((ch === "N" || ch === "n" || ch === "E" || ch === "e" || ch === "X" || ch === "x") && s[this.pos + 1] === "'") || + ((ch === "U" || ch === "u") && s[this.pos + 1] === "&" && s[this.pos + 2] === "'") || + (ch === "_" && /[A-Za-z0-9:._-]/.test(s[this.pos + 1]))) { + let start = this.pos; + let unicodeString = false; + let escapeChar = "\\"; + if (ch === "N" || ch === "n" || ch === "E" || ch === "e" || ch === "X" || ch === "x") { + this.pos++; + } else if ((ch === "U" || ch === "u") && s[this.pos + 1] === "&") { + this.pos += 2; + unicodeString = true; + } else if (ch === "_") { + this.pos++; + while (this.pos < len && /[A-Za-z0-9:._-]/.test(s[this.pos])) { + this.pos++; + } + if (s[this.pos] !== "'") { + this.pos = start; + } + } + if (s[this.pos] === "'") { + let literal = readQuotedString(!unicodeString); + let value = literal.value; + let p = skipWhitespace(this.pos); + while (s[p] === "'") { + this.pos = p; + const extra = readQuotedString(!unicodeString); + value += extra.value; + p = skipWhitespace(this.pos); + } + if (startsWithKeywordAt(p, "UESCAPE")) { + let q = skipWhitespace(p + "UESCAPE".length); + if (s[q] === "'") { + this.pos = q; + const esc = readQuotedString(false); + if (esc.value.length !== 1) { + throw new Error("UESCAPE must be a single character"); + } + escapeChar = esc.value; + p = skipWhitespace(this.pos); } - break; } - if (s[this.pos] === "\\" && this.pos + 1 < s.length) { - value += s[this.pos + 1]; - this.pos += 2; - continue; + this.pos = p; + if (unicodeString) { + value = decodeUnicodeEscapes(value, escapeChar); } - value += s[this.pos++]; + this.tokens.push({ type: "STRING", value, start, end: this.pos }); + continue; + } else { + this.pos = start; } - if (s[this.pos] === "'") this.pos++; - this.tokens.push({ type: "STRING", value, start, end: this.pos }); - continue; } // quoted identifiers - if (ch === '"' || ch === "`") { + if (ch === "[") { + const ident = tryReadBracketIdentifier(); + if (ident) { + this.tokens.push({ type: "IDENT", value: ident.value, start: ident.start, end: ident.end }); + continue; + } + } + if ((ch === "U" || ch === "u") && s[this.pos + 1] === "&" && s[this.pos + 2] === "\"") { const start = this.pos; - const quote = ch; - let value = ""; - this.pos++; - while (this.pos < s.length) { - if (s[this.pos] === quote) { - if (s[this.pos + 1] === quote) { - value += quote; - this.pos += 2; - continue; - } - break; + this.pos += 2; + let { value, end } = readQuotedIdentifier("\""); + let p = skipWhitespace(this.pos); + let escapeChar = "\\"; + if (startsWithKeywordAt(p, "UESCAPE")) { + let q = skipWhitespace(p + "UESCAPE".length); + if (s[q] !== "'") { + throw new Error("UESCAPE requires a quoted escape character"); } - value += s[this.pos++]; + this.pos = q; + const esc = readQuotedString(false); + if (esc.value.length !== 1) { + throw new Error("UESCAPE must be a single character"); + } + escapeChar = esc.value; + p = skipWhitespace(this.pos); } - if (s[this.pos] === quote) this.pos++; + this.pos = p; + value = decodeUnicodeEscapes(value, escapeChar); this.tokens.push({ type: "IDENT", value, start, end: this.pos }); continue; } + if (ch === '"' || ch === "`") { + if (ch === "\"") { + const { value, start, end, isString } = readDoubleQuotedMaybeString(); + this.tokens.push({ type: isString ? "STRING" : "IDENT", value, start, end }); + } else { + const { value, start, end } = readQuotedIdentifier(ch); + this.tokens.push({ type: "IDENT", value, start, end }); + } + continue; + } // numbers (including leading dot and exponent) if (/[0-9]/.test(ch) || (ch === "." && /[0-9]/.test(s[this.pos + 1]))) { const start = this.pos; @@ -122,11 +334,22 @@ class CalciteLexer { continue; } // identifiers - if (/[A-Za-z_]/.test(ch)) { + if (isIdentStart(ch)) { const start = this.pos; let value = ""; - while (this.pos < s.length && /[A-Za-z0-9_]/.test(s[this.pos])) { - value += s[this.pos++]; + while (this.pos < s.length) { + const c = s[this.pos]; + if (isIdentPart(c)) { + value += c; + this.pos++; + continue; + } + if (c === "-" && isIdentStart(s[this.pos + 1])) { + value += c; + this.pos++; + continue; + } + break; } this.tokens.push({ type: "IDENT", value, start, end: this.pos }); continue; @@ -152,9 +375,32 @@ class CalciteParser { this.tokens = tokens || []; this.pos = 0; } - peek() { return this.tokens[this.pos] || { type: "EOF", value: null }; } - peekN(n) { return this.tokens[this.pos + n] || { type: "EOF", value: null }; } - next() { return this.tokens[this.pos++] || { type: "EOF", value: null }; } + isCommentToken(t) { return t && t.type === "COMMENT_LINE"; } + peekRaw() { return this.tokens[this.pos] || { type: "EOF", value: null }; } + peek() { + let i = this.pos; + while (this.isCommentToken(this.tokens[i])) i++; + return this.tokens[i] || { type: "EOF", value: null }; + } + peekN(n) { + let i = this.pos; + let count = 0; + while (i < this.tokens.length) { + const t = this.tokens[i]; + if (!this.isCommentToken(t)) { + if (count === n) return t; + count++; + } + i++; + } + return { type: "EOF", value: null }; + } + nextRaw() { return this.tokens[this.pos++] || { type: "EOF", value: null }; } + next() { + let t = this.nextRaw(); + while (this.isCommentToken(t)) t = this.nextRaw(); + return t || { type: "EOF", value: null }; + } isEOF() { return this.peek().type === "EOF"; } isSymbol(value) { const t = this.peek(); @@ -264,12 +510,12 @@ class CalciteParser { "UNION", "INTERSECT", "EXCEPT", "JOIN", "INNER", "LEFT", "RIGHT", "FULL", "CROSS", "ASOF", "NATURAL", "SET", "USING", "ON", "WHEN", "FOR", - "MATCH_RECOGNIZE", + "MATCH_RECOGNIZE", "MATCH_CONDITION", "PIVOT", "UNPIVOT", "TABLESAMPLE", ]); return keywords.has(value); } isTableHintsStart() { - return this.isSymbol("/") && this.isSymbolAt("*", 1) && this.isSymbolAt("+", 2); + return this.peek().type === "HINT"; } expect(type) { const t = this.peek(); @@ -278,6 +524,39 @@ class CalciteParser { } return this.next(); } + collectLineComments() { + const comments = []; + while (this.isCommentToken(this.peekRaw())) { + const t = this.nextRaw(); + comments.push(t.value || ""); + } + return comments; + } + ExpressionUntilKeyword(keyword) { + let depth = 0; + let idx = -1; + for (let i = this.pos; i < this.tokens.length; i++) { + const t = this.tokens[i]; + if (t.type === "SYMBOL") { + if (t.value === "(" || t.value === "[" || t.value === "{") depth++; + else if (t.value === ")" || t.value === "]" || t.value === "}") depth = Math.max(0, depth - 1); + } + if (depth === 0 && t.type === "IDENT" && String(t.value).toUpperCase() === keyword) { + idx = i; + break; + } + } + if (idx === -1) { + throw new Error(`Expected keyword ${keyword} after expression`); + } + const subTokens = this.tokens.slice(this.pos, idx); + subTokens.push({ type: "EOF", value: null }); + const subParser = new CalciteParser(subTokens); + const expr = subParser.Expression(); + subParser.expect("EOF"); + this.pos = idx; + return expr; + } notImplemented(rule) { const t = this.peek(); throw new Error(`Not implemented: ${rule} at token ${t.type}`); @@ -285,6 +564,7 @@ class CalciteParser { SqlStmtList() { const statements = []; + const leadingComments = this.collectLineComments(); if (!this.isEOF()) { statements.push(this.SqlStmt()); while (this.acceptSymbol(";")) { @@ -293,7 +573,7 @@ class CalciteParser { } } this.expect("EOF"); - return { type: "SqlStmtList", statements }; + return { type: "SqlStmtList", leadingComments, statements }; } SqlStmtEof() { @@ -777,6 +1057,7 @@ class CalciteParser { SqlSelect() { this.expectKeyword("SELECT"); + const selectComments = this.collectLineComments(); let hints = null; if (this.isTableHintsStart()) { hints = this.TableHints(); @@ -818,6 +1099,7 @@ class CalciteParser { } return { type: "SqlSelect", + selectComments, hints, stream, setQuantifier, @@ -1052,10 +1334,17 @@ class CalciteParser { const joinType = this.JoinType(); const table = this.TableRef(); let condition = null; - if (this.acceptKeyword("ON")) { + let matchCondition = null; + if (joinType === "ASOF JOIN" && this.acceptKeyword("MATCH_CONDITION")) { + matchCondition = this.ExpressionUntilKeyword("ON"); + this.expectKeyword("ON"); condition = { type: "On", expr: this.Expression() }; - } else if (this.acceptKeyword("USING")) { - condition = { type: "Using", columns: this.ParenthesizedSimpleIdentifierList() }; + } else { + if (this.acceptKeyword("ON")) { + condition = { type: "On", expr: this.Expression() }; + } else if (this.acceptKeyword("USING")) { + condition = { type: "Using", columns: this.ParenthesizedSimpleIdentifierList() }; + } } if (!natural && joinType !== "CROSS JOIN" && !condition) { throw new Error("JOIN requires ON or USING"); @@ -1063,7 +1352,10 @@ class CalciteParser { if (natural && condition) { throw new Error("NATURAL JOIN cannot use ON or USING"); } - return { type: "JoinTable", natural, joinType, table, condition }; + if (matchCondition && joinType !== "ASOF JOIN") { + throw new Error("MATCH_CONDITION is only valid for ASOF JOIN"); + } + return { type: "JoinTable", natural, joinType, table, matchCondition, condition }; } TableRef() { @@ -1080,11 +1372,18 @@ class CalciteParser { TableRef3() { let base; + let hints = null; + let extend = null; + let over = null; + let snapshot = null; + let matchRecognize = null; if (this.acceptKeyword("LATERAL")) { if (this.isSymbol("(")) { this.expectSymbol("("); const query = this.OrderedQueryOrExpr(); this.expectSymbol(")"); + over = this.TableOverOpt(); + if (this.isKeyword("MATCH_RECOGNIZE")) matchRecognize = this.MatchRecognize(); base = { type: "LateralSubquery", query }; } else if (this.acceptKeyword("UNNEST")) { this.expectSymbol("("); @@ -1099,6 +1398,8 @@ class CalciteParser { this.expectSymbol("("); const query = this.OrderedQueryOrExpr(); this.expectSymbol(")"); + over = this.TableOverOpt(); + if (this.isKeyword("MATCH_RECOGNIZE")) matchRecognize = this.MatchRecognize(); base = { type: "Subquery", query }; } else if (this.isKeyword("UNNEST")) { this.expectKeyword("UNNEST"); @@ -1123,6 +1424,17 @@ class CalciteParser { this.expectSymbol(")"); base = { type: "ImplicitTableFunctionCall", name, args }; } else { + if (this.isTableHintsStart()) { + hints = this.TableHints(); + } + if (this.isKeyword("EXTEND")) { + extend = this.ExtendTable(); + } + over = this.TableOverOpt(); + if (this.isKeyword("FOR")) { + snapshot = this.Snapshot(); + } + if (this.isKeyword("MATCH_RECOGNIZE")) matchRecognize = this.MatchRecognize(); base = { type: "TableName", name }; } } else { @@ -1133,8 +1445,6 @@ class CalciteParser { if (this.isKeyword("PIVOT")) pivot = this.Pivot(); let unpivot = null; if (this.isKeyword("UNPIVOT")) unpivot = this.Unpivot(); - let matchRecognize = null; - if (this.isKeyword("MATCH_RECOGNIZE")) matchRecognize = this.MatchRecognize(); // alias let alias = null; let columns = null; @@ -1156,7 +1466,7 @@ class CalciteParser { if (this.isKeyword("TABLESAMPLE")) { tablesample = this.Tablesample(); } - return { type: "TableRef", base, pivot, unpivot, matchRecognize, alias, columns, tablesample }; + return { type: "TableRef", base, hints, extend, over, snapshot, matchRecognize, pivot, unpivot, alias, columns, tablesample }; } Snapshot() { @@ -2363,11 +2673,15 @@ class CalciteParser { const t = this.peek(); if (t.type === "NUMBER") { this.next(); - return { type: "UnsignedNumericLiteral", value: t.value }; + const raw = String(t.value); + let kind = "INTEGER"; + if (/[eE]/.test(raw)) kind = "APPROX"; + else if (raw.includes(".")) kind = "DECIMAL"; + return { type: "UnsignedNumericLiteral", kind, value: t.value }; } if (this.acceptKeyword("DECIMAL")) { const literal = this.SimpleStringLiteral(); - return { type: "UnsignedNumericLiteral", value: { type: "DECIMAL", literal } }; + return { type: "UnsignedNumericLiteral", kind: "DECIMAL_STRING", value: { type: "DECIMAL", literal } }; } throw new Error("Invalid UnsignedNumericLiteral"); } @@ -2680,15 +2994,12 @@ class CalciteParser { } TableHints() { - this.expectSymbol("/"); - this.expectSymbol("*"); - this.expectSymbol("+"); - const hints = [this.AddHint()]; - while (this.acceptSymbol(",")) { - hints.push(this.AddHint()); + const t = this.peek(); + if (t.type !== "HINT") { + throw new Error(`Expected HINT but got ${t.type}:${t.value}`); } - this.expectSymbol("*"); - this.expectSymbol("/"); + this.next(); + const hints = t.value ? t.value.split(",").map((s) => s.trim()).filter(Boolean) : []; return { type: "TableHints", hints }; } diff --git a/sql-formatter-test.js b/sql-formatter-test.js new file mode 100644 index 0000000..f0c86cc --- /dev/null +++ b/sql-formatter-test.js @@ -0,0 +1,73 @@ +'use strict'; + +const { formatSql } = require('./sql-formatter'); + +const cases = [ + { + name: 'basic-select', + sql: 'SELECT a FROM t', + expect: `SELECT + a +FROM + t`, + }, + { + name: 'select-where', + sql: 'SELECT a FROM t WHERE b = 1', + expect: `SELECT + a +FROM + t +WHERE b = 1`, + }, + { + name: 'select-line-comment', + sql: "SELECT -- keep comment\n a\nFROM t", + expectIncludes: ['SELECT -- keep comment'], + }, + { + name: 'nested-subquery', + sql: 'SELECT u.name FROM (SELECT id, name FROM users WHERE active = 1) AS u', + expect: `SELECT + u.name +FROM + ( + SELECT + id + , name + FROM + users + WHERE active = 1 + ) u`, + }, +]; + +let failed = 0; +for (const c of cases) { + try { + const out = formatSql(c.sql); + let ok = true; + if (c.expect !== undefined) { + ok = out.trim() === c.expect.trim(); + } else if (c.expectIncludes) { + ok = c.expectIncludes.every((frag) => out.includes(frag)); + } + if (!ok) { + failed++; + console.error(`NG ${c.name}: output missing expected fragments`); + console.error(out); + } else { + console.log(`OK ${c.name}`); + } + } catch (err) { + failed++; + console.error(`NG ${c.name}: ${err.message}`); + } +} + +if (failed > 0) { + console.error(`\nFAILED: ${failed}`); + process.exit(1); +} + +console.log(`\nALL OK: ${cases.length}`); diff --git a/sql-formatter.js b/sql-formatter.js new file mode 100644 index 0000000..f27ca93 --- /dev/null +++ b/sql-formatter.js @@ -0,0 +1,230 @@ +'use strict'; + +const { CalciteLexer, CalciteParser } = require('./spec/apache-calcite-Parser'); + +function formatSql(sql) { + const lexer = new CalciteLexer(sql); + const tokens = lexer.tokenize(); + const parser = new CalciteParser(tokens); + const ast = parser.SqlStmtList(); + return renderNode(ast, { indent: 0 }).trim(); +} + +function renderNode(node, ctx) { + if (!node) return ''; + switch (node.type) { + case 'SqlStmtList': + { + const lines = []; + if (node.leadingComments && node.leadingComments.length) { + node.leadingComments.forEach(c => lines.push(`-- ${c}`)); + } + lines.push(...node.statements.map(stmt => renderNode(stmt, ctx))); + return lines.filter(Boolean).join('\n'); + } + case 'OrderedQueryOrExpr': { + const withList = node.query && node.query.withList ? renderNode(node.query.withList, ctx) : null; + const base = node.query ? renderNode(node.query, ctx) : ''; + const lines = []; + if (withList) lines.push(withList); + if (base) lines.push(base); + if (node.orderByLimitOpt) lines.push(renderOrderByLimitOpt(node.orderByLimitOpt, ctx)); + return lines.filter(Boolean).join('\n'); + } + case 'QueryOrExpr': + return renderNode(node.leaf, ctx); + case 'SqlSelect': + return renderSelect(node, ctx); + case 'AddSelectItem': + return renderSelectItem(node, ctx); + case 'SelectExpression': + if (node.star) return '*'; + return renderNode(node.expr, ctx); + case 'TableRef': + return renderTableRef(node, ctx); + case 'Subquery': { + const inner = renderNode(node.query, { ...ctx, indent: 0 }); + const pad = indent(ctx, 1); + const body = inner ? inner.split('\n').map(line => pad + line).join('\n') : pad; + return `(\n${body}\n${indent(ctx)})`; + } + case 'LateralSubquery': { + const inner = renderNode(node.query, { ...ctx, indent: 0 }); + const pad = indent(ctx, 1); + const body = inner ? inner.split('\n').map(line => pad + line).join('\n') : pad; + return `LATERAL (\n${body}\n${indent(ctx)})`; + } + case 'TableName': + return renderNode(node.name, ctx); + case 'CompoundTableIdentifier': + return node.parts.map(p => (p.type === 'Identifier' ? (p.value || p.name) : '*')).join('.'); + case 'JoinTable': + return renderJoin(node, ctx); + case 'CommaJoin': + return `, ${renderNode(node.table, ctx)}`; + case 'FromClause': + return renderFrom(node, ctx); + case 'Where': + return `WHERE ${renderNode(node.expr, ctx)}`; + case 'GroupBy': + return renderGroupBy(node, ctx); + case 'OrderBy': + return renderOrderBy(node, ctx); + case 'OrderItemList': + return node.items.map(it => renderNode(it, ctx)).join('\n'); + case 'AddOrderItem': + return renderOrderItem(node, ctx); + case 'GroupingElementList': + return node.items.map(it => renderNode(it, ctx)).join('\n'); + case 'StringLiteral': + return `'${node.value}'`; + case 'NumericLiteral': + if (node.value && typeof node.value === 'object' && node.value.value !== undefined) { + return String(node.value.value); + } + return String(node.value); + case 'UnsignedNumericLiteral': + return String(node.value); + case 'Literal': + return String(node.value); + case 'Identifier': + return node.value || node.name; + case 'CompoundIdentifier': + return node.parts.map(p => (p.type === 'Identifier' ? (p.value || p.name) : '*')).join('.'); + case 'ParenthesizedExpression': + return `(${renderNode(node.node, ctx)})`; + case 'ParenExpression': + return `(${renderNode(node.node, ctx)})`; + case 'AddGroupingElement': + if (node.kind === 'EXPR') return renderNode(node.expr, ctx); + if (node.list) return renderNode(node.list, ctx); + return ''; + case 'Expression2b': + return renderExpression2b(node, ctx); + case 'BinaryExpression': + return `${renderNode(node.left, ctx)} ${renderBinaryOp(node.operator)} ${renderNode(node.right, ctx)}`; + default: + return `[${node.type}]`; + } +} + +function renderSelect(node, ctx) { + const lines = []; + if (node.selectComments && node.selectComments.length > 0) { + lines.push(`SELECT -- ${node.selectComments.join(' ')}`); + } else { + lines.push('SELECT'); + } + const items = node.selectItems || []; + items.forEach((item, idx) => { + const prefix = idx === 0 ? indent(ctx, 1) : `${indent(ctx, 1)}, `; + lines.push(prefix + renderNode(item, { ...ctx, indent: ctx.indent + 1 })); + }); + if (node.from) { + lines.push(`${indent(ctx)}FROM`); + lines.push(`${indent(ctx, 1)}${renderNode(node.from, { ...ctx, indent: ctx.indent + 1 })}`); + } + if (node.where) { + lines.push(`${indent(ctx)}${renderNode(node.where, { ...ctx, indent: ctx.indent + 1 })}`); + } + if (node.groupBy) { + lines.push(`${indent(ctx)}${renderNode(node.groupBy, ctx)}`); + } + if (node.having) { + lines.push(`${indent(ctx)}${renderNode(node.having, { ...ctx, indent: ctx.indent + 1 })}`); + } + if (node.orderBy) { + lines.push(`${indent(ctx)}${renderNode(node.orderBy, ctx)}`); + } + return lines.join('\n'); +} + +function renderSelectItem(node, ctx) { + const expr = renderNode(node.expr, ctx); + if (node.alias) return `${expr} AS ${node.alias.name || node.alias}`; + return expr; +} + +function renderFrom(node, ctx) { + const base = renderNode(node.first, ctx); + const joins = (node.joins || []).map(j => renderNode(j, ctx)); + return [base, ...joins].join('\n' + indent(ctx)); +} + +function renderTableRef(node, ctx) { + if (node.base) { + const base = renderNode(node.base, ctx); + const alias = node.alias ? (node.alias.value || node.alias.name || node.alias) : null; + return alias ? `${base} ${alias}` : base; + } + return '[TableRef]'; +} + +function renderGroupBy(node, ctx) { + const items = node.list.items.map(it => renderNode(it, ctx)); + if (items.length === 0) return 'GROUP BY'; + const lines = ['GROUP BY']; + lines.push(`${indent(ctx, 1)}${items[0]}`); + for (let i = 1; i < items.length; i++) { + lines.push(`${indent(ctx, 1)}, ${items[i]}`); + } + return lines.join('\n'); +} + +function renderOrderBy(node, ctx) { + const items = node.list.items.map(it => renderOrderItem(it, ctx)); + if (items.length === 0) return 'ORDER BY'; + const lines = ['ORDER BY']; + lines.push(`${indent(ctx, 1)}${items[0]}`); + for (let i = 1; i < items.length; i++) { + lines.push(`${indent(ctx, 1)}, ${items[i]}`); + } + return lines.join('\n'); +} + +function renderOrderItem(node, ctx) { + const expr = renderNode(node.expr, ctx); + const dir = node.direction ? ` ${node.direction}` : ''; + return `${expr}${dir}`; +} + +function renderJoin(node, ctx) { + const base = `${node.joinType} ${renderNode(node.table, ctx)}`; + if (!node.condition) return base; + if (node.condition.type === 'On') { + const expr = renderNode(node.condition.expr, { ...ctx, indent: ctx.indent + 1 }); + return `${base}\n${indent(ctx, 1)}ON ${expr}`; + } + if (node.condition.type === 'Using') { + const cols = node.condition.columns.items.map(c => c.value || c.name).join(', '); + return `${base}\n${indent(ctx, 1)}USING (${cols})`; + } + return base; +} + +function renderOrderByLimitOpt(opt, ctx) { + if (!opt) return ''; + const lines = []; + if (opt.orderBy) lines.push(renderOrderBy(opt.orderBy, ctx)); + if (opt.limit) lines.push(`LIMIT ${renderNode(opt.limit.value, ctx)}`); + if (opt.offset) lines.push(`OFFSET ${renderNode(opt.offset.value, ctx)}`); + if (opt.fetch) lines.push(`FETCH ${opt.fetch.mode} ${renderNode(opt.fetch.value, ctx)} ${opt.fetch.rows} ONLY`); + return lines.filter(Boolean).join('\n'); +} + +function renderExpression2b(node, ctx) { + const base = renderNode(node.base, ctx); + return base; +} + +function renderBinaryOp(op) { + if (!op) return ''; + if (typeof op === 'string') return op; + return op.op || ''; +} + +function indent(ctx, extra = 0) { + return ' '.repeat((ctx.indent + extra) * 4); +} + +module.exports = { formatSql }; From f1f077f2f7c2ba252ffce20b7da8c94bca24f7c3 Mon Sep 17 00:00:00 2001 From: Toshiki Iga Date: Thu, 29 Jan 2026 01:00:24 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=BC=E3=83=9E?= =?UTF-8?q?=E3=83=83=E3=83=88=E3=83=86=E3=82=B9=E3=83=88=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=20(#9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit フォーマットテスト追加 --- sql-formatter-test.js | 30 ++++++++++++++++++++++++++++-- sql-formatter.js | 3 ++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/sql-formatter-test.js b/sql-formatter-test.js index f0c86cc..2d4f0ff 100644 --- a/sql-formatter-test.js +++ b/sql-formatter-test.js @@ -18,13 +18,38 @@ FROM a FROM t -WHERE b = 1`, +WHERE + b = 1`, }, { name: 'select-line-comment', sql: "SELECT -- keep comment\n a\nFROM t", expectIncludes: ['SELECT -- keep comment'], }, + { + name: 'select-group-by', + sql: 'SELECT a, b FROM t GROUP BY a, b', + expect: `SELECT + a + , b +FROM + t +GROUP BY + a + , b`, + }, + { + name: 'select-order-by', + sql: 'SELECT a, b FROM t ORDER BY a DESC, b', + expect: `SELECT + a + , b +FROM + t +ORDER BY + a DESC + , b`, + }, { name: 'nested-subquery', sql: 'SELECT u.name FROM (SELECT id, name FROM users WHERE active = 1) AS u', @@ -37,7 +62,8 @@ FROM , name FROM users - WHERE active = 1 + WHERE + active = 1 ) u`, }, ]; diff --git a/sql-formatter.js b/sql-formatter.js index f27ca93..edecbc9 100644 --- a/sql-formatter.js +++ b/sql-formatter.js @@ -125,7 +125,8 @@ function renderSelect(node, ctx) { lines.push(`${indent(ctx, 1)}${renderNode(node.from, { ...ctx, indent: ctx.indent + 1 })}`); } if (node.where) { - lines.push(`${indent(ctx)}${renderNode(node.where, { ...ctx, indent: ctx.indent + 1 })}`); + lines.push(`${indent(ctx)}WHERE`); + lines.push(`${indent(ctx, 1)}${renderNode(node.where.expr, { ...ctx, indent: ctx.indent + 1 })}`); } if (node.groupBy) { lines.push(`${indent(ctx)}${renderNode(node.groupBy, ctx)}`);