activerecord 6.1.6 → 7.0.0.alpha1

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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +728 -1279
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +0 -10
  7. data/lib/active_record/associations/association.rb +31 -9
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +8 -2
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  13. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  14. data/lib/active_record/associations/builder/has_many.rb +3 -2
  15. data/lib/active_record/associations/builder/has_one.rb +2 -1
  16. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  17. data/lib/active_record/associations/collection_association.rb +14 -23
  18. data/lib/active_record/associations/collection_proxy.rb +8 -3
  19. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  20. data/lib/active_record/associations/has_many_association.rb +1 -1
  21. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  22. data/lib/active_record/associations/has_one_association.rb +10 -7
  23. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  24. data/lib/active_record/associations/preloader/association.rb +161 -47
  25. data/lib/active_record/associations/preloader/batch.rb +51 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +37 -11
  28. data/lib/active_record/associations/preloader.rb +46 -110
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +1 -1
  31. data/lib/active_record/associations.rb +76 -81
  32. data/lib/active_record/asynchronous_queries_tracker.rb +57 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +41 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +7 -5
  39. data/lib/active_record/attribute_methods/serialization.rb +66 -12
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +6 -9
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +3 -18
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -0
  48. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +31 -558
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +11 -4
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
  56. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
  57. data/lib/active_record/connection_adapters/abstract/transaction.rb +3 -3
  58. data/lib/active_record/connection_adapters/abstract_adapter.rb +112 -66
  59. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
  60. data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -23
  61. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
  62. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
  63. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  64. data/lib/active_record/connection_adapters/pool_config.rb +1 -3
  65. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -14
  66. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  67. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  69. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  70. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  73. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  74. data/lib/active_record/connection_adapters/postgresql/quoting.rb +6 -6
  75. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  76. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +157 -100
  79. data/lib/active_record/connection_adapters/schema_cache.rb +36 -37
  80. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -19
  81. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
  82. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
  83. data/lib/active_record/connection_adapters.rb +6 -5
  84. data/lib/active_record/connection_handling.rb +20 -38
  85. data/lib/active_record/core.rb +111 -110
  86. data/lib/active_record/database_configurations/connection_url_resolver.rb +0 -1
  87. data/lib/active_record/database_configurations/database_config.rb +12 -0
  88. data/lib/active_record/database_configurations/hash_config.rb +27 -1
  89. data/lib/active_record/database_configurations/url_config.rb +2 -2
  90. data/lib/active_record/database_configurations.rb +17 -9
  91. data/lib/active_record/delegated_type.rb +33 -11
  92. data/lib/active_record/destroy_association_async_job.rb +1 -1
  93. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  94. data/lib/active_record/dynamic_matchers.rb +1 -1
  95. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  96. data/lib/active_record/encryption/cipher.rb +53 -0
  97. data/lib/active_record/encryption/config.rb +44 -0
  98. data/lib/active_record/encryption/configurable.rb +61 -0
  99. data/lib/active_record/encryption/context.rb +35 -0
  100. data/lib/active_record/encryption/contexts.rb +72 -0
  101. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  102. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  103. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  104. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  105. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  106. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  107. data/lib/active_record/encryption/encryptor.rb +155 -0
  108. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  109. data/lib/active_record/encryption/errors.rb +15 -0
  110. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  111. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
  112. data/lib/active_record/encryption/key.rb +28 -0
  113. data/lib/active_record/encryption/key_generator.rb +42 -0
  114. data/lib/active_record/encryption/key_provider.rb +46 -0
  115. data/lib/active_record/encryption/message.rb +33 -0
  116. data/lib/active_record/encryption/message_serializer.rb +80 -0
  117. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  118. data/lib/active_record/encryption/properties.rb +76 -0
  119. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  120. data/lib/active_record/encryption/scheme.rb +99 -0
  121. data/lib/active_record/encryption.rb +55 -0
  122. data/lib/active_record/enum.rb +41 -41
  123. data/lib/active_record/errors.rb +66 -3
  124. data/lib/active_record/fixture_set/file.rb +15 -1
  125. data/lib/active_record/fixture_set/table_row.rb +40 -5
  126. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  127. data/lib/active_record/fixtures.rb +16 -11
  128. data/lib/active_record/future_result.rb +139 -0
  129. data/lib/active_record/gem_version.rb +4 -4
  130. data/lib/active_record/inheritance.rb +55 -17
  131. data/lib/active_record/insert_all.rb +34 -5
  132. data/lib/active_record/integration.rb +1 -1
  133. data/lib/active_record/internal_metadata.rb +1 -5
  134. data/lib/active_record/locking/optimistic.rb +10 -9
  135. data/lib/active_record/log_subscriber.rb +6 -2
  136. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  137. data/lib/active_record/middleware/database_selector.rb +8 -3
  138. data/lib/active_record/migration/command_recorder.rb +4 -4
  139. data/lib/active_record/migration/compatibility.rb +89 -10
  140. data/lib/active_record/migration/join_table.rb +1 -1
  141. data/lib/active_record/migration.rb +109 -79
  142. data/lib/active_record/model_schema.rb +45 -31
  143. data/lib/active_record/nested_attributes.rb +3 -3
  144. data/lib/active_record/no_touching.rb +2 -2
  145. data/lib/active_record/null_relation.rb +2 -6
  146. data/lib/active_record/persistence.rb +134 -45
  147. data/lib/active_record/query_cache.rb +2 -2
  148. data/lib/active_record/query_logs.rb +203 -0
  149. data/lib/active_record/querying.rb +15 -5
  150. data/lib/active_record/railtie.rb +117 -17
  151. data/lib/active_record/railties/controller_runtime.rb +1 -1
  152. data/lib/active_record/railties/databases.rake +72 -48
  153. data/lib/active_record/readonly_attributes.rb +11 -0
  154. data/lib/active_record/reflection.rb +45 -44
  155. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  156. data/lib/active_record/relation/batches.rb +3 -3
  157. data/lib/active_record/relation/calculations.rb +39 -26
  158. data/lib/active_record/relation/delegation.rb +6 -6
  159. data/lib/active_record/relation/finder_methods.rb +31 -22
  160. data/lib/active_record/relation/merger.rb +20 -13
  161. data/lib/active_record/relation/predicate_builder.rb +1 -6
  162. data/lib/active_record/relation/query_attribute.rb +5 -11
  163. data/lib/active_record/relation/query_methods.rb +230 -47
  164. data/lib/active_record/relation/record_fetch_warning.rb +2 -2
  165. data/lib/active_record/relation/spawn_methods.rb +2 -2
  166. data/lib/active_record/relation/where_clause.rb +8 -4
  167. data/lib/active_record/relation.rb +166 -77
  168. data/lib/active_record/result.rb +17 -2
  169. data/lib/active_record/runtime_registry.rb +2 -4
  170. data/lib/active_record/sanitization.rb +11 -7
  171. data/lib/active_record/schema_dumper.rb +3 -3
  172. data/lib/active_record/schema_migration.rb +0 -4
  173. data/lib/active_record/scoping/default.rb +61 -12
  174. data/lib/active_record/scoping/named.rb +3 -11
  175. data/lib/active_record/scoping.rb +40 -22
  176. data/lib/active_record/serialization.rb +1 -1
  177. data/lib/active_record/signed_id.rb +1 -1
  178. data/lib/active_record/tasks/database_tasks.rb +106 -22
  179. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  180. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
  181. data/lib/active_record/test_databases.rb +1 -1
  182. data/lib/active_record/test_fixtures.rb +4 -4
  183. data/lib/active_record/timestamp.rb +3 -4
  184. data/lib/active_record/transactions.rb +9 -14
  185. data/lib/active_record/translation.rb +2 -2
  186. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  187. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  188. data/lib/active_record/type/internal/timezone.rb +2 -2
  189. data/lib/active_record/type/serialized.rb +1 -1
  190. data/lib/active_record/type/type_map.rb +17 -20
  191. data/lib/active_record/type.rb +1 -2
  192. data/lib/active_record/validations/associated.rb +1 -1
  193. data/lib/active_record.rb +170 -2
  194. data/lib/arel/attributes/attribute.rb +0 -8
  195. data/lib/arel/crud.rb +18 -22
  196. data/lib/arel/delete_manager.rb +2 -4
  197. data/lib/arel/insert_manager.rb +2 -3
  198. data/lib/arel/nodes/casted.rb +1 -1
  199. data/lib/arel/nodes/delete_statement.rb +8 -13
  200. data/lib/arel/nodes/insert_statement.rb +2 -2
  201. data/lib/arel/nodes/select_core.rb +2 -2
  202. data/lib/arel/nodes/select_statement.rb +2 -2
  203. data/lib/arel/nodes/update_statement.rb +3 -2
  204. data/lib/arel/predications.rb +1 -1
  205. data/lib/arel/select_manager.rb +10 -4
  206. data/lib/arel/table.rb +0 -1
  207. data/lib/arel/tree_manager.rb +0 -12
  208. data/lib/arel/update_manager.rb +2 -4
  209. data/lib/arel/visitors/dot.rb +80 -90
  210. data/lib/arel/visitors/mysql.rb +6 -1
  211. data/lib/arel/visitors/postgresql.rb +0 -10
  212. data/lib/arel/visitors/to_sql.rb +43 -2
  213. data/lib/arel.rb +1 -1
  214. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  215. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  216. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  217. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  218. metadata +55 -17
@@ -31,6 +31,9 @@ module ActiveRecord
31
31
  @loaded = false
32
32
  @predicate_builder = predicate_builder
33
33
  @delegate_to_klass = false
34
+ @future_result = nil
35
+ @records = nil
36
+ @limited_count = nil
34
37
  end
35
38
 
36
39
  def initialize_copy(other)
@@ -67,8 +70,12 @@ module ActiveRecord
67
70
  # user = users.new { |user| user.name = 'Oscar' }
68
71
  # user.name # => Oscar
69
72
  def new(attributes = nil, &block)
70
- block = current_scope_restoring_block(&block)
71
- scoping { _new(attributes, &block) }
73
+ if attributes.is_a?(Array)
74
+ attributes.collect { |attr| new(attr, &block) }
75
+ else
76
+ block = current_scope_restoring_block(&block)
77
+ scoping { _new(attributes, &block) }
78
+ end
72
79
  end
73
80
  alias build new
74
81
 
@@ -148,7 +155,7 @@ module ActiveRecord
148
155
  # above can be alternatively written this way:
149
156
  #
150
157
  # # Find the first user named "Scarlett" or create a new one with a
151
- # # different last name.
158
+ # # particular last name.
152
159
  # User.find_or_create_by(first_name: 'Scarlett') do |user|
153
160
  # user.last_name = 'Johansson'
154
161
  # end
@@ -175,7 +182,7 @@ module ActiveRecord
175
182
  find_by(attributes) || create!(attributes, &block)
176
183
  end
177
184
 
178
- # Attempts to create a record with the given attributes in a table that has a unique constraint
185
+ # Attempts to create a record with the given attributes in a table that has a unique database constraint
179
186
  # on one or several of its columns. If a row already exists with one or several of these
180
187
  # unique constraints, the exception such an insertion would normally raise is caught,
181
188
  # and the existing record with those attributes is found using #find_by!.
@@ -186,7 +193,7 @@ module ActiveRecord
186
193
  #
187
194
  # There are several drawbacks to #create_or_find_by, though:
188
195
  #
189
- # * The underlying table must have the relevant columns defined with unique constraints.
196
+ # * The underlying table must have the relevant columns defined with unique database constraints.
190
197
  # * A unique constraint violation may be triggered by only one, or at least less than all,
191
198
  # of the given attributes. This means that the subsequent #find_by! may fail to find a
192
199
  # matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
@@ -257,13 +264,20 @@ module ActiveRecord
257
264
 
258
265
  # Returns size of the records.
259
266
  def size
260
- loaded? ? @records.length : count(:all)
267
+ if loaded?
268
+ records.length
269
+ else
270
+ count(:all)
271
+ end
261
272
  end
262
273
 
263
274
  # Returns true if there are no records.
264
275
  def empty?
265
- return @records.empty? if loaded?
266
- !exists?
276
+ if loaded?
277
+ records.empty?
278
+ else
279
+ !exists?
280
+ end
267
281
  end
268
282
 
269
283
  # Returns true if there are no records.
@@ -281,13 +295,15 @@ module ActiveRecord
281
295
  # Returns true if there is exactly one record.
282
296
  def one?
283
297
  return super if block_given?
284
- limit_value ? records.one? : size == 1
298
+ return records.one? if limit_value || loaded?
299
+ limited_count == 1
285
300
  end
286
301
 
287
302
  # Returns true if there is more than one record.
288
303
  def many?
289
304
  return super if block_given?
290
- limit_value ? records.many? : size > 1
305
+ return records.many? if limit_value || loaded?
306
+ limited_count > 1
291
307
  end
292
308
 
293
309
  # Returns a stable cache key that can be used to identify this query.
@@ -344,7 +360,7 @@ module ActiveRecord
344
360
  def compute_cache_version(timestamp_column) # :nodoc:
345
361
  timestamp_column = timestamp_column.to_s
346
362
 
347
- if loaded? || distinct_value
363
+ if loaded?
348
364
  size = records.size
349
365
  if size > 0
350
366
  timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
@@ -357,6 +373,7 @@ module ActiveRecord
357
373
 
358
374
  if collection.has_limit_or_offset?
359
375
  query = collection.select("#{column} AS collection_cache_key_timestamp")
376
+ query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
360
377
  subquery_alias = "subquery_for_cache_key"
361
378
  subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
362
379
  arel = query.build_subquery(subquery_alias, select_values % subquery_column)
@@ -400,15 +417,28 @@ module ActiveRecord
400
417
  # end
401
418
  # # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
402
419
  #
420
+ # If <tt>all_queries: true</tt> is passed, scoping will apply to all queries
421
+ # for the relation including +update+ and +delete+ on instances.
422
+ # Once +all_queries+ is set to true it cannot be set to false in a
423
+ # nested block.
424
+ #
403
425
  # Please check unscoped if you want to remove all previous scopes (including
404
426
  # the default_scope) during the execution of a block.
405
- def scoping
406
- already_in_scope? ? yield : _scoping(self) { yield }
427
+ def scoping(all_queries: nil, &block)
428
+ registry = klass.scope_registry
429
+ if global_scope?(registry) && all_queries == false
430
+ raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
431
+ elsif already_in_scope?(registry)
432
+ yield
433
+ else
434
+ _scoping(self, registry, all_queries, &block)
435
+ end
407
436
  end
408
437
 
409
438
  def _exec_scope(*args, &block) # :nodoc:
410
439
  @delegate_to_klass = true
411
- _scoping(nil) { instance_exec(*args, &block) || self }
440
+ registry = klass.scope_registry
441
+ _scoping(nil, registry) { instance_exec(*args, &block) || self }
412
442
  ensure
413
443
  @delegate_to_klass = false
414
444
  end
@@ -440,17 +470,6 @@ module ActiveRecord
440
470
  def update_all(updates)
441
471
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?
442
472
 
443
- arel = eager_loading? ? apply_join_dependency.arel : build_arel
444
- arel.source.left = table
445
-
446
- stmt = Arel::UpdateManager.new
447
- stmt.table(arel.source)
448
- stmt.key = table[primary_key]
449
- stmt.take(arel.limit)
450
- stmt.offset(arel.offset)
451
- stmt.order(*arel.orders)
452
- stmt.wheres = arel.constraints
453
-
454
473
  if updates.is_a?(Hash)
455
474
  if klass.locking_enabled? &&
456
475
  !updates.key?(klass.locking_column) &&
@@ -458,11 +477,16 @@ module ActiveRecord
458
477
  attr = table[klass.locking_column]
459
478
  updates[attr.name] = _increment_attribute(attr)
460
479
  end
461
- stmt.set _substitute_values(updates)
480
+ values = _substitute_values(updates)
462
481
  else
463
- stmt.set Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
482
+ values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
464
483
  end
465
484
 
485
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel
486
+ arel.source.left = table
487
+
488
+ stmt = arel.compile_update(values, table[primary_key])
489
+
466
490
  klass.connection.update(stmt, "#{klass} Update All").tap { reset }
467
491
  end
468
492
 
@@ -474,6 +498,14 @@ module ActiveRecord
474
498
  end
475
499
  end
476
500
 
501
+ def update!(id = :all, attributes) # :nodoc:
502
+ if id == :all
503
+ each { |record| record.update!(attributes) }
504
+ else
505
+ klass.update!(id, attributes)
506
+ end
507
+ end
508
+
477
509
  # Updates the counters of the records in the current relation.
478
510
  #
479
511
  # ==== Parameters
@@ -583,15 +615,9 @@ module ActiveRecord
583
615
  arel = eager_loading? ? apply_join_dependency.arel : build_arel
584
616
  arel.source.left = table
585
617
 
586
- stmt = Arel::DeleteManager.new
587
- stmt.from(arel.source)
588
- stmt.key = table[primary_key]
589
- stmt.take(arel.limit)
590
- stmt.offset(arel.offset)
591
- stmt.order(*arel.orders)
592
- stmt.wheres = arel.constraints
618
+ stmt = arel.compile_delete(table[primary_key])
593
619
 
594
- klass.connection.delete(stmt, "#{klass} Destroy").tap { reset }
620
+ klass.connection.delete(stmt, "#{klass} Delete All").tap { reset }
595
621
  end
596
622
 
597
623
  # Finds and destroys all records matching the specified conditions.
@@ -620,6 +646,32 @@ module ActiveRecord
620
646
  where(*args).delete_all
621
647
  end
622
648
 
649
+ # Schedule the query to be performed from a background thread pool.
650
+ #
651
+ # Post.where(published: true).load_async # => #<ActiveRecord::Relation>
652
+ def load_async
653
+ return load if !connection.async_enabled?
654
+
655
+ unless loaded?
656
+ result = exec_main_query(async: connection.current_transaction.closed?)
657
+
658
+ if result.is_a?(Array)
659
+ @records = result
660
+ else
661
+ @future_result = result
662
+ end
663
+ @loaded = true
664
+ end
665
+
666
+ self
667
+ end
668
+
669
+ # Returns <tt>true</tt> if the relation was scheduled on the background
670
+ # thread pool.
671
+ def scheduled?
672
+ !!@future_result
673
+ end
674
+
623
675
  # Causes the records to be loaded from the database if they have not
624
676
  # been loaded already. You can use this if for some reason you need
625
677
  # to explicitly load some records before actually using them. The
@@ -627,7 +679,7 @@ module ActiveRecord
627
679
  #
628
680
  # Post.where(published: true).load # => #<ActiveRecord::Relation>
629
681
  def load(&block)
630
- unless loaded?
682
+ if !loaded? || scheduled?
631
683
  @records = exec_queries(&block)
632
684
  @loaded = true
633
685
  end
@@ -642,11 +694,14 @@ module ActiveRecord
642
694
  end
643
695
 
644
696
  def reset
697
+ @future_result&.cancel
698
+ @future_result = nil
645
699
  @delegate_to_klass = false
646
700
  @to_sql = @arel = @loaded = @should_eager_load = nil
647
701
  @offsets = @take = nil
648
702
  @cache_keys = nil
649
- @records = [].freeze
703
+ @records = nil
704
+ @limited_count = nil
650
705
  self
651
706
  end
652
707
 
@@ -655,16 +710,14 @@ module ActiveRecord
655
710
  # User.where(name: 'Oscar').to_sql
656
711
  # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
657
712
  def to_sql
658
- @to_sql ||= begin
659
- if eager_loading?
660
- apply_join_dependency do |relation, join_dependency|
661
- relation = join_dependency.apply_column_aliases(relation)
662
- relation.to_sql
663
- end
664
- else
665
- conn = klass.connection
666
- conn.unprepared_statement { conn.to_sql(arel) }
713
+ @to_sql ||= if eager_loading?
714
+ apply_join_dependency do |relation, join_dependency|
715
+ relation = join_dependency.apply_column_aliases(relation)
716
+ relation.to_sql
667
717
  end
718
+ else
719
+ conn = klass.connection
720
+ conn.unprepared_statement { conn.to_sql(arel) }
668
721
  end
669
722
  end
670
723
 
@@ -722,6 +775,10 @@ module ActiveRecord
722
775
  @values.dup
723
776
  end
724
777
 
778
+ def values_for_queries # :nodoc:
779
+ @values.except(:extending, :skip_query_cache, :strict_loading)
780
+ end
781
+
725
782
  def inspect
726
783
  subject = loaded? ? records : annotate("loading for inspect")
727
784
  entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
@@ -756,11 +813,9 @@ module ActiveRecord
756
813
  def preload_associations(records) # :nodoc:
757
814
  preload = preload_values
758
815
  preload += includes_values unless eager_loading?
759
- preloader = nil
760
816
  scope = strict_loading_value ? StrictLoadingScope : nil
761
817
  preload.each do |associations|
762
- preloader ||= build_preloader
763
- preloader.preload records, associations, scope
818
+ ActiveRecord::Associations::Preloader.new(records: records, associations: associations, scope: scope).call
764
819
  end
765
820
  end
766
821
 
@@ -775,8 +830,12 @@ module ActiveRecord
775
830
  end
776
831
 
777
832
  private
778
- def already_in_scope?
779
- @delegate_to_klass && klass.current_scope(true)
833
+ def already_in_scope?(registry)
834
+ @delegate_to_klass && registry.current_scope(klass, true)
835
+ end
836
+
837
+ def global_scope?(registry)
838
+ registry.global_current_scope(klass, true)
780
839
  end
781
840
 
782
841
  def current_scope_restoring_block(&block)
@@ -799,11 +858,20 @@ module ActiveRecord
799
858
  klass.create!(attributes, &block)
800
859
  end
801
860
 
802
- def _scoping(scope)
803
- previous, klass.current_scope = klass.current_scope(true), scope
861
+ def _scoping(scope, registry, all_queries = false)
862
+ previous = registry.current_scope(klass, true)
863
+ registry.set_current_scope(klass, scope)
864
+
865
+ if all_queries
866
+ previous_global = registry.global_current_scope(klass, true)
867
+ registry.set_global_current_scope(klass, scope)
868
+ end
804
869
  yield
805
870
  ensure
806
- klass.current_scope = previous
871
+ registry.set_current_scope(klass, previous)
872
+ if all_queries
873
+ registry.set_global_current_scope(klass, previous_global)
874
+ end
807
875
  end
808
876
 
809
877
  def _substitute_values(values)
@@ -826,23 +894,15 @@ module ActiveRecord
826
894
 
827
895
  def exec_queries(&block)
828
896
  skip_query_cache_if_necessary do
829
- records =
830
- if where_clause.contradiction?
831
- []
832
- elsif eager_loading?
833
- apply_join_dependency do |relation, join_dependency|
834
- if relation.null_relation?
835
- []
836
- else
837
- relation = join_dependency.apply_column_aliases(relation)
838
- rows = connection.select_all(relation.arel, "SQL")
839
- join_dependency.instantiate(rows, strict_loading_value, &block)
840
- end.freeze
841
- end
842
- else
843
- klass.find_by_sql(arel, &block).freeze
844
- end
897
+ rows = if scheduled?
898
+ future = @future_result
899
+ @future_result = nil
900
+ future.result
901
+ else
902
+ exec_main_query
903
+ end
845
904
 
905
+ records = instantiate_records(rows, &block)
846
906
  preload_associations(records) unless skip_preloading_value
847
907
 
848
908
  records.each(&:readonly!) if readonly_value
@@ -852,18 +912,43 @@ module ActiveRecord
852
912
  end
853
913
  end
854
914
 
855
- def skip_query_cache_if_necessary
856
- if skip_query_cache_value
857
- uncached do
858
- yield
915
+ def exec_main_query(async: false)
916
+ skip_query_cache_if_necessary do
917
+ if where_clause.contradiction?
918
+ [].freeze
919
+ elsif eager_loading?
920
+ apply_join_dependency do |relation, join_dependency|
921
+ if relation.null_relation?
922
+ [].freeze
923
+ else
924
+ relation = join_dependency.apply_column_aliases(relation)
925
+ @_join_dependency = join_dependency
926
+ connection.select_all(relation.arel, "SQL", async: async)
927
+ end
928
+ end
929
+ else
930
+ klass._query_by_sql(arel, async: async)
859
931
  end
932
+ end
933
+ end
934
+
935
+ def instantiate_records(rows, &block)
936
+ return [].freeze if rows.empty?
937
+ if eager_loading?
938
+ records = @_join_dependency.instantiate(rows, strict_loading_value, &block).freeze
939
+ @_join_dependency = nil
940
+ records
860
941
  else
861
- yield
942
+ klass._load_from_sql(rows, &block).freeze
862
943
  end
863
944
  end
864
945
 
865
- def build_preloader
866
- ActiveRecord::Associations::Preloader.new
946
+ def skip_query_cache_if_necessary(&block)
947
+ if skip_query_cache_value
948
+ uncached(&block)
949
+ else
950
+ yield
951
+ end
867
952
  end
868
953
 
869
954
  def references_eager_loaded_tables?
@@ -889,5 +974,9 @@ module ActiveRecord
889
974
  # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
890
975
  string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
891
976
  end
977
+
978
+ def limited_count
979
+ @limited_count ||= limit(2).count
980
+ end
892
981
  end
893
982
  end
@@ -36,6 +36,10 @@ module ActiveRecord
36
36
 
37
37
  attr_reader :columns, :rows, :column_types
38
38
 
39
+ def self.empty # :nodoc:
40
+ EMPTY
41
+ end
42
+
39
43
  def initialize(columns, rows, column_types = {})
40
44
  @columns = columns
41
45
  @rows = rows
@@ -43,6 +47,9 @@ module ActiveRecord
43
47
  @column_types = column_types
44
48
  end
45
49
 
50
+ EMPTY = new([].freeze, [].freeze, {}.freeze)
51
+ private_constant :EMPTY
52
+
46
53
  # Returns true if this result set includes the column named +name+
47
54
  def includes_column?(name)
48
55
  @columns.include? name
@@ -57,9 +64,9 @@ module ActiveRecord
57
64
  # row as parameter.
58
65
  #
59
66
  # Returns an +Enumerator+ if no block is given.
60
- def each
67
+ def each(&block)
61
68
  if block_given?
62
- hash_rows.each { |row| yield row }
69
+ hash_rows.each(&block)
63
70
  else
64
71
  hash_rows.to_enum { @rows.size }
65
72
  end
@@ -91,6 +98,14 @@ module ActiveRecord
91
98
  n ? hash_rows.last(n) : hash_rows.last
92
99
  end
93
100
 
101
+ def result # :nodoc:
102
+ self
103
+ end
104
+
105
+ def cancel # :nodoc:
106
+ self
107
+ end
108
+
94
109
  def cast_values(type_overrides = {}) # :nodoc:
95
110
  if columns.one?
96
111
  # Separated to avoid allocating an array per row
@@ -16,9 +16,7 @@ module ActiveRecord
16
16
 
17
17
  attr_accessor :sql_runtime
18
18
 
19
- [:sql_runtime].each do |val|
20
- class_eval %{ def self.#{val}; instance.#{val}; end }, __FILE__, __LINE__
21
- class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
22
- end
19
+ def self.sql_runtime; instance.sql_runtime; end
20
+ def self.sql_runtime=(x); instance.sql_runtime = x; end
23
21
  end
24
22
  end
@@ -54,7 +54,7 @@ module ActiveRecord
54
54
  # Accepts an array, or string of SQL conditions and sanitizes
55
55
  # them into a valid SQL fragment for an ORDER clause.
56
56
  #
57
- # sanitize_sql_for_order(["field(id, ?)", [1,3,2]])
57
+ # sanitize_sql_for_order([Arel.sql("field(id, ?)"), [1,3,2]])
58
58
  # # => "field(id, 1,3,2)"
59
59
  #
60
60
  # sanitize_sql_for_order("id ASC")
@@ -137,14 +137,18 @@ module ActiveRecord
137
137
  def disallow_raw_sql!(args, permit: connection.column_name_matcher) # :nodoc:
138
138
  unexpected = nil
139
139
  args.each do |arg|
140
- next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s)
140
+ next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s.strip)
141
141
  (unexpected ||= []) << arg
142
142
  end
143
143
 
144
144
  if unexpected
145
145
  raise(ActiveRecord::UnknownAttributeReference,
146
- "Query method called with non-attribute argument(s): " +
147
- unexpected.map(&:inspect).join(", ")
146
+ "Dangerous query method (method whose arguments are used as raw " \
147
+ "SQL) called with non-attribute argument(s): " \
148
+ "#{unexpected.map(&:inspect).join(", ")}." \
149
+ "This method should not be called with user-provided values, such as request " \
150
+ "parameters or model attributes. Known-safe values can be passed " \
151
+ "by wrapping them in Arel.sql()."
148
152
  )
149
153
  end
150
154
  end
@@ -183,13 +187,13 @@ module ActiveRecord
183
187
  if value.respond_to?(:map) && !value.acts_like?(:string)
184
188
  values = value.map { |v| v.respond_to?(:id_for_database) ? v.id_for_database : v }
185
189
  if values.empty?
186
- c.quote(nil)
190
+ c.quote_bound_value(nil)
187
191
  else
188
- values.map! { |v| c.quote(v) }.join(",")
192
+ values.map! { |v| c.quote_bound_value(v) }.join(",")
189
193
  end
190
194
  else
191
195
  value = value.id_for_database if value.respond_to?(:id_for_database)
192
- c.quote(value)
196
+ c.quote_bound_value(value)
193
197
  end
194
198
  end
195
199
 
@@ -7,14 +7,14 @@ module ActiveRecord
7
7
  #
8
8
  # This class is used to dump the database schema for some connection to some
9
9
  # output format (i.e., ActiveRecord::Schema).
10
- class SchemaDumper #:nodoc:
10
+ class SchemaDumper # :nodoc:
11
11
  private_class_method :new
12
12
 
13
13
  ##
14
14
  # :singleton-method:
15
15
  # A list of tables which should not be dumped to the schema.
16
- # Acceptable values are strings as well as regexp if ActiveRecord::Base.schema_format == :ruby.
17
- # Only strings are accepted if ActiveRecord::Base.schema_format == :sql.
16
+ # Acceptable values are strings as well as regexp if ActiveRecord.schema_format == :ruby.
17
+ # Only strings are accepted if ActiveRecord.schema_format == :sql.
18
18
  cattr_accessor :ignore_tables, default: []
19
19
 
20
20
  ##
@@ -10,10 +10,6 @@ module ActiveRecord
10
10
  # to be executed the next time.
11
11
  class SchemaMigration < ActiveRecord::Base # :nodoc:
12
12
  class << self
13
- def _internal?
14
- true
15
- end
16
-
17
13
  def primary_key
18
14
  "version"
19
15
  end