activerecord 5.1.7 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (261) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +372 -765
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -5
  5. data/examples/performance.rb +2 -0
  6. data/examples/simple.rb +2 -0
  7. data/lib/active_record/aggregations.rb +6 -5
  8. data/lib/active_record/association_relation.rb +4 -2
  9. data/lib/active_record/associations/alias_tracker.rb +19 -27
  10. data/lib/active_record/associations/association.rb +16 -27
  11. data/lib/active_record/associations/association_scope.rb +38 -50
  12. data/lib/active_record/associations/belongs_to_association.rb +20 -10
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -7
  14. data/lib/active_record/associations/builder/association.rb +4 -7
  15. data/lib/active_record/associations/builder/belongs_to.rb +4 -5
  16. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +2 -0
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +43 -35
  22. data/lib/active_record/associations/collection_proxy.rb +12 -15
  23. data/lib/active_record/associations/foreign_association.rb +2 -0
  24. data/lib/active_record/associations/has_many_association.rb +3 -1
  25. data/lib/active_record/associations/has_many_through_association.rb +7 -18
  26. data/lib/active_record/associations/has_one_association.rb +4 -1
  27. data/lib/active_record/associations/has_one_through_association.rb +8 -7
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -56
  29. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -9
  31. data/lib/active_record/associations/join_dependency.rb +23 -43
  32. data/lib/active_record/associations/preloader/association.rb +45 -61
  33. data/lib/active_record/associations/preloader/through_association.rb +71 -79
  34. data/lib/active_record/associations/preloader.rb +17 -37
  35. data/lib/active_record/associations/singular_association.rb +14 -10
  36. data/lib/active_record/associations/through_association.rb +25 -10
  37. data/lib/active_record/associations.rb +31 -54
  38. data/lib/active_record/attribute_assignment.rb +2 -5
  39. data/lib/active_record/attribute_decorators.rb +3 -2
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  41. data/lib/active_record/attribute_methods/dirty.rb +25 -214
  42. data/lib/active_record/attribute_methods/primary_key.rb +7 -6
  43. data/lib/active_record/attribute_methods/query.rb +2 -0
  44. data/lib/active_record/attribute_methods/read.rb +8 -2
  45. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  47. data/lib/active_record/attribute_methods/write.rb +21 -9
  48. data/lib/active_record/attribute_methods.rb +65 -24
  49. data/lib/active_record/attributes.rb +6 -5
  50. data/lib/active_record/autosave_association.rb +8 -11
  51. data/lib/active_record/base.rb +2 -0
  52. data/lib/active_record/callbacks.rb +8 -10
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +2 -0
  55. data/lib/active_record/collection_cache_key.rb +11 -7
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +111 -38
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +157 -29
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +7 -2
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -32
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +14 -5
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +57 -2
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -78
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +81 -96
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +111 -183
  69. data/lib/active_record/connection_adapters/column.rb +3 -1
  70. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -2
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -30
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +2 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -11
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +2 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -1
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -6
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +246 -110
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +58 -82
  117. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +18 -1
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +71 -1
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +80 -90
  126. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  127. data/lib/active_record/connection_handling.rb +4 -2
  128. data/lib/active_record/core.rb +39 -60
  129. data/lib/active_record/counter_cache.rb +15 -12
  130. data/lib/active_record/define_callbacks.rb +5 -3
  131. data/lib/active_record/dynamic_matchers.rb +9 -9
  132. data/lib/active_record/enum.rb +17 -13
  133. data/lib/active_record/errors.rb +54 -21
  134. data/lib/active_record/explain.rb +3 -1
  135. data/lib/active_record/explain_registry.rb +2 -0
  136. data/lib/active_record/explain_subscriber.rb +2 -0
  137. data/lib/active_record/fixture_set/file.rb +2 -0
  138. data/lib/active_record/fixtures.rb +67 -60
  139. data/lib/active_record/gem_version.rb +4 -2
  140. data/lib/active_record/inheritance.rb +49 -19
  141. data/lib/active_record/integration.rb +58 -19
  142. data/lib/active_record/internal_metadata.rb +2 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  144. data/lib/active_record/locking/optimistic.rb +14 -17
  145. data/lib/active_record/locking/pessimistic.rb +9 -6
  146. data/lib/active_record/log_subscriber.rb +43 -0
  147. data/lib/active_record/migration/command_recorder.rb +11 -9
  148. data/lib/active_record/migration/compatibility.rb +40 -2
  149. data/lib/active_record/migration/join_table.rb +2 -0
  150. data/lib/active_record/migration.rb +189 -139
  151. data/lib/active_record/model_schema.rb +16 -21
  152. data/lib/active_record/nested_attributes.rb +18 -6
  153. data/lib/active_record/no_touching.rb +3 -1
  154. data/lib/active_record/null_relation.rb +2 -0
  155. data/lib/active_record/persistence.rb +166 -16
  156. data/lib/active_record/query_cache.rb +11 -6
  157. data/lib/active_record/querying.rb +3 -1
  158. data/lib/active_record/railtie.rb +61 -3
  159. data/lib/active_record/railties/console_sandbox.rb +2 -0
  160. data/lib/active_record/railties/controller_runtime.rb +2 -0
  161. data/lib/active_record/railties/databases.rake +46 -36
  162. data/lib/active_record/readonly_attributes.rb +3 -2
  163. data/lib/active_record/reflection.rb +110 -192
  164. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  165. data/lib/active_record/relation/batches.rb +20 -5
  166. data/lib/active_record/relation/calculations.rb +30 -8
  167. data/lib/active_record/relation/delegation.rb +15 -27
  168. data/lib/active_record/relation/finder_methods.rb +75 -78
  169. data/lib/active_record/relation/from_clause.rb +2 -8
  170. data/lib/active_record/relation/merger.rb +51 -20
  171. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  172. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  173. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  174. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  175. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  176. data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
  177. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  178. data/lib/active_record/relation/predicate_builder.rb +53 -78
  179. data/lib/active_record/relation/query_attribute.rb +26 -2
  180. data/lib/active_record/relation/query_methods.rb +89 -88
  181. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  182. data/lib/active_record/relation/spawn_methods.rb +3 -1
  183. data/lib/active_record/relation/where_clause.rb +65 -68
  184. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  185. data/lib/active_record/relation.rb +95 -208
  186. data/lib/active_record/result.rb +2 -0
  187. data/lib/active_record/runtime_registry.rb +2 -0
  188. data/lib/active_record/sanitization.rb +129 -121
  189. data/lib/active_record/schema.rb +4 -2
  190. data/lib/active_record/schema_dumper.rb +36 -26
  191. data/lib/active_record/schema_migration.rb +2 -0
  192. data/lib/active_record/scoping/default.rb +6 -7
  193. data/lib/active_record/scoping/named.rb +21 -7
  194. data/lib/active_record/scoping.rb +9 -8
  195. data/lib/active_record/secure_token.rb +2 -0
  196. data/lib/active_record/serialization.rb +2 -0
  197. data/lib/active_record/statement_cache.rb +22 -12
  198. data/lib/active_record/store.rb +3 -1
  199. data/lib/active_record/suppressor.rb +2 -0
  200. data/lib/active_record/table_metadata.rb +12 -3
  201. data/lib/active_record/tasks/database_tasks.rb +26 -15
  202. data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
  203. data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
  204. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  205. data/lib/active_record/timestamp.rb +5 -12
  206. data/lib/active_record/touch_later.rb +2 -0
  207. data/lib/active_record/transactions.rb +9 -7
  208. data/lib/active_record/translation.rb +2 -0
  209. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  210. data/lib/active_record/type/date.rb +2 -0
  211. data/lib/active_record/type/date_time.rb +2 -0
  212. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  213. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  214. data/lib/active_record/type/internal/timezone.rb +2 -0
  215. data/lib/active_record/type/json.rb +30 -0
  216. data/lib/active_record/type/serialized.rb +2 -4
  217. data/lib/active_record/type/text.rb +2 -0
  218. data/lib/active_record/type/time.rb +2 -0
  219. data/lib/active_record/type/type_map.rb +2 -0
  220. data/lib/active_record/type/unsigned_integer.rb +2 -0
  221. data/lib/active_record/type.rb +4 -1
  222. data/lib/active_record/type_caster/connection.rb +2 -0
  223. data/lib/active_record/type_caster/map.rb +3 -1
  224. data/lib/active_record/type_caster.rb +2 -0
  225. data/lib/active_record/validations/absence.rb +2 -0
  226. data/lib/active_record/validations/associated.rb +2 -0
  227. data/lib/active_record/validations/length.rb +2 -0
  228. data/lib/active_record/validations/presence.rb +2 -0
  229. data/lib/active_record/validations/uniqueness.rb +35 -5
  230. data/lib/active_record/validations.rb +2 -0
  231. data/lib/active_record/version.rb +2 -0
  232. data/lib/active_record.rb +11 -4
  233. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  234. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  235. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  236. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  237. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  238. data/lib/rails/generators/active_record/migration.rb +2 -0
  239. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  240. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  241. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  242. data/lib/rails/generators/active_record.rb +3 -1
  243. metadata +24 -36
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  251. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  252. data/lib/active_record/attribute.rb +0 -240
  253. data/lib/active_record/attribute_mutation_tracker.rb +0 -122
  254. data/lib/active_record/attribute_set/builder.rb +0 -126
  255. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  256. data/lib/active_record/attribute_set.rb +0 -113
  257. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  258. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  259. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  260. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  261. data/lib/active_record/type/internal/abstract_json.rb +0 -37
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "thread"
2
4
  require "concurrent/map"
