やったこと
次の記事で実装されていた SQL の CTE を可視化するツールが便利だった。
https://note.com/dd_techblog/n/n3876f38cc5fc
ただ、記事では Python で実装されており、ツールを使うにはサーバを建てる必要があり少し面倒だった。なので、 GitHub Pages からサクッと使えるよう JavaScript で実装し直してみた。
次のリンクから実際に触れる。
以下、試行錯誤したときのメモ
Python → JavaScript への実装
先の記事 を自分で動かしてみた。そのときの GitHub リポジトリがこちら:
https://github.com/yamamoto-yuta/sql-cte-visualizer?tab=readme-ov-file
この中の main.py
コピーし、 ChatGPT へ「 JavaScript で書き直して」と指示することで、実装の叩きを作った。というか、可視化の部分以外はこれでいけた。
SQL パーサーを用いたアプローチ
実は当初は記事にある正規表現ではなく SQL パーサーを用いた方法を検討していた。理由は ChatGPT に投げた際に Python → JavaScript の変換で正規表現部分がうまくいかなかったため(後にうまくいくことがわかったため、正規表現を用いた方法に戻した)。
その際、 SQL パーサーは「 node-sql-parse 」というライブラリを採用した。
https://github.com/taozhi8833998/node-sql-parser
(SQL パーサーの選定には次の記事が参考になった)
https://zenn.dev/carenet/articles/d42b236ae69bc5
node-sql-parser でのパースの例を以下に示す。
SQL
|
|
パース結果
|
|
node-sql-parser を使っていた時に発生していた既知の不具合は コチラ にストックしてある。
現時点で判明している不具合の多くは node-sql-parser が使用しているパーサーの改修が必要そうな雰囲気(要検証)。その場合、 node-sql-parser は内部で PEG.js というパーサージェネレータを使用しており、おそらく下記の部分を回収することになるのではないかと思われる(が、今回は一旦ここまでで…)。
該当コード: https://github.com/taozhi8833998/node-sql-parser/blob/master/pegjs/bigquery.pegjs
「PEG.js is 何?」は次の記事が参考になった。
https://qiita.com/kujirahand/items/eab914bc77cf1bc0837c
可視化方法の検討
先の記事 では Graphviz を用いて可視化していた。当初、 Graphviz が JavaScript でも使えることを知らずに別のライブラリでの実装を試みていた。そのときのメモを下記に示す(現在は Graphviz を用いている)。
D3.js
当初、 D3.js での可視化を試みていた。理由は D3.js の hierarchy が今回の用途に使えそうだったから(後に使えないことが分かったが…)。
hierarchy の実装については次の記事が参考になった。
https://zenn.dev/yuji/articles/7eb96460317222
前述の「使えないことが分かった」についてだが、「同名の子ノードが複数存在できてしまう」ことが問題となった。
例えば、次のような SQL クエリがあったとする。
|
|
その場合…
正規表現による可視化(本来) | hierarchy による可視化 |
---|---|
このように可視化してほしい。 | しかし、 hierarchy だと次のような可視化になってしまう。 |
見てみると、 source1
が複数に分かれてしまっていることがわかる、おそらく子ノードが結合するような構造に hierarchy を適用するのは適切ではなかったものと思われる…(公式ドキュメント等を調べても、子ノードが結合するような構造に適用したサンプルは見つけられなかった)。
なお、D3.js 以外では Mermaid.js も検討したが、公式の Live Editor のようにユーザ入力に応じで図を更新する方法が分からず沼ったので断念した。
Graphviz
先の記事 と同じく Graphviz が JavaScript でも使えることが分かったので、最終的にこちらを採用した。
Graphviz の操作は次の記事が参考になった。
https://qiita.com/mmmmk/items/f7c70024938b0e38e4c9
node-sql-parser を使っていたときの実装
node-sql-parser を使っていたときの CTE 可視化の実装を下記に示す。
node-sql-parser や hierarchy の実装についてはほぼ公式ドキュメントや参考記事の通り。
独自で実装したのは node-sql-parser で得られた構文木を hierarchy で可視化できるよう変換する処理。
node-sql-parser から得られる構文木のフォーマットは次のとおり。
|
|
hierarchy のフォーマットは次のとおり。
|
|
変換処理は主に次のステップで行うようにした。
- 構文木のデータから CTE 同士の依存関係データを作成する
- 最後の SELECT ~ FROM で FROM している CTE に先の依存関係データを結合する
1 つ目のステップの実装コードは次のとおりである( originalJson
に構文木のデータが格納されている)。ポイントは、 CTE が CTE を FROM しているケースにも対応できるよう、再帰的に依存関係を特定している点である。
|
|
2 つ目のステップの実装コードは次のとおり。
|
|
各ステップの動作例を以下に記す。
例えば、次のような SQL があったとする。
|
|
この SQL から得られる構文木は次のようになる。
長いので折りたたみ
|
|
1 つ目のステップでは、この構文木のデータから次のような依存関係データを作成する。
|
|
2 つ目のステップを経て、最終的に次のような hierarchy 用データになる。今回のクエリでは、最終クエリは cte_depth_1
を FROM していたので、1 つ目のステップで得られた cte_depth_1
の依存関係データを結合すれば OK となる。
|
|