diff options
Diffstat (limited to 'activerecord/lib')
28 files changed, 178 insertions, 54 deletions
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 58a4694880..1844b29ccb 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -49,6 +49,7 @@ module ActiveRecord autoload :ModelSchema autoload :NestedAttributes autoload :NoTouching + autoload :TouchLater autoload :Persistence autoload :QueryCache autoload :Querying diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 5e3e5f709b..c5c2178ee2 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1565,10 +1565,7 @@ module ActiveRecord # # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration # def change - # create_table :developers_projects, id: false do |t| - # t.integer :developer_id - # t.integer :project_id - # end + # create_join_table :developers, :projects # end # end # @@ -1718,10 +1715,8 @@ module ActiveRecord join_model = builder.through_model - # FIXME: we should move this to the internal constants. Also people - # should never directly access this constant so I'm not happy about - # setting it. const_set join_model.name, join_model + private_constant join_model.name middle_reflection = builder.middle_reflection join_model diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb index ec135d49b7..97eb007f62 100644 --- a/activerecord/lib/active_record/associations/builder/belongs_to.rb +++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb @@ -60,7 +60,7 @@ module ActiveRecord::Associations::Builder klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly) end - def self.touch_record(o, foreign_key, name, touch) # :nodoc: + def self.touch_record(o, foreign_key, name, touch, touch_method) # :nodoc: old_foreign_id = o.changed_attributes[foreign_key] if old_foreign_id @@ -75,9 +75,9 @@ module ActiveRecord::Associations::Builder if old_record if touch != true - old_record.touch touch + old_record.send(touch_method, touch) else - old_record.touch + old_record.send(touch_method) end end end @@ -85,9 +85,9 @@ module ActiveRecord::Associations::Builder record = o.send name if record && record.persisted? if touch != true - record.touch touch + record.send(touch_method, touch) else - record.touch + record.send(touch_method) end end end @@ -98,7 +98,8 @@ module ActiveRecord::Associations::Builder touch = reflection.options[:touch] callback = lambda { |record| - BelongsTo.touch_record(record, foreign_key, n, touch) + touch_method = touching_delayed_records? ? :touch : :touch_later + BelongsTo.touch_record(record, foreign_key, n, touch, touch_method) } model.after_save callback, if: :changed? diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb index 93dc4ae118..97b57a6a55 100644 --- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb +++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb @@ -85,7 +85,7 @@ module ActiveRecord::Associations::Builder def middle_reflection(join_model) middle_name = [lhs_model.name.downcase.pluralize, - association_name].join('_').gsub(/::/, '_').to_sym + association_name].join('_'.freeze).gsub('::'.freeze, '_'.freeze).to_sym middle_options = middle_options join_model HasMany.create_reflection(lhs_model, diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 88531205a1..6caadb4ce8 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -370,6 +370,8 @@ module ActiveRecord replace_common_records_in_memory(other_array, original_target) if other_array != original_target transaction { replace_records(other_array, original_target) } + else + other_array end end end diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 4897ec44e9..29e8a0edc1 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -135,7 +135,7 @@ module ActiveRecord if scope.klass.primary_key count = scope.destroy_all.length else - scope.each(&:_run_destroy_callbacks) + scope.each { |record| record.run_callbacks :destroy } arel = scope.arel diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 93550a69f1..67490ecd97 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -309,6 +309,7 @@ module ActiveRecord #:nodoc: include Aggregations include Transactions include NoTouching + include TouchLater include Reflection include Serialization include Store diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index f44e5af5de..2fcba8e309 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -289,25 +289,24 @@ module ActiveRecord end def destroy #:nodoc: - _run_destroy_callbacks { super } + run_callbacks(:destroy) { super } end def touch(*) #:nodoc: - _run_touch_callbacks { super } + run_callbacks(:touch) { super } end private - def create_or_update(*) #:nodoc: - _run_save_callbacks { super } + run_callbacks(:save) { super } end def _create_record #:nodoc: - _run_create_callbacks { super } + run_callbacks(:create) { super } end def _update_record(*) #:nodoc: - _run_update_callbacks { super } + run_callbacks(:update) { super } end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index d99dc9a5db..8c50f3d1a3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -358,7 +358,7 @@ module ActiveRecord synchronize do owner = conn.owner - conn._run_checkin_callbacks do + conn.run_callbacks :checkin do conn.expire end @@ -449,7 +449,7 @@ module ActiveRecord end def checkout_and_verify(c) - c._run_checkout_callbacks do + c.run_callbacks :checkout do c.verify! end c diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index d2840b9498..91c7298983 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -77,7 +77,7 @@ module ActiveRecord # Quotes a string, escaping any ' (single quote) and \ (backslash) # characters. def quote_string(s) - s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode) + s.gsub('\\'.freeze, '\&\&'.freeze).gsub("'".freeze, "''".freeze) # ' (for ruby-mode) end # Quotes the column name. Defaults to no quoting. diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index a768ee2d70..cb83d0022c 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -138,7 +138,7 @@ module ActiveRecord end def foreign_table_name - name.to_s.pluralize + Base.pluralize_table_names ? name.to_s.pluralize : name end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 72e019066e..ecb4868c13 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -377,6 +377,9 @@ module ActiveRecord # [<tt>:force</tt>] # Set to +:cascade+ to drop dependent objects as well. # Defaults to false. + # [<tt>:if_exists</tt>] + # Set to +true+ to only drop the table if it exists. + # Defaults to false. # # Although this command ignores most +options+ and the block if one is given, # it can be helpful to provide these in a migration's +change+ method so it can be reverted. @@ -387,6 +390,10 @@ module ActiveRecord # Adds a new column to the named table. # See TableDefinition#column for details of the options you can use. + # + # Note: Not all options will be available, generally this command should + # ignore most of them. In favor of doing a low-level call to simply + # create a column. def add_column(table_name, column_name, type, options = {}) at = create_alter_table table_name at.add_column(column_name, type, options) @@ -530,6 +537,8 @@ module ActiveRecord # # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active # + # Note: Partial indexes are only supported for PostgreSQL and SQLite 3.8.0+. + # # ====== Creating an index with a specific method # # add_index(:developers, :name, using: 'btree') @@ -667,7 +676,10 @@ module ActiveRecord # remove_reference(:products, :user, index: true, foreign_key: true) # def remove_reference(table_name, ref_name, options = {}) - remove_foreign_key table_name, ref_name.to_s.pluralize if options[:foreign_key] + if options[:foreign_key] + reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name + remove_foreign_key(table_name, reference_name) + end remove_column(table_name, "#{ref_name}_id") remove_column(table_name, "#{ref_name}_type") if options[:polymorphic] @@ -684,8 +696,8 @@ module ActiveRecord # +to_table+ contains the referenced primary key. # # The foreign key will be named after the following pattern: <tt>fk_rails_<identifier></tt>. - # +identifier+ is a 10 character long random string. A custom name can be specified with - # the <tt>:name</tt> option. + # +identifier+ is a 10 character long string which is deterministically generated from the + # +from_table+ and +column+. A custom name can be specified with the <tt>:name</tt> option. # # ====== Creating a simple foreign key # 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 b7c7ff1187..76aee452ca 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -13,6 +13,10 @@ module ActiveRecord end end + class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition + attr_accessor :charset, :collation + end + class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition include ColumnMethods @@ -23,8 +27,16 @@ module ActiveRecord column.type = :integer column.auto_increment = true end + column.charset = options[:charset] + column.collation = options[:collation] column end + + private + + def create_column_definition(name, type) + ColumnDefinition.new(name, type) + end end class Table < ActiveRecord::ConnectionAdapters::Table @@ -60,6 +72,23 @@ module ActiveRecord add_column_position!(change_column_sql, column_options(o.column)) end + def column_options(o) + column_options = super + column_options[:charset] = o.charset + column_options[:collation] = o.collation + column_options + end + + def add_column_options!(sql, options) + if options[:charset] + sql << " CHARACTER SET #{options[:charset]}" + end + if options[:collation] + sql << " COLLATE #{options[:collation]}" + end + super + end + def add_column_position!(sql, options) if options[:first] sql << " FIRST" @@ -99,9 +128,18 @@ module ActiveRecord spec = super spec.delete(:precision) if /time/ === column.sql_type && column.precision == 0 spec.delete(:limit) if :boolean === column.type + if column.collation && table_name = column.instance_variable_get(:@table_name) + @collation_cache ||= {} + @collation_cache[table_name] ||= select_one("SHOW TABLE STATUS LIKE '#{table_name}'")["Collation"] + spec[:collation] = column.collation.inspect if column.collation != @collation_cache[table_name] + end spec end + def migration_keys + super + [:collation] + end + class Column < ConnectionAdapters::Column # :nodoc: delegate :strict, :collation, :extra, to: :sql_type_metadata, allow_nil: true @@ -562,6 +600,21 @@ module ActiveRecord rename_table_indexes(table_name, new_name) end + # Drops a table from the database. + # + # [<tt>:force</tt>] + # Set to +:cascade+ to drop dependent objects as well. + # Defaults to false. + # [<tt>:if_exists</tt>] + # Set to +true+ to only drop the table if it exists. + # Defaults to false. + # [<tt>:temporary</tt>] + # Set to +true+ to drop temporary table. + # Defaults to false. + # + # Although this command ignores most +options+ and the block if one is given, + # it can be helpful to provide these in a migration's +change+ method so it can be reverted. + # In that case, +options+ and the block will be used by create_table. def drop_table(table_name, options = {}) execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}" end diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index a67127bd71..f4dda5154e 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -28,6 +28,7 @@ module ActiveRecord @null = null @default = default @default_function = default_function + @table_name = nil end def has_default? diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index cb0b9cdbe9..168180cfd3 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -87,7 +87,7 @@ module ActiveRecord SQL end - def drop_table(table_name, options = {}) + def drop_table(table_name, options = {}) # :nodoc: execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}" end @@ -422,7 +422,7 @@ module ActiveRecord end # Changes the default value of a table column. - def change_column_default(table_name, column_name, default) + def change_column_default(table_name, column_name, default) # :nodoc: clear_cache! column = column_for(table_name, column_name) return unless column diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 00fc69c878..332ac9d88c 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -568,9 +568,9 @@ module ActiveRecord case default # Quoted types when /\A[\(B]?'(.*)'::/m - $1.gsub(/''/, "'") + $1.gsub("''".freeze, "'".freeze) # Boolean types - when 'true', 'false' + when 'true'.freeze, 'false'.freeze default # Numeric types when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/ diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 9b7cba08de..1ad910c4bc 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -302,7 +302,7 @@ module ActiveRecord assign_attributes(attributes) if attributes yield self if block_given? - _run_initialize_callbacks + run_callbacks :initialize end # Initialize an empty model object from +coder+. +coder+ should be @@ -329,8 +329,8 @@ module ActiveRecord self.class.define_attribute_methods - _run_find_callbacks - _run_initialize_callbacks + run_callbacks :find + run_callbacks :initialize self end @@ -366,7 +366,7 @@ module ActiveRecord @attributes = @attributes.dup @attributes.reset(self.class.primary_key) - _run_initialize_callbacks + run_callbacks(:initialize) @new_record = true @destroyed = false diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index 6b26d7be78..af816a278e 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -31,9 +31,10 @@ module ActiveRecord end def sql(event) - self.class.runtime += event.duration return unless logger.debug? + self.class.runtime += event.duration + payload = event.payload return if IGNORE_PAYLOAD_NAMES.include?(payload[:name]) diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index 75adcccce6..3674f672cb 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -328,12 +328,8 @@ module ActiveRecord @default_attributes = nil @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column @attributes_builder = nil - @column_names = nil - @attribute_types = nil @columns = nil @columns_hash = nil - @content_columns = nil - @default_attributes = nil @attribute_names = nil end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index a6176dffdb..a1e1073792 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -462,9 +462,10 @@ module ActiveRecord # ball = Ball.new # ball.touch(:updated_at) # => raises ActiveRecordError # - def touch(*names, time: current_time_from_proper_timezone) + def touch(*names, time: nil) raise ActiveRecordError, "cannot touch on a new record object" unless persisted? + time ||= current_time_from_proper_timezone attributes = timestamp_attributes_for_update_in_model attributes.concat(names) diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb index af4840476c..8727e46cb3 100644 --- a/activerecord/lib/active_record/railties/controller_runtime.rb +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -19,7 +19,7 @@ module ActiveRecord end def cleanup_view_runtime - if ActiveRecord::Base.connected? + if logger.info? && ActiveRecord::Base.connected? db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime self.db_runtime = (db_runtime || 0) + db_rt_before_render runtime = super diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 8f16de3519..402b317d9c 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -130,9 +130,9 @@ module ActiveRecord # the plucked column names, if they can be deduced. Plucking an SQL fragment # returns String values by default. # - # Person.pluck(:id) - # # SELECT people.id FROM people - # # => [1, 2, 3] + # Person.pluck(:name) + # # SELECT people.name FROM people + # # => ['David', 'Jeremy', 'Jose'] # # Person.pluck(:id, :name) # # SELECT people.id, people.name FROM people @@ -150,6 +150,8 @@ module ActiveRecord # # SELECT DATEDIFF(updated_at, created_at) FROM people # # => ['0', '27761', '173'] # + # See also +ids+. + # def pluck(*column_names) column_names.map! do |column_name| if column_name.is_a?(Symbol) && attribute_alias?(column_name) diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index da95920571..eaeaf0321b 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -105,7 +105,10 @@ HEADER end def table(table, stream) - columns = @connection.columns(table) + columns = @connection.columns(table).map do |column| + column.instance_variable_set(:@table_name, table) + column + end begin tbl = StringIO.new diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb index 5ec2c88b47..3590b8846e 100644 --- a/activerecord/lib/active_record/scoping/default.rb +++ b/activerecord/lib/active_record/scoping/default.rb @@ -35,7 +35,7 @@ module ActiveRecord # Are there attributes associated with this scope? def scope_attributes? # :nodoc: - super || default_scopes.any? + super || default_scopes.any? || respond_to?(:default_scope) end def before_remove_const #:nodoc: diff --git a/activerecord/lib/active_record/touch_later.rb b/activerecord/lib/active_record/touch_later.rb new file mode 100644 index 0000000000..4352a0ffea --- /dev/null +++ b/activerecord/lib/active_record/touch_later.rb @@ -0,0 +1,50 @@ +module ActiveRecord + # = Active Record Touch Later + module TouchLater + extend ActiveSupport::Concern + + included do + before_commit_without_transaction_enrollment :touch_deferred_attributes + end + + def touch_later(*names) # :nodoc: + raise ActiveRecordError, "cannot touch on a new record object" unless persisted? + + @_defer_touch_attrs ||= timestamp_attributes_for_update_in_model + @_defer_touch_attrs |= names + @_touch_time = current_time_from_proper_timezone + + surreptitiously_touch @_defer_touch_attrs + self.class.connection.add_transaction_record self + end + + def touch(*names, time: nil) # :nodoc: + if has_defer_touch_attrs? + names |= @_defer_touch_attrs + end + super(*names, time: time) + end + + private + def surreptitiously_touch(attrs) + attrs.each { |attr| write_attribute attr, @_touch_time } + clear_attribute_changes attrs + end + + def touch_deferred_attributes + if has_defer_touch_attrs? && persisted? + @_touching_delayed_records = true + touch(*@_defer_touch_attrs, time: @_touch_time) + @_touching_delayed_records, @_defer_touch_attrs, @_touch_time = nil, nil, nil + end + end + + def has_defer_touch_attrs? + defined?(@_defer_touch_attrs) && @_defer_touch_attrs.present? + end + + def touching_delayed_records? + defined?(@_touching_delayed_records) && @_touching_delayed_records + end + end +end diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 2293d1b258..311dacb449 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -319,8 +319,8 @@ module ActiveRecord end def before_committed! # :nodoc: - _run_before_commit_without_transaction_enrollment_callbacks - _run_before_commit_callbacks + run_callbacks :before_commit_without_transaction_enrollment + run_callbacks :before_commit end # Call the +after_commit+ callbacks. @@ -329,8 +329,8 @@ module ActiveRecord # but call it after the commit of a destroyed object. def committed!(should_run_callbacks: true) #:nodoc: if should_run_callbacks && destroyed? || persisted? - _run_commit_without_transaction_enrollment_callbacks - _run_commit_callbacks + run_callbacks :commit_without_transaction_enrollment + run_callbacks :commit end ensure force_clear_transaction_record_state @@ -340,8 +340,8 @@ module ActiveRecord # state should be rolled back to the beginning or just to the last savepoint. def rolledback!(force_restore_state: false, should_run_callbacks: true) #:nodoc: if should_run_callbacks - _run_rollback_without_transaction_enrollment_callbacks - _run_rollback_callbacks + run_callbacks :rollback + run_callbacks :rollback_without_transaction_enrollment end ensure restore_transaction_record_state(force_restore_state) diff --git a/activerecord/lib/active_record/validations/presence.rb b/activerecord/lib/active_record/validations/presence.rb index 75d5bd5a35..a9b791397b 100644 --- a/activerecord/lib/active_record/validations/presence.rb +++ b/activerecord/lib/active_record/validations/presence.rb @@ -43,6 +43,10 @@ module ActiveRecord # deletes the associated object, thus putting the parent object into an invalid # state. # + # NOTE: This validation will not fail while using it with an association + # if the latter was assigned but not valid. If you want to ensure that + # it is both present and valid, you also need to use +validates_associated+. + # # Configuration options: # * <tt>:message</tt> - A custom error message (default is: "can't be blank"). # * <tt>:on</tt> - Specifies the contexts where this validation is active. diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 9be4b10a55..5106f4e127 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -76,6 +76,8 @@ module ActiveRecord klass.connection.case_sensitive_comparison(table, attribute, column, value) end klass.unscoped.where(comparison) + rescue RangeError + klass.none end def scope_relation(record, table, relation) |