3
5
  require "monitor"
@@ -61,15 +63,13 @@ module ActiveRecord
61
63
  # There are several connection-pooling-related options that you can add to
62
64
  # your database connection configuration:
63
65
  #
64
- # * +pool+: number indicating size of connection pool (default 5)
65
- # * +checkout_timeout+: number of seconds to block and wait for a connection
66
- # before giving up and raising a timeout error (default 5 seconds).
67
- # * +reaping_frequency+: frequency in seconds to periodically run the
68
- # Reaper, which attempts to find and recover connections from dead
69
- # threads, which can occur if a programmer forgets to close a
70
- # connection at the end of a thread or a thread dies unexpectedly.
71
- # Regardless of this setting, the Reaper will be invoked before every
72
- # blocking wait. (Default +nil+, which means don't schedule the Reaper).
66
+ # * +pool+: maximum number of connections the pool may manage (default 5).
67
+ # * +idle_timeout+: number of seconds that a connection will be kept
68
+ # unused in the pool before it is automatically disconnected (default
69
+ # 300 seconds). Set this to zero to keep connections forever.
70
+ # * +checkout_timeout+: number of seconds to wait for a connection to
71
+ # become available before giving up and raising a timeout error (default
72
+ # 5 seconds).
73
73
  #
74
74
  #--
