activerecord 5.1.7 → 5.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (259) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +221 -900
  3. data/README.rdoc +3 -3
  4. data/examples/performance.rb +2 -0
  5. data/examples/simple.rb +2 -0
  6. data/lib/active_record.rb +10 -3
  7. data/lib/active_record/aggregations.rb +2 -0
  8. data/lib/active_record/association_relation.rb +2 -0
  9. data/lib/active_record/associations.rb +13 -42
  10. data/lib/active_record/associations/alias_tracker.rb +17 -17
  11. data/lib/active_record/associations/association.rb +11 -22
  12. data/lib/active_record/associations/association_scope.rb +32 -44
  13. data/lib/active_record/associations/belongs_to_association.rb +6 -4
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -1
  15. data/lib/active_record/associations/builder/association.rb +2 -5
  16. data/lib/active_record/associations/builder/belongs_to.rb +7 -12
  17. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  19. data/lib/active_record/associations/builder/has_many.rb +2 -0
  20. data/lib/active_record/associations/builder/has_one.rb +2 -0
  21. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  22. data/lib/active_record/associations/collection_association.rb +41 -33
  23. data/lib/active_record/associations/collection_proxy.rb +11 -14
  24. data/lib/active_record/associations/foreign_association.rb +2 -0
  25. data/lib/active_record/associations/has_many_association.rb +4 -2
  26. data/lib/active_record/associations/has_many_through_association.rb +4 -2
  27. data/lib/active_record/associations/has_one_association.rb +3 -1
  28. data/lib/active_record/associations/has_one_through_association.rb +3 -1
  29. data/lib/active_record/associations/join_dependency.rb +22 -40
  30. data/lib/active_record/associations/join_dependency/join_association.rb +17 -56
  31. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  32. data/lib/active_record/associations/join_dependency/join_part.rb +2 -9
  33. data/lib/active_record/associations/preloader.rb +17 -37
  34. data/lib/active_record/associations/preloader/association.rb +42 -58
  35. data/lib/active_record/associations/preloader/through_association.rb +71 -79
  36. data/lib/active_record/associations/singular_association.rb +14 -10
  37. data/lib/active_record/associations/through_association.rb +3 -1
  38. data/lib/active_record/attribute_assignment.rb +2 -0
  39. data/lib/active_record/attribute_decorators.rb +3 -2
  40. data/lib/active_record/attribute_methods.rb +47 -7
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  42. data/lib/active_record/attribute_methods/dirty.rb +25 -214
  43. data/lib/active_record/attribute_methods/primary_key.rb +7 -6
  44. data/lib/active_record/attribute_methods/query.rb +2 -0
  45. data/lib/active_record/attribute_methods/read.rb +8 -2
  46. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  48. data/lib/active_record/attribute_methods/write.rb +21 -9
  49. data/lib/active_record/attributes.rb +7 -6
  50. data/lib/active_record/autosave_association.rb +5 -11
  51. data/lib/active_record/base.rb +2 -0
  52. data/lib/active_record/callbacks.rb +6 -8
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +2 -0
  55. data/lib/active_record/collection_cache_key.rb +10 -5
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +110 -35
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +120 -28
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +7 -2
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -33
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +13 -5
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +40 -2
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +103 -63
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +62 -90
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +75 -138
  69. data/lib/active_record/connection_adapters/column.rb +3 -1
  70. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +3 -1
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -6
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -30
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +91 -1
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +2 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -1
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -11
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -1
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +3 -5
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -0
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +2 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +11 -7
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +79 -65
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +47 -82
  116. data/lib/active_record/connection_adapters/schema_cache.rb +2 -0
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  118. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +19 -2
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  122. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  123. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +71 -1
  124. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -89
  125. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  126. data/lib/active_record/connection_handling.rb +4 -2
  127. data/lib/active_record/core.rb +27 -57
  128. data/lib/active_record/counter_cache.rb +15 -12
  129. data/lib/active_record/define_callbacks.rb +5 -3
  130. data/lib/active_record/dynamic_matchers.rb +9 -9
  131. data/lib/active_record/enum.rb +15 -13
  132. data/lib/active_record/errors.rb +54 -21
  133. data/lib/active_record/explain.rb +3 -1
  134. data/lib/active_record/explain_registry.rb +2 -0
  135. data/lib/active_record/explain_subscriber.rb +2 -0
  136. data/lib/active_record/fixture_set/file.rb +2 -0
  137. data/lib/active_record/fixtures.rb +40 -24
  138. data/lib/active_record/gem_version.rb +5 -3
  139. data/lib/active_record/inheritance.rb +6 -5
  140. data/lib/active_record/integration.rb +58 -19
  141. data/lib/active_record/internal_metadata.rb +2 -0
  142. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  143. data/lib/active_record/locking/optimistic.rb +31 -20
  144. data/lib/active_record/locking/pessimistic.rb +10 -7
  145. data/lib/active_record/log_subscriber.rb +2 -0
  146. data/lib/active_record/migration.rb +47 -21
  147. data/lib/active_record/migration/command_recorder.rb +11 -9
  148. data/lib/active_record/migration/compatibility.rb +20 -2
  149. data/lib/active_record/migration/join_table.rb +2 -0
  150. data/lib/active_record/model_schema.rb +29 -38
  151. data/lib/active_record/nested_attributes.rb +18 -6
  152. data/lib/active_record/no_touching.rb +3 -1
  153. data/lib/active_record/null_relation.rb +2 -0
  154. data/lib/active_record/persistence.rb +184 -40
  155. data/lib/active_record/query_cache.rb +17 -12
  156. data/lib/active_record/querying.rb +3 -1
  157. data/lib/active_record/railtie.rb +54 -1
  158. data/lib/active_record/railties/console_sandbox.rb +2 -0
  159. data/lib/active_record/railties/controller_runtime.rb +2 -0
  160. data/lib/active_record/railties/databases.rake +41 -28
  161. data/lib/active_record/readonly_attributes.rb +3 -2
  162. data/lib/active_record/reflection.rb +100 -182
  163. data/lib/active_record/relation.rb +61 -193
  164. data/lib/active_record/relation/batches.rb +20 -5
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  166. data/lib/active_record/relation/calculations.rb +40 -23
  167. data/lib/active_record/relation/delegation.rb +10 -27
  168. data/lib/active_record/relation/finder_methods.rb +53 -49
  169. data/lib/active_record/relation/from_clause.rb +2 -8
  170. data/lib/active_record/relation/merger.rb +22 -19
  171. data/lib/active_record/relation/predicate_builder.rb +42 -79
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +54 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -6
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  179. data/lib/active_record/relation/query_attribute.rb +9 -2
  180. data/lib/active_record/relation/query_methods.rb +80 -69
  181. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  182. data/lib/active_record/relation/spawn_methods.rb +2 -0
  183. data/lib/active_record/relation/where_clause.rb +50 -67
  184. data/lib/active_record/relation/where_clause_factory.rb +4 -46
  185. data/lib/active_record/result.rb +2 -0
  186. data/lib/active_record/runtime_registry.rb +2 -0
  187. data/lib/active_record/sanitization.rb +15 -9
  188. data/lib/active_record/schema.rb +3 -1
  189. data/lib/active_record/schema_dumper.rb +24 -23
  190. data/lib/active_record/schema_migration.rb +2 -0
  191. data/lib/active_record/scoping.rb +9 -8
  192. data/lib/active_record/scoping/default.rb +6 -7
  193. data/lib/active_record/scoping/named.rb +15 -7
  194. data/lib/active_record/secure_token.rb +2 -0
  195. data/lib/active_record/serialization.rb +2 -0
  196. data/lib/active_record/statement_cache.rb +22 -12
  197. data/lib/active_record/store.rb +2 -0
  198. data/lib/active_record/suppressor.rb +2 -0
  199. data/lib/active_record/table_metadata.rb +3 -1
  200. data/lib/active_record/tasks/database_tasks.rb +23 -12
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
  203. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  204. data/lib/active_record/timestamp.rb +5 -12
  205. data/lib/active_record/touch_later.rb +2 -0
  206. data/lib/active_record/transactions.rb +9 -7
  207. data/lib/active_record/translation.rb +2 -0
  208. data/lib/active_record/type.rb +4 -1
  209. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  210. data/lib/active_record/type/date.rb +2 -0
  211. data/lib/active_record/type/date_time.rb +2 -0
  212. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  213. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  214. data/lib/active_record/type/internal/timezone.rb +2 -0
  215. data/lib/active_record/type/json.rb +30 -0
  216. data/lib/active_record/type/serialized.rb +2 -4
  217. data/lib/active_record/type/text.rb +2 -0
  218. data/lib/active_record/type/time.rb +2 -0
  219. data/lib/active_record/type/type_map.rb +2 -0
  220. data/lib/active_record/type/unsigned_integer.rb +2 -0
  221. data/lib/active_record/type_caster.rb +2 -0
  222. data/lib/active_record/type_caster/connection.rb +2 -0
  223. data/lib/active_record/type_caster/map.rb +2 -0
  224. data/lib/active_record/validations.rb +2 -0
  225. data/lib/active_record/validations/absence.rb +2 -0
  226. data/lib/active_record/validations/associated.rb +2 -0
  227. data/lib/active_record/validations/length.rb +2 -0
  228. data/lib/active_record/validations/presence.rb +2 -0
  229. data/lib/active_record/validations/uniqueness.rb +36 -6
  230. data/lib/active_record/version.rb +2 -0
  231. data/lib/rails/generators/active_record.rb +3 -1
  232. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  233. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  234. data/lib/rails/generators/active_record/migration.rb +2 -0
  235. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  236. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  237. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  238. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  239. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  240. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  241. metadata +25 -38
  242. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  243. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  244. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  245. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  246. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  247. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  248. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  249. data/lib/active_record/attribute.rb +0 -240
  250. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  251. data/lib/active_record/attribute_mutation_tracker.rb +0 -122
  252. data/lib/active_record/attribute_set.rb +0 -113
  253. data/lib/active_record/attribute_set/builder.rb +0 -126
  254. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  255. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  256. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  257. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  258. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  259. data/lib/active_record/type/internal/abstract_json.rb +0 -37
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  # = Active Record \Relation
3
5
  class Relation
