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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +596 -450
- 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.rb +11 -4
- data/lib/active_record/aggregations.rb +6 -5
- data/lib/active_record/association_relation.rb +7 -5
- data/lib/active_record/associations.rb +77 -85
- data/lib/active_record/associations/alias_tracker.rb +23 -32
- data/lib/active_record/associations/association.rb +49 -35
- data/lib/active_record/associations/association_scope.rb +55 -55
- data/lib/active_record/associations/belongs_to_association.rb +30 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
- data/lib/active_record/associations/builder/association.rb +4 -7
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- 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 +66 -53
- data/lib/active_record/associations/collection_proxy.rb +30 -73
- data/lib/active_record/associations/foreign_association.rb +2 -0
- data/lib/active_record/associations/has_many_association.rb +13 -2
- data/lib/active_record/associations/has_many_through_association.rb +37 -19
- data/lib/active_record/associations/has_one_association.rb +14 -1
- data/lib/active_record/associations/has_one_through_association.rb +13 -8
- data/lib/active_record/associations/join_dependency.rb +52 -96
- data/lib/active_record/associations/join_dependency/join_association.rb +22 -75
- data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
- data/lib/active_record/associations/preloader.rb +17 -37
- data/lib/active_record/associations/preloader/association.rb +53 -92
- data/lib/active_record/associations/preloader/through_association.rb +72 -73
- data/lib/active_record/associations/singular_association.rb +14 -16
- data/lib/active_record/associations/through_association.rb +27 -12
- data/lib/active_record/attribute_assignment.rb +2 -5
- data/lib/active_record/attribute_decorators.rb +3 -2
- data/lib/active_record/attribute_methods.rb +65 -24
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
- data/lib/active_record/attribute_methods/dirty.rb +33 -216
- data/lib/active_record/attribute_methods/primary_key.rb +10 -13
- data/lib/active_record/attribute_methods/query.rb +2 -0
- data/lib/active_record/attribute_methods/read.rb +9 -3
- 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 +22 -19
- data/lib/active_record/attributes.rb +7 -6
- data/lib/active_record/autosave_association.rb +15 -13
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/callbacks.rb +12 -6
- 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 +15 -11
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +120 -39
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +192 -37
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -25
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +65 -7
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -87
- data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
- data/lib/active_record/connection_adapters/abstract_adapter.rb +86 -98
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +126 -189
- data/lib/active_record/connection_adapters/column.rb +4 -2
- data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +13 -2
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -15
- 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 -23
- 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 +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +13 -1
- 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 +3 -1
- 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 -11
- 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 +8 -2
- 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/quoting.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +50 -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 +258 -129
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -87
- 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 +24 -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 +75 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +90 -96
- 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 +41 -61
- data/lib/active_record/counter_cache.rb +20 -15
- data/lib/active_record/define_callbacks.rb +5 -3
- data/lib/active_record/dynamic_matchers.rb +9 -9
- data/lib/active_record/enum.rb +18 -13
- data/lib/active_record/errors.rb +60 -15
- 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 +30 -42
- data/lib/active_record/locking/pessimistic.rb +10 -7
- data/lib/active_record/log_subscriber.rb +46 -4
- data/lib/active_record/migration.rb +189 -139
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +81 -29
- data/lib/active_record/migration/join_table.rb +2 -0
- data/lib/active_record/model_schema.rb +74 -58
- 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 +199 -54
- data/lib/active_record/query_cache.rb +8 -10
- data/lib/active_record/querying.rb +5 -3
- data/lib/active_record/railtie.rb +62 -6
- 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 +48 -38
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +137 -207
- data/lib/active_record/relation.rb +132 -207
- data/lib/active_record/relation/batches.rb +32 -17
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/calculations.rb +66 -25
- data/lib/active_record/relation/delegation.rb +45 -29
- data/lib/active_record/relation/finder_methods.rb +76 -85
- data/lib/active_record/relation/from_clause.rb +2 -8
- data/lib/active_record/relation/merger.rb +53 -23
- data/lib/active_record/relation/predicate_builder.rb +60 -79
- 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/query_attribute.rb +28 -2
- data/lib/active_record/relation/query_methods.rb +135 -103
- data/lib/active_record/relation/record_fetch_warning.rb +2 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -2
- data/lib/active_record/relation/where_clause.rb +65 -67
- data/lib/active_record/relation/where_clause_factory.rb +5 -48
- 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.rb +12 -10
- data/lib/active_record/scoping/default.rb +10 -7
- data/lib/active_record/scoping/named.rb +40 -12
- 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 +38 -26
- data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
- data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
- data/lib/active_record/timestamp.rb +13 -6
- data/lib/active_record/touch_later.rb +2 -0
- data/lib/active_record/transactions.rb +32 -27
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type.rb +4 -1
- 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 +6 -0
- 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_caster.rb +2 -0
- data/lib/active_record/type_caster/connection.rb +2 -0
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/validations.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 +36 -6
- data/lib/active_record/version.rb +2 -0
- data/lib/rails/generators/active_record.rb +3 -1
- 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.rb +2 -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/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
- 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.rb +0 -240
- data/lib/active_record/attribute/user_provided_default.rb +0 -30
- data/lib/active_record/attribute_mutation_tracker.rb +0 -113
- data/lib/active_record/attribute_set.rb +0 -113
- data/lib/active_record/attribute_set/builder.rb +0 -124
- data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
- 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 -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
|
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,7 +188,9 @@ module ActiveRecord
|
|
191
188
|
t0 = Time.now
|
192
189
|
elapsed = 0
|
193
190
|
loop do
|
194
|
-
|
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
|
-
#
|
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+
|
285
|
-
# A reaper instantiated with a
|
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
|
289
|
-
#
|
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
|
-
@
|
328
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
916
|
+
# in order to look up the correct connection pool.
|
865
917
|
class ConnectionHandler
|
866
|
-
def
|
867
|
-
|
868
|
-
|
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
|
-
|
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(
|
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
|
+
|
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
|
-
|
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
|
-
|
48
|
+
sql, binds = visitor.accept(arel.ast, collector).value
|
49
|
+
query = klass.query(sql)
|
25
50
|
else
|
26
|
-
|
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
|
33
|
-
sql =
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
# *
|
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
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
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
|
-
|
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
|
-
|
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
|
378
|
-
if relation.is_a?(Relation)
|
379
|
-
relation
|
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
|