activerecord 7.0.0 → 7.1.2

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 (251) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1701 -1039
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +18 -3
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +17 -12
  15. data/lib/active_record/associations/collection_proxy.rb +22 -12
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +27 -17
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency.rb +20 -14
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  23. data/lib/active_record/associations/preloader.rb +13 -10
  24. data/lib/active_record/associations/singular_association.rb +1 -1
  25. data/lib/active_record/associations/through_association.rb +22 -11
  26. data/lib/active_record/associations.rb +362 -236
  27. data/lib/active_record/attribute_assignment.rb +0 -2
  28. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  29. data/lib/active_record/attribute_methods/dirty.rb +52 -34
  30. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  31. data/lib/active_record/attribute_methods/query.rb +28 -16
  32. data/lib/active_record/attribute_methods/read.rb +18 -5
  33. data/lib/active_record/attribute_methods/serialization.rb +172 -69
  34. data/lib/active_record/attribute_methods/write.rb +3 -3
  35. data/lib/active_record/attribute_methods.rb +110 -28
  36. data/lib/active_record/attributes.rb +3 -3
  37. data/lib/active_record/autosave_association.rb +56 -10
  38. data/lib/active_record/base.rb +10 -5
  39. data/lib/active_record/callbacks.rb +16 -32
  40. data/lib/active_record/coders/column_serializer.rb +61 -0
  41. data/lib/active_record/coders/json.rb +1 -1
  42. data/lib/active_record/coders/yaml_column.rb +70 -34
  43. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +164 -89
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  45. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  46. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  47. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  48. data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
  49. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  50. data/lib/active_record/connection_adapters/abstract/quoting.rb +52 -8
  51. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  52. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  53. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
  54. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  55. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +302 -131
  56. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  57. data/lib/active_record/connection_adapters/abstract_adapter.rb +513 -106
  58. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -104
  59. data/lib/active_record/connection_adapters/column.rb +9 -0
  60. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  61. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
  62. data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -12
  63. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  64. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  65. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  66. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
  67. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  69. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  70. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +16 -3
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -45
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
  75. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  76. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  77. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +4 -2
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
  80. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  81. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  82. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  83. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +372 -63
  84. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  85. data/lib/active_record/connection_adapters/postgresql_adapter.rb +359 -197
  86. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  87. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  88. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
  89. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +22 -5
  90. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  91. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +41 -22
  92. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +242 -81
  93. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  94. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  95. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  96. data/lib/active_record/connection_adapters.rb +3 -1
  97. data/lib/active_record/connection_handling.rb +73 -96
  98. data/lib/active_record/core.rb +142 -153
  99. data/lib/active_record/counter_cache.rb +46 -25
  100. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -0
  101. data/lib/active_record/database_configurations/database_config.rb +9 -3
  102. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  103. data/lib/active_record/database_configurations/url_config.rb +17 -11
  104. data/lib/active_record/database_configurations.rb +87 -34
  105. data/lib/active_record/delegated_type.rb +9 -4
  106. data/lib/active_record/deprecator.rb +7 -0
  107. data/lib/active_record/destroy_association_async_job.rb +2 -0
  108. data/lib/active_record/disable_joins_association_relation.rb +1 -1
  109. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  110. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  111. data/lib/active_record/encryption/config.rb +25 -1
  112. data/lib/active_record/encryption/configurable.rb +13 -14
  113. data/lib/active_record/encryption/context.rb +10 -3
  114. data/lib/active_record/encryption/contexts.rb +8 -4
  115. data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
  116. data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
  117. data/lib/active_record/encryption/encryptable_record.rb +38 -22
  118. data/lib/active_record/encryption/encrypted_attribute_type.rb +19 -8
  119. data/lib/active_record/encryption/encryptor.rb +7 -7
  120. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
  121. data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -86
  122. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  123. data/lib/active_record/encryption/key_generator.rb +12 -1
  124. data/lib/active_record/encryption/message.rb +1 -1
  125. data/lib/active_record/encryption/message_serializer.rb +2 -0
  126. data/lib/active_record/encryption/properties.rb +4 -4
  127. data/lib/active_record/encryption/scheme.rb +20 -23
  128. data/lib/active_record/encryption.rb +1 -0
  129. data/lib/active_record/enum.rb +113 -29
  130. data/lib/active_record/errors.rb +108 -15
  131. data/lib/active_record/explain.rb +23 -3
  132. data/lib/active_record/explain_subscriber.rb +1 -1
  133. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  134. data/lib/active_record/fixture_set/render_context.rb +2 -0
  135. data/lib/active_record/fixture_set/table_row.rb +29 -8
  136. data/lib/active_record/fixtures.rb +121 -73
  137. data/lib/active_record/future_result.rb +30 -5
  138. data/lib/active_record/gem_version.rb +3 -3
  139. data/lib/active_record/inheritance.rb +30 -16
  140. data/lib/active_record/insert_all.rb +57 -10
  141. data/lib/active_record/integration.rb +10 -10
  142. data/lib/active_record/internal_metadata.rb +120 -30
  143. data/lib/active_record/locking/optimistic.rb +32 -18
  144. data/lib/active_record/locking/pessimistic.rb +8 -5
  145. data/lib/active_record/log_subscriber.rb +39 -17
  146. data/lib/active_record/marshalling.rb +56 -0
  147. data/lib/active_record/message_pack.rb +124 -0
  148. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  149. data/lib/active_record/middleware/database_selector.rb +18 -13
  150. data/lib/active_record/middleware/shard_selector.rb +7 -5
  151. data/lib/active_record/migration/command_recorder.rb +108 -10
  152. data/lib/active_record/migration/compatibility.rb +158 -64
  153. data/lib/active_record/migration/default_strategy.rb +23 -0
  154. data/lib/active_record/migration/execution_strategy.rb +19 -0
  155. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  156. data/lib/active_record/migration.rb +274 -117
  157. data/lib/active_record/model_schema.rb +86 -54
  158. data/lib/active_record/nested_attributes.rb +24 -6
  159. data/lib/active_record/normalization.rb +167 -0
  160. data/lib/active_record/persistence.rb +200 -47
  161. data/lib/active_record/promise.rb +84 -0
  162. data/lib/active_record/query_cache.rb +3 -21
  163. data/lib/active_record/query_logs.rb +87 -51
  164. data/lib/active_record/query_logs_formatter.rb +41 -0
  165. data/lib/active_record/querying.rb +16 -3
  166. data/lib/active_record/railtie.rb +128 -62
  167. data/lib/active_record/railties/controller_runtime.rb +12 -8
  168. data/lib/active_record/railties/databases.rake +145 -146
  169. data/lib/active_record/railties/job_runtime.rb +23 -0
  170. data/lib/active_record/readonly_attributes.rb +32 -5
  171. data/lib/active_record/reflection.rb +189 -45
  172. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  173. data/lib/active_record/relation/batches.rb +190 -61
  174. data/lib/active_record/relation/calculations.rb +208 -83
  175. data/lib/active_record/relation/delegation.rb +23 -9
  176. data/lib/active_record/relation/finder_methods.rb +77 -16
  177. data/lib/active_record/relation/merger.rb +2 -0
  178. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  179. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  180. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  181. data/lib/active_record/relation/predicate_builder.rb +26 -14
  182. data/lib/active_record/relation/query_attribute.rb +25 -1
  183. data/lib/active_record/relation/query_methods.rb +430 -77
  184. data/lib/active_record/relation/spawn_methods.rb +18 -1
  185. data/lib/active_record/relation.rb +98 -41
  186. data/lib/active_record/result.rb +25 -9
  187. data/lib/active_record/runtime_registry.rb +10 -1
  188. data/lib/active_record/sanitization.rb +57 -16
  189. data/lib/active_record/schema.rb +36 -22
  190. data/lib/active_record/schema_dumper.rb +65 -23
  191. data/lib/active_record/schema_migration.rb +68 -33
  192. data/lib/active_record/scoping/default.rb +20 -12
  193. data/lib/active_record/scoping/named.rb +2 -2
  194. data/lib/active_record/scoping.rb +2 -1
  195. data/lib/active_record/secure_password.rb +60 -0
  196. data/lib/active_record/secure_token.rb +21 -3
  197. data/lib/active_record/serialization.rb +5 -0
  198. data/lib/active_record/signed_id.rb +9 -7
  199. data/lib/active_record/store.rb +16 -11
  200. data/lib/active_record/suppressor.rb +3 -1
  201. data/lib/active_record/table_metadata.rb +16 -3
  202. data/lib/active_record/tasks/database_tasks.rb +138 -107
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  206. data/lib/active_record/test_fixtures.rb +123 -99
  207. data/lib/active_record/timestamp.rb +27 -15
  208. data/lib/active_record/token_for.rb +113 -0
  209. data/lib/active_record/touch_later.rb +11 -6
  210. data/lib/active_record/transactions.rb +39 -13
  211. data/lib/active_record/translation.rb +1 -1
  212. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  213. data/lib/active_record/type/internal/timezone.rb +7 -2
  214. data/lib/active_record/type/serialized.rb +8 -4
  215. data/lib/active_record/type/time.rb +4 -0
  216. data/lib/active_record/validations/absence.rb +1 -1
  217. data/lib/active_record/validations/associated.rb +3 -3
  218. data/lib/active_record/validations/numericality.rb +5 -4
  219. data/lib/active_record/validations/presence.rb +5 -28
  220. data/lib/active_record/validations/uniqueness.rb +50 -5
  221. data/lib/active_record/validations.rb +8 -4
  222. data/lib/active_record/version.rb +1 -1
  223. data/lib/active_record.rb +143 -16
  224. data/lib/arel/errors.rb +10 -0
  225. data/lib/arel/factory_methods.rb +4 -0
  226. data/lib/arel/filter_predications.rb +1 -1
  227. data/lib/arel/nodes/and.rb +4 -0
  228. data/lib/arel/nodes/binary.rb +6 -1
  229. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  230. data/lib/arel/nodes/cte.rb +36 -0
  231. data/lib/arel/nodes/filter.rb +1 -1
  232. data/lib/arel/nodes/fragments.rb +35 -0
  233. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  234. data/lib/arel/nodes/leading_join.rb +8 -0
  235. data/lib/arel/nodes/node.rb +111 -2
  236. data/lib/arel/nodes/sql_literal.rb +6 -0
  237. data/lib/arel/nodes/table_alias.rb +4 -0
  238. data/lib/arel/nodes.rb +4 -0
  239. data/lib/arel/predications.rb +2 -0
  240. data/lib/arel/table.rb +9 -5
  241. data/lib/arel/visitors/mysql.rb +8 -1
  242. data/lib/arel/visitors/to_sql.rb +81 -17
  243. data/lib/arel/visitors/visitor.rb +2 -2
  244. data/lib/arel.rb +16 -2
  245. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  246. data/lib/rails/generators/active_record/migration.rb +3 -1
  247. data/lib/rails/generators/active_record/model/USAGE +113 -0
  248. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  249. metadata +51 -15
  250. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  251. data/lib/active_record/null_relation.rb +0 -63