4
6
  MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
5
- :order, :joins, :left_joins, :left_outer_joins, :references,
7
+ :order, :joins, :left_outer_joins, :references,
6
8
  :extending, :unscope]
7
9
 
8
10
  SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
9
- :reverse_order, :distinct, :create_with]
11
+ :reverse_order, :distinct, :create_with, :skip_query_cache]
10
12
  CLAUSE_METHODS = [:where, :having, :from]
11
13
  INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
12
14
 
@@ -18,6 +20,7 @@ module ActiveRecord
18
20
  attr_reader :table, :klass, :loaded, :predicate_builder
19
21
  alias :model :klass
20
22
  alias :loaded? :loaded
23
+ alias :locked? :lock_value
21
24
 
22
25
  def initialize(klass, table, predicate_builder, values = {})
23
26
  @klass = klass
@@ -33,74 +36,6 @@ module ActiveRecord
33
36
  reset
34
37
  end
35
38
 
36
- def insert(values) # :nodoc:
37
- primary_key_value = nil
38
-
39
- if primary_key && Hash === values
40
- primary_key_value = values[values.keys.find { |k|
41
- k.name == primary_key
42
- }]
43
-
44
- if !primary_key_value && klass.prefetch_primary_key?
45
- primary_key_value = klass.next_sequence_value
46
- values[arel_attribute(klass.primary_key)] = primary_key_value
47
- end
48
- end
49
-
50
- im = arel.create_insert
51
- im.into @table
52
-
53
- substitutes, binds = substitute_values values
54
-
55
- if values.empty? # empty insert
56
- im.values = Arel.sql(connection.empty_insert_statement_value)
57
- else
58
- im.insert substitutes
59
- end
60
-
61
- @klass.connection.insert(
62
- im,
63
- "SQL",
64
- primary_key || false,
65
- primary_key_value,
66
- nil,
67
- binds)
68
- end
69
-
70
- def _update_record(values, constraints) # :nodoc:
71
- substitutes, binds = substitute_values values
72
-
73
- scope = @klass.unscoped
74
-
75
- if @klass.finder_needs_type_condition?
76
- scope.unscope!(where: @klass.inheritance_column)
77
- end
78
-
79
- relation = scope.where(constraints)
80
- bvs = binds + relation.bound_attributes
81
- um = relation
82
- .arel
83
- .compile_update(substitutes, @klass.primary_key)
84
-
85
- @klass.connection.update(
86
- um,
87
- "SQL",
88
- bvs,
89
- )
90
- end
91
-
92
- def substitute_values(values) # :nodoc:
93
- binds = []
94
- substitutes = []
95
-
96
- values.each do |arel_attr, value|
97
- binds.push QueryAttribute.new(arel_attr.name, value, klass.type_for_attribute(arel_attr.name))
98
- substitutes.push [arel_attr, Arel::Nodes::BindParam.new]
99
- end
100
-
101
- [substitutes, binds]
102
- end
103
-
104
39
  def arel_attribute(name) # :nodoc:
105
40
  klass.arel_attribute(name, table)
106
41
  end
@@ -247,9 +182,10 @@ module ActiveRecord
247
182
  end
248
183
 
249
184
  # Converts relation objects to Array.
250
- def to_a
185
+ def to_ary
251
186
  records.dup
252
187
  end
188
+ alias to_a to_ary
253
189
 
254
190
  def records # :nodoc:
255
191
  load
@@ -363,6 +299,11 @@ module ActiveRecord
363
299
  def update_all(updates)
364
300
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?
365
301
 
302
+ if eager_loading?
303
+ relation = apply_join_dependency
304
+ return relation.update_all(updates)
305
+ end
306
+
366
307
  stmt = Arel::UpdateManager.new
367
308
 
368
309
  stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
@@ -377,51 +318,7 @@ module ActiveRecord
377
318
  stmt.wheres = arel.constraints
378
319
  end
379
320
 
380
- @klass.connection.update stmt, "SQL", bound_attributes
381
- end
382
-
383
- # Updates an object (or multiple objects) and saves it to the database, if validations pass.
384
- # The resulting object is returned whether the object was saved successfully to the database or not.
385
- #
386
- # ==== Parameters
387
- #
388
- # * +id+ - This should be the id or an array of ids to be updated.
389
- # * +attributes+ - This should be a hash of attributes or an array of hashes.
390
- #
391
- # ==== Examples
392
- #
393
- # # Updates one record
394
- # Person.update(15, user_name: 'Samuel', group: 'expert')
395
- #
396
- # # Updates multiple records
397
- # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
398
- # Person.update(people.keys, people.values)
399
- #
400
- # # Updates multiple records from the result of a relation
401
- # people = Person.where(group: 'expert')
402
- # people.update(group: 'masters')
403
- #
404
- # Note: Updating a large number of records will run an
405
- # UPDATE query for each record, which may cause a performance
406
- # issue. So if it is not needed to run callbacks for each update, it is
407
- # preferred to use #update_all for updating all records using
408
- # a single query.
409
- def update(id = :all, attributes)
410
- if id.is_a?(Array)
411
- id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
412
- elsif id == :all
413
- records.each { |record| record.update(attributes) }
414
- else
415
- if ActiveRecord::Base === id
416
- raise ArgumentError, <<-MSG.squish
417
- You are passing an instance of ActiveRecord::Base to `update`.
418
- Please pass the id of the object by calling `.id`.
419
- MSG
420
- end
421
- object = find(id)
422
- object.update(attributes)
423
- object
424
- end
321
+ @klass.connection.update stmt, "#{@klass} Update All"
425
322
  end
