activerecord 5.1.0 → 5.2.3

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 +5 -5
  2. data/CHANGELOG.md +596 -450
  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.rb +11 -4
  8. data/lib/active_record/aggregations.rb +6 -5
  9. data/lib/active_record/association_relation.rb +7 -5
  10. data/lib/active_record/associations.rb +77 -85
  11. data/lib/active_record/associations/alias_tracker.rb +23 -32
  12. data/lib/active_record/associations/association.rb +49 -35
  13. data/lib/active_record/associations/association_scope.rb +55 -55
  14. data/lib/active_record/associations/belongs_to_association.rb +30 -11
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  16. data/lib/active_record/associations/builder/association.rb +4 -7
  17. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  18. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  20. data/lib/active_record/associations/builder/has_many.rb +2 -0
  21. data/lib/active_record/associations/builder/has_one.rb +2 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  23. data/lib/active_record/associations/collection_association.rb +66 -53
  24. data/lib/active_record/associations/collection_proxy.rb +30 -73
  25. data/lib/active_record/associations/foreign_association.rb +2 -0
  26. data/lib/active_record/associations/has_many_association.rb +13 -2
  27. data/lib/active_record/associations/has_many_through_association.rb +37 -19
  28. data/lib/active_record/associations/has_one_association.rb +14 -1
  29. data/lib/active_record/associations/has_one_through_association.rb +13 -8
  30. data/lib/active_record/associations/join_dependency.rb +52 -96
  31. data/lib/active_record/associations/join_dependency/join_association.rb +22 -75
  32. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  33. data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
  34. data/lib/active_record/associations/preloader.rb +17 -37
  35. data/lib/active_record/associations/preloader/association.rb +53 -92
  36. data/lib/active_record/associations/preloader/through_association.rb +72 -73
  37. data/lib/active_record/associations/singular_association.rb +14 -16
  38. data/lib/active_record/associations/through_association.rb +27 -12
  39. data/lib/active_record/attribute_assignment.rb +2 -5
  40. data/lib/active_record/attribute_decorators.rb +3 -2
  41. data/lib/active_record/attribute_methods.rb +65 -24
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +33 -216
  44. data/lib/active_record/attribute_methods/primary_key.rb +10 -13
  45. data/lib/active_record/attribute_methods/query.rb +2 -0
  46. data/lib/active_record/attribute_methods/read.rb +9 -3
  47. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  49. data/lib/active_record/attribute_methods/write.rb +22 -19
  50. data/lib/active_record/attributes.rb +7 -6
  51. data/lib/active_record/autosave_association.rb +15 -13
  52. data/lib/active_record/base.rb +2 -0
  53. data/lib/active_record/callbacks.rb +12 -6
  54. data/lib/active_record/coders/json.rb +2 -0
  55. data/lib/active_record/coders/yaml_column.rb +2 -0
  56. data/lib/active_record/collection_cache_key.rb +15 -11
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +120 -39
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +192 -37
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -2
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -25
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +65 -7
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -87
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +86 -98
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +126 -189
  70. data/lib/active_record/connection_adapters/column.rb +4 -2
  71. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +13 -2
  73. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -15
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -23
  80. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
  81. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  82. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
  85. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +13 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  99. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  101. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +8 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  109. data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
  110. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  111. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +50 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  113. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +258 -129
  115. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  116. data/lib/active_record/connection_adapters/postgresql/utils.rb +3 -1
  117. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -87
  118. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  119. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +24 -1
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +75 -1
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +90 -96
  127. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  128. data/lib/active_record/connection_handling.rb +4 -2
  129. data/lib/active_record/core.rb +41 -61
  130. data/lib/active_record/counter_cache.rb +20 -15
  131. data/lib/active_record/define_callbacks.rb +5 -3
  132. data/lib/active_record/dynamic_matchers.rb +9 -9
  133. data/lib/active_record/enum.rb +18 -13
  134. data/lib/active_record/errors.rb +60 -15
  135. data/lib/active_record/explain.rb +3 -1
  136. data/lib/active_record/explain_registry.rb +2 -0
  137. data/lib/active_record/explain_subscriber.rb +2 -0
  138. data/lib/active_record/fixture_set/file.rb +2 -0
  139. data/lib/active_record/fixtures.rb +67 -60
  140. data/lib/active_record/gem_version.rb +4 -2
  141. data/lib/active_record/inheritance.rb +49 -19
  142. data/lib/active_record/integration.rb +58 -19
  143. data/lib/active_record/internal_metadata.rb +2 -0
  144. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  145. data/lib/active_record/locking/optimistic.rb +30 -42
  146. data/lib/active_record/locking/pessimistic.rb +10 -7
  147. data/lib/active_record/log_subscriber.rb +46 -4
  148. data/lib/active_record/migration.rb +189 -139
  149. data/lib/active_record/migration/command_recorder.rb +11 -9
  150. data/lib/active_record/migration/compatibility.rb +81 -29
  151. data/lib/active_record/migration/join_table.rb +2 -0
  152. data/lib/active_record/model_schema.rb +74 -58
  153. data/lib/active_record/nested_attributes.rb +18 -6
  154. data/lib/active_record/no_touching.rb +3 -1
  155. data/lib/active_record/null_relation.rb +2 -0
  156. data/lib/active_record/persistence.rb +199 -54
  157. data/lib/active_record/query_cache.rb +8 -10
  158. data/lib/active_record/querying.rb +5 -3
  159. data/lib/active_record/railtie.rb +62 -6
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +2 -0
  162. data/lib/active_record/railties/databases.rake +48 -38
  163. data/lib/active_record/readonly_attributes.rb +3 -2
  164. data/lib/active_record/reflection.rb +137 -207
  165. data/lib/active_record/relation.rb +132 -207
  166. data/lib/active_record/relation/batches.rb +32 -17
  167. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  168. data/lib/active_record/relation/calculations.rb +66 -25
  169. data/lib/active_record/relation/delegation.rb +45 -29
  170. data/lib/active_record/relation/finder_methods.rb +76 -85
  171. data/lib/active_record/relation/from_clause.rb +2 -8
  172. data/lib/active_record/relation/merger.rb +53 -23
  173. data/lib/active_record/relation/predicate_builder.rb +60 -79
  174. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  175. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  176. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  177. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  178. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  179. data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
  180. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  181. data/lib/active_record/relation/query_attribute.rb +28 -2
  182. data/lib/active_record/relation/query_methods.rb +135 -103
  183. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  184. data/lib/active_record/relation/spawn_methods.rb +4 -2
  185. data/lib/active_record/relation/where_clause.rb +65 -67
  186. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  187. data/lib/active_record/result.rb +2 -0
  188. data/lib/active_record/runtime_registry.rb +2 -0
  189. data/lib/active_record/sanitization.rb +129 -121
  190. data/lib/active_record/schema.rb +4 -2
  191. data/lib/active_record/schema_dumper.rb +36 -26
  192. data/lib/active_record/schema_migration.rb +2 -0
  193. data/lib/active_record/scoping.rb +12 -10
  194. data/lib/active_record/scoping/default.rb +10 -7
  195. data/lib/active_record/scoping/named.rb +40 -12
  196. data/lib/active_record/secure_token.rb +2 -0
  197. data/lib/active_record/serialization.rb +2 -0
  198. data/lib/active_record/statement_cache.rb +22 -12
  199. data/lib/active_record/store.rb +3 -1
  200. data/lib/active_record/suppressor.rb +2 -0
  201. data/lib/active_record/table_metadata.rb +12 -3
  202. data/lib/active_record/tasks/database_tasks.rb +38 -26
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  206. data/lib/active_record/timestamp.rb +13 -6
  207. data/lib/active_record/touch_later.rb +2 -0
  208. data/lib/active_record/transactions.rb +32 -27
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type.rb +4 -1
  211. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  212. data/lib/active_record/type/date.rb +2 -0
  213. data/lib/active_record/type/date_time.rb +2 -0
  214. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  215. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  216. data/lib/active_record/type/internal/timezone.rb +2 -0
  217. data/lib/active_record/type/json.rb +30 -0
  218. data/lib/active_record/type/serialized.rb +6 -0
  219. data/lib/active_record/type/text.rb +2 -0
  220. data/lib/active_record/type/time.rb +2 -0
  221. data/lib/active_record/type/type_map.rb +2 -0
  222. data/lib/active_record/type/unsigned_integer.rb +2 -0
  223. data/lib/active_record/type_caster.rb +2 -0
  224. data/lib/active_record/type_caster/connection.rb +2 -0
  225. data/lib/active_record/type_caster/map.rb +3 -1
  226. data/lib/active_record/validations.rb +2 -0
  227. data/lib/active_record/validations/absence.rb +2 -0
  228. data/lib/active_record/validations/associated.rb +2 -0
  229. data/lib/active_record/validations/length.rb +2 -0
  230. data/lib/active_record/validations/presence.rb +2 -0
  231. data/lib/active_record/validations/uniqueness.rb +36 -6
  232. data/lib/active_record/version.rb +2 -0
  233. data/lib/rails/generators/active_record.rb +3 -1
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  236. data/lib/rails/generators/active_record/migration.rb +2 -0
  237. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  238. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  239. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  240. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  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.rb +0 -240
  252. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  253. data/lib/active_record/attribute_mutation_tracker.rb +0 -113
  254. data/lib/active_record/attribute_set.rb +0 -113
  255. data/lib/active_record/attribute_set/builder.rb +0 -124
  256. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  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 -33
