From 0b682e4b05c8f58c77c655650af6638c483ac903 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Sat, 17 May 2014 12:24:13 -0600 Subject: 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. --- .../connection_adapters/abstract_adapter.rb | 38 +++++++++++++++- .../connection_adapters/abstract_mysql_adapter.rb | 26 +++++------ .../active_record/connection_adapters/column.rb | 41 +++--------------- .../connection_adapters/mysql_adapter.rb | 1 + .../connection_adapters/postgresql/column.rb | 5 --- .../connection_adapters/postgresql/oid.rb | 13 +++--- .../connection_adapters/postgresql_adapter.rb | 9 ---- .../lib/active_record/connection_adapters/type.rb | 20 +++++++++ .../connection_adapters/type/binary.rb | 11 +++++ .../connection_adapters/type/boolean.rb | 11 +++++ .../active_record/connection_adapters/type/date.rb | 11 +++++ .../connection_adapters/type/date_time.rb | 13 ++++++ .../connection_adapters/type/decimal.rb | 11 +++++ .../connection_adapters/type/float.rb | 11 +++++ .../connection_adapters/type/integer.rb | 11 +++++ .../connection_adapters/type/string.rb | 11 +++++ .../active_record/connection_adapters/type/text.rb | 13 ++++++ .../active_record/connection_adapters/type/time.rb | 11 +++++ .../connection_adapters/type/timestamp.rb | 11 +++++ .../connection_adapters/type/type_map.rb | 50 ++++++++++++++++++++++ .../connection_adapters/type/value.rb | 1 + 21 files changed, 258 insertions(+), 71 deletions(-) create mode 100644 activerecord/lib/active_record/connection_adapters/type/binary.rb create mode 100644 activerecord/lib/active_record/connection_adapters/type/boolean.rb create mode 100644 activerecord/lib/active_record/connection_adapters/type/date.rb create mode 100644 activerecord/lib/active_record/connection_adapters/type/date_time.rb create mode 100644 activerecord/lib/active_record/connection_adapters/type/decimal.rb create mode 100644 activerecord/lib/active_record/connection_adapters/type/float.rb create mode 100644 activerecord/lib/active_record/connection_adapters/type/integer.rb create mode 100644 activerecord/lib/active_record/connection_adapters/type/string.rb create mode 100644 activerecord/lib/active_record/connection_adapters/type/text.rb create mode 100644 activerecord/lib/active_record/connection_adapters/type/time.rb create mode 100644 activerecord/lib/active_record/connection_adapters/type/timestamp.rb create mode 100644 activerecord/lib/active_record/connection_adapters/type/type_map.rb (limited to 'activerecord/lib/active_record') 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 supplier_id in supplier_id int(11). @@ -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 -- cgit v1.2.3