activerecord 7.0.4.3 → 7.1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1657 -1274
  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 +20 -4
  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 +16 -10
  15. data/lib/active_record/associations/collection_proxy.rb +20 -10
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +20 -13
  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 +10 -8
  21. data/lib/active_record/associations/preloader/association.rb +31 -7
  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 +327 -222
  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 +150 -31
  34. data/lib/active_record/attribute_methods/time_zone_conversion.rb +0 -4
  35. data/lib/active_record/attribute_methods/write.rb +3 -3
  36. data/lib/active_record/attribute_methods.rb +108 -26
  37. data/lib/active_record/attributes.rb +3 -3
  38. data/lib/active_record/autosave_association.rb +55 -9
  39. data/lib/active_record/base.rb +7 -2
  40. data/lib/active_record/callbacks.rb +16 -32
  41. data/lib/active_record/coders/column_serializer.rb +61 -0
  42. data/lib/active_record/coders/json.rb +1 -1
  43. data/lib/active_record/coders/yaml_column.rb +70 -42
  44. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  45. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  46. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  47. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +74 -51
  48. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  49. data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
  50. data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
  51. data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
  52. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  53. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  54. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +155 -25
  55. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +290 -124
  56. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  57. data/lib/active_record/connection_adapters/abstract_adapter.rb +509 -102
  58. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -112
  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 -14
  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 +1 -1
  66. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +18 -13
  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/money.rb +3 -2
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +2 -2
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +15 -8
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  79. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  80. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  81. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +362 -60
  83. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +354 -193
  85. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  86. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  87. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
  88. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +9 -5
  89. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -9
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +211 -83
  92. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  93. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  94. data/lib/active_record/connection_adapters/trilogy_adapter.rb +262 -0
  95. data/lib/active_record/connection_adapters.rb +3 -1
  96. data/lib/active_record/connection_handling.rb +72 -95
  97. data/lib/active_record/core.rb +175 -153
  98. data/lib/active_record/counter_cache.rb +46 -25
  99. data/lib/active_record/database_configurations/database_config.rb +9 -3
  100. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  101. data/lib/active_record/database_configurations/url_config.rb +17 -11
  102. data/lib/active_record/database_configurations.rb +86 -33
  103. data/lib/active_record/delegated_type.rb +9 -4
  104. data/lib/active_record/deprecator.rb +7 -0
  105. data/lib/active_record/destroy_association_async_job.rb +2 -0
  106. data/lib/active_record/disable_joins_association_relation.rb +1 -1
  107. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  108. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  109. data/lib/active_record/encryption/config.rb +25 -1
  110. data/lib/active_record/encryption/configurable.rb +12 -19
  111. data/lib/active_record/encryption/context.rb +10 -3
  112. data/lib/active_record/encryption/contexts.rb +5 -1
  113. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  114. data/lib/active_record/encryption/encryptable_record.rb +42 -18
  115. data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
  116. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  117. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  118. data/lib/active_record/encryption/key_generator.rb +12 -1
  119. data/lib/active_record/encryption/message_serializer.rb +2 -0
  120. data/lib/active_record/encryption/properties.rb +3 -3
  121. data/lib/active_record/encryption/scheme.rb +19 -22
  122. data/lib/active_record/encryption.rb +1 -0
  123. data/lib/active_record/enum.rb +112 -28
  124. data/lib/active_record/errors.rb +112 -18
  125. data/lib/active_record/explain.rb +23 -3
  126. data/lib/active_record/explain_subscriber.rb +1 -1
  127. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  128. data/lib/active_record/fixture_set/render_context.rb +2 -0
  129. data/lib/active_record/fixture_set/table_row.rb +29 -8
  130. data/lib/active_record/fixtures.rb +135 -71
  131. data/lib/active_record/future_result.rb +31 -5
  132. data/lib/active_record/gem_version.rb +4 -4
  133. data/lib/active_record/inheritance.rb +30 -16
  134. data/lib/active_record/insert_all.rb +57 -10
  135. data/lib/active_record/integration.rb +8 -8
  136. data/lib/active_record/internal_metadata.rb +120 -30
  137. data/lib/active_record/locking/optimistic.rb +32 -18
  138. data/lib/active_record/locking/pessimistic.rb +5 -2
  139. data/lib/active_record/log_subscriber.rb +29 -12
  140. data/lib/active_record/marshalling.rb +56 -0
  141. data/lib/active_record/message_pack.rb +124 -0
  142. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  143. data/lib/active_record/middleware/database_selector.rb +9 -11
  144. data/lib/active_record/middleware/shard_selector.rb +3 -1
  145. data/lib/active_record/migration/command_recorder.rb +105 -7
  146. data/lib/active_record/migration/compatibility.rb +157 -58
  147. data/lib/active_record/migration/default_strategy.rb +23 -0
  148. data/lib/active_record/migration/execution_strategy.rb +19 -0
  149. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  150. data/lib/active_record/migration.rb +271 -114
  151. data/lib/active_record/model_schema.rb +64 -44
  152. data/lib/active_record/nested_attributes.rb +24 -6
  153. data/lib/active_record/normalization.rb +167 -0
  154. data/lib/active_record/persistence.rb +195 -42
  155. data/lib/active_record/promise.rb +84 -0
  156. data/lib/active_record/query_cache.rb +3 -21
  157. data/lib/active_record/query_logs.rb +77 -52
  158. data/lib/active_record/query_logs_formatter.rb +41 -0
  159. data/lib/active_record/querying.rb +15 -2
  160. data/lib/active_record/railtie.rb +109 -47
  161. data/lib/active_record/railties/controller_runtime.rb +14 -9
  162. data/lib/active_record/railties/databases.rake +142 -148
  163. data/lib/active_record/railties/job_runtime.rb +23 -0
  164. data/lib/active_record/readonly_attributes.rb +32 -5
  165. data/lib/active_record/reflection.rb +182 -44
  166. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  167. data/lib/active_record/relation/batches.rb +190 -61
  168. data/lib/active_record/relation/calculations.rb +232 -81
  169. data/lib/active_record/relation/delegation.rb +23 -9
  170. data/lib/active_record/relation/finder_methods.rb +77 -16
  171. data/lib/active_record/relation/merger.rb +2 -0
  172. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  173. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  174. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  175. data/lib/active_record/relation/predicate_builder.rb +26 -14
  176. data/lib/active_record/relation/query_attribute.rb +25 -1
  177. data/lib/active_record/relation/query_methods.rb +387 -71
  178. data/lib/active_record/relation/spawn_methods.rb +18 -1
  179. data/lib/active_record/relation.rb +91 -35
  180. data/lib/active_record/result.rb +25 -9
  181. data/lib/active_record/runtime_registry.rb +24 -1
  182. data/lib/active_record/sanitization.rb +51 -11
  183. data/lib/active_record/schema.rb +2 -3
  184. data/lib/active_record/schema_dumper.rb +50 -7
  185. data/lib/active_record/schema_migration.rb +68 -33
  186. data/lib/active_record/scoping/default.rb +15 -5
  187. data/lib/active_record/scoping/named.rb +2 -2
  188. data/lib/active_record/scoping.rb +2 -1
  189. data/lib/active_record/secure_password.rb +60 -0
  190. data/lib/active_record/secure_token.rb +21 -3
  191. data/lib/active_record/signed_id.rb +7 -5
  192. data/lib/active_record/store.rb +9 -9
  193. data/lib/active_record/suppressor.rb +3 -1
  194. data/lib/active_record/table_metadata.rb +16 -3
  195. data/lib/active_record/tasks/database_tasks.rb +127 -105
  196. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  197. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  198. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  199. data/lib/active_record/test_fixtures.rb +113 -96
  200. data/lib/active_record/timestamp.rb +27 -15
  201. data/lib/active_record/token_for.rb +113 -0
  202. data/lib/active_record/touch_later.rb +11 -6
  203. data/lib/active_record/transactions.rb +39 -13
  204. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  205. data/lib/active_record/type/internal/timezone.rb +7 -2
  206. data/lib/active_record/type/serialized.rb +8 -4
  207. data/lib/active_record/type/time.rb +4 -0
  208. data/lib/active_record/validations/absence.rb +1 -1
  209. data/lib/active_record/validations/numericality.rb +5 -4
  210. data/lib/active_record/validations/presence.rb +5 -28
  211. data/lib/active_record/validations/uniqueness.rb +47 -2
  212. data/lib/active_record/validations.rb +8 -4
  213. data/lib/active_record/version.rb +1 -1
  214. data/lib/active_record.rb +121 -16
  215. data/lib/arel/errors.rb +10 -0
  216. data/lib/arel/factory_methods.rb +4 -0
  217. data/lib/arel/filter_predications.rb +1 -1
  218. data/lib/arel/nodes/and.rb +4 -0
  219. data/lib/arel/nodes/binary.rb +6 -1
  220. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  221. data/lib/arel/nodes/cte.rb +36 -0
  222. data/lib/arel/nodes/filter.rb +1 -1
  223. data/lib/arel/nodes/fragments.rb +35 -0
  224. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  225. data/lib/arel/nodes/leading_join.rb +8 -0
  226. data/lib/arel/nodes/node.rb +111 -2
  227. data/lib/arel/nodes/sql_literal.rb +6 -0
  228. data/lib/arel/nodes/table_alias.rb +4 -0
  229. data/lib/arel/nodes.rb +4 -0
  230. data/lib/arel/predications.rb +2 -0
  231. data/lib/arel/table.rb +9 -5
  232. data/lib/arel/visitors/mysql.rb +8 -1
  233. data/lib/arel/visitors/to_sql.rb +81 -17
  234. data/lib/arel/visitors/visitor.rb +2 -2
  235. data/lib/arel.rb +16 -2
  236. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  237. data/lib/rails/generators/active_record/migration.rb +3 -1
  238. data/lib/rails/generators/active_record/model/USAGE +113 -0
  239. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  240. metadata +48 -12
  241. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  242. data/lib/active_record/null_relation.rb +0 -63
