migrateでのintegerの扱い

2009年はエコの年なので(いま決めました)、もったいないを重点的に追求していきます。まず手始めに、DB(MySQL)で安直に int(11)を使っているケースを弾劾します。

Rails2.1.0の場合

class CreateTests < ActiveRecord::Migration
  def self.up
    create_table "tests", :force => true do |t|
      t.integer :int0,  :limit => 0
      t.integer :int3,  :limit => 3
      t.integer :int4,  :limit => 4
      t.integer :int8,  :limit => 8
      t.integer :int9,  :limit => 9
      t.integer :int20, :limit => 20
      t.integer :int21, :limit => 21
    end
  end

これを『rake:migrete:up VERSION=14桁の日付』で実施すると Testsテーブルにはこんなフィールドが作られます。

  int0  smallint(6)
  int3  smallint(3)
  int4  int(4)
  int8  int(8)
  int9  bigint(9)
  int20 bigint(20)
  int21 int(11)
  case limit
  when 0..3
    "smallint(#{limit})"
  when 4..8
    "int(#{limit})"
  when 9..20
    "bigint(#{limit})"
  else
    'int(11)'
  end
activerecord-2.1.0/lib/active_record/connection_adapters/mysql_adapter.rb

limitが 0〜3は smallint、4〜8は int、9〜20は bigint、それ以外(21以上)は int(11)にしています。limit=0の場合の smallint(0)は MySQLでは通らないようですね。smallint(6)になっていますね。

Rails2.2.2の場合

class CreateTests < ActiveRecord::Migration
  def self.up
    create_table "tests", :force => true do |t|
      t.integer :int0,  :limit => 0
      t.integer :int1,  :limit => 1
      t.integer :int2,  :limit => 2
      t.integer :int3,  :limit => 3
      t.integer :int4,  :limit => 4
      t.integer :int5,  :limit => 5
      t.integer :int8,  :limit => 8
      t.integer :int9,  :limit => 9
      t.integer :int11, :limit => 11
      t.integer :int20, :limit => 20
      t.integer :int21, :limit => 21
    end
  end

これを『rake:migrete:up VERSION=14桁の日付』で実施すると Testsテーブルにはこんなフィールドが作られます。

  int0  int(11)
  int1  tinyint(4)
  int2  smallint(6)
  int3  mediumint(9)
  int4  int(11)
  int5  bigint(20)
  int8  bigint(20)
  int9  int(11)
  int11 int(11)
  int20 int(11)
  int21 int(11)
  case limit
  when 1; 'tinyint'
  when 2; 'smallint'
  when 3; 'mediumint'
  when nil, 4, 11; 'int(11)'  # compatibility with MySQL default
  when 5..8; 'bigint'
  else raise(ActiveRecordError, "No integer type has byte size #{limit}")
  end
activerecord-2.2.2/lib/active_record/connection_adapters/mysql_adapter.rb

limitが 1は tinyint、2は smallint、3は mediumint、4と11とnil(指定なし)は int(11)、5〜8は bigint、それ以外はエラーにしているはずですが、上の実行結果を見るとエラーのケースは raiseしないでデフォルト値=int(11)をセットするみたいです。(raiseで上がった先までソースを読んでいないのでいい加減です)

まとめ

こんなところが 2.1.0と 2.2.2で大きく変わっているとは思いませんでした。Rails2.2.2で節約するつもりで limit=5とか指定して、実は bigint(20)でとられていたという罠にはまらないよう要注意です。

参考