activerecord 5.1.7 → 5.2.8.1

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 +629 -661
  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 +7 -5
  9. data/lib/active_record/associations/alias_tracker.rb +19 -27
  10. data/lib/active_record/associations/association.rb +41 -37
  11. data/lib/active_record/associations/association_scope.rb +38 -50
  12. data/lib/active_record/associations/belongs_to_association.rb +27 -8
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +4 -7
  15. data/lib/active_record/associations/builder/belongs_to.rb +12 -4
  16. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  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 +59 -47
  22. data/lib/active_record/associations/collection_proxy.rb +20 -49
  23. data/lib/active_record/associations/foreign_association.rb +2 -0
  24. data/lib/active_record/associations/has_many_association.rb +12 -1
  25. data/lib/active_record/associations/has_many_through_association.rb +36 -30
  26. data/lib/active_record/associations/has_one_association.rb +12 -1
  27. data/lib/active_record/associations/has_one_through_association.rb +13 -8
  28. data/lib/active_record/associations/join_dependency/join_association.rb +39 -63
  29. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
  31. data/lib/active_record/associations/join_dependency.rb +48 -93
  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 +18 -38
  35. data/lib/active_record/associations/singular_association.rb +14 -16
  36. data/lib/active_record/associations/through_association.rb +26 -11
  37. data/lib/active_record/associations.rb +40 -63
  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 +30 -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 +9 -3
  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 +35 -19
  51. data/lib/active_record/base.rb +2 -0
  52. data/lib/active_record/callbacks.rb +8 -6
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +15 -1
  55. data/lib/active_record/collection_cache_key.rb +12 -8
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +139 -41
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +174 -33
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +15 -5
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -31
  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 +64 -6
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +152 -81
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +84 -97
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +92 -165
  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 +13 -2
  72. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +47 -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 +2 -0
  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 +5 -3
  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 -2
  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 +50 -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 +233 -111
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +3 -1
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +57 -73
  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 +22 -0
  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 +75 -1
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +81 -94
  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 +51 -61
  129. data/lib/active_record/counter_cache.rb +10 -3
  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 +18 -13
  133. data/lib/active_record/errors.rb +42 -3
  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 +5 -3
  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 +47 -9
  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 +167 -16
  156. data/lib/active_record/query_cache.rb +6 -8
  157. data/lib/active_record/querying.rb +4 -2
  158. data/lib/active_record/railtie.rb +80 -6
  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 +108 -194
  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 +45 -19
  167. data/lib/active_record/relation/delegation.rb +45 -27
  168. data/lib/active_record/relation/finder_methods.rb +75 -76
  169. data/lib/active_record/relation/from_clause.rb +2 -8
  170. data/lib/active_record/relation/merger.rb +53 -23
  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 +60 -79
  179. data/lib/active_record/relation/query_attribute.rb +28 -2
  180. data/lib/active_record/relation/query_methods.rb +128 -99
  181. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  182. data/lib/active_record/relation/spawn_methods.rb +4 -2
  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 +120 -214
  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 +8 -9
  193. data/lib/active_record/scoping/named.rb +23 -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 +23 -13
  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 +25 -14
  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 +6 -6
  206. data/lib/active_record/touch_later.rb +2 -0
  207. data/lib/active_record/transactions.rb +33 -28
  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 -0
  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 +26 -39
  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
@@ -270,7 +267,7 @@ module ActiveRecord
270
267
  # Connections must be leased while holding the main pool mutex. This is
271
268
  # an internal subclass that also +.leases+ returned connections while
272
269
  # still in queue's critical section (queue synchronizes with the same
273
- # +@lock+ as the main pool) so that a returned connection is already
270
+ # <tt>@lock</tt> as the main pool) so that a returned connection is already
274
271
  # leased and there is no need to re-enter synchronized block.
275
272
  class ConnectionLeasingQueue < Queue # :nodoc:
276
273
  include BiasableQueue
@@ -283,12 +280,12 @@ module ActiveRecord
283
280
  end
284
281
  end
285
282
 
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.
283
+ # Every +frequency+ seconds, the reaper will call +reap+ and +flush+ on
284
+ # +pool+. A reaper instantiated with a zero frequency will never reap
285
+ # the connection pool.
289
286
  #
290
- # Configure the frequency by setting "reaping_frequency" in your
291
- # database yaml file.
287
+ # Configure the frequency by setting +reaping_frequency+ in your database
288
+ # yaml file (default 60 seconds).
292
289
  class Reaper
