SQL linterの1つであるsqlfluffの環境をdocker composeでサクッと構築したい。
背景
最近はAIにSQLを書いてもらうことも多く、少し前からlinter/formatterを使いたいと考えていた。そんな中で開発チームに新しく参加した方のコーディングスタイルが既存メンバーのものと大きく異なっていたため、本格的にlinter導入を検討し始めた。
linterはいくつか選択肢があるがsqlfluffはpythonパッケージで、組織の環境にマッチしているため少し触ってみようという次第。
執筆時点では勝手に導入を検討している段階で、業務では使用していない。
構築
今回は以下のようにコンテナ構築用のdocker-composeと確認対象のSQLファイルを作成する。
│ docker-compose.yaml
│
└── sql
tables.sql
transactions.sql
まずは肝となるdocker-compose.yamlを作成する。
sqlfluffは公式イメージが配布されているのでこちらを使用。業務で使うときにはバージョンを固定しよう。
sqlfluffが対象のファイルを読めるようにマウントしておく。
services:
lint:
image: sqlfluff/sqlfluff:latest
volumes:
- type: bind
source: ./sql/
target: /sql/
サンプルSQLには適当に以下のようなものを用意。ちなみにDBはPostgreSQL。
create table users (
id serial primary key,
username varchar(50) not null unique,
email varchar(100) not null unique,
created_at timestamp default current_timestamp
);
create table orders (
id serial primary key,
user_id int references users(id),
product_name varchar(100) not null,
quantity int not null,
order_date timestamp default current_timestamp
);
insert into users(
username,
email
) values
('hoge', 'hoge@example.com'),
('huga', 'huga@example.com')
;
insert into orders(
user_id,
product_name,
quantity,
order_date
) values
(1, 'Product A', 2, now()),
(1, 'Product B', 1, now()),
(2, 'Product C', 5, now())
;
select
product_name,
quantity,
order_date,
username,
email
from
orders
left join
users on orders.user_id = users.id
where
orders.id = '1'
;
動作確認
sqlfluffでは、sqlfluff lintでlintを、sqlfluff fixでformatを実行できる。
公式イメージはコンテナのエントリーポイントを/sqlfluffに設定しているので実際のコマンドは以下を使用した。
docker compose run --rm lint lint ./ --dialect postgres
サービス名をlintとしたため分かりにくいが1つ目のlintがコンテナ指定、2つ目のlintがsqlfluffのコマンドになっている。その後は対象ファイルと言語を指定。
主要な言語は基本的に網羅しているように思われる。詳細はDialects Referenceを参照。
上記のコマンドを実行した結果、次のようにエラー発生位置と抵触しているルール、詳細が表示された。
[+] Creating 1/1
✔ Network sqlfluff_default Created 0.2s
== [tables.sql] FAIL
L: 10 | P: 33 | LT01 | Expected single whitespace between naked identifier and
| start bracket '('. [layout.spacing]
== [transaction.sql] FAIL
L: 1 | P: 18 | LT01 | Expected single whitespace between naked identifier and
| start bracket '('. [layout.spacing]
L: 6 | P: 29 | CV06 | Statements must end with a semi-colon.
| [convention.terminator]
...
L: 21 | P: 5 | RF02 | Unqualified reference 'product_name' found in select
| with more than one referenced table/view.
| [references.qualification]
...
All Finished 📜 🎉!
「かっこの前にスペースが無い」、「改行セミコロンするな」、「select句で元のテーブルを明示せよ」
と怒られている。続いてfixコマンドを実行した。
==== finding fixable violations ====
== [tables.sql] FAIL
L: 10 | P: 33 | LT01 | Expected single whitespace between naked identifier and
| start bracket '('. [layout.spacing]
== [tables.sql] FIXED
== [transaction.sql] FAIL
L: 1 | P: 18 | LT01 | Expected single whitespace between naked identifier and
| start bracket '('. [layout.spacing]
...
== [transaction.sql] FIXED
6 fixable linting violations found
[5 unfixable linting violations found]
「かっこの前にスペースが無い」、「改行セミコロンするな」のエラーは解消してくれた。
一方で「select句で元のテーブルを明示せよ」は自動処理できなかった。テーブル構造を理解しないと対処できないので仕方ない。
まとめ
docker-composeを使ってsqlfluffを動かせる環境を作った。軽く動かすだけなら非常に簡単にできた。
AIが爆速でクエリを量産する中でスタイルを意識するのは疲れる。fixコマンドでは自動で修正できないパターンもあるため完全にお任せとはいかないが、CIで動かしてできる限り楽をしたい。
また、公式でチームに採用する手順をまとめてくれている。
これを参考に周囲へのプレゼンを行い、うまくいけばスタイルバトルが始まる...
https://docs.sqlfluff.com/en/stable/guides/setup/teamrollout.html