75
75
  # Synchronization policy:
@@ -80,11 +80,8 @@ module ActiveRecord
80
80
  # * private methods that require being called in a +synchronize+ blocks
81
81
  # are now explicitly documented
82
82
  class ConnectionPool
83
- # Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool
84
- # with which it shares a Monitor. But could be a generic Queue.
85
- #
86
- # The Queue in stdlib's 'thread' could replace this class except
87
- # stdlib's doesn't support waiting with a timeout.
83
+ # Threadsafe, fair, LIFO queue. Meant to be used by ConnectionPool
84
+ # with which it shares a Monitor.
88
85
  class Queue
89
86
  def initialize(lock = Monitor.new)
90
87
  @lock = lock
@@ -173,7 +170,7 @@ module ActiveRecord
173
170
 
174
171
  # Removes and returns the head of the queue if possible, or +nil+.
175
172
  def remove
176
- @queue.shift
173
+ @queue.pop
177
174
  end
178
175
 
179
176
  # Remove and return the head the queue if the number of
@@ -191,9 +188,7 @@ module ActiveRecord
191
188
  t0 = Time.now
192
189
  elapsed = 0
193
190
  loop do
194
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
195
- @cond.wait(timeout - elapsed)
196
- end
191
+ @cond.wait(timeout - elapsed)
197
192
 
198
193
  return remove if any?
199
194
 
@@ -270,7 +265,7 @@ module ActiveRecord
270
265
  # Connections must be leased while holding the main pool mutex. This is
271
266
  # an internal subclass that also +.leases+ returned connections while
272
267
  # still in queue's critical section (queue synchronizes with the same
273
- # +@lock+ as the main pool) so that a returned connection is already
268
+ # <tt>@lock</tt> as the main pool) so that a returned connection is already
274
269
  # leased and there is no need to re-enter synchronized block.
275
270
  class ConnectionLeasingQueue < Queue # :nodoc:
276
271
  include BiasableQueue
@@ -283,12 +278,12 @@ module ActiveRecord
283
278
  end
284
279
  end
285
280
 
286
- # Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
287
- # A reaper instantiated with a +nil+ frequency will never reap the
288
- # connection pool.
281
+ # Every +frequency+ seconds, the reaper will call +reap+ and +flush+ on
282
+ # +pool+. A reaper instantiated with a zero frequency will never reap
283
+ # the connection pool.
289
284
  #
290
- # Configure the frequency by setting "reaping_frequency" in your
291
- # database yaml file.
285
+ # Configure the frequency by setting +reaping_frequency+ in your database
286
+ # yaml file (default 60 seconds).
292
287
  class Reaper
293
288
  attr_reader :pool, :frequency
294
289
 
@@ -298,11 +293,12 @@ module ActiveRecord
298
293
  end
299
294
 
300
295
  def run
301
- return unless frequency
296
+ return unless frequency && frequency > 0
302
297
  Thread.new(frequency, pool) { |t, p|
303
298
  loop do
304
299
  sleep t
305
300
  p.reap
301
+ p.flush
306
302
  end
307
303
  }
308
304
  end
@@ -326,8 +322,10 @@ module ActiveRecord
326
322
  @spec = spec
327
323
 
328
324
  @checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
329
- @reaper = Reaper.new(self, (spec.config[:reaping_frequency] && spec.config[:reaping_frequency].to_f))
330
- @reaper.run
325
+ if @idle_timeout = spec.config.fetch(:idle_timeout, 300)
326
+ @idle_timeout = @idle_timeout.to_f
327
+ @idle_timeout = nil if @idle_timeout <= 0
328
+ end
331
329
 
332
330
  # default max pool size to 5
333
331
  @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
@@ -340,7 +338,7 @@ module ActiveRecord
340
338
  # then that +thread+ does indeed own that +conn+. However, an absence of a such
341
339
  # mapping does not mean that the +thread+ doesn't own the said connection. In
342
340
  # that case +conn.owner+ attr should be consulted.
343
- # Access and modification of +@thread_cached_conns+ does not require
341
+ # Access and modification of <tt>@thread_cached_conns</tt> does not require
344
342
  # synchronization.
345
343
  @thread_cached_conns = Concurrent::Map.new(initial_capacity: @size)
346
344
 
@@ -357,6 +355,12 @@ module ActiveRecord
357
355
  @available = ConnectionLeasingQueue.new self
358
356
 
359
357
  @lock_thread = false
358
+
359
+ # +reaping_frequency+ is configurable mostly for historical reasons, but it could
360
+ # also be useful if someone wants a very low +idle_timeout+.
361
+ reaping_frequency = spec.config.fetch(:reaping_frequency, 60)
362
+ @reaper = Reaper.new(self, reaping_frequency && reaping_frequency.to_f)
363
+ @reaper.run
360
364
  end
361
365
 
362
366
  def lock_thread=(lock_thread)
@@ -449,6 +453,21 @@ module ActiveRecord
449
453
  disconnect(false)
450
454
  end
451
455
 
456
+ # Discards all connections in the pool (even if they're currently
457
+ # leased!), along with the pool itself. Any further interaction with the
458
+ # pool (except #spec and #schema_cache) is undefined.
459
+ #
460
+ # See AbstractAdapter#discard!
461
+ def discard! # :nodoc:
462
+ synchronize do
463
+ return if @connections.nil? # already discarded
464
+ @connections.each do |conn|
465
+ conn.discard!
466
+ end
467
+ @connections = @available = @thread_cached_conns = nil
468
+ end
469
+ end
470
+
452
471
  # Clears the cache which maps classes and re-connects connections that
453
472
  # require reloading.
454
473
  #
@@ -574,6 +593,35 @@ module ActiveRecord
574
593
  end
575
594
  end
576
595
 