@@ -10,35 +10,33 @@ require "active_record/connection_adapters/abstract/connection_pool/reaper"
10
10
  module ActiveRecord
11
11
  module ConnectionAdapters
12
12
  module AbstractPool # :nodoc:
13
- def get_schema_cache(connection)
14
- self.schema_cache ||= SchemaCache.new(connection)
15
- schema_cache.connection = connection
16
- schema_cache
17
- end
18
-
19
- def set_schema_cache(cache)
20
- self.schema_cache = cache
21
- end
22
-
23
- def lazily_set_schema_cache
24
- return unless ActiveRecord.lazily_load_schema_cache
25
-
26
- cache = SchemaCache.load_from(db_config.lazy_schema_cache_path)
27
- set_schema_cache(cache)
28
- end
29
13
  end
30
14
 
31
15
  class NullPool # :nodoc:
32
16
  include ConnectionAdapters::AbstractPool
33
17
 
34
- attr_accessor :schema_cache
18
+ class NullConfig # :nodoc:
19
+ def method_missing(*)
20
+ nil
21
+ end
22
+ end
23
+ NULL_CONFIG = NullConfig.new # :nodoc:
24
+
25
+ def schema_reflection
26
+ SchemaReflection.new(nil)
27
+ end
35
28
 
36
29
  def connection_class; end
