activerecord 7.1.5.1 → 7.2.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +515 -2445
  3. data/README.rdoc +15 -15
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +1 -1
  6. data/lib/active_record/associations/alias_tracker.rb +25 -19
  7. data/lib/active_record/associations/association.rb +9 -8
  8. data/lib/active_record/associations/belongs_to_association.rb +14 -7
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +3 -4
  13. data/lib/active_record/associations/builder/has_one.rb +3 -4
  14. data/lib/active_record/associations/collection_association.rb +6 -4
  15. data/lib/active_record/associations/collection_proxy.rb +14 -1
  16. data/lib/active_record/associations/has_many_association.rb +1 -1
  17. data/lib/active_record/associations/join_dependency/join_association.rb +29 -28
  18. data/lib/active_record/associations/join_dependency.rb +5 -5
  19. data/lib/active_record/associations/nested_error.rb +47 -0
  20. data/lib/active_record/associations/preloader/association.rb +2 -1
  21. data/lib/active_record/associations/preloader/branch.rb +7 -1
  22. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  23. data/lib/active_record/associations/singular_association.rb +6 -0
  24. data/lib/active_record/associations/through_association.rb +1 -1
  25. data/lib/active_record/associations.rb +33 -16
  26. data/lib/active_record/attribute_assignment.rb +1 -11
  27. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +1 -1
  29. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  30. data/lib/active_record/attribute_methods/read.rb +4 -16
  31. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  32. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -10
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +60 -71
  35. data/lib/active_record/attributes.rb +55 -42
  36. data/lib/active_record/autosave_association.rb +13 -32
  37. data/lib/active_record/base.rb +2 -3
  38. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
  39. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
  40. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -65
  41. data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
  42. data/lib/active_record/connection_adapters/abstract/query_cache.rb +159 -74
  43. data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
  44. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  45. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +14 -5
  46. data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
  47. data/lib/active_record/connection_adapters/abstract_adapter.rb +18 -46
  48. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +32 -6
  49. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  50. data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
  51. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -1
  52. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
  53. data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -23
  54. data/lib/active_record/connection_adapters/pool_config.rb +7 -6
  55. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
  56. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  57. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  58. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  59. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  60. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
  61. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -13
  62. data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
  63. data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
  64. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  65. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
  66. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
  67. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  68. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  69. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  70. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
  71. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +107 -75
  72. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
  73. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
  74. data/lib/active_record/connection_adapters.rb +121 -0
  75. data/lib/active_record/connection_handling.rb +56 -41
  76. data/lib/active_record/core.rb +53 -37
  77. data/lib/active_record/counter_cache.rb +18 -9
  78. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  79. data/lib/active_record/database_configurations/database_config.rb +15 -4
  80. data/lib/active_record/database_configurations/hash_config.rb +38 -34
  81. data/lib/active_record/database_configurations/url_config.rb +20 -1
  82. data/lib/active_record/database_configurations.rb +1 -1
  83. data/lib/active_record/delegated_type.rb +24 -0
  84. data/lib/active_record/dynamic_matchers.rb +2 -2
  85. data/lib/active_record/encryption/encryptable_record.rb +2 -2
  86. data/lib/active_record/encryption/encrypted_attribute_type.rb +22 -2
  87. data/lib/active_record/encryption/encryptor.rb +17 -2
  88. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  89. data/lib/active_record/encryption/message_serializer.rb +4 -0
  90. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  91. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  92. data/lib/active_record/encryption.rb +0 -2
  93. data/lib/active_record/enum.rb +10 -1
  94. data/lib/active_record/errors.rb +16 -11
  95. data/lib/active_record/explain.rb +13 -24
  96. data/lib/active_record/fixtures.rb +37 -31
  97. data/lib/active_record/future_result.rb +8 -4
  98. data/lib/active_record/gem_version.rb +3 -3
  99. data/lib/active_record/inheritance.rb +4 -2
  100. data/lib/active_record/insert_all.rb +18 -15
  101. data/lib/active_record/integration.rb +4 -1
  102. data/lib/active_record/internal_metadata.rb +48 -34
  103. data/lib/active_record/locking/optimistic.rb +7 -6
  104. data/lib/active_record/log_subscriber.rb +0 -21
  105. data/lib/active_record/marshalling.rb +1 -4
  106. data/lib/active_record/message_pack.rb +1 -1
  107. data/lib/active_record/migration/command_recorder.rb +2 -3
  108. data/lib/active_record/migration/compatibility.rb +5 -3
  109. data/lib/active_record/migration/default_strategy.rb +4 -5
  110. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  111. data/lib/active_record/migration.rb +85 -76
  112. data/lib/active_record/model_schema.rb +28 -68
  113. data/lib/active_record/nested_attributes.rb +13 -16
  114. data/lib/active_record/normalization.rb +3 -7
  115. data/lib/active_record/persistence.rb +30 -352
  116. data/lib/active_record/query_cache.rb +18 -6
  117. data/lib/active_record/query_logs.rb +15 -0
  118. data/lib/active_record/querying.rb +21 -9
  119. data/lib/active_record/railtie.rb +50 -62
  120. data/lib/active_record/railties/controller_runtime.rb +13 -4
  121. data/lib/active_record/railties/databases.rake +41 -44
  122. data/lib/active_record/reflection.rb +90 -35
  123. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  124. data/lib/active_record/relation/batches.rb +3 -3
  125. data/lib/active_record/relation/calculations.rb +94 -61
  126. data/lib/active_record/relation/delegation.rb +8 -11
  127. data/lib/active_record/relation/finder_methods.rb +16 -2
  128. data/lib/active_record/relation/merger.rb +4 -6
  129. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  130. data/lib/active_record/relation/predicate_builder.rb +3 -3
  131. data/lib/active_record/relation/query_methods.rb +196 -57
  132. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  133. data/lib/active_record/relation/spawn_methods.rb +2 -18
  134. data/lib/active_record/relation/where_clause.rb +7 -19
  135. data/lib/active_record/relation.rb +496 -72
  136. data/lib/active_record/result.rb +31 -44
  137. data/lib/active_record/runtime_registry.rb +39 -0
  138. data/lib/active_record/sanitization.rb +24 -19
  139. data/lib/active_record/schema.rb +8 -6
  140. data/lib/active_record/schema_dumper.rb +19 -9
  141. data/lib/active_record/schema_migration.rb +30 -14
  142. data/lib/active_record/signed_id.rb +11 -1
  143. data/lib/active_record/statement_cache.rb +7 -7
  144. data/lib/active_record/table_metadata.rb +1 -10
  145. data/lib/active_record/tasks/database_tasks.rb +76 -70
  146. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  147. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  148. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  149. data/lib/active_record/test_fixtures.rb +81 -91
  150. data/lib/active_record/testing/query_assertions.rb +121 -0
  151. data/lib/active_record/timestamp.rb +1 -1
  152. data/lib/active_record/token_for.rb +22 -12
  153. data/lib/active_record/touch_later.rb +1 -1
  154. data/lib/active_record/transaction.rb +68 -0
  155. data/lib/active_record/transactions.rb +43 -14
  156. data/lib/active_record/translation.rb +0 -2
  157. data/lib/active_record/type/serialized.rb +1 -3
  158. data/lib/active_record/type_caster/connection.rb +4 -4
  159. data/lib/active_record/validations/associated.rb +9 -3
  160. data/lib/active_record/validations/uniqueness.rb +14 -10
  161. data/lib/active_record/validations.rb +4 -1
  162. data/lib/active_record.rb +149 -40
  163. data/lib/arel/alias_predication.rb +1 -1
  164. data/lib/arel/collectors/bind.rb +2 -0
  165. data/lib/arel/collectors/composite.rb +7 -0
  166. data/lib/arel/collectors/sql_string.rb +1 -1
  167. data/lib/arel/collectors/substitute_binds.rb +1 -1
  168. data/lib/arel/nodes/binary.rb +0 -6
  169. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  170. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  171. data/lib/arel/nodes/node.rb +4 -3
  172. data/lib/arel/nodes/sql_literal.rb +7 -0
  173. data/lib/arel/nodes.rb +2 -2
  174. data/lib/arel/predications.rb +1 -1
  175. data/lib/arel/select_manager.rb +1 -1
  176. data/lib/arel/tree_manager.rb +3 -2
  177. data/lib/arel/update_manager.rb +2 -1
  178. data/lib/arel/visitors/dot.rb +1 -0
  179. data/lib/arel/visitors/mysql.rb +9 -4
  180. data/lib/arel/visitors/postgresql.rb +1 -12
  181. data/lib/arel/visitors/to_sql.rb +29 -16
  182. data/lib/arel.rb +7 -3
  183. metadata +20 -15
