diff options
Diffstat (limited to 'activerecord/lib')
19 files changed, 97 insertions, 56 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index eb21712f96..cd5fdd5964 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1176,6 +1176,12 @@ module ActiveRecord # Specify the foreign key used for the association. By default this is guessed to be the name # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+ # association will use "person_id" as the default <tt>:foreign_key</tt>. + # [:foreign_type] + # Specify the column used to store the associated object's type, if this is a polymorphic + # association. By default this is guessed to be the name of the polymorphic association + # specified on "as" option with a "_type" suffix. So a class that defines a + # <tt>has_many :tags, as: :taggable</tt> association will use "taggable_type" as the + # default <tt>:foreign_type</tt>. # [:primary_key] # Specify the name of the column to use as the primary key for the association. By default this is +id+. # [:dependent] @@ -1323,6 +1329,12 @@ module ActiveRecord # Specify the foreign key used for the association. By default this is guessed to be the name # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association # will use "person_id" as the default <tt>:foreign_key</tt>. + # [:foreign_type] + # Specify the column used to store the associated object's type, if this is a polymorphic + # association. By default this is guessed to be the name of the polymorphic association + # specified on "as" option with a "_type" suffix. So a class that defines a + # <tt>has_one :tag, as: :taggable</tt> association will use "taggable_type" as the + # default <tt>:foreign_type</tt>. # [:primary_key] # Specify the method that returns the primary key used for the association. By default this is +id+. # [:as] diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb index dcbd57e61d..0ac10531e5 100644 --- a/activerecord/lib/active_record/associations/association_scope.rb +++ b/activerecord/lib/active_record/associations/association_scope.rb @@ -10,8 +10,8 @@ module ActiveRecord @block = block end - def bind_value(scope, column, value, alias_tracker) - substitute = alias_tracker.connection.substitute_at(column) + def bind_value(scope, column, value, connection) + substitute = connection.substitute_at(column) scope.bind_values += [[column, @block.call(value)]] substitute end @@ -81,38 +81,38 @@ module ActiveRecord table.create_join(table, table.create_on(constraint), join_type) end - def column_for(table_name, column_name, alias_tracker) - columns = alias_tracker.connection.schema_cache.columns_hash(table_name) + def column_for(table_name, column_name, connection) + columns = connection.schema_cache.columns_hash(table_name) columns[column_name] end - def bind_value(scope, column, value, alias_tracker) - @bind_substitution.bind_value scope, column, value, alias_tracker + def bind_value(scope, column, value, connection) + @bind_substitution.bind_value scope, column, value, connection end - def bind(scope, table_name, column_name, value, tracker) - column = column_for table_name, column_name, tracker - bind_value scope, column, value, tracker + def bind(scope, table_name, column_name, value, connection) + column = column_for table_name, column_name, connection + bind_value scope, column, value, connection end - def last_chain_scope(scope, table, reflection, owner, tracker, assoc_klass) + def last_chain_scope(scope, table, reflection, owner, connection, assoc_klass) join_keys = reflection.join_keys(assoc_klass) key = join_keys.key foreign_key = join_keys.foreign_key - bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker + bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], connection scope = scope.where(table[key].eq(bind_val)) if reflection.type value = owner.class.base_class.name - bind_val = bind scope, table.table_name, reflection.type, value, tracker + bind_val = bind scope, table.table_name, reflection.type, value, connection scope = scope.where(table[reflection.type].eq(bind_val)) else scope end end - def next_chain_scope(scope, table, reflection, tracker, assoc_klass, foreign_table, next_reflection) + def next_chain_scope(scope, table, reflection, connection, assoc_klass, foreign_table, next_reflection) join_keys = reflection.join_keys(assoc_klass) key = join_keys.key foreign_key = join_keys.foreign_key @@ -121,7 +121,7 @@ module ActiveRecord if reflection.type value = next_reflection.klass.base_class.name - bind_val = bind scope, table.table_name, reflection.type, value, tracker + bind_val = bind scope, table.table_name, reflection.type, value, connection scope = scope.where(table[reflection.type].eq(bind_val)) end @@ -131,19 +131,20 @@ module ActiveRecord def add_constraints(scope, owner, assoc_klass, refl, tracker) chain = refl.chain scope_chain = refl.scope_chain + connection = tracker.connection tables = construct_tables(chain, assoc_klass, refl, tracker) owner_reflection = chain.last table = tables.last - scope = last_chain_scope(scope, table, owner_reflection, owner, tracker, assoc_klass) + scope = last_chain_scope(scope, table, owner_reflection, owner, connection, assoc_klass) chain.each_with_index do |reflection, i| table, foreign_table = tables.shift, tables.first unless reflection == chain.last next_reflection = chain[i + 1] - scope = next_chain_scope(scope, table, reflection, tracker, assoc_klass, foreign_table, next_reflection) + scope = next_chain_scope(scope, table, reflection, connection, assoc_klass, foreign_table, next_reflection) end is_first_chain = i == 0 diff --git a/activerecord/lib/active_record/associations/builder/has_many.rb b/activerecord/lib/active_record/associations/builder/has_many.rb index 4c8c826f76..1b87f92170 100644 --- a/activerecord/lib/active_record/associations/builder/has_many.rb +++ b/activerecord/lib/active_record/associations/builder/has_many.rb @@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder end def valid_options - super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table] + super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type] end def self.valid_dependent_options diff --git a/activerecord/lib/active_record/associations/builder/has_one.rb b/activerecord/lib/active_record/associations/builder/has_one.rb index c194c8ae9a..1387717396 100644 --- a/activerecord/lib/active_record/associations/builder/has_one.rb +++ b/activerecord/lib/active_record/associations/builder/has_one.rb @@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder end def valid_options - valid = super + [:as] + valid = super + [:as, :foreign_type] valid += [:through, :source, :source_type] if options[:through] valid end diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb index 33d9d2002c..87274dd4e1 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -12,7 +12,7 @@ module ActiveRecord if value.is_a?(Array) value.map { |v| type_cast_from_user(v) } elsif value.respond_to?(:in_time_zone) - value.in_time_zone + value.in_time_zone || super end end diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb index 05138ae36d..3a76f5262d 100644 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ b/activerecord/lib/active_record/attribute_set/builder.rb @@ -76,7 +76,9 @@ module ActiveRecord unless @materialized values.each_key { |key| self[key] } types.each_key { |key| self[key] } - @materialized = true + unless frozen? + @materialized = true + end end delegate_hash end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index e0a4af5359..c4506885ed 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -263,6 +263,16 @@ module ActiveRecord # QUOTING ================================================== + # Quote date/time values for use in SQL input. Includes microseconds + # if the value is a Time responding to usec. + def quoted_date(value) #:nodoc: + if value.acts_like?(:time) && value.respond_to?(:usec) + "#{super}.#{sprintf("%06d", value.usec)}" + else + super + end + end + # Returns a bind substitution value given a bind +column+ # NOTE: The column param is currently being used by the sqlserver-adapter def substitute_at(column, _unused = 0) 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 a741314ac6..69582ebb6f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -656,14 +656,15 @@ module ActiveRecord m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1) m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1) m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1) - m.register_type %r(^bigint)i, Type::Integer.new(limit: 8) - m.register_type %r(^int)i, Type::Integer.new(limit: 4) - m.register_type %r(^mediumint)i, Type::Integer.new(limit: 3) - m.register_type %r(^smallint)i, Type::Integer.new(limit: 2) - m.register_type %r(^tinyint)i, Type::Integer.new(limit: 1) m.register_type %r(^float)i, Type::Float.new(limit: 24) m.register_type %r(^double)i, Type::Float.new(limit: 53) + register_integer_type m, %r(^bigint)i, limit: 8 + register_integer_type m, %r(^int)i, limit: 4 + register_integer_type m, %r(^mediumint)i, limit: 3 + register_integer_type m, %r(^smallint)i, limit: 2 + register_integer_type m, %r(^tinyint)i, limit: 1 + m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans m.alias_type %r(set)i, 'varchar' m.alias_type %r(year)i, 'integer' @@ -676,6 +677,16 @@ module ActiveRecord end end + def register_integer_type(mapping, key, options) # :nodoc: + mapping.register_type(key) do |sql_type| + if /unsigned/i =~ sql_type + Type::UnsignedInteger.new(options) + else + Type::Integer.new(options) + end + end + 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/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index d19bd80576..75f244b3f3 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -74,14 +74,6 @@ module ActiveRecord @connection.escape(string) end - def quoted_date(value) - if value.acts_like?(:time) && value.respond_to?(:usec) - "#{super}.#{sprintf("%06d", value.usec)}" - else - super - end - end - #-- # CONNECTION MANAGEMENT ==================================== #++ diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb index cd5efe2bb8..c203e6c604 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb @@ -34,6 +34,9 @@ module ActiveRecord end def type_cast_from_user(value) + if value.is_a?(::String) + value = parse_pg_array(value) + end type_cast_array(value, :type_cast_from_user) end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb index 991c41327f..607848884b 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb @@ -43,16 +43,12 @@ module ActiveRecord # Quote date/time values for use in SQL input. Includes microseconds # if the value is a Time responding to usec. def quoted_date(value) #:nodoc: - result = super - if value.acts_like?(:time) && value.respond_to?(:usec) - result = "#{result}.#{sprintf("%06d", value.usec)}" - end - if value.year <= 0 bce_year = format("%04d", -value.year + 1) - result = result.sub(/^-?\d+/, bce_year) + " BC" + super.sub(/^-?\d+/, bce_year) + " BC" + else + super end - result end # Does not quote function default values for UUID columns diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index c3865a8fdd..0f7e0fac01 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -256,16 +256,6 @@ module ActiveRecord %Q("#{name.to_s.gsub('"', '""')}") end - # Quote date/time values for use in SQL input. Includes microseconds - # if the value is a Time responding to usec. - def quoted_date(value) #:nodoc: - if value.respond_to?(:usec) - "#{super}.#{sprintf("%06d", value.usec)}" - else - super - end - end - #-- # DATABASE STATEMENTS ====================================== #++ diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 4202bc5665..9849e03036 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -277,7 +277,7 @@ module ActiveRecord def initialize(name, scope, options, active_record) super @automatic_inverse_of = nil - @type = options[:as] && "#{options[:as]}_type" + @type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type") @foreign_type = options[:foreign_type] || "#{name}_type" @constructable = calculate_constructable(macro, options) @association_scope_cache = {} diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index c8ebb41131..71673324eb 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -177,7 +177,7 @@ module ActiveRecord relation.select_values = column_names.map { |cn| columns_hash.key?(cn) ? arel_table[cn] : cn } - result = klass.connection.select_all(relation.arel, nil, bind_values) + result = klass.connection.select_all(relation.arel, nil, relation.arel.bind_values + bind_values) result.cast_values(klass.column_types) end end @@ -317,7 +317,7 @@ module ActiveRecord relation.group_values = group relation.select_values = select_values - calculated_data = @klass.connection.select_all(relation, nil, bind_values) + calculated_data = @klass.connection.select_all(relation, nil, relation.arel.bind_values + bind_values) if association key_ids = calculated_data.collect { |row| row[group_aliases.first] } diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb index 3c291f28e3..919bc58ba5 100644 --- a/activerecord/lib/active_record/store.rb +++ b/activerecord/lib/active_record/store.rb @@ -15,11 +15,15 @@ module ActiveRecord # You can set custom coder to encode/decode your serialized attributes to/from different formats. # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+. # - # NOTE - If you are using PostgreSQL specific columns like +hstore+ or +json+ there is no need for + # NOTE: If you are using PostgreSQL specific columns like +hstore+ or +json+ there is no need for # the serialization provided by +store+. Simply use +store_accessor+ instead to generate # the accessor methods. Be aware that these columns use a string keyed hash and do not allow access # using a symbol. # + # NOTE: The default validations with the exception of +uniqueness+ will work. + # For example, if you want to check for +uniqueness+ with +hstore+ you will + # need to use a custom validation to handle it. + # # Examples: # # class User < ActiveRecord::Base diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index f92e1de03b..01e8f69b02 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -265,7 +265,7 @@ module ActiveRecord def assert_valid_transaction_action(actions) if (actions - ACTIONS).any? - raise ArgumentError, ":on conditions for after_commit and after_rollback callbacks have to be one of #{ACTIONS.join(",")}" + raise ArgumentError, ":on conditions for after_commit and after_rollback callbacks have to be one of #{ACTIONS}" end end end diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb index e5acbbb6b3..250e8d5b23 100644 --- a/activerecord/lib/active_record/type.rb +++ b/activerecord/lib/active_record/type.rb @@ -17,6 +17,7 @@ require 'active_record/type/serialized' require 'active_record/type/string' require 'active_record/type/text' require 'active_record/type/time' +require 'active_record/type/unsigned_integer' require 'active_record/type/type_map' require 'active_record/type/hash_lookup_type_map' diff --git a/activerecord/lib/active_record/type/integer.rb b/activerecord/lib/active_record/type/integer.rb index 750f353472..fc260a081a 100644 --- a/activerecord/lib/active_record/type/integer.rb +++ b/activerecord/lib/active_record/type/integer.rb @@ -5,7 +5,7 @@ module ActiveRecord def initialize(*) super - @range = -max_value...max_value + @range = min_value...max_value end def type @@ -46,6 +46,10 @@ module ActiveRecord limit = self.limit || 4 1 << (limit * 8 - 1) # 8 bits per byte with one bit for sign end + + def min_value + -max_value + end end end end diff --git a/activerecord/lib/active_record/type/unsigned_integer.rb b/activerecord/lib/active_record/type/unsigned_integer.rb new file mode 100644 index 0000000000..ed3e527483 --- /dev/null +++ b/activerecord/lib/active_record/type/unsigned_integer.rb @@ -0,0 +1,15 @@ +module ActiveRecord + module Type + class UnsignedInteger < Integer # :nodoc: + private + + def max_value + super * 2 + end + + def min_value + 0 + end + end + end +end |