37
30
  def checkin(_); end
38
31
  def remove(_); end
39
32
  def async_executor; end
33
+ def db_config
34
+ NULL_CONFIG
35
+ end
40
36
  end
41
37
 
38
+ # = Active Record Connection Pool
39
+ #
42
40
  # Connection pool base class for managing Active Record database
43
41
  # connections.
44
42
  #
@@ -53,19 +51,17 @@ module ActiveRecord
53
51
  # handle cases in which there are more threads than connections: if all
54
52
  # connections have been checked out, and a thread tries to checkout a
55
53
  # connection anyway, then ConnectionPool will wait until some other thread
56
- # has checked in a connection.
54
+ # has checked in a connection, or the +checkout_timeout+ has expired.
57
55
  #
58
56
  # == Obtaining (checking out) a connection
59
57
  #
60
58
  # Connections can be obtained and used from a connection pool in several
61
59
  # ways:
62
60
  #
63
- # 1. Simply use {ActiveRecord::Base.connection}[rdoc-ref:ConnectionHandling.connection]
64
- # as with Active Record 2.1 and
65
- # earlier (pre-connection-pooling). Eventually, when you're done with
66
- # the connection(s) and wish it to be returned to the pool, you call
67
- # {ActiveRecord::Base.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
68
- # This will be the default behavior for Active Record when used in conjunction with
61
+ # 1. Simply use {ActiveRecord::Base.connection}[rdoc-ref:ConnectionHandling.connection].
62
+ # When you're done with the connection(s) and wish it to be returned to the pool, you call
63
+ # {ActiveRecord::Base.connection_handler.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
64
+ # This is the default behavior for Active Record when used in conjunction with
69
65
  # Action Pack's request handling cycle.
70
66
  # 2. Manually check out a connection from the pool with
71
67
  # {ActiveRecord::Base.connection_pool.checkout}[rdoc-ref:#checkout]. You are responsible for
@@ -78,6 +74,12 @@ module ActiveRecord
78
74
  # Connections in the pool are actually AbstractAdapter objects (or objects
79
75
  # compatible with AbstractAdapter's interface).
80
76
  #
77
+ # While a thread has a connection checked out from the pool using one of the
78
+ # above three methods, that connection will automatically be the one used
79
+ # by ActiveRecord queries executing on that thread. It is not required to
80
+ # explicitly pass the checked out connection to \Rails models or queries, for
81
+ # example.
82
+ #
81
83
  # == Options
