Fix undesirable RangeError by Type::Integer. Add Type::UnsignedInteger.

This commit is contained in:
Ryuta Kamizono 2014-11-27 10:06:49 +09:00
parent 6961afefd2
commit b61a93b44e
8 changed files with 119 additions and 6 deletions

@ -1,3 +1,7 @@
* Fix undesirable RangeError by Type::Integer. Add Type::UnsignedInteger.
*Ryuta Kamizono*
* Add `foreign_type` option to `has_one` and `has_many` association macros.
This option enables to define the column name of associated object's type for polymorphic associations.

@ -656,14 +656,15 @@ def initialize_type_map(m) # :nodoc:
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
m.register_type %r(^bigint)i, Type::Integer.new(limit: 8)
m.register_type %r(^int)i, Type::Integer.new(limit: 4)
m.register_type %r(^mediumint)i, Type::Integer.new(limit: 3)
m.register_type %r(^smallint)i, Type::Integer.new(limit: 2)
m.register_type %r(^tinyint)i, Type::Integer.new(limit: 1)
m.register_type %r(^float)i, Type::Float.new(limit: 24)
m.register_type %r(^double)i, Type::Float.new(limit: 53)
register_integer_type m, %r(^bigint)i, limit: 8
register_integer_type m, %r(^int)i, limit: 4
register_integer_type m, %r(^mediumint)i, limit: 3
register_integer_type m, %r(^smallint)i, limit: 2
register_integer_type m, %r(^tinyint)i, limit: 1
m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans
m.alias_type %r(set)i, 'varchar'
m.alias_type %r(year)i, 'integer'
@ -676,6 +677,16 @@ def initialize_type_map(m) # :nodoc:
end
end
def register_integer_type(mapping, key, options) # :nodoc:
mapping.register_type(key) do |sql_type|
if /unsigned/i =~ sql_type
Type::UnsignedInteger.new(options)
else
Type::Integer.new(options)
end
end
end
# MySQL is too stupid to create a temporary table for use subquery, so we have
# to give it some prompting in the form of a subsubquery. Ugh!
def subquery_for(key, select)

@ -17,6 +17,7 @@
require 'active_record/type/string'
require 'active_record/type/text'
require 'active_record/type/time'
require 'active_record/type/unsigned_integer'
require 'active_record/type/type_map'
require 'active_record/type/hash_lookup_type_map'

@ -5,7 +5,7 @@ class Integer < Value # :nodoc:
def initialize(*)
super
@range = -max_value...max_value
@range = min_value...max_value
end
def type
@ -46,6 +46,10 @@ def max_value
limit = self.limit || 4
1 << (limit * 8 - 1) # 8 bits per byte with one bit for sign
end
def min_value
-max_value
end
end
end
end

@ -0,0 +1,15 @@
module ActiveRecord
module Type
class UnsignedInteger < Integer # :nodoc:
private
def max_value
super * 2
end
def min_value
0
end
end
end
end

@ -0,0 +1,30 @@
require "cases/helper"
class UnsignedTypeTest < ActiveRecord::TestCase
self.use_transactional_fixtures = false
class UnsignedType < ActiveRecord::Base
end
setup do
@connection = ActiveRecord::Base.connection
@connection.create_table("unsigned_types", force: true) do |t|
t.column :unsigned_integer, "int unsigned"
end
end
teardown do
@connection.drop_table "unsigned_types"
end
test "unsigned int max value is in range" do
assert expected = UnsignedType.create(unsigned_integer: 4294967295)
assert_equal expected, UnsignedType.find_by(unsigned_integer: 4294967295)
end
test "minus value is out of range" do
assert_raise(RangeError) do
UnsignedType.create(unsigned_integer: -10)
end
end
end

@ -0,0 +1,30 @@
require "cases/helper"
class UnsignedTypeTest < ActiveRecord::TestCase
self.use_transactional_fixtures = false
class UnsignedType < ActiveRecord::Base
end
setup do
@connection = ActiveRecord::Base.connection
@connection.create_table("unsigned_types", force: true) do |t|
t.column :unsigned_integer, "int unsigned"
end
end
teardown do
@connection.drop_table "unsigned_types"
end
test "unsigned int max value is in range" do
assert expected = UnsignedType.create(unsigned_integer: 4294967295)
assert_equal expected, UnsignedType.find_by(unsigned_integer: 4294967295)
end
test "minus value is out of range" do
assert_raise(RangeError) do
UnsignedType.create(unsigned_integer: -10)
end
end
end

@ -0,0 +1,18 @@
require "cases/helper"
require "models/company"
module ActiveRecord
module Type
class UnsignedIntegerTest < ActiveRecord::TestCase
test "unsigned int max value is in range" do
assert_equal(4294967295, UnsignedInteger.new.type_cast_from_user("4294967295"))
end
test "minus value is out of range" do
assert_raises(::RangeError) do
UnsignedInteger.new.type_cast_from_user("-1")
end
end
end
end
end