@@ -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,7 +188,9 @@ module ActiveRecord
191
188
  t0 = Time.now
192
189
  elapsed = 0
193
190
  loop do
194
- @cond.wait(timeout - elapsed)
191
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
192
+ @cond.wait(timeout - elapsed)
193
+ end
195
194
 
196
195
  return remove if any?
197
196
 
@@ -268,7 +267,7 @@ module ActiveRecord
268
267
  # Connections must be leased while holding the main pool mutex. This is
269
268
  # an internal subclass that also +.leases+ returned connections while
270
269
  # still in queue's critical section (queue synchronizes with the same
271
- # +@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
272
271
  # leased and there is no need to re-enter synchronized block.
273
272
  class ConnectionLeasingQueue < Queue # :nodoc:
274
273
  include BiasableQueue
@@ -281,12 +280,12 @@ module ActiveRecord
281
280
  end
282
281
  end
283
282
 
284
- # Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
285
- # A reaper instantiated with a +nil+ frequency will never reap the
286
- # 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.
287
286
  #
288
- # Configure the frequency by setting "reaping_frequency" in your
289
- # database yaml file.
287
+ # Configure the frequency by setting +reaping_frequency+ in your database
288
+ # yaml file (default 60 seconds).
290
289
  class Reaper
291
290
  attr_reader :pool, :frequency