82
84
  #
83
85
  # There are several connection-pooling-related options that you can add to
@@ -105,11 +107,9 @@ module ActiveRecord
105
107
  include ConnectionAdapters::AbstractPool
106
108
 
107
109
  attr_accessor :automatic_reconnect, :checkout_timeout
108
- attr_reader :db_config, :size, :reaper, :pool_config, :connection_class, :async_executor, :role, :shard
110
+ attr_reader :db_config, :size, :reaper, :pool_config, :async_executor, :role, :shard
109
111
 
110
- alias_method :connection_klass, :connection_class
111
- deprecate :connection_klass
112
- delegate :schema_cache, :schema_cache=, to: :pool_config
112
+ delegate :schema_reflection, :schema_reflection=, to: :pool_config
113
113
 
114
114
  # Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
115
115
  # object which describes database connection information (e.g. adapter,
@@ -122,7 +122,6 @@ module ActiveRecord
122
122
 
123
123
  @pool_config = pool_config
124
124
  @db_config = pool_config.db_config
125
- @connection_class = pool_config.connection_class
126
125
  @role = pool_config.role
127
126
  @shard = pool_config.shard
128
127
 
@@ -158,18 +157,20 @@ module ActiveRecord
158
157
 
159
158
  @async_executor = build_async_executor
160
159
 
161
- lazily_set_schema_cache
162
-
163
160
  @reaper = Reaper.new(self, db_config.reaping_frequency)
164
161
  @reaper.run
165
162
  end
166
163
 
167
164
  def lock_thread=(lock_thread)
168
165
  if lock_thread
169
- @lock_thread = Thread.current
166
+ @lock_thread = ActiveSupport::IsolatedExecutionState.context
170
167
  else
171
168
  @lock_thread = nil
172
169
  end
170
+
171
+ if (active_connection = @thread_cached_conns[connection_cache_key(current_thread)])
172
+ active_connection.lock_thread = @lock_thread
173
+ end
173
174
  end
174
175
 
175
176
  # Retrieve the connection associated with the current thread, or call
@@ -181,6 +182,12 @@ module ActiveRecord
181
182
  @thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
182
183
  end
183
184
 
185
+ def connection_class # :nodoc:
186
+ pool_config.connection_class
187
+ end
188
+ alias :connection_klass :connection_class
189
+ deprecate :connection_klass, deprecator: ActiveRecord.deprecator
190
+
184
191
  # Returns true if there is an open connection being used for the current thread.
185
192
  #
186
193
  # This method only works for connections that have been obtained through
@@ -197,18 +204,23 @@ module ActiveRecord
197
204
  # This method only works for connections that have been obtained through
198
205
  # #connection or #with_connection methods, connections obtained through
199
206
  # #checkout will not be automatically released.
200
- def release_connection(owner_thread = Thread.current)
207
+ def release_connection(owner_thread = ActiveSupport::IsolatedExecutionState.context)
201
208
  if conn = @thread_cached_conns.delete(connection_cache_key(owner_thread))
202
209
  checkin conn
203
210
  end
204
211
  end
205
212
 
206
- # If a connection obtained through #connection or #with_connection methods
207
- # already exists yield it to the block. If no such connection
208
- # exists checkout a connection, yield it to the block, and checkin the
209
- # connection when finished.
213
+ # Yields a connection from the connection pool to the block. If no connection
214
+ # is already checked out by the current thread, a connection will be checked
215
+ # out from the pool, yielded to the block, and then returned to the pool when
216
+ # the block is finished. If a connection has already been checked out on the
217
+ # current thread, such as via #connection or #with_connection, that existing
218
+ # connection will be the one yielded and it will not be returned to the pool
219
+ # automatically at the end of the block; it is expected that such an existing
220
+ # connection will be properly returned to the pool by the code that checked
221
+ # it out.
210
222
  def with_connection
211
- unless conn = @thread_cached_conns[connection_cache_key(Thread.current)]
223
+ unless conn = @thread_cached_conns[connection_cache_key(ActiveSupport::IsolatedExecutionState.context)]
212
224
  conn = connection
213
225
  fresh_connection = true
214
226
  end
@@ -338,7 +350,9 @@ module ActiveRecord
338
350
  # Raises:
339
351
  # - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
340
352
  def checkout(checkout_timeout = @checkout_timeout)
341
- checkout_and_verify(acquire_connection(checkout_timeout))
353
+ connection = checkout_and_verify(acquire_connection(checkout_timeout))
354
+ connection.lock_thread = @lock_thread
355
+ connection
342
356
  end
343
357
 
