Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #4353: bigint_support_in_migrations.3.diff

File bigint_support_in_migrations.3.diff, 11.9 kB (added by shammond@northpub.com, 2 years ago)

Improved implementation that doesn't break other adapters.

  • test/migration_test.rb

    old new  
    7272      Person.connection.drop_table :testings rescue nil 
    7373    end 
    7474 
     75    def test_create_table_with_big_id 
     76      Person.connection.create_table :testings, :use_big_id => true do |t| 
     77        t.column :name, :string 
     78      end 
     79 
     80      columns = Person.connection.columns(:testings) 
     81      assert columns.find { |c| c.name == 'id' and c.type == :integer and c.limit == 8} if current_adapter?(:MysqlAdapter) 
     82      assert columns.find { |c| c.name == 'id' and c.type == :integer and c.limit == 8} if current_adapter?(:SqliteAdapter) 
     83    ensure 
     84      Person.connection.drop_table :testings rescue nil       
     85    end 
     86         
    7587    def test_create_table_with_not_null_column 
    7688      Person.connection.create_table :testings do |t| 
    7789        t.column :foo, :string, :null => false 
     
    178190      assert_equal TrueClass, bob.male?.class 
    179191    end 
    180192 
     193    def test_integer_limits 
     194      Person.delete_all 
     195      Person.connection.add_column 'people', 'tiny', :integer, :limit => 1 
     196      Person.connection.add_column 'people', 'small', :integer, :limit => 2 
     197      Person.connection.add_column 'people', 'medium', :integer, :limit => 3 
     198      Person.connection.add_column 'people', 'normal', :integer, :limit => 4 
     199      Person.connection.add_column 'people', 'five_bytes', :integer, :limit => 5 
     200      Person.connection.add_column 'people', 'big', :integer, :limit => 8 
     201      Person.connection.add_column 'people', 'ten_bytes', :integer, :limit => 10 
     202      Person.connection.add_column 'people', 'short_string', :string, :limit => 9 
     203 
     204      assert_nothing_raised { Person.create :tiny => 123, :small => 1234,  
     205        :medium => 123456, :normal => 123456789, :five_bytes => 123456789123,  
     206        :big => 123456789123456789, :ten_bytes => 1208925819614629174706176, 
     207        :short_string => '123456789'} 
     208      bob = Person.find(:first) 
     209         
     210      assert_equal 123, bob.tiny 
     211      assert_equal 1234, bob.small 
     212      assert_equal 123456, bob.medium 
     213      assert_equal 123456789, bob.normal 
     214      assert_equal 123456789123, bob.five_bytes 
     215      assert_equal 123456789123456789, bob.big 
     216      # number too big for mysql and should be truncated 
     217      assert_not_equal 1208925819614629174706176, bob.ten_bytes if current_adapter?(:MysqlAdapter) 
     218      assert_equal '123456789', bob.short_string 
     219       
     220      if current_adapter?(:MysqlAdapter) 
     221        person_columns = Person.columns_hash 
     222        assert_equal 1, person_columns['tiny'].limit 
     223        assert_equal 2, person_columns['small'].limit 
     224        assert_equal 3, person_columns['medium'].limit 
     225        assert_equal 4, person_columns['normal'].limit 
     226        assert_equal 8, person_columns['five_bytes'].limit # no five-byte type in mysql, use 8-bytes 
     227        assert_equal 8, person_columns['big'].limit 
     228        assert_equal 8, person_columns['ten_bytes'].limit 
     229        assert_equal 9, person_columns['short_string'].limit 
     230      end 
     231    end 
     232         
    181233    def test_add_remove_single_field_using_string_arguments 
    182234      assert !Person.column_methods_hash.include?(:last_name) 
    183235 
     
    282334      new_columns = Person.connection.columns(Person.table_name, "#{name} Columns") 
    283335      assert_nil new_columns.find { |c| c.name == 'age' and c.type == :integer } 
    284336      assert new_columns.find { |c| c.name == 'age' and c.type == :string } 
    285     end     
     337    end 
    286338 
    287339    def test_change_column_with_new_default 
    288340      Person.connection.add_column "people", "administrator", :boolean, :default => 1 
     
    294346      assert !Person.new.administrator? 
    295347    end     
    296348 
     349    def test_change_integer_column_limits 
     350      Person.connection.add_column 'people', 'weight', :integer, :limit => 2 
     351      Person.connection.add_column 'people', 'age', :integer, :limit => 4 
     352       
     353      old_columns = Person.connection.columns(Person.table_name, "#{name} Columns") 
     354      assert old_columns.find { |c| c.name == 'weight' and c.type == :integer and c.limit == 2} 
     355      assert old_columns.find { |c| c.name == 'age' and c.type == :integer and c.limit == 4} 
     356 
     357      assert_nothing_raised { Person.connection.change_column 'people', 'weight', :integer, :limit => 4 } 
     358      assert_nothing_raised { Person.connection.change_column 'people', 'age', :integer, :limit => 8 } 
     359       
     360      new_columns = Person.connection.columns(Person.table_name, "#{name} Columns") 
     361      assert_nil new_columns.find { |c| c.name == 'weight' and c.type == :integer and c.limit == 2} 
     362      assert_nil new_columns.find { |c| c.name == 'age' and c.type == :integer and c.limit == 4} 
     363      assert new_columns.find { |c| c.name == 'weight' and c.type == :integer and c.limit == 4 } 
     364      assert new_columns.find { |c| c.name == 'age' and c.type == :integer and c.limit == 8 } 
     365    end 
     366     
    297367    def test_add_table 
    298368      assert !Reminder.table_exists? 
    299369       
  • lib/active_record/connection_adapters/abstract/schema_statements.rb

    old new  
    4545      # The +options+ hash can include the following keys: 
    4646      # [<tt>:id</tt>] 
    4747      #   Set to true or false to add/not add a primary key column 
    48       #   automatically.  Defaults to true. 
     48      # [<tt>:use_big_id</tt>] 
     49      #   Set to true or false to use the largest supported integer type as the 
     50      #   primary key column.  Defaults to false. 
    4951      # [<tt>:primary_key</tt>] 
    5052      #   The name of the primary key, if one is to be added automatically. 
    5153      #   Defaults to +id+. 
     
    7577      #    name varchar(80) 
    7678      #  ) 
    7779      # 
     80      # ====== Use the largest integer type supported as the primary key column 
     81      #  create_table(:objects, :use_big_id => true) do |t| 
     82      #    t.column :name, :string, :limit => 80 
     83      #  end 
     84      # generates (in MySQL): 
     85      #  CREATE TABLE objects ( 
     86      #    id bigint(21) UNSIGNED DEFAULT NULL auto_increment PRIMARY KEY, 
     87      #    name varchar(80) 
     88      #  ) 
     89      # 
    7890      # ====== Do not add a primary key column 
    7991      #  create_table(:categories_suppliers, :id => false) do |t| 
    8092      #    t.column :category_id, :integer 
     
    89101      # See also TableDefinition#column for details on how to create columns. 
    90102      def create_table(name, options = {}) 
    91103        table_definition = TableDefinition.new(self) 
    92         table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false 
     104        table_definition.primary_key(options[:primary_key] || "id", options[:use_big_id] || false) unless options[:id] == false 
    93105 
    94106        yield table_definition 
    95107 
     
    254266 
    255267 
    256268      def type_to_sql(type, limit = nil) #:nodoc: 
    257         native = native_database_types[type] 
     269        unless self.class.method_defined? :native_database_type 
     270          native = native_database_types[type] 
     271        else 
     272          # If database defines unusual type and/or limits (like MySQL's integer types) 
     273          # define a method native_database_type and return the correct type 
     274          # and limit 
     275          native = native_database_type(type, limit) 
     276          limit = nil # native_database_type is responsible for returning the correct limit 
     277        end 
    258278        limit ||= native[:limit] 
    259279        column_type_sql = native[:name] 
    260280        column_type_sql << "(#{limit})" if limit 
  • lib/active_record/connection_adapters/abstract/schema_definitions.rb

    old new  
    194194 
    195195      # Appends a primary key definition to the table definition. 
    196196      # Can be called multiple times, but this is probably not a good idea. 
    197       def primary_key(name) 
    198         column(name, native[:primary_key]) 
     197      def primary_key(name, use_big_id = false) 
     198        key_type = ( use_big_id && !native[:big_primary_key].nil? ) ? :big_primary_key : :primary_key 
     199        column(name, native[key_type]) 
    199200      end 
    200201 
    201202      # Returns a ColumnDefinition for the column with name +name+. 
     
    212213      # 
    213214      # Available options are (none of these exists by default): 
    214215      # * <tt>:limit</tt>: 
    215       #   Requests a maximum column length (<tt>:string</tt>, <tt>:text</tt>, 
    216       #   <tt>:binary</tt> or <tt>:integer</tt> columns only) 
     216      #   Requests a maximum column length in bytes (<tt>:string</tt>, <tt>:text</tt>, 
     217      #   <tt>:binary</tt> or <tt>:integer</tt> columns only). In MySQL this option 
     218      #   determines the integer type that will be used, so <tt>:limit => 8</tt> 
     219      #   will produce a column of type <tt>bigint</tt> and <tt>:limit => 3</tt> 
     220      #   will produce a column of type <tt>mediumint</tt>. 
    217221      # * <tt>:default</tt>: 
    218222      #   The column's default value.  You cannot explicitely set the default 
    219223      #   value to +NULL+.  Simply leave off this option if you want a +NULL+ 
  • lib/active_record/connection_adapters/mysql_adapter.rb

    old new  
    4646          return :string  if field_type =~ /enum/i 
    4747          super 
    4848        end 
     49 
     50        def extract_limit(sql_type) 
     51          case sql_type 
     52            when /tinyint/ then return 1 
     53            when /smallint/ then return 2 
     54            when /mediumint/ then return 3 
     55            when /bigint/ then return 8 
     56            when /int/ then return 4 
     57          end 
     58          super 
     59        end 
    4960    end 
    5061 
    5162    # The MySQL adapter will work with both Ruby/MySQL, which is a Ruby-based MySQL adapter that comes bundled with Active Record, and with 
     
    99110      def native_database_types #:nodoc 
    100111        { 
    101112          :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY", 
     113          :big_primary_key => "bigint(21) UNSIGNED DEFAULT NULL auto_increment PRIMARY KEY", 
    102114          :string      => { :name => "varchar", :limit => 255 }, 
    103115          :text        => { :name => "text" }, 
     116          :tinyint     => { :name => "tinyint", :limit => 4 }, 
     117          :smallint    => { :name => "smallint", :limit => 6 }, 
     118          :mediumint   => { :name => "mediumint", :limit => 9 }, 
    104119          :integer     => { :name => "int", :limit => 11 }, 
     120          :bigint      => { :name => "bigint", :limit => 21 }, 
    105121          :float       => { :name => "float" }, 
    106122          :datetime    => { :name => "datetime" }, 
    107123          :timestamp   => { :name => "datetime" }, 
     
    112128        } 
    113129      end 
    114130 
     131      def native_database_type(type, limit=nil) 
     132        mysql_integer_types = %w{tinyint smallint mediumint int bigint}         
     133        if type == :integer 
     134          native_type = case limit 
     135                          when nil then native_database_types[:integer] 
     136                          when 1 then native_database_types[:tinyint] 
     137                          when 2  then native_database_types[:smallint] 
     138                          when 3 then native_database_types[:mediumint] 
     139                          when 4 then native_database_types[:integer] 
     140                          else native_database_types[:bigint] 
     141                        end 
     142        else native_type = native_database_types[type] 
     143        end 
     144        type_with_correct_limit = native_type.clone # make a copy so we can change the limit 
     145        type_with_correct_limit[:limit] = limit unless limit.nil? or mysql_integer_types.include? type_with_correct_limit[:name] 
    115146 
     147        type_with_correct_limit 
     148      end 
     149 
    116150      # QUOTING ================================================== 
    117151 
    118152      def quote(value, column = nil)