292
291
 
@@ -296,11 +295,12 @@ module ActiveRecord
296
295
  end
297
296
 
298
297
  def run
299
- return unless frequency
298
+ return unless frequency && frequency > 0
300
299
  Thread.new(frequency, pool) { |t, p|
301
300
  loop do
302
301
  sleep t
303
302
  p.reap
303
+ p.flush
304
304
  end
305
305
  }
306
306
  end
@@ -324,8 +324,10 @@ module ActiveRecord
324
324
  @spec = spec
325
325
 
326
326
  @checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
327
- @reaper = Reaper.new(self, (spec.config[:reaping_frequency] && spec.config[:reaping_frequency].to_f))
328
- @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
329
331
 
330
332
  # default max pool size to 5
331
333
  @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
@@ -338,7 +340,7 @@ module ActiveRecord
338
340
  # then that +thread+ does indeed own that +conn+. However, an absence of a such
339
341
  # mapping does not mean that the +thread+ doesn't own the said connection. In
340
342
  # that case +conn.owner+ attr should be consulted.
341
- # Access and modification of +@thread_cached_conns+ does not require
343
+ # Access and modification of <tt>@thread_cached_conns</tt> does not require
342
344
  # synchronization.
343
345
  @thread_cached_conns = Concurrent::Map.new(initial_capacity: @size)
344
346
 
@@ -355,6 +357,12 @@ module ActiveRecord
355
357
  @available = ConnectionLeasingQueue.new self
