diff options
author | Sean Griffin <sean@thoughtbot.com> | 2014-05-17 12:24:13 -0600 |
---|---|---|
committer | Sean Griffin <sean@thoughtbot.com> | 2014-05-19 08:07:21 -0700 |
commit | 0b682e4b05c8f58c77c655650af6638c483ac903 (patch) | |
tree | e22d197ed7d828dad7f249d8878a8fb8cff1ebd2 /activerecord/lib | |
parent | a0eec57ef0fbbbb2910a46ca65bd5b565ca0534c (diff) | |
download | rails-0b682e4b05c8f58c77c655650af6638c483ac903.tar.gz rails-0b682e4b05c8f58c77c655650af6638c483ac903.tar.bz2 rails-0b682e4b05c8f58c77c655650af6638c483ac903.zip |
Delegate `Column#type` to the injected type object
The decision to wrap type registrations in a proc was made for two
reasons.
1. Some cases need to make an additional decision based on the type
(e.g. a `Decimal` with a 0 scale)
2. Aliased types are automatically updated if they type they point to is
updated later. If a user or another adapter decides to change the
object used for `decimal` columns, `numeric`, and `number` will
automatically point to the new type, without having to track what
types are aliased explicitly.
Everything else here should be pretty straightforward. PostgreSQL ranges
had to change slightly, since the `simplified_type` method is gone.
Diffstat (limited to 'activerecord/lib')
21 files changed, 258 insertions, 71 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index d9c939689f..8a7a869eec 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -361,10 +361,46 @@ module ActiveRecord pool.checkin self end + def type_map # :nodoc: + @type_map ||= Type::TypeMap.new.tap do |mapping| + initialize_type_map(mapping) + end + end + protected def lookup_cast_type(sql_type) # :nodoc: - Type::Value.new + type_map.lookup(sql_type) + end + + def initialize_type_map(m) # :nodoc: + m.register_type %r(boolean)i, Type::Boolean.new + m.register_type %r(char)i, Type::String.new + m.register_type %r(binary)i, Type::Binary.new + m.alias_type %r(blob)i, 'binary' + m.register_type %r(text)i, Type::Text.new + m.alias_type %r(clob)i, 'text' + m.register_type %r(date)i, Type::Date.new + m.register_type %r(time)i, Type::Time.new + m.register_type %r(timestamp)i, Type::Timestamp.new + m.register_type %r(datetime)i, Type::DateTime.new + m.alias_type %r(numeric)i, 'decimal' + m.alias_type %r(number)i, 'decimal' + m.register_type %r(float)i, Type::Float.new + m.alias_type %r(double)i, 'float' + m.register_type %r(int)i, Type::Integer.new + m.register_type(%r(decimal)i) do |sql_type| + if Type.extract_scale(sql_type) == 0 + Type::Integer.new + else + Type::Decimal.new + end + end + end + + def reload_type_map # :nodoc: + type_map.clear + initialize_type_map(type_map) end def translate_exception_class(e, sql) diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 7074f69583..5eb2e86d48 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -97,18 +97,6 @@ module ActiveRecord private - def simplified_type(field_type) - return :boolean if adapter.emulate_booleans && field_type.downcase.index("tinyint(1)") - - case field_type - when /enum/i, /set/i then :string - when /year/i then :integer - when /bit/i then :binary - else - super - end - end - def extract_limit(sql_type) case sql_type when /^enum\((.+)\)/i @@ -318,6 +306,11 @@ module ActiveRecord # DATABASE STATEMENTS ====================================== + def clear_cache! + super + reload_type_map + end + # Executes the SQL statement in the context of this connection. def execute(sql, name = nil) log(sql, name) { @connection.query(sql) } @@ -645,6 +638,15 @@ module ActiveRecord protected + def initialize_type_map(m) + super + m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans + m.alias_type %r(enum)i, 'varchar' + m.alias_type %r(set)i, 'varchar' + m.alias_type %r(year)i, 'integer' + m.alias_type %r(bit)i, 'binary' + 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) diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index 3bab325e42..0087c20b88 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -13,11 +13,13 @@ module ActiveRecord ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/ end - attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale, :default_function + attr_reader :name, :default, :cast_type, :limit, :null, :sql_type, :precision, :scale, :default_function attr_accessor :primary, :coder alias :encoded? :coder + delegate :type, to: :cast_type + # Instantiates a new column in the table. # # +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int(11)</tt>. @@ -35,7 +37,6 @@ module ActiveRecord @limit = extract_limit(sql_type) @precision = extract_precision(sql_type) @scale = extract_scale(sql_type) - @type = simplified_type(sql_type) @default = extract_default(default) @default_function = nil @primary = nil @@ -256,6 +257,8 @@ module ActiveRecord end private + delegate :extract_scale, to: Type + def extract_limit(sql_type) $1.to_i if sql_type =~ /\((.*)\)/ end @@ -263,40 +266,6 @@ module ActiveRecord def extract_precision(sql_type) $2.to_i if sql_type =~ /^(numeric|decimal|number)\((\d+)(,\d+)?\)/i end - - def extract_scale(sql_type) - case sql_type - when /^(numeric|decimal|number)\((\d+)\)/i then 0 - when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i - end - end - - def simplified_type(field_type) - case field_type - when /int/i - :integer - when /float|double/i - :float - when /decimal|numeric|number/i - extract_scale(field_type) == 0 ? :integer : :decimal - when /datetime/i - :datetime - when /timestamp/i - :timestamp - when /time/i - :time - when /date/i - :date - when /clob/i, /text/i - :text - when /blob/i, /binary/i - :binary - when /char/i - :string - when /boolean/i - :boolean - end - end end end # :startdoc: diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 69e2b0ab2b..bf09bfe217 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -223,6 +223,7 @@ module ActiveRecord # Clears the prepared statements cache. def clear_cache! + super @statements.clear end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb index 77fdebbbc9..1dd8acc257 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb @@ -165,11 +165,6 @@ module ActiveRecord super end end - - # Maps PostgreSQL-specific data types to logical Rails types. - def simplified_type(field_type) - @oid_type.simplified_type(field_type) || super - end end end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index a97c33ae6f..90bf6c6d1a 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -2,10 +2,7 @@ module ActiveRecord module ConnectionAdapters module PostgreSQL module OID # :nodoc: - class Type - def type; end - def simplified_type(sql_type); type end - + class Type < Type::Value def infinity(options = {}) ::Float::INFINITY * (options[:negative] ? -1 : 1) end @@ -136,11 +133,11 @@ module ActiveRecord end class Range < Type - attr_reader :subtype - def simplified_type(sql_type); sql_type.to_sym end + attr_reader :subtype, :type - def initialize(subtype) + def initialize(subtype, type) @subtype = subtype + @type = type end def extract_bounds(value) @@ -412,7 +409,7 @@ This is not reliable and will be removed in the future. def register_range_type(row) if subtype = @store[row['rngsubtype'].to_i] - register row['oid'], OID::Range.new(subtype) + register row['oid'], OID::Range.new(subtype, row['typname'].to_sym) end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 183d0c4ec6..59e157744f 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -539,10 +539,6 @@ module ActiveRecord private - def type_map - @type_map - end - def get_oid_type(oid, fmod, column_name) if !type_map.key?(oid) initialize_type_map(type_map, [oid]) @@ -554,11 +550,6 @@ module ActiveRecord } end - def reload_type_map - type_map.clear - initialize_type_map(type_map) - end - def initialize_type_map(type_map, oids = nil) if supports_ranges? query = <<-SQL diff --git a/activerecord/lib/active_record/connection_adapters/type.rb b/activerecord/lib/active_record/connection_adapters/type.rb index 1b27377cde..34b1e9e39e 100644 --- a/activerecord/lib/active_record/connection_adapters/type.rb +++ b/activerecord/lib/active_record/connection_adapters/type.rb @@ -1,8 +1,28 @@ require 'active_record/connection_adapters/type/value' +require 'active_record/connection_adapters/type/binary' +require 'active_record/connection_adapters/type/boolean' +require 'active_record/connection_adapters/type/date' +require 'active_record/connection_adapters/type/date_time' +require 'active_record/connection_adapters/type/decimal' +require 'active_record/connection_adapters/type/float' +require 'active_record/connection_adapters/type/integer' +require 'active_record/connection_adapters/type/string' +require 'active_record/connection_adapters/type/text' +require 'active_record/connection_adapters/type/time' +require 'active_record/connection_adapters/type/timestamp' +require 'active_record/connection_adapters/type/type_map' module ActiveRecord module ConnectionAdapters module Type # :nodoc: + class << self + def extract_scale(sql_type) + case sql_type + when /^(numeric|decimal|number)\((\d+)\)/i then 0 + when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i + end + end + end end end end diff --git a/activerecord/lib/active_record/connection_adapters/type/binary.rb b/activerecord/lib/active_record/connection_adapters/type/binary.rb new file mode 100644 index 0000000000..168d824d3d --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/binary.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class Binary < Value # :nodoc: + def type + :binary + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/boolean.rb b/activerecord/lib/active_record/connection_adapters/type/boolean.rb new file mode 100644 index 0000000000..938d227632 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/boolean.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class Boolean < Value # :nodoc: + def type + :boolean + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/date.rb b/activerecord/lib/active_record/connection_adapters/type/date.rb new file mode 100644 index 0000000000..1632f3c8f4 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/date.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class Date < Value # :nodoc: + def type + :date + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/date_time.rb b/activerecord/lib/active_record/connection_adapters/type/date_time.rb new file mode 100644 index 0000000000..1d7d3cfbbf --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/date_time.rb @@ -0,0 +1,13 @@ +require 'active_record/connection_adapters/type/timestamp' + +module ActiveRecord + module ConnectionAdapters + module Type + class DateTime < Timestamp # :nodoc: + def type + :datetime + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/decimal.rb b/activerecord/lib/active_record/connection_adapters/type/decimal.rb new file mode 100644 index 0000000000..5b39ea9e2f --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/decimal.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class Decimal < Value # :nodoc: + def type + :decimal + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/float.rb b/activerecord/lib/active_record/connection_adapters/type/float.rb new file mode 100644 index 0000000000..089169e7c9 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/float.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class Float < Value # :nodoc: + def type + :float + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/integer.rb b/activerecord/lib/active_record/connection_adapters/type/integer.rb new file mode 100644 index 0000000000..5510a11bd4 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/integer.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class Integer < Value # :nodoc: + def type + :integer + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/string.rb b/activerecord/lib/active_record/connection_adapters/type/string.rb new file mode 100644 index 0000000000..0feb4299f5 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/string.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class String < Value # :nodoc: + def type + :string + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/text.rb b/activerecord/lib/active_record/connection_adapters/type/text.rb new file mode 100644 index 0000000000..ee5842a3fc --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/text.rb @@ -0,0 +1,13 @@ +require 'active_record/connection_adapters/type/string' + +module ActiveRecord + module ConnectionAdapters + module Type + class Text < String # :nodoc: + def type + :text + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/time.rb b/activerecord/lib/active_record/connection_adapters/type/time.rb new file mode 100644 index 0000000000..a3a687a8ad --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/time.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class Time < Value # :nodoc: + def type + :time + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/timestamp.rb b/activerecord/lib/active_record/connection_adapters/type/timestamp.rb new file mode 100644 index 0000000000..92bf0a1954 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/timestamp.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class Timestamp < Value # :nodoc: + def type + :timestamp + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/type_map.rb b/activerecord/lib/active_record/connection_adapters/type/type_map.rb new file mode 100644 index 0000000000..d89171a820 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/type_map.rb @@ -0,0 +1,50 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class TypeMap # :nodoc: + def initialize + @mapping = {} + end + + def lookup(lookup_key) + matching_pair = @mapping.reverse_each.detect do |key, _| + key === lookup_key + end + + if matching_pair + matching_pair.last.call(lookup_key) + else + default_value + end + end + + def register_type(key, value = nil, &block) + raise ::ArgumentError unless value || block + + if block + @mapping[key] = block + else + @mapping[key] = proc { value } + end + end + + def alias_type(key, target_key) + register_type(key) do |sql_type| + metadata = sql_type[/\(.*\)/, 0] + lookup("#{target_key}#{metadata}") + end + end + + def clear + @mapping.clear + end + + private + + def default_value + @default_value ||= Value.new + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/value.rb b/activerecord/lib/active_record/connection_adapters/type/value.rb index 36f680050f..f7d7b9351b 100644 --- a/activerecord/lib/active_record/connection_adapters/type/value.rb +++ b/activerecord/lib/active_record/connection_adapters/type/value.rb @@ -2,6 +2,7 @@ module ActiveRecord module ConnectionAdapters module Type class Value # :nodoc: + def type; end end end end |