activerecord 7.2.4 → 8.1.4

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 (210) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1013 -878
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/association_relation.rb +2 -1
  5. data/lib/active_record/associations/association.rb +69 -27
  6. data/lib/active_record/associations/association_scope.rb +1 -1
  7. data/lib/active_record/associations/belongs_to_association.rb +2 -2
  8. data/lib/active_record/associations/builder/association.rb +23 -11
  9. data/lib/active_record/associations/builder/belongs_to.rb +36 -8
  10. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  11. data/lib/active_record/associations/builder/has_one.rb +1 -1
  12. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  13. data/lib/active_record/associations/collection_association.rb +7 -3
  14. data/lib/active_record/associations/collection_proxy.rb +26 -4
  15. data/lib/active_record/associations/deprecation.rb +88 -0
  16. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  17. data/lib/active_record/associations/errors.rb +3 -0
  18. data/lib/active_record/associations/foreign_association.rb +3 -1
  19. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  20. data/lib/active_record/associations/has_one_association.rb +1 -0
  21. data/lib/active_record/associations/join_dependency.rb +3 -2
  22. data/lib/active_record/associations/preloader/association.rb +3 -3
  23. data/lib/active_record/associations/preloader/batch.rb +7 -1
  24. data/lib/active_record/associations/preloader/branch.rb +1 -0
  25. data/lib/active_record/associations/singular_association.rb +8 -3
  26. data/lib/active_record/associations.rb +192 -24
  27. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  28. data/lib/active_record/attribute_methods/primary_key.rb +4 -8
  29. data/lib/active_record/attribute_methods/query.rb +34 -0
  30. data/lib/active_record/attribute_methods/serialization.rb +18 -5
  31. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
  32. data/lib/active_record/attributes.rb +3 -0
  33. data/lib/active_record/autosave_association.rb +69 -27
  34. data/lib/active_record/base.rb +1 -2
  35. data/lib/active_record/callbacks.rb +1 -2
  36. data/lib/active_record/coders/column_serializer.rb +1 -1
  37. data/lib/active_record/coders/json.rb +16 -6
  38. data/lib/active_record/coders/yaml_column.rb +2 -2
  39. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +35 -28
  40. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
  41. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +68 -13
  42. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +429 -93
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +148 -74
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +27 -5
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -25
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +32 -35
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +124 -34
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +129 -63
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +132 -65
  53. data/lib/active_record/connection_adapters/column.rb +14 -4
  54. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  55. data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
  56. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  57. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
  58. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -66
  59. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
  60. data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -10
  61. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  62. data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
  63. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +79 -61
  64. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
  65. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
  67. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  69. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  70. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
  71. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +14 -33
  72. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +71 -32
  73. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +156 -68
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +78 -105
  75. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  76. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  77. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
  78. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
  79. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  80. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
  81. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +15 -16
  82. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +104 -37
  83. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  84. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +41 -66
  85. data/lib/active_record/connection_adapters/trilogy_adapter.rb +15 -18
  86. data/lib/active_record/connection_adapters.rb +1 -56
  87. data/lib/active_record/connection_handling.rb +25 -2
  88. data/lib/active_record/core.rb +33 -17
  89. data/lib/active_record/counter_cache.rb +35 -8
  90. data/lib/active_record/database_configurations/database_config.rb +9 -1
  91. data/lib/active_record/database_configurations/hash_config.rb +74 -9
  92. data/lib/active_record/database_configurations/url_config.rb +15 -3
  93. data/lib/active_record/database_configurations.rb +7 -3
  94. data/lib/active_record/delegated_type.rb +1 -1
  95. data/lib/active_record/disable_joins_association_relation.rb +5 -1
  96. data/lib/active_record/dynamic_matchers.rb +54 -69
  97. data/lib/active_record/encryption/config.rb +3 -1
  98. data/lib/active_record/encryption/encryptable_record.rb +8 -8
  99. data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
  100. data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
  101. data/lib/active_record/encryption/encryptor.rb +28 -8
  102. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  103. data/lib/active_record/encryption/scheme.rb +9 -2
  104. data/lib/active_record/enum.rb +33 -30
  105. data/lib/active_record/errors.rb +33 -9
  106. data/lib/active_record/explain.rb +1 -1
  107. data/lib/active_record/explain_registry.rb +51 -2
  108. data/lib/active_record/filter_attribute_handler.rb +76 -0
  109. data/lib/active_record/fixtures.rb +2 -4
  110. data/lib/active_record/future_result.rb +15 -9
  111. data/lib/active_record/gem_version.rb +2 -2
  112. data/lib/active_record/inheritance.rb +9 -4
  113. data/lib/active_record/insert_all.rb +14 -9
  114. data/lib/active_record/locking/optimistic.rb +8 -1
  115. data/lib/active_record/locking/pessimistic.rb +5 -0
  116. data/lib/active_record/log_subscriber.rb +31 -29
  117. data/lib/active_record/message_pack.rb +13 -0
  118. data/lib/active_record/middleware/shard_selector.rb +34 -17
  119. data/lib/active_record/migration/command_recorder.rb +56 -13
  120. data/lib/active_record/migration/compatibility.rb +37 -24
  121. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  122. data/lib/active_record/migration.rb +48 -42
  123. data/lib/active_record/model_schema.rb +40 -15
  124. data/lib/active_record/nested_attributes.rb +14 -12
  125. data/lib/active_record/persistence.rb +163 -133
  126. data/lib/active_record/query_cache.rb +22 -15
  127. data/lib/active_record/query_logs.rb +100 -52
  128. data/lib/active_record/query_logs_formatter.rb +17 -28
  129. data/lib/active_record/querying.rb +21 -9
  130. data/lib/active_record/railtie.rb +35 -30
  131. data/lib/active_record/railties/controller_runtime.rb +11 -6
  132. data/lib/active_record/railties/databases.rake +26 -38
  133. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  134. data/lib/active_record/railties/job_runtime.rb +10 -11
  135. data/lib/active_record/reflection.rb +54 -22
  136. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  137. data/lib/active_record/relation/batches.rb +147 -73
  138. data/lib/active_record/relation/calculations.rb +60 -43
  139. data/lib/active_record/relation/delegation.rb +25 -15
  140. data/lib/active_record/relation/finder_methods.rb +73 -43
  141. data/lib/active_record/relation/merger.rb +12 -10
  142. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  143. data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -9
  144. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
  145. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  146. data/lib/active_record/relation/predicate_builder.rb +23 -8
  147. data/lib/active_record/relation/query_attribute.rb +3 -1
  148. data/lib/active_record/relation/query_methods.rb +200 -96
  149. data/lib/active_record/relation/spawn_methods.rb +7 -7
  150. data/lib/active_record/relation/where_clause.rb +2 -9
  151. data/lib/active_record/relation.rb +127 -87
  152. data/lib/active_record/result.rb +109 -24
  153. data/lib/active_record/runtime_registry.rb +42 -58
  154. data/lib/active_record/sanitization.rb +9 -6
  155. data/lib/active_record/schema.rb +1 -1
  156. data/lib/active_record/schema_dumper.rb +17 -10
  157. data/lib/active_record/schema_migration.rb +2 -1
  158. data/lib/active_record/scoping/named.rb +5 -2
  159. data/lib/active_record/scoping.rb +0 -1
  160. data/lib/active_record/signed_id.rb +44 -16
  161. data/lib/active_record/statement_cache.rb +24 -20
  162. data/lib/active_record/store.rb +53 -22
  163. data/lib/active_record/structured_event_subscriber.rb +85 -0
  164. data/lib/active_record/table_metadata.rb +6 -23
  165. data/lib/active_record/tasks/abstract_tasks.rb +83 -0
  166. data/lib/active_record/tasks/database_tasks.rb +85 -85
  167. data/lib/active_record/tasks/mysql_database_tasks.rb +4 -58
  168. data/lib/active_record/tasks/postgresql_database_tasks.rb +7 -40
  169. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
  170. data/lib/active_record/test_databases.rb +18 -4
  171. data/lib/active_record/test_fixtures.rb +52 -6
  172. data/lib/active_record/testing/query_assertions.rb +8 -2
  173. data/lib/active_record/timestamp.rb +4 -2
  174. data/lib/active_record/token_for.rb +1 -1
  175. data/lib/active_record/transaction.rb +2 -5
  176. data/lib/active_record/transactions.rb +56 -32
  177. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  178. data/lib/active_record/type/internal/timezone.rb +7 -0
  179. data/lib/active_record/type/json.rb +13 -2
  180. data/lib/active_record/type/serialized.rb +16 -4
  181. data/lib/active_record/type/type_map.rb +1 -1
  182. data/lib/active_record/type_caster/connection.rb +2 -1
  183. data/lib/active_record/validations/associated.rb +1 -1
  184. data/lib/active_record/validations/uniqueness.rb +8 -8
  185. data/lib/active_record.rb +85 -50
  186. data/lib/arel/alias_predication.rb +2 -0
  187. data/lib/arel/collectors/bind.rb +2 -2
  188. data/lib/arel/collectors/sql_string.rb +1 -1
  189. data/lib/arel/collectors/substitute_binds.rb +2 -2
  190. data/lib/arel/crud.rb +6 -11
  191. data/lib/arel/nodes/binary.rb +1 -1
  192. data/lib/arel/nodes/count.rb +2 -2
  193. data/lib/arel/nodes/function.rb +4 -10
  194. data/lib/arel/nodes/named_function.rb +2 -2
  195. data/lib/arel/nodes/node.rb +2 -2
  196. data/lib/arel/nodes/sql_literal.rb +1 -1
  197. data/lib/arel/nodes.rb +0 -2
  198. data/lib/arel/predications.rb +1 -3
  199. data/lib/arel/select_manager.rb +7 -2
  200. data/lib/arel/table.rb +3 -7
  201. data/lib/arel/visitors/dot.rb +0 -3
  202. data/lib/arel/visitors/postgresql.rb +55 -0
  203. data/lib/arel/visitors/sqlite.rb +55 -8
  204. data/lib/arel/visitors/to_sql.rb +4 -22
  205. data/lib/arel.rb +3 -1
  206. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  207. metadata +15 -12
  208. data/lib/active_record/explain_subscriber.rb +0 -34
  209. data/lib/active_record/normalization.rb +0 -163
  210. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
