activerecord 6.0.1 → 6.1.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1363 -647
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_record/aggregations.rb +5 -6
  6. data/lib/active_record/association_relation.rb +26 -15
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +55 -37
  9. data/lib/active_record/associations/association_scope.rb +19 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +23 -10
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  12. data/lib/active_record/associations/builder/association.rb +32 -5
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +38 -13
  20. data/lib/active_record/associations/collection_proxy.rb +14 -7
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -3
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
  26. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  27. data/lib/active_record/associations/join_dependency.rb +73 -42
  28. data/lib/active_record/associations/preloader/association.rb +49 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +12 -7
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +119 -12
  34. data/lib/active_record/attribute_assignment.rb +10 -9
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  36. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  37. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  38. data/lib/active_record/attribute_methods/query.rb +3 -6
  39. data/lib/active_record/attribute_methods/read.rb +8 -12
  40. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  42. data/lib/active_record/attribute_methods/write.rb +12 -21
  43. data/lib/active_record/attribute_methods.rb +64 -54
  44. data/lib/active_record/attributes.rb +33 -9
  45. data/lib/active_record/autosave_association.rb +56 -41
  46. data/lib/active_record/base.rb +2 -14
  47. data/lib/active_record/callbacks.rb +153 -24
  48. data/lib/active_record/coders/yaml_column.rb +24 -3
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +190 -136
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -38
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -9
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -35
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +145 -52
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +94 -36
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +63 -77
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +136 -111
  62. data/lib/active_record/connection_adapters/column.rb +15 -1
  63. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  64. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  65. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
  67. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  68. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  69. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  70. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  71. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  72. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -13
  73. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  74. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  75. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  76. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
  79. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  81. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +30 -4
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  102. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql_adapter.rb +80 -66
  104. data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
  105. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  106. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
  107. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  108. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  109. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  110. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -57
  111. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  112. data/lib/active_record/connection_adapters.rb +52 -0
  113. data/lib/active_record/connection_handling.rb +218 -87
  114. data/lib/active_record/core.rb +276 -68
  115. data/lib/active_record/counter_cache.rb +4 -1
  116. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  117. data/lib/active_record/database_configurations/database_config.rb +52 -9
  118. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  119. data/lib/active_record/database_configurations/url_config.rb +15 -41
  120. data/lib/active_record/database_configurations.rb +125 -85
  121. data/lib/active_record/delegated_type.rb +209 -0
  122. data/lib/active_record/destroy_association_async_job.rb +36 -0
  123. data/lib/active_record/dynamic_matchers.rb +2 -3
  124. data/lib/active_record/enum.rb +80 -38
  125. data/lib/active_record/errors.rb +47 -12
  126. data/lib/active_record/explain.rb +9 -5
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +10 -17
  129. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  130. data/lib/active_record/fixture_set/render_context.rb +1 -1
  131. data/lib/active_record/fixture_set/table_row.rb +2 -3
  132. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  133. data/lib/active_record/fixtures.rb +58 -12
  134. data/lib/active_record/gem_version.rb +3 -3
  135. data/lib/active_record/inheritance.rb +40 -21
  136. data/lib/active_record/insert_all.rb +42 -9
  137. data/lib/active_record/integration.rb +3 -5
  138. data/lib/active_record/internal_metadata.rb +18 -7
  139. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  140. data/lib/active_record/locking/optimistic.rb +33 -18
  141. data/lib/active_record/locking/pessimistic.rb +6 -2
  142. data/lib/active_record/log_subscriber.rb +28 -9
  143. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  144. data/lib/active_record/middleware/database_selector/resolver.rb +6 -2
  145. data/lib/active_record/middleware/database_selector.rb +4 -2
  146. data/lib/active_record/migration/command_recorder.rb +53 -45
  147. data/lib/active_record/migration/compatibility.rb +75 -21
  148. data/lib/active_record/migration/join_table.rb +0 -1
  149. data/lib/active_record/migration.rb +115 -85
  150. data/lib/active_record/model_schema.rb +117 -15
  151. data/lib/active_record/nested_attributes.rb +2 -5
  152. data/lib/active_record/no_touching.rb +1 -1
  153. data/lib/active_record/null_relation.rb +0 -1
  154. data/lib/active_record/persistence.rb +50 -46
  155. data/lib/active_record/query_cache.rb +15 -5
  156. data/lib/active_record/querying.rb +12 -7
  157. data/lib/active_record/railtie.rb +65 -45
  158. data/lib/active_record/railties/console_sandbox.rb +2 -4
  159. data/lib/active_record/railties/databases.rake +280 -99
  160. data/lib/active_record/readonly_attributes.rb +4 -0
  161. data/lib/active_record/reflection.rb +77 -63
  162. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  163. data/lib/active_record/relation/batches.rb +38 -32
  164. data/lib/active_record/relation/calculations.rb +106 -45
  165. data/lib/active_record/relation/delegation.rb +9 -7
  166. data/lib/active_record/relation/finder_methods.rb +45 -16
  167. data/lib/active_record/relation/from_clause.rb +5 -1
  168. data/lib/active_record/relation/merger.rb +27 -26
  169. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  170. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  171. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  172. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  173. data/lib/active_record/relation/predicate_builder.rb +59 -40
  174. data/lib/active_record/relation/query_methods.rb +341 -188
  175. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  176. data/lib/active_record/relation/spawn_methods.rb +8 -8
  177. data/lib/active_record/relation/where_clause.rb +111 -62
  178. data/lib/active_record/relation.rb +116 -83
  179. data/lib/active_record/result.rb +41 -34
  180. data/lib/active_record/runtime_registry.rb +2 -2
  181. data/lib/active_record/sanitization.rb +6 -17
  182. data/lib/active_record/schema_dumper.rb +34 -4
  183. data/lib/active_record/schema_migration.rb +2 -8
  184. data/lib/active_record/scoping/default.rb +1 -4
  185. data/lib/active_record/scoping/named.rb +7 -18
  186. data/lib/active_record/scoping.rb +0 -1
  187. data/lib/active_record/secure_token.rb +16 -8
  188. data/lib/active_record/serialization.rb +5 -3
  189. data/lib/active_record/signed_id.rb +116 -0
  190. data/lib/active_record/statement_cache.rb +20 -4
  191. data/lib/active_record/store.rb +9 -4
  192. data/lib/active_record/suppressor.rb +2 -2
  193. data/lib/active_record/table_metadata.rb +42 -36
  194. data/lib/active_record/tasks/database_tasks.rb +140 -113
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  197. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  198. data/lib/active_record/test_databases.rb +5 -4
  199. data/lib/active_record/test_fixtures.rb +87 -20
  200. data/lib/active_record/timestamp.rb +4 -7
  201. data/lib/active_record/touch_later.rb +20 -21
  202. data/lib/active_record/transactions.rb +25 -72
  203. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  204. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  205. data/lib/active_record/type/serialized.rb +6 -3
  206. data/lib/active_record/type/time.rb +10 -0
  207. data/lib/active_record/type/type_map.rb +0 -1
  208. data/lib/active_record/type/unsigned_integer.rb +0 -1
  209. data/lib/active_record/type.rb +8 -2
  210. data/lib/active_record/type_caster/connection.rb +0 -1
  211. data/lib/active_record/type_caster/map.rb +8 -5
  212. data/lib/active_record/validations/associated.rb +1 -2
  213. data/lib/active_record/validations/numericality.rb +35 -0
  214. data/lib/active_record/validations/uniqueness.rb +24 -4
  215. data/lib/active_record/validations.rb +3 -3
  216. data/lib/active_record.rb +7 -13
  217. data/lib/arel/attributes/attribute.rb +4 -0
  218. data/lib/arel/collectors/bind.rb +5 -0
  219. data/lib/arel/collectors/composite.rb +8 -0
  220. data/lib/arel/collectors/sql_string.rb +7 -0
  221. data/lib/arel/collectors/substitute_binds.rb +7 -0
  222. data/lib/arel/nodes/binary.rb +82 -8
  223. data/lib/arel/nodes/bind_param.rb +8 -0
  224. data/lib/arel/nodes/casted.rb +21 -9
  225. data/lib/arel/nodes/equality.rb +6 -9
  226. data/lib/arel/nodes/grouping.rb +3 -0
  227. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  228. data/lib/arel/nodes/in.rb +8 -1
  229. data/lib/arel/nodes/infix_operation.rb +13 -1
  230. data/lib/arel/nodes/join_source.rb +1 -1
  231. data/lib/arel/nodes/node.rb +7 -6
  232. data/lib/arel/nodes/ordering.rb +27 -0
  233. data/lib/arel/nodes/sql_literal.rb +3 -0
  234. data/lib/arel/nodes/table_alias.rb +7 -3
  235. data/lib/arel/nodes/unary.rb +0 -1
  236. data/lib/arel/nodes.rb +3 -1
  237. data/lib/arel/predications.rb +17 -24
  238. data/lib/arel/select_manager.rb +1 -2
  239. data/lib/arel/table.rb +13 -5
  240. data/lib/arel/visitors/dot.rb +14 -3
  241. data/lib/arel/visitors/mysql.rb +11 -1
  242. data/lib/arel/visitors/postgresql.rb +15 -5
  243. data/lib/arel/visitors/sqlite.rb +0 -1
  244. data/lib/arel/visitors/to_sql.rb +89 -79
  245. data/lib/arel/visitors/visitor.rb +0 -1
  246. data/lib/arel/visitors.rb +0 -7
  247. data/lib/arel.rb +5 -9
  248. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  249. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  250. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  251. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  252. data/lib/rails/generators/active_record/migration.rb +6 -2
  253. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  254. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  255. metadata +30 -29
  256. data/lib/active_record/attribute_decorators.rb +0 -90
  257. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  258. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  259. data/lib/active_record/define_callbacks.rb +0 -22
  260. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  261. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  262. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  263. data/lib/arel/attributes.rb +0 -22
  264. data/lib/arel/visitors/depth_first.rb +0 -204
  265. data/lib/arel/visitors/ibm_db.rb +0 -34
  266. data/lib/arel/visitors/informix.rb +0 -62
  267. data/lib/arel/visitors/mssql.rb +0 -157
  268. data/lib/arel/visitors/oracle.rb +0 -159
  269. data/lib/arel/visitors/oracle12.rb +0 -66
  270. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -19,6 +19,10 @@ module ActiveRecord