@@ -1,29 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/enumerable"
4
- require "active_support/core_ext/hash/indifferent_access"
5
- require "active_support/core_ext/string/filters"
4
+ require "active_support/core_ext/module/delegation"
6
5
  require "active_support/parameter_filter"
7
6
  require "concurrent/map"
8
7
 
9
8
  module ActiveRecord
9
+ # = Active Record \Core
10
10
  module Core
11
11
  extend ActiveSupport::Concern
12
+ include ActiveModel::Access
12
13
 
13
14
  included do
14
15
  ##
15
16
  # :singleton-method:
16
17
  #
17
- # Accepts a logger conforming to the interface of Log4r which is then
18
- # passed on to any new database connections made and which can be
19
- # retrieved on both a class and instance level by calling +logger+.
18
+ # Accepts a logger conforming to the interface of Log4r or the default
19
+ # Ruby +Logger+ class, which is then passed on to any new database
20
+ # connections made. You can retrieve this logger by calling +logger+ on
21
+ # either an Active Record model class or an Active Record model instance.
20
22
  class_attribute :logger, instance_writer: false
21
23
 
24
+ class_attribute :_destroy_association_async_job, instance_accessor: false, default: "ActiveRecord::DestroyAssociationAsyncJob"
25
+
26
+ # The job class used to destroy associations in the background.
27
+ def self.destroy_association_async_job
28
+ if _destroy_association_async_job.is_a?(String)
29
+ self._destroy_association_async_job = _destroy_association_async_job.constantize
30
+ end
31
+ _destroy_association_async_job
32
+ rescue NameError => error
33
+ raise NameError, "Unable to load destroy_association_async_job: #{error.message}"
34
+ end
35
+
36
+ singleton_class.alias_method :destroy_association_async_job=, :_destroy_association_async_job=
37
+ delegate :destroy_association_async_job, to: :class
38
+
22
39
  ##