@@ -1,9 +1,130 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/string/filters"
4
+
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters
5
7
  extend ActiveSupport::Autoload
6
8
 
9
+ @adapters = {}
10
+
11
+ class << self
12
+ # Registers a custom database adapter.
13
+ #
14
+ # Can also be used to define aliases.
15
+ #
16
+ # == Example
17
+ #
18
+ # ActiveRecord::ConnectionAdapters.register("megadb", "MegaDB::ActiveRecordAdapter", "mega_db/active_record_adapter")
19
+ #
20
+ # ActiveRecord::ConnectionAdapters.register("mysql", "ActiveRecord::ConnectionAdapters::TrilogyAdapter", "active_record/connection_adapters/trilogy_adapter")
21
+ #
22
+ def register(name, class_name, path = class_name.underscore)
23
+ @adapters[name.to_s] = [class_name, path]
24
+ end
25
+
26
+ def resolve(adapter_name) # :nodoc:
27
+ # Require the adapter itself and give useful feedback about
28
+ # 1. Missing adapter gems.
29
+ # 2. Incorrectly registered adapters.
30
+ # 3. Adapter gems' missing dependencies.
31
+ class_name, path_to_adapter = @adapters[adapter_name.to_s]
32
+
33
+ unless class_name
34
+ # To provide better error messages for adapters expecting the pre-7.2 adapter registration API, we attempt
35
+ # to load the adapter file from the old location which was required by convention, and then raise an error
36
+ # describing how to upgrade the adapter to the new API.
37
+ legacy_adapter_path = "active_record/connection_adapters/#{adapter_name}_adapter"
38
+ legacy_adapter_connection_method_name = "#{adapter_name}_connection".to_sym
39
+
40
+ begin
41
+ require legacy_adapter_path
42
+ # If we reach here it means we found the found a file that may be the legacy adapter and should raise.
43
+ if ActiveRecord::ConnectionHandling.method_defined?(legacy_adapter_connection_method_name)
44
+ # If we find the connection method then we care certain it is a legacy adapter.
45
+ deprecation_message = <<~MSG.squish
46
+ Database configuration specifies '#{adapter_name}' adapter but that adapter has not been registered.
47
+ Rails 7.2 has changed the way Active Record database adapters are loaded. The adapter needs to be
48
+ updated to register itself rather than being loaded by convention.
49
+ Ensure that the adapter in the Gemfile is at the latest version. If it is, then the adapter may need to
50
+ be modified.
51
+ See:
52
+ https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters.html#method-c-register
53
+ MSG
54
+
55
+ exception_message = <<~MSG.squish
56
+ Database configuration specifies '#{adapter_name}' adapter but that adapter has not been registered.
57
+ Ensure that the adapter in the Gemfile is at the latest version. If it is, then the adapter may need to
58
+ be modified.
59
+ MSG
60
+ else
61
+ # If we do not find the connection method we are much less certain it is a legacy adapter. Even though the
62
+ # file exists in the location defined by convenntion, it does not necessarily mean that file is supposed
63
+ # to define the adapter the legacy way. So raise an error that explains both possibilities.
64
+ deprecation_message = <<~MSG.squish
65
+ Database configuration specifies nonexistent '#{adapter_name}' adapter.
66
+ Available adapters are: #{@adapters.keys.sort.join(", ")}.
67
+ Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary
68
+ adapter gem to your Gemfile if it's not in the list of available adapters.
69
+ Rails 7.2 has changed the way Active Record database adapters are loaded. Ensure that the adapter in
70
+ the Gemfile is at the latest version. If it is up to date, the adapter may need to be modified.
71
+ See:
72
+ https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters.html#method-c-register
73
+ MSG
74
+
75
+ exception_message = <<~MSG.squish
76
+ Database configuration specifies nonexistent '#{adapter_name}' adapter.
77
+ Available adapters are: #{@adapters.keys.sort.join(", ")}.
78
+ Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary
79
+ adapter gem to your Gemfile and that it is at its latest version. If it is up to date, the adapter may
80
+ need to be modified.
81
+ MSG
82
+ end
83
+
84
+ ActiveRecord.deprecator.warn(deprecation_message)
85
+ raise AdapterNotFound, exception_message
86
+ rescue LoadError => error
87
+ # The adapter was not found in the legacy location so fall through to the error handling for a missing adapter.
88
+ end
89
+
90
+ raise AdapterNotFound, <<~MSG.squish
91
+ Database configuration specifies nonexistent '#{adapter_name}' adapter.
92
+ Available adapters are: #{@adapters.keys.sort.join(", ")}.
93
+ Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary
94
+ adapter gem to your Gemfile if it's not in the list of available adapters.
95
+ MSG
96
+ end
97
+
98
+ unless Object.const_defined?(class_name)
99
+ begin
100
+ require path_to_adapter
101
+ rescue LoadError => error
102
+ # We couldn't require the adapter itself.
103
+ if error.path == path_to_adapter
104
+ # We can assume here that a non-builtin adapter was specified and the path
105
+ # registered by the adapter gem is incorrect.
106
+ raise LoadError, "Error loading the '#{adapter_name}' Active Record adapter. Ensure that the path registered by the adapter gem is correct. #{error.message}", error.backtrace
107
+ else
108
+ # Bubbled up from the adapter require. Prefix the exception message
109
+ # with some guidance about how to address it and reraise.
110
+ raise LoadError, "Error loading the '#{adapter_name}' Active Record adapter. Missing a gem it depends on? #{error.message}", error.backtrace
111
+ end
112
+ end
113
+ end
114
+
115
+ begin
116
+ Object.const_get(class_name)
117
+ rescue NameError => error
118
+ raise AdapterNotFound, "Could not load the #{class_name} Active Record adapter (#{error.message})."
119
+ end
120
+ end
121
+ end
122
+
123
+ register "sqlite3", "ActiveRecord::ConnectionAdapters::SQLite3Adapter", "active_record/connection_adapters/sqlite3_adapter"
124
+ register "mysql2", "ActiveRecord::ConnectionAdapters::Mysql2Adapter", "active_record/connection_adapters/mysql2_adapter"
125
+ register "trilogy", "ActiveRecord::ConnectionAdapters::TrilogyAdapter", "active_record/connection_adapters/trilogy_adapter"
126
+ register "postgresql", "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter", "active_record/connection_adapters/postgresql_adapter"
127
+
7
128
  eager_autoload do
