activerecord 7.0.4.3 → 7.1.3.4

Sign up to get free protection for your applications and to get access to all the features.
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