daily pastebin goal
53%
SHARE
TWEET

4.1.10 4.2.0 diff on query_methods.rb

a guest Jun 8th, 2015 229 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
  2. index cfeca05..6e384fa 100644
  3. --- a/activerecord/lib/active_record/relation/query_methods.rb
  4. +++ b/activerecord/lib/active_record/relation/query_methods.rb
  5. @@ -1,4 +1,5 @@
  6.  require 'active_support/core_ext/array/wrap'
  7. +require 'active_support/core_ext/string/filters'
  8.  require 'active_model/forbidden_attributes_protection'
  9.  
  10.  module ActiveRecord
  11. @@ -67,6 +68,7 @@ module ActiveRecord
  12.                                               #
  13.          def #{name}_values=(values)          # def select_values=(values)
  14.            raise ImmutableRelation if @loaded #   raise ImmutableRelation if @loaded
  15. +          check_cached_relation
  16.            @values[:#{name}] = values         #   @values[:select] = values
  17.          end                                  # end
  18.        CODE
  19. @@ -84,11 +86,22 @@ module ActiveRecord
  20.        class_eval <<-CODE, __FILE__, __LINE__ + 1
  21.          def #{name}_value=(value)            # def readonly_value=(value)
  22.            raise ImmutableRelation if @loaded #   raise ImmutableRelation if @loaded
  23. +          check_cached_relation
  24.            @values[:#{name}] = value          #   @values[:readonly] = value
  25.          end                                  # end
  26.        CODE
  27.      end
  28.  
  29. +    def check_cached_relation # :nodoc:
  30. +      if defined?(@arel) && @arel
  31. +        @arel = nil
  32. +        ActiveSupport::Deprecation.warn(<<-MSG.squish)
  33. +          Modifying already cached Relation. The cache will be reset. Use a
  34. +          cloned Relation to prevent this warning.
  35. +        MSG
  36. +      end
  37. +    end
  38. +
  39.      def create_with_value # :nodoc:
  40.        @values[:create_with] || {}
  41.      end
  42. @@ -173,7 +186,7 @@ module ActiveRecord
  43.  
  44.      # Use to indicate that the given +table_names+ are referenced by an SQL string,
  45.      # and should therefore be JOINed in any query rather than loaded separately.
  46. -    # This method only works in conjuction with +includes+.
  47. +    # This method only works in conjunction with +includes+.
  48.      # See #includes for more details.
  49.      #
  50.      #   User.includes(:posts).where("posts.name = 'foo'")
  51. @@ -207,7 +220,7 @@ module ActiveRecord
  52.      # fields are retrieved:
  53.      #
  54.      #   Model.select(:field)
  55. -    #   # => [#<Model field:value>]
  56. +    #   # => [#<Model id: nil, field: "value">]
  57.      #
  58.      # Although in the above example it looks as though this method returns an
  59.      # array, it actually returns a relation object and can have other query
  60. @@ -216,12 +229,12 @@ module ActiveRecord
  61.      # The argument to the method can also be an array of fields.
  62.      #
  63.      #   Model.select(:field, :other_field, :and_one_more)
  64. -    #   # => [#<Model field: "value", other_field: "value", and_one_more: "value">]
  65. +    #   # => [#<Model id: nil, field: "value", other_field: "value", and_one_more: "value">]
  66.      #
  67.      # You can also use one or more strings, which will be used unchanged as SELECT fields.
  68.      #
  69.      #   Model.select('field AS field_one', 'other_field AS field_two')
  70. -    #   # => [#<Model field: "value", other_field: "value">]
  71. +    #   # => [#<Model id: nil, field: "value", other_field: "value">]
  72.      #
  73.      # If an alias was specified, it will be accessible from the resulting objects:
  74.      #
  75. @@ -229,7 +242,7 @@ module ActiveRecord
  76.      #   # => "value"
  77.      #
  78.      # Accessing attributes of an object that do not have fields retrieved by a select
  79. -    # will throw <tt>ActiveModel::MissingAttributeError</tt>:
  80. +    # except +id+ will throw <tt>ActiveModel::MissingAttributeError</tt>:
  81.      #
  82.      #   Model.select(:field).first.other_field
  83.      #   # => ActiveModel::MissingAttributeError: missing attribute: other_field
  84. @@ -266,6 +279,10 @@ module ActiveRecord
  85.      #
  86.      #   User.group('name AS grouped_name, age')
  87.      #   => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
  88. +    #
  89. +    # Passing in an array of attributes to group by is also supported.
  90. +    #   User.select([:id, :first_name]).group(:id, :first_name).first(3)
  91. +    #   => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
  92.      def group(*args)
  93.        check_if_method_has_arguments!(:group, args)
  94.        spawn.group!(*args)
  95. @@ -280,15 +297,6 @@ module ActiveRecord
  96.  
  97.      # Allows to specify an order attribute:
  98.      #
  99. -    #   User.order('name')
  100. -    #   => SELECT "users".* FROM "users" ORDER BY name
  101. -    #
  102. -    #   User.order('name DESC')
  103. -    #   => SELECT "users".* FROM "users" ORDER BY name DESC
  104. -    #
  105. -    #   User.order('name DESC, email')
  106. -    #   => SELECT "users".* FROM "users" ORDER BY name DESC, email
  107. -    #
  108.      #   User.order(:name)
  109.      #   => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
  110.      #
  111. @@ -297,6 +305,15 @@ module ActiveRecord
  112.      #
  113.      #   User.order(:name, email: :desc)
  114.      #   => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
  115. +    #
  116. +    #   User.order('name')
  117. +    #   => SELECT "users".* FROM "users" ORDER BY name
  118. +    #
  119. +    #   User.order('name DESC')
  120. +    #   => SELECT "users".* FROM "users" ORDER BY name DESC
  121. +    #
  122. +    #   User.order('name DESC, email')
  123. +    #   => SELECT "users".* FROM "users" ORDER BY name DESC, email
  124.      def order(*args)
  125.        check_if_method_has_arguments!(:order, args)
  126.        spawn.order!(*args)
  127. @@ -410,19 +427,17 @@ module ActiveRecord
  128.      #   => SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
  129.      def joins(*args)
  130.        check_if_method_has_arguments!(:joins, args)
  131. -
  132. -      args.compact!
  133. -      args.flatten!
  134. -
  135.        spawn.joins!(*args)
  136.      end
  137.  
  138.      def joins!(*args) # :nodoc:
  139. +      args.compact!
  140. +      args.flatten!
  141.        self.joins_values += args
  142.        self
  143.      end
  144.  
  145. -    def bind(value)
  146. +    def bind(value) # :nodoc:
  147.        spawn.bind!(value)
  148.      end
  149.  
  150. @@ -560,18 +575,14 @@ module ActiveRecord
  151.        end
  152.      end
  153.  
  154. -    def where!(opts = :chain, *rest) # :nodoc:
  155. -      if opts == :chain
  156. -        WhereChain.new(self)
  157. -      else
  158. -        if Hash === opts
  159. -          opts = sanitize_forbidden_attributes(opts)
  160. -          references!(PredicateBuilder.references(opts))
  161. -        end
  162. -
  163. -        self.where_values += build_where(opts, rest)
  164. -        self
  165. +    def where!(opts, *rest) # :nodoc:
  166. +      if Hash === opts
  167. +        opts = sanitize_forbidden_attributes(opts)
  168. +        references!(PredicateBuilder.references(opts))
  169.        end
  170. +
  171. +      self.where_values += build_where(opts, rest)
  172. +      self
  173.      end
  174.  
  175.      # Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
  176. @@ -833,7 +844,9 @@ module ActiveRecord
  177.      end
  178.  
  179.      def reverse_order! # :nodoc:
  180. -      self.reverse_order_value = !reverse_order_value
  181. +      orders = order_values.uniq
  182. +      orders.reject!(&:blank?)
  183. +      self.order_values = reverse_sql_order(orders)
  184.        self
  185.      end
  186.  
  187. @@ -849,29 +862,23 @@ module ActiveRecord
  188.  
  189.        build_joins(arel, joins_values.flatten) unless joins_values.empty?
  190.  
  191. -      collapse_wheres(arel, (where_values - ['']).uniq)
  192. +      collapse_wheres(arel, (where_values - [''])) #TODO: Add uniq with real value comparison / ignore uniqs that have binds
  193.  
  194.        arel.having(*having_values.uniq.reject(&:blank?)) unless having_values.empty?
  195.  
  196.        arel.take(connection.sanitize_limit(limit_value)) if limit_value
  197.        arel.skip(offset_value.to_i) if offset_value
  198. -      arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
  199. +
  200. +      arel.group(*group_values.uniq.reject(&:blank?)) unless group_values.empty?
  201.  
  202.        build_order(arel)
  203.  
  204. -      build_select(arel)
  205. +      build_select(arel, select_values.uniq)
  206.  
  207.        arel.distinct(distinct_value)
  208.        arel.from(build_from) if from_value
  209.        arel.lock(lock_value) if lock_value
  210.  
  211. -      # Reorder bind indexes if joins produced bind values
  212. -      bvs = arel.bind_values + bind_values
  213. -      arel.ast.grep(Arel::Nodes::BindParam).each_with_index do |bp, i|
  214. -        column = bvs[i].first
  215. -        bp.replace connection.substitute_at(column, i)
  216. -      end
  217. -
  218.        arel
  219.      end
  220.  
  221. @@ -885,8 +892,9 @@ module ActiveRecord
  222.  
  223.        case scope
  224.        when :order
  225. -        self.reverse_order_value = false
  226.          result = []
  227. +      when :where
  228. +        self.bind_values = []
  229.        else
  230.          result = [] unless single_val_method
  231.        end
  232. @@ -899,7 +907,7 @@ module ActiveRecord
  233.  
  234.        where_values.reject! do |rel|
  235.          case rel
  236. -        when Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual
  237. +        when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThanOrEqual
  238.            subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right)
  239.            subrelation.name == target_value
  240.          end
  241. @@ -937,18 +945,14 @@ module ActiveRecord
  242.      def build_where(opts, other = [])
  243.        case opts
  244.        when String, Array
  245. -        #TODO: Remove duplication with: /activerecord/lib/active_record/sanitization.rb:113
  246. -        values = Hash === other.first ? other.first.values : other
  247. -
  248. -        values.grep(ActiveRecord::Relation) do |rel|
  249. -          self.bind_values += rel.bind_values
  250. -        end
  251. -
  252.          [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
  253.        when Hash
  254.          opts = PredicateBuilder.resolve_column_aliases(klass, opts)
  255. -        attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
  256.  
  257. +        tmp_opts, bind_values = create_binds(opts)
  258. +        self.bind_values += bind_values
  259. +
  260. +        attributes = @klass.send(:expand_hash_conditions_for_aggregates, tmp_opts)
  261.          add_relations_to_bind_values(attributes)
  262.  
  263.          PredicateBuilder.build_from_hash(klass, attributes, table)
  264. @@ -957,6 +961,46 @@ module ActiveRecord
  265.        end
  266.      end
  267.  
  268. +    def create_binds(opts)
  269. +      bindable, non_binds = opts.partition do |column, value|
  270. +        case value
  271. +        when String, Integer, ActiveRecord::StatementCache::Substitute
  272. +          @klass.columns_hash.include? column.to_s
  273. +        else
  274. +          false
  275. +        end
  276. +      end
  277. +
  278. +      association_binds, non_binds = non_binds.partition do |column, value|
  279. +        value.is_a?(Hash) && association_for_table(column)
  280. +      end
  281. +
  282. +      new_opts = {}
  283. +      binds = []
  284. +
  285. +      bindable.each do |(column,value)|
  286. +        binds.push [@klass.columns_hash[column.to_s], value]
  287. +        new_opts[column] = connection.substitute_at(column)
  288. +      end
  289. +
  290. +      association_binds.each do |(column, value)|
  291. +        association_relation = association_for_table(column).klass.send(:relation)
  292. +        association_new_opts, association_bind = association_relation.send(:create_binds, value)
  293. +        new_opts[column] = association_new_opts
  294. +        binds += association_bind
  295. +      end
  296. +
  297. +      non_binds.each { |column,value| new_opts[column] = value }
  298. +
  299. +      [new_opts, binds]
  300. +    end
  301. +
  302. +    def association_for_table(table_name)
  303. +      table_name = table_name.to_s
  304. +      @klass._reflect_on_association(table_name) ||
  305. +        @klass._reflect_on_association(table_name.singularize)
  306. +    end
  307. +
  308.      def build_from
  309.        opts, name = from_value
  310.        case opts
  311. @@ -998,33 +1042,34 @@ module ActiveRecord
  312.          join_list
  313.        )
  314.  
  315. -      joins = join_dependency.join_constraints stashed_association_joins
  316. +      join_infos = join_dependency.join_constraints stashed_association_joins
  317.  
  318. -      joins.each { |join| manager.from(join) }
  319. +      join_infos.each do |info|
  320. +        info.joins.each { |join| manager.from(join) }
  321. +        manager.bind_values.concat info.binds
  322. +      end
  323.  
  324.        manager.join_sources.concat(join_list)
  325.  
  326.        manager
  327.      end
  328.  
  329. -    def build_select(arel)
  330. -      if select_values.any?
  331. -        arel.project(*arel_columns(select_values.uniq))
  332. +    def build_select(arel, selects)
  333. +      if !selects.empty?
  334. +        expanded_select = selects.map do |field|
  335. +          if (Symbol === field || String === field) && columns_hash.key?(field.to_s)
  336. +            arel_table[field]
  337. +          else
  338. +            field
  339. +          end
  340. +        end
  341. +
  342. +        arel.project(*expanded_select)
  343.        else
  344.          arel.project(@klass.arel_table[Arel.star])
  345.        end
  346.      end
  347.  
  348. -    def arel_columns(columns)
  349. -      columns.map do |field|
  350. -        if columns_hash.key?(field.to_s)
  351. -          arel_table[field]
  352. -        else
  353. -          field
  354. -        end
  355. -      end
  356. -    end
  357. -
  358.      def reverse_sql_order(order_query)
  359.        order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
  360.  
  361. @@ -1050,15 +1095,19 @@ module ActiveRecord
  362.      def build_order(arel)
  363.        orders = order_values.uniq
  364.        orders.reject!(&:blank?)
  365. -      orders = reverse_sql_order(orders) if reverse_order_value
  366.  
  367.        arel.order(*orders) unless orders.empty?
  368.      end
  369.  
  370. +    VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
  371. +                        'asc', 'desc', 'ASC', 'DESC'] # :nodoc:
  372. +
  373.      def validate_order_args(args)
  374. -      args.grep(Hash) do |h|
  375. -        unless (h.values - [:asc, :desc]).empty?
  376. -          raise ArgumentError, 'Direction should be :asc or :desc'
  377. +      args.each do |arg|
  378. +        next unless arg.is_a?(Hash)
  379. +        arg.each do |_key, value|
  380. +          raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
  381. +                               "directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
  382.          end
  383.        end
  384.      end
  385. @@ -1080,7 +1129,7 @@ module ActiveRecord
  386.          when Hash
  387.            arg.map { |field, dir|
  388.              field = klass.attribute_alias(field) if klass.attribute_alias?(field)
  389. -            table[field].send(dir)
  390. +            table[field].send(dir.downcase)
  391.            }
  392.          else
  393.            arg
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top