344
358
  # Check-in a database connection back into the pool, indicating that you
@@ -355,6 +369,7 @@ module ActiveRecord
355
369
  conn.expire
356
370
  end
357
371
 
372
+ conn.lock_thread = nil
358
373
  @available.add conn
359
374
  end
360
375
  end
@@ -448,8 +463,7 @@ module ActiveRecord
448
463
  @available.num_waiting
449
464
  end
450
465
 
451
- # Return connection pool's usage statistic
452
- # Example:
466
+ # Returns the connection pool's usage statistic.
453
467
  #
454
468
  # ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
455
469
  def stat
@@ -510,7 +524,7 @@ module ActiveRecord
510
524
  end
511
525
 
512
526
  def current_thread
513
- @lock_thread || Thread.current
527
+ @lock_thread || ActiveSupport::IsolatedExecutionState.context
514
528
  end
515
529
 
516
530
  # Take control of all existing connections so a "group" action such as
@@ -527,13 +541,13 @@ module ActiveRecord
527
541
  def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
528
542
  collected_conns = synchronize do
529
543
  # account for our own connections
530
- @connections.select { |conn| conn.owner == Thread.current }
544
+ @connections.select { |conn| conn.owner == ActiveSupport::IsolatedExecutionState.context }
531
545
  end
532
546
 
533
547
  newly_checked_out = []
534
548
  timeout_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (@checkout_timeout * 2)
535
549
 
536
- @available.with_a_bias_for(Thread.current) do
550
+ @available.with_a_bias_for(ActiveSupport::IsolatedExecutionState.context) do
537
551
  loop do
538
552
  synchronize do
539
553
  return if collected_conns.size == @connections.size && @now_connecting == 0
@@ -580,7 +594,7 @@ module ActiveRecord
580
594
 
581
595
  thread_report = []
582
596
  @connections.each do |conn|
583
- unless conn.owner == Thread.current
597
+ unless conn.owner == ActiveSupport::IsolatedExecutionState.context
584
598
  thread_report << "#{conn} is owned by #{conn.owner}"
585
599
  end
586
600
  end
@@ -641,7 +655,13 @@ module ActiveRecord
641
655
  conn
642
656
  else
643
657
  reap
644
- @available.poll(checkout_timeout)
658
+ # Retry after reaping, which may return an available connection,
659
+ # remove an inactive connection, or both
660
+ if conn = @available.poll || try_to_checkout_new_connection
661
+ conn
662
+ else
663
+ @available.poll(checkout_timeout)
664
+ end
645
665
  end
646
666
  end
647
667
 
@@ -653,9 +673,12 @@ module ActiveRecord
653
673
  alias_method :release, :remove_connection_from_thread_cache
654
674
 
655
675
  def new_connection
656
- Base.public_send(db_config.adapter_method, db_config.configuration_hash).tap do |conn|
657
- conn.check_version
658
- end
676
+ connection = Base.public_send(db_config.adapter_method, db_config.configuration_hash)
677
+ connection.pool = self
678
+ connection.check_version
679
+ connection
680
+ rescue ConnectionNotEstablished => ex
681
+ raise ex.set_pool(self)
659
682
  end
660
683
 
661
684
  # If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
@@ -702,10 +725,10 @@ module ActiveRecord
702
725
 
703
726
  def checkout_and_verify(c)
704
727
  c._run_checkout_callbacks do
705
- c.verify!
728
+ c.clean!
706
729
  end
707
730
  c
708
- rescue
731
+ rescue Exception
709
732
  remove c
710
733
  c.disconnect!
711
734
  raise
@@ -7,6 +7,11 @@ module ActiveRecord
7
7
  64
8
8
  end
9
9
 
10
+ # Returns the maximum length of a table name.
11
+ def table_name_length
12
+ max_identifier_length
13
+ end
14
+
10
15
  # Returns the maximum length of a table alias.
11
16
  def table_alias_length
12
17
  max_identifier_length
@@ -15,7 +15,12 @@ module ActiveRecord
15
15
  end
16
16
 
17
17
  def to_sql_and_binds(arel_or_sql_string, binds = [], preparable = nil) # :nodoc:
18
+ # Arel::TreeManager -> Arel::Node
18
19
  if arel_or_sql_string.respond_to?(:ast)
20
+ arel_or_sql_string = arel_or_sql_string.ast
21
+ end
22
+
23
+ if Arel.arel_node?(arel_or_sql_string) && !(String === arel_or_sql_string)
19
24
  unless binds.empty?
20
25
  raise "Passing bind parameters with an arel AST is forbidden. " \
21
26
  "The values must be stored on the AST directly"
