Rails Migrations, MySQL, unsigned integers, primary keys, and a lot of fun times
Although the standard MySQL type for id columns is an unsigned int, Rails has no support for them and uses regular signed int(11) as the default for primary keys. In addition, most of my tables are less than 16 million rows (max rows for an unsigned mediumint) and so it saves space to use smaller sized numeric types such as smallint or mediumint to save space.
Inspired by Rob Anderton’s patch which was dismissed as an uncommon requirement, I built yet another plugin to allow customization of primary keys, support unsigned integers and allow users to specify column width for numeric types.
script/plugin install http://github.com/blythedunham/mysql_migration_optimizer/tree/master
Specifying Column Width and Unsigned Integers
The MySQL connection adapter was ignoring the :precision and :scale options and so I just loaded em up to allow unsigned numeric types and allow column width to be specified which was supported on postgresql but not MySQL.
add_column :giraffe, :beer_count, :integer, :limit => 2, :precision => :unsigned, :scale => 4 #SQL: ALTER TABLE `giraffe` ADD `beer_count` smallint(4) UNSIGNED
- :limit determines the numeric type. Rails introduced smart integer columns in 2.1 which more or less translates to the number of bytes needed for storage. Here’s a little map taken from the adapter code. Do be careful, however, as some versions of Rails (2.1) break foreign keys.
:limit Numeric Type Column Size 1 tinyint 1 byte 2 smallint 2 bytes 3 mediumint 3 bytes nil, 4, 11 int(11) 4 bytes 5 to 8 bigint 8 bytes - :scale: column width explained here by Matt Thommes number in parenthesis (4) .
- :precision : specify :signed or :unsigned. Default will be :unsigned unless you specify set MySqlMigrationOptimizer.default_sign = :signed
in your enviroment.rb.
Customizing Primary Keys
create_table "animal", :force => true, :primary_column => {:type=>:integer, :limit => 3, :scale=>5} do |t| end #SQL: CREATE TABLE `animal` ( `id` mediumint(5) UNSIGNED NOT NULL auto_increment PRIMARY KEY) ENGINE=InnoDB
If you really want to get crazy you can specify some other type. Here’s the rope, I’ll leave the room if you want to hang yourself.
# Specify a string column as the primary key create_table "blah", :force => true, :primary_column => {:type=>:string, :limit=>25, :auto_increment=>false} do |t|# end #SQL: CREATE TABLE `blah` (`id` varchar(25) NOT NULL PRIMARY KEY) ENGINE=InnoDB
Do be careful to use the same numeric type for the foreign key columns as the referenced id column.
add_columnther_table, :blah_id, :integer, :limit => 3, :scale=>5
If you have already generated a lot of tables and do not want to change all the old column types, specify the default to be :signed in config/environment.rb. Any new columns should then specify :precision => :unsigned
MySqlMigrationOptimizer.default_sign = :signed
The schemadumper has been amended to work correctly.




Leave a Reply