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:
parent
b44174f8c8
commit
b404613c97
8 changed files with 25 additions and 28 deletions
|
@ -9,12 +9,7 @@ module ActiveRecord
|
||||||
# records are quoted as their primary key
|
# records are quoted as their primary key
|
||||||
return value.quoted_id if value.respond_to?(:quoted_id)
|
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
|
if 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)
|
|
||||||
value = column.cast_type.type_cast_for_database(value)
|
value = column.cast_type.type_cast_for_database(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -29,12 +24,7 @@ module ActiveRecord
|
||||||
return value.id
|
return value.id
|
||||||
end
|
end
|
||||||
|
|
||||||
# FIXME: The only case we get an object other than nil or a real column
|
if 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)
|
|
||||||
value = column.cast_type.type_cast_for_database(value)
|
value = column.cast_type.type_cast_for_database(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -94,6 +94,7 @@ module ActiveRecord
|
||||||
|
|
||||||
def quote_value(value, column)
|
def quote_value(value, column)
|
||||||
column.sql_type ||= type_to_sql(column.type, column.limit, column.precision, column.scale)
|
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)
|
@conn.quote(value, column)
|
||||||
end
|
end
|
||||||
|
@ -114,6 +115,10 @@ module ActiveRecord
|
||||||
MSG
|
MSG
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def type_for_column(column)
|
||||||
|
@conn.lookup_cast_type(column.sql_type)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,7 +15,7 @@ module ActiveRecord
|
||||||
# are typically created by methods in TableDefinition, and added to the
|
# are typically created by methods in TableDefinition, and added to the
|
||||||
# +columns+ attribute of said TableDefinition object, in order to be used
|
# +columns+ attribute of said TableDefinition object, in order to be used
|
||||||
# for generating a number of table creation or table changing SQL statements.
|
# 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?
|
def primary_key?
|
||||||
primary_key || type.to_sym == :primary_key
|
primary_key || type.to_sym == :primary_key
|
||||||
|
|
|
@ -375,12 +375,12 @@ module ActiveRecord
|
||||||
Column.new(name, default, cast_type, sql_type, null)
|
Column.new(name, default, cast_type, sql_type, null)
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def lookup_cast_type(sql_type) # :nodoc:
|
def lookup_cast_type(sql_type) # :nodoc:
|
||||||
type_map.lookup(sql_type)
|
type_map.lookup(sql_type)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
def initialize_type_map(m) # :nodoc:
|
def initialize_type_map(m) # :nodoc:
|
||||||
register_class_with_limit m, %r(boolean)i, Type::Boolean
|
register_class_with_limit m, %r(boolean)i, Type::Boolean
|
||||||
register_class_with_limit m, %r(char)i, Type::String
|
register_class_with_limit m, %r(char)i, Type::String
|
||||||
|
|
|
@ -31,6 +31,14 @@ module ActiveRecord
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def type_for_column(column)
|
||||||
|
if column.array
|
||||||
|
@conn.lookup_cast_type("#{column.sql_type}[]")
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module SchemaStatements
|
module SchemaStatements
|
||||||
|
|
|
@ -383,6 +383,11 @@ module ActiveRecord
|
||||||
PostgreSQL::Table.new(table_name, base)
|
PostgreSQL::Table.new(table_name, base)
|
||||||
end
|
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
|
protected
|
||||||
|
|
||||||
# Returns the version of the connected PostgreSQL server.
|
# Returns the version of the connected PostgreSQL server.
|
||||||
|
|
|
@ -15,10 +15,10 @@ module ActiveRecord
|
||||||
|
|
||||||
def test_type_cast_binary_encoding_without_logger
|
def test_type_cast_binary_encoding_without_logger
|
||||||
@conn.extend(Module.new { def logger; end })
|
@conn.extend(Module.new { def logger; end })
|
||||||
cast_type = Type::String.new
|
column = Column.new(nil, nil, Type::String.new)
|
||||||
binary = SecureRandom.hex
|
binary = SecureRandom.hex
|
||||||
expected = binary.dup.encode!(Encoding::UTF_8)
|
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
|
end
|
||||||
|
|
||||||
def test_type_cast_symbol
|
def test_type_cast_symbol
|
||||||
|
|
|
@ -83,12 +83,10 @@ module ActiveRecord
|
||||||
|
|
||||||
def test_quote_with_quoted_id
|
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), nil)
|
||||||
assert_equal 1, @quoter.quote(Struct.new(:quoted_id).new(1), 'foo')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_quote_nil
|
def test_quote_nil
|
||||||
assert_equal 'NULL', @quoter.quote(nil, nil)
|
assert_equal 'NULL', @quoter.quote(nil, nil)
|
||||||
assert_equal 'NULL', @quoter.quote(nil, 'foo')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_quote_true
|
def test_quote_true
|
||||||
|
@ -102,48 +100,39 @@ module ActiveRecord
|
||||||
def test_quote_float
|
def test_quote_float
|
||||||
float = 1.2
|
float = 1.2
|
||||||
assert_equal float.to_s, @quoter.quote(float, nil)
|
assert_equal float.to_s, @quoter.quote(float, nil)
|
||||||
assert_equal float.to_s, @quoter.quote(float, Object.new)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_quote_fixnum
|
def test_quote_fixnum
|
||||||
fixnum = 1
|
fixnum = 1
|
||||||
assert_equal fixnum.to_s, @quoter.quote(fixnum, nil)
|
assert_equal fixnum.to_s, @quoter.quote(fixnum, nil)
|
||||||
assert_equal fixnum.to_s, @quoter.quote(fixnum, Object.new)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_quote_bignum
|
def test_quote_bignum
|
||||||
bignum = 1 << 100
|
bignum = 1 << 100
|
||||||
assert_equal bignum.to_s, @quoter.quote(bignum, nil)
|
assert_equal bignum.to_s, @quoter.quote(bignum, nil)
|
||||||
assert_equal bignum.to_s, @quoter.quote(bignum, Object.new)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_quote_bigdecimal
|
def test_quote_bigdecimal
|
||||||
bigdec = BigDecimal.new((1 << 100).to_s)
|
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, nil)
|
||||||
assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, Object.new)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_dates_and_times
|
def test_dates_and_times
|
||||||
@quoter.extend(Module.new { def quoted_date(value) 'lol' end })
|
@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, 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, 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, nil)
|
||||||
assert_equal "'lol'", @quoter.quote(DateTime.now, Object.new)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_crazy_object
|
def test_crazy_object
|
||||||
crazy = Class.new.new
|
crazy = Class.new.new
|
||||||
expected = "'#{YAML.dump(crazy)}'"
|
expected = "'#{YAML.dump(crazy)}'"
|
||||||
assert_equal expected, @quoter.quote(crazy, nil)
|
assert_equal expected, @quoter.quote(crazy, nil)
|
||||||
assert_equal expected, @quoter.quote(crazy, Object.new)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_crazy_object_calls_quote_string
|
def test_crazy_object_calls_quote_string
|
||||||
crazy = Class.new { def initialize; @lol = 'lo\l' end }.new
|
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, nil)
|
||||||
assert_match "lo\\\\l", @quoter.quote(crazy, Object.new)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_quote_string_no_column
|
def test_quote_string_no_column
|
||||||
|
|
Loading…
Reference in a new issue