mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #15151 from sgrif/sg-add-type-to-column
Add a type object to Column constructor
This commit is contained in:
commit
dbbcc8388a
14 changed files with 86 additions and 57 deletions
|
@ -3,6 +3,7 @@ require 'bigdecimal'
|
|||
require 'bigdecimal/util'
|
||||
require 'active_support/core_ext/benchmark'
|
||||
require 'active_record/connection_adapters/schema_cache'
|
||||
require 'active_record/connection_adapters/type'
|
||||
require 'active_record/connection_adapters/abstract/schema_dumper'
|
||||
require 'active_record/connection_adapters/abstract/schema_creation'
|
||||
require 'monitor'
|
||||
|
@ -362,6 +363,10 @@ module ActiveRecord
|
|||
|
||||
protected
|
||||
|
||||
def lookup_cast_type(sql_type) # :nodoc:
|
||||
Type::Value.new
|
||||
end
|
||||
|
||||
def translate_exception_class(e, sql)
|
||||
message = "#{e.class.name}: #{e.message}: #{sql}"
|
||||
@logger.error message if @logger
|
||||
|
|
|
@ -56,11 +56,11 @@ module ActiveRecord
|
|||
class Column < ConnectionAdapters::Column # :nodoc:
|
||||
attr_reader :collation, :strict, :extra
|
||||
|
||||
def initialize(name, default, sql_type = nil, null = true, collation = nil, strict = false, extra = "")
|
||||
def initialize(name, default, cast_type, sql_type = nil, null = true, collation = nil, strict = false, extra = "")
|
||||
@strict = strict
|
||||
@collation = collation
|
||||
@extra = extra
|
||||
super(name, default, sql_type, null)
|
||||
super(name, default, cast_type, sql_type, null)
|
||||
end
|
||||
|
||||
def extract_default(default)
|
||||
|
@ -263,8 +263,9 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
# Overridden by the adapters to instantiate their specific Column type.
|
||||
def new_column(field, default, type, null, collation, extra = "") # :nodoc:
|
||||
Column.new(field, default, type, null, collation, extra)
|
||||
def new_column(field, default, sql_type, null, collation, extra = "") # :nodoc:
|
||||
cast_type = lookup_cast_type(sql_type)
|
||||
Column.new(field, default, cast_type, sql_type, null, collation, extra)
|
||||
end
|
||||
|
||||
# Must return the Mysql error number from the exception, if the exception has an
|
||||
|
|
|
@ -22,12 +22,14 @@ module ActiveRecord
|
|||
#
|
||||
# +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int(11)</tt>.
|
||||
# +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
|
||||
# +cast_type+ is the object used for type casting and type information.
|
||||
# +sql_type+ is used to extract the column's length, if necessary. For example +60+ in
|
||||
# <tt>company_name varchar(60)</tt>.
|
||||
# It will be mapped to one of the standard Rails SQL types in the <tt>type</tt> attribute.
|
||||
# +null+ determines if this column allows +NULL+ values.
|
||||
def initialize(name, default, sql_type = nil, null = true)
|
||||
def initialize(name, default, cast_type, sql_type = nil, null = true)
|
||||
@name = name
|
||||
@cast_type = cast_type
|
||||
@sql_type = sql_type
|
||||
@null = null
|
||||
@limit = extract_limit(sql_type)
|
||||
|
|
|
@ -69,8 +69,9 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
def new_column(field, default, type, null, collation, extra = "") # :nodoc:
|
||||
Column.new(field, default, type, null, collation, strict_mode?, extra)
|
||||
def new_column(field, default, sql_type, null, collation, extra = "") # :nodoc:
|
||||
cast_type = lookup_cast_type(sql_type)
|
||||
Column.new(field, default, cast_type, sql_type, null, collation, strict_mode?, extra)
|
||||
end
|
||||
|
||||
def error_number(exception)
|
||||
|
|
|
@ -156,8 +156,9 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
def new_column(field, default, type, null, collation, extra = "") # :nodoc:
|
||||
Column.new(field, default, type, null, collation, strict_mode?, extra)
|
||||
def new_column(field, default, sql_type, null, collation, extra = "") # :nodoc:
|
||||
cast_type = lookup_cast_type(sql_type)
|
||||
Column.new(field, default, cast_type, sql_type, null, collation, strict_mode?, extra)
|
||||
end
|
||||
|
||||
def error_number(exception) # :nodoc:
|
||||
|
|
|
@ -12,10 +12,10 @@ module ActiveRecord
|
|||
|
||||
if sql_type =~ /\[\]$/
|
||||
@array = true
|
||||
super(name, default_value, sql_type[0..sql_type.length - 3], null)
|
||||
super(name, default_value, oid_type, sql_type[0..sql_type.length - 3], null)
|
||||
else
|
||||
@array = false
|
||||
super(name, default_value, sql_type, null)
|
||||
super(name, default_value, oid_type, sql_type, null)
|
||||
end
|
||||
|
||||
@default_function = default if has_default_function?(default_value, default)
|
||||
|
|
|
@ -394,7 +394,9 @@ module ActiveRecord
|
|||
field["dflt_value"] = $1.gsub('""', '"')
|
||||
end
|
||||
|
||||
SQLite3Column.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0)
|
||||
sql_type = field['type']
|
||||
cast_type = lookup_cast_type(sql_type)
|
||||
SQLite3Column.new(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'].to_i == 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
require 'active_record/connection_adapters/type/value'
|
||||
|
||||
module ActiveRecord
|
||||
module ConnectionAdapters
|
||||
module Type # :nodoc:
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
module ActiveRecord
|
||||
module ConnectionAdapters
|
||||
module Type
|
||||
class Value # :nodoc:
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -29,6 +29,7 @@ module ActiveRecord
|
|||
@columns[table_name] << ActiveRecord::ConnectionAdapters::Column.new(
|
||||
name.to_s,
|
||||
options[:default],
|
||||
lookup_cast_type(sql_type.to_s),
|
||||
sql_type.to_s,
|
||||
options[:null])
|
||||
end
|
||||
|
|
|
@ -9,13 +9,13 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def test_type_cast_true
|
||||
c = Column.new(nil, 1, 'boolean')
|
||||
c = Column.new(nil, 1, Type::Value.new, 'boolean')
|
||||
assert_equal 1, @conn.type_cast(true, nil)
|
||||
assert_equal 1, @conn.type_cast(true, c)
|
||||
end
|
||||
|
||||
def test_type_cast_false
|
||||
c = Column.new(nil, 1, 'boolean')
|
||||
c = Column.new(nil, 1, Type::Value.new, 'boolean')
|
||||
assert_equal 0, @conn.type_cast(false, nil)
|
||||
assert_equal 0, @conn.type_cast(false, c)
|
||||
end
|
||||
|
|
|
@ -47,13 +47,13 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def test_type_cast_true
|
||||
c = Column.new(nil, 1, 'int')
|
||||
c = Column.new(nil, 1, Type::Value.new, 'int')
|
||||
assert_equal 't', @conn.type_cast(true, nil)
|
||||
assert_equal 1, @conn.type_cast(true, c)
|
||||
end
|
||||
|
||||
def test_type_cast_false
|
||||
c = Column.new(nil, 1, 'int')
|
||||
c = Column.new(nil, 1, Type::Value.new, 'int')
|
||||
assert_equal 'f', @conn.type_cast(false, nil)
|
||||
assert_equal 0, @conn.type_cast(false, c)
|
||||
end
|
||||
|
@ -61,16 +61,16 @@ module ActiveRecord
|
|||
def test_type_cast_string
|
||||
assert_equal '10', @conn.type_cast('10', nil)
|
||||
|
||||
c = Column.new(nil, 1, 'int')
|
||||
c = Column.new(nil, 1, Type::Value.new, 'int')
|
||||
assert_equal 10, @conn.type_cast('10', c)
|
||||
|
||||
c = Column.new(nil, 1, 'float')
|
||||
c = Column.new(nil, 1, Type::Value.new, 'float')
|
||||
assert_equal 10.1, @conn.type_cast('10.1', c)
|
||||
|
||||
c = Column.new(nil, 1, 'binary')
|
||||
c = Column.new(nil, 1, Type::Value.new, 'binary')
|
||||
assert_equal '10.1', @conn.type_cast('10.1', c)
|
||||
|
||||
c = Column.new(nil, 1, 'date')
|
||||
c = Column.new(nil, 1, Type::Value.new, 'date')
|
||||
assert_equal '10.1', @conn.type_cast('10.1', c)
|
||||
end
|
||||
|
||||
|
|
|
@ -12,13 +12,13 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def test_can_set_coder
|
||||
column = Column.new("title", nil, "varchar(20)")
|
||||
column = Column.new("title", nil, Type::Value.new, "varchar(20)")
|
||||
column.coder = YAML
|
||||
assert_equal YAML, column.coder
|
||||
end
|
||||
|
||||
def test_encoded?
|
||||
column = Column.new("title", nil, "varchar(20)")
|
||||
column = Column.new("title", nil, Type::Value.new, "varchar(20)")
|
||||
assert !column.encoded?
|
||||
|
||||
column.coder = YAML
|
||||
|
@ -26,7 +26,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def test_type_case_coded_column
|
||||
column = Column.new("title", nil, "varchar(20)")
|
||||
column = Column.new("title", nil, Type::Value.new, "varchar(20)")
|
||||
column.coder = YAML
|
||||
assert_equal "hello", column.type_cast("--- hello")
|
||||
end
|
||||
|
@ -34,7 +34,7 @@ module ActiveRecord
|
|||
# Avoid column definitions in create table statements like:
|
||||
# `title` varchar(255) DEFAULT NULL
|
||||
def test_should_not_include_default_clause_when_default_is_null
|
||||
column = Column.new("title", nil, "varchar(20)")
|
||||
column = Column.new("title", nil, Type::Value.new, "varchar(20)")
|
||||
column_def = ColumnDefinition.new(
|
||||
column.name, "string",
|
||||
column.limit, column.precision, column.scale, column.default, column.null)
|
||||
|
@ -42,7 +42,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def test_should_include_default_clause_when_default_is_present
|
||||
column = Column.new("title", "Hello", "varchar(20)")
|
||||
column = Column.new("title", "Hello", Type::Value.new, "varchar(20)")
|
||||
column_def = ColumnDefinition.new(
|
||||
column.name, "string",
|
||||
column.limit, column.precision, column.scale, column.default, column.null)
|
||||
|
@ -50,7 +50,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def test_should_specify_not_null_if_null_option_is_false
|
||||
column = Column.new("title", "Hello", "varchar(20)", false)
|
||||
column = Column.new("title", "Hello", Type::Value.new, "varchar(20)", false)
|
||||
column_def = ColumnDefinition.new(
|
||||
column.name, "string",
|
||||
column.limit, column.precision, column.scale, column.default, column.null)
|
||||
|
@ -59,68 +59,68 @@ module ActiveRecord
|
|||
|
||||
if current_adapter?(:MysqlAdapter)
|
||||
def test_should_set_default_for_mysql_binary_data_types
|
||||
binary_column = MysqlAdapter::Column.new("title", "a", "binary(1)")
|
||||
binary_column = MysqlAdapter::Column.new("title", "a", Type::Value.new, "binary(1)")
|
||||
assert_equal "a", binary_column.default
|
||||
|
||||
varbinary_column = MysqlAdapter::Column.new("title", "a", "varbinary(1)")
|
||||
varbinary_column = MysqlAdapter::Column.new("title", "a", Type::Value.new, "varbinary(1)")
|
||||
assert_equal "a", varbinary_column.default
|
||||
end
|
||||
|
||||
def test_should_not_set_default_for_blob_and_text_data_types
|
||||
assert_raise ArgumentError do
|
||||
MysqlAdapter::Column.new("title", "a", "blob")
|
||||
MysqlAdapter::Column.new("title", "a", Type::Value.new, "blob")
|
||||
end
|
||||
|
||||
assert_raise ArgumentError do
|
||||
MysqlAdapter::Column.new("title", "Hello", "text")
|
||||
MysqlAdapter::Column.new("title", "Hello", Type::Value.new, "text")
|
||||
end
|
||||
|
||||
text_column = MysqlAdapter::Column.new("title", nil, "text")
|
||||
text_column = MysqlAdapter::Column.new("title", nil, Type::Value.new, "text")
|
||||
assert_equal nil, text_column.default
|
||||
|
||||
not_null_text_column = MysqlAdapter::Column.new("title", nil, "text", false)
|
||||
not_null_text_column = MysqlAdapter::Column.new("title", nil, Type::Value.new, "text", false)
|
||||
assert_equal "", not_null_text_column.default
|
||||
end
|
||||
|
||||
def test_has_default_should_return_false_for_blob_and_text_data_types
|
||||
blob_column = MysqlAdapter::Column.new("title", nil, "blob")
|
||||
blob_column = MysqlAdapter::Column.new("title", nil, Type::Value.new, "blob")
|
||||
assert !blob_column.has_default?
|
||||
|
||||
text_column = MysqlAdapter::Column.new("title", nil, "text")
|
||||
text_column = MysqlAdapter::Column.new("title", nil, Type::Value.new, "text")
|
||||
assert !text_column.has_default?
|
||||
end
|
||||
end
|
||||
|
||||
if current_adapter?(:Mysql2Adapter)
|
||||
def test_should_set_default_for_mysql_binary_data_types
|
||||
binary_column = Mysql2Adapter::Column.new("title", "a", "binary(1)")
|
||||
binary_column = Mysql2Adapter::Column.new("title", "a", Type::Value.new, "binary(1)")
|
||||
assert_equal "a", binary_column.default
|
||||
|
||||
varbinary_column = Mysql2Adapter::Column.new("title", "a", "varbinary(1)")
|
||||
varbinary_column = Mysql2Adapter::Column.new("title", "a", Type::Value.new, "varbinary(1)")
|
||||
assert_equal "a", varbinary_column.default
|
||||
end
|
||||
|
||||
def test_should_not_set_default_for_blob_and_text_data_types
|
||||
assert_raise ArgumentError do
|
||||
Mysql2Adapter::Column.new("title", "a", "blob")
|
||||
Mysql2Adapter::Column.new("title", "a", Type::Value.new, "blob")
|
||||
end
|
||||
|
||||
assert_raise ArgumentError do
|
||||
Mysql2Adapter::Column.new("title", "Hello", "text")
|
||||
Mysql2Adapter::Column.new("title", "Hello", Type::Value.new, "text")
|
||||
end
|
||||
|
||||
text_column = Mysql2Adapter::Column.new("title", nil, "text")
|
||||
text_column = Mysql2Adapter::Column.new("title", nil, Type::Value.new, "text")
|
||||
assert_equal nil, text_column.default
|
||||
|
||||
not_null_text_column = Mysql2Adapter::Column.new("title", nil, "text", false)
|
||||
not_null_text_column = Mysql2Adapter::Column.new("title", nil, Type::Value.new, "text", false)
|
||||
assert_equal "", not_null_text_column.default
|
||||
end
|
||||
|
||||
def test_has_default_should_return_false_for_blob_and_text_data_types
|
||||
blob_column = Mysql2Adapter::Column.new("title", nil, "blob")
|
||||
blob_column = Mysql2Adapter::Column.new("title", nil, Type::Value.new, "blob")
|
||||
assert !blob_column.has_default?
|
||||
|
||||
text_column = Mysql2Adapter::Column.new("title", nil, "text")
|
||||
text_column = Mysql2Adapter::Column.new("title", nil, Type::Value.new, "text")
|
||||
assert !text_column.has_default?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ module ActiveRecord
|
|||
module ConnectionAdapters
|
||||
class ColumnTest < ActiveRecord::TestCase
|
||||
def test_type_cast_boolean
|
||||
column = Column.new("field", nil, "boolean")
|
||||
column = Column.new("field", nil, Type::Value.new, "boolean")
|
||||
assert column.type_cast('').nil?
|
||||
assert column.type_cast(nil).nil?
|
||||
|
||||
|
@ -36,14 +36,14 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def test_type_cast_string
|
||||
column = Column.new("field", nil, "varchar")
|
||||
column = Column.new("field", nil, Type::Value.new, "varchar")
|
||||
assert_equal "1", column.type_cast(true)
|
||||
assert_equal "0", column.type_cast(false)
|
||||
assert_equal "123", column.type_cast(123)
|
||||
end
|
||||
|
||||
def test_type_cast_integer
|
||||
column = Column.new("field", nil, "integer")
|
||||
column = Column.new("field", nil, Type::Value.new, "integer")
|
||||
assert_equal 1, column.type_cast(1)
|
||||
assert_equal 1, column.type_cast('1')
|
||||
assert_equal 1, column.type_cast('1ignore')
|
||||
|
@ -56,50 +56,50 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def test_type_cast_non_integer_to_integer
|
||||
column = Column.new("field", nil, "integer")
|
||||
column = Column.new("field", nil, Type::Value.new, "integer")
|
||||
assert_nil column.type_cast([1,2])
|
||||
assert_nil column.type_cast({1 => 2})
|
||||
assert_nil column.type_cast((1..2))
|
||||
end
|
||||
|
||||
def test_type_cast_activerecord_to_integer
|
||||
column = Column.new("field", nil, "integer")
|
||||
column = Column.new("field", nil, Type::Value.new, "integer")
|
||||
firm = Firm.create(:name => 'Apple')
|
||||
assert_nil column.type_cast(firm)
|
||||
end
|
||||
|
||||
def test_type_cast_object_without_to_i_to_integer
|
||||
column = Column.new("field", nil, "integer")
|
||||
column = Column.new("field", nil, Type::Value.new, "integer")
|
||||
assert_nil column.type_cast(Object.new)
|
||||
end
|
||||
|
||||
def test_type_cast_nan_and_infinity_to_integer
|
||||
column = Column.new("field", nil, "integer")
|
||||
column = Column.new("field", nil, Type::Value.new, "integer")
|
||||
assert_nil column.type_cast(Float::NAN)
|
||||
assert_nil column.type_cast(1.0/0.0)
|
||||
end
|
||||
|
||||
def test_type_cast_float
|
||||
column = Column.new("field", nil, "float")
|
||||
column = Column.new("field", nil, Type::Value.new, "float")
|
||||
assert_equal 1.0, column.type_cast("1")
|
||||
end
|
||||
|
||||
def test_type_cast_decimal
|
||||
column = Column.new("field", nil, "decimal")
|
||||
column = Column.new("field", nil, Type::Value.new, "decimal")
|
||||
assert_equal BigDecimal.new("0"), column.type_cast(BigDecimal.new("0"))
|
||||
assert_equal BigDecimal.new("123"), column.type_cast(123.0)
|
||||
assert_equal BigDecimal.new("1"), column.type_cast(:"1")
|
||||
end
|
||||
|
||||
def test_type_cast_binary
|
||||
column = Column.new("field", nil, "binary")
|
||||
column = Column.new("field", nil, Type::Value.new, "binary")
|
||||
assert_equal nil, column.type_cast(nil)
|
||||
assert_equal "1", column.type_cast("1")
|
||||
assert_equal 1, column.type_cast(1)
|
||||
end
|
||||
|
||||
def test_type_cast_time
|
||||
column = Column.new("field", nil, "time")
|
||||
column = Column.new("field", nil, Type::Value.new, "time")
|
||||
assert_equal nil, column.type_cast(nil)
|
||||
assert_equal nil, column.type_cast('')
|
||||
assert_equal nil, column.type_cast('ABC')
|
||||
|
@ -109,7 +109,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def test_type_cast_datetime_and_timestamp
|
||||
[Column.new("field", nil, "datetime"), Column.new("field", nil, "timestamp")].each do |column|
|
||||
[Column.new("field", nil, Type::Value.new, "datetime"), Column.new("field", nil, Type::Value.new, "timestamp")].each do |column|
|
||||
assert_equal nil, column.type_cast(nil)
|
||||
assert_equal nil, column.type_cast('')
|
||||
assert_equal nil, column.type_cast(' ')
|
||||
|
@ -121,7 +121,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def test_type_cast_date
|
||||
column = Column.new("field", nil, "date")
|
||||
column = Column.new("field", nil, Type::Value.new, "date")
|
||||
assert_equal nil, column.type_cast(nil)
|
||||
assert_equal nil, column.type_cast('')
|
||||
assert_equal nil, column.type_cast(' ')
|
||||
|
@ -132,7 +132,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def test_type_cast_duration_to_integer
|
||||
column = Column.new("field", nil, "integer")
|
||||
column = Column.new("field", nil, Type::Value.new, "integer")
|
||||
assert_equal 1800, column.type_cast(30.minutes)
|
||||
assert_equal 7200, column.type_cast(2.hours)
|
||||
end
|
||||
|
@ -147,7 +147,7 @@ module ActiveRecord
|
|||
|
||||
if current_adapter?(:SQLite3Adapter)
|
||||
def test_binary_encoding
|
||||
column = SQLite3Column.new("field", nil, "binary")
|
||||
column = SQLite3Column.new("field", nil, Type::Value.new, "binary")
|
||||
utf8_string = "a string".encode(Encoding::UTF_8)
|
||||
type_cast = column.type_cast(utf8_string)
|
||||
|
||||
|
|
Loading…
Reference in a new issue