8
129
  autoload :AbstractAdapter
9
130
  end
@@ -243,22 +243,64 @@ module ActiveRecord
243
243
  # Clears the query cache for all connections associated with the current thread.
244
244
  def clear_query_caches_for_current_thread
245
245
  connection_handler.each_connection_pool do |pool|
246
- pool.connection.clear_query_cache if pool.active_connection?
246
+ pool.clear_query_cache
247
247
  end
248
248
  end
249
249
 
250
250
  # Returns the connection currently associated with the class. This can
251
251
  # also be used to "borrow" the connection to do database work unrelated
252
252
  # to any of the specific Active Records.
253
+ # The connection will remain leased for the entire duration of the request
254
+ # or job, or until +#release_connection+ is called.
255
+ def lease_connection
256
+ connection_pool.lease_connection
257
+ end
258
+
259
+ # Soft deprecated. Use +#with_connection+ or +#lease_connection+ instead.
253
260
  def connection
254
- retrieve_connection
261
+ pool = connection_pool
262
+ if pool.permanent_lease?
263
+ case ActiveRecord.permanent_connection_checkout
264
+ when :deprecated
265
+ ActiveRecord.deprecator.warn <<~MESSAGE
266
+ Called deprecated `ActiveRecord::Base.connection` method.
267
+
268
+ Either use `with_connection` or `lease_connection`.
269
+ MESSAGE
270
+ when :disallowed
271
+ raise ActiveRecordError, <<~MESSAGE
272
+ Called deprecated `ActiveRecord::Base.connection` method.
273
+
274
+ Either use `with_connection` or `lease_connection`.
275
+ MESSAGE
276
+ end
277
+ pool.lease_connection
278
+ else
279
+ pool.active_connection
280
+ end
281
+ end
282
+
283
+ # Return the currently leased connection into the pool
284
+ def release_connection
285
+ connection_pool.release_connection
286
+ end
287
+
288
+ # Checkouts a connection from the pool, yield it and then check it back in.
289
+ # If a connection was already leased via #lease_connection or a parent call to
290
+ # #with_connection, that same connection is yieled.
291
+ # If #lease_connection is called inside the block, the connection won't be checked
292
+ # back in.
293
+ # If #connection is called inside the block, the connection won't be checked back in
294
+ # unless the +prevent_permanent_checkout+ argument is set to +true+.
295
+ def with_connection(prevent_permanent_checkout: false, &block)
296
+ connection_pool.with_connection(prevent_permanent_checkout: prevent_permanent_checkout, &block)
255
297
  end