426
323
 
427
324
  # Destroys the records by instantiating each
@@ -444,33 +341,6 @@ module ActiveRecord
444
341
  records.each(&:destroy).tap { reset }
445
342
  end
446
343
 
447
- # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
448
- # therefore all callbacks and filters are fired off before the object is deleted. This method is
449
- # less efficient than #delete but allows cleanup methods and other actions to be run.
450
- #
451
- # This essentially finds the object (or multiple objects) with the given id, creates a new object
452
- # from the attributes, and then calls destroy on it.
453
- #
454
- # ==== Parameters
455
- #
456
- # * +id+ - Can be either an Integer or an Array of Integers.
457
- #
458
- # ==== Examples
459
- #
460
- # # Destroy a single object
461
- # Todo.destroy(1)
462
- #
463
- # # Destroy multiple objects
464
- # todos = [1,2,3]
465
- # Todo.destroy(todos)
466
- def destroy(id)
467
- if id.is_a?(Array)
468
- id.map { |one_id| destroy(one_id) }
469
- else
470
- find(id).destroy
471
- end
472
- end
473
-
474
344
  # Deletes the records without instantiating the records
475
345
  # first, and hence not calling the {#destroy}[rdoc-ref:Persistence#destroy]
476
346
  # method nor invoking callbacks.
@@ -498,6 +368,11 @@ module ActiveRecord
498
368
  raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
499
369
  end
500
370
 
