Dataform で SQLFluff が使えるプラグインを作った
モチベーション
SQLFluff を使って dbt を開発すると SQL が決められたルールに保たれて可読性や保守性が増します。
ただ SQLFluff は Dataform(SQLX)に対応してなく愚直に適応しようとするとエラーとなってしまいます。
dbt で SQLFluff が使えているのは plugin として実装されているからであり Dataform も実装すればイケるのでは?という期待を持っていました。
以下がそのプラグインである sqlfluff-templater-dbt
になります。
sqlfluff-templater-dataform の紹介
そこで作ったのが sqlfluff-templater-dataform
になります。
試しに使ってみると以下のようにルールに即していない箇所を指摘してくれます。
> cat definitions/test.sqlx
config {
type: "table",
schema: "schama",
tags: [
"test"
],
description: "test sqlx file for lint by sqlfluff-templater-dataform.",
columns: {
user_id: "ユーザーID",
name: "ユーザー名",
age: "年齢",
order_date: "注文日",
order_count: "注文回数"
}
}
wItH users as (
select user_id,
name,
age
from ${ref('users')}
)
, user_orders AS (
select user_id, order_id, DATE(created_at) AS order_date from ${ref('user_orders')}
)
SELECt users.user_id, name, age, order_date, count(order_id) AS order_count
from users left outer join user_orders on users.user_id = user_orders.user_id
group by user_id, name, age, order_date
> sqlfluff lint
== [definitions/test.sqlx] FAIL
L: 17 | P: 1 | CP01 | Keywords must be upper case.
| [capitalisation.keywords]
L: 17 | P: 12 | CP01 | Keywords must be upper case.
| [capitalisation.keywords]
L: 18 | P: 1 | CP01 | Keywords must be upper case.
| [capitalisation.keywords]
L: 18 | P: 1 | LT02 | Expected indent of 4 spaces. [layout.indent]
L: 18 | P: 1 | LT09 | Select targets should be on a new line unless there is
| only one select target. [layout.select_targets]
...
All Finished 📜 🎉!
できることできないこと
できることとしては SQLFluff の機能を Dataform(SQLX)に適応することができます。
普段と同じく .sqlfluff
を用意してそこに設定や矯正したいルールや記述します。
Google Cloud と接続することはないですが、${ref()}
のコンパイルの都合上 project_id
と dataset_id
をセットする必要があります。
[sqlfluff]
templater = dataform
dialect = bigquery
sql_file_exts = .sqlx
[sqlfluff:templater:dataform]
project_id = project_id
dataset_id = dataform
[sqlfluff:rules:capitalisation.keywords]
# 予約語の大文字小文字
capitalisation_policy = upper
できないことは Javascript の解釈です。
Dataform には js{}
ブロックを用いて JavaScript を実行してその結果を SQL に埋め込むことができます。
今回 SQLFluff が Python の実装なので一旦非対応としました。
js{}
が含まれる場合そのファイルはスキップされます。
実装に関して
sqlfluff-templater-dbt
をデバッグしていくと DbtTemplater
の process()
が毎回呼ばれていることがわかります。
こちらを参考に DataformTemplater
というクラスを作り RawTemplater
を継承(dbt の場合 JinjaTemplater
を継承)し process
メソッドを実装することにしました。
sqlfluff-templater-dbt
の process
メソッドを読むと SQL をコンパイルしてテンプレート部分({{ config() }}
など)の開始位置と終了位置を把握していました。
dbt の場合、コンパイルには dbt の Python パッケージを使用し、テンプレート部分の抽出には JinjaTemplater
が使われます。
一方、Dataform にはこのような Python パッケージやテンプレートのパーサーがないため、自作する必要がありました。
実装としては、${ref()}
の部分は .sqlfluff
で指定された project_id
と dataset_id
を使ってテーブル名を再現し、テンプレート部分(Dataform の場合は config{}
など)は正規表現で取り除く形にしています。
さいごに
念願だった SQLFluff の Dataform 適応ができるようになりました!
ただし、JavaScript を使った SQLX の lint には対応していないという課題も残っています。
私が関わっているプロジェクトでは幸い JavaScript の使用頻度が低いですが、JavaScript を多用している場合には不便かもしれません。これを改善するのが今後の目標です。(JavaScript と SQL の責務が曖昧になるため、使用を避けているという側面もあります。)
その他、使いにくいところや機能要望がありましたら GitHub Issue の方でお願いします!
ぜひコントリビュートもお待ちしています🙏
Discussion
terraform
→templater
のTypoかもしれません!指摘ありがとうございます!
直しました🙏