596
+ # Disconnect all connections that have been idle for at least
597
+ # +minimum_idle+ seconds. Connections currently checked out, or that were
598
+ # checked in less than +minimum_idle+ seconds ago, are unaffected.
599
+ def flush(minimum_idle = @idle_timeout)
600
+ return if minimum_idle.nil?
601
+
602
+ idle_connections = synchronize do
603
+ @connections.select do |conn|
604
+ !conn.in_use? && conn.seconds_idle >= minimum_idle
605
+ end.each do |conn|
606
+ conn.lease
607
+
608
+ @available.delete conn
609
+ @connections.delete conn
610
+ end
611
+ end
612
+
613
+ idle_connections.each do |conn|
614
+ conn.disconnect!
615
+ end
616
+ end
617
+
618
+ # Disconnect all currently idle connections. Connections currently checked
619
+ # out are unaffected.
620
+ def flush!
621
+ reap
622
+ flush(-1)
623
+ end
624
+
577
625
  def num_waiting_in_queue # :nodoc:
578
626
  @available.num_waiting
579
627
  end
@@ -681,7 +729,7 @@ module ActiveRecord
681
729
  # this block can't be easily moved into attempt_to_checkout_all_existing_connections's
682
730
  # rescue block, because doing so would put it outside of synchronize section, without
683
731
  # being in a critical section thread_report might become inaccurate
684
- msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds"
732
+ msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds".dup
685
733
 
686
734
  thread_report = []
687
735
  @connections.each do |conn|
@@ -736,10 +784,10 @@ module ActiveRecord
736
784
  # Implementation detail: the connection returned by +acquire_connection+
737
785
  # will already be "+connection.lease+ -ed" to the current thread.
738
786
  def acquire_connection(checkout_timeout)
739
- # NOTE: we rely on +@available.poll+ and +try_to_checkout_new_connection+ to
787
+ # NOTE: we rely on <tt>@available.poll</tt> and +try_to_checkout_new_connection+ to
740
788
  # +conn.lease+ the returned connection (and to do this in a +synchronized+
741
789
  # section). This is not the cleanest implementation, as ideally we would
742
- # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to +@available.poll+
790
+ # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to <tt>@available.poll</tt>
743
791
  # and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
744
792
  # of the said methods and avoid an additional +synchronize+ overhead.
745
793
  if conn = @available.poll || try_to_checkout_new_connection
@@ -763,7 +811,7 @@ module ActiveRecord
763
811
  end
764
812
  end
765
813
 
766
- # If the pool is not at a +@size+ limit, establish new connection. Connecting
814
+ # If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
767
815
  # to the DB is done outside main synchronized section.
768
816
  #--
769
817
  # Implementation constraint: a newly established connection returned by this
@@ -829,7 +877,7 @@ module ActiveRecord
829
877
  # end
830
878
  #
831
879
  # class Book < ActiveRecord::Base
832
- # establish_connection "library_db"
880
+ # establish_connection :library_db
833
881
  # end
834
882
  #
835
883
  # class ScaryBook < Book
@@ -861,15 +909,35 @@ module ActiveRecord
861
909
  # All Active Record models use this handler to determine the connection pool that they
862
910
  # should use.
863
911
  #
864
- # The ConnectionHandler class is not coupled with the Active models, as it has no knowlodge
912
+ # The ConnectionHandler class is not coupled with the Active models, as it has no knowledge
865
913
  # about the model. The model needs to pass a specification name to the handler,
866
- # in order to lookup the correct connection pool.
914
+ # in order to look up the correct connection pool.
867
915
  class ConnectionHandler
916
+ def self.unowned_pool_finalizer(pid_map) # :nodoc:
917
+ lambda do |_|
918
+ discard_unowned_pools(pid_map)
919
+ end
920
+ end
921
+
922
+ def self.discard_unowned_pools(pid_map) # :nodoc:
923
+ pid_map.each do |pid, pools|
924
+ pools.values.compact.each(&:discard!) unless pid == Process.pid
925
+ end
926
+ end
927
+
868
928
  def initialize
869
929
  # These caches are keyed by spec.name (ConnectionSpecification#name).
870
930
  @owner_to_pool = Concurrent::Map.new(initial_capacity: 2) do |h, k|
931
+ # Discard the parent's connection pools immediately; we have no need
932
+ # of them
933
+ ConnectionHandler.discard_unowned_pools(h)
934
+
871
935
  h[k] = Concurrent::Map.new(initial_capacity: 2)
872
936
  end
937
+
938
+ # Backup finalizer: if the forked child never needed a pool, the above
939
+ # early discard has not occurred
940
+ ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
873
941
  end
874
942
 
875
943
  def connection_pool_list
