activerecord 7.1.5.2 → 8.1.2.1

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 (244) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +567 -2443
  3. data/README.rdoc +16 -16
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +3 -2
  6. data/lib/active_record/associations/alias_tracker.rb +31 -23
  7. data/lib/active_record/associations/association.rb +43 -12
  8. data/lib/active_record/associations/belongs_to_association.rb +31 -8
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/association.rb +23 -11
  11. data/lib/active_record/associations/builder/belongs_to.rb +18 -4
  12. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  14. data/lib/active_record/associations/builder/has_many.rb +3 -4
  15. data/lib/active_record/associations/builder/has_one.rb +3 -4
  16. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  17. data/lib/active_record/associations/collection_association.rb +17 -9
  18. data/lib/active_record/associations/collection_proxy.rb +36 -5
  19. data/lib/active_record/associations/deprecation.rb +88 -0
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  21. data/lib/active_record/associations/errors.rb +268 -0
  22. data/lib/active_record/associations/has_many_association.rb +1 -1
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -3
  24. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +4 -2
  26. data/lib/active_record/associations/nested_error.rb +47 -0
  27. data/lib/active_record/associations/preloader/association.rb +4 -3
  28. data/lib/active_record/associations/preloader/batch.rb +7 -1
  29. data/lib/active_record/associations/preloader/branch.rb +8 -1
  30. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  31. data/lib/active_record/associations/singular_association.rb +14 -3
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +250 -315
  34. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  35. data/lib/active_record/attribute_assignment.rb +0 -2
  36. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  37. data/lib/active_record/attribute_methods/primary_key.rb +25 -61
  38. data/lib/active_record/attribute_methods/query.rb +34 -0
  39. data/lib/active_record/attribute_methods/read.rb +1 -13
  40. data/lib/active_record/attribute_methods/serialization.rb +20 -27
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
  42. data/lib/active_record/attribute_methods.rb +71 -75
  43. data/lib/active_record/attributes.rb +96 -68
  44. data/lib/active_record/autosave_association.rb +93 -58
  45. data/lib/active_record/base.rb +5 -7
  46. data/lib/active_record/callbacks.rb +1 -1
  47. data/lib/active_record/coders/json.rb +14 -5
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
  62. data/lib/active_record/connection_adapters/column.rb +17 -4
  63. data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
  64. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
  65. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
  68. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
  70. data/lib/active_record/connection_adapters/pool_config.rb +14 -13
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
  74. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  75. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  80. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
  81. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
  82. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
  83. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
  85. data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
  86. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  87. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
  88. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
  89. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
  90. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  91. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
  92. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
  94. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  95. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
  96. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
  97. data/lib/active_record/connection_adapters.rb +66 -0
  98. data/lib/active_record/connection_handling.rb +88 -46
  99. data/lib/active_record/core.rb +142 -58
  100. data/lib/active_record/counter_cache.rb +52 -18
  101. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
  102. data/lib/active_record/database_configurations/database_config.rb +28 -5
  103. data/lib/active_record/database_configurations/hash_config.rb +103 -41
  104. data/lib/active_record/database_configurations/url_config.rb +30 -1
  105. data/lib/active_record/database_configurations.rb +8 -4
  106. data/lib/active_record/delegated_type.rb +43 -19
  107. data/lib/active_record/dynamic_matchers.rb +56 -71
  108. data/lib/active_record/encryption/config.rb +3 -1
  109. data/lib/active_record/encryption/encryptable_record.rb +12 -12
  110. data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
  111. data/lib/active_record/encryption/encryptor.rb +58 -22
  112. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  113. data/lib/active_record/encryption/key_provider.rb +1 -1
  114. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  115. data/lib/active_record/encryption/message_serializer.rb +4 -0
  116. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  117. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  118. data/lib/active_record/encryption/scheme.rb +9 -2
  119. data/lib/active_record/enum.rb +57 -36
  120. data/lib/active_record/errors.rb +76 -26
  121. data/lib/active_record/explain.rb +14 -25
  122. data/lib/active_record/explain_registry.rb +51 -2
  123. data/lib/active_record/filter_attribute_handler.rb +73 -0
  124. data/lib/active_record/fixture_set/table_row.rb +19 -2
  125. data/lib/active_record/fixtures.rb +39 -35
  126. data/lib/active_record/future_result.rb +21 -13
  127. data/lib/active_record/gem_version.rb +3 -3
  128. data/lib/active_record/inheritance.rb +5 -3
  129. data/lib/active_record/insert_all.rb +28 -20
  130. data/lib/active_record/integration.rb +4 -1
  131. data/lib/active_record/internal_metadata.rb +48 -34
  132. data/lib/active_record/locking/optimistic.rb +15 -7
  133. data/lib/active_record/locking/pessimistic.rb +5 -0
  134. data/lib/active_record/log_subscriber.rb +3 -34
  135. data/lib/active_record/message_pack.rb +1 -1
  136. data/lib/active_record/middleware/shard_selector.rb +34 -17
  137. data/lib/active_record/migration/command_recorder.rb +46 -14
  138. data/lib/active_record/migration/compatibility.rb +40 -25
  139. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  140. data/lib/active_record/migration/default_strategy.rb +4 -5
  141. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  142. data/lib/active_record/migration.rb +119 -103
  143. data/lib/active_record/model_schema.rb +66 -78
  144. data/lib/active_record/nested_attributes.rb +17 -9
  145. data/lib/active_record/persistence.rb +161 -454
  146. data/lib/active_record/query_cache.rb +33 -15
  147. data/lib/active_record/query_logs.rb +106 -39
  148. data/lib/active_record/query_logs_formatter.rb +17 -28
  149. data/lib/active_record/querying.rb +28 -16
  150. data/lib/active_record/railtie.rb +61 -74
  151. data/lib/active_record/railties/controller_runtime.rb +19 -5
  152. data/lib/active_record/railties/databases.rake +64 -78
  153. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  154. data/lib/active_record/railties/job_runtime.rb +10 -11
  155. data/lib/active_record/reflection.rb +147 -53
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  157. data/lib/active_record/relation/batches.rb +153 -73
  158. data/lib/active_record/relation/calculations.rb +146 -96
  159. data/lib/active_record/relation/delegation.rb +30 -23
  160. data/lib/active_record/relation/finder_methods.rb +64 -34
  161. data/lib/active_record/relation/merger.rb +12 -14
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  163. data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
  165. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  166. data/lib/active_record/relation/predicate_builder.rb +25 -10
  167. data/lib/active_record/relation/query_attribute.rb +4 -2
  168. data/lib/active_record/relation/query_methods.rb +351 -124
  169. data/lib/active_record/relation/spawn_methods.rb +9 -25
  170. data/lib/active_record/relation/where_clause.rb +17 -30
  171. data/lib/active_record/relation.rb +590 -124
  172. data/lib/active_record/result.rb +131 -59
  173. data/lib/active_record/runtime_registry.rb +44 -21
  174. data/lib/active_record/sanitization.rb +33 -25
  175. data/lib/active_record/schema.rb +8 -6
  176. data/lib/active_record/schema_dumper.rb +66 -31
  177. data/lib/active_record/schema_migration.rb +31 -14
  178. data/lib/active_record/scoping/named.rb +6 -2
  179. data/lib/active_record/scoping.rb +0 -1
  180. data/lib/active_record/secure_token.rb +3 -3
  181. data/lib/active_record/signed_id.rb +68 -20
  182. data/lib/active_record/statement_cache.rb +29 -25
  183. data/lib/active_record/store.rb +51 -22
  184. data/lib/active_record/structured_event_subscriber.rb +85 -0
  185. data/lib/active_record/table_metadata.rb +7 -33
  186. data/lib/active_record/tasks/abstract_tasks.rb +76 -0
  187. data/lib/active_record/tasks/database_tasks.rb +118 -90
  188. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
  189. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
  190. data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
  191. data/lib/active_record/test_databases.rb +14 -4
  192. data/lib/active_record/test_fixtures.rb +123 -89
  193. data/lib/active_record/testing/query_assertions.rb +127 -0
  194. data/lib/active_record/timestamp.rb +6 -4
  195. data/lib/active_record/token_for.rb +22 -12
  196. data/lib/active_record/touch_later.rb +1 -1
  197. data/lib/active_record/transaction.rb +129 -0
  198. data/lib/active_record/transactions.rb +97 -18
  199. data/lib/active_record/translation.rb +0 -2
  200. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  201. data/lib/active_record/type/internal/timezone.rb +7 -0
  202. data/lib/active_record/type/json.rb +15 -2
  203. data/lib/active_record/type/serialized.rb +12 -7
  204. data/lib/active_record/type/type_map.rb +1 -1
  205. data/lib/active_record/type_caster/connection.rb +5 -4
  206. data/lib/active_record/validations/associated.rb +10 -4
  207. data/lib/active_record/validations/uniqueness.rb +23 -18
  208. data/lib/active_record/validations.rb +4 -1
  209. data/lib/active_record.rb +203 -59
  210. data/lib/arel/alias_predication.rb +3 -1
  211. data/lib/arel/collectors/bind.rb +4 -2
  212. data/lib/arel/collectors/composite.rb +7 -0
  213. data/lib/arel/collectors/sql_string.rb +2 -2
  214. data/lib/arel/collectors/substitute_binds.rb +3 -3
  215. data/lib/arel/crud.rb +8 -11
  216. data/lib/arel/delete_manager.rb +5 -0
  217. data/lib/arel/nodes/binary.rb +1 -7
  218. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  219. data/lib/arel/nodes/count.rb +2 -2
  220. data/lib/arel/nodes/delete_statement.rb +4 -2
  221. data/lib/arel/nodes/function.rb +4 -10
  222. data/lib/arel/nodes/named_function.rb +2 -2
  223. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  224. data/lib/arel/nodes/node.rb +6 -5
  225. data/lib/arel/nodes/sql_literal.rb +8 -1
  226. data/lib/arel/nodes/update_statement.rb +4 -2
  227. data/lib/arel/nodes.rb +2 -4
  228. data/lib/arel/predications.rb +1 -1
  229. data/lib/arel/select_manager.rb +14 -5
  230. data/lib/arel/table.rb +3 -7
  231. data/lib/arel/tree_manager.rb +3 -2
  232. data/lib/arel/update_manager.rb +7 -1
  233. data/lib/arel/visitors/dot.rb +3 -3
  234. data/lib/arel/visitors/mysql.rb +9 -4
  235. data/lib/arel/visitors/postgresql.rb +56 -12
  236. data/lib/arel/visitors/sqlite.rb +80 -8
  237. data/lib/arel/visitors/to_sql.rb +34 -37
  238. data/lib/arel.rb +10 -4
  239. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  240. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  241. metadata +23 -14
  242. data/lib/active_record/explain_subscriber.rb +0 -34
  243. data/lib/active_record/normalization.rb +0 -167
  244. data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -25,8 +25,8 @@ module ActiveRecord