@@ -25,7 +30,7 @@ module ActiveRecord
25
30
 
26
31
  if prepared_statements
27
32
  collector.preparable = true
28
- sql, binds = visitor.compile(arel_or_sql_string.ast, collector)
33
+ sql, binds = visitor.compile(arel_or_sql_string, collector)
29
34
 
30
35
  if binds.length > bind_params_length
31
36
  unprepared_statement do
@@ -34,7 +39,7 @@ module ActiveRecord
34
39
  end
35
40
  preparable = collector.preparable
36
41
  else
37
- sql = visitor.compile(arel_or_sql_string.ast, collector)
42
+ sql = visitor.compile(arel_or_sql_string, collector)
38
43
  end
39
44
  [sql.freeze, binds, preparable]
40
45
  else
@@ -65,18 +70,18 @@ module ActiveRecord
65
70
 
66
71
  select(sql, name, binds, prepare: prepared_statements && preparable, async: async && FutureResult::SelectAll)
67
72
  rescue ::RangeError
68
- ActiveRecord::Result.empty
73
+ ActiveRecord::Result.empty(async: async)
69
74
  end
70
75
 
71
76
  # Returns a record hash with the column names as keys and column values
72
77
  # as values.
73
- def select_one(arel, name = nil, binds = [])
74
- select_all(arel, name, binds).first
78
+ def select_one(arel, name = nil, binds = [], async: false)
79
+ select_all(arel, name, binds, async: async).then(&:first)
75
80
  end
76
81
 
77
82
  # Returns a single value from a record
78
- def select_value(arel, name = nil, binds = [])
79
- single_value_from_rows(select_rows(arel, name, binds))
83
+ def select_value(arel, name = nil, binds = [], async: false)
84
+ select_rows(arel, name, binds, async: async).then { |rows| single_value_from_rows(rows) }
80
85
  end
81
86
 
82
87
  # Returns an array of the values of the first column in a select:
@@ -87,8 +92,8 @@ module ActiveRecord
87
92
 
88
93
  # Returns an array of arrays containing the field values.
89
94
  # Order is the same as that returned by +columns+.
90
- def select_rows(arel, name = nil, binds = [])
91
- select_all(arel, name, binds).rows
95
+ def select_rows(arel, name = nil, binds = [], async: false)
96
+ select_all(arel, name, binds, async: async).then(&:rows)
92
97
  end
93
98
 
94
99
  def query_value(sql, name = nil) # :nodoc:
@@ -100,7 +105,7 @@ module ActiveRecord
100
105
  end
101
106
 
102
107
  def query(sql, name = nil) # :nodoc:
103
- exec_query(sql, name).rows
108
+ internal_exec_query(sql, name).rows
104
109
  end
105
110
 
106
111
  # Determines whether the SQL statement is a write query.
@@ -110,47 +115,63 @@ module ActiveRecord
110
115
 
111
116
  # Executes the SQL statement in the context of this connection and returns
112
117
  # the raw result from the connection adapter.
118
+ #
119
+ # Setting +allow_retry+ to true causes the db to reconnect and retry
120
+ # executing the SQL statement in case of a connection-related exception.
121
+ # This option should only be enabled for known idempotent queries.
122
+ #
123
+ # Note: the query is assumed to have side effects and the query cache
124
+ # will be cleared. If the query is read-only, consider using #select_all
125
+ # instead.
126
+ #
113
127
  # Note: depending on your database connector, the result returned by this
114
- # method may be manually memory managed. Consider using the exec_query
128
+ # method may be manually memory managed. Consider using #exec_query
115
129
  # wrapper instead.
116
- def execute(sql, name = nil)
117
- raise NotImplementedError
130
+ def execute(sql, name = nil, allow_retry: false)
131
+ internal_execute(sql, name, allow_retry: allow_retry)
118
132
  end
119
133
 
120
134
  # Executes +sql+ statement in the context of this connection using
121
135
  # +binds+ as the bind substitutes. +name+ is logged along with
122
136
  # the executed +sql+ statement.
137
+ #
138
+ # Note: the query is assumed to have side effects and the query cache
139
+ # will be cleared. If the query is read-only, consider using #select_all
140
+ # instead.
123
141
  def exec_query(sql, name = "SQL", binds = [], prepare: false)
124
- raise NotImplementedError
142
+ internal_exec_query(sql, name, binds, prepare: prepare)
125
143
  end
126
144
 
127
145
  # Executes insert +sql+ statement in the context of this connection using
128
146
  # +binds+ as the bind substitutes. +name+ is logged along with
129
147
  # the executed +sql+ statement.
