diff options
Diffstat (limited to 'activerecord/lib/active_record/relation.rb')
-rw-r--r-- | activerecord/lib/active_record/relation.rb | 178 |
1 files changed, 74 insertions, 104 deletions
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 7a1552856b..ccd75ec5d2 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -1,5 +1,3 @@ -require "arel/collectors/bind" - module ActiveRecord # = Active Record \Relation class Relation @@ -31,9 +29,7 @@ module ActiveRecord end def initialize_copy(other) - # This method is a hot spot, so for now, use Hash[] to dup the hash. - # https://bugs.ruby-lang.org/issues/7166 - @values = Hash[@values] + @values = @values.dup reset end @@ -64,8 +60,8 @@ module ActiveRecord @klass.connection.insert( im, - 'SQL', - primary_key, + "SQL", + primary_key || false, primary_key_value, nil, binds) @@ -88,7 +84,7 @@ module ActiveRecord @klass.connection.update( um, - 'SQL', + "SQL", bvs, ) end @@ -247,7 +243,6 @@ module ActiveRecord # Please see further details in the # {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain]. def explain - #TODO: Fix for binds. exec_explain(collecting_queries_for_explain { exec_queries }) end @@ -367,6 +362,9 @@ module ActiveRecord # # # Update all books that match conditions, but limit it to 5 ordered by date # Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David') + # + # # Update all invoices and set the number column to its id value. + # Invoice.update_all('number = id') def update_all(updates) raise ArgumentError, "Empty list of attributes to change" if updates.blank? @@ -375,7 +373,7 @@ module ActiveRecord stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates)) stmt.table(table) - if joins_values.any? + if has_join_values? @klass.connection.join_to_update(stmt, arel, arel_attribute(primary_key)) else stmt.key = arel_attribute(primary_key) @@ -384,7 +382,7 @@ module ActiveRecord stmt.wheres = arel.constraints end - @klass.connection.update stmt, 'SQL', bound_attributes + @klass.connection.update stmt, "SQL", bound_attributes end # Updates an object (or multiple objects) and saves it to the database, if validations pass. @@ -448,16 +446,8 @@ module ActiveRecord # ==== Examples # # Person.where(age: 0..18).destroy_all - def destroy_all(conditions = nil) - if conditions - ActiveSupport::Deprecation.warn(<<-MESSAGE.squish) - Passing conditions to destroy_all is deprecated and will be removed in Rails 5.1. - To achieve the same use where(conditions).destroy_all. - MESSAGE - where(conditions).destroy_all - else - records.each(&:destroy).tap { reset } - end + def destroy_all + records.each(&:destroy).tap { reset } end # Destroy an object (or multiple objects) that has the given id. The object is instantiated first, @@ -505,41 +495,28 @@ module ActiveRecord # # Post.limit(100).delete_all # # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit - def delete_all(conditions = nil) - invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method| - if MULTI_VALUE_METHODS.include?(method) - send("#{method}_values").any? - elsif SINGLE_VALUE_METHODS.include?(method) - send("#{method}_value") - elsif CLAUSE_METHODS.include?(method) - send("#{method}_clause").any? - end - } + def delete_all + invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method| + value = get_value(method) + SINGLE_VALUE_METHODS.include?(method) ? value : value.any? + end if invalid_methods.any? raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}") end - if conditions - ActiveSupport::Deprecation.warn(<<-MESSAGE.squish) - Passing conditions to delete_all is deprecated and will be removed in Rails 5.1. - To achieve the same use where(conditions).delete_all. - MESSAGE - where(conditions).delete_all - else - stmt = Arel::DeleteManager.new - stmt.from(table) + stmt = Arel::DeleteManager.new + stmt.from(table) - if joins_values.any? - @klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key)) - else - stmt.wheres = arel.constraints - end + if has_join_values? + @klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key)) + else + stmt.wheres = arel.constraints + end - affected = @klass.connection.delete(stmt, 'SQL', bound_attributes) + affected = @klass.connection.delete(stmt, "SQL", bound_attributes) - reset - affected - end + reset + affected end # Deletes the row with a primary key matching the +id+ argument, using a @@ -571,8 +548,8 @@ module ActiveRecord # return value is the relation itself, not the records. # # Post.where(published: true).load # => #<ActiveRecord::Relation> - def load - exec_queries unless loaded? + def load(&block) + exec_queries(&block) unless loaded? self end @@ -597,19 +574,16 @@ module ActiveRecord # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar' def to_sql @to_sql ||= begin - relation = self - connection = klass.connection - visitor = connection.visitor + relation = self if eager_loading? find_with_associations { |rel| relation = rel } end - binds = relation.bound_attributes - binds = connection.prepare_binds_for_database(binds) - binds.map! { |value| connection.quote(value) } - collect = visitor.accept(relation.arel.ast, Arel::Collectors::Bind.new) - collect.substitute_binds(binds).join + conn = klass.connection + conn.unprepared_statement { + conn.to_sql(relation.arel, relation.bound_attributes) + } end end @@ -640,15 +614,6 @@ module ActiveRecord includes_values & joins_values end - # {#uniq}[rdoc-ref:QueryMethods#uniq] and - # {#uniq!}[rdoc-ref:QueryMethods#uniq!] are silently deprecated. - # #uniq_value delegates to #distinct_value to maintain backwards compatibility. - # Use #distinct_value instead. - def uniq_value - distinct_value - end - deprecate uniq_value: :distinct_value - # Compares two relations for equality. def ==(other) case other @@ -662,7 +627,7 @@ module ActiveRecord end def pretty_print(q) - q.pp(self.records) + q.pp(records) end # Returns true if relation is blank. @@ -671,12 +636,12 @@ module ActiveRecord end def values - Hash[@values] + @values.dup end def inspect entries = records.take([limit_value, 11].compact.min).map!(&:inspect) - entries[10] = '...' if entries.size == 11 + entries[10] = "..." if entries.size == 11 "#<#{self.class.name} [#{entries.join(', ')}]>" end @@ -690,48 +655,53 @@ module ActiveRecord private - def exec_queries - @records = eager_loading? ? find_with_associations.freeze : @klass.find_by_sql(arel, bound_attributes).freeze - - preload = preload_values - preload += includes_values unless eager_loading? - preloader = build_preloader - preload.each do |associations| - preloader.preload @records, associations + def has_join_values? + joins_values.any? || left_outer_joins_values.any? end - @records.each(&:readonly!) if readonly_value + def exec_queries(&block) + @records = eager_loading? ? find_with_associations.freeze : @klass.find_by_sql(arel, bound_attributes, &block).freeze - @loaded = true - @records - end + preload = preload_values + preload += includes_values unless eager_loading? + preloader = nil + preload.each do |associations| + preloader ||= build_preloader + preloader.preload @records, associations + end - def build_preloader - ActiveRecord::Associations::Preloader.new - end + @records.each(&:readonly!) if readonly_value - def references_eager_loaded_tables? - joined_tables = arel.join_sources.map do |join| - if join.is_a?(Arel::Nodes::StringJoin) - tables_in_string(join.left) - else - [join.left.table_name, join.left.table_alias] - end + @loaded = true + @records end - joined_tables += [table.name, table.table_alias] + def build_preloader + ActiveRecord::Associations::Preloader.new + end - # always convert table names to downcase as in Oracle quoted table names are in uppercase - joined_tables = joined_tables.flatten.compact.map(&:downcase).uniq + def references_eager_loaded_tables? + joined_tables = arel.join_sources.map do |join| + if join.is_a?(Arel::Nodes::StringJoin) + tables_in_string(join.left) + else + [join.left.table_name, join.left.table_alias] + end + end - (references_values - joined_tables).any? - end + joined_tables += [table.name, table.table_alias] - def tables_in_string(string) - return [] if string.blank? - # always convert table names to downcase as in Oracle quoted table names are in uppercase - # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries - string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ['raw_sql_'] - end + # always convert table names to downcase as in Oracle quoted table names are in uppercase + joined_tables = joined_tables.flatten.compact.map(&:downcase).uniq + + (references_values - joined_tables).any? + end + + def tables_in_string(string) + return [] if string.blank? + # always convert table names to downcase as in Oracle quoted table names are in uppercase + # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries + string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ["raw_sql_"] + end end end |