activerecord 7.1.4 → 7.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +515 -2387
  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 +27 -25
  18. data/lib/active_record/associations/join_dependency.rb +1 -1
  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 -6
  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/interval.rb +1 -1
  57. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  58. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  59. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
  60. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -11
  61. data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
  62. data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
  63. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  64. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
  65. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
  66. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  67. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  68. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  69. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
  70. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +107 -75
  71. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
  72. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
  73. data/lib/active_record/connection_adapters.rb +121 -0
  74. data/lib/active_record/connection_handling.rb +56 -41
  75. data/lib/active_record/core.rb +52 -36
  76. data/lib/active_record/counter_cache.rb +18 -9
  77. data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
  78. data/lib/active_record/database_configurations/database_config.rb +15 -4
  79. data/lib/active_record/database_configurations/hash_config.rb +38 -34
  80. data/lib/active_record/database_configurations/url_config.rb +20 -1
  81. data/lib/active_record/database_configurations.rb +1 -1
  82. data/lib/active_record/delegated_type.rb +24 -0
  83. data/lib/active_record/dynamic_matchers.rb +2 -2
  84. data/lib/active_record/encryption/encryptable_record.rb +2 -2
  85. data/lib/active_record/encryption/encrypted_attribute_type.rb +22 -2
  86. data/lib/active_record/encryption/encryptor.rb +17 -2
  87. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  88. data/lib/active_record/encryption/message_serializer.rb +4 -0
  89. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  90. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  91. data/lib/active_record/enum.rb +11 -2
  92. data/lib/active_record/errors.rb +16 -11
  93. data/lib/active_record/explain.rb +13 -24
  94. data/lib/active_record/fixtures.rb +37 -31
  95. data/lib/active_record/future_result.rb +8 -4
  96. data/lib/active_record/gem_version.rb +3 -3
  97. data/lib/active_record/inheritance.rb +4 -2
  98. data/lib/active_record/insert_all.rb +18 -15
  99. data/lib/active_record/integration.rb +4 -1
  100. data/lib/active_record/internal_metadata.rb +48 -34
  101. data/lib/active_record/locking/optimistic.rb +7 -6
  102. data/lib/active_record/log_subscriber.rb +0 -21
  103. data/lib/active_record/message_pack.rb +1 -1
  104. data/lib/active_record/migration/command_recorder.rb +2 -3
  105. data/lib/active_record/migration/compatibility.rb +5 -3
  106. data/lib/active_record/migration/default_strategy.rb +4 -5
  107. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  108. data/lib/active_record/migration.rb +85 -76
  109. data/lib/active_record/model_schema.rb +28 -67
  110. data/lib/active_record/nested_attributes.rb +11 -3
  111. data/lib/active_record/normalization.rb +3 -7
  112. data/lib/active_record/persistence.rb +30 -352
  113. data/lib/active_record/query_cache.rb +18 -6
  114. data/lib/active_record/query_logs.rb +15 -0
  115. data/lib/active_record/querying.rb +21 -9
  116. data/lib/active_record/railtie.rb +48 -57
  117. data/lib/active_record/railties/controller_runtime.rb +13 -4
  118. data/lib/active_record/railties/databases.rake +41 -44
  119. data/lib/active_record/reflection.rb +90 -35
  120. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  121. data/lib/active_record/relation/batches.rb +3 -3
  122. data/lib/active_record/relation/calculations.rb +94 -61
  123. data/lib/active_record/relation/delegation.rb +8 -11
  124. data/lib/active_record/relation/finder_methods.rb +16 -2
  125. data/lib/active_record/relation/merger.rb +4 -6
  126. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  127. data/lib/active_record/relation/predicate_builder.rb +3 -3
  128. data/lib/active_record/relation/query_methods.rb +196 -57
  129. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  130. data/lib/active_record/relation/spawn_methods.rb +2 -18
  131. data/lib/active_record/relation/where_clause.rb +7 -19
  132. data/lib/active_record/relation.rb +496 -72
  133. data/lib/active_record/result.rb +31 -44
  134. data/lib/active_record/runtime_registry.rb +39 -0
  135. data/lib/active_record/sanitization.rb +24 -19
  136. data/lib/active_record/schema.rb +8 -6
  137. data/lib/active_record/schema_dumper.rb +19 -9
  138. data/lib/active_record/schema_migration.rb +30 -14
  139. data/lib/active_record/signed_id.rb +11 -1
  140. data/lib/active_record/statement_cache.rb +7 -7
  141. data/lib/active_record/table_metadata.rb +1 -10
  142. data/lib/active_record/tasks/database_tasks.rb +76 -59
  143. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  144. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  145. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  146. data/lib/active_record/test_fixtures.rb +81 -91
  147. data/lib/active_record/testing/query_assertions.rb +121 -0
  148. data/lib/active_record/timestamp.rb +1 -1
  149. data/lib/active_record/token_for.rb +22 -12
  150. data/lib/active_record/touch_later.rb +1 -1
  151. data/lib/active_record/transaction.rb +68 -0
  152. data/lib/active_record/transactions.rb +43 -14
  153. data/lib/active_record/translation.rb +0 -2
  154. data/lib/active_record/type/serialized.rb +1 -3
  155. data/lib/active_record/type_caster/connection.rb +4 -4
  156. data/lib/active_record/validations/associated.rb +9 -3
  157. data/lib/active_record/validations/uniqueness.rb +14 -10
  158. data/lib/active_record/validations.rb +4 -1
  159. data/lib/active_record.rb +149 -40
  160. data/lib/arel/alias_predication.rb +1 -1
  161. data/lib/arel/collectors/bind.rb +2 -0
  162. data/lib/arel/collectors/composite.rb +7 -0
  163. data/lib/arel/collectors/sql_string.rb +1 -1
  164. data/lib/arel/collectors/substitute_binds.rb +1 -1
  165. data/lib/arel/nodes/binary.rb +0 -6
  166. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  167. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  168. data/lib/arel/nodes/node.rb +4 -3
  169. data/lib/arel/nodes/sql_literal.rb +7 -0
  170. data/lib/arel/nodes.rb +2 -2
  171. data/lib/arel/predications.rb +1 -1
  172. data/lib/arel/select_manager.rb +1 -1
  173. data/lib/arel/tree_manager.rb +3 -2
  174. data/lib/arel/update_manager.rb +2 -1
  175. data/lib/arel/visitors/dot.rb +1 -0
  176. data/lib/arel/visitors/mysql.rb +9 -4
  177. data/lib/arel/visitors/postgresql.rb +1 -12
  178. data/lib/arel/visitors/to_sql.rb +29 -16
  179. data/lib/arel.rb +7 -3
  180. metadata +17 -12
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)