371
+ if eager_loading?
372
+ relation = apply_join_dependency
373
+ return relation.delete_all
374
+ end
375
+
501
376
  stmt = Arel::DeleteManager.new
502
377
  stmt.from(table)
503
378
 
@@ -507,35 +382,12 @@ module ActiveRecord
507
382
  stmt.wheres = arel.constraints
508
383
  end
509
384
 
510
- affected = @klass.connection.delete(stmt, "SQL", bound_attributes)
385
+ affected = @klass.connection.delete(stmt, "#{@klass} Destroy")
511
386
 
512
387
  reset
513
388
  affected
514
389
  end
515
390
 
516
- # Deletes the row with a primary key matching the +id+ argument, using a
517
- # SQL +DELETE+ statement, and returns the number of rows deleted. Active
518
- # Record objects are not instantiated, so the object's callbacks are not
519
- # executed, including any <tt>:dependent</tt> association options.
520
- #
521
- # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
522
- #
523
- # Note: Although it is often much faster than the alternative,
524
- # #destroy, skipping callbacks might bypass business logic in
525
- # your application that ensures referential integrity or performs other
526
- # essential jobs.
527
- #
528
- # ==== Examples
529
- #
530
- # # Delete a single row
531
- # Todo.delete(1)
532
- #
533
- # # Delete multiple rows
534
- # Todo.delete([2,3,4])
535
- def delete(id_or_array)
536
- where(primary_key => id_or_array).delete_all
537
- end
538
-
539
391
  # Causes the records to be loaded from the database if they have not
540
392
  # been loaded already. You can use this if for some reason you need
541
393
  # to explicitly load some records before actually using them. The
@@ -555,8 +407,7 @@ module ActiveRecord
555
407
  end
556
408
 
557
409
  def reset
558
- @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
559
- @should_eager_load = @join_dependency = nil
410
+ @to_sql = @arel = @loaded = @should_eager_load = nil
560
411
  @records = [].freeze
561
412
  @offsets = {}
562
413
  self
@@ -576,7 +427,7 @@ module ActiveRecord
576
427
 
577
428
  conn = klass.connection
578
429
  conn.unprepared_statement {
579
- conn.to_sql(relation.arel, relation.bound_attributes)
430
+ conn.to_sql(relation.arel)
580
431
  }
581
432
  end
582
433
  end
@@ -585,12 +436,12 @@ module ActiveRecord
585
436
  #
586
437
  # User.where(name: 'Oscar').where_values_hash
587
438
  # # => {name: "Oscar"}
588
- def where_values_hash(relation_table_name = table_name)
439
+ def where_values_hash(relation_table_name = klass.table_name)
589
440
  where_clause.to_h(relation_table_name)
590
441
  end
591
442
 
592
443
  def scope_for_create
593
- @scope_for_create ||= where_values_hash.merge(create_with_value)
444
+ where_values_hash.merge!(create_with_value.stringify_keys)
594
445
  end
595
446
 
596
447
  # Returns true if relation needs eager loading.
@@ -650,6 +501,11 @@ module ActiveRecord
650
501
  limit_value || offset_value
651
502
  end
652
503
 
504
+ def alias_tracker(joins = [], aliases = nil) # :nodoc:
505
+ joins += [aliases] if aliases
506
+ ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
507
+ end
508
+
653
509
  protected
654
510
 
655
511
  def load_records(records)
@@ -664,32 +520,44 @@ module ActiveRecord
664
520
  end
665
521
 
666
522
  def exec_queries(&block)
667
- @records =
668
- if eager_loading?
669
- find_with_associations do |relation, join_dependency|
670
- if ActiveRecord::NullRelation === relation
671
- []
672
- else
673
- rows = connection.select_all(relation.arel, "SQL", relation.bound_attributes)
674
- join_dependency.instantiate(rows, &block)
675
- end.freeze
523
+ skip_query_cache_if_necessary do
524
+ @records =
525
+ if eager_loading?
526
+ find_with_associations do |relation, join_dependency|
527
+ if ActiveRecord::NullRelation === relation
528
+ []
529
+ else
530
+ rows = connection.select_all(relation.arel, "SQL")
531
+ join_dependency.instantiate(rows, &block)
532
+ end.freeze
533
+ end
534
+ else
535
+ klass.find_by_sql(arel, &block).freeze
676
536
  end