293
290
  attr_reader :pool, :frequency
294
291
 
@@ -298,11 +295,12 @@ module ActiveRecord
298
295
  end
299
296
 
300
297
  def run
301
- return unless frequency
298
+ return unless frequency && frequency > 0
302
299
  Thread.new(frequency, pool) { |t, p|
303
300
  loop do
304
301
  sleep t
305
302
  p.reap
303
+ p.flush
306
304
  end
307
305
  }
308
306
  end
@@ -312,7 +310,7 @@ module ActiveRecord
312
310
  include QueryCache::ConnectionPoolConfiguration
313
311
 
314
312
  attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
315
- attr_reader :spec, :connections, :size, :reaper
313
+ attr_reader :spec, :size, :reaper
316
314
 
317
315
  # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
318
316
  # object which describes database connection information (e.g. adapter,
@@ -326,8 +324,10 @@ module ActiveRecord
326
324
  @spec = spec
327
325
 
328
326
  @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
327
+ if @idle_timeout = spec.config.fetch(:idle_timeout, 300)
328
+ @idle_timeout = @idle_timeout.to_f
329
+ @idle_timeout = nil if @idle_timeout <= 0
330
+ end
331
331
 
332
332
  # default max pool size to 5
333
333
  @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
@@ -340,7 +340,7 @@ module ActiveRecord
340
340
  # then that +thread+ does indeed own that +conn+. However, an absence of a such
341
341
  # mapping does not mean that the +thread+ doesn't own the said connection. In
342
342
  # that case +conn.owner+ attr should be consulted.
343
- # Access and modification of +@thread_cached_conns+ does not require
343
+ # Access and modification of <tt>@thread_cached_conns</tt> does not require
344
344
  # synchronization.
345
345
  @thread_cached_conns = Concurrent::Map.new(initial_capacity: @size)
346
346
 
@@ -357,6 +357,12 @@ module ActiveRecord
357
357
  @available = ConnectionLeasingQueue.new self
358
358
 
359
359
  @lock_thread = false
360
+
361
+ # +reaping_frequency+ is configurable mostly for historical reasons, but it could
362
+ # also be useful if someone wants a very low +idle_timeout+.
363
+ reaping_frequency = spec.config.fetch(:reaping_frequency, 60)
364
+ @reaper = Reaper.new(self, reaping_frequency && reaping_frequency.to_f)
365
+ @reaper.run
360
366
  end
361
367
 
362
368
  def lock_thread=(lock_thread)
@@ -373,7 +379,7 @@ module ActiveRecord
373
379
  # #connection can be called any number of times; the connection is
374
380
  # held in a cache keyed by a thread.
375
381
  def connection
376
- @thread_cached_conns[connection_cache_key(@lock_thread || Thread.current)] ||= checkout
382
+ @thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
377
383
  end
378
384
 
379
385
  # Returns true if there is an open connection being used for the current thread.
@@ -382,7 +388,7 @@ module ActiveRecord
382
388
  # #connection or #with_connection methods. Connections obtained through
383
389
  # #checkout will not be detected by #active_connection?
384
390
  def active_connection?
385
- @thread_cached_conns[connection_cache_key(Thread.current)]
391
+ @thread_cached_conns[connection_cache_key(current_thread)]
386
392
  end
387
393
 
388
394
  # Signal that the thread is finished with the current connection.
@@ -417,6 +423,21 @@ module ActiveRecord
417
423
  synchronize { @connections.any? }
418
424
  end
419
425
 
426
+ # Returns an array containing the connections currently in the pool.
427
+ # Access to the array does not require synchronization on the pool because
428
+ # the array is newly created and not retained by the pool.
429
+ #
430
+ # However; this method bypasses the ConnectionPool's thread-safe connection
431
+ # access pattern. A returned connection may be owned by another thread,
432
+ # unowned, or by happen-stance owned by the calling thread.
433
+ #
434
+ # Calling methods on a connection without ownership is subject to the
435
+ # thread-safety guarantees of the underlying method. Many of the methods
436
+ # on connection adapter classes are inherently multi-thread unsafe.
437
+ def connections
438
+ synchronize { @connections.dup }
439
+ end
440
+
420
441
  # Disconnects all connections in the pool, and clears the pool.
421
442
  #
422
443
  # Raises:
@@ -449,6 +470,21 @@ module ActiveRecord
449
470
  disconnect(false)
450
471
  end