data/lib/active_record.rb CHANGED
@@ -26,9 +26,11 @@
26
26
  require "active_support"
27
27
  require "active_support/rails"
28
28
  require "active_support/ordered_options"
29
+ require "active_support/core_ext/array/conversions"
29
30
  require "active_model"
30
31
  require "arel"
31
32
  require "yaml"
33
+ require "zlib"
32
34
 
33
35
  require "active_record/version"
34
36
  require "active_record/deprecator"
@@ -51,6 +53,7 @@ module ActiveRecord
51
53
  autoload :Enum
52
54
  autoload :Explain
53
55
  autoload :FixtureSet, "active_record/fixtures"
56
+ autoload :FilterAttributeHandler
54
57
  autoload :Inheritance
55
58
  autoload :Integration
56
59
  autoload :InternalMetadata
@@ -61,7 +64,6 @@ module ActiveRecord
61
64
  autoload :ModelSchema
62
65
  autoload :NestedAttributes
63
66
  autoload :NoTouching
64
- autoload :Normalization
65
67
  autoload :Persistence
66
68
  autoload :QueryCache
67
69
  autoload :QueryLogs
@@ -121,7 +123,7 @@ module ActiveRecord
121
123
  end
122
124
  end
123
125
 
124
- module Coders
126
+ module Coders # :nodoc:
125
127
  autoload :ColumnSerializer, "active_record/coders/column_serializer"