23
40
  # :singleton-method:
24
41
  #
25
- # Specifies the job used to destroy associations in the background
26
- class_attribute :destroy_association_async_job, instance_writer: false, instance_predicate: false, default: false
42
+ # Specifies the maximum number of records that will be destroyed in a
43
+ # single background job by the <tt>dependent: :destroy_async</tt>
44
+ # association option. When +nil+ (default), all dependent records will be
45
+ # destroyed in a single background job. If specified, the records to be
46
+ # destroyed will be split into multiple background jobs.
47
+ class_attribute :destroy_association_async_batch_size, instance_writer: false, instance_predicate: false, default: nil
27
48
 
28
49
  ##
29
50
  # Contains the database configuration - as is typically stored in config/database.yml -
@@ -33,19 +54,19 @@ module ActiveRecord
33
54
  #
34
55
  # development:
35
56
  # adapter: sqlite3
36
- # database: db/development.sqlite3
57
+ # database: storage/development.sqlite3
37
58
  #
38
59
  # production:
39
60
  # adapter: sqlite3
40
- # database: db/production.sqlite3
61
+ # database: storage/production.sqlite3
41
62
  #
42
63
  # ...would result in ActiveRecord::Base.configurations to look like this:
43
64
  #
44
65
  # #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800 @configurations=[
45
66
  # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
46
- # @name="primary", @config={adapter: "sqlite3", database: "db/development.sqlite3"}>,
67
+ # @name="primary", @config={adapter: "sqlite3", database: "storage/development.sqlite3"}>,
47
68
  # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90 @env_name="production",
