犯人はMysqlAdapter
というわけで何でそんな理不尽なことが起きるのかを突き止めました。ActiveRecord::ConnectionAdapters::MysqlAdapterのnative_database_typesメソッドの中身。
def native_database_types #:nodoc { :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY", :string => { :name => "varchar", :limit => 255 }, :text => { :name => "text" }, :integer => { :name => "int", :limit => 11 }, :float => { :name => "float" }, :datetime => { :name => "datetime" }, :timestamp => { :name => "datetime" }, :time => { :name => "time" }, :date => { :name => "date" }, :binary => { :name => "blob" }, :boolean => { :name => "tinyint", :limit => 1 } } end
という風になっているので、primary_keyに指定されたカラムは嫌でもint(11)になってしまいます。
ただし、これは、set_primary_key/set_primary_keysを使って複合キーでないキーを指定した場合の話です。varcharのカラムひとつをPKに使用とした場合とか。複合キーの場合は、まだ未確認だけど、昨日紹介したcomposite_migrationsが解決してくれちゃってるっぽいっす。
で、とりあえず回避する方法として、composite_migrationsプラグインのcomposite_migrations.rbを以下のように変更してみたら期待していたCREATE TABLE文が作成されるようになりました。
ActiveRecord::ConnectionAdapters::ColumnDefinition.class_eval <<-'EOF' def to_sql if name.is_a? Array column_sql = "PRIMARY KEY (#{name.join(',')})" elsif type == :primary_key column_sql = "PRIMARY KEY (#{name})" else column_sql = "#{base.quote_column_name(name)} #{type_to_sql(type.to_sym, limit)}" add_column_options!(column_sql, :null => null, :default => default) end column_sql end EOF ActiveRecord::ConnectionAdapters::ColumnDefinition.send(:alias_method, :to_s, :to_sql) ActiveRecord::ConnectionAdapters::SchemaStatements.class_eval do def create_table_yield_before_pk(name, options = {}) table_definition = ActiveRecord::ConnectionAdapters::TableDefinition.new(self) yield table_definition table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false if options[:force] drop_table(name) rescue nil end create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE " create_sql << "#{name} (" create_sql << table_definition.to_sql create_sql << ") #{options[:options]}" execute create_sql end end ActiveRecord::ConnectionAdapters::SchemaStatements.send(:alias_method, :create_table_yield_after_pk, :create_table) ActiveRecord::ConnectionAdapters::SchemaStatements.send(:alias_method, :create_table, :create_table_yield_before_pk) ActiveRecord::ConnectionAdapters::TableDefinition.class_eval do def primary_key_for_string(name) col = self[name] return primary_key_int(name) if col.nil? || name.is_a?(Array) || (col.type != :string) column = ActiveRecord::ConnectionAdapters::ColumnDefinition.new(@base, name, :primary_key) @columns << column unless @columns.include? column self end end ActiveRecord::ConnectionAdapters::TableDefinition.send(:alias_method, :primary_key_int, :primary_key) ActiveRecord::ConnectionAdapters::TableDefinition.send(:alias_method, :primary_key, :primary_key_for_string)
コードが汚いけど、とりあえずこれでマイグレーションはOK。
でも何かおかしいんですよ。varchar1つだけのPKのモデルが。