356
358
 
357
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
358
366
  end
359
367
 
360
368
  def lock_thread=(lock_thread)
@@ -447,6 +455,21 @@ module ActiveRecord
447
455
  disconnect(false)
448
456
  end
449
457
 
458
+ # Discards all connections in the pool (even if they're currently
459
+ # leased!), along with the pool itself. Any further interaction with the
460
+ # pool (except #spec and #schema_cache) is undefined.
461
+ #
462
+ # See AbstractAdapter#discard!
463
+ def discard! # :nodoc:
464
+ synchronize do
465
+ return if @connections.nil? # already discarded
466
+ @connections.each do |conn|
467
+ conn.discard!
468
+ end
469
+ @connections = @available = @thread_cached_conns = nil
470
+ end
471
+ end
472
+
450
473
  # Clears the cache which maps classes and re-connects connections that
451
474
  # require reloading.
452
475
  #
@@ -572,6 +595,35 @@ module ActiveRecord
572
595
  end
573
596
  end
574
597
 
598
+ # Disconnect all connections that have been idle for at least
599
+ # +minimum_idle+ seconds. Connections currently checked out, or that were
600
+ # checked in less than +minimum_idle+ seconds ago, are unaffected.
601
+ def flush(minimum_idle = @idle_timeout)
602
+ return if minimum_idle.nil?
603
+
604
+ idle_connections = synchronize do
605
+ @connections.select do |conn|
606
+ !conn.in_use? && conn.seconds_idle >= minimum_idle
607
+ end.each do |conn|
608
+ conn.lease
609
+
610
+ @available.delete conn
611
+ @connections.delete conn
612
+ end
613
+ end
614
+
615
+ idle_connections.each do |conn|
616
+ conn.disconnect!
617
+ end
618
+ end
619
+
620
+ # Disconnect all currently idle connections. Connections currently checked
621
+ # out are unaffected.
622
+ def flush!
623
+ reap
624
+ flush(-1)
625
+ end
626
+
575
627
  def num_waiting_in_queue # :nodoc:
576
628
  @available.num_waiting
577
629
  end
@@ -679,7 +731,7 @@ module ActiveRecord
679
731
  # this block can't be easily moved into attempt_to_checkout_all_existing_connections's
680
732
  # rescue block, because doing so would put it outside of synchronize section, without
681
733
  # being in a critical section thread_report might become inaccurate
682
- msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds"
734
+ msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds".dup
683
735
 
684
736
  thread_report = []
685
737
  @connections.each do |conn|
@@ -734,10 +786,10 @@ module ActiveRecord
734
786
  # Implementation detail: the connection returned by +acquire_connection+
735
787
  # will already be "+connection.lease+ -ed" to the current thread.
736
788
  def acquire_connection(checkout_timeout)
737
- # NOTE: we rely on +@available.poll+ and +try_to_checkout_new_connection+ to
789
+ # NOTE: we rely on <tt>@available.poll</tt> and +try_to_checkout_new_connection+ to
738
790
  # +conn.lease+ the returned connection (and to do this in a +synchronized+
739
791
  # section). This is not the cleanest implementation, as ideally we would
740
- # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to +@available.poll+
792
+ # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to <tt>@available.poll</tt>
741
793
  # and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
742
794
  # of the said methods and avoid an additional +synchronize+ overhead.
743
795
  if conn = @available.poll || try_to_checkout_new_connection
@@ -761,7 +813,7 @@ module ActiveRecord
761
813
  end
762
814
  end
763
815
 
764
- # If the pool is not at a +@size+ limit, establish new connection. Connecting
816
+ # If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
765
817
  # to the DB is done outside main synchronized section.
766
818
  #--
767
819
  # Implementation constraint: a newly established connection returned by this
@@ -827,7 +879,7 @@ module ActiveRecord
827
879
  # end
828
880
  #
829
881
  # class Book < ActiveRecord::Base
830
- # establish_connection "library_db"
882
+ # establish_connection :library_db
831
883
  # end
832
884
  #
833
885
  # class ScaryBook < Book
