Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

SQLite on Railsシリーズ(03)SQLite拡張機能を読み込む(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

参考: Rails 8はSQLiteで大幅に強化された「個人が扱えるフレームワーク」(翻訳)|YassLab 株式会社

日本語タイトルは内容に即したものにしました。

SQLite on Railsシリーズ(03)SQLite拡張機能を読み込む(翻訳)

今回もRuby on Railsアプリケーションを強化するためにSQLiteを強化します。本記事では、SQLiteデータベースに拡張機能を読み込む方法について詳しく説明します。


個人的には、基本的にSQLiteの機能は完全であると思っていますが、SQLiteでサポートされていない特定のデータベースニーズが生じる場合もあります。ありがたいことに、SQLiteは豊富な拡張機能エコシステムを提供しています。パッケージマネージャ(非公式)---sqlpkg標準ライブラリ(非公式)---sqlean、およびAlex Garciaによるさまざまな拡張機能の豊富なコレクションが揃っています。SQLite 拡張機能のインストールに関する一般的な概要については以下の記事をどうぞ。

参考: How to install an SQLite extension

しかし私たちが欲しいのは、RailsアプリケーションにSQLite拡張機能を手軽にインストールして読み込める方法です。残念ながら、現時点のsqlpkgsqleanの拡張機能コレクションはRuby gemリリースを提供していません。
しかしありがたいことに、Alex Garciaは各拡張機能をRuby gemとしてリリースしています。Alexの拡張機能はすべてRubyGemsのプロファイルで見つけられます。これらの拡張機能の1つをインストールして読み込む手軽な方法を見ていくことにしましょう。

これらはRuby gemなので、インストールは簡単です。bundle add 拡張機能名を実行するだけです。読み込みはもっと込み入っています。

拡張機能を読み込む前に、まずSQLiteデータベースの拡張機能読み込みを有効にしておく必要があります。Ruby用のsqlite3-rubyアダプタは、そのための#enable_load_extensionメソッドを提供します。
Alex Garciaの拡張機能では、拡張機能を読み込むRuby拡張機能クラスに.loadメソッドを提供します。したがって、RubyでSQLiteの拡張機能を読み込むには、次の操作をすべて実行する必要があります。

@raw_connection.enable_load_extension(true)
SqliteExtension.load(@raw_connection)
@raw_connection.enable_load_extension(false)

ただし、開発エクスペリエンスを損なわない形でRailsを強化したいと思います。では、この機能をエレガントに公開するにはどうすればよいでしょうか?ありがたいことに、前回の記事/config/database.ymlファイルに設定されたオプションを使ってデータベースを設定するためのフックを提供するSQLite3アダプタの機能強化を既に導入してあります。

拡張機能名の配列を受け取るextensionsセクションのサポートも追加できます。次に、これらの拡張機能名を反復処理して読み込むように以下のようにconfigure_connectionメソッドに追加できます。

module RailsExt
  module SQLite3Adapter
    def configure_connection
      # ...

      @raw_connection.enable_load_extension(true)
      @config[:extensions].each do |extension_name|
        require extension_name
        extension_classname = extension_name.camelize
        extension_class = extension_classname.constantize
        extension_class.load(@raw_connection)
      rescue LoadError
        Rails.logger.error("Failed to find the SQLite extension gem: #{extension_name}. Skipping...")
      rescue NameError
        Rails.logger.error("Failed to find the SQLite extension class: #{extension_classname}. Skipping...")
      end
      @raw_connection.enable_load_extension(false)
    end
  end
end

bundle add {拡張機能名}コマンドを実行したら、/config/database.ymlファイルのextensionsセクションに拡張機能を追加すればおしまいです。

RailsExt::SQLite3Adapterが残りの処理を行い、起こりうるエラーにも対処します。つまり、次のようなdefaultセクションを作成することで、ULIDをサポートするために拡張機能を読み込めます。

default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  # BUSY例外をスローする直前にコネクションを試行する
  retries: 1000
  extensions:
    - sqlite_ulid

この方法でSQLite拡張機能を読み込む場合の嬉しい点は、拡張機能が(Gemfileで)明示的にインストールされることと、拡張子が(database.ymlファイル内で)明示的に読み込まれることと、SQLiteアダプタの既存の拡張機能の上に自然に構築されることです。全体として、拡張されたアダプターは プラグマ構成と拡張機能の読み込みをサポートするようになりました。
さらに、データベース設定のおかげでGitブランチを切り替えるとデータベースも切り替わる方法が強化されます。

これにより、ローカル開発を強力にする豊富な機能セットが提供されます。

次回の記事では、Litestream をインストールして設定し、productionデータベースでポイントインタイムのバックアップとリカバリーを実現する方法について詳しく説明します。どうぞお楽しみに。

原注

本記事で行った最終的な実装についてはGistで参照できます。

関連記事

SQLite on Railsシリーズ(01)Gitブランチごとにデータベースを切り替える(翻訳)

SQLite on Railsシリーズ(02)SQLiteをチューニングで強化する(翻訳)

Rails 7.1: SQLite3アダプタのデフォルトコネクション設定が最適化された(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。