diff options
Diffstat (limited to 'activerecord/lib/active_record/relation.rb')
-rw-r--r-- | activerecord/lib/active_record/relation.rb | 126 |
1 files changed, 54 insertions, 72 deletions
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index d042fe5f8b..ef629dcb3b 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 @@ -279,12 +274,7 @@ module ActiveRecord def empty? return @records.empty? if loaded? - if limit_value == 0 - true - else - c = count(:all) - c.respond_to?(:zero?) ? c.zero? : c.empty? - end + limit_value == 0 || !exists? end # Returns true if there are no records. @@ -389,7 +379,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. @@ -511,15 +501,10 @@ 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 - } + 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 @@ -540,7 +525,7 @@ module ActiveRecord stmt.wheres = arel.constraints end - affected = @klass.connection.delete(stmt, 'SQL', bound_attributes) + affected = @klass.connection.delete(stmt, "SQL", bound_attributes) reset affected @@ -576,8 +561,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 @@ -602,19 +587,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 @@ -667,7 +649,7 @@ module ActiveRecord end def pretty_print(q) - q.pp(self.records) + q.pp(records) end # Returns true if relation is blank. @@ -676,12 +658,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 @@ -695,48 +677,48 @@ module ActiveRecord private - def exec_queries - @records = eager_loading? ? find_with_associations.freeze : @klass.find_by_sql(arel, bound_attributes).freeze + def exec_queries(&block) + @records = eager_loading? ? find_with_associations.freeze : @klass.find_by_sql(arel, bound_attributes, &block).freeze - preload = preload_values - preload += includes_values unless eager_loading? - preloader = build_preloader - preload.each do |associations| - preloader.preload @records, associations - end + preload = preload_values + preload += includes_values unless eager_loading? + preloader = build_preloader + preload.each do |associations| + preloader.preload @records, associations + end - @records.each(&:readonly!) if readonly_value + @records.each(&:readonly!) if readonly_value - @loaded = true - @records - end + @loaded = true + @records + end - def build_preloader - ActiveRecord::Associations::Preloader.new - end + def build_preloader + ActiveRecord::Associations::Preloader.new + end - 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] + 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 - end - joined_tables += [table.name, table.table_alias] + joined_tables += [table.name, table.table_alias] - # always convert table names to downcase as in Oracle quoted table names are in uppercase - joined_tables = joined_tables.flatten.compact.map(&:downcase).uniq + # 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 + (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 + 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 |