@@ -859,17 +911,41 @@ module ActiveRecord
859
911
  # All Active Record models use this handler to determine the connection pool that they
860
912
  # should use.
861
913
  #
862
- # The ConnectionHandler class is not coupled with the Active models, as it has no knowlodge
914
+ # The ConnectionHandler class is not coupled with the Active models, as it has no knowledge
863
915
  # about the model. The model needs to pass a specification name to the handler,
864
- # in order to lookup the correct connection pool.
916
+ # in order to look up the correct connection pool.
865
917
  class ConnectionHandler
866
- def initialize
867
- # These caches are keyed by spec.name (ConnectionSpecification#name).
868
- @owner_to_pool = Concurrent::Map.new(initial_capacity: 2) do |h, k|
918
+ def self.create_owner_to_pool # :nodoc:
919
+ Concurrent::Map.new(initial_capacity: 2) do |h, k|
920
+ # Discard the parent's connection pools immediately; we have no need
921
+ # of them
922
+ discard_unowned_pools(h)
923
+
869
924
  h[k] = Concurrent::Map.new(initial_capacity: 2)
870
925
  end
871
926
  end
872
927
 
928
+ def self.unowned_pool_finalizer(pid_map) # :nodoc:
929
+ lambda do |_|
930
+ discard_unowned_pools(pid_map)
931
+ end
932
+ end
933
+
934
+ def self.discard_unowned_pools(pid_map) # :nodoc:
935
+ pid_map.each do |pid, pools|
936
+ pools.values.compact.each(&:discard!) unless pid == Process.pid
937
+ end
938
+ end
939
+
940
+ def initialize
941
+ # These caches are keyed by spec.name (ConnectionSpecification#name).
942
+ @owner_to_pool = ConnectionHandler.create_owner_to_pool
943
+
944
+ # Backup finalizer: if the forked child never needed a pool, the above
945
+ # early discard has not occurred
946
+ ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
947
+ end
948
+
873
949
  def connection_pool_list
874
950
  owner_to_pool.values.compact
875
951
  end
@@ -921,6 +997,13 @@ module ActiveRecord
921
997
  connection_pool_list.each(&:disconnect!)
922
998
  end
923
999
 
1000
+ # Disconnects all currently idle connections.
1001
+ #
1002
+ # See ConnectionPool#flush! for details.
1003
+ def flush_idle_connections!
1004
+ connection_pool_list.each(&:flush!)
1005
+ end
1006
+
924
1007
  # Locate the connection of the nearest super class. This can be an
925
1008
  # active or defined connection: if it is the latter, it will be
926
1009
  # opened and set as the active connection for the class it was defined
@@ -928,9 +1011,7 @@ module ActiveRecord
928
1011
  def retrieve_connection(spec_name) #:nodoc:
929
1012
  pool = retrieve_connection_pool(spec_name)
930
1013
  raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found." unless pool
931
- conn = pool.connection
932
- raise ConnectionNotEstablished, "No connection for '#{spec_name}' in connection pool" unless conn
933
- conn
1014
+ pool.connection
934
1015
  end
935
1016
 
936
1017
  # 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
@@ -51,9 +79,7 @@ module ActiveRecord
51
79
 
52
80
  # Returns a single value from a record
53
81
  def select_value(arel, name = nil, binds = [])
54
- if result = select_rows(arel, name, binds).first
55
- result.first
56
- end
82
+ single_value_from_rows(select_rows(arel, name, binds))
57
83
  end
58
84
 
59
85
  # Returns an array of the values of the first column in a select:
@@ -68,6 +94,18 @@ module ActiveRecord
68
94
  select_all(arel, name, binds).rows
69
95
  end
70
96
 
97
+ def query_value(sql, name = nil) # :nodoc:
98
+ single_value_from_rows(query(sql, name))
99
+ end
100
+
101
+ def query_values(sql, name = nil) # :nodoc:
102
+ query(sql, name).map(&:first)
103
+ end
104
+
105
+ def query(sql, name = nil) # :nodoc:
106
+ exec_query(sql, name).rows
107
+ end
108
+
71
109
  # Executes the SQL statement in the context of this connection and returns