451
472
 
473
+ # Discards all connections in the pool (even if they're currently
474
+ # leased!), along with the pool itself. Any further interaction with the
475
+ # pool (except #spec and #schema_cache) is undefined.
476
+ #
477
+ # See AbstractAdapter#discard!
478
+ def discard! # :nodoc:
479
+ synchronize do
480
+ return if @connections.nil? # already discarded
481
+ @connections.each do |conn|
482
+ conn.discard!
483
+ end
484
+ @connections = @available = @thread_cached_conns = nil
485
+ end
486
+ end
487
+
452
488
  # Clears the cache which maps classes and re-connects connections that
453
489
  # require reloading.
454
490
  #
@@ -574,6 +610,35 @@ module ActiveRecord
574
610
  end
575
611
  end
576
612
 
613
+ # Disconnect all connections that have been idle for at least
614
+ # +minimum_idle+ seconds. Connections currently checked out, or that were
615
+ # checked in less than +minimum_idle+ seconds ago, are unaffected.
616
+ def flush(minimum_idle = @idle_timeout)
617
+ return if minimum_idle.nil?
618
+
619
+ idle_connections = synchronize do
620
+ @connections.select do |conn|
621
+ !conn.in_use? && conn.seconds_idle >= minimum_idle
622
+ end.each do |conn|
623
+ conn.lease
624
+
625
+ @available.delete conn
626
+ @connections.delete conn
627
+ end
628
+ end
629
+
630
+ idle_connections.each do |conn|
631
+ conn.disconnect!
632
+ end
633
+ end
634
+
635
+ # Disconnect all currently idle connections. Connections currently checked
636
+ # out are unaffected.
637
+ def flush!
638
+ reap
639
+ flush(-1)
640
+ end
641
+
577
642
  def num_waiting_in_queue # :nodoc:
578
643
  @available.num_waiting
579
644
  end
@@ -618,6 +683,10 @@ module ActiveRecord
618
683
  thread
619
684
  end
620
685
 
686
+ def current_thread
687
+ @lock_thread || Thread.current
688
+ end
689
+
621
690
  # Take control of all existing connections so a "group" action such as
622
691
  # reload/disconnect can be performed safely. It is no longer enough to
623
692
  # wrap it in +synchronize+ because some pool's actions are allowed
@@ -681,7 +750,7 @@ module ActiveRecord
681
750
  # this block can't be easily moved into attempt_to_checkout_all_existing_connections's
682
751
  # rescue block, because doing so would put it outside of synchronize section, without
683
752
  # being in a critical section thread_report might become inaccurate
684
- msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds"
753
+ msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds".dup
685
754
 
686
755
  thread_report = []
687
756
  @connections.each do |conn|
@@ -736,10 +805,10 @@ module ActiveRecord
736
805
  # Implementation detail: the connection returned by +acquire_connection+
737
806
  # will already be "+connection.lease+ -ed" to the current thread.
738
807
  def acquire_connection(checkout_timeout)
739
- # NOTE: we rely on +@available.poll+ and +try_to_checkout_new_connection+ to
808
+ # NOTE: we rely on <tt>@available.poll</tt> and +try_to_checkout_new_connection+ to
740
809
  # +conn.lease+ the returned connection (and to do this in a +synchronized+
741
810
  # 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+
811
+ # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to <tt>@available.poll</tt>
743
812
  # and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
744
813
  # of the said methods and avoid an additional +synchronize+ overhead.
745
814
  if conn = @available.poll || try_to_checkout_new_connection
@@ -763,7 +832,7 @@ module ActiveRecord
763
832
  end
764
833
  end
765
834
 
766
- # If the pool is not at a +@size+ limit, establish new connection. Connecting
835
+ # If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
767
836
  # to the DB is done outside main synchronized section.
768
837
  #--
769
838
  # Implementation constraint: a newly established connection returned by this
@@ -829,7 +898,7 @@ module ActiveRecord
829
898
  # end
830
899
  #
831
900
  # class Book < ActiveRecord::Base
832
- # establish_connection "library_db"
901
+ # establish_connection :library_db
833
902
  # end
834
903
  #
835
904
  # class ScaryBook < Book
@@ -861,17 +930,41 @@ module ActiveRecord
861
930
  # All Active Record models use this handler to determine the connection pool that they
862
931
  # should use.
863
932
  #