677
- else
678
- klass.find_by_sql(arel, bound_attributes, &block).freeze
537
+
538
+ preload = preload_values
539
+ preload += includes_values unless eager_loading?
540
+ preloader = nil
541
+ preload.each do |associations|
542
+ preloader ||= build_preloader
543
+ preloader.preload @records, associations
679
544
  end
680
545
 
681
- preload = preload_values
682
- preload += includes_values unless eager_loading?
683
- preloader = nil
684
- preload.each do |associations|
685
- preloader ||= build_preloader
686
- preloader.preload @records, associations
687
- end
546
+ @records.each(&:readonly!) if readonly_value
688
547
 
689
- @records.each(&:readonly!) if readonly_value
548
+ @loaded = true
549
+ @records
550
+ end
551
+ end
690
552
 
691
- @loaded = true
692
- @records
553
+ def skip_query_cache_if_necessary
554
+ if skip_query_cache_value
555
+ uncached do
556
+ yield
557
+ end
558
+ else
559
+ yield
560
+ end
693
561
  end
694
562
 
695
563
  def build_preloader
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_record/relation/batches/batch_enumerator"
2
4
 
3
5
  module ActiveRecord
@@ -45,7 +47,12 @@ module ActiveRecord
45
47
  # handle from 10000 and beyond by setting the +:start+ and +:finish+
46
48
  # option on each worker.
47
49
  #
48
- # # Let's process from record 10_000 on.
50
+ # # In worker 1, let's process until 9999 records.
51
+ # Person.find_each(finish: 9_999) do |person|
52
+ # person.party_all_night!
53
+ # end
54
+ #
55
+ # # In worker 2, let's process from record 10_000 and onwards.
49
56
  # Person.find_each(start: 10_000) do |person|
50
57
  # person.party_all_night!
51
58
  # end
@@ -209,6 +216,7 @@ module ActiveRecord
209
216
 
210
217
  relation = relation.reorder(batch_order).limit(batch_limit)
211
218
  relation = apply_limits(relation, start, finish)
219
+ relation.skip_query_cache! # Retaining the results in the query cache would undermine the point of batching
212
220
  batch_relation = relation
213
221
 
214
222
  loop do
@@ -243,20 +251,27 @@ module ActiveRecord
243
251
  end
244
252
  end
245
253
 
246
- batch_relation = relation.where(arel_attribute(primary_key).gt(primary_key_offset))
254
+ attr = Relation::QueryAttribute.new(primary_key, primary_key_offset, klass.type_for_attribute(primary_key))
255
+ batch_relation = relation.where(arel_attribute(primary_key).gt(Arel::Nodes::BindParam.new(attr)))
247
256
  end
248
257
  end
249
258
 
250
259
  private
251
260
 
252
261
  def apply_limits(relation, start, finish)
253
- relation = relation.where(arel_attribute(primary_key).gteq(start)) if start
254
- relation = relation.where(arel_attribute(primary_key).lteq(finish)) if finish
262
+ if start
263
+ attr = Relation::QueryAttribute.new(primary_key, start, klass.type_for_attribute(primary_key))
264
+ relation = relation.where(arel_attribute(primary_key).gteq(Arel::Nodes::BindParam.new(attr)))
265
+ end
266
+ if finish
267
+ attr = Relation::QueryAttribute.new(primary_key, finish, klass.type_for_attribute(primary_key))
268
+ relation = relation.where(arel_attribute(primary_key).lteq(Arel::Nodes::BindParam.new(attr)))
269
+ end
255
270
  relation
256
271
  end
257
272
 
258
273
  def batch_order
259
- "#{quoted_table_name}.#{quoted_primary_key} ASC"
274
+ arel_attribute(primary_key).asc
260
275
  end
261
276
 
262
277
  def act_on_ignored_order(error_on_ignore)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Batches
3
5
  class BatchEnumerator
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Calculations
3
5
  # Count the records.
@@ -37,7 +39,16 @@ module ActiveRecord
37
39
  # Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
38
40
  # between databases. In invalid cases, an error from the database is thrown.
39
41
  def count(column_name = nil)
40
- return super() if block_given?
42
+ if block_given?
43
+ unless column_name.nil?
44
+ ActiveSupport::Deprecation.warn \
45
+ "When `count' is called with a block, it ignores other arguments. " \
46
+ "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
47
+ end
48
+
49
+ return super()
50
+ end
51
+
41
52
  calculate(:count, column_name)