25
25
  # You can set custom coder to encode/decode your serialized attributes to/from different formats.
26
26
  # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
27
27
  #
28
- # NOTE: If you are using structured database data types (e.g. PostgreSQL +hstore+/+json+, or MySQL 5.7+
29
- # +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
28
+ # NOTE: If you are using structured database data types (e.g. PostgreSQL +hstore+/+json+, MySQL 5.7+
29
+ # +json+, or SQLite 3.38+ +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
30
30
  # Simply use {.store_accessor}[rdoc-ref:ClassMethods#store_accessor] instead to generate
31
31
  # the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
32
32
  # using a symbol.
@@ -146,37 +146,43 @@ module ActiveRecord
146
146
  define_method("#{accessor_key}_changed?") do
147
147
  return false unless attribute_changed?(store_attribute)
148
148
  prev_store, new_store = changes[store_attribute]
149
- prev_store&.dig(key) != new_store&.dig(key)
149
+ accessor = store_accessor_for(store_attribute)
150
+ accessor.get(prev_store, key) != accessor.get(new_store, key)
150
151
  end
151
152
 
152
153
  define_method("#{accessor_key}_change") do
153
154
  return unless attribute_changed?(store_attribute)
154
155
  prev_store, new_store = changes[store_attribute]
155
- [prev_store&.dig(key), new_store&.dig(key)]
156
+ accessor = store_accessor_for(store_attribute)
157
+ [accessor.get(prev_store, key), accessor.get(new_store, key)]
156
158
  end