256
298
 
257
299
  attr_writer :connection_specification_name
258
300
 
259
301
  # Returns the connection specification name from the current class or its parent.
260
302
  def connection_specification_name
261
- if !defined?(@connection_specification_name) || @connection_specification_name.nil?
303
+ if @connection_specification_name.nil?
262
304
  return self == Base ? Base.name : superclass.connection_specification_name
263
305
  end
264
306
  @connection_specification_name
@@ -279,8 +321,12 @@ module ActiveRecord
279
321
  connection_pool.db_config
280
322
  end
281
323
 
324
+ def adapter_class # :nodoc:
325
+ connection_pool.db_config.adapter_class
326
+ end
327
+
282
328
  def connection_pool
283
- connection_handler.retrieve_connection_pool(connection_specification_name, role: current_role, shard: current_shard) || raise(ConnectionNotEstablished)
329
+ connection_handler.retrieve_connection_pool(connection_specification_name, role: current_role, shard: current_shard, strict: true)
284
330
  end
285
331
 
286
332
  def retrieve_connection
@@ -292,16 +338,9 @@ module ActiveRecord
292
338
  connection_handler.connected?(connection_specification_name, role: current_role, shard: current_shard)
293
339
  end
294
340
 
295
- def remove_connection(name = nil)
296
- if name
297
- ActiveRecord.deprecator.warn(<<-MSG.squish)
298
- The name argument for `#remove_connection` is deprecated without replacement
299
- and will be removed in Rails 7.2. `#remove_connection` should always be called
300
- on the connection class directly, which makes the name argument obsolete.
301
- MSG
302
- end
341
+ def remove_connection
342
+ name = @connection_specification_name if defined?(@connection_specification_name)
303
343
 