864
- # The ConnectionHandler class is not coupled with the Active models, as it has no knowlodge
933
+ # The ConnectionHandler class is not coupled with the Active models, as it has no knowledge
865
934
  # about the model. The model needs to pass a specification name to the handler,
866
- # in order to lookup the correct connection pool.
935
+ # in order to look up the correct connection pool.
867
936
  class ConnectionHandler
868
- def initialize
869
- # These caches are keyed by spec.name (ConnectionSpecification#name).
870
- @owner_to_pool = Concurrent::Map.new(initial_capacity: 2) do |h, k|
937
+ def self.create_owner_to_pool # :nodoc:
938
+ Concurrent::Map.new(initial_capacity: 2) do |h, k|
939
+ # Discard the parent's connection pools immediately; we have no need
940
+ # of them
941
+ discard_unowned_pools(h)
942
+
871
943
  h[k] = Concurrent::Map.new(initial_capacity: 2)
872
944
  end
873
945
  end
874
946
 
947
+ def self.unowned_pool_finalizer(pid_map) # :nodoc:
948
+ lambda do |_|
949
+ discard_unowned_pools(pid_map)
950
+ end
951
+ end
952
+
953
+ def self.discard_unowned_pools(pid_map) # :nodoc:
954
+ pid_map.each do |pid, pools|
955
+ pools.values.compact.each(&:discard!) unless pid == Process.pid
956
+ end
957
+ end
958
+
959
+ def initialize
960
+ # These caches are keyed by spec.name (ConnectionSpecification#name).
961
+ @owner_to_pool = ConnectionHandler.create_owner_to_pool
962
+
963
+ # Backup finalizer: if the forked child never needed a pool, the above
964
+ # early discard has not occurred
965
+ ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
966
+ end
967
+
875
968
  def connection_pool_list
876
969
  owner_to_pool.values.compact
877
970
  end
@@ -923,6 +1016,13 @@ module ActiveRecord
923
1016
  connection_pool_list.each(&:disconnect!)
924
1017
  end
925
1018
 
1019
+ # Disconnects all currently idle connections.
1020
+ #
1021
+ # See ConnectionPool#flush! for details.
1022
+ def flush_idle_connections!
1023
+ connection_pool_list.each(&:flush!)
1024
+ end
1025
+
926
1026
  # Locate the connection of the nearest super class. This can be an
927
1027
  # active or defined connection: if it is the latter, it will be
928
1028
  # opened and set as the active connection for the class it was defined
@@ -930,9 +1030,7 @@ module ActiveRecord
930
1030
  def retrieve_connection(spec_name) #:nodoc:
931
1031
  pool = retrieve_connection_pool(spec_name)
932
1032
  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
1033
+ pool.connection
936
1034
  end
937
1035
 
938
1036
  # 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
@@ -60,6 +62,11 @@ module ActiveRecord
60
62
  def joins_per_query
61
63
  256
62
64
  end
65
+
66
+ private
67
+ def bind_params_length
68
+ 65535
69
+ end
63
70
  end
64
71
  end
65
72
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters # :nodoc:
3
5
  module DatabaseStatements
@@ -7,35 +9,61 @@ 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
+
24
+ if prepared_statements
25
+ sql, binds = visitor.accept(arel_or_sql_string.ast, collector).value
26
+
27
+ if binds.length > bind_params_length
28
+ unprepared_statement do
29
+ sql, binds = to_sql_and_binds(arel_or_sql_string)
30
+ visitor.preparable = false
31
+ end
32
+ end
33
+ else
34
+ sql = visitor.accept(arel_or_sql_string.ast, collector).value
35
+ end
36
+ [sql.freeze, binds]
14
37
  else
15
- arel.dup.freeze
38
+ visitor.preparable = false if prepared_statements
39
+ [arel_or_sql_string.dup.freeze, binds]
16
40
  end
17
41
  end
42
+ private :to_sql_and_binds
18
43
 
19
44
  # This is used in the StatementCache object. It returns an object that
20
45
  # can be used to query the database repeatedly.
21
46
  def cacheable_query(klass, arel) # :nodoc:
22
- collected = visitor.accept(arel.ast, collector)
23
47
  if prepared_statements
24
- klass.query(collected.value)
48
+ sql, binds = visitor.accept(arel.ast, collector).value
49
+ query = klass.query(sql)
25
50
  else
26
- klass.partial_query(collected.value)
51
+ collector = PartialQueryCollector.new
52
+ parts, binds = visitor.accept(arel.ast, collector).value
53
+ query = klass.partial_query(parts)
27
54
  end