48
- # @name="primary", @config={adapter: "sqlite3", database: "db/production.sqlite3"}>
69
+ # @name="primary", @config={adapter: "sqlite3", database: "storage/production.sqlite3"}>
49
70
  # ]>
50
71
  def self.configurations=(config)
51
72
  @@configurations = ActiveRecord::DatabaseConfigurations.new(config)
@@ -60,7 +81,7 @@ module ActiveRecord
60
81
  ##
61
82
  # :singleton-method:
62
83
  # Force enumeration of all columns in SELECT statements.
63
- # e.g. `SELECT first_name, last_name FROM ...` instead of `SELECT * FROM ...`
84
+ # e.g. <tt>SELECT first_name, last_name FROM ...</tt> instead of <tt>SELECT * FROM ...</tt>
64
85
  # This avoids +PreparedStatementCacheExpired+ errors when a column is added
65
86
  # to the database while the app is running.
66
87
  class_attribute :enumerate_columns_in_select_statements, instance_accessor: false, default: false
@@ -71,6 +92,8 @@ module ActiveRecord
71
92
 
72
93
  class_attribute :has_many_inversing, instance_accessor: false, default: false
73
94
 
95
+ class_attribute :run_commit_callbacks_on_first_saved_instances_in_transaction, instance_accessor: false, default: true
96
+
74
97
  class_attribute :default_connection_handler, instance_writer: false
75
98
 
76
99
  class_attribute :default_role, instance_writer: false
@@ -99,33 +122,6 @@ module ActiveRecord
99
122
  ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] = handler
100
123
  end
101
124
 
102
- def self.connection_handlers
103
- if ActiveRecord.legacy_connection_handling
104
- else
105
- raise NotImplementedError, "The new connection handling does not support accessing multiple connection handlers."
106
- end
107
-
108
- @@connection_handlers ||= {}
109
- end
110
-
111
- def self.connection_handlers=(handlers)
112
- if ActiveRecord.legacy_connection_handling
113
- ActiveSupport::Deprecation.warn(<<~MSG)
114
- Using legacy connection handling is deprecated. Please set
115
- `legacy_connection_handling` to `false` in your application.
116
-
117
- The new connection handling does not support `connection_handlers`
118
- getter and setter.
119
-
120
- Read more about how to migrate at: https://guides.rubyonrails.org/active_record_multiple_databases.html#migrate-to-the-new-connection-handling
121
- MSG
122
- else
123
- raise NotImplementedError, "The new connection handling does not support multiple connection handlers."
124
- end
125
-
126
- @@connection_handlers = handlers
127
- end
128
-
129
125
  def self.asynchronous_queries_session # :nodoc:
130
126
  asynchronous_queries_tracker.current_session
131
127
  end
@@ -145,16 +141,12 @@ module ActiveRecord
145
141
  # ActiveRecord::Base.current_role #=> :reading
146
142
  # end
147
143
  def self.current_role
148
- if ActiveRecord.legacy_connection_handling
149
- connection_handlers.key(connection_handler) || default_role
150
- else
151
- connected_to_stack.reverse_each do |hash|
152
- return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
153
- return hash[:role] if hash[:role] && hash[:klasses].include?(connection_class_for_self)
154
- end
155
-
156
- default_role
144
+ connected_to_stack.reverse_each do |hash|
145
+ return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
146
+ return hash[:role] if hash[:role] && hash[:klasses].include?(connection_class_for_self)
157
147
  end
148
+
149
+ default_role
158
150
  end
159
151
 
160
152
  # Returns the symbol representing the current connected shard.
@@ -186,16 +178,12 @@ module ActiveRecord
186
178
  # ActiveRecord::Base.current_preventing_writes #=> false
187
179
  # end
188
180
  def self.current_preventing_writes
189
- if ActiveRecord.legacy_connection_handling
190
- connection_handler.prevent_writes
191
- else
192
- connected_to_stack.reverse_each do |hash|
193
- return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
194
- return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_class_for_self)
195
- end
196
-
197
- false
181
+ connected_to_stack.reverse_each do |hash|
182
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
183
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_class_for_self)
198
184
  end
185
+
186
+ false
199
187
  end
200
188
 
201
189
  def self.connected_to_stack # :nodoc:
@@ -238,7 +226,7 @@ module ActiveRecord
238
226
  def self.strict_loading_violation!(owner:, reflection:) # :nodoc:
239
227
  case ActiveRecord.action_on_strict_loading_violation
240
228
  when :raise
241
- message = "`#{owner}` is marked for strict_loading. The `#{reflection.klass}` association named `:#{reflection.name}` cannot be lazily loaded."
229
+ message = reflection.strict_loading_violation_message(owner)
242
230
  raise ActiveRecord::StrictLoadingViolationError.new(message)
243
231
  when :log
244
232
  name = "strict_loading_violation.active_record"
@@ -252,19 +240,6 @@ module ActiveRecord
252
240
  @find_by_statement_cache = { true => Concurrent::Map.new, false => Concurrent::Map.new }
