PostgreSQL+RailsでテーブルのIDにBIGSERIALを使う

こんにちは、河野です。

RailsとPostgresqlの組み合わせでmigrationを通してテーブルを作成するとき、デフォルトでは、IDのデータ型はSERIAL(INT)になります。

特にデータ量が多くない場合には問題ないのですが、データ量多くなったときにINTの上限値(2147483647)を超えてしまうとデータがインサートできなくなり、大変な事態になります。

実は、先日あるプロジェクトで、テーブルのIDがINTの上限を超えてエラーになってしましました。IDをBIGINTに変更することで対応できたので良かったのですが、そもそもテーブル作成時にBIGINTにしておけば問題は発生しませんでした。

では、どうやったらテーブル作成時にBIGINT(PostgreSQLなのでBIGSERIAL)を使用することができるでしょうか。

create_tableのオプションでIDの型を指定する

create_tableのオプションに:idというのがあるので、それを:bigserialにします。

すごいシンプル!

確認

rakeを実行して、

psqlで確認します。

ちゃんと、historiesのidの型がbigintになっていますね!

コードを見ていて気づいた

create_tableの挙動はどうなっているのか、確認してみました。
ActiveRecord::ConnectionAdapters::SchemaStatements

options[:id]がfalseではない、かつ、options[:as]がfalse(nil)になっているときに、create_table_definitionで取得した、TableDefinitionのprimary_keyというメソッドを呼び出していることがわかります。
では、primary_keyはどういうメソッドなのでしょうか。(というか、primary_keyっていうメソッドあるんですね。)

ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnMethods::TableDefinition

uuidに関しての記述があって、superを呼び出していますね。

ActiveRecord::ConnectionAdapters::TableDefinition

columnメソッドのラッパーになっています。
typeが :primary_keyになっていて、optionに :primary_key => true を追加しているだけですね。
つまりtypeのところに、:bigserialが渡せたらなんとかなりそうです。

改めてcreate_tableの挙動を確認すると、

options.fetch(:id, :primary_key) の戻り値が、columnメソッドのtypeに渡るようになっています。

では、:bigserialがホントに使えるのか。PostgreSQLAdapter の NATIVE_DATABASE_TYPES を見てみました。

ActiveRecord::ConnectionAdapters::PostgreSQLAdapter

問題ないようですね。

まとめ

ということで、最初に記載した通りですが、以下のようにするとBIGSERIALが使えるようになります。

または、こういう書き方も大丈夫なようですね。

ちなみに。

create_table (ActiveRecord::ConnectionAdapters::SchemaStatements) - APIdock
↑こちらのcreate_tableのリファレンスを見ると、

Whether to automatically add a primary key column. Defaults to true. Join tables for has_and_belongs_to_many should set it to false.

と書いてあって、いかにもtrueかfalseしか設定できないような感じがします。

でも、実際には型の指定ができる、ということですね。なんとか出来ないかと思ってコードを見たのですが、大変勉強になりました。コード見るの大事ですね。

Comments are closed, but you can leave a trackback: Trackback URL.