55
+ [query, binds]
28
56
  end
29
57
 
30
58
  # Returns an ActiveRecord::Result instance.
31
59
  def select_all(arel, name = nil, binds = [], preparable: nil)
32
- arel, binds = binds_from_relation arel, binds
33
- sql = to_sql(arel, binds)
34
- if !prepared_statements || (arel.is_a?(String) && preparable.nil?)
35
- preparable = false
36
- else
37
- preparable = visitor.preparable
60
+ arel = arel_from_relation(arel)
61
+ sql, binds = to_sql_and_binds(arel, binds)
62
+
63
+ if preparable.nil?
64
+ preparable = prepared_statements ? visitor.preparable : false
38
65
  end
66
+
39
67
  if prepared_statements && preparable
40
68
  select_prepared(sql, name, binds)
41
69
  else
@@ -130,26 +158,30 @@ module ActiveRecord
130
158
  # If the next id was calculated in advance (as in Oracle), it should be
131
159
  # passed in as +id_value+.
132
160
  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)
161
+ sql, binds = to_sql_and_binds(arel, binds)
162
+ value = exec_insert(sql, name, binds, pk, sequence_name)
134
163
  id_value || last_inserted_id(value)
135
164
  end
136
165
  alias create insert
137
166
 
138
167
  # Executes the update statement and returns the number of rows affected.
139
168
  def update(arel, name = nil, binds = [])
140
- exec_update(to_sql(arel, binds), name, binds)
169
+ sql, binds = to_sql_and_binds(arel, binds)
170
+ exec_update(sql, name, binds)
141
171
  end
142
172
 
143
173
  # Executes the delete statement and returns the number of rows affected.
144
174
  def delete(arel, name = nil, binds = [])
145
- exec_delete(to_sql(arel, binds), name, binds)
175
+ sql, binds = to_sql_and_binds(arel, binds)
176
+ exec_delete(sql, name, binds)
146
177
  end
147
178
 
148
179
  # Returns +true+ when the connection adapter supports prepared statement
149
180
  # caching, otherwise returns +false+
150
- def supports_statement_cache?
151
- false
181
+ def supports_statement_cache? # :nodoc:
182
+ true
152
183
  end
184
+ deprecate :supports_statement_cache?
153
185
 
154
186
  # Runs the given block in a database transaction, and returns the result
155
187
  # of the block.
@@ -162,7 +194,7 @@ module ActiveRecord
162
194
  #
163
195
  # In order to get around this problem, #transaction will emulate the effect
164
196
  # of nested transactions, by using savepoints:
165
- # http://dev.mysql.com/doc/refman/5.7/en/savepoint.html
197
+ # https://dev.mysql.com/doc/refman/5.7/en/savepoint.html
166
198
  # Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
167
199
  # supports savepoints.
168
200
  #
@@ -214,7 +246,7 @@ module ActiveRecord
214
246
  # You should consult the documentation for your database to understand the
215
247
  # semantics of these different levels:
216
248
  #
217
- # * http://www.postgresql.org/docs/current/static/transaction-iso.html
249
+ # * https://www.postgresql.org/docs/current/static/transaction-iso.html
218
250
  # * https://dev.mysql.com/doc/refman/5.7/en/set-transaction.html
219
251
  #
220
252
  # An ActiveRecord::TransactionIsolationError will be raised if:
@@ -305,6 +337,9 @@ module ActiveRecord
305
337
 
306
338
  # Inserts the given fixture into the table. Overridden in adapters that require
307
339
  # something beyond a simple insert (eg. Oracle).
340
+ # Most of adapters should implement `insert_fixtures` that leverages bulk SQL insert.
341
+ # We keep this method to provide fallback
342
+ # for databases like sqlite that do not support bulk inserts.
308
343
  def insert_fixture(fixture, table_name)
309
344
  fixture = fixture.stringify_keys
310
345
 
@@ -317,16 +352,50 @@ module ActiveRecord
317
352
  raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
318
353
  end
319
354
  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
355
+
356
+ table = Arel::Table.new(table_name)
357
+
358
+ values = binds.map do |bind|
359
+ value = with_yaml_fallback(bind.value_for_database)
360
+ [table[bind.name], value]
327
361
  end
328
362
 