304
- name ||= @connection_specification_name if defined?(@connection_specification_name)
305
344
  # if removing a connection that has a pool, we reset the
306
345
  # connection_specification_name so it will use the parent
307
346
  # pool.
@@ -312,39 +351,15 @@ module ActiveRecord
312
351
  connection_handler.remove_connection_pool(name, role: current_role, shard: current_shard)
313
352
  end
314
353
 
315
- def clear_cache! # :nodoc:
316
- connection.schema_cache.clear!
317
- end
318
-
319
- def clear_active_connections!(role = nil)
320
- deprecation_for_delegation(__method__)
321
- connection_handler.clear_active_connections!(role)
322
- end
323
-
324
- def clear_reloadable_connections!(role = nil)
325
- deprecation_for_delegation(__method__)
326
- connection_handler.clear_reloadable_connections!(role)
354
+ def schema_cache # :nodoc:
355
+ connection_pool.schema_cache
327
356
  end
328
357
 
329
- def clear_all_connections!(role = nil)
330
- deprecation_for_delegation(__method__)
331
- connection_handler.clear_all_connections!(role)
332
- end
333
-
334
- def flush_idle_connections!(role = nil)
335
- deprecation_for_delegation(__method__)
336
- connection_handler.flush_idle_connections!(role)
358
+ def clear_cache! # :nodoc:
359
+ connection_pool.schema_cache.clear!
337
360
  end