42
53
  end
43
54
 
@@ -73,7 +84,16 @@ module ActiveRecord
73
84
  #
74
85
  # Person.sum(:age) # => 4562
75
86
  def sum(column_name = nil)
76
- return super() if block_given?
87
+ if block_given?
88
+ unless column_name.nil?
89
+ ActiveSupport::Deprecation.warn \
90
+ "When `sum' is called with a block, it ignores other arguments. " \
91
+ "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
92
+ end
93
+
94
+ return super()
95
+ end
96
+
77
97
  calculate(:sum, column_name)
78
98
  end
79
99
 
@@ -110,15 +130,8 @@ module ActiveRecord
110
130
  # end
111
131
  def calculate(operation, column_name)
112
132
  if has_include?(column_name)
113
- relation = construct_relation_for_association_calculations
114
-
115
- if operation.to_s.downcase == "count"
116
- relation.distinct!
117
- # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
118
- if (column_name == :all || column_name.nil?) && select_values.empty?
119
- relation.order_values = []
120
- end
121
- end
133
+ relation = apply_join_dependency
134
+ relation.distinct! if operation.to_s.downcase == "count"
122
135
 
123
136
  relation.calculate(operation, column_name)
124
137
  else
@@ -167,13 +180,15 @@ module ActiveRecord
167
180
  end
168
181
 
169
182
  if has_include?(column_names.first)
170
- construct_relation_for_association_calculations.pluck(*column_names)
183
+ relation = apply_join_dependency
184
+ relation.pluck(*column_names)
171
185
  else
186
+ enforce_raw_sql_whitelist(column_names)
172
187
  relation = spawn
173
188
  relation.select_values = column_names.map { |cn|
174
189
  @klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn
175
190
  }
176
- result = klass.connection.select_all(relation.arel, nil, bound_attributes)
191
+ result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
177
192
  result.cast_values(klass.attribute_types)
178
193
  end
179
194
  end
@@ -202,7 +217,7 @@ module ActiveRecord
202
217
  if operation == "count"
203
218
  column_name ||= select_for_count
204
219
  if column_name == :all
205
- if distinct && (group_values.any? || select_values.empty? && order_values.empty?)
220
+ if distinct && (group_values.any? || !(has_limit_or_offset? && order_values.any?))
206
221
  column_name = primary_key
207
222
  end
208
223
  elsif column_name =~ /\s*DISTINCT[\s(]+/i
@@ -234,7 +249,7 @@ module ActiveRecord
234
249
  def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
235
250
  column_alias = column_name
236
251
 
237
- if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
252
+ if operation == "count" && has_limit_or_offset?
238
253
  # Shortcut when limit is zero.
239
254
  return 0 if limit_value == 0
240
255
 
@@ -257,7 +272,7 @@ module ActiveRecord
257
272
  query_builder = relation.arel
258
273
  end
259
274
 
260
- result = @klass.connection.select_all(query_builder, nil, bound_attributes)
275
+ result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil) }
261
276
  row = result.first
262
277
  value = row && row.values.first
263
278
  type = result.column_types.fetch(column_alias) do
@@ -308,7 +323,7 @@ module ActiveRecord
308
323
  relation.group_values = group_fields
309
324
  relation.select_values = select_values
310
325
 
311
- calculated_data = @klass.connection.select_all(relation, nil, relation.bound_attributes)
326
+ calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
312
327
 
313
328
  if association
314
329
  key_ids = calculated_data.collect { |row| row[group_aliases.first] }
@@ -376,12 +391,14 @@ module ActiveRecord
376
391
  end
377
392
 
378
393
  def build_count_subquery(relation, column_name, distinct)
379
- if column_name == :all
380
- relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
381
- else
382
- column_alias = Arel.sql("count_column")
383
- relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
384
- end
394
+ relation.select_values = [
395
+ if column_name == :all
396
+ distinct ? table[Arel.star] : Arel.sql(FinderMethods::ONE_AS_ONE)
397
+ else
398
+ column_alias = Arel.sql("count_column")
399
+ aggregate_column(column_name).as(column_alias)
400
+ end
401
+ ]
385
402
 
386
403
  subquery = relation.arel.as(Arel.sql("subquery_for_count"))
387
404
  select_value = operation_over_aggregate_column(column_alias || Arel.star, "count", false)