<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: Sanj's Code Snippets</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Fri, 25 Jul 2008 08:11:40 GMT</pubDate>
    <description>DZone Snippets: Sanj's Code Snippets</description>
    <item>
      <title>Using 64 bit primary keys with migrations</title>
      <link>http://snippets.dzone.com/posts/show/4422</link>
      <description>I've got an application which may need more IDs than the standard 32 bit ones typically provide.  MySQL has a BIGINT variable type, but it is a little tricky to convince Rails and Migrations to use it.  The instructions are for MySQL, but should be easily transferable to other DBs.&lt;br /&gt;&lt;br /&gt;1.  Monkeypatch ActiveRecord::ConnectionAdapters::MysqlAdapter#native_database_types by appending it to config/environment.rb and add two new types.  I've named them int64 and int64_pk&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;class ActiveRecord::ConnectionAdapters::MysqlAdapter&lt;br /&gt;  def native_database_types #:nodoc:&lt;br /&gt;    {&lt;br /&gt;      :primary_key =&gt; "int(11) DEFAULT NULL auto_increment PRIMARY KEY",&lt;br /&gt;      :int64_pk    =&gt; "bigint DEFAULT NULL auto_increment PRIMARY KEY",&lt;br /&gt;      :int64       =&gt; { :name =&gt; "bigint" },&lt;br /&gt;      :string      =&gt; { :name =&gt; "varchar", :limit =&gt; 255 },&lt;br /&gt;      :text        =&gt; { :name =&gt; "text" },&lt;br /&gt;      :integer     =&gt; { :name =&gt; "int", :limit =&gt; 11 },&lt;br /&gt;      :float       =&gt; { :name =&gt; "float" },&lt;br /&gt;      :decimal     =&gt; { :name =&gt; "decimal" },&lt;br /&gt;      :datetime    =&gt; { :name =&gt; "datetime" },&lt;br /&gt;      :timestamp   =&gt; { :name =&gt; "datetime" },&lt;br /&gt;      :time        =&gt; { :name =&gt; "time" },&lt;br /&gt;      :date        =&gt; { :name =&gt; "date" },&lt;br /&gt;      :binary      =&gt; { :name =&gt; "blob" },&lt;br /&gt;      :boolean     =&gt; { :name =&gt; "tinyint", :limit =&gt; 1 },&lt;br /&gt;    }&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;2.  In the table creation migration, create the table WITHOUT a primary key column, and then add the column manually, referencing int64_pk above:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;create_table :slices, :id =&gt; false do |t|&lt;br /&gt;       t.column :cheese_id, :integer&lt;br /&gt;       t.column :plant, :string&lt;br /&gt;       t.column :date, :datetime&lt;br /&gt;     end&lt;br /&gt;    add_column :events, :id, :int64_pk&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;3.  In tables referencing this one, mark the foreign_id fields as int64:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;create_table :fondues do |t|&lt;br /&gt;      t.column :cheese_id, :int64&lt;br /&gt;      t.column :party_id, :integer&lt;br /&gt;      t.column :kirsch_id, :integer&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;4.  It turns out that the c-based mysql bindings do NOT do let you set primary keys via the sequence because it coerces the&lt;br /&gt;insert_id into 32 bits at mysql.c:532.  There are two way to fix this:&lt;br /&gt;&lt;br /&gt;a.  Use the pure-ruby gem (ie, remove the c-gem)&lt;br /&gt;&lt;br /&gt;b.  Patch and rebuild the c-gem by changing line 352 (in version 2.7):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;   return INT2NUM(mysql_insert_id(GetHandler(obj)));&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Extending this to use LL2NUM solves the issue:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;   return LL2NUM(mysql_insert_id(GetHandler(obj)));&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;5.  One last thing, in environment.rb:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;  # Use SQL instead of Active Record's schema dumper when creating the test database.&lt;br /&gt;  # This is necessary if your schema can't be completely dumped by the schema dumper,&lt;br /&gt;  # like if you have constraints or database-specific column types&lt;br /&gt;  config.active_record.schema_format = :sql&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;We need to deal with sql, rather than ruby, schemas.&lt;br /&gt;&lt;br /&gt;----------------------&lt;br /&gt;&lt;br /&gt;Note that I've decided that ONLY the cheese table needs 64bit IDs -- other references are still integers.  If you wanted ALL primary keys to be 64 bits, you could have the monkeypatch look like:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&lt;br /&gt;class ActiveRecord::ConnectionAdapters::MysqlAdapter&lt;br /&gt;  def native_database_types #:nodoc:&lt;br /&gt;    {&lt;br /&gt;      :primary_key    =&gt; "bigint DEFAULT NULL auto_increment PRIMARY KEY",&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;</description>
      <pubDate>Wed, 15 Aug 2007 13:23:00 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/4422</guid>
      <author>sanj (Sanjay Vakil)</author>
    </item>
  </channel>
</rss>