126
128
  autoload :JSON, "active_record/coders/json"
127
129
  autoload :YAMLColumn, "active_record/coders/yaml_column"
@@ -173,7 +175,8 @@ module ActiveRecord
173
175
  extend ActiveSupport::Autoload
174
176
 
175
177
  autoload :DatabaseTasks
176
- autoload :MySQLDatabaseTasks, "active_record/tasks/mysql_database_tasks"
178
+ autoload :AbstractTasks, "active_record/tasks/abstract_tasks"
179
+ autoload :MySQLDatabaseTasks, "active_record/tasks/mysql_database_tasks"
177
180
  autoload :PostgreSQLDatabaseTasks, "active_record/tasks/postgresql_database_tasks"
178
181
  autoload :SQLiteDatabaseTasks, "active_record/tasks/sqlite_database_tasks"
179
182
  end
@@ -196,6 +199,20 @@ module ActiveRecord
196
199
  singleton_class.attr_accessor :schema_cache_ignored_tables
197
200
  self.schema_cache_ignored_tables = []
198
201
 
202
+ # Checks to see if the +table_name+ is ignored by checking
203
+ # against the +schema_cache_ignored_tables+ option.
204
+ #
205
+ # ActiveRecord.schema_cache_ignored_table?(:developers)
206
+ #
207
+ def self.schema_cache_ignored_table?(table_name)
208
+ ActiveRecord.schema_cache_ignored_tables.any? do |ignored|
209
+ ignored === table_name
210
+ end
211
+ end
212
+
213
+ singleton_class.attr_accessor :database_cli
214
+ self.database_cli = { postgresql: "psql", mysql: %w[mysql mysql5], sqlite: "sqlite3" }
215
+
199
216
  singleton_class.attr_reader :default_timezone
200
217
 
201
218
  # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
@@ -244,6 +261,9 @@ module ActiveRecord
244
261
  ##
245
262
  # :singleton-method: db_warnings_ignore
246
263
  # Specify allowlist of database warnings.
264
+ # Can be a string, regular expression, or an error code from the database.
265
+ #
266
+ # ActiveRecord::Base.db_warnings_ignore = [/`SHOW WARNINGS` did not return the warnings/, "01000"]
247
267
  singleton_class.attr_accessor :db_warnings_ignore
248
268
  self.db_warnings_ignore = []
249
269
 
@@ -253,14 +273,6 @@ module ActiveRecord
253
273
  singleton_class.attr_accessor :reading_role