253
241
  end
254
242
 
255
- def inherited(child_class) # :nodoc:
256
- # initialize cache at class definition for thread safety
257
- child_class.initialize_find_by_cache
258
- unless child_class.base_class?
259
- klass = self
260
- until klass.base_class?
261
- klass.initialize_find_by_cache
262
- klass = klass.superclass
263
- end
264
- end
265
- super
266
- end
267
-
268
243
  def find(*ids) # :nodoc:
269
244
  # We don't have cache keys for this stuff yet
270
245
  return super unless ids.length == 1
@@ -274,14 +249,8 @@ module ActiveRecord
274
249
 
275
250
  return super if StatementCache.unsupported_value?(id)
276
251
 
277
- key = primary_key
278
-
279
- statement = cached_find_by_statement(key) { |params|
280
- where(key => params.bind).limit(1)
281
- }
282
-
283
- statement.execute([id], connection).first ||
284
- raise(RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id))
252
+ cached_find_by([primary_key], [id]) ||
253
+ raise(RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}", name, primary_key, id))
285
254
  end
286
255
 
287
256
  def find_by(*args) # :nodoc:
@@ -313,48 +282,13 @@ module ActiveRecord
313
282
  h[key] = value
314
283
  end
315
284
 
316
- keys = hash.keys
317
- statement = cached_find_by_statement(keys) { |params|
318
- wheres = keys.index_with { params.bind }
319
- where(wheres).limit(1)
320
- }
321
-
322
- begin
323
- statement.execute(hash.values, connection).first
324
- rescue TypeError
325
- raise ActiveRecord::StatementInvalid
326
- end
285
+ cached_find_by(hash.keys, hash.values)
327
286
  end
328
287
 
329
288
  def find_by!(*args) # :nodoc:
330
289
  find_by(*args) || where(*args).raise_record_not_found_exception!
331
290
  end
332
291
 
333
- %w(
334
- reading_role writing_role legacy_connection_handling default_timezone index_nested_attribute_errors
335
- verbose_query_logs queues warn_on_records_fetched_greater_than maintain_test_schema
336
- application_record_class action_on_strict_loading_violation schema_format error_on_ignored_order
337
- timestamped_migrations dump_schema_after_migration dump_schemas suppress_multiple_database_warning
338
- ).each do |attr|
339
- module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
340
- def #{attr}
341
- ActiveSupport::Deprecation.warn(<<~MSG)
342
- ActiveRecord::Base.#{attr} is deprecated and will be removed in Rails 7.1.
343
- Use `ActiveRecord.#{attr}` instead.
344
- MSG
345
- ActiveRecord.#{attr}
346
- end
347
-
348
- def #{attr}=(value)
349
- ActiveSupport::Deprecation.warn(<<~MSG)
350
- ActiveRecord::Base.#{attr}= is deprecated and will be removed in Rails 7.1.
351
- Use `ActiveRecord.#{attr}=` instead.
352
- MSG
353
- ActiveRecord.#{attr} = value
354
- end
355
- RUBY
356
- end
357
-
358
292
  def initialize_generated_modules # :nodoc:
359
293
  generated_association_methods
360
294
  end
@@ -371,10 +305,10 @@ module ActiveRecord
371
305
 
372
306
  # Returns columns which shouldn't be exposed while calling +#inspect+.
373
307
  def filter_attributes
374
- if defined?(@filter_attributes)
375
- @filter_attributes
376
- else
308
+ if @filter_attributes.nil?
377
309
  superclass.filter_attributes
310
+ else
311
+ @filter_attributes
378
312
  end
379
313
  end
380
314
 
@@ -385,13 +319,13 @@ module ActiveRecord
385
319
  end
386
320
 
387
321
  def inspection_filter # :nodoc:
388
- if defined?(@filter_attributes)
322
+ if @filter_attributes.nil?
323
+ superclass.inspection_filter
324
+ else
389
325
  @inspection_filter ||= begin
390
326
  mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
391
327
  ActiveSupport::ParameterFilter.new(@filter_attributes, mask: mask)
392
328
  end
393
- else
394
- superclass.inspection_filter
395
329
  end
396
330
  end
397
331
 
@@ -411,12 +345,7 @@ module ActiveRecord
411
345
  end
412
346
  end
413
347
 
414
- # Overwrite the default class equality method to provide support for decorated models.
415
- def ===(object) # :nodoc:
416
- object.is_a?(self)
417
- end
418
-
419
- # Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
348
+ # Returns an instance of +Arel::Table+ loaded with the current table name.
420
349
  def arel_table # :nodoc:
421
350
  @arel_table ||= Arel::Table.new(table_name, klass: self)
422
351
  end
@@ -435,6 +364,28 @@ module ActiveRecord
435
364
  end
436
365
 
437
366
  private