@@ -923,6 +991,13 @@ module ActiveRecord
923
991
  connection_pool_list.each(&:disconnect!)
924
992
  end
925
993
 
994
+ # Disconnects all currently idle connections.
995
+ #
996
+ # See ConnectionPool#flush! for details.
997
+ def flush_idle_connections!
998
+ connection_pool_list.each(&:flush!)
999
+ end
1000
+
926
1001
  # Locate the connection of the nearest super class. This can be an
927
1002
  # active or defined connection: if it is the latter, it will be
928
1003
  # opened and set as the active connection for the class it was defined
@@ -930,9 +1005,7 @@ module ActiveRecord
930
1005
  def retrieve_connection(spec_name) #:nodoc:
931
1006
  pool = retrieve_connection_pool(spec_name)
932
1007
  raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found." unless pool
933
- conn = pool.connection
934
- raise ConnectionNotEstablished, "No connection for '#{spec_name}' in connection pool" unless conn
935
- conn
1008
+ pool.connection
936
1009
  end
937
1010
 
938
1011
  # Returns true if a connection that's accessible to this class has
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters # :nodoc:
3
5
  module DatabaseLimits
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters # :nodoc:
3
5
  module DatabaseStatements
@@ -7,30 +9,43 @@ module ActiveRecord
7
9
  end
8
10
 
9
11
  # Converts an arel AST to SQL
10
- def to_sql(arel, binds = [])
11
- if arel.respond_to?(:ast)
12
- collected = visitor.accept(arel.ast, collector)
13
- collected.compile(binds, self).freeze
12
+ def to_sql(arel_or_sql_string, binds = [])
13
+ sql, _ = to_sql_and_binds(arel_or_sql_string, binds)
14
+ sql
15
+ end
16
+
17
+ def to_sql_and_binds(arel_or_sql_string, binds = []) # :nodoc:
18
+ if arel_or_sql_string.respond_to?(:ast)
19
+ unless binds.empty?
20
+ raise "Passing bind parameters with an arel AST is forbidden. " \
21
+ "The values must be stored on the AST directly"
22
+ end
23
+ sql, binds = visitor.accept(arel_or_sql_string.ast, collector).value
24
+ [sql.freeze, binds || []]
14
25
  else
15
- arel.dup.freeze
26
+ [arel_or_sql_string.dup.freeze, binds]
16
27
  end
17
28
  end
29
+ private :to_sql_and_binds
18
30
 
19
31
  # This is used in the StatementCache object. It returns an object that
20
32
  # can be used to query the database repeatedly.
21
33
  def cacheable_query(klass, arel) # :nodoc:
22
- collected = visitor.accept(arel.ast, collector)
23
34
  if prepared_statements
24
- klass.query(collected.value)
35
+ sql, binds = visitor.accept(arel.ast, collector).value
36
+ query = klass.query(sql)
25
37
  else
26
- klass.partial_query(collected.value)
38
+ collector = PartialQueryCollector.new
39
+ parts, binds = visitor.accept(arel.ast, collector).value
40
+ query = klass.partial_query(parts)
27
41
  end
42
+ [query, binds]
28
43
  end
29
44
 
30
45
  # Returns an ActiveRecord::Result instance.
31
46
  def select_all(arel, name = nil, binds = [], preparable: nil)
32
- arel, binds = binds_from_relation arel, binds
33
- sql = to_sql(arel, binds)
47
+ arel = arel_from_relation(arel)
48
+ sql, binds = to_sql_and_binds(arel, binds)
34
49
  if !prepared_statements || (arel.is_a?(String) && preparable.nil?)
35
50
  preparable = false
36
51
  else
@@ -130,26 +145,30 @@ module ActiveRecord
130
145
  # If the next id was calculated in advance (as in Oracle), it should be
131
146
  # passed in as +id_value+.
132
147
  def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
133
- value = exec_insert(to_sql(arel, binds), name, binds, pk, sequence_name)
148
+ sql, binds = to_sql_and_binds(arel, binds)
149
+ value = exec_insert(sql, name, binds, pk, sequence_name)
134
150
  id_value || last_inserted_id(value)
135
151
  end
136
152
  alias create insert
137
153
 
138
154
  # Executes the update statement and returns the number of rows affected.
139
155
  def update(arel, name = nil, binds = [])
