Note

3年後の自分のために書いています

gem 'pg' コードリーディング

なぜだか急に 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_cPGconnPG::Connection なのではと思って読んでたら、ビンゴ。

rb_cPGconn 変数代入してるとこ 見ると、 rb_define_class_underrb_mPG に所属する定数として定義されていて、rb_mPG 変数代入してるとこ(C 拡張の初期化時に呼ばれる)を見ると、rb_define_module でモジュール PG として定義されていた。

で initialize の処理は rb_define_methodpgconn_init として定義されていた。

pgconn_init これが読みたかったんだよう 😭(ドキュメントもバッチリ書いてある…)

とりあえずいろいろやって conn オブジェクトを返してるね。(ここまで読んで満足してる)

conn.exec

ここpgconn_async_exec として定義されてる。

pgconn_async_exec これが読みたかry

大事そうな処理としては postgres にクエリ投げてるっぽい pgconn_send_query と非同期に結果取得してるっぽい pgconn_get_last_result かな。

pgconn_get_last_resultPG::Result が返る。(PG::Result の定義は ここ っぽいけど力尽きました)

所感

  • 途中から PostgreSQL とか pg gem とかどうでもよくなってた
  • ruby の C API の読み方がちょっと分かって良かった

参考