254
274
  self.reading_role = :reading
255
275
 
256
- def self.legacy_connection_handling=(_)
257
- raise ArgumentError, <<~MSG.squish
258
- The `legacy_connection_handling` setter was deprecated in 7.0 and removed in 7.1,
259
- but is still defined in your configuration. Please remove this call as it no longer
260
- has any effect."
261
- MSG
262
- end
263
-
264
276
  ##
265
277
  # :singleton-method: async_query_executor
266
278
  # Sets the async_query_executor for an application. By default the thread pool executor
@@ -279,6 +291,7 @@ module ActiveRecord
279
291
  def self.global_thread_pool_async_query_executor # :nodoc:
280
292
  concurrency = global_executor_concurrency || 4
281
293
  @global_thread_pool_async_query_executor ||= Concurrent::ThreadPoolExecutor.new(
294
+ name: "ActiveRecord-global-async-query-executor",
282
295
  min_threads: 0,
283
296
  max_threads: concurrency,
284
297
  max_queue: concurrency * 4,
@@ -344,28 +357,8 @@ module ActiveRecord
344
357
  singleton_class.attr_accessor :run_after_transaction_callbacks_in_order_defined
345
358
  self.run_after_transaction_callbacks_in_order_defined = false
346
359
 
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 8.0.
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 8.0.
358
- WARNING
359
- end
360
-
361
- ##
362
- # :singleton-method: warn_on_records_fetched_greater_than
363
- # Specify a threshold for the size of query result sets. If the number of
364
- # records in the set exceeds the threshold, a warning is logged. This can
365
- # be used to identify queries which load thousands of records and
366
- # potentially cause memory bloat.
367
- singleton_class.attr_accessor :warn_on_records_fetched_greater_than
368
- self.warn_on_records_fetched_greater_than = false
360
+ singleton_class.attr_accessor :raise_on_missing_required_finder_order_columns
361
+ self.raise_on_missing_required_finder_order_columns = false
369
362
 
370
363
  singleton_class.attr_accessor :application_record_class
371
364
  self.application_record_class = nil
@@ -384,7 +377,8 @@ module ActiveRecord
384
377
  # specific) SQL statements. If :ruby, the schema is dumped as an
385
378
  # ActiveRecord::Schema file which can be loaded into any database that
386
379
  # supports migrations. Use :ruby if you want to have different database
387
- # adapters for, e.g., your development and test environments.
380
+ # adapters for, e.g., your development and test environments. This can be
381
+ # overridden per-database in the database configuration.
388
382
  singleton_class.attr_accessor :schema_format
389
383
  self.schema_format = :ruby
390
384
 
@@ -416,6 +410,12 @@ module ActiveRecord
416
410
  singleton_class.attr_accessor :migration_strategy
417
411
  self.migration_strategy = Migration::DefaultStrategy
418
412
 
413
+ ##
414
+ # :singleton-method: schema_versions_formatter
415
+ # Specify the formatter used by schema dumper to format versions information.
416
+ singleton_class.attr_accessor :schema_versions_formatter
417
+ self.schema_versions_formatter = Migration::DefaultSchemaVersionsFormatter
418
+
419
419
  ##
420
420
  # :singleton-method: dump_schema_after_migration
421
421
  # Specify whether schema dump should happen at the end of the
@@ -444,20 +444,6 @@ module ActiveRecord
444
444
  singleton_class.attr_accessor :verify_foreign_keys_for_fixtures
445
445
  self.verify_foreign_keys_for_fixtures = false
446
446
 
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 8.0.
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 8.0.
458
- WARNING
459
- end
460
-
461
447
  singleton_class.attr_accessor :query_transformers
462
448
  self.query_transformers = []
463
449
 
@@ -490,6 +476,29 @@ module ActiveRecord
490
476
  singleton_class.attr_accessor :generate_secure_token_on
491
477
  self.generate_secure_token_on = :create
492
478
 
479
+ def self.deprecated_associations_options=(options)
480
+ raise ArgumentError, "deprecated_associations_options must be a hash" unless options.is_a?(Hash)
481
+
482
+ valid_keys = [:mode, :backtrace]
483
+
484
+ invalid_keys = options.keys - valid_keys
485
+ unless invalid_keys.empty?
486
+ inflected_key = invalid_keys.size == 1 ? "key" : "keys"
487
+ raise ArgumentError, "invalid deprecated_associations_options #{inflected_key} #{invalid_keys.map(&:inspect).to_sentence} (valid keys are #{valid_keys.map(&:inspect).to_sentence})"
488
+ end
489
+
490
+ options.each do |key, value|
491
+ ActiveRecord::Associations::Deprecation.send("#{key}=", value)
492
+ end
493
+ end
494
+
495
+ def self.deprecated_associations_options
496
+ {
497
+ mode: ActiveRecord::Associations::Deprecation.mode,
498
+ backtrace: ActiveRecord::Associations::Deprecation.backtrace
499
+ }
500
+ end
501
+
493
502
  def self.marshalling_format_version
494
503
  Marshalling.format_version
495
504
  end
@@ -526,6 +535,13 @@ module ActiveRecord
526
535
  }