157
159
 
158
160
  define_method("#{accessor_key}_was") do
159
161
  return unless attribute_changed?(store_attribute)
160
162
  prev_store, _new_store = changes[store_attribute]
161
- prev_store&.dig(key)
163
+ accessor = store_accessor_for(store_attribute)
164
+ accessor.get(prev_store, key)
162
165
  end
163
166
 
164
167
  define_method("saved_change_to_#{accessor_key}?") do
165
168
  return false unless saved_change_to_attribute?(store_attribute)
166
169
  prev_store, new_store = saved_changes[store_attribute]
167
- prev_store&.dig(key) != new_store&.dig(key)
170
+ accessor = store_accessor_for(store_attribute)
171
+ accessor.get(prev_store, key) != accessor.get(new_store, key)
168
172
  end
169
173
 
170
174
  define_method("saved_change_to_#{accessor_key}") do
171
175
  return unless saved_change_to_attribute?(store_attribute)
172
176
  prev_store, new_store = saved_changes[store_attribute]
173
- [prev_store&.dig(key), new_store&.dig(key)]
177
+ accessor = store_accessor_for(store_attribute)
178
+ [accessor.get(prev_store, key), accessor.get(new_store, key)]
174
179
  end
175
180
 
176
181
  define_method("#{accessor_key}_before_last_save") do
