mirror of https://github.com/rails/rails
teaching the mysql adapter how to typecast strings returned from the database
This commit is contained in:
parent
0736e169dc
commit
d08fee3f28
|
@ -298,6 +298,127 @@ module ActiveRecord
|
|||
@connection.insert_id
|
||||
end
|
||||
|
||||
class Result < ActiveRecord::Result
|
||||
def initialize(columns, rows, column_types)
|
||||
super(columns, rows)
|
||||
@column_types = column_types
|
||||
end
|
||||
end
|
||||
|
||||
module Fields
|
||||
class Type
|
||||
def type; end
|
||||
|
||||
def type_cast_for_write(value)
|
||||
value
|
||||
end
|
||||
end
|
||||
|
||||
class Identity < Type
|
||||
def type_cast(value); value; end
|
||||
end
|
||||
|
||||
class Integer < Type
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
||||
value.to_i rescue value ? 1 : 0
|
||||
end
|
||||
end
|
||||
|
||||
class Date < Type
|
||||
def type; :date; end
|
||||
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
||||
# FIXME: probably we can improve this since we know it is mysql
|
||||
# specific
|
||||
ConnectionAdapters::Column.value_to_date value
|
||||
end
|
||||
end
|
||||
|
||||
class DateTime < Type
|
||||
def type; :datetime; end
|
||||
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
||||
# FIXME: probably we can improve this since we know it is mysql
|
||||
# specific
|
||||
ConnectionAdapters::Column.string_to_time value
|
||||
end
|
||||
end
|
||||
|
||||
class Time < Type
|
||||
def type; :time; end
|
||||
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
||||
# FIXME: probably we can improve this since we know it is mysql
|
||||
# specific
|
||||
ConnectionAdapters::Column.string_to_dummy_time value
|
||||
end
|
||||
end
|
||||
|
||||
class Float < Type
|
||||
def type; :float; end
|
||||
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
||||
value.to_f
|
||||
end
|
||||
end
|
||||
|
||||
class Decimal < Type
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
||||
ConnectionAdapters::Column.value_to_decimal value
|
||||
end
|
||||
end
|
||||
|
||||
class Boolean < Type
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
||||
ConnectionAdapters::Column.value_to_boolean value
|
||||
end
|
||||
end
|
||||
|
||||
TYPES = {}
|
||||
|
||||
# Register an MySQL +type_id+ with a typcasting object in
|
||||
# +type+.
|
||||
def self.register_type(type_id, type)
|
||||
TYPES[type_id] = type
|
||||
end
|
||||
|
||||
def self.alias_type(new, old)
|
||||
TYPES[new] = TYPES[old]
|
||||
end
|
||||
|
||||
register_type Mysql::Field::TYPE_TINY, Fields::Boolean.new
|
||||
register_type Mysql::Field::TYPE_LONG, Fields::Integer.new
|
||||
alias_type Mysql::Field::TYPE_LONGLONG, Mysql::Field::TYPE_LONG
|
||||
alias_type Mysql::Field::TYPE_NEWDECIMAL, Mysql::Field::TYPE_LONG
|
||||
|
||||
register_type Mysql::Field::TYPE_VAR_STRING, Fields::Identity.new
|
||||
register_type Mysql::Field::TYPE_BLOB, Fields::Identity.new
|
||||
register_type Mysql::Field::TYPE_DATE, Fields::Date.new
|
||||
register_type Mysql::Field::TYPE_DATETIME, Fields::DateTime.new
|
||||
register_type Mysql::Field::TYPE_TIME, Fields::Time.new
|
||||
register_type Mysql::Field::TYPE_FLOAT, Fields::Float.new
|
||||
|
||||
Mysql::Field.constants.grep(/TYPE/).map { |class_name|
|
||||
Mysql::Field.const_get class_name
|
||||
}.reject { |const| TYPES.key? const }.each do |const|
|
||||
register_type const, Fields::Identity.new
|
||||
end
|
||||
end
|
||||
|
||||
def exec_without_stmt(sql, name = 'SQL') # :nodoc:
|
||||
# Some queries, like SHOW CREATE TABLE don't work through the prepared
|
||||
# statement API. For those queries, we need to use this method. :'(
|
||||
|
@ -306,8 +427,17 @@ module ActiveRecord
|
|||
affected_rows = @connection.affected_rows
|
||||
|
||||
if result
|
||||
cols = result.fetch_fields.map { |field| field.name }
|
||||
result_set = ActiveRecord::Result.new(cols, result.to_a)
|
||||
types = {}
|
||||
result.fetch_fields.each { |field|
|
||||
if field.decimals > 0
|
||||
types[field.name] = Fields::Decimal.new
|
||||
else
|
||||
types[field.name] = Fields::TYPES.fetch(field.type) {
|
||||
Fields::Identity.new
|
||||
}
|
||||
end
|
||||
}
|
||||
result_set = Result.new(types.keys, result.to_a, types)
|
||||
result.free
|
||||
else
|
||||
result_set = ActiveRecord::Result.new([], [])
|
||||
|
|
|
@ -433,7 +433,7 @@ class CalculationsTest < ActiveRecord::TestCase
|
|||
Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
|
||||
|
||||
# TODO: Investigate why PG isn't being typecast
|
||||
if current_adapter?(:PostgreSQLAdapter)
|
||||
if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:MysqlAdapter)
|
||||
assert_equal "7", Company.includes(:contracts).maximum(:developer_id)
|
||||
else
|
||||
assert_equal 7, Company.includes(:contracts).maximum(:developer_id)
|
||||
|
@ -444,7 +444,7 @@ class CalculationsTest < ActiveRecord::TestCase
|
|||
Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
|
||||
|
||||
# TODO: Investigate why PG isn't being typecast
|
||||
if current_adapter?(:PostgreSQLAdapter)
|
||||
if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:MysqlAdapter)
|
||||
assert_equal "7", Company.includes(:contracts).minimum(:developer_id)
|
||||
else
|
||||
assert_equal 7, Company.includes(:contracts).minimum(:developer_id)
|
||||
|
|
Loading…
Reference in New Issue