概要
StandardSQL(BigQuery)のSQL Linter/Formatterを作りました.
需要はあんまりなさそうですが、せっかくなので内容について少し書いてみます.
コードはこちら
https://github.com/shigeru0215/sqlint
モチベーション
仕事で結構BigQueryのSQLを書くことがあるのですが、メンバによって細かい書き方が違ったのでチーム内でルールを決めました.
理由は、主に見やすさのためです.
例えば、以下のような感じです.
select /* 予約後はすべて小文字 */
a /* インデントは4つずつ */
, b /* 改行のカンマは行末ではなく行頭にする */
from
table1 as t1
inner join table2 as t2
on t1.x = t2.y /* fromで一段下げ, onでも一段下げる */
where
x > 10
limit
100
ただ、SQLが小さければレビューで指摘すればいいですが、百行超えをチェックするのは大変です.
そこでLinterで手軽にチェックできるようにすれば、CIとかにも仕込めて便利かと思って作りました.
使い方
Flake8をイメージしてもらえると良いかと思います
pip install
PyPi link
執筆時のバージョンは 0.2.4
です
pip install sqlint
Lint
こんな感じに、lint/formatしたいSQLがあるとします.
$ cat example.sql
select
a,
b
FROM
table1 as t1
join table2 as t2
on t1.x= t2.y
where
x > 10
limit 100
そのまま引数に与えると、いろいろ教えてくれます.
$ sqlint example.sql
example.sql (L2, 6): comma must be head of line
example.sql (L3, 1): indent steps must be 4 multiples, but 3 spaces
example.sql (L4, 1): reserved keywords must be lower case: FROM -> from
example.sql (L6, 5): join context must be fully: JOIN -> INNER JOIN
example.sql (L7, 16): whitespace must be before binary operator: x=
指摘内容は上から
- カンマは行末ではなく行頭にする ★
- インデントは4つずつのところ、3つになっている ★
- FROMなどの予約後は、全て小文字にする ★
- JOINは省略せずに、INNER JOINなどフルコンテキストで書く
- 演算子の左右はスペースを開ける
です.
内容の是非は前述の通りローカルルールですが、★をつけた部分は設定ファイルで変更できます(後述)
Format
-f
オプションをつけると、元ファイルは変更せずにフォーマットされたSQLを出力します.
※ 現状では、Lintで指摘しない部分も修正されるので注意です.
$ sqlint example.sql -f
select
a
, b
from
table1 as t1
inner join table2 as t2
on t1.x = t2.y
where
x > 10
limit
100
蛇足ですが、Lint自体は割と簡単(土日の正味2日ぐらい?)でできていて、チームメンバに展開しました.
そしたらフォーマット機能もほしいと言われたので追加実装してたのですが、面倒で手こずってしばらく放置でした.
Formatterの実装で、データ構造がイケていなくてLinterの方も書き換えたりしたので、最初の設計は大事だなーと感じました.
設定ファイルについて
設定ファイルはiniファイルで指定します.
flake8
などに相乗りで、 tox.ini
に書いておくと良いでしょう.
[sqlint]
max-line-length = 128
# Default lint settings
# Comma position in breaking lines
# - head (default)
# - end
comma-position = head
# Reserved keyword style.
# - lower(default): All words are lower. (e.g) select
# - upper-head: Only a head of words is upper. (e.g) Select
# - upper-all: All head word is upper. (e.g) SELECT
keyword-style = lower
# indent steps in breaking a line
indent-steps = 4
[flake8]
# ...
- max-line-length: Integer
- 1行の最大文字数で、これを超える行はいい感じに途中で改行します(デフォルト 128)
- (注意) 改行のロジックが甘いので、コメントが入ってると変な感じなることもあります(要修正)
- comma-position: [head|end]
- 改行時のカンマの位置を行頭か行末か指定します(デフォルト 行頭)
- keyword-style: [lower|upper-head|upper-all]
- 予約後の小文字/大文字指定です(デフォルト lower)
- ex) [
select
|Select
|SELECT
]
- indent-steps: Integer
- インデントをいくずつにするかの指定です(デフォルト 4)
-c
オプションで任意の設定ファイルを指定できます.
$ sqlint example.sql -f -c /path/tox.ini
追加機能とか実装した感想
追加または修正する予定の機能たちです.いつ追加されるかは未定です.
もしバグとかあったら、教えていただけると感謝です.
Linting/Format機能がつたないので修正
前述の通りチームのルールに合わせてLintingをしています.
が、まだ実装できていない部分や、チームのルール以外にもチェックできる点があると思います.
例えば、
- 宣言されていないエイリアスを使おうとしている
- joinのon句で比較演算子の左右を、Join先(From句に与えられているテーブル)が左に来るように統一する
- サブクエリを禁止する
などなど.
また、Format機能もBigQueryのものよりましとはいえ、まだまだ改善の余地があります.
特にコメントを適当に処理しているので、SQLによってはだいぶずれると思います.
Chrome Extensionにしたい
Chrome extensionにして、BQのコンソール上で直接Formatしたいなと思って結構頑張ってました.
ただ、コンソール上に入力されたSQLをextension側で取得する方法がわからなかったので、保留になってます.
正確には離散的に取得できるときはあるのですが、入力時にリアルタイムで取得する方法がわかりませんでした.
BigQueryやChrom extensionに詳しい方いましたら、教えていただけると幸いです.