177
182
  return unless saved_change_to_attribute?(store_attribute)
178
183
  prev_store, _new_store = saved_changes[store_attribute]
179
- prev_store&.dig(key)
184
+ accessor = store_accessor_for(store_attribute)
185
+ accessor.get(prev_store, key)
180
186
  end
181
187
  end
182
188
  end
@@ -217,43 +223,66 @@ module ActiveRecord
217
223
  end
218
224
 
219
225
  def store_accessor_for(store_attribute)
220
- type_for_attribute(store_attribute).accessor
226
+ type_for_attribute(store_attribute).tap do |type|
227
+ unless type.respond_to?(:accessor)
228
+ raise ConfigurationError, "the column '#{store_attribute}' has not been configured as a store. Please make sure the column is declared serializable via 'ActiveRecord.store' or, if your database supports it, use a structured column type like hstore or json."
229
+ end
230
+ end.accessor
221
231
  end
222
232
 
223
233
  class HashAccessor # :nodoc:
234
+ def self.get(store_object, key)
235
+ if store_object
236
+ store_object[key]
237
+ end
238
+ end
239
+
224
240
  def self.read(object, attribute, key)
225
- prepare(object, attribute)
226
- object.public_send(attribute)[key]
241
+ store_object = prepare(object, attribute)
242
+ store_object[key]
227
243
  end
228
244
 
229
245
  def self.write(object, attribute, key, value)
230
- prepare(object, attribute)
231
- object.public_send(attribute)[key] = value if value != read(object, attribute, key)
246
+ store_object = prepare(object, attribute)
247
+ store_object[key] = value if value != store_object[key]
232
248
  end
233
249
 
234
250
  def self.prepare(object, attribute)
235
- object.public_send :"#{attribute}=", {} unless object.send(attribute)
251
+ store_object = object.public_send(attribute)
252
+
253
+ if store_object.nil?
254
+ store_object = {}
255
+ object.public_send(:"#{attribute}=", store_object)
256
+ end
257
+
258
+ store_object
236
259
  end
237
260
  end
238
261
 
239
262
  class StringKeyedHashAccessor < HashAccessor # :nodoc:
263
+ def self.get(store_object, key)
264
+ super store_object, Symbol === key ? key.name : key.to_s
265
+ end
266
+
240
267
  def self.read(object, attribute, key)
241
- super object, attribute, key.to_s
268
+ super object, attribute, Symbol === key ? key.name : key.to_s
242
269
  end
243
270
 
244
271
  def self.write(object, attribute, key, value)
245
- super object, attribute, key.to_s, value
272
+ super object, attribute, Symbol === key ? key.name : key.to_s, value
246
273
  end
247
274
  end
248
275
 
249
276
  class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor # :nodoc:
250
- def self.prepare(object, store_attribute)
251
- attribute = object.send(store_attribute)
252
- unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
253
- attribute = IndifferentCoder.as_indifferent_hash(attribute)
254
- object.public_send :"#{store_attribute}=", attribute
277
+ def self.prepare(object, attribute)
278
+ store_object = object.public_send(attribute)
279
+
280
+ unless store_object.is_a?(ActiveSupport::HashWithIndifferentAccess)
281
+ store_object = IndifferentCoder.as_indifferent_hash(store_object)
282
+ object.public_send :"#{attribute}=", store_object
255
283
  end