367
+ def inherited(subclass)
368
+ super
369
+
370
+ # initialize cache at class definition for thread safety
371
+ subclass.initialize_find_by_cache
372
+ unless subclass.base_class?
373
+ klass = self
374
+ until klass.base_class?
375
+ klass.initialize_find_by_cache
376
+ klass = klass.superclass
377
+ end
378
+ end
379
+
380
+ subclass.class_eval do
381
+ @arel_table = nil
382
+ @predicate_builder = nil
383
+ @inspection_filter = nil
384
+ @filter_attributes = nil
385
+ @generated_association_methods = nil
386
+ end
387
+ end
388
+
438
389
  def relation
439
390
  relation = Relation.create(self)
440
391
 
@@ -448,6 +399,19 @@ module ActiveRecord
448
399
  def table_metadata
449
400
  TableMetadata.new(self, arel_table)
450
401
  end
402
+
403
+ def cached_find_by(keys, values)
404
+ statement = cached_find_by_statement(keys) { |params|
405
+ wheres = keys.index_with { params.bind }
406
+ where(wheres).limit(1)
407
+ }
408
+
409
+ begin
410
+ statement.execute(values, connection).first
411
+ rescue TypeError
412
+ raise ActiveRecord::StatementInvalid
413
+ end
414
+ end
451
415
  end
452
416
 
453
417
  # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
@@ -455,7 +419,7 @@ module ActiveRecord
455
419
  # In both instances, valid attribute keys are determined by the column names of the associated table --
456
420
  # hence you can't have attributes that aren't part of the table columns.
457
421
  #
458
- # ==== Example:
422
+ # ==== Example
459
423
  # # Instantiates a single new object
460
424
  # User.new(first_name: 'Jamie')
461
425
  def initialize(attributes = nil)
@@ -533,12 +497,17 @@ module ActiveRecord
533
497
  # only, not its associations. The extent of a "deep" copy is application
534
498
  # specific and is therefore left to the application to implement according
535
499
  # to its need.
536
- # The dup method does not preserve the timestamps (created|updated)_(at|on).
500
+ # The dup method does not preserve the timestamps (created|updated)_(at|on)
501
+ # and locking column.
537
502
 
538
503
  ##
539
504
  def initialize_dup(other) # :nodoc:
540
505
  @attributes = @attributes.deep_dup
541
- @attributes.reset(@primary_key)
506
+ if self.class.composite_primary_key?
507
+ @primary_key.each { |key| @attributes.reset(key) }
508
+ else
509
+ @attributes.reset(@primary_key)
510
+ end
542
511
 
543
512
  _run_initialize_callbacks
544
513
 
@@ -568,6 +537,27 @@ module ActiveRecord
568
537
  coder["active_record_yaml_version"] = 2
569
538
  end
570
539
 
540
+ ##
541
+ # :method: slice
542
+ #
543
+ # :call-seq: slice(*methods)
544
+ #
545
+ # Returns a hash of the given methods with their names as keys and returned
546
+ # values as values.
547
+ #
548
+ #--
549
+ # Implemented by ActiveModel::Access#slice.
550
+
551
+ ##
552
+ # :method: values_at
553
+ #
554
+ # :call-seq: values_at(*methods)
555
+ #
556
+ # Returns an array of the values returned by the given methods.
557
+ #
558
+ #--
559
+ # Implemented by ActiveModel::Access#values_at.
560
+
571
561
  # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
572
562
  # is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.
573
563
  #
@@ -580,7 +570,7 @@ module ActiveRecord
580
570
  def ==(comparison_object)
581
571
  super ||
582
572
  comparison_object.instance_of?(self.class) &&
583
- !id.nil? &&
573
+ primary_key_values_present? &&
584
574
  comparison_object.id == id
585
575
  end
586
576
  alias :eql? :==
@@ -590,7 +580,7 @@ module ActiveRecord
590
580
  def hash
591
581
  id = self.id
592
582
 
593
- if id
583
+ if primary_key_values_present?
594
584
  self.class.hash ^ id.hash
595
585
  else
596
586
  super
@@ -642,25 +632,33 @@ module ActiveRecord
642
632
  #
643
633
  # user = User.first
644
634
  # user.strict_loading! # => true
645
- # user.comments
635
+ # user.address.city
636
+ # => ActiveRecord::StrictLoadingViolationError
637
+ # user.comments.to_a
646
638
  # => ActiveRecord::StrictLoadingViolationError
647
639
  #
648
- # === Parameters:
640
+ # ==== Parameters
649
641
  #
650
- # * value - Boolean specifying whether to enable or disable strict loading.
651
- # * mode - Symbol specifying strict loading mode. Defaults to :all. Using
652
- # :n_plus_one_only mode will only raise an error if an association
653
- # that will lead to an n plus one query is lazily loaded.
642
+ # * +value+ - Boolean specifying whether to enable or disable strict loading.
643
+ # * <tt>:mode</tt> - Symbol specifying strict loading mode. Defaults to :all. Using
644
+ # :n_plus_one_only mode will only raise an error if an association that
645
+ # will lead to an n plus one query is lazily loaded.
654
646
  #