329
- execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", "Fixture Insert"
363
+ manager = Arel::InsertManager.new
364
+ manager.into(table)
365
+ manager.insert(values)
366
+ execute manager.to_sql, "Fixture Insert"
367
+ end
368
+
369
+ # Inserts a set of fixtures into the table. Overridden in adapters that require
370
+ # something beyond a simple insert (eg. Oracle).
371
+ def insert_fixtures(fixtures, table_name)
372
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
373
+ `insert_fixtures` is deprecated and will be removed in the next version of Rails.
374
+ Consider using `insert_fixtures_set` for performance improvement.
375
+ MSG
376
+ return if fixtures.empty?
377
+
378
+ execute(build_fixture_sql(fixtures, table_name), "Fixtures Insert")
379
+ end
380
+
381
+ def insert_fixtures_set(fixture_set, tables_to_delete = [])
382
+ fixture_inserts = fixture_set.map do |table_name, fixtures|
383
+ next if fixtures.empty?
384
+
385
+ build_fixture_sql(fixtures, table_name)
386
+ end.compact
387
+
388
+ table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name table}".dup }
389
+ total_sql = Array.wrap(combine_multi_statements(table_deletes + fixture_inserts))
390
+
391
+ disable_referential_integrity do
392
+ transaction(requires_new: true) do
393
+ total_sql.each do |sql|
394
+ execute sql, "Fixtures Load"
395
+ yield if block_given?
396
+ end
397
+ end
398
+ end
330
399
  end
331
400
 
332
401
  def empty_insert_statement_value
@@ -358,6 +427,44 @@ module ActiveRecord
358
427
  alias join_to_delete join_to_update
359
428
 
360
429
  private
430
+ def default_insert_value(column)
431
+ Arel.sql("DEFAULT")
432
+ end
433
+
434
+ def build_fixture_sql(fixtures, table_name)
435
+ columns = schema_cache.columns_hash(table_name)
436
+
437
+ values = fixtures.map do |fixture|
438
+ fixture = fixture.stringify_keys
439
+
440
+ unknown_columns = fixture.keys - columns.keys
441
+ if unknown_columns.any?
442
+ raise Fixture::FixtureError, %(table "#{table_name}" has no columns named #{unknown_columns.map(&:inspect).join(', ')}.)
443
+ end
444
+
445
+ columns.map do |name, column|
446
+ if fixture.key?(name)
447
+ type = lookup_cast_type_from_column(column)
448
+ bind = Relation::QueryAttribute.new(name, fixture[name], type)
449
+ with_yaml_fallback(bind.value_for_database)
450
+ else
451
+ default_insert_value(column)
452
+ end
453
+ end
454
+ end
455
+
456
+ table = Arel::Table.new(table_name)
457
+ manager = Arel::InsertManager.new
458
+ manager.into(table)
459
+ columns.each_key { |column| manager.columns << table[column] }
460
+ manager.values = manager.create_values_list(values)
461
+
462
+ manager.to_sql
463
+ end
464
+
465
+ def combine_multi_statements(total_sql)
466
+ total_sql.join(";\n")
467
+ end
361
468
 
362
469
  # Returns a subquery for the given key using the join information.
363
470
  def subquery_for(key, select)
@@ -388,11 +495,45 @@ module ActiveRecord
388
495
  row && row.first
389
496
  end
390
497
 
391
- def binds_from_relation(relation, binds)
392
- if relation.is_a?(Relation) && binds.empty?
393
- relation, binds = relation.arel, relation.bound_attributes
498
+ def arel_from_relation(relation)
499
+ if relation.is_a?(Relation)
500
+ relation.arel
501
+ else
502
+ relation
503
+ end
504
+ end
505
+
506
+ # Fixture value is quoted by Arel, however scalar values
507
+ # are not quotable. In this case we want to convert
508
+ # the column value to YAML.
509
+ def with_yaml_fallback(value)
510
+ if value.is_a?(Hash) || value.is_a?(Array)
511
+ YAML.dump(value)
512
+ else
513
+ value
514
+ end
515
+ end
516
+
517
+ class PartialQueryCollector
518
+ def initialize
519
+ @parts = []
520
+ @binds = []
521
+ end
522
+
523
+ def <<(str)
524
+ @parts << str
525
+ self
526
+ end
527
+
528
+ def add_bind(obj)
529
+ @binds << obj
530
+ @parts << Arel::Nodes::BindParam.new(1)
531
+ self
532
+ end
533
+
534
+ def value
535
+ [@parts, @binds]
394
536
  end
395
- [relation, binds]
396
537
  end
397
538
  end
398
539
  end