338
361
 
339
362
  private
340
- def deprecation_for_delegation(method)
341
- ActiveRecord.deprecator.warn(<<-MSG.squish)
342
- Calling `ActiveRecord::Base.#{method} is deprecated. Please
343
- call the method directly on the connection handler; for
344
- example: `ActiveRecord::Base.connection_handler.#{method}`.
345
- MSG
346
- end
347
-
348
363
  def resolve_config_for_connection(config_or_env)
349
364
  raise "Anonymous class is not allowed." unless name
350
365
 
@@ -73,7 +73,7 @@ module ActiveRecord
73
73
  end
74
74
  self.configurations = {}
75
75
 
76
- # Returns fully resolved ActiveRecord::DatabaseConfigurations object
76
+ # Returns a fully resolved ActiveRecord::DatabaseConfigurations object.
77
77
  def self.configurations
78
78
  @@configurations
79
79
  end
@@ -102,6 +102,9 @@ module ActiveRecord
102
102
 
103
103
  class_attribute :shard_selector, instance_accessor: false, default: nil
104
104
 
105
+ # Specifies the attributes that will be included in the output of the #inspect method
106
+ class_attribute :attributes_for_inspect, instance_accessor: false, default: [:id]
107
+
105
108
  def self.application_record_class? # :nodoc:
106
109
  if ActiveRecord.application_record_class
107
110
  self == ActiveRecord.application_record_class
@@ -346,12 +349,12 @@ module ActiveRecord
346
349
 
347
350
  # Returns a string like 'Post(id:integer, title:string, body:text)'
348
351
  def inspect # :nodoc:
349
- if self == Base || singleton_class?
352
+ if self == Base
350
353
  super
351
354
  elsif abstract_class?
352
355
  "#{super}(abstract)"
353
356
  elsif !connected?
354
- "#{super} (call '#{super}.connection' to establish a connection)"
357
+ "#{super} (call '#{super}.lease_connection' to establish a connection)"
355
358
  elsif table_exists?
356
359
  attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
357
360
  "#{super}(#{attr_list})"
@@ -373,7 +376,7 @@ module ActiveRecord
373
376
  TypeCaster::Map.new(self)
374
377
  end
375
378
 
376
- def cached_find_by_statement(key, &block) # :nodoc:
379
+ def cached_find_by_statement(connection, key, &block) # :nodoc:
377
380
  cache = @find_by_statement_cache[connection.prepared_statements]
378
381
  cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
379
382
  end
@@ -416,21 +419,23 @@ module ActiveRecord
416
419
  end
417
420
 
418
421
  def cached_find_by(keys, values)
419
- statement = cached_find_by_statement(keys) { |params|
420
- wheres = keys.index_with do |key|
421
- if key.is_a?(Array)
422
- [key.map { params.bind }]
423
- else
424
- params.bind
422
+ with_connection do |connection|
423
+ statement = cached_find_by_statement(connection, keys) { |params|
424
+ wheres = keys.index_with do |key|
425
+ if key.is_a?(Array)
426
+ [key.map { params.bind }]
427
+ else
428
+ params.bind
429
+ end
425
430
  end
426
- end
427
- where(wheres).limit(1)
428
- }
431
+ where(wheres).limit(1)
432
+ }
429
433
 
430
- begin
431
- statement.execute(values.flatten, connection).first
432
- rescue TypeError
433
- raise ActiveRecord::StatementInvalid
434
+ begin
435
+ statement.execute(values.flatten, connection, allow_retry: true).first
436
+ rescue TypeError
437
+ raise ActiveRecord::StatementInvalid
438
+ end
434
439
  end
435
440
  end
436
441
  end
