きっかけ
普段業務では BigQuery に export された GA4 (Google Analytics 4) のデータを加工して社内に提供しています。 加工クエリは BigQuery 内で GoogleSQL 用いて行っています。 GoogleSQL 特有の関数や記法があるため、クエリのテストをローカルで実行するようなことがやりづらかったりします。
そんなときに見つけたのが BigQuery emulator です。 その名の通り BigQuery をエミュレートしてくれるものです。
しかし困ったことに、NET.HOST
等の関数群に対応していませんでした。
GA4 のログを加工する際に NET.*
関数はわりと使うので、これさえ対応してくれていればなーと思いました。
そこで、欲しい機能があるなら PR を送ったらいいじゃないと思ってコントリビュートすることにしました。 GoogleSQL をエミュレートする実装は goccy/go-zetasqlite にあるため、今回 PR を送ったのはこちらのリポジトリです。
作業
まずはコードを読んで、どんなふうに動作するのかを理解するところからはじめました。
README の How it works を見ると、ZetaSQL を SQLite に変換して実行すると書かれています。
でも一方で、実装 を見ると、以下のように Go 言語内で処理しているように見えます。
func CURRENT_DATE(zone string) (Value, error) {
loc, err := toLocation(zone)
if err != nil {
return nil, err
}
return CURRENT_DATE_WITH_TIME(time.Now().In(loc))
}
結局どっちで処理してるの?という疑問を取り除くところからはじめました。
go-zetasqlite は go-sqlite3 に依存していますが、これには Go の関数を SQL の関数として登録する機能があるようですね (ref)。
// RegisterFunc makes a Go function available as a SQLite function.
これのおかげで、Go 言語の関数として実装すれば良いということでした。
具体的な作業は PR の中身 を見ると分かるのですが、Go の net/url
等のコアライブラリを呼び出しているだけですね。
でも普段 Go 言語を書かないので、どのライブラリにどんな API があるかも知らなかったため、最適なものを探すのに時間がかかりました。
感想
goccy さんには丁寧に対応していただきましたし、修正を加えてからの再レビューも早く、良い contributer experience でした。
よく OSS の issue で、「こういう機能は考えてないの?」というコメントに対してメンテナーが「いいアイデアがあれば PR 送ってね!」と返しているのを見かけたりしますが、その通りですね。エンジニアとして、欲しけりゃ作れ精神は大切にしたいものです。