sqlite3-ruby1.2.1にsqlite3_load_extension()がない!
あれこれ見ていて気がついた。現状では、C言語で正規表現処理を書いてもRubyからSQLiteに組み込めない、ってことかな。sqlite3-ruby経由だと".load"も"select load_extencion()"もエラーになるわけで。
よって選択肢は2つ。
DBを改造・ビルドして使う、というのは荒技すぎるかも。まずはRubyライブラリ側の修正でなんとかしてみませう。
sqlite3-rubyを改造する:作業メモ:あとでまとめる。
とりあえずOSXオンリーで、とにかく動くだけの改造。
gemでインストールされた部分(/opt/local/lib/ruby/gems/1.8/gems/sqlite3-ruby-1.2.1/)を覗いてみる。すると
/opt/local/lib/ruby/gems/1.8/gems/sqlite3-ruby-1.2.1/ext/sqlite3_api/
sqlite3_api.i
sqlite3_api_wrap.c
がある。.iってインターフェース定義だよね。ビルドでは不要かな・・・えーい面倒だ、両方とも切れ切れ、切り捨てい!(最低)
sqlite3_api.i (create_functionの上に差し込み)
int sqlite3_load_extension(sqlite3*,const char*name,char*entrypoint,char**errmsg);
sqlite3_api_wrap.c (create_functionの上に差し込み)
/* ちょう適当。エラーメッセージ文字列を無視。 */ static VALUE _wrap_sqlite3_load_extension(int argc, VALUE *argv, VALUE self) { sqlite3 *arg1 = (sqlite3 *) 0 ; char *arg2 = (char *) 0 ; char *arg3 = (char *) 0 ; char *arg4 = (char *) NULL ; int result; VALUE vresult = Qnil; if ((argc < 4) || (argc > 4)) rb_raise(rb_eArgError, "wrong # of arguments(%d for 4)",argc); SWIG_ConvertPtr(argv[0], (void **) &arg1, SWIGTYPE_p_sqlite3, 0); arg2 = StringValuePtr(argv[1]); arg3 = StringValuePtr(argv[2]); result = (int)sqlite3_load_extension(arg1,(char const *)arg2,arg3,NULL); vresult = INT2NUM(result); return vresult; }
このように書き換えて、
$ sudo make clean $ sudo make all
これで sqlite3_api.bundle がビルドされるので、sqlite3-ruby-1.2.1/lib にコピー。
つぎ、sqlite3-ruby-1.2.1/lib/sqlite3 で
$ mate .
と打ち込んでTextMateで開き、プロジェクト検索で「create_function」定義を含むファイルを抽出。
sqlite3-ruby-1.2.1/lib/sqlite3/database.rb
sqlite3-ruby-1.2.1/lib/sqlite3/driver/dl/driver.rb
sqlite3-ruby-1.2.1/lib/sqlite3/driver/dl/api.rb
sqlite3-ruby-1.2.1/lib/sqlite3/driver/native/driver.rb
の4本。かってに改蔵。
database.rb (create_functionの上に差し込み)
def load_extension( name ) puts "begin load_extension(database.rb)..." result = @driver.load_extension( @handle, name, nil, nil ) puts "end load_extension(database.rb): #{result}" # Error.check( result, self ) self end※エラーチェックを呼ぶと落ちるのでコメントアウト。
dirver/dl/api.rb (create_functionの上に差し込み)
extern "int sqlite3_load_extension(db,const char*,const char*,const char**)"
dirver/dl/driver.rb (create_functionの上に差し込み)
def load_extension( db, name, entrypoint, msg ) puts "begin load_extension(dl/driver.rb) [#{name}]..." result = API.sqlite3_load_extension( db, name, '', '' ) puts "end load_extension(dl/driver.rb): #{result}" return result end
driver/nateve/driver.rb (create_functionの上に差し込み)
def load_extension( db, name, entrypoint, msg ) puts "begin load_extension(native/driver.rb) [#{name}]..." result = API.sqlite3_load_extension( db, name, '', '' ) puts "end load_extension(native/driver.rb): #{result}" return result end
改造は以上。
ためしにRailsから呼んでみる。ApplicationControllerに強引にテストコードを追加。
app/controllers/application.rb
class ApplicationController < ActionController::Base # ... db = ActiveRecord::Base.connection.instance_variable_get(:@connection) db.load_extension('a') end
として、適当にページを表示してコンソールを見ると、
begin load_extension(database.rb)... begin load_extension(native/driver.rb) [a]... end load_extension(native/driver.rb): 1 end load_extension(database.rb): 1
と出た。load_extension を呼び、失敗値を返しているように見える。とりあえず動作した。
あとは実際に機能する拡張モジュールを書いて試してみれば良い。が、疲れたので本日はここまで。