256
- attribute
284
+
285
+ store_object
257
286
  end
258
287
  end
259
288
 
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/structured_event_subscriber"
4
+
5
+ module ActiveRecord
6
+ class StructuredEventSubscriber < ActiveSupport::StructuredEventSubscriber # :nodoc:
7
+ IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
8
+
9
+ def strict_loading_violation(event)
10
+ owner = event.payload[:owner]
11
+ reflection = event.payload[:reflection]
12
+
13
+ emit_debug_event("active_record.strict_loading_violation",
14
+ owner: owner.name,
15
+ class: reflection.polymorphic? ? nil : reflection.klass.name,
16
+ name: reflection.name,
17
+ )
18
+ end
19
+ debug_only :strict_loading_violation
20
+
21
+ def sql(event)
22
+ payload = event.payload
23
+
24
+ return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
25
+
26
+ binds = nil
27
+
28
+ if payload[:binds]&.any?
29
+ casted_params = type_casted_binds(payload[:type_casted_binds])
30
+
31
+ binds = []
32
+ payload[:binds].each_with_index do |attr, i|
33
+ attribute_name = if attr.respond_to?(:name)
34
+ attr.name
35
+ elsif attr.respond_to?(:[]) && attr[i].respond_to?(:name)
36
+ attr[i].name
37
+ else
38
+ nil
39
+ end
40
+
41
+ filtered_params = filter(attribute_name, casted_params[i])
42
+
43
+ binds << render_bind(attr, filtered_params)
44
+ end
45
+ end
46
+
47
+ emit_debug_event("active_record.sql",
48
+ async: payload[:async],
49
+ name: payload[:name],
50
+ sql: payload[:sql],
51
+ cached: payload[:cached],
52
+ lock_wait: payload[:lock_wait],
53
+ binds: binds,
54
+ duration_ms: event.duration.round(2),
55
+ )
56
+ end
57
+ debug_only :sql
58
+
59
+ private
60
+ def type_casted_binds(casted_binds)
61
+ casted_binds.respond_to?(:call) ? casted_binds.call : casted_binds
62
+ end
63
+
64
+ def render_bind(attr, value)
65
+ case attr
66
+ when ActiveModel::Attribute
67
+ if attr.type.binary? && attr.value
68
+ value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
69
+ end
70
+ when Array
71
+ attr = attr.first
72
+ else
73
+ attr = nil
74
+ end
75
+
76
+ [attr&.name, value]
77
+ end
78
+
79
+ def filter(name, value)
80
+ ActiveRecord::Base.inspection_filter.filter_param(name, value)
81
+ end
82
+ end
83
+ end
84
+
85
+ ActiveRecord::StructuredEventSubscriber.attach_to :active_record
@@ -2,12 +2,9 @@
2
2
 
3
3
  module ActiveRecord
4
4
  class TableMetadata # :nodoc:
5
- delegate :join_primary_key, :join_primary_type, :join_foreign_key, :join_foreign_type, to: :reflection
6
-
7
- def initialize(klass, arel_table, reflection = nil)
5
+ def initialize(klass, arel_table)
8
6
  @klass = klass
9
7
  @arel_table = arel_table
10
- @reflection = reflection
11
8
  end
12
9
 
13
10
  def primary_key
@@ -22,17 +19,8 @@ module ActiveRecord
22
19
  klass&.columns_hash&.key?(column_name)
23
20
  end
24
21
 
25
- def associated_with?(table_name)
26
- if reflection = klass&._reflect_on_association(table_name)
27
- reflection
28
- elsif ActiveRecord.allow_deprecated_singular_associations_name && reflection = klass&._reflect_on_association(table_name.singularize)
29
- ActiveRecord.deprecator.warn(<<~MSG)
30
- Referring to a singular association (e.g. `#{reflection.name}`) by its plural name (e.g. `#{reflection.plural_name}`) is deprecated.
31
-
32
- To convert this deprecation warning to an error and enable more performant behavior, set config.active_record.allow_deprecated_singular_associations_name = false.
33
- MSG
34
- reflection
35
- end
22
+ def associated_with(table_name)
23
+ klass&._reflect_on_association(table_name)
36
24
  end