@@ -450,7 +455,7 @@ module ActiveRecord
450
455
  init_internals
451
456
  initialize_internals_callback
452
457
 
453
- assign_attributes(attributes) if attributes
458
+ super
454
459
 
455
460
  yield self if block_given?
456
461
  _run_initialize_callbacks
@@ -719,21 +724,14 @@ module ActiveRecord
719
724
  self.class.connection_handler
720
725
  end
721
726
 
722
- # Returns the contents of the record as a nicely formatted string.
727
+ # Returns the attributes specified by <tt>.attributes_for_inspect</tt> as a nicely formatted string.
723
728
  def inspect
724
- # We check defined?(@attributes) not to issue warnings if the object is
725
- # allocated but not initialized.
726
- inspection = if defined?(@attributes) && @attributes
727
- attribute_names.filter_map do |name|
728
- if _has_attribute?(name)
729
- "#{name}: #{attribute_for_inspect(name)}"
730
- end
731
- end.join(", ")
732
- else
733
- "not initialized"
734
- end
729
+ inspect_with_attributes(attributes_for_inspect)
730
+ end
735
731
 
736
- "#<#{self.class} #{inspection}>"
732
+ # Returns the full contents of the record as a nicely formatted string.
733
+ def full_inspect
734
+ inspect_with_attributes(attribute_names)
737
735
  end
738
736
 
739
737
  # Takes a PP and prettily prints this record to it, allowing you to get a nice result from <tt>pp record</tt>
@@ -741,17 +739,17 @@ module ActiveRecord
741
739
  def pretty_print(pp)
742
740
  return super if custom_inspect_method_defined?
743
741
  pp.object_address_group(self) do
744
- if defined?(@attributes) && @attributes
745
- attr_names = self.class.attribute_names.select { |name| _has_attribute?(name) }
742
+ if @attributes
743
+ attr_names = attributes_for_inspect.select { |name| _has_attribute?(name.to_s) }
746
744
  pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
745
+ attr_name = attr_name.to_s
747
746
  pp.breakable " "
748
747
  pp.group(1) do
749
748
  pp.text attr_name
750
749
  pp.text ":"
751
750
  pp.breakable
752
- value = _read_attribute(attr_name)
753
- value = inspection_filter.filter_param(attr_name, value) unless value.nil?
754
- pp.pp value
751
+ value = attribute_for_inspect(attr_name)
752
+ pp.text value
755
753
  end
756
754
  end
757
755
  else
@@ -789,7 +787,6 @@ module ActiveRecord
789
787
  @strict_loading_mode = :all
790
788
 
791
789
  klass.define_attribute_methods
792
- klass.generate_alias_attributes
793
790
  end
794
791
 
795
792
  def initialize_internals_callback
@@ -809,5 +806,24 @@ module ActiveRecord
809
806
  def inspection_filter
810
807
  self.class.inspection_filter
811
808
  end
809
+
810
+ def inspect_with_attributes(attributes_to_list)
811
+ inspection = if @attributes
812
+ attributes_to_list.filter_map do |name|
813
+ name = name.to_s
814
+ if _has_attribute?(name)
815
+ "#{name}: #{attribute_for_inspect(name)}"
816
+ end
817
+ end.join(", ")
818
+ else
819
+ "not initialized"
820
+ end
821
+
822
+ "#<#{self.class} #{inspection}>"
823
+ end
824
+
825
+ def attributes_for_inspect
826
+ self.class.attributes_for_inspect == :all ? attribute_names : self.class.attributes_for_inspect
827
+ end
812
828
  end
813
829
  end
@@ -7,6 +7,7 @@ module ActiveRecord
7
7
 
8
8
  included do
9
9
  class_attribute :_counter_cache_columns, instance_accessor: false, default: []
10
+ class_attribute :counter_cached_association_names, instance_writer: false, default: []
10
11
  end
11
12
 
12
13
  module ClassMethods
@@ -181,14 +182,26 @@ module ActiveRecord
181
182
  def counter_cache_column?(name) # :nodoc:
182
183
  _counter_cache_columns.include?(name)
183
184
  end