19
19
  def readonly_attributes
20
20
  _attr_readonly
21
21
  end
22
+
23
+ def readonly_attribute?(name) # :nodoc:
24
+ _attr_readonly.include?(name)
25
+ end
22
26
  end
23
27
  end
24
28
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/string/filters"
4
- require "concurrent/map"
5
4
 
6
5
  module ActiveRecord
7
6
  # = Active Record Reflection
@@ -163,13 +162,7 @@ module ActiveRecord
163
162
  # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
164
163
  # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
165
164
  def class_name
166
- @class_name ||= (options[:class_name] || derive_class_name).to_s
167
- end
168
-
169
- JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
170
-
171
- def join_keys
172
- @join_keys ||= get_join_keys(klass)
165
+ @class_name ||= -(options[:class_name] || derive_class_name).to_s
173
166
  end
174
167
 
175
168
  # Returns a list of scopes that should be applied for this Reflection
@@ -183,25 +176,27 @@ module ActiveRecord
183
176
  scope_chain_items = join_scopes(table, predicate_builder)
184
177
  klass_scope = klass_join_scope(table, predicate_builder)
185
178
 
186
- key = join_keys.key
187
- foreign_key = join_keys.foreign_key
188
-
189
- klass_scope.where!(table[key].eq(foreign_table[foreign_key]))
190
-
191
179
  if type