527
536
  )
528
537
 
538
+ ##
539
+ # :singleton-method: message_verifiers
540
+ #
541
+ # ActiveSupport::MessageVerifiers instance for Active Record. If you are using
542
+ # Rails, this will be set to +Rails.application.message_verifiers+.
543
+ singleton_class.attr_accessor :message_verifiers
544
+
529
545
  def self.eager_load!
530
546
  super
531
547
  ActiveRecord::Locking.eager_load!
@@ -578,13 +594,32 @@ module ActiveRecord
578
594
  open_transactions = []
579
595
  Base.connection_handler.each_connection_pool do |pool|
580
596
  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
597
+ current_transaction = active_connection.current_transaction
598
+
599
+ if current_transaction.open? && current_transaction.joinable?
600
+ open_transactions << current_transaction
583
601
  end
584
602
  end
585
603
  end
586
604
  open_transactions
587
605
  end
606
+
607
+ def self.default_transaction_isolation_level=(isolation_level) # :nodoc:
608
+ ActiveSupport::IsolatedExecutionState[:active_record_transaction_isolation] = isolation_level
609
+ end
610
+
611
+ def self.default_transaction_isolation_level # :nodoc:
612
+ ActiveSupport::IsolatedExecutionState[:active_record_transaction_isolation]
613
+ end
614
+
615
+ # Sets a transaction isolation level for all connection pools within the block.
616
+ def self.with_transaction_isolation_level(isolation_level, &block)
617
+ original_level = self.default_transaction_isolation_level
618
+ self.default_transaction_isolation_level = isolation_level
619
+ yield
620
+ ensure
621
+ self.default_transaction_isolation_level = original_level
622
+ end
588
623
  end
589
624
 
590
625
  ActiveSupport.on_load(:active_record) do
@@ -3,6 +3,8 @@
3
3
  module Arel # :nodoc: all
4
4
  module AliasPredication
5
5
  def as(other)
6
+ other = other.name if other.is_a?(Symbol)
7
+
6
8
  Nodes::As.new self, Nodes::SqlLiteral.new(other, retryable: true)
7
9
  end
8
10
  end
@@ -13,12 +13,12 @@ module Arel # :nodoc: all
13
13
  self
14
14
  end
15
15
 
16
- def add_bind(bind, &block)
16
+ def add_bind(bind, &)
17
17
  @binds << bind
18
18
  self
19
19
  end
20
20
 
21
- def add_binds(binds, proc_for_binds = nil)
21
+ def add_binds(binds, proc_for_binds = nil, &)
22
22
  @binds.concat proc_for_binds ? binds.map(&proc_for_binds) : binds
23
23
  self
24
24
  end
@@ -12,7 +12,7 @@ module Arel # :nodoc: all
12
12
  @bind_index = 1
13
13
  end
14
14
 
15
- def add_bind(bind)
15
+ def add_bind(bind, &)
16
16
  self << yield(@bind_index)
17
17
  @bind_index += 1
18
18
  self
@@ -15,12 +15,12 @@ module Arel # :nodoc: all
15
15
  self
16
16
  end
17
17
 
18
- def add_bind(bind)
18
+ def add_bind(bind, &)
19
19
  bind = bind.value_for_database if bind.respond_to?(:value_for_database)
20
20
  self << quoter.quote(bind)
21
21
  end
22
22
 
23
- def add_binds(binds, proc_for_binds = nil)
23
+ def add_binds(binds, proc_for_binds = nil, &)
24
24
  self << binds.map { |bind| quoter.quote(bind) }.join(", ")
25
25
  end
26
26
 
data/lib/arel/crud.rb CHANGED
@@ -14,12 +14,7 @@ module Arel # :nodoc: all
14
14
  InsertManager.new
15
15
  end
16
16
 
17
- def compile_update(
18
- values,
19
- key = nil,
20
- having_clause = nil,
21
- group_values_columns = []
22
- )
17
+ def compile_update(values, key = nil)
23
18
  um = UpdateManager.new(source)
24
19
  um.set(values)
25
20
  um.take(limit)
@@ -29,12 +24,12 @@ module Arel # :nodoc: all
29
24
  um.comment(comment)
30
25
  um.key = key
31
26
 
32
- um.group(group_values_columns) unless group_values_columns.empty?
33
- um.having(having_clause) unless having_clause.nil?
27
+ um.ast.groups = @ctx.groups
28
+ @ctx.havings.each { |h| um.having(h) }
34
29
  um
