LoginSignup
11
6

More than 3 years have passed since last update.

StandardSQLのLinterを作りました

Last updated at Posted at 2019-10-22

概要

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 に書いておくと良いでしょう.

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に詳しい方いましたら、教えていただけると幸いです.

11
6
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
6