:limit, , , , , , , , , , , " />

Rails Migrations, MySQL, unsigned integers, primary keys, and a lot of fun times

April 7, 2009
Customize your primary keys. Pic by Kerri on flickr.

Customize your primary keys. Pic by Kerri on flickr.

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
Rails ignores signed! pic by alex suarez flikr

Rails ignores signs! pic by alex suarez flikr

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|#


Do be careful to use the same numeric type for the foreign key columns as the referenced id column.

add_column  :o ther_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