35
30
  end
36
31
 
37
- def compile_delete(key = nil, having_clause = nil, group_values_columns = [])
32
+ def compile_delete(key = nil)
38
33
  dm = DeleteManager.new(source)
39
34
  dm.take(limit)
40
35
  dm.offset(offset)
@@ -42,8 +37,8 @@ module Arel # :nodoc: all
42
37
  dm.wheres = constraints
43
38
  dm.comment(comment)
44
39
  dm.key = key
45
- dm.group(group_values_columns) unless group_values_columns.empty?
46
- dm.having(having_clause) unless having_clause.nil?
40
+ dm.ast.groups = @ctx.groups
41
+ @ctx.havings.each { |h| dm.having(h) }
47
42
  dm
48
43
  end
49
44
  end
@@ -30,7 +30,7 @@ module Arel # :nodoc: all
30
30
  end
31
31
 
32
32
  module FetchAttribute
33
- def fetch_attribute
33
+ def fetch_attribute(&)
34
34
  if left.is_a?(Arel::Attributes::Attribute)
35
35
  yield left
36
36
  elsif right.is_a?(Arel::Attributes::Attribute)
@@ -3,8 +3,8 @@
3
3
  module Arel # :nodoc: all
4
4
  module Nodes
5
5
  class Count < Arel::Nodes::Function
6
- def initialize(expr, distinct = false, aliaz = nil)
7
- super(expr, aliaz)
6
+ def initialize(expr, distinct = false)
7
+ super(expr)
8
8
  @distinct = distinct
9
9
  end
10
10
  end
@@ -5,28 +5,22 @@ module Arel # :nodoc: all
5
5
  class Function < Arel::Nodes::NodeExpression
6
6
  include Arel::WindowPredications
7
7
  include Arel::FilterPredications
8
- attr_accessor :expressions, :alias, :distinct
9
8
 
10
- def initialize(expr, aliaz = nil)
9
+ attr_accessor :expressions, :distinct
10
+
11
+ def initialize(expr)
11
12
  super()
12
13
  @expressions = expr
13
- @alias = aliaz && SqlLiteral.new(aliaz)
14
14
  @distinct = false
15
15
  end
16
16
 
17
- def as(aliaz)
18
- self.alias = SqlLiteral.new(aliaz)
19
- self
20
- end
21
-
22
17
  def hash
23
- [@expressions, @alias, @distinct].hash
18
+ [@expressions, @distinct].hash
24
19
  end
25
20
 
26
21
  def eql?(other)
27
22
  self.class == other.class &&
28
23
  self.expressions == other.expressions &&
29
- self.alias == other.alias &&
30
24
  self.distinct == other.distinct
31
25
  end
32
26
  alias :== :eql?
@@ -5,8 +5,8 @@ module Arel # :nodoc: all
5
5
  class NamedFunction < Arel::Nodes::Function
6
6
  attr_accessor :name
7
7
 
8
- def initialize(name, expr, aliaz = nil)
9
- super(expr, aliaz)
8
+ def initialize(name, expr)
9
+ super(expr)
10
10
  @name = name
11
11
  end
12
12
 
@@ -6,7 +6,7 @@ module Arel # :nodoc: all
6
6
  #
7
7
  # Active Record uses Arel to compose SQL statements. Instead of building SQL strings directly, it's building an
8
8
  # abstract syntax tree (AST) of the statement using various types of Arel::Nodes::Node. Each node represents a
9
- # fragment of a SQL statement.
9
+ # fragment of an SQL statement.
10
10
  #
11
11
  # The intermediate representation allows Arel to compile the statement into the database's specific SQL dialect
12
12
  # only before sending it without having to care about the nuances of each database when building the statement.
@@ -152,7 +152,7 @@ module Arel # :nodoc: all
152
152
  end
153
153
  end
154
154
 
155
- def fetch_attribute
155
+ def fetch_attribute(&)
156
156
  end
157
157
 
158
158
  def equality?; false; end
@@ -19,7 +19,7 @@ module Arel # :nodoc: all
19
19
  coder.scalar = self.to_s
20
20
  end
21
21
 
22
- def fetch_attribute
22
+ def fetch_attribute(&)
23
23
  end
24
24
 
25
25
  def +(other)
data/lib/arel/nodes.rb CHANGED
@@ -45,8 +45,6 @@ require "arel/nodes/cte"
45
45
  require "arel/nodes/nary"
46
46
 
47
47
  # function
48
- # FIXME: Function + Alias can be rewritten as a Function and Alias node.
49
- # We should make Function a Unary node and deprecate the use of "aliaz"
50
48
  require "arel/nodes/function"
51
49
  require "arel/nodes/count"
52
50
  require "arel/nodes/extract"