185
+
186
+ def load_schema! # :nodoc:
187
+ super
188
+
189
+ association_names = _reflections.filter_map do |name, reflection|
190
+ next unless reflection.belongs_to? && reflection.counter_cache_column
191
+
192
+ name.to_sym
193
+ end
194
+
195
+ self.counter_cached_association_names |= association_names
196
+ end
184
197
  end
185
198
 
186
199
  private
187
200
  def _create_record(attribute_names = self.attribute_names)
188
201
  id = super
189
202
 
190
- each_counter_cached_associations do |association|
191
- association.increment_counters
203
+ counter_cached_association_names.each do |association_name|
204
+ association(association_name).increment_counters
192
205
  end
193
206
 
194
207
  id
@@ -198,7 +211,9 @@ module ActiveRecord
198
211
  affected_rows = super
199
212
 
200
213
  if affected_rows > 0
201
- each_counter_cached_associations do |association|
214
+ counter_cached_association_names.each do |association_name|
215
+ association = association(association_name)
216
+
202
217
  unless destroyed_by_association && _foreign_keys_equal?(destroyed_by_association.foreign_key, association.reflection.foreign_key)
203
218
  association.decrement_counters
204
219
  end
@@ -208,12 +223,6 @@ module ActiveRecord
208
223
  affected_rows
209
224
  end
210
225
 
211
- def each_counter_cached_associations
212
- _reflections.each do |name, reflection|
213
- yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
214
- end
215
- end
216
-
217
226
  def _foreign_keys_equal?(fkey1, fkey2)
218
227
  fkey1 == fkey2 || Array(fkey1).map(&:to_sym) == Array(fkey2).map(&:to_sym)
219
228
  end
@@ -25,8 +25,7 @@ module ActiveRecord
25
25
  def initialize(url)
26
26
  raise "Database URL cannot be empty" if url.blank?
27
27
  @uri = uri_parser.parse(url)
28
- @adapter = @uri.scheme && @uri.scheme.tr("-", "_")
29
- @adapter = "postgresql" if @adapter == "postgres"
28
+ @adapter = resolved_adapter
30
29
 
31
30
  if @uri.opaque
32
31
  @uri.opaque, @query = @uri.opaque.split("?", 2)
@@ -46,7 +45,7 @@ module ActiveRecord
46
45
  attr_reader :uri
47
46
 
48
47
  def uri_parser
49
- @uri_parser ||= URI::RFC2396_Parser.new
48
+ @uri_parser ||= URI::Parser.new
50
49
  end
51
50
 
52
51
  # Converts the query parameters of the URI into a hash.
@@ -80,6 +79,12 @@ module ActiveRecord
80
79
  end
81
80
  end
82
81
 
82
+ def resolved_adapter
83
+ adapter = uri.scheme && @uri.scheme.tr("-", "_")
84
+ adapter = ActiveRecord.protocol_adapters[adapter] || adapter
85
+ adapter
86
+ end
87
+
83
88
  # Returns name of the database.
84
89
  def database_from_path
85
90
  if @adapter == "sqlite3"
@@ -11,14 +11,21 @@ module ActiveRecord
11
11
  def initialize(env_name, name)
12
12
  @env_name = env_name
13
13
  @name = name
14
+ @adapter_class = nil
14
15
  end
15
16
 
16
- def adapter_method
17
- "#{adapter}_connection"
17
+ def adapter_class
18
+ @adapter_class ||= ActiveRecord::ConnectionAdapters.resolve(adapter)
18
19
  end
19
20
 
20
- def adapter_class_method
21
- "#{adapter}_adapter_class"
21
+ def new_connection
22
+ adapter_class.new(configuration_hash)
23
+ end
24
+
25
+ def validate!
26
+ adapter_class if adapter
27
+
28
+ true
22
29
  end
23
30
 
24
31
  def host
@@ -84,6 +91,10 @@ module ActiveRecord
84
91
  def schema_cache_path
85
92
  raise NotImplementedError
86
93
  end
94
+
95
+ def use_metadata_table?
96
+ raise NotImplementedError
97
+ end
87
98
  end
88
99
  end
89
100
  end