72
110
  # the raw result from the connection adapter.
73
111
  # Note: depending on your database connector, the result returned by this
@@ -120,26 +158,30 @@ module ActiveRecord
120
158
  # If the next id was calculated in advance (as in Oracle), it should be
121
159
  # passed in as +id_value+.
122
160
  def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
123
- 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)
124
163
  id_value || last_inserted_id(value)
125
164
  end
126
165
  alias create insert
127
166
 
128
167
  # Executes the update statement and returns the number of rows affected.
129
168
  def update(arel, name = nil, binds = [])
130
- exec_update(to_sql(arel, binds), name, binds)
169
+ sql, binds = to_sql_and_binds(arel, binds)
170
+ exec_update(sql, name, binds)
131
171
  end
132
172
 
133
173
  # Executes the delete statement and returns the number of rows affected.
134
174
  def delete(arel, name = nil, binds = [])
135
- exec_delete(to_sql(arel, binds), name, binds)
175
+ sql, binds = to_sql_and_binds(arel, binds)
176
+ exec_delete(sql, name, binds)
136
177
  end
137
178
 
138
179
  # Returns +true+ when the connection adapter supports prepared statement
139
180
  # caching, otherwise returns +false+
140
- def supports_statement_cache?
141
- false
181
+ def supports_statement_cache? # :nodoc:
182
+ true
142
183
  end
184
+ deprecate :supports_statement_cache?
143
185
 
144
186
  # Runs the given block in a database transaction, and returns the result
145
187
  # of the block.
@@ -152,7 +194,7 @@ module ActiveRecord
152
194
  #
153
195
  # In order to get around this problem, #transaction will emulate the effect
154
196
  # of nested transactions, by using savepoints:
155
- # http://dev.mysql.com/doc/refman/5.7/en/savepoint.html
197
+ # https://dev.mysql.com/doc/refman/5.7/en/savepoint.html
156
198
  # Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
157
199
  # supports savepoints.
158
200
  #
@@ -204,7 +246,7 @@ module ActiveRecord
204
246
  # You should consult the documentation for your database to understand the
205
247
  # semantics of these different levels:
206
248
  #
207
- # * http://www.postgresql.org/docs/current/static/transaction-iso.html
249
+ # * https://www.postgresql.org/docs/current/static/transaction-iso.html
208
250
  # * https://dev.mysql.com/doc/refman/5.7/en/set-transaction.html
209
251
  #
210
252
  # An ActiveRecord::TransactionIsolationError will be raised if:
@@ -295,6 +337,9 @@ module ActiveRecord
295
337
 
296
338
  # Inserts the given fixture into the table. Overridden in adapters that require
297
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.
298
343
  def insert_fixture(fixture, table_name)
299
344
  fixture = fixture.stringify_keys
300
345
 
@@ -307,16 +352,50 @@ module ActiveRecord
307
352
  raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
308
353
  end
309
354
  end
310
- key_list = fixture.keys.map { |name| quote_column_name(name) }
311
- value_list = binds.map(&:value_for_database).map do |value|
312
- begin
313
- quote(value)
314
- rescue TypeError
315
- quote(YAML.dump(value))
316
- 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]
317
361
  end
318
362
 
319
- 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
320
399
  end
321
400
 
322
401
  def empty_insert_statement_value
@@ -348,6 +427,44 @@ module ActiveRecord
348
427
  alias join_to_delete join_to_update
349
428
 
350
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
351
468
 
352
469
  # Returns a subquery for the given key using the join information.
353
470
  def subquery_for(key, select)
@@ -370,15 +487,53 @@ module ActiveRecord
370
487
  end
371
488
 
372
489
  def last_inserted_id(result)
373
- row = result.rows.first
490
+ single_value_from_rows(result.rows)
491
+ end
492
+
493
+ def single_value_from_rows(rows)
494
+ row = rows.first
374
495
  row && row.first
375
496
  end
376
497
 
377
- def binds_from_relation(relation, binds)
378
- if relation.is_a?(Relation) && binds.empty?
379
- 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]
380
536
  end
381
- [relation, binds]
382
537
  end
383
538
  end
384
539
  end