@@ -231,9 +231,7 @@ module Arel # :nodoc: all
231
231
  private
232
232
  def grouping_any(method_id, others, *extras)
233
233
  nodes = others.map { |expr| send(method_id, expr, *extras) }
234
- Nodes::Grouping.new nodes.inject { |memo, node|
235
- Nodes::Or.new([memo, node])
236
- }
234
+ Nodes::Grouping.new Nodes::Or.new(nodes)
237
235
  end
238
236
 
239
237
  def grouping_all(method_id, others, *extras)
@@ -74,8 +74,13 @@ module Arel # :nodoc: all
74
74
  def group(*columns)
75
75
  columns.each do |column|
76
76
  # FIXME: backwards compat
77
- column = Nodes::SqlLiteral.new(column) if String === column
78
- column = Nodes::SqlLiteral.new(column.to_s) if Symbol === column
77
+ case column
78
+ when Nodes::SqlLiteral
79
+ when String
80
+ column = Nodes::SqlLiteral.new(column)
81
+ when Symbol
82
+ column = Nodes::SqlLiteral.new(column.name)
83
+ end
79
84
 
80
85
  @ctx.groups.push Nodes::Group.new column
81
86
  end
data/lib/arel/table.rb CHANGED
@@ -12,13 +12,9 @@ module Arel # :nodoc: all
12
12
  attr_reader :table_alias
13
13
 
14
14
  def initialize(name, as: nil, klass: nil, type_caster: klass&.type_caster)
15
- @name =
16
- case name
17
- when Symbol then name.to_s
18
- else
19
- name
20
- end
15
+ name = name.name if name.is_a?(Symbol)
21
16
 
17
+ @name = name
22
18
  @klass = klass
23
19
  @type_caster = type_caster
24
20
 
@@ -84,7 +80,7 @@ module Arel # :nodoc: all
84
80
  end
85
81
 
86
82
  def [](name, table = self)
87
- name = name.to_s if name.is_a?(Symbol)
83
+ name = name.name if name.is_a?(Symbol)
88
84
  name = @klass.attribute_aliases[name] || name if @klass
89
85
  Attribute.new(table, name)
90
86
  end
@@ -34,7 +34,6 @@ module Arel # :nodoc: all
34
34
  def visit_Arel_Nodes_Function(o)
35
35
  visit_edge o, "expressions"
36
36
  visit_edge o, "distinct"
37
- visit_edge o, "alias"
38
37
  end
39
38
 
40
39
  def visit_Arel_Nodes_Unary(o)
@@ -108,14 +107,12 @@ module Arel # :nodoc: all
108
107
 
109
108
  def visit_Arel_Nodes_Extract(o)
110
109
  visit_edge o, "expressions"
111
- visit_edge o, "alias"
112
110
  end
113
111
 
114
112
  def visit_Arel_Nodes_NamedFunction(o)
115
113
  visit_edge o, "name"
116
114
  visit_edge o, "expressions"
117
115
  visit_edge o, "distinct"
118
- visit_edge o, "alias"
119
116
  end
120
117
 
121
118
  def visit_Arel_Nodes_InsertStatement(o)
@@ -4,6 +4,55 @@ module Arel # :nodoc: all
4
4
  module Visitors
5
5
  class PostgreSQL < Arel::Visitors::ToSql
6
6
  private
7
+ def visit_Arel_Nodes_UpdateStatement(o, collector)
8
+ collector.retryable = false
9
+ o = prepare_update_statement(o)
10
+
11
+ collector << "UPDATE "
12
+
13
+ # UPDATE with JOIN is in the form of:
14
+ #
15
+ # UPDATE t1 AS __active_record_update_alias
16
+ # SET ..
17
+ # FROM t1 JOIN t2 ON t2.join_id = t1.join_id ..
18
+ # WHERE t1.id = __active_record_update_alias.id AND ..
19
+ if has_join_sources?(o)
20
+ collector = visit o.relation.left, collector
21
+ collect_nodes_for o.values, collector, " SET "
22
+ collector << " FROM "
23
+ collector = inject_join o.relation.right, collector, " "
24
+ else
25
+ collector = visit o.relation, collector
26
+ collect_nodes_for o.values, collector, " SET "
27
+ end
28
+
29
+ collect_nodes_for o.wheres, collector, " WHERE ", " AND "
30
+ collect_nodes_for o.orders, collector, " ORDER BY "
31
+ maybe_visit o.limit, collector
32
+ maybe_visit o.comment, collector
33
+ end
34
+
35
+ # In the simple case, PostgreSQL allows us to place FROM or JOINs directly into the UPDATE
36
+ # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
37
+ # these, we must use a subquery.
38
+ def prepare_update_statement(o)
39
+ if o.key && has_join_sources?(o) && !has_group_by_and_having?(o) && !has_limit_or_offset_or_orders?(o)
40
+ # Join clauses cannot reference the target table, so alias the
41
+ # updated table, place the entire relation in the FROM clause, and
42
+ # add a self-join (which requires the primary key)
43
+ stmt = o.clone
44
+ stmt.relation, stmt.wheres = o.relation.clone, o.wheres.clone
45
+ stmt.relation.right = [stmt.relation.left, *stmt.relation.right]
46
+ stmt.relation.left = stmt.relation.left.alias("__active_record_update_alias")
47
+ Array.wrap(o.key).each do |key|
48
+ stmt.wheres << key.eq(stmt.relation.left[key.name])
49
+ end
50
+ stmt
51
+ else
52
+ super
53
+ end
54
+ end
55
+
7
56
  def visit_Arel_Nodes_Matches(o, collector)