655
- # === Example:
647
+ # ==== Examples
656
648
  #
657
649
  # user = User.first
658
650
  # user.strict_loading!(false) # => false
659
- # user.comments
660
- # => #<ActiveRecord::Associations::CollectionProxy>
651
+ # user.address.city # => "Tatooine"
652
+ # user.comments.to_a # => [#<Comment:0x00...]
653
+ #
654
+ # user.strict_loading!(mode: :n_plus_one_only)
655
+ # user.address.city # => "Tatooine"
656
+ # user.comments.to_a # => [#<Comment:0x00...]
657
+ # user.comments.first.ratings.to_a
658
+ # => ActiveRecord::StrictLoadingViolationError
661
659
  def strict_loading!(value = true, mode: :all)
662
660
  unless [:all, :n_plus_one_only].include?(mode)
663
- raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only]."
661
+ raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only] but #{mode.inspect} was provided."
664
662
  end
665
663
 
666
664
  @strict_loading_mode = mode
@@ -688,7 +686,7 @@ module ActiveRecord
688
686
  # We check defined?(@attributes) not to issue warnings if the object is
689
687
  # allocated but not initialized.
690
688
  inspection = if defined?(@attributes) && @attributes
691
- self.class.attribute_names.filter_map do |name|
689
+ attribute_names.filter_map do |name|
692
690
  if _has_attribute?(name)
693
691
  "#{name}: #{attribute_for_inspect(name)}"
694
692
  end
@@ -725,16 +723,6 @@ module ActiveRecord
725
723
  end
726
724
  end
727
725
 
728
- # Returns a hash of the given methods with their names as keys and returned values as values.
729
- def slice(*methods)
730
- methods.flatten.index_with { |method| public_send(method) }.with_indifferent_access
731
- end
732
-
733
- # Returns an array of the values returned by the given methods.
734
- def values_at(*methods)
735
- methods.flatten.map! { |method| public_send(method) }
736
- end
737
-
738
726
  private
739
727
  # +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
740
728
  # the array, and then rescues from the possible +NoMethodError+. If those elements are
@@ -763,6 +751,7 @@ module ActiveRecord
763
751
  @strict_loading_mode = :all
764
752
 
765
753
  klass.define_attribute_methods
754
+ klass.generate_alias_attributes
766
755
  end
767
756
 
768
757
  def initialize_internals_callback
@@ -5,6 +5,10 @@ module ActiveRecord
5
5
  module CounterCache
6
6
  extend ActiveSupport::Concern
7
7
 
8
+ included do
9
+ class_attribute :_counter_cache_columns, instance_accessor: false, default: []
10
+ end
11
+
8
12
  module ClassMethods
9
13
  # Resets one or more counter caches to their correct value using an SQL
10
14
  # count query. This is useful when adding new counter caches, or if the
@@ -29,6 +33,7 @@ module ActiveRecord
29
33
  def reset_counters(id, *counters, touch: nil)
30
34
  object = find(id)
31
35
 
36
+ updates = {}
32
37
  counters.each do |counter_association|
33
38
  has_many_association = _reflect_on_association(counter_association)
34
39
  unless has_many_association
@@ -47,19 +52,21 @@ module ActiveRecord
47
52
  reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
48
53
  counter_name = reflection.counter_cache_column
49
54
 
50
- updates = { counter_name => object.send(counter_association).count(:all) }
51
-
52
- if touch
53
- names = touch if touch != true
54
- names = Array.wrap(names)
55
- options = names.extract_options!
56
- touch_updates = touch_attributes_with_time(*names, **options)
57
- updates.merge!(touch_updates)
58
- end
55
+ count_was = object.send(counter_name)
56
+ count = object.send(counter_association).count(:all)
57
+ updates[counter_name] = count if count != count_was
58
+ end
59
59
 
60
- unscoped.where(primary_key => object.id).update_all(updates)
60
+ if touch
61
+ names = touch if touch != true
62
+ names = Array.wrap(names)
63
+ options = names.extract_options!
64
+ touch_updates = touch_attributes_with_time(*names, **options)
65
+ updates.merge!(touch_updates)
61
66
  end
62
67
 
68
+ unscoped.where(primary_key => object.id).update_all(updates) if updates.any?
69
+
63
70
  true
64
71
  end
65
72
 
@@ -80,28 +87,28 @@ module ActiveRecord
80
87
  #
81
88
  # ==== Examples
82
89
  #
83
- # # For the Post with id of 5, decrement the comment_count by 1, and
84
- # # increment the action_count by 1
85
- # Post.update_counters 5, comment_count: -1, action_count: 1
90
+ # # For the Post with id of 5, decrement the comments_count by 1, and
91
+ # # increment the actions_count by 1
92
+ # Post.update_counters 5, comments_count: -1, actions_count: 1
86
93
  # # Executes the following SQL:
87
94
  # # UPDATE posts
88
- # # SET comment_count = COALESCE(comment_count, 0) - 1,
89
- # # action_count = COALESCE(action_count, 0) + 1
95
+ # # SET comments_count = COALESCE(comments_count, 0) - 1,
96
+ # # actions_count = COALESCE(actions_count, 0) + 1
90
97
  # # WHERE id = 5
91
98
  #
92
- # # For the Posts with id of 10 and 15, increment the comment_count by 1
93
- # Post.update_counters [10, 15], comment_count: 1
99
+ # # For the Posts with id of 10 and 15, increment the comments_count by 1
100
+ # Post.update_counters [10, 15], comments_count: 1
94
101
  # # Executes the following SQL:
95
102
  # # UPDATE posts
96
- # # SET comment_count = COALESCE(comment_count, 0) + 1
103
+ # # SET comments_count = COALESCE(comments_count, 0) + 1
97
104
  # # WHERE id IN (10, 15)
98
105
  #
99
- # # For the Posts with id of 10 and 15, increment the comment_count by 1
106
+ # # For the Posts with id of 10 and 15, increment the comments_count by 1
100
107
  # # and update the updated_at value for each counter.
101
- # Post.update_counters [10, 15], comment_count: 1, touch: true
108
+ # Post.update_counters [10, 15], comments_count: 1, touch: true
102
109
  # # Executes the following SQL:
103
110
  # # UPDATE posts
104
- # # SET comment_count = COALESCE(comment_count, 0) + 1,
111
+ # # SET comments_count = COALESCE(comments_count, 0) + 1,
105
112
  # # `updated_at` = '2016-10-13T09:59:23-05:00'
106
113
  # # WHERE id IN (10, 15)
107
114
  def update_counters(id, counters)
@@ -119,6 +126,7 @@ module ActiveRecord
119
126
  #
120
127
  # * +counter_name+ - The name of the field that should be incremented.
121
128
  # * +id+ - The id of the object that should be incremented or an array of ids.
129
+ # * <tt>:by</tt> - The amount by which to increment the value. Defaults to +1+.
122
130
  # * <tt>:touch</tt> - Touch timestamp columns when updating.
123
131
  # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
124
132
  # touch that column or an array of symbols to touch just those ones.
@@ -129,10 +137,14 @@ module ActiveRecord
129
137
  # DiscussionBoard.increment_counter(:posts_count, 5)
130
138
  #
131
139
  # # Increment the posts_count column for the record with an id of 5
140
+ # # by a specific amount.
141
+ # DiscussionBoard.increment_counter(:posts_count, 5, by: 3)
142
+ #
143
+ # # Increment the posts_count column for the record with an id of 5
132
144
  # # and update the updated_at value.
133
145
  # DiscussionBoard.increment_counter(:posts_count, 5, touch: true)
134
- def increment_counter(counter_name, id, touch: nil)
135
- update_counters(id, counter_name => 1, touch: touch)
146
+ def increment_counter(counter_name, id, by: 1, touch: nil)
147
+ update_counters(id, counter_name => by, touch: touch)
136
148
  end
137
149
 
138
150
  # Decrement a numeric field by one, via a direct SQL update.
@@ -144,6 +156,7 @@ module ActiveRecord
144
156
  #
145
157
  # * +counter_name+ - The name of the field that should be decremented.
146
158
  # * +id+ - The id of the object that should be decremented or an array of ids.
159
+ # * <tt>:by</tt> - The amount by which to decrement the value. Defaults to +1+.
147
160
  # * <tt>:touch</tt> - Touch timestamp columns when updating.
148
161
  # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
149
162
  # touch that column or an array of symbols to touch just those ones.
@@ -154,10 +167,18 @@ module ActiveRecord
154
167
  # DiscussionBoard.decrement_counter(:posts_count, 5)
155
168
  #
156
169
  # # Decrement the posts_count column for the record with an id of 5
170
+ # by a specific amount.
171
+ # DiscussionBoard.decrement_counter(:posts_count, 5, by: 3)
172
+ #
173
+ # # Decrement the posts_count column for the record with an id of 5
157
174
  # # and update the updated_at value.
158
175
  # DiscussionBoard.decrement_counter(:posts_count, 5, touch: true)
159
- def decrement_counter(counter_name, id, touch: nil)
160
- update_counters(id, counter_name => -1, touch: touch)
176
+ def decrement_counter(counter_name, id, by: 1, touch: nil)
177
+ update_counters(id, counter_name => -by, touch: touch)
178
+ end
179
+
180
+ def counter_cache_column?(name) # :nodoc:
181
+ _counter_cache_columns.include?(name)
161
182
  end
162
183
  end
163
184
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "uri"
4
4
  require "active_support/core_ext/enumerable"
5
+ require "active_support/core_ext/hash/reverse_merge"
5
6
 
6
7
  module ActiveRecord
7
8
  class DatabaseConfigurations