はじめに
こんにちは、2024年4月入社の川渕皓太です。
先日、当社では新しいSQLフォーマッタであるuroborosql-fmtをリリースしました。このツールは当社が公開しているPostgreSQL向けのSQLコーディング規約に基づいてSQLをフォーマットするものです。
uroborosql-fmtの基本的な情報は以下の記事を参照してください。
uroborosql-fmtはuroborosql、go-twowaysql、domaといった2WaySQLに対応しています。
2WaySQLとはそのまま実行することもでき、アプリケーションで読み込んでバインドパラメータの指定などをして実行することも出来ます。このように2つの実行方法があることから2WaySQLと呼ばれます
select |
本記事ではuroborosql-fmtにおいて2WaySQLのフォーマットに対応した方法を説明していきます。
uroborosql-fmtのフォーマット方法概要
- tree-sitter-sqlで入力SQLをパースしてCSTを取得
- 取得したCSTを解析して独自の木構造の構造体に変換
- 変換した構造体から整形したSQLを生成して出力
課題点
2WaySQLでは以下のように、特別なSQLコメントを使用してバインドパラメータや条件の指定ができます。
select |
このようなSQLは2WaySQLとしては正しいですが、通常のSQL構文としては不正(where句の内部のand
が不足)であり、通常のSQLパーサではパースすることができません。そのため、先述した方法ではフォーマットが行えません。
解決方法として主に以下の二つが考えられます。
- 入力SQLのIF文を解析して複数のSQLを生成し、それらのSQLをフォーマット後にマージする
- 2WaySQLに対応したパーサを作成する
2WaySQLのIF文はどこに記述しても不正とはならず、文法ファイルが複雑になると考えたため、今回は方法1の「入力SQLのIF文を解析して複数のSQLを生成し、それらのSQLをフォーマット後にマージする」 案を採用しました。
2WaySQLをフォーマットする方法概要
- 入力SQLのIF文等の分岐を解析して生成されうるSQLを分岐網羅的に生成
- 生成したすべてのSQLをフォーマット
- フォーマット後のすべてのSQLをマージして出力
2WaySQLのフォーマット方法詳細
具体例として以下のようなSQLをフォーマットする場合を考えます。
select |
1. 生成されうるSQLを分岐網羅的に生成
SQLを1行目から順にたどっていき、IF分岐を解析して以下のような木構造を作成します。
木構造を作成する際、ソースコード上で出現が早いほど左の子になるよう作成します。
この木構造の葉は生成されうるSQLを示しています。葉は分岐網羅を満たすように生成します。
2. 生成されたSQLのフォーマット
次に生成された木構造の葉をすべてフォーマットします。
単純なSQLのフォーマット方法はこちらの記事で詳細に説明しているため割愛します。
3. フォーマット後のSQLをマージ
木の深いところから順にマージしていき、最終的に木構造の根が全体の最終フォーマット結果になるようにします。
マージは以下の処理を行うことで実現します。
2つのSQLの各行を比較 |
具体的に以下のマージを説明していきます。
3行目まで一致するのでそのまま描画
select
/*IF hoge*/
aaa4行目は一致しない(左:
/*IF huga*/
、右:/*ELIF foo*/
)ので、まず左の子から6行目の/*END*/
の1行前まで描画select
/*IF hoge*/
aaa
/*IF huga*/
, bbb右の子の6行目の
/*END*/
の1行前まで描画select
/*IF hoge*/
aaa
/*IF huga*/
, bbb
/*ELIF foo*/
, ccc/*END*/
を描画select
/*IF hoge*/
aaa
/*IF huga*/
, bbb
/*ELIF foo*/
, ccc
/*END*/行の比較を再開。これ以降はすべて一致するのでそのまま描画
select
/*IF hoge*/
aaa
/*IF huga*/
, bbb
/*ELIF foo*/
, ccc
/*END*/
/*END*/
from
table1
これで①のマージ処理が完了しました。
同様のマージ処理を②においても実行することで、最終的に以下のようなフォーマット結果が得られます。
select |
さいごに
当社が作成したSQLフォーマッタであるuroborosql-fmtにおける2WaySQLのフォーマット方法を説明しました。
uroborosql-fmtは元のSQLを壊していないか検証するロジックを組み込んでいますが、万が一元のSQLが変更されている等の不具合があればissueに起票してくださると幸いです。
元のSQLを壊していないか検証するロジックについては以下の記事で紹介していますので是非ご覧ください。
関連記事
- 新しいSQLフォーマッターであるuroboroSQL-fmtをリリースしました | フューチャー技術ブログ
- Engineer Camp2022 RustでSQLフォーマッタ作成(前編) | フューチャー技術ブログ
- Engineer Camp2022 RustでSQLフォーマッタ作成(後編) | フューチャー技術ブログ
- Language Server Protocolを用いたVSCode拡張機能開発 (前編) | フューチャー技術ブログ
- Language Server Protocolを用いたVSCode拡張機能開発 (後編) | フューチャー技術ブログ
- Rust製SQLフォーマッタをnapi-rsを利用してVSCode拡張機能化 | フューチャー技術ブログ
- C/C++を呼び出しているRustのWASM化 | フューチャー技術ブログ