Advertisement
Guest User

4.1.10 4.2.0 diff on query_methods.rb

a guest
Jun 8th, 2015
336
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.04 KB | None | 0 0
  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
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement