activerecord 7.1.5.1 → 7.2.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +515 -2445
  3. data/README.rdoc +15 -15
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +1 -1
  6. data/lib/active_record/associations/alias_tracker.rb +25 -19
  7. data/lib/active_record/associations/association.rb +9 -8
  8. data/lib/active_record/associations/belongs_to_association.rb +14 -7
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +3 -4
  13. data/lib/active_record/associations/builder/has_one.rb +3 -4
  14. data/lib/active_record/associations/collection_association.rb +6 -4
  15. data/lib/active_record/associations/collection_proxy.rb +14 -1
  16. data/lib/active_record/associations/has_many_association.rb +1 -1
  17. data/lib/active_record/associations/join_dependency/join_association.rb +29 -28
  18. data/lib/active_record/associations/join_dependency.rb +5 -5
  19. data/lib/active_record/associations/nested_error.rb +47 -0
  20. data/lib/active_record/associations/preloader/association.rb +2 -1
  21. data/lib/active_record/associations/preloader/branch.rb +7 -1
  22. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  23. data/lib/active_record/associations/singular_association.rb +6 -0
  24. data/lib/active_record/associations/through_association.rb +1 -1
  25. data/lib/active_record/associations.rb +33 -16
  26. data/lib/active_record/attribute_assignment.rb +1 -11
  27. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +1 -1
  29. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  30. data/lib/active_record/attribute_methods/read.rb +4 -16
  31. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  32. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -10
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +60 -71
  35. data/lib/active_record/attributes.rb +55 -42
  36. data/lib/active_record/autosave_association.rb +13 -32
  37. data/lib/active_record/base.rb +2 -3
  38. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
  39. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
  40. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -65
  41. data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
  42. data/lib/active_record/connection_adapters/abstract/query_cache.rb +159 -74
  43. data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
  44. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  45. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +14 -5
  46. data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
  47. data/lib/active_record/connection_adapters/abstract_adapter.rb +18 -46
  48. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +32 -6
  49. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  50. data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
  51. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -1
  52. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
  53. data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -23
  54. data/lib/active_record/connection_adapters/pool_config.rb +7 -6
  55. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
  56. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  57. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  58. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  59. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  60. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
  61. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -13
  62. data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
  63. data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
  64. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  65. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
  66. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
  67. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  68. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  69. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  70. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
  71. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +107 -75
  72. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
  73. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
  74. data/lib/active_record/connection_adapters.rb +121 -0
  75. data/lib/active_record/connection_handling.rb +56 -41
  76. data/lib/active_record/core.rb +53 -37
  77. data/lib/active_record/counter_cache.rb +18 -9
  78. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  79. data/lib/active_record/database_configurations/database_config.rb +15 -4
  80. data/lib/active_record/database_configurations/hash_config.rb +38 -34
  81. data/lib/active_record/database_configurations/url_config.rb +20 -1
  82. data/lib/active_record/database_configurations.rb +1 -1
  83. data/lib/active_record/delegated_type.rb +24 -0
  84. data/lib/active_record/dynamic_matchers.rb +2 -2
  85. data/lib/active_record/encryption/encryptable_record.rb +2 -2
  86. data/lib/active_record/encryption/encrypted_attribute_type.rb +22 -2
  87. data/lib/active_record/encryption/encryptor.rb +17 -2
  88. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  89. data/lib/active_record/encryption/message_serializer.rb +4 -0
  90. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  91. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  92. data/lib/active_record/encryption.rb +0 -2
  93. data/lib/active_record/enum.rb +10 -1
  94. data/lib/active_record/errors.rb +16 -11
  95. data/lib/active_record/explain.rb +13 -24
  96. data/lib/active_record/fixtures.rb +37 -31
  97. data/lib/active_record/future_result.rb +8 -4
  98. data/lib/active_record/gem_version.rb +3 -3
  99. data/lib/active_record/inheritance.rb +4 -2
  100. data/lib/active_record/insert_all.rb +18 -15
  101. data/lib/active_record/integration.rb +4 -1
  102. data/lib/active_record/internal_metadata.rb +48 -34
  103. data/lib/active_record/locking/optimistic.rb +7 -6
  104. data/lib/active_record/log_subscriber.rb +0 -21
  105. data/lib/active_record/marshalling.rb +1 -4
  106. data/lib/active_record/message_pack.rb +1 -1
  107. data/lib/active_record/migration/command_recorder.rb +2 -3
  108. data/lib/active_record/migration/compatibility.rb +5 -3
  109. data/lib/active_record/migration/default_strategy.rb +4 -5
  110. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  111. data/lib/active_record/migration.rb +85 -76
  112. data/lib/active_record/model_schema.rb +28 -68
  113. data/lib/active_record/nested_attributes.rb +13 -16
  114. data/lib/active_record/normalization.rb +3 -7
  115. data/lib/active_record/persistence.rb +30 -352
  116. data/lib/active_record/query_cache.rb +18 -6
  117. data/lib/active_record/query_logs.rb +15 -0
  118. data/lib/active_record/querying.rb +21 -9
  119. data/lib/active_record/railtie.rb +50 -62
  120. data/lib/active_record/railties/controller_runtime.rb +13 -4
  121. data/lib/active_record/railties/databases.rake +41 -44
  122. data/lib/active_record/reflection.rb +90 -35
  123. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  124. data/lib/active_record/relation/batches.rb +3 -3
  125. data/lib/active_record/relation/calculations.rb +94 -61
  126. data/lib/active_record/relation/delegation.rb +8 -11
  127. data/lib/active_record/relation/finder_methods.rb +16 -2
  128. data/lib/active_record/relation/merger.rb +4 -6
  129. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  130. data/lib/active_record/relation/predicate_builder.rb +3 -3
  131. data/lib/active_record/relation/query_methods.rb +196 -57
  132. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  133. data/lib/active_record/relation/spawn_methods.rb +2 -18
  134. data/lib/active_record/relation/where_clause.rb +7 -19
  135. data/lib/active_record/relation.rb +496 -72
  136. data/lib/active_record/result.rb +31 -44
  137. data/lib/active_record/runtime_registry.rb +39 -0
  138. data/lib/active_record/sanitization.rb +24 -19
  139. data/lib/active_record/schema.rb +8 -6
  140. data/lib/active_record/schema_dumper.rb +19 -9
  141. data/lib/active_record/schema_migration.rb +30 -14
  142. data/lib/active_record/signed_id.rb +11 -1
  143. data/lib/active_record/statement_cache.rb +7 -7
  144. data/lib/active_record/table_metadata.rb +1 -10
  145. data/lib/active_record/tasks/database_tasks.rb +76 -70
  146. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  147. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  148. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  149. data/lib/active_record/test_fixtures.rb +81 -91
  150. data/lib/active_record/testing/query_assertions.rb +121 -0
  151. data/lib/active_record/timestamp.rb +1 -1
  152. data/lib/active_record/token_for.rb +22 -12
  153. data/lib/active_record/touch_later.rb +1 -1
  154. data/lib/active_record/transaction.rb +68 -0
  155. data/lib/active_record/transactions.rb +43 -14
  156. data/lib/active_record/translation.rb +0 -2
  157. data/lib/active_record/type/serialized.rb +1 -3
  158. data/lib/active_record/type_caster/connection.rb +4 -4
  159. data/lib/active_record/validations/associated.rb +9 -3
  160. data/lib/active_record/validations/uniqueness.rb +14 -10
  161. data/lib/active_record/validations.rb +4 -1
  162. data/lib/active_record.rb +149 -40
  163. data/lib/arel/alias_predication.rb +1 -1
  164. data/lib/arel/collectors/bind.rb +2 -0
  165. data/lib/arel/collectors/composite.rb +7 -0
  166. data/lib/arel/collectors/sql_string.rb +1 -1
  167. data/lib/arel/collectors/substitute_binds.rb +1 -1
  168. data/lib/arel/nodes/binary.rb +0 -6
  169. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  170. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  171. data/lib/arel/nodes/node.rb +4 -3
  172. data/lib/arel/nodes/sql_literal.rb +7 -0
  173. data/lib/arel/nodes.rb +2 -2
  174. data/lib/arel/predications.rb +1 -1
  175. data/lib/arel/select_manager.rb +1 -1
  176. data/lib/arel/tree_manager.rb +3 -2
  177. data/lib/arel/update_manager.rb +2 -1
  178. data/lib/arel/visitors/dot.rb +1 -0
  179. data/lib/arel/visitors/mysql.rb +9 -4
  180. data/lib/arel/visitors/postgresql.rb +1 -12
  181. data/lib/arel/visitors/to_sql.rb +29 -16
  182. data/lib/arel.rb +7 -3
  183. metadata +20 -15
data/lib/active_record.rb CHANGED
@@ -25,6 +25,7 @@
25
25
 
26
26
  require "active_support"
27
27
  require "active_support/rails"
28
+ require "active_support/ordered_options"
28
29
  require "active_model"
29
30
  require "arel"
30
31
  require "yaml"
@@ -85,6 +86,7 @@ module ActiveRecord
85
86
  autoload :Timestamp
86
87
  autoload :TokenFor
87
88
  autoload :TouchLater
89
+ autoload :Transaction
88
90
  autoload :Transactions
89
91
  autoload :Translation
90
92
  autoload :Validations
@@ -128,6 +130,8 @@ module ActiveRecord
128
130
  module AttributeMethods
129
131
  extend ActiveSupport::Autoload
130
132
 
133
+ autoload :CompositePrimaryKey
134
+
131
135
  eager_autoload do
132
136
  autoload :BeforeTypeCast
133
137
  autoload :Dirty
@@ -177,12 +181,15 @@ module ActiveRecord
177
181
  singleton_class.attr_accessor :disable_prepared_statements
178
182
  self.disable_prepared_statements = false
179
183
 
184
+ ##
185
+ # :singleton-method: lazily_load_schema_cache
180
186
  # Lazily load the schema cache. This option will load the schema cache
181
- # when a connection is established rather than on boot. If set,
182
- # +config.active_record.use_schema_cache_dump+ will be set to false.
187
+ # when a connection is established rather than on boot.
183
188
  singleton_class.attr_accessor :lazily_load_schema_cache
184
189
  self.lazily_load_schema_cache = false
185
190
 
191
+ ##
192
+ # :singleton-method: schema_cache_ignored_tables
186
193
  # A list of tables or regex's to match tables to ignore when
187
194
  # dumping the schema cache. For example if this is set to +[/^_/]+
188
195
  # the schema cache will not dump tables named with an underscore.
@@ -203,6 +210,8 @@ module ActiveRecord
203
210
 
204
211
  self.default_timezone = :utc
205
212
 
213
+ ##
214
+ # :singleton-method: db_warnings_action
206
215
  # The action to take when database query produces warning.
207
216
  # Must be one of :ignore, :log, :raise, :report, or a custom proc.
208
217
  # The default is :ignore.
@@ -232,6 +241,8 @@ module ActiveRecord
232
241
 
233
242
  self.db_warnings_action = :ignore
234
243
 
244
+ ##
245
+ # :singleton-method: db_warnings_ignore
235
246
  # Specify allowlist of database warnings.
236
247
  singleton_class.attr_accessor :db_warnings_ignore
237
248
  self.db_warnings_ignore = []
@@ -250,6 +261,8 @@ module ActiveRecord
250
261
  MSG
251
262
  end
252
263
 
264
+ ##
265
+ # :singleton-method: async_query_executor
253
266
  # Sets the async_query_executor for an application. By default the thread pool executor
254
267
  # set to +nil+ which will not run queries in the background. Applications must configure
255
268
  # a thread pool executor to use this feature. Options are:
@@ -277,7 +290,7 @@ module ActiveRecord
277
290
  # with the global thread pool async query executor.
278
291
  def self.global_executor_concurrency=(global_executor_concurrency)
279
292
  if self.async_query_executor.nil? || self.async_query_executor == :multi_thread_pool
280
- raise ArgumentError, "`global_executor_concurrency` cannot be set when the executor is nil or set to `:multi_thread_pool`. For multiple thread pools, please set the concurrency in your database configuration."
293
+ raise ArgumentError, "`global_executor_concurrency` cannot be set when using the executor is nil or set to multi_thead_pool. For multiple thread pools, please set the concurrency in your database configuration."
281
294
  end
282
295
 
283
296
  @global_executor_concurrency = global_executor_concurrency
@@ -287,11 +300,22 @@ module ActiveRecord
287
300
  @global_executor_concurrency ||= nil
288
301
  end
289
302
 
303
+ @permanent_connection_checkout = true
304
+ singleton_class.attr_reader :permanent_connection_checkout
305
+
306
+ # Defines whether +ActiveRecord::Base.connection+ is allowed, deprecated, or entirely disallowed.
307
+ def self.permanent_connection_checkout=(value)
308
+ unless [true, :deprecated, :disallowed].include?(value)
309
+ raise ArgumentError, "permanent_connection_checkout must be one of: `true`, `:deprecated` or `:disallowed`"
310
+ end
311
+ @permanent_connection_checkout = value
312
+ end
313
+
290
314
  singleton_class.attr_accessor :index_nested_attribute_errors
291
315
  self.index_nested_attribute_errors = false
292
316
 
293
317
  ##
294
- # :singleton-method:
318
+ # :singleton-method: verbose_query_logs
295
319
  #
296
320
  # Specifies if the methods calling database queries should be logged below
297
321
  # their relevant queries. Defaults to false.
@@ -299,7 +323,7 @@ module ActiveRecord
299
323
  self.verbose_query_logs = false
300
324
 
301
325
  ##
302
- # :singleton-method:
326
+ # :singleton-method: queues
303
327
  #
304
328
  # Specifies the names of the queues used by background jobs.
305
329
  singleton_class.attr_accessor :queues
@@ -320,11 +344,22 @@ module ActiveRecord
320
344
  singleton_class.attr_accessor :run_after_transaction_callbacks_in_order_defined
321
345
  self.run_after_transaction_callbacks_in_order_defined = false
322
346
 
323
- singleton_class.attr_accessor :commit_transaction_on_non_local_return
324
- self.commit_transaction_on_non_local_return = false
347
+ def self.commit_transaction_on_non_local_return
348
+ ActiveRecord.deprecator.warn <<-WARNING.squish
349
+ `Rails.application.config.active_record.commit_transaction_on_non_local_return`
350
+ is deprecated and will be removed in Rails 7.3.
351
+ WARNING
352
+ end
353
+
354
+ def self.commit_transaction_on_non_local_return=(value)
355
+ ActiveRecord.deprecator.warn <<-WARNING.squish
356
+ `Rails.application.config.active_record.commit_transaction_on_non_local_return`
357
+ is deprecated and will be removed in Rails 7.3.
358
+ WARNING
359
+ end
325
360
 
326
361
  ##
327
- # :singleton-method:
362
+ # :singleton-method: warn_on_records_fetched_greater_than
328
363
  # Specify a threshold for the size of query result sets. If the number of
329
364
  # records in the set exceeds the threshold, a warning is logged. This can
330
365
  # be used to identify queries which load thousands of records and
@@ -336,14 +371,14 @@ module ActiveRecord
336
371
  self.application_record_class = nil
337
372
 
338
373
  ##
339
- # :singleton-method:
374
+ # :singleton-method: action_on_strict_loading_violation
340
375
  # Set the application to log or raise when an association violates strict loading.
341
376
  # Defaults to :raise.
342
377
  singleton_class.attr_accessor :action_on_strict_loading_violation
343
378
  self.action_on_strict_loading_violation = :raise
344
379
 
345
380
  ##
346
- # :singleton-method:
381
+ # :singleton-method: schema_format
347
382
  # Specifies the format to use when dumping the database schema with Rails'
348
383
  # Rakefile. If :sql, the schema is dumped as (potentially database-
349
384
  # specific) SQL statements. If :ruby, the schema is dumped as an
@@ -354,7 +389,7 @@ module ActiveRecord
354
389
  self.schema_format = :ruby
355
390
 
356
391
  ##
357
- # :singleton-method:
392
+ # :singleton-method: error_on_ignored_order
358
393
  # Specifies if an error should be raised if the query has an order being
359
394
  # ignored when doing batch queries. Useful in applications where the
360
395
  # scope being ignored is error-worthy, rather than a warning.
@@ -362,19 +397,27 @@ module ActiveRecord
362
397
  self.error_on_ignored_order = false
363
398
 
364
399
  ##
365
- # :singleton-method:
400
+ # :singleton-method: timestamped_migrations
366
401
  # Specify whether or not to use timestamps for migration versions
367
402
  singleton_class.attr_accessor :timestamped_migrations
368
403
  self.timestamped_migrations = true
369
404
 
370
405
  ##
371
- # :singleton-method:
406
+ # :singleton-method: validate_migration_timestamps
407
+ # Specify whether or not to validate migration timestamps. When set, an error
408
+ # will be raised if a timestamp is more than a day ahead of the timestamp
409
+ # associated with the current time. +timestamped_migrations+ must be set to true.
410
+ singleton_class.attr_accessor :validate_migration_timestamps
411
+ self.validate_migration_timestamps = false
412
+
413
+ ##
414
+ # :singleton-method: migration_strategy
372
415
  # Specify strategy to use for executing migrations.
373
416
  singleton_class.attr_accessor :migration_strategy
374
417
  self.migration_strategy = Migration::DefaultStrategy
375
418
 
376
419
  ##
377
- # :singleton-method:
420
+ # :singleton-method: dump_schema_after_migration
378
421
  # Specify whether schema dump should happen at the end of the
379
422
  # bin/rails db:migrate command. This is true by default, which is useful for the
380
423
  # development environment. This should ideally be false in the production
@@ -383,7 +426,7 @@ module ActiveRecord
383
426
  self.dump_schema_after_migration = true
384
427
 
385
428
  ##
386
- # :singleton-method:
429
+ # :singleton-method: dump_schemas
387
430
  # Specifies which database schemas to dump when calling db:schema:dump.
388
431
  # If the value is :schema_search_path (the default), any schemas listed in
389
432
  # schema_search_path are dumped. Use :all to dump all schemas regardless
@@ -392,22 +435,8 @@ module ActiveRecord
392
435
  singleton_class.attr_accessor :dump_schemas
393
436
  self.dump_schemas = :schema_search_path
394
437
 
395
- def self.suppress_multiple_database_warning
396
- ActiveRecord.deprecator.warn(<<-MSG.squish)
397
- config.active_record.suppress_multiple_database_warning is deprecated and will be removed in Rails 7.2.
398
- It no longer has any effect and should be removed from the configuration file.
399
- MSG
400
- end
401
-
402
- def self.suppress_multiple_database_warning=(value)
403
- ActiveRecord.deprecator.warn(<<-MSG.squish)
404
- config.active_record.suppress_multiple_database_warning= is deprecated and will be removed in Rails 7.2.
405
- It no longer has any effect and should be removed from the configuration file.
406
- MSG
407
- end
408
-
409
438
  ##
410
- # :singleton-method:
439
+ # :singleton-method: verify_foreign_keys_for_fixtures
411
440
  # If true, Rails will verify all foreign keys in the database after loading fixtures.
412
441
  # An error will be raised if there are any foreign key violations, indicating incorrectly
413
442
  # written fixtures.
@@ -415,25 +444,32 @@ module ActiveRecord
415
444
  singleton_class.attr_accessor :verify_foreign_keys_for_fixtures
416
445
  self.verify_foreign_keys_for_fixtures = false
417
446
 
418
- ##
419
- # :singleton-method:
420
- # If true, Rails will continue allowing plural association names in where clauses on singular associations
421
- # This behavior will be removed in Rails 7.2.
422
- singleton_class.attr_accessor :allow_deprecated_singular_associations_name
423
- self.allow_deprecated_singular_associations_name = true
447
+ def self.allow_deprecated_singular_associations_name
448
+ ActiveRecord.deprecator.warn <<-WARNING.squish
449
+ `Rails.application.config.active_record.allow_deprecated_singular_associations_name`
450
+ is deprecated and will be removed in Rails 7.3.
451
+ WARNING
452
+ end
453
+
454
+ def self.allow_deprecated_singular_associations_name=(value)
455
+ ActiveRecord.deprecator.warn <<-WARNING.squish
456
+ `Rails.application.config.active_record.allow_deprecated_singular_associations_name`
457
+ is deprecated and will be removed in Rails 7.3.
458
+ WARNING
459
+ end
424
460
 
425
461
  singleton_class.attr_accessor :query_transformers
426
462
  self.query_transformers = []
427
463
 
428
464
  ##
429
- # :singleton-method:
465
+ # :singleton-method: use_yaml_unsafe_load
430
466
  # Application configurable boolean that instructs the YAML Coder to use
431
467
  # an unsafe load if set to true.
432
468
  singleton_class.attr_accessor :use_yaml_unsafe_load
433
469
  self.use_yaml_unsafe_load = false
434
470
 
435
471
  ##
436
- # :singleton-method:
472
+ # :singleton-method: raise_int_wider_than_64bit
437
473
  # Application configurable boolean that denotes whether or not to raise
438
474
  # an exception when the PostgreSQLAdapter is provided with an integer that
439
475
  # is wider than signed 64bit representation
@@ -441,14 +477,14 @@ module ActiveRecord
441
477
  self.raise_int_wider_than_64bit = true
442
478
 
443
479
  ##
444
- # :singleton-method:
480
+ # :singleton-method: yaml_column_permitted_classes
445
481
  # Application configurable array that provides additional permitted classes
446
482
  # to Psych safe_load in the YAML Coder
447
483
  singleton_class.attr_accessor :yaml_column_permitted_classes
448
484
  self.yaml_column_permitted_classes = [Symbol]
449
485
 
450
486
  ##
451
- # :singleton-method:
487
+ # :singleton-method: generate_secure_token_on
452
488
  # Controls when to generate a value for <tt>has_secure_token</tt>
453
489
  # declarations. Defaults to <tt>:create</tt>.
454
490
  singleton_class.attr_accessor :generate_secure_token_on
@@ -462,6 +498,34 @@ module ActiveRecord
462
498
  Marshalling.format_version = value
463
499
  end
464
500
 
501
+ ##
502
+ # :singleton-method: protocol_adapters
503
+ # Provides a mapping between database protocols/DBMSs and the
504
+ # underlying database adapter to be used. This is used only by the
505
+ # <tt>DATABASE_URL</tt> environment variable.
506
+ #
507
+ # == Example
508
+ #
509
+ # DATABASE_URL="mysql://myuser:mypass@localhost/somedatabase"
510
+ #
511
+ # The above URL specifies that MySQL is the desired protocol/DBMS, and the
512
+ # application configuration can then decide which adapter to use. For this example
513
+ # the default mapping is from <tt>mysql</tt> to <tt>mysql2</tt>, but <tt>:trilogy</tt>
514
+ # is also supported.
515
+ #
516
+ # ActiveRecord.protocol_adapters.mysql = "mysql2"
517
+ #
518
+ # The protocols names are arbitrary, and external database adapters can be
519
+ # registered and set here.
520
+ singleton_class.attr_accessor :protocol_adapters
521
+ self.protocol_adapters = ActiveSupport::InheritableOptions.new(
522
+ {
523
+ sqlite: "sqlite3",
524
+ mysql: "mysql2",
525
+ postgres: "postgresql",
526
+ }
527
+ )
528
+
465
529
  def self.eager_load!
466
530
  super
467
531
  ActiveRecord::Locking.eager_load!
@@ -476,6 +540,51 @@ module ActiveRecord
476
540
  def self.disconnect_all!
477
541
  ConnectionAdapters::PoolConfig.disconnect_all!
478
542
  end
543
+
544
+ # Registers a block to be called after all the current transactions have been
545
+ # committed.
546
+ #
547
+ # If there is no currently open transaction, the block is called immediately.
548
+ #
549
+ # If there are multiple nested transactions, the block is called after the outermost one
550
+ # has been committed,
551
+ #
552
+ # If any of the currently open transactions is rolled back, the block is never called.
553
+ #
554
+ # If multiple transactions are open across multiple databases, the block will be invoked
555
+ # if and once all of them have been committed. But note that nesting transactions across
556
+ # two distinct databases is a sharding anti-pattern that comes with a world of hurts.
557
+ def self.after_all_transactions_commit(&block)
558
+ open_transactions = all_open_transactions
559
+
560
+ if open_transactions.empty?
561
+ yield
562
+ elsif open_transactions.size == 1
563
+ open_transactions.first.after_commit(&block)
564
+ else
565
+ count = open_transactions.size
566
+ callback = -> do
567
+ count -= 1
568
+ block.call if count.zero?
569
+ end
570
+ open_transactions.each do |t|
571
+ t.after_commit(&callback)
572
+ end
573
+ open_transactions = nil # rubocop:disable Lint/UselessAssignment avoid holding it in the closure
574
+ end
575
+ end
576
+
577
+ def self.all_open_transactions # :nodoc:
578
+ open_transactions = []
579
+ Base.connection_handler.each_connection_pool do |pool|
580
+ if active_connection = pool.active_connection
581
+ if active_connection.current_transaction.open? && active_connection.current_transaction.joinable?
582
+ open_transactions << active_connection.current_transaction
583
+ end
584
+ end
585
+ end
586
+ open_transactions
587
+ end
479
588
  end
480
589
 
481
590
  ActiveSupport.on_load(:active_record) do
@@ -3,7 +3,7 @@
3
3
  module Arel # :nodoc: all
4
4
  module AliasPredication
5
5
  def as(other)
6
- Nodes::As.new self, Nodes::SqlLiteral.new(other)
6
+ Nodes::As.new self, Nodes::SqlLiteral.new(other, retryable: true)
7
7
  end
8
8
  end
9
9
  end
@@ -3,6 +3,8 @@
3
3
  module Arel # :nodoc: all
4
4
  module Collectors
5
5
  class Bind
6
+ attr_accessor :retryable
7
+
6
8
  def initialize
7
9
  @binds = []
8
10
  end
@@ -4,12 +4,19 @@ module Arel # :nodoc: all
4
4
  module Collectors
5
5
  class Composite
6
6
  attr_accessor :preparable
7
+ attr_reader :retryable
7
8
 
8
9
  def initialize(left, right)
9
10
  @left = left
10
11
  @right = right
11
12
  end
12
13
 
14
+ def retryable=(retryable)
15
+ left.retryable = retryable
16
+ right.retryable = retryable
17
+ @retryable = retryable
18
+ end
19
+
13
20
  def <<(str)
14
21
  left << str
15
22
  right << str
@@ -5,7 +5,7 @@ require "arel/collectors/plain_string"
5
5
  module Arel # :nodoc: all
6
6
  module Collectors
7
7
  class SQLString < PlainString
8
- attr_accessor :preparable
8
+ attr_accessor :preparable, :retryable
9
9
 
10
10
  def initialize(*)
11
11
  super
@@ -3,7 +3,7 @@
3
3
  module Arel # :nodoc: all
4
4
  module Collectors
5
5
  class SubstituteBinds
6
- attr_accessor :preparable
6
+ attr_accessor :preparable, :retryable
7
7
 
8
8
  def initialize(quoter, delegate_collector)
9
9
  @quoter = quoter
