Go
SQL
Go2Day 12

golang で 2 Way SQL

空いていたので穴埋め。


はじめに

データベースを扱うプロジェクトでは、オンコードで SQL を書く事が割と多いのですが、そういったソースコードに埋め込まれた SQL はプレースホルダを使って値を取るので直接実行する事は出来ません。

select * from foo where id = :id and bar = :bar

RDBMS によっては :変数名 ではなく ?$1 といった表記をする物もあります。こういった SQL は直接実行できない為、どうしても結果がイメージし辛くなるのですが、2 Way SQL という方法を使う事で解決できる事があります。


2 Way SQL とは

2 Way SQL は SQL のコメントに IF や ELSE、END といった制御構文を埋め込む事で、直接実行する事も出来るし、プレースホルダを使ったオンコード用の SQL としても使う事ができるといった物です。筆者が知る限り明確な仕様は見当たらないのですが、昔は Doma/S2Dao といったデータベースの抽象化レイヤと合わせて利用されて来ました。

select * from foo where id = /*id*/5 /* IF enabled */and bar = /*bar*/'foo' /*END*/

この SQL を実行するとコメントは無視される為、実際は以下が実行されます。

select * from foo where id = 5 and bar = 'foo'

このコメントに記述された IF/ELSE/END と、値の直前に書かれたコメントが 2 Way SQL です。


2 Way SQL のルール

select * from foo where id = /*id*/5 /* IF enabled */and bar = /*bar*/'foo' /*END*/

この SQL に制御パラメータ enabled を true 渡すと IF 文が有効になるため、id と bar による抽出が有効になります。また false で渡すと IF 文が無効となるため id のみの抽出となります。

select * from foo where id = :id

この様に /*bar*/'foo' はプレースホルダ bar に置き換えられます。


2 Way SQL の実装

随分前ですが golang で実装しました。

https://github.com/mattn/ways2go

以下の様に使います。

env := make(map[string]interface{})

env["enabled"] = true

sql2way := `
select
*
from
foo
where
id = /*id*/5
/* IF enabled */
and bar = /*bar*/3
/*END*/
`

sql, err := Eval(sql2way, env, ways2go.Question)

正常に評価されると以下の SQL を得る事が出来ます。

select

*
from
foo
where
id = ?
and bar = ?

データベースによってはプレースホルダの形式が異なります。ways2go では以下の形式をサポートしています。

識別
プレースホルダ

Question
?

Dollar
$1

Colon
:foo

これを使えばサンプルとして実行する事もでき、プログラムから実行する事もできる SQL を同じファイルで管理する事が出来ます。


まとめ

大規模なプロジェクトで SQL が散乱する様なコードベースでは SQL を外部ファイルに切り出すことでソースコードがスッキリするかもしれません。ただし実は筆者も昔に作ったまま時間が経っており、テストは書いてあるのですが実務で使った事がありません。皆さんからの PR や機能追加をお待ちしております。