Edited at

ActiveRecord 4.1+MySQLで任意のカラムをPKにする

More than 3 years have passed since last update.

ActiveRecord 4.1+MySQLで、特別な拡張を入れなくても任意の名前・型のカラムをPKにできたのでメモしておきます。


書き方


名前だけ変える

primary_key: 'my_id'

create_table "items", primary_key: 'my_id', force: true do |t|

...
end


型だけ変える

id: 'bigint primary key auto_increment'

create_table "items", id: 'bigint primary key auto_increment', force: true do |t|

...
end


名前・型を変える

primary_key: 'my_id', id: 'bigint primary key auto_increment'

create_table "items", primary_key: 'my_id', id: 'bigint primary key auto_increment', force: true do |t|

...
end


ダンプ

ダンプすると名前の情報は出力されますが型の情報(id: xxx)は出力されません。

create_table "items", primary_key: "my_id", force: true do |t|

...
end


解説

create_tableでPKの定義を生成しているのが以下の箇所:

schema_statements.rb#L190

unless options[:id] == false

pk = options.fetch(:primary_key) {
Base.get_primary_key table_name.to_s.singularize
}

td.primary_key pk, options.fetch(:id, :primary_key), options
end

:idfalseでないとき、:primary_keyの値を名前、:idの値を型としてPKを定義しています。

MySQLのネイティブな型の定義は以下の通り:

abstract_mysql_adapter.rb#L152

NATIVE_DATABASE_TYPES = {

:primary_key => "int(11) auto_increment PRIMARY KEY",
:string => { :name => "varchar", :limit => 255 },
:text => { :name => "text" },
:integer => { :name => "int", :limit => 4 },
:float => { :name => "float" },
:decimal => { :name => "decimal" },
:datetime => { :name => "datetime" },
:timestamp => { :name => "datetime" },
:time => { :name => "time" },
:date => { :name => "date" },
:binary => { :name => "blob" },
:boolean => { :name => "tinyint", :limit => 1 }
}

実際にSQLを発行するときはtype_to_sqlメソッドでネイティブな型に変換していますが

if native = native_database_types[type.to_sym]

...
else
type.to_s
end

native_database_typesにないものはそのままSQLの型として使われるので、:idに「任意の型」+primary keyを設定すると、任意の型でPKを作成できます。