192
180
  klass_scope.where!(type => foreign_klass.polymorphic_name)
193
181
  end
194
182
 
183
+ scope_chain_items.inject(klass_scope, &:merge!)
184
+
185
+ primary_key = join_primary_key
186
+ foreign_key = join_foreign_key
187
+
188
+ klass_scope.where!(table[primary_key].eq(foreign_table[foreign_key]))
189
+
195
190
  if klass.finder_needs_type_condition?
196
191
  klass_scope.where!(klass.send(:type_condition, table))
197
192
  end
198
193
 
199
- scope_chain_items.inject(klass_scope, &:merge!)
194
+ klass_scope
200
195
  end
201
196
 
202
- def join_scopes(table, predicate_builder) # :nodoc:
197
+ def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
203
198
  if scope
204
- [scope_for(build_scope(table, predicate_builder))]
199
+ [scope_for(build_scope(table, predicate_builder, klass))]
205
200
  else
206
201
  []
207
202
  end
@@ -217,14 +212,14 @@ module ActiveRecord
217
212
  end
218
213
 
219
214
  def counter_cache_column
220
- if belongs_to?
215
+ @counter_cache_column ||= if belongs_to?
221
216
  if options[:counter_cache] == true
222
- "#{active_record.name.demodulize.underscore.pluralize}_count"
217
+ -"#{active_record.name.demodulize.underscore.pluralize}_count"
223
218
  elsif options[:counter_cache]
224
- options[:counter_cache].to_s
219
+ -options[:counter_cache].to_s
225
220
  end
226
221
  else
227
- options[:counter_cache] ? options[:counter_cache].to_s : "#{name}_count"
222
+ -(options[:counter_cache]&.to_s || "#{name}_count")
228
223
  end
229
224
  end
230
225
 
@@ -271,7 +266,7 @@ module ActiveRecord
271
266
  def has_cached_counter?
272
267
  options[:counter_cache] ||
273
268
  inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
274
- !!active_record.columns_hash[counter_cache_column]
269
+ active_record.has_attribute?(counter_cache_column)
275
270
  end
276
271
 
277
272
  def counter_must_be_updated_by_has_many?
@@ -286,11 +281,7 @@ module ActiveRecord
286
281
  collect_join_chain
287
282
  end
288
283
 
289
- def get_join_keys(association_klass)
290
- JoinKeys.new(join_primary_key(association_klass), join_foreign_key)
291
- end
292
-
293
- def build_scope(table, predicate_builder = predicate_builder(table))
284
+ def build_scope(table, predicate_builder = predicate_builder(table), klass = self.klass)
294
285
  Relation.create(
295
286
  klass,
296
287
  table: table,
@@ -298,12 +289,8 @@ module ActiveRecord
298
289
  )
299
290
  end
300
291
 
301
- def join_primary_key(*)
302
- foreign_key
303
- end
304
-
305
- def join_foreign_key
306
- active_record_primary_key
292
+ def strict_loading?
293
+ options[:strict_loading]
307
294
  end
308
295
 
309
296
  protected
@@ -427,22 +414,21 @@ module ActiveRecord
427
414
 
428
415
  def initialize(name, scope, options, active_record)
429
416
  super
430
- @type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
431
- @foreign_type = options[:polymorphic] && (options[:foreign_type] || "#{name}_type")
417
+ @type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
418
+ @foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
432
419
  @constructable = calculate_constructable(macro, options)
433
- @association_scope_cache = Concurrent::Map.new
434
420
 
435
421
  if options[:class_name] && options[:class_name].class == Class
436
422
  raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
437
423
  end
438
424
  end
439
425
 
440
- def association_scope_cache(conn, owner, &block)
441
- key = conn.prepared_statements
426
+ def association_scope_cache(klass, owner, &block)
427
+ key = self
442
428
  if polymorphic?
443
429
  key = [key, owner._read_attribute(@foreign_type)]
444
430
  end
445
- @association_scope_cache.compute_if_absent(key) { StatementCache.create(conn, &block) }
431
+ klass.cached_find_by_statement(key, &block)
446
432
  end
447
433
 
448
434
  def constructable? # :nodoc:
@@ -450,24 +436,31 @@ module ActiveRecord
450
436
  end
451
437
 
452
438
  def join_table
453
- @join_table ||= options[:join_table] || derive_join_table
439
+ @join_table ||= -(options[:join_table]&.to_s || derive_join_table)
454
440
  end
455
441
 
456
442
  def foreign_key
457
- @foreign_key ||= options[:foreign_key] || derive_foreign_key.freeze
443
+ @foreign_key ||= -(options[:foreign_key]&.to_s || derive_foreign_key)
458
444
  end
459
445
 
460
446
  def association_foreign_key
461
- @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
447
+ @association_foreign_key ||= -(options[:association_foreign_key]&.to_s || class_name.foreign_key)
462
448
  end
463
449
 
464
- # klass option is necessary to support loading polymorphic associations
465
450
  def association_primary_key(klass = nil)
466
- options[:primary_key] || primary_key(klass || self.klass)
451
+ primary_key(klass || self.klass)
467
452
  end
468
453
 
469
454
  def active_record_primary_key
470
- @active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
455
+ @active_record_primary_key ||= -(options[:primary_key]&.to_s || primary_key(active_record))
456
+ end
457
+
458
+ def join_primary_key(klass = nil)
459
+ foreign_key
460
+ end
461
+
462
+ def join_foreign_key
463
+ active_record_primary_key
471
464
  end
472
465
 
473
466
  def check_validity!
@@ -508,7 +501,7 @@ module ActiveRecord
508
501
  # This is for clearing cache on the reflection. Useful for tests that need to compare
509
502
  # SQL queries on associations.
510
503
  def clear_association_scope_cache # :nodoc:
511
- @association_scope_cache.clear
504
+ klass.initialize_find_by_cache
512
505
  end
513
506
 
514
507
  def nested?
@@ -590,7 +583,6 @@ module ActiveRecord
590
583
  end
591
584
 
592
585
  private
593
-
594
586
  def calculate_constructable(macro, options)
595
587
  true
596
588
  end
@@ -620,7 +612,7 @@ module ActiveRecord
620
612
  end
621
613
 
622
614
  if valid_inverse_reflection?(reflection)
623
- return inverse_name
615
+ inverse_name
624
616
  end
625
617
  end
626
618
  end
@@ -631,6 +623,7 @@ module ActiveRecord
631
623
  # with the current reflection's klass name.
632
624
  def valid_inverse_reflection?(reflection)
633
625
  reflection &&
626
+ foreign_key == reflection.foreign_key &&
634
627
  klass <= reflection.active_record &&
635
628
  can_find_inverse_of_automatically?(reflection)
636
629
  end
@@ -684,10 +677,6 @@ module ActiveRecord
684
677
  Associations::HasManyAssociation
685
678
  end
686
679
  end
687
-
688
- def association_primary_key(klass = nil)
689
- primary_key(klass || self.klass)
690
- end
691
680
  end
692
681
 
693
682
  class HasOneReflection < AssociationReflection # :nodoc:
@@ -704,7 +693,6 @@ module ActiveRecord
704
693
  end
705
694
 
706
695
  private
707
-
708
696
  def calculate_constructable(macro, options)
709
697
  !options[:through]
710
698
  end
@@ -723,6 +711,15 @@ module ActiveRecord
723
711
  end
724
712
  end
725
713
 
714
+ # klass option is necessary to support loading polymorphic associations
715
+ def association_primary_key(klass = nil)
716
+ if primary_key = options[:primary_key]
717
+ @association_primary_key ||= -primary_key.to_s
718
+ else
719
+ primary_key(klass || self.klass)
720
+ end
721
+ end
722
+
726
723
  def join_primary_key(klass = nil)
727
724
  polymorphic? ? association_primary_key(klass) : association_primary_key
728
725
  end
@@ -731,6 +728,10 @@ module ActiveRecord
731
728
  foreign_key
732
729
  end
733
730
 
731
+ def join_foreign_type
732
+ foreign_type
733
+ end
734
+
734
735
  private
735
736
  def can_find_inverse_of_automatically?(_)
736
737
  !polymorphic? && super
@@ -752,8 +753,8 @@ module ActiveRecord
752
753
  # Holds all the metadata about a :through association as it was specified
753
754
  # in the Active Record class.
754
755
  class ThroughReflection < AbstractReflection #:nodoc:
755
- delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for,
756
- :active_record_primary_key, :type, :get_join_keys, to: :source_reflection
756
+ delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
757
+ :active_record_primary_key, :join_foreign_key, to: :source_reflection
757
758
 
758
759
  def initialize(delegate_reflection)
759
760
  @delegate_reflection = delegate_reflection
@@ -839,8 +840,8 @@ module ActiveRecord
839
840
  source_reflection.scopes + super
840
841
  end
841
842
 