@@ -111,12 +111,6 @@ module Arel # :nodoc: all
111
111
  end
112
112
  end
113
113
 
114
- class Or < Binary
115
- def fetch_attribute(&block)
116
- left.fetch_attribute(&block) && right.fetch_attribute(&block)
117
- end
118
- end
119
-
120
114
  %w{
121
115
  Assignment
122
116
  Join
@@ -6,13 +6,17 @@ module Arel # :nodoc: all
6
6
  attr_reader :sql_with_placeholders, :positional_binds, :named_binds
7
7
 
8
8
  def initialize(sql_with_placeholders, positional_binds, named_binds)
9
- if !positional_binds.empty? && !named_binds.empty?
10
- raise BindError.new("cannot mix positional and named binds", sql_with_placeholders)
11
- elsif !positional_binds.empty?
9
+ has_positional = !(positional_binds.nil? || positional_binds.empty?)
10
+ has_named = !(named_binds.nil? || named_binds.empty?)
11
+
12
+ if has_positional
13
+ if has_named
14
+ raise BindError.new("cannot mix positional and named binds", sql_with_placeholders)
15
+ end
12
16
  if positional_binds.size != (expected = sql_with_placeholders.count("?"))
13
17
  raise BindError.new("wrong number of bind variables (#{positional_binds.size} for #{expected})", sql_with_placeholders)
14
18
  end
15
- elsif !named_binds.empty?
19
+ elsif has_named
16
20
  tokens_in_string = sql_with_placeholders.scan(/:(?<!::)([a-zA-Z]\w*)/).flatten.map(&:to_sym).uniq
17
21
  tokens_in_hash = named_binds.keys.map(&:to_sym).uniq
18
22
 
@@ -26,7 +30,7 @@ module Arel # :nodoc: all
26
30
  end
27
31
 
28
32
  @sql_with_placeholders = sql_with_placeholders
29
- if !positional_binds.empty?
33
+ if has_positional
30
34
  @positional_binds = positional_binds
31
35
  @named_binds = nil
32
36
  else
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Arel # :nodoc: all
4
4
  module Nodes
5
- class And < Arel::Nodes::NodeExpression
5
+ class Nary < Arel::Nodes::NodeExpression
6
6
  attr_reader :children
7
7
 
8
8
  def initialize(children)
@@ -23,7 +23,7 @@ module Arel # :nodoc: all
23
23
  end
24
24
 
25
25
  def hash
26
- children.hash
26
+ [self.class, children].hash
27
27
  end
28
28
 
29
29
  def eql?(other)
@@ -32,5 +32,8 @@ module Arel # :nodoc: all
32
32
  end
33
33
  alias :== :eql?
34
34
  end
35
+
36
+ And = Class.new(Nary)
37
+ Or = Class.new(Nary)
35
38
  end
36
39
  end
@@ -127,7 +127,7 @@ module Arel # :nodoc: all
127
127
  # Factory method to create a Nodes::Grouping node that has an Nodes::Or
128
128
  # node as a child.
129
129
  def or(right)
130
- Nodes::Grouping.new Nodes::Or.new(self, right)
130
+ Nodes::Grouping.new Nodes::Or.new([self, right])
131
131
  end
132
132
 
133
133
  ###
@@ -147,8 +147,9 @@ module Arel # :nodoc: all
147
147
  # Maybe we should just use `Table.engine`? :'(
148
148
  def to_sql(engine = Table.engine)
149
149
  collector = Arel::Collectors::SQLString.new
150
- collector = engine.connection.visitor.accept self, collector
151
- collector.value
150
+ engine.with_connection do |connection|
151
+ connection.visitor.accept(self, collector).value
152
+ end
152
153
  end
153
154
 
154
155
  def fetch_attribute
@@ -8,6 +8,13 @@ module Arel # :nodoc: all
8
8
  include Arel::AliasPredication
9
9
  include Arel::OrderPredications
10
10
 
11
+ attr_reader :retryable
12
+
13
+ def initialize(string, retryable: false)
14
+ @retryable = retryable
15
+ super(string)
16
+ end
17
+
11
18
  def encode_with(coder)
12
19
  coder.scalar = self.to_s
13
20
  end
data/lib/arel/nodes.rb CHANGED
@@ -41,8 +41,8 @@ require "arel/nodes/matches"
41
41
  require "arel/nodes/regexp"
42
42
  require "arel/nodes/cte"
43
43
 
44
- # nary
45
- require "arel/nodes/and"
44
+ # nary (And and Or)
45
+ require "arel/nodes/nary"
46
46
 
47
47
  # function
48
48
  # FIXME: Function + Alias can be rewritten as a Function and Alias node.
@@ -232,7 +232,7 @@ module Arel # :nodoc: all
232
232
  def grouping_any(method_id, others, *extras)
233
233
  nodes = others.map { |expr| send(method_id, expr, *extras) }
234
234
  Nodes::Grouping.new nodes.inject { |memo, node|
235
- Nodes::Or.new(memo, node)
235
+ Nodes::Or.new([memo, node])
236
236
  }
237
237
  end
238
238
 
@@ -46,7 +46,7 @@ module Arel # :nodoc: all
46
46
  end
47
47
 
48
48
  def as(other)
49
- create_table_alias grouping(@ast), Nodes::SqlLiteral.new(other)
49
+ create_table_alias grouping(@ast), Nodes::SqlLiteral.new(other, retryable: true)
50
50
  end
51
51
 
52
52
  def lock(locking = Arel.sql("FOR UPDATE"))
@@ -52,8 +52,9 @@ module Arel # :nodoc: all
52
52
 
53
53
  def to_sql(engine = Table.engine)
54
54
  collector = Arel::Collectors::SQLString.new
55
- collector = engine.connection.visitor.accept @ast, collector
56
- collector.value
55
+ engine.with_connection do |connection|
56
+ connection.visitor.accept(@ast, collector).value
57
+ end
57
58
  end
58
59
 
59
60
  def initialize_copy(other)
@@ -16,7 +16,8 @@ module Arel # :nodoc: all
16
16
  end
17
17
 
18
18
  def set(values)
19
- if String === values
19
+ case values
20
+ when String, Nodes::BoundSqlLiteral
20
21
  @ast.values = [values]
21
22
  else
22
23
  @ast.values = values.map { |column, value|
@@ -191,6 +191,7 @@ module Arel # :nodoc: all
191
191
  end
192
192
  end
193
193
  alias :visit_Arel_Nodes_And :visit__children
194
+ alias :visit_Arel_Nodes_Or :visit__children
194
195
  alias :visit_Arel_Nodes_With :visit__children
195
196
 
196
197
  def visit_String(o)
@@ -27,7 +27,7 @@ module Arel # :nodoc: all
27
27
  end
28
28
 
29
29
  def visit_Arel_Nodes_SelectCore(o, collector)
30
- o.froms ||= Arel.sql("DUAL")
30
+ o.froms ||= Arel.sql("DUAL", retryable: true)
31
31
  super
32
32
  end
33
33
 
@@ -59,9 +59,14 @@ module Arel # :nodoc: all
59
59
  infix_value o, collector, " NOT REGEXP "
60
60
  end
61
61
 
62
- # no-op
63
62
  def visit_Arel_Nodes_NullsFirst(o, collector)
64
- visit o.expr, collector
63
+ visit(o.expr.expr, collector) << " IS NOT NULL, "
64
+ visit(o.expr, collector)
65
+ end
66
+
67
+ def visit_Arel_Nodes_NullsLast(o, collector)
68
+ visit(o.expr.expr, collector) << " IS NULL, "
69
+ visit(o.expr, collector)
65
70
  end
66
71
 
67
72
  def visit_Arel_Nodes_Cte(o, collector)
@@ -98,7 +103,7 @@ module Arel # :nodoc: all
98
103
  Nodes::SelectStatement.new.tap do |stmt|
99
104
  core = stmt.cores.last
100
105
  core.froms = Nodes::Grouping.new(subselect).as("__active_record_temp")
101
- core.projections = [Arel.sql(quote_column_name(key.name))]
106
+ core.projections = [Arel.sql(quote_column_name(key.name), retryable: true)]
102
107
  end
103
108
  end
104
109
  end
@@ -63,7 +63,7 @@ module Arel # :nodoc: all
63
63
 
64
64
  def visit_Arel_Nodes_Lateral(o, collector)
65
65
  collector << "LATERAL "
66
- grouping_parentheses o, collector
66
+ grouping_parentheses o.expr, collector
67
67
  end
68
68
 
69
69
  def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
@@ -83,17 +83,6 @@ module Arel # :nodoc: all
83
83
 
84
84
  def bind_block; BIND_BLOCK; end
85
85
 
86
- # Used by Lateral visitor to enclose select queries in parentheses
87
- def grouping_parentheses(o, collector)
88
- if o.expr.is_a? Nodes::SelectStatement
89
- collector << "("
90
- visit o.expr, collector
91
- collector << ")"
92
- else
93
- visit o.expr, collector
94
- end
95
- end
96
-
97
86
  # Utilized by GroupingSet, Cube & RollUp visitors to
98
87
  # handle grouping aggregation semantics
99
88
  def grouping_array_or_grouping_element(o, collector)