1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Always pass a column with a type object to quote

The only case where we got a column that was not `nil`, but did not
respond to `cast_type` was when type casting the default value during
schema creation. We can look up the cast type, and add that object to
the column definition. Will allow us to consistently rely on the type
objects for type casting in all directions.
This commit is contained in:
Sean Griffin 2014-06-28 14:44:49 -06:00
parent b44174f8c8
commit b404613c97
8 changed files with 25 additions and 28 deletions

View file

@ -9,12 +9,7 @@ module ActiveRecord
# records are quoted as their primary key
return value.quoted_id if value.respond_to?(:quoted_id)
# FIXME: The only case we get an object other than nil or a real column
# is `SchemaStatements#add_column` with a PG array that has a non-empty default
# value. Is this really the only case? Are we missing tests for other types?
# We should have a real column object passed (or nil) here, and check for that
# instead
if column.respond_to?(:cast_type)
if column
value = column.cast_type.type_cast_for_database(value)
end
@ -29,12 +24,7 @@ module ActiveRecord
return value.id
end
# FIXME: The only case we get an object other than nil or a real column
# is `SchemaStatements#add_column` with a PG array that has a non-empty default
# value. Is this really the only case? Are we missing tests for other types?
# We should have a real column object passed (or nil) here, and check for that
# instead
if column.respond_to?(:cast_type)
if column
value = column.cast_type.type_cast_for_database(value)
end

View file

@ -94,6 +94,7 @@ module ActiveRecord
def quote_value(value, column)
column.sql_type ||= type_to_sql(column.type, column.limit, column.precision, column.scale)
column.cast_type ||= type_for_column(column)
@conn.quote(value, column)
end
@ -114,6 +115,10 @@ module ActiveRecord
MSG
end
end
def type_for_column(column)
@conn.lookup_cast_type(column.sql_type)
end
end
end
end

View file

@ -15,7 +15,7 @@ module ActiveRecord
# are typically created by methods in TableDefinition, and added to the
# +columns+ attribute of said TableDefinition object, in order to be used
# for generating a number of table creation or table changing SQL statements.
class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :primary_key, :sql_type) #:nodoc:
class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :primary_key, :sql_type, :cast_type) #:nodoc:
def primary_key?
primary_key || type.to_sym == :primary_key

View file

@ -375,12 +375,12 @@ module ActiveRecord
Column.new(name, default, cast_type, sql_type, null)
end
protected
def lookup_cast_type(sql_type) # :nodoc:
type_map.lookup(sql_type)
end
protected
def initialize_type_map(m) # :nodoc:
register_class_with_limit m, %r(boolean)i, Type::Boolean
register_class_with_limit m, %r(char)i, Type::String

View file

@ -31,6 +31,14 @@ module ActiveRecord
super
end
end
def type_for_column(column)
if column.array
@conn.lookup_cast_type("#{column.sql_type}[]")
else
super
end
end
end
module SchemaStatements

View file

@ -383,6 +383,11 @@ module ActiveRecord
PostgreSQL::Table.new(table_name, base)
end
def lookup_cast_type(sql_type) # :nodoc:
oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first['oid'].to_i
super(oid)
end
protected
# Returns the version of the connected PostgreSQL server.

View file

@ -15,10 +15,10 @@ module ActiveRecord
def test_type_cast_binary_encoding_without_logger
@conn.extend(Module.new { def logger; end })
cast_type = Type::String.new
column = Column.new(nil, nil, Type::String.new)
binary = SecureRandom.hex
expected = binary.dup.encode!(Encoding::UTF_8)
assert_equal expected, @conn.type_cast(binary, cast_type)
assert_equal expected, @conn.type_cast(binary, column)
end
def test_type_cast_symbol

View file

@ -83,12 +83,10 @@ module ActiveRecord
def test_quote_with_quoted_id
assert_equal 1, @quoter.quote(Struct.new(:quoted_id).new(1), nil)
assert_equal 1, @quoter.quote(Struct.new(:quoted_id).new(1), 'foo')
end
def test_quote_nil
assert_equal 'NULL', @quoter.quote(nil, nil)
assert_equal 'NULL', @quoter.quote(nil, 'foo')
end
def test_quote_true
@ -102,48 +100,39 @@ module ActiveRecord
def test_quote_float
float = 1.2
assert_equal float.to_s, @quoter.quote(float, nil)
assert_equal float.to_s, @quoter.quote(float, Object.new)
end
def test_quote_fixnum
fixnum = 1
assert_equal fixnum.to_s, @quoter.quote(fixnum, nil)
assert_equal fixnum.to_s, @quoter.quote(fixnum, Object.new)
end
def test_quote_bignum
bignum = 1 << 100
assert_equal bignum.to_s, @quoter.quote(bignum, nil)
assert_equal bignum.to_s, @quoter.quote(bignum, Object.new)
end
def test_quote_bigdecimal
bigdec = BigDecimal.new((1 << 100).to_s)
assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, nil)
assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, Object.new)
end
def test_dates_and_times
@quoter.extend(Module.new { def quoted_date(value) 'lol' end })
assert_equal "'lol'", @quoter.quote(Date.today, nil)
assert_equal "'lol'", @quoter.quote(Date.today, Object.new)
assert_equal "'lol'", @quoter.quote(Time.now, nil)
assert_equal "'lol'", @quoter.quote(Time.now, Object.new)
assert_equal "'lol'", @quoter.quote(DateTime.now, nil)
assert_equal "'lol'", @quoter.quote(DateTime.now, Object.new)
end
def test_crazy_object
crazy = Class.new.new
expected = "'#{YAML.dump(crazy)}'"
assert_equal expected, @quoter.quote(crazy, nil)
assert_equal expected, @quoter.quote(crazy, Object.new)
end
def test_crazy_object_calls_quote_string
crazy = Class.new { def initialize; @lol = 'lo\l' end }.new
assert_match "lo\\\\l", @quoter.quote(crazy, nil)
assert_match "lo\\\\l", @quoter.quote(crazy, Object.new)
end
def test_quote_string_no_column