842
- def join_scopes(table, predicate_builder) # :nodoc:
843
- source_reflection.join_scopes(table, predicate_builder) + super
843
+ def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
844
+ source_reflection.join_scopes(table, predicate_builder, klass) + super
844
845
  end
845
846
 
846
847
  def has_scope?
@@ -860,7 +861,15 @@ module ActiveRecord
860
861
  def association_primary_key(klass = nil)
861
862
  # Get the "actual" source reflection if the immediate source reflection has a
862
863
  # source reflection itself
863
- actual_source_reflection.options[:primary_key] || primary_key(klass || self.klass)
864
+ if primary_key = actual_source_reflection.options[:primary_key]
865
+ @association_primary_key ||= -primary_key.to_s
866
+ else
867
+ primary_key(klass || self.klass)
868
+ end
869
+ end
870
+
871
+ def join_primary_key(klass = self.klass)
872
+ source_reflection.join_primary_key(klass)
864
873
  end
865
874
 
866
875
  # Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
@@ -909,7 +918,7 @@ module ActiveRecord
909
918
 
910
919
  def check_validity!
911
920
  if through_reflection.nil?
912
- raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
921
+ raise HasManyThroughAssociationNotFoundError.new(active_record, self)
913
922
  end
914
923
 
915
924
  if through_reflection.polymorphic?
@@ -996,16 +1005,17 @@ module ActiveRecord
996
1005
  end
997
1006
 
998
1007
  class PolymorphicReflection < AbstractReflection # :nodoc:
999
- delegate :klass, :scope, :plural_name, :type, :get_join_keys, :scope_for, to: :@reflection
1008
+ delegate :klass, :scope, :plural_name, :type, :join_primary_key, :join_foreign_key,
1009
+ :name, :scope_for, to: :@reflection
1000
1010
 
1001
1011
  def initialize(reflection, previous_reflection)
1002
1012
  @reflection = reflection
1003
1013
  @previous_reflection = previous_reflection
1004
1014
  end
1005
1015
 
1006
- def join_scopes(table, predicate_builder) # :nodoc:
1016
+ def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
1007
1017
  scopes = @previous_reflection.join_scopes(table, predicate_builder) + super
1008
- scopes << build_scope(table, predicate_builder).instance_exec(nil, &source_type_scope)
1018
+ scopes << build_scope(table, predicate_builder, klass).instance_exec(nil, &source_type_scope)
1009
1019
  end
1010
1020
 
1011
1021
  def constraints
@@ -1021,7 +1031,7 @@ module ActiveRecord
1021
1031
  end
1022
1032
 
1023
1033
  class RuntimeReflection < AbstractReflection # :nodoc:
1024
- delegate :scope, :type, :constraints, :get_join_keys, to: :@reflection
1034
+ delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
1025
1035
 
1026
1036
  def initialize(reflection, association)
1027
1037
  @reflection = reflection
@@ -1033,7 +1043,11 @@ module ActiveRecord
1033
1043
  end
1034
1044
 
1035
1045
  def aliased_table
1036
- @aliased_table ||= Arel::Table.new(table_name, type_caster: klass.type_caster)
1046
+ klass.arel_table
1047
+ end
1048
+
1049
+ def join_primary_key(klass = self.klass)
1050
+ @reflection.join_primary_key(klass)
1037
1051
  end
1038
1052
 
1039
1053
  def all_includes; yield; end
@@ -41,19 +41,35 @@ module ActiveRecord
41
41
  end
42
42
  end
43
43
 
44
- # Delegates #delete_all, #update_all, #destroy_all methods to each batch.
44
+ # Deletes records in batches. Returns the total number of rows affected.
45
45
  #
46
- # People.in_batches.delete_all
47
- # People.where('age < 10').in_batches.destroy_all
48
- # People.in_batches.update_all('age = age + 1')
49
- [:delete_all, :update_all, :destroy_all].each do |method|
50
- define_method(method) do |*args, &block|
51
- @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false).each do |relation|
52
- relation.send(method, *args, &block)
53
- end
46
+ # Person.in_batches.delete_all
47
+ #
48
+ # See Relation#delete_all for details of how each batch is deleted.
49
+ def delete_all
50
+ sum(&:delete_all)
51
+ end
52
+
53
+ # Updates records in batches. Returns the total number of rows affected.
54
+ #
55
+ # Person.in_batches.update_all("age = age + 1")
56
+ #
57
+ # See Relation#update_all for details of how each batch is updated.
58
+ def update_all(updates)
59
+ sum do |relation|
60
+ relation.update_all(updates)
54
61
  end