130
- def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
131
- sql, binds = sql_for_insert(sql, pk, binds)
132
- exec_query(sql, name, binds)
148
+ # Some adapters support the `returning` keyword argument which allows to control the result of the query:
149
+ # `nil` is the default value and maintains default behavior. If an array of column names is passed -
150
+ # the result will contain values of the specified columns from the inserted row.
151
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)
152
+ sql, binds = sql_for_insert(sql, pk, binds, returning)
153
+ internal_exec_query(sql, name, binds)
133
154
  end
134
155
 
135
156
  # Executes delete +sql+ statement in the context of this connection using
136
157
  # +binds+ as the bind substitutes. +name+ is logged along with
137
158
  # the executed +sql+ statement.
138
159
  def exec_delete(sql, name = nil, binds = [])
139
- exec_query(sql, name, binds)
160
+ internal_exec_query(sql, name, binds)
140
161
  end
141
162
 
142
163
  # Executes update +sql+ statement in the context of this connection using
143
164
  # +binds+ as the bind substitutes. +name+ is logged along with
144
165
  # the executed +sql+ statement.
145
166
  def exec_update(sql, name = nil, binds = [])
146
- exec_query(sql, name, binds)
167
+ internal_exec_query(sql, name, binds)
147
168
  end
148
169
 
149
170
  def exec_insert_all(sql, name) # :nodoc:
150
- exec_query(sql, name)
171
+ internal_exec_query(sql, name)
151
172
  end
152
173
 
153
- def explain(arel, binds = []) # :nodoc:
174
+ def explain(arel, binds = [], options = []) # :nodoc:
154
175
  raise NotImplementedError
155
176
  end
156
177
 
@@ -162,9 +183,15 @@ module ActiveRecord
162
183
  #
163
184
  # If the next id was calculated in advance (as in Oracle), it should be
164
185
  # passed in as +id_value+.
165
- def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
186
+ # Some adapters support the `returning` keyword argument which allows defining the return value of the method:
187
+ # `nil` is the default value and maintains default behavior. If an array of column names is passed -
188
+ # an array of is returned from the method representing values of the specified columns from the inserted row.
189
+ def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
166
190
  sql, binds = to_sql_and_binds(arel, binds)
167
- value = exec_insert(sql, name, binds, pk, sequence_name)
191
+ value = exec_insert(sql, name, binds, pk, sequence_name, returning: returning)
192
+
193
+ return returning_column_values(value) unless returning.nil?
194
+
168
195
  id_value || last_inserted_id(value)
169
196
  end
170
197
  alias create insert
@@ -187,7 +214,7 @@ module ActiveRecord
187
214
  end
188
215
 
189
216
  def truncate_tables(*table_names) # :nodoc:
190
- table_names -= [schema_migration.table_name, InternalMetadata.table_name]
217
+ table_names -= [schema_migration.table_name, internal_metadata.table_name]
191
218
 
192
219
  return if table_names.empty?
193
220
 
@@ -304,8 +331,9 @@ module ActiveRecord
304
331
  # * You are joining an existing open transaction
305
332
  # * You are creating a nested (savepoint) transaction
306
333
  #
307
- # The mysql2 and postgresql adapters support setting the transaction
334
+ # The mysql2, trilogy, and postgresql adapters support setting the transaction
308
335
  # isolation level.
336
+ # :args: (requires_new: nil, isolation: nil, &block)
309
337
  def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
310
338
  if !requires_new && current_transaction.joinable?
311
339
  if isolation
@@ -323,7 +351,8 @@ module ActiveRecord
323
351
 
324
352
  delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction,
325
353
  :commit_transaction, :rollback_transaction, :materialize_transactions,
326
- :disable_lazy_transactions!, :enable_lazy_transactions!, to: :transaction_manager
354
+ :disable_lazy_transactions!, :enable_lazy_transactions!, :dirty_current_transaction,
355
+ to: :transaction_manager
327
356
 
328
357
  def mark_transaction_written_if_write(sql) # :nodoc:
329
358
  transaction = current_transaction
@@ -336,8 +365,24 @@ module ActiveRecord
336
365
  current_transaction.open?
337
366
  end
338
367
 
339
- def reset_transaction # :nodoc:
368
+ def reset_transaction(restore: false) # :nodoc:
369
+ # Store the existing transaction state to the side
370
+ old_state = @transaction_manager if restore && @transaction_manager&.restorable?
371
+
340
372
  @transaction_manager = ConnectionAdapters::TransactionManager.new(self)