140
- exec_update(to_sql(arel, binds), name, binds)
156
+ sql, binds = to_sql_and_binds(arel, binds)
157
+ exec_update(sql, name, binds)
141
158
  end
142
159
 
143
160
  # Executes the delete statement and returns the number of rows affected.
144
161
  def delete(arel, name = nil, binds = [])
145
- exec_delete(to_sql(arel, binds), name, binds)
162
+ sql, binds = to_sql_and_binds(arel, binds)
163
+ exec_delete(sql, name, binds)
146
164
  end
147
165
 
148
166
  # Returns +true+ when the connection adapter supports prepared statement
149
167
  # caching, otherwise returns +false+
150
- def supports_statement_cache?
151
- false
168
+ def supports_statement_cache? # :nodoc:
169
+ true
152
170
  end
171
+ deprecate :supports_statement_cache?
153
172
 
154
173
  # Runs the given block in a database transaction, and returns the result
155
174
  # of the block.
@@ -162,7 +181,7 @@ module ActiveRecord
162
181
  #
163
182
  # In order to get around this problem, #transaction will emulate the effect
164
183
  # of nested transactions, by using savepoints:
165
- # http://dev.mysql.com/doc/refman/5.7/en/savepoint.html
184
+ # https://dev.mysql.com/doc/refman/5.7/en/savepoint.html
166
185
  # Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
167
186
  # supports savepoints.
168
187
  #
@@ -214,7 +233,7 @@ module ActiveRecord
214
233
  # You should consult the documentation for your database to understand the
215
234
  # semantics of these different levels:
216
235
  #
217
- # * http://www.postgresql.org/docs/current/static/transaction-iso.html
236
+ # * https://www.postgresql.org/docs/current/static/transaction-iso.html
218
237
  # * https://dev.mysql.com/doc/refman/5.7/en/set-transaction.html
219
238
  #
220
239
  # An ActiveRecord::TransactionIsolationError will be raised if:
@@ -305,6 +324,9 @@ module ActiveRecord
305
324
 
306
325
  # Inserts the given fixture into the table. Overridden in adapters that require
307
326
  # something beyond a simple insert (eg. Oracle).
327
+ # Most of adapters should implement `insert_fixtures` that leverages bulk SQL insert.
328
+ # We keep this method to provide fallback
329
+ # for databases like sqlite that do not support bulk inserts.
308
330
  def insert_fixture(fixture, table_name)
309
331
  fixture = fixture.stringify_keys
310
332
 
@@ -317,16 +339,50 @@ module ActiveRecord
317
339
  raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
318
340
  end
319
341
  end
320
- key_list = fixture.keys.map { |name| quote_column_name(name) }
321
- value_list = binds.map(&:value_for_database).map do |value|
322
- begin
323
- quote(value)
324
- rescue TypeError
325
- quote(YAML.dump(value))
326
- end
342
+
343
+ table = Arel::Table.new(table_name)
344
+
345
+ values = binds.map do |bind|
346
+ value = with_yaml_fallback(bind.value_for_database)
347
+ [table[bind.name], value]
327
348
  end
328
349
 
329
- execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", "Fixture Insert"
350
+ manager = Arel::InsertManager.new
351
+ manager.into(table)
352
+ manager.insert(values)
353
+ execute manager.to_sql, "Fixture Insert"
354
+ end
355
+
356
+ # Inserts a set of fixtures into the table. Overridden in adapters that require
357
+ # something beyond a simple insert (eg. Oracle).
358
+ def insert_fixtures(fixtures, table_name)
359
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
360
+ `insert_fixtures` is deprecated and will be removed in the next version of Rails.
361
+ Consider using `insert_fixtures_set` for performance improvement.
362
+ MSG
363
+ return if fixtures.empty?
364
+
365
+ execute(build_fixture_sql(fixtures, table_name), "Fixtures Insert")
366
+ end
367
+
368
+ def insert_fixtures_set(fixture_set, tables_to_delete = [])
369
+ fixture_inserts = fixture_set.map do |table_name, fixtures|
370
+ next if fixtures.empty?
371
+
372
+ build_fixture_sql(fixtures, table_name)
373
+ end.compact
374
+
375
+ table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name table}".dup }
376
+ total_sql = Array.wrap(combine_multi_statements(table_deletes + fixture_inserts))
377
+
378
+ disable_referential_integrity do
379
+ transaction(requires_new: true) do
380
+ total_sql.each do |sql|
381
+ execute sql, "Fixtures Load"
382
+ yield if block_given?
383
+ end
384
+ end
385
+ end
330
386
  end
