なぜだか急に pg gem の実装が気になった。
# コネクション張って SELECT するだけのコード require 'pg' conn = PG.connect(dbname: 'hoge') conn.exec('SELECT * FROM fuga') do |result| result.each do |row| puts row end end
👆 のコードを実行した時に何が起こっているかを追う。
バージョン
pg 1.2.3
PG.connect
PG.connect の定義は lib/pg.rb にある。
はいはい PG::Connection のインスタンスを返してるのね。って PG::Connection クラス 見てみたらインスタンスメソッド一つもないし、example コード の PG::Connection.open も定義されてない。
ここでハマってしまったが、結論から言うと全て ext/ 以下の C 言語のコードで定義されていた。
実装的には ext/pg_connection.c にあった。
まず .open 含むいくつかの特異メソッドは .new の alias だった。
alias 張るのに使われてる SINGLETON_ALIAS は自前で定義したマクロだった。(内部的にはrb_define_alias 使ってる)
alias 張られてるクラスの rb_cPGconn が PG::Connection なのではと思って読んでたら、ビンゴ。
rb_cPGconn 変数代入してるとこ 見ると、 rb_define_class_under で rb_mPG に所属する定数として定義されていて、rb_mPG 変数代入してるとこ(C 拡張の初期化時に呼ばれる)を見ると、rb_define_module でモジュール PG として定義されていた。
で initialize の処理は rb_define_method で pgconn_init として定義されていた。
pgconn_init これが読みたかったんだよう 😭(ドキュメントもバッチリ書いてある…)
とりあえずいろいろやって conn オブジェクトを返してるね。(ここまで読んで満足してる)
conn.exec
ここ で pgconn_async_exec として定義されてる。
pgconn_async_exec これが読みたかry
大事そうな処理としては postgres にクエリ投げてるっぽい pgconn_send_query と非同期に結果取得してるっぽい pgconn_get_last_result かな。
pgconn_get_last_result で PG::Result が返る。(PG::Result の定義は ここ っぽいけど力尽きました)
所感
- 途中から PostgreSQL とか pg gem とかどうでもよくなってた
- ruby の C API の読み方がちょっと分かって良かった