55
62
  end
56
63
 
64
+ # Destroys records in batches.
65
+ #
66
+ # Person.where("age < 10").in_batches.destroy_all
67
+ #
68
+ # See Relation#destroy_all for details of how each batch is destroyed.
69
+ def destroy_all
70
+ each(&:destroy_all)
71
+ end
72
+
57
73
  # Yields an ActiveRecord::Relation object for each batch of records.
58
74
  #
59
75
  # Person.in_batches.each do |relation|
@@ -37,6 +37,7 @@ module ActiveRecord
37
37
  # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
38
38
  # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
39
39
  # an order is present in the relation.
40
+ # * <tt>:order</tt> - Specifies the primary key order (can be :asc or :desc). Defaults to :asc.
40
41
  #
41
42
  # Limits are honored, and if present there is no requirement for the batch
42
43
  # size: it can be less than, equal to, or greater than the limit.
@@ -57,22 +58,22 @@ module ActiveRecord
57
58
  # person.party_all_night!
58
59
  # end
59
60
  #
60
- # NOTE: It's not possible to set the order. That is automatically set to
61
- # ascending on the primary key ("id ASC") to make the batch ordering
62
- # work. This also means that this method only works when the primary key is
61
+ # NOTE: Order can be ascending (:asc) or descending (:desc). It is automatically set to
62
+ # ascending on the primary key ("id ASC").
63
+ # This also means that this method only works when the primary key is
63
64
  # orderable (e.g. an integer or string).
64
65
  #
65
66
  # NOTE: By its nature, batch processing is subject to race conditions if
66
67
  # other processes are modifying the database.
67
- def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
68
+ def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc)
68
69
  if block_given?
69
- find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do |records|
70
+ find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do |records|
70
71
  records.each { |record| yield record }
71
72
  end
72
73
  else
73
- enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
74
+ enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do
74
75
  relation = self
75
- apply_limits(relation, start, finish).size
76
+ apply_limits(relation, start, finish, order).size
76
77
  end
77
78
  end
78
79
  end
@@ -101,6 +102,7 @@ module ActiveRecord
101
102
  # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
102
103
  # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
103
104
  # an order is present in the relation.
105
+ # * <tt>:order</tt> - Specifies the primary key order (can be :asc or :desc). Defaults to :asc.
104
106
  #
105
107
  # Limits are honored, and if present there is no requirement for the batch
106
108
  # size: it can be less than, equal to, or greater than the limit.
@@ -116,23 +118,23 @@ module ActiveRecord
116
118
  # group.each { |person| person.party_all_night! }
117
119
  # end
118
120
  #
119
- # NOTE: It's not possible to set the order. That is automatically set to
120
- # ascending on the primary key ("id ASC") to make the batch ordering
121
- # work. This also means that this method only works when the primary key is
121
+ # NOTE: Order can be ascending (:asc) or descending (:desc). It is automatically set to
122
+ # ascending on the primary key ("id ASC").
123
+ # This also means that this method only works when the primary key is
122
124
  # orderable (e.g. an integer or string).
123
125
  #
124
126
  # NOTE: By its nature, batch processing is subject to race conditions if
125
127
  # other processes are modifying the database.
126
- def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
128
+ def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc)
127
129
  relation = self
128
130
  unless block_given?