373
+
374
+ if block_given?
375
+ # Reconfigure the connection without any transaction state in the way
376
+ result = yield
377
+
378
+ # Now the connection's fully established, we can swap back
379
+ if old_state
380
+ @transaction_manager = old_state
381
+ @transaction_manager.restore_transactions
382
+ end
383
+
384
+ result
385
+ end
341
386
  end
342
387
 
343
388
  # Register a record with the current transaction so that its after_commit and after_rollback callbacks
@@ -372,10 +417,18 @@ module ActiveRecord
372
417
  # done if the transaction block raises an exception or returns false.
373
418
  def rollback_db_transaction
374
419
  exec_rollback_db_transaction
420
+ rescue ActiveRecord::ConnectionNotEstablished, ActiveRecord::ConnectionFailed
421
+ # Connection's gone; that counts as a rollback
375
422
  end
376
423
 
377
424
  def exec_rollback_db_transaction() end # :nodoc:
378
425
 
426
+ def restart_db_transaction
427
+ exec_restart_db_transaction
428
+ end
429
+
430
+ def exec_restart_db_transaction() end # :nodoc:
431
+
379
432
  def rollback_to_savepoint(name = nil)
380
433
  exec_rollback_to_savepoint(name)
381
434
  end
@@ -393,7 +446,7 @@ module ActiveRecord
393
446
  # something beyond a simple insert (e.g. Oracle).
394
447
  # Most of adapters should implement +insert_fixtures_set+ that leverages bulk SQL insert.
395
448
  # We keep this method to provide fallback
396
- # for databases like sqlite that do not support bulk inserts.
449
+ # for databases like SQLite that do not support bulk inserts.
397
450
  def insert_fixture(fixture, table_name)
398
451
  execute(build_fixture_sql(Array.wrap(fixture), table_name), "Fixture Insert")
399
452
  end
@@ -454,13 +507,30 @@ module ActiveRecord
454
507
  HIGH_PRECISION_CURRENT_TIMESTAMP
455
508
  end
456
509
 
510
+ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
511
+ raise NotImplementedError
512
+ end
513
+
457
514
  private
515
+ def internal_execute(sql, name = "SCHEMA", allow_retry: false, materialize_transactions: true)
516
+ sql = transform_query(sql)
517
+ check_if_write_query(sql)
518
+
519
+ mark_transaction_written_if_write(sql)
520
+
521
+ raw_execute(sql, name, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
522
+ end
523
+
458
524
  def execute_batch(statements, name = nil)
459
525
  statements.each do |statement|
460
- execute(statement, name)
526
+ internal_execute(statement, name)
461
527
  end
462
528
  end
463
529
 
530
+ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
531
+ raise NotImplementedError
532
+ end
533
+
464
534
  DEFAULT_INSERT_VALUE = Arel.sql("DEFAULT").freeze
465
535
  private_constant :DEFAULT_INSERT_VALUE
466
536
 
@@ -557,10 +627,28 @@ module ActiveRecord
557
627
  return future_result
558
628
  end
559
629
 
560
- exec_query(sql, name, binds, prepare: prepare)
630
+ result = internal_exec_query(sql, name, binds, prepare: prepare)
631
+ if async
632
+ FutureResult::Complete.new(result)
633
+ else
634
+ result
635
+ end
561
636
  end
562
637
 
563
- def sql_for_insert(sql, pk, binds)
638
+ def sql_for_insert(sql, pk, binds, returning) # :nodoc:
639
+ if supports_insert_returning?
640
+ if pk.nil?
641
+ # Extract the table from the insert sql. Yuck.
642
+ table_ref = extract_table_ref_from_insert_sql(sql)
643
+ pk = primary_key(table_ref) if table_ref
644
+ end
645
+
646
+ returning_columns = returning || Array(pk)
647
+
648
+ returning_columns_statement = returning_columns.map { |c| quote_column_name(c) }.join(", ")
649
+ sql = "#{sql} RETURNING #{returning_columns_statement}" if returning_columns.any?
650
+ end
651
+
564
652
  [sql, binds]
565
653
  end
566
654
 
@@ -568,6 +656,10 @@ module ActiveRecord
568
656
  single_value_from_rows(result.rows)
569
657
  end
570
658
 
659
+ def returning_column_values(result)
660
+ [last_inserted_id(result)]
661
+ end
662
+
571
663
  def single_value_from_rows(rows)
572
664
  row = rows.first
573
665
  row && row.first
@@ -580,6 +672,12 @@ module ActiveRecord
580
672
  relation
581
673
  end
582
674
  end
675
+
676
+ def extract_table_ref_from_insert_sql(sql)
677
+ if sql =~ /into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im
678
+ $1.delete('"').strip
679
+ end
680
+ end
583
681
  end
584
682
  end
585
683
  end