37
25
 
38
26
  def associated_table(table_name)
@@ -51,26 +39,14 @@ module ActiveRecord
51
39
  if association_klass
52
40
  arel_table = association_klass.arel_table
53
41
  arel_table = arel_table.alias(table_name) if arel_table.name != table_name
54
- TableMetadata.new(association_klass, arel_table, reflection)
42
+ TableMetadata.new(association_klass, arel_table)
55
43
  else
56
44
  type_caster = TypeCaster::Connection.new(klass, table_name)
57
45
  arel_table = Arel::Table.new(table_name, type_caster: type_caster)
58
- TableMetadata.new(nil, arel_table, reflection)
46
+ TableMetadata.new(nil, arel_table)
59
47
  end
60
48
  end
61
49
 
62
- def polymorphic_association?
63
- reflection&.polymorphic?
64
- end
65
-
66
- def polymorphic_name_association
67
- reflection&.polymorphic_name
68
- end
69
-
70
- def through_association?
71
- reflection&.through_reflection?
72
- end
73
-
74
50
  def reflect_on_aggregation(aggregation_name)
75
51
  klass&.reflect_on_aggregation(aggregation_name)
76
52
  end
@@ -78,9 +54,7 @@ module ActiveRecord
78
54
 
79
55
  def predicate_builder
80
56
  if klass
81
- predicate_builder = klass.predicate_builder.dup
82
- predicate_builder.instance_variable_set(:@table, self)
83
- predicate_builder
57
+ klass.predicate_builder.with(self)
84
58
  else
85
59
  PredicateBuilder.new(self)
86
60
  end
@@ -89,6 +63,6 @@ module ActiveRecord
89
63
  attr_reader :arel_table
90
64
 
91
65
  private
92
- attr_reader :klass, :reflection
66
+ attr_reader :klass
93
67
  end
94
68
  end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Tasks # :nodoc:
5
+ class AbstractTasks # :nodoc:
6
+ def self.using_database_configurations?
7
+ true
8
+ end
9
+
10
+ def initialize(db_config)
11
+ @db_config = db_config
12
+ @configuration_hash = db_config.configuration_hash
13
+ end
14
+
15
+ def charset
16
+ connection.encoding
17
+ end
18
+
19
+ def collation
20
+ connection.collation
21
+ end
22
+
23
+ def check_current_protected_environment!(db_config, migration_class)
24
+ with_temporary_pool(db_config, migration_class) do |pool|
25
+ migration_context = pool.migration_context
26
+ current = migration_context.current_environment
27
+ stored = migration_context.last_stored_environment
28
+
29
+ if migration_context.protected_environment?
30
+ raise ActiveRecord::ProtectedEnvironmentError.new(stored)
31
+ end
32
+
33
+ if stored && stored != current
34
+ raise ActiveRecord::EnvironmentMismatchError.new(current: current, stored: stored)
35
+ end
36
+ rescue ActiveRecord::NoDatabaseError
37
+ end
38
+ end
39
+
40
+ private
41
+ attr_reader :db_config, :configuration_hash
42
+
43
+ def connection
44
+ ActiveRecord::Base.lease_connection
45
+ end
46
+
47
+ def establish_connection(config = db_config)
48
+ ActiveRecord::Base.establish_connection(config)
49
+ end
50
+
51
+ def configuration_hash_without_database
52
+ configuration_hash.merge(database: nil)
53
+ end
54
+
55
+ def run_cmd(cmd, *args, **opts)
56
+ fail run_cmd_error(cmd, args) unless Kernel.system(cmd, *args, opts)
57
+ end
58
+
59
+ def run_cmd_error(cmd, args)
60
+ msg = +"failed to execute:\n"
61
+ msg << "#{cmd} #{args.join(' ')}\n\n"
62
+ msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
63
+ msg
64
+ end
65
+
66
+ def with_temporary_pool(db_config, migration_class, clobber: false)
67
+ original_db_config = migration_class.connection_db_config
68
+ pool = migration_class.connection_handler.establish_connection(db_config, clobber: clobber)
69
+
70
+ yield pool
71
+ ensure
72
+ migration_class.connection_handler.establish_connection(original_db_config, clobber: clobber)
73
+ end
74
+ end
75
+ end
76
+ end