129
- return to_enum(:find_in_batches, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
130
- total = apply_limits(relation, start, finish).size
131
+ return to_enum(:find_in_batches, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do
132
+ total = apply_limits(relation, start, finish, order).size
131
133
  (total - 1).div(batch_size) + 1
132
134
  end
133
135
  end
134
136
 
135
- in_batches(of: batch_size, start: start, finish: finish, load: true, error_on_ignore: error_on_ignore) do |batch|
137
+ in_batches(of: batch_size, start: start, finish: finish, load: true, error_on_ignore: error_on_ignore, order: order) do |batch|
136
138
  yield batch.to_a
137
139
  end
138
140
  end
@@ -165,6 +167,7 @@ module ActiveRecord
165
167
  # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
166
168
  # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
167
169
  # an order is present in the relation.
170
+ # * <tt>:order</tt> - Specifies the primary key order (can be :asc or :desc). Defaults to :asc.
168
171
  #
169
172
  # Limits are honored, and if present there is no requirement for the batch
170
173
  # size, it can be less than, equal, or greater than the limit.
@@ -191,19 +194,23 @@ module ActiveRecord
191
194
  #
192
195
  # Person.in_batches.each_record(&:party_all_night!)
193
196
  #
194
- # NOTE: It's not possible to set the order. That is automatically set to
195
- # ascending on the primary key ("id ASC") to make the batch ordering
196
- # consistent. Therefore the primary key must be orderable, e.g. an integer
197
- # or a string.
197
+ # NOTE: Order can be ascending (:asc) or descending (:desc). It is automatically set to
198
+ # ascending on the primary key ("id ASC").
199
+ # This also means that this method only works when the primary key is
200
+ # orderable (e.g. an integer or string).
198
201
  #
199
202
  # NOTE: By its nature, batch processing is subject to race conditions if
200
203
  # other processes are modifying the database.
201
- def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil)
204
+ def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil, order: :asc)
202
205
  relation = self
203
206
  unless block_given?
204
207
  return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
205
208
  end
206
209
 
210
+ unless [:asc, :desc].include?(order)
211
+ raise ArgumentError, ":order must be :asc or :desc, got #{order.inspect}"
212
+ end
213
+
207
214
  if arel.orders.present?
208
215
  act_on_ignored_order(error_on_ignore)
209
216
  end
@@ -214,8 +221,8 @@ module ActiveRecord
214
221
  batch_limit = remaining if remaining < batch_limit
215
222
  end
216
223
 
217
- relation = relation.reorder(batch_order).limit(batch_limit)
218
- relation = apply_limits(relation, start, finish)
224
+ relation = relation.reorder(batch_order(order)).limit(batch_limit)
225
+ relation = apply_limits(relation, start, finish, order)
219
226
  relation.skip_query_cache! # Retaining the results in the query cache would undermine the point of batching
220
227
  batch_relation = relation
221
228
 
@@ -252,29 +259,28 @@ module ActiveRecord
252
259
  end
253
260
 
254
261
  batch_relation = relation.where(
255
- bind_attribute(primary_key, primary_key_offset) { |attr, bind| attr.gt(bind) }
262
+ predicate_builder[primary_key, primary_key_offset, order == :desc ? :lt : :gt]
256
263
  )
257
264
  end
258
265
  end
259
266
 
260
267
  private
261
-
262
- def apply_limits(relation, start, finish)
263
- relation = apply_start_limit(relation, start) if start
264
- relation = apply_finish_limit(relation, finish) if finish
268
+ def apply_limits(relation, start, finish, order)
269
+ relation = apply_start_limit(relation, start, order) if start
270
+ relation = apply_finish_limit(relation, finish, order) if finish
265
271
  relation
266
272
  end
267
273
 
268
- def apply_start_limit(relation, start)
269
- relation.where(bind_attribute(primary_key, start) { |attr, bind| attr.gteq(bind) })
274
+ def apply_start_limit(relation, start, order)
275
+ relation.where(predicate_builder[primary_key, start, order == :desc ? :lteq : :gteq])
270
276
  end
271
277
 
272
- def apply_finish_limit(relation, finish)
273
- relation.where(bind_attribute(primary_key, finish) { |attr, bind| attr.lteq(bind) })
278
+ def apply_finish_limit(relation, finish, order)
279
+ relation.where(predicate_builder[primary_key, finish, order == :desc ? :gteq : :lteq])
274
280
  end
275
281
 
276
- def batch_order
277
- arel_attribute(primary_key).asc
282
+ def batch_order(order)
283
+ table[primary_key].public_send(order)
278
284
  end
279
285
 
280
286
  def act_on_ignored_order(error_on_ignore)