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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +372 -765
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -5
- data/examples/performance.rb +2 -0
- data/examples/simple.rb +2 -0
- data/lib/active_record/aggregations.rb +6 -5
- data/lib/active_record/association_relation.rb +4 -2
- data/lib/active_record/associations/alias_tracker.rb +19 -27
- data/lib/active_record/associations/association.rb +16 -27
- data/lib/active_record/associations/association_scope.rb +38 -50
- data/lib/active_record/associations/belongs_to_association.rb +20 -10
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -7
- data/lib/active_record/associations/builder/association.rb +4 -7
- data/lib/active_record/associations/builder/belongs_to.rb +4 -5
- data/lib/active_record/associations/builder/collection_association.rb +1 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +2 -0
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +43 -35
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/foreign_association.rb +2 -0
- data/lib/active_record/associations/has_many_association.rb +3 -1
- data/lib/active_record/associations/has_many_through_association.rb +7 -18
- data/lib/active_record/associations/has_one_association.rb +4 -1
- data/lib/active_record/associations/has_one_through_association.rb +8 -7
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -56
- data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -9
- data/lib/active_record/associations/join_dependency.rb +23 -43
- data/lib/active_record/associations/preloader/association.rb +45 -61
- data/lib/active_record/associations/preloader/through_association.rb +71 -79
- data/lib/active_record/associations/preloader.rb +17 -37
- data/lib/active_record/associations/singular_association.rb +14 -10
- data/lib/active_record/associations/through_association.rb +25 -10
- data/lib/active_record/associations.rb +31 -54
- data/lib/active_record/attribute_assignment.rb +2 -5
- data/lib/active_record/attribute_decorators.rb +3 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
- data/lib/active_record/attribute_methods/dirty.rb +25 -214
- data/lib/active_record/attribute_methods/primary_key.rb +7 -6
- data/lib/active_record/attribute_methods/query.rb +2 -0
- data/lib/active_record/attribute_methods/read.rb +8 -2
- data/lib/active_record/attribute_methods/serialization.rb +23 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
- data/lib/active_record/attribute_methods/write.rb +21 -9
- data/lib/active_record/attribute_methods.rb +65 -24
- data/lib/active_record/attributes.rb +6 -5
- data/lib/active_record/autosave_association.rb +8 -11
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/callbacks.rb +8 -10
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +2 -0
- data/lib/active_record/collection_cache_key.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +111 -38
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +157 -29
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -32
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +57 -2
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -78
- data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
- data/lib/active_record/connection_adapters/abstract_adapter.rb +81 -96
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +111 -183
- data/lib/active_record/connection_adapters/column.rb +3 -1
- data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -2
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -30
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/column.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -6
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +246 -110
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +58 -82
- data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +18 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +71 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +80 -90
- data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
- data/lib/active_record/connection_handling.rb +4 -2
- data/lib/active_record/core.rb +39 -60
- data/lib/active_record/counter_cache.rb +15 -12
- data/lib/active_record/define_callbacks.rb +5 -3
- data/lib/active_record/dynamic_matchers.rb +9 -9
- data/lib/active_record/enum.rb +17 -13
- data/lib/active_record/errors.rb +54 -21
- data/lib/active_record/explain.rb +3 -1
- data/lib/active_record/explain_registry.rb +2 -0
- data/lib/active_record/explain_subscriber.rb +2 -0
- data/lib/active_record/fixture_set/file.rb +2 -0
- data/lib/active_record/fixtures.rb +67 -60
- data/lib/active_record/gem_version.rb +4 -2
- data/lib/active_record/inheritance.rb +49 -19
- data/lib/active_record/integration.rb +58 -19
- data/lib/active_record/internal_metadata.rb +2 -0
- data/lib/active_record/legacy_yaml_adapter.rb +3 -1
- data/lib/active_record/locking/optimistic.rb +14 -17
- data/lib/active_record/locking/pessimistic.rb +9 -6
- data/lib/active_record/log_subscriber.rb +43 -0
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +40 -2
- data/lib/active_record/migration/join_table.rb +2 -0
- data/lib/active_record/migration.rb +189 -139
- data/lib/active_record/model_schema.rb +16 -21
- data/lib/active_record/nested_attributes.rb +18 -6
- data/lib/active_record/no_touching.rb +3 -1
- data/lib/active_record/null_relation.rb +2 -0
- data/lib/active_record/persistence.rb +166 -16
- data/lib/active_record/query_cache.rb +11 -6
- data/lib/active_record/querying.rb +3 -1
- data/lib/active_record/railtie.rb +61 -3
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +2 -0
- data/lib/active_record/railties/databases.rake +46 -36
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +110 -192
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/batches.rb +20 -5
- data/lib/active_record/relation/calculations.rb +30 -8
- data/lib/active_record/relation/delegation.rb +15 -27
- data/lib/active_record/relation/finder_methods.rb +75 -78
- data/lib/active_record/relation/from_clause.rb +2 -8
- data/lib/active_record/relation/merger.rb +51 -20
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
- data/lib/active_record/relation/predicate_builder.rb +53 -78
- data/lib/active_record/relation/query_attribute.rb +26 -2
- data/lib/active_record/relation/query_methods.rb +89 -88
- data/lib/active_record/relation/record_fetch_warning.rb +2 -0
- data/lib/active_record/relation/spawn_methods.rb +3 -1
- data/lib/active_record/relation/where_clause.rb +65 -68
- data/lib/active_record/relation/where_clause_factory.rb +5 -48
- data/lib/active_record/relation.rb +95 -208
- data/lib/active_record/result.rb +2 -0
- data/lib/active_record/runtime_registry.rb +2 -0
- data/lib/active_record/sanitization.rb +129 -121
- data/lib/active_record/schema.rb +4 -2
- data/lib/active_record/schema_dumper.rb +36 -26
- data/lib/active_record/schema_migration.rb +2 -0
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +21 -7
- data/lib/active_record/scoping.rb +9 -8
- data/lib/active_record/secure_token.rb +2 -0
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +22 -12
- data/lib/active_record/store.rb +3 -1
- data/lib/active_record/suppressor.rb +2 -0
- data/lib/active_record/table_metadata.rb +12 -3
- data/lib/active_record/tasks/database_tasks.rb +26 -15
- data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
- data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
- data/lib/active_record/timestamp.rb +5 -12
- data/lib/active_record/touch_later.rb +2 -0
- data/lib/active_record/transactions.rb +9 -7
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +2 -0
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +2 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +2 -4
- data/lib/active_record/type/text.rb +2 -0
- data/lib/active_record/type/time.rb +2 -0
- data/lib/active_record/type/type_map.rb +2 -0
- data/lib/active_record/type/unsigned_integer.rb +2 -0
- data/lib/active_record/type.rb +4 -1
- data/lib/active_record/type_caster/connection.rb +2 -0
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/type_caster.rb +2 -0
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +2 -0
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/presence.rb +2 -0
- data/lib/active_record/validations/uniqueness.rb +35 -5
- data/lib/active_record/validations.rb +2 -0
- data/lib/active_record/version.rb +2 -0
- data/lib/active_record.rb +11 -4
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration.rb +2 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +3 -1
- metadata +24 -36
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- data/lib/active_record/associations/preloader/has_many.rb +0 -15
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -18
- data/lib/active_record/attribute/user_provided_default.rb +0 -30
- data/lib/active_record/attribute.rb +0 -240
- data/lib/active_record/attribute_mutation_tracker.rb +0 -122
- data/lib/active_record/attribute_set/builder.rb +0 -126
- data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
- data/lib/active_record/attribute_set.rb +0 -113
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
- 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
|
65
|
-
# * +
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
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,
|
84
|
-
# with which it shares a Monitor.
|
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.
|
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
|
-
|
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
|
-
#
|
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+
|
287
|
-
# A reaper instantiated with a
|
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
|
291
|
-
#
|
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
|
-
@
|
330
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
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 DatabaseStatements
|
@@ -7,30 +9,43 @@ module ActiveRecord
|
|
7
9
|
end
|
8
10
|
|
9
11
|
# Converts an arel AST to SQL
|
10
|
-
def to_sql(
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
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
|
-
|
35
|
+
sql, binds = visitor.accept(arel.ast, collector).value
|
36
|
+
query = klass.query(sql)
|
25
37
|
else
|
26
|
-
|
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
|
33
|
-
sql =
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
# *
|
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
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
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
|
-
|
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
|
392
|
-
if relation.is_a?(Relation)
|
393
|
-
relation
|
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
|
94
|
-
sql =
|
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
|
|