`vet` - クエリのリンティング
v1.19.0で追加
sqlc vet
は、クエリを一連のリントルールにかけて実行します。
ルールはsqlc
設定ファイルで定義されます。ルールは名前、メッセージ、およびCommon Expression Language(CEL)式で構成されます。式はcel-goを使用して評価されます。式がtrue
と評価される場合、sqlc vet
は与えられたメッセージを使用してエラーを報告します。
リントルールの定義
各リントルールのCEL式は、以下のprotoメッセージで定義された変数を介して、sqlc設定とクエリからの情報にアクセスできます。
message Config
{
string version = 1;
string engine = 2 ;
repeated string schema = 3;
repeated string queries = 4;
}
message Query
{
// SQL body
string sql = 1;
// Name of the query
string name = 2;
// One of "many", "one", "exec", etc.
string cmd = 3;
// Query parameters, if any
repeated Parameter params = 4;
}
message Parameter
{
int32 number = 1;
}
この基本情報に加えて、PostgreSQLまたはMySQLのデータベース接続が設定されている場合、各CEL式はpostgresql.explain
およびmysql.explain
変数を介してクエリでEXPLAIN ...
を実行した出力にアクセスできます。この出力は非常に複雑で、クエリの構造に依存しますが、sqlcは可能な限り多くの情報を解析して提供しようと試みます。詳細については、EXPLAIN ...
出力を使用するルールを参照してください。
CEL式環境で利用可能な基本的な設定とクエリ情報のみを使用したいくつかのルール例を以下に示します。これらの例は単純ですが、書くことができるルールの種類の雰囲気を掴むことができます。
version: 2
sql:
- schema: "query.sql"
queries: "query.sql"
engine: "postgresql"
gen:
go:
package: "authors"
out: "db"
rules:
- no-pg
- no-delete
- only-one-param
- no-exec
rules:
- name: no-pg
message: "invalid engine: postgresql"
rule: |
config.engine == "postgresql"
- name: no-delete
message: "don't use delete statements"
rule: |
query.sql.contains("DELETE")
- name: only-one-param
message: "too many parameters"
rule: |
query.params.size() > 1
- name: no-exec
message: "don't use exec"
rule: |
query.cmd == "exec"
EXPLAIN ...
出力を使用するルール
v1.20.0で追加
CEL式環境には、EXPLAIN ...
出力を含む2つの変数、postgresql.explain
とmysql.explain
があります。sqlc
は、設定されたデータベースエンジンに関連付けられた変数のみを入力し、データベース接続が設定されている場合にのみ入力します。
postgresql
エンジンの場合、sqlc
は以下を実行します:
EXPLAIN (ANALYZE false, VERBOSE, COSTS, SETTINGS, BUFFERS, FORMAT JSON) ...
ここで"..."
はクエリ文字列で、出力をPostgreSQLExplainprotoメッセージに解析します。
mysql
エンジンの場合、sqlc
は以下を実行します:
EXPLAIN FORMAT=JSON ...
ここで"..."
はクエリ文字列で、出力をMySQLExplainprotoメッセージに解析します。
これらのprotoメッセージ定義はここに含めるには長すぎますが、sqlc
ソースツリー内のprotos
ディレクトリで見つけることができます。
EXPLAIN ...
からの出力はクエリの構造に依存するため、汎用的な例を提供するのは少し困難です。詳細については、PostgreSQLドキュメントとMySQLドキュメントを参照してください。
...
rules:
- name: postgresql-query-too-costly
message: "Query cost estimate is too high"
rule: "postgresql.explain.plan.total_cost > 1.0"
- name: postgresql-no-seq-scan
message: "Query plan results in a sequential scan"
rule: "postgresql.explain.plan.node_type == 'Seq Scan'"
- name: mysql-query-too-costly
message: "Query cost estimate is too high"
rule: "has(mysql.explain.query_block.cost_info) && double(mysql.explain.query_block.cost_info.query_cost) > 2.0"
- name: mysql-must-use-primary-key
message: "Query plan doesn't use primary key"
rule: "has(mysql.explain.query_block.table.key) && mysql.explain.query_block.table.key != 'PRIMARY'"
EXPLAIN ...
出力に依存するルールを構築する際、データベースから返される実際のJSONを見ることが役立つ場合があります。環境変数SQLCDEBUG=dumpexplain=1
を設定すると、sqlc
はそれを出力します。この環境変数をダミールールと一緒に使用して、すべてのクエリのEXPLAIN ...
出力を見ることができます。
version: 2
sql:
- schema: "query.sql"
queries: "query.sql"
engine: "postgresql"
database:
uri: "postgresql://postgres:postgres@localhost:5432/postgres"
gen:
go:
package: "db"
out: "db"
rules:
- debug
rules:
- name: debug
rule: "!has(postgresql.explain)" # A dummy rule to trigger explain
uri
で設定されたデータベースは、vet
が正しく動作するために最新のスキーマを持つ必要があり、sqlc
はデータベースにスキーママイグレーションを適用しないことに注意してください。EXPLAIN ...
出力に依存するルールでsqlc vet
を実行する前に、選択したマイグレーションツールを使用して必要なテーブルとオブジェクトを作成してください。
または、管理データベースを設定して、sqlc
が正しいスキーマを持つホスト型エフェメラルデータベースを自動的に作成するようにすることもできます。
組み込みルール
sqlc/db-prepare
データベース接続が設定されている場合、組み込みのsqlc/db-prepare
ルールを実行できます。このルールは、接続されたデータベースに対して各クエリの準備を試み、失敗があれば報告します。
version: 2
sql:
- schema: "schema.sql"
queries: "query.sql"
engine: "postgresql"
gen:
go:
package: "authors"
out: "db"
database:
uri: "postgresql://postgres:password@localhost:5432/postgres"
rules:
- sqlc/db-prepare
uri
で設定されたデータベースは、vet
が正しく動作するために最新のスキーマを持つ必要があり、sqlc
はデータベースにスキーママイグレーションを適用しないことに注意してください。sqlc/db-prepare
ルールでsqlc vet
を実行する前に、選択したマイグレーションツールを使用して必要なテーブルとオブジェクトを作成してください。
または、管理データベースを設定して、sqlc
が正しいスキーマを持つホスト型エフェメラルデータベースを自動的に作成するようにすることもできます。
version: 2
cloud:
project: "<PROJECT_ID>"
sql:
- schema: "schema.sql"
queries: "query.sql"
engine: "postgresql"
gen:
go:
package: "authors"
out: "db"
database:
managed: true
rules:
- sqlc/db-prepare
実際の動作を見るには、authorsの例をチェックしてください。
リントルールの実行
定義されたルールの名前をsqlパッケージのrulesリストに追加すると、sqlc vet
はそのルールをパッケージ内のすべてのクエリに対して評価します。
以下の例では、2つのルールが定義されていますが、1つだけが有効になっています。
version: 2
sql:
- schema: "query.sql"
queries: "query.sql"
engine: "postgresql"
gen:
go:
package: "authors"
out: "db"
rules:
- no-delete
rules:
- name: no-pg
message: "invalid engine: postgresql"
rule: |
config.engine == "postgresql"
- name: no-delete
message: "don't use delete statements"
rule: |
query.sql.contains("DELETE")
リントルールのオプトアウト
任意のクエリについて、@sqlc-vet-disable
クエリアノテーションを使用してsqlc vet
にリントルールを評価しないよう指示できます。アノテーションは無視するルールのリストを受け取ります。
/* name: GetAuthor :one */
/* @sqlc-vet-disable sqlc/db-prepare no-pg */
SELECT * FROM authors
WHERE id = ? LIMIT 1;
ルールは複数の行に分割することもできます。
/* name: GetAuthor :one */
/* @sqlc-vet-disable sqlc/db-prepare */
/* @sqlc-vet-disable no-pg */
SELECT * FROM authors
WHERE id = ? LIMIT 1;
クエリのすべてのルールをスキップするには、パラメータなしで@sqlc-vet-disable
アノテーションを提供できます。
/* name: GetAuthor :one */
/* @sqlc-vet-disable */
SELECT * FROM authors
WHERE id = ? LIMIT 1;
原文:https://docs.sqlc.dev/en/latest/howto/vet.html