8
57
  op = o.case_sensitive ? " LIKE " : " ILIKE "
9
58
  collector = infix_value o, collector, op
@@ -66,6 +115,12 @@ module Arel # :nodoc: all
66
115
  grouping_parentheses o.expr, collector
67
116
  end
68
117
 
118
+ def visit_Arel_Nodes_InnerJoin(o, collector)
119
+ return super if o.right
120
+ collector << "CROSS JOIN "
121
+ visit o.left, collector
122
+ end
123
+
69
124
  def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
70
125
  collector = visit o.left, collector
71
126
  collector << " IS NOT DISTINCT FROM "
@@ -4,6 +4,61 @@ module Arel # :nodoc: all
4
4
  module Visitors
5
5
  class SQLite < Arel::Visitors::ToSql
6
6
  private
7
+ def visit_Arel_Nodes_UpdateStatement(o, collector)
8
+ collector.retryable = false
9
+ o = prepare_update_statement(o)
10
+
11
+ collector << "UPDATE "
12
+
13
+ # UPDATE with JOIN is in the form of:
14
+ #
15
+ # UPDATE t1 AS __active_record_update_alias
16
+ # SET ..
17
+ # FROM t1 JOIN t2 ON t2.join_id = t1.join_id ..
18
+ # WHERE t1.id = __active_record_update_alias.id AND ..
19
+ if has_join_sources?(o)
20
+ collector = visit o.relation.left, collector
21
+ collect_nodes_for o.values, collector, " SET "
22
+ collector << " FROM "
23
+ collector = inject_join o.relation.right, collector, " "
24
+ else
25
+ collector = visit o.relation, collector
26
+ collect_nodes_for o.values, collector, " SET "
27
+ end
28
+
29
+ collect_nodes_for o.wheres, collector, " WHERE ", " AND "
30
+ collect_nodes_for o.orders, collector, " ORDER BY "
31
+ maybe_visit o.limit, collector
32
+ maybe_visit o.comment, collector
33
+ end
34
+
35
+ def prepare_update_statement(o)
36
+ # Sqlite need to be built with the SQLITE_ENABLE_UPDATE_DELETE_LIMIT compile-time option
37
+ # to support LIMIT/OFFSET/ORDER in UPDATE and DELETE statements.
38
+ if o.key && has_join_sources?(o) && !has_group_by_and_having?(o) && !has_limit_or_offset_or_orders?(o)
39
+ # Join clauses cannot reference the target table, so alias the
40
+ # updated table, place the entire relation in the FROM clause, and
41
+ # add a self-join (which requires the primary key)
42
+ stmt = o.clone
43
+ stmt.relation, stmt.wheres = o.relation.clone, o.wheres.clone
44
+ stmt.relation.right = [stmt.relation.left, *stmt.relation.right]
45
+ stmt.relation.left = stmt.relation.left.alias("__active_record_update_alias")
46
+ Array.wrap(o.key).each do |key|
47
+ stmt.wheres << key.eq(stmt.relation.left[key.name])
48
+ end
49
+ stmt
50
+ else
51
+ super
52
+ end
53
+ end
54
+
55
+ def visit_Arel_Nodes_TableAlias(o, collector)
56
+ # "AS" is not optional in "{UPDATE | DELETE} table AS alias ..."
57
+ collector = visit o.relation, collector
58
+ collector << " AS "
59
+ collector << quote_table_name(o.name)
60
+ end
61
+
7
62
  # Locks are not supported in SQLite
8
63
  def visit_Arel_Nodes_Lock(o, collector)
9
64
  collector
@@ -14,14 +69,6 @@ module Arel # :nodoc: all
14
69
  super
15
70
  end
16
71
 
17
- def visit_Arel_Nodes_True(o, collector)
18
- collector << "1"
19
- end
20
-
21
- def visit_Arel_Nodes_False(o, collector)
22
- collector << "0"
23
- end
24
-
25
72
  def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
26
73
  collector = visit o.left, collector
27
74
  collector << " IS "