331
387
 
332
388
  def empty_insert_statement_value
@@ -358,6 +414,44 @@ module ActiveRecord
358
414
  alias join_to_delete join_to_update
359
415
 
360
416
  private
417
+ def default_insert_value(column)
418
+ Arel.sql("DEFAULT")
419
+ end
420
+
421
+ def build_fixture_sql(fixtures, table_name)
422
+ columns = schema_cache.columns_hash(table_name)
423
+
424
+ values = fixtures.map do |fixture|
425
+ fixture = fixture.stringify_keys
426
+
427
+ unknown_columns = fixture.keys - columns.keys
428
+ if unknown_columns.any?
429
+ raise Fixture::FixtureError, %(table "#{table_name}" has no columns named #{unknown_columns.map(&:inspect).join(', ')}.)
430
+ end
431
+
432
+ columns.map do |name, column|
433
+ if fixture.key?(name)
434
+ type = lookup_cast_type_from_column(column)
435
+ bind = Relation::QueryAttribute.new(name, fixture[name], type)
436
+ with_yaml_fallback(bind.value_for_database)
437
+ else
438
+ default_insert_value(column)
439
+ end
440
+ end
441
+ end
442
+
443
+ table = Arel::Table.new(table_name)
444
+ manager = Arel::InsertManager.new
445
+ manager.into(table)
446
+ columns.each_key { |column| manager.columns << table[column] }
447
+ manager.values = manager.create_values_list(values)
448
+
449
+ manager.to_sql
450
+ end
451
+
452
+ def combine_multi_statements(total_sql)
453
+ total_sql.join(";\n")
454
+ end
361
455
 
362
456
  # Returns a subquery for the given key using the join information.
363
457
  def subquery_for(key, select)
@@ -388,11 +482,45 @@ module ActiveRecord
388
482
  row && row.first
389
483
  end
390
484
 
391
- def binds_from_relation(relation, binds)
392
- if relation.is_a?(Relation) && binds.empty?
393
- relation, binds = relation.arel, relation.bound_attributes
485
+ def arel_from_relation(relation)
486
+ if relation.is_a?(Relation)
487
+ relation.arel
488
+ else
489
+ relation
490
+ end
491
+ end
492
+
493
+ # Fixture value is quoted by Arel, however scalar values
494
+ # are not quotable. In this case we want to convert
495
+ # the column value to YAML.
496
+ def with_yaml_fallback(value)
497
+ if value.is_a?(Hash) || value.is_a?(Array)
498
+ YAML.dump(value)
499
+ else
500
+ value
501
+ end
502
+ end
503
+
504
+ class PartialQueryCollector
505
+ def initialize
506
+ @parts = []
507
+ @binds = []
508
+ end
509
+
510
+ def <<(str)
511
+ @parts << str
512
+ self
513
+ end
514
+
515
+ def add_bind(obj)
516
+ @binds << obj
517
+ @parts << Arel::Nodes::BindParam.new(1)
518
+ self
519
+ end
520
+
521
+ def value
522
+ [@parts, @binds]
394
523
  end
395
- [relation, binds]
396
524
  end
397
525
  end
398
526
  end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
4
+
1
5
  module ActiveRecord
2
6
  module ConnectionAdapters # :nodoc:
3
7
  module QueryCache
@@ -90,8 +94,8 @@ module ActiveRecord
90
94
 
91
95
  def select_all(arel, name = nil, binds = [], preparable: nil)
92
96
  if @query_cache_enabled && !locked?(arel)
93
- arel, binds = binds_from_relation arel, binds
94
- sql = to_sql(arel, binds)
97
+ arel = arel_from_relation(arel)
98
+ sql, binds = to_sql_and_binds(arel, binds)
95
99
  cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable) }
96
100
  else
97
101
  super
@@ -124,6 +128,7 @@ module ActiveRecord
124
128
  # If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
125
129
  # queries should not be cached.
126
130
  def locked?(arel)
131
+ arel = arel.arel if arel.is_a?(Relation)
127
132
  arel.respond_to?(:locked) && arel.locked
128
133
  end
129
134