activerecord 6.0.0.beta2 → 6.0.2.rc1
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 +471 -9
- data/README.rdoc +3 -1
- data/lib/active_record.rb +0 -1
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +4 -3
- data/lib/active_record/associations/association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +6 -2
- data/lib/active_record/associations/collection_proxy.rb +2 -2
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/join_dependency.rb +14 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +12 -3
- data/lib/active_record/associations/preloader.rb +13 -8
- data/lib/active_record/associations/preloader/association.rb +34 -30
- data/lib/active_record/associations/preloader/through_association.rb +48 -28
- data/lib/active_record/attribute_methods.rb +3 -53
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +47 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +21 -7
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +109 -11
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +88 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +79 -22
- data/lib/active_record/connection_adapters/abstract/transaction.rb +12 -4
- data/lib/active_record/connection_adapters/abstract_adapter.rb +114 -34
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +78 -73
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -2
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +10 -7
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +39 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +68 -27
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -114
- data/lib/active_record/connection_handling.rb +31 -13
- data/lib/active_record/core.rb +23 -24
- data/lib/active_record/database_configurations.rb +73 -44
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +12 -12
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +1 -1
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/middleware/database_selector.rb +3 -3
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -6
- data/lib/active_record/migration.rb +62 -44
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +10 -0
- data/lib/active_record/model_schema.rb +3 -0
- data/lib/active_record/persistence.rb +206 -13
- data/lib/active_record/querying.rb +17 -12
- data/lib/active_record/railtie.rb +0 -1
- data/lib/active_record/railties/databases.rake +127 -25
- data/lib/active_record/reflection.rb +3 -3
- data/lib/active_record/relation.rb +99 -20
- data/lib/active_record/relation/calculations.rb +38 -40
- data/lib/active_record/relation/delegation.rb +22 -30
- data/lib/active_record/relation/finder_methods.rb +17 -12
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_methods.rb +228 -76
- data/lib/active_record/relation/where_clause.rb +9 -5
- data/lib/active_record/sanitization.rb +33 -4
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +10 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +9 -13
- data/lib/active_record/tasks/database_tasks.rb +109 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
- data/lib/active_record/test_databases.rb +1 -16
- data/lib/active_record/test_fixtures.rb +1 -0
- data/lib/active_record/timestamp.rb +26 -16
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -46
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +3 -5
- data/lib/arel.rb +12 -5
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +7 -2
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +107 -131
- data/lib/arel/visitors/visitor.rb +9 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +16 -12
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -16,40 +16,32 @@ module ActiveRecord
|
|
16
16
|
|
17
17
|
# Returns the primary key column's value.
|
18
18
|
def id
|
19
|
-
|
20
|
-
primary_key = self.class.primary_key
|
21
|
-
_read_attribute(primary_key) if primary_key
|
19
|
+
_read_attribute(@primary_key)
|
22
20
|
end
|
23
21
|
|
24
22
|
# Sets the primary key column's value.
|
25
23
|
def id=(value)
|
26
|
-
|
27
|
-
primary_key = self.class.primary_key
|
28
|
-
_write_attribute(primary_key, value) if primary_key
|
24
|
+
_write_attribute(@primary_key, value)
|
29
25
|
end
|
30
26
|
|
31
27
|
# Queries the primary key column's value.
|
32
28
|
def id?
|
33
|
-
|
34
|
-
query_attribute(self.class.primary_key)
|
29
|
+
query_attribute(@primary_key)
|
35
30
|
end
|
36
31
|
|
37
32
|
# Returns the primary key column's value before type cast.
|
38
33
|
def id_before_type_cast
|
39
|
-
|
40
|
-
read_attribute_before_type_cast(self.class.primary_key)
|
34
|
+
read_attribute_before_type_cast(@primary_key)
|
41
35
|
end
|
42
36
|
|
43
37
|
# Returns the primary key column's previous value.
|
44
38
|
def id_was
|
45
|
-
|
46
|
-
attribute_was(self.class.primary_key)
|
39
|
+
attribute_was(@primary_key)
|
47
40
|
end
|
48
41
|
|
49
42
|
# Returns the primary key column's value from the database.
|
50
43
|
def id_in_database
|
51
|
-
|
52
|
-
attribute_in_database(self.class.primary_key)
|
44
|
+
attribute_in_database(@primary_key)
|
53
45
|
end
|
54
46
|
|
55
47
|
private
|
@@ -122,7 +114,7 @@ module ActiveRecord
|
|
122
114
|
#
|
123
115
|
# Project.primary_key # => "foo_id"
|
124
116
|
def primary_key=(value)
|
125
|
-
@primary_key = value && value.to_s
|
117
|
+
@primary_key = value && -value.to_s
|
126
118
|
@quoted_primary_key = nil
|
127
119
|
@attributes_builder = nil
|
128
120
|
end
|
@@ -16,8 +16,7 @@ module ActiveRecord
|
|
16
16
|
when true then true
|
17
17
|
when false, nil then false
|
18
18
|
else
|
19
|
-
|
20
|
-
if column.nil?
|
19
|
+
if !type_for_attribute(attr_name) { false }
|
21
20
|
if Numeric === value || value !~ /[^0-9]/
|
22
21
|
!value.to_i.zero?
|
23
22
|
else
|
@@ -33,7 +32,7 @@ module ActiveRecord
|
|
33
32
|
end
|
34
33
|
|
35
34
|
private
|
36
|
-
#
|
35
|
+
# Dispatch target for <tt>*?</tt> attribute methods.
|
37
36
|
def attribute?(attribute_name)
|
38
37
|
query_attribute(attribute_name)
|
39
38
|
end
|
@@ -9,14 +9,11 @@ module ActiveRecord
|
|
9
9
|
private
|
10
10
|
|
11
11
|
def define_method_attribute(name)
|
12
|
-
sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
|
13
|
-
|
14
12
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
15
13
|
generated_attribute_methods, name
|
16
14
|
) do |temp_method_name, attr_name_expr|
|
17
15
|
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
18
16
|
def #{temp_method_name}
|
19
|
-
#{sync_with_transaction_state}
|
20
17
|
name = #{attr_name_expr}
|
21
18
|
_read_attribute(name) { |n| missing_attribute(n, caller) }
|
22
19
|
end
|
@@ -30,19 +27,16 @@ module ActiveRecord
|
|
30
27
|
# to a date object, like Date.new(2004, 12, 12)).
|
31
28
|
def read_attribute(attr_name, &block)
|
32
29
|
name = attr_name.to_s
|
33
|
-
|
34
|
-
name = self.class.attribute_alias(name)
|
35
|
-
end
|
30
|
+
name = self.class.attribute_aliases[name] || name
|
36
31
|
|
37
|
-
|
38
|
-
name = primary_key if name == "id" && primary_key
|
39
|
-
sync_with_transaction_state if name == primary_key
|
32
|
+
name = @primary_key if name == "id" && @primary_key
|
40
33
|
_read_attribute(name, &block)
|
41
34
|
end
|
42
35
|
|
43
36
|
# This method exists to avoid the expensive primary_key check internally, without
|
44
37
|
# breaking compatibility with the read_attribute API
|
45
38
|
def _read_attribute(attr_name, &block) # :nodoc
|
39
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
46
40
|
@attributes.fetch_value(attr_name.to_s, &block)
|
47
41
|
end
|
48
42
|
|
@@ -13,15 +13,12 @@ module ActiveRecord
|
|
13
13
|
private
|
14
14
|
|
15
15
|
def define_method_attribute=(name)
|
16
|
-
sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
|
17
|
-
|
18
16
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
19
17
|
generated_attribute_methods, name, writer: true,
|
20
18
|
) do |temp_method_name, attr_name_expr|
|
21
19
|
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
22
20
|
def #{temp_method_name}(value)
|
23
21
|
name = #{attr_name_expr}
|
24
|
-
#{sync_with_transaction_state}
|
25
22
|
_write_attribute(name, value)
|
26
23
|
end
|
27
24
|
RUBY
|
@@ -34,31 +31,28 @@ module ActiveRecord
|
|
34
31
|
# turned into +nil+.
|
35
32
|
def write_attribute(attr_name, value)
|
36
33
|
name = attr_name.to_s
|
37
|
-
|
38
|
-
name = self.class.attribute_alias(name)
|
39
|
-
end
|
34
|
+
name = self.class.attribute_aliases[name] || name
|
40
35
|
|
41
|
-
|
42
|
-
name = primary_key if name == "id" && primary_key
|
43
|
-
sync_with_transaction_state if name == primary_key
|
36
|
+
name = @primary_key if name == "id" && @primary_key
|
44
37
|
_write_attribute(name, value)
|
45
38
|
end
|
46
39
|
|
47
40
|
# This method exists to avoid the expensive primary_key check internally, without
|
48
41
|
# breaking compatibility with the write_attribute API
|
49
42
|
def _write_attribute(attr_name, value) # :nodoc:
|
43
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
50
44
|
@attributes.write_from_user(attr_name.to_s, value)
|
51
45
|
value
|
52
46
|
end
|
53
47
|
|
54
48
|
private
|
55
49
|
def write_attribute_without_type_cast(attr_name, value)
|
56
|
-
|
57
|
-
@attributes.write_cast_value(
|
50
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
51
|
+
@attributes.write_cast_value(attr_name.to_s, value)
|
58
52
|
value
|
59
53
|
end
|
60
54
|
|
61
|
-
#
|
55
|
+
# Dispatch target for <tt>*=</tt> attribute methods.
|
62
56
|
def attribute=(attribute_name, value)
|
63
57
|
_write_attribute(attribute_name, value)
|
64
58
|
end
|
@@ -41,6 +41,9 @@ module ActiveRecord
|
|
41
41
|
# +range+ (PostgreSQL only) specifies that the type should be a range (see the
|
42
42
|
# examples below).
|
43
43
|
#
|
44
|
+
# When using a symbol for +cast_type+, extra options are forwarded to the
|
45
|
+
# constructor of the type object.
|
46
|
+
#
|
44
47
|
# ==== Examples
|
45
48
|
#
|
46
49
|
# The type detected by Active Record can be overridden.
|
@@ -112,6 +115,16 @@ module ActiveRecord
|
|
112
115
|
# my_float_range: 1.0..3.5
|
113
116
|
# }
|
114
117
|
#
|
118
|
+
# Passing options to the type constructor
|
119
|
+
#
|
120
|
+
# # app/models/my_model.rb
|
121
|
+
# class MyModel < ActiveRecord::Base
|
122
|
+
# attribute :small_int, :integer, limit: 2
|
123
|
+
# end
|
124
|
+
#
|
125
|
+
# MyModel.create(small_int: 65537)
|
126
|
+
# # => Error: 65537 is out of range for the limit of two bytes
|
127
|
+
#
|
115
128
|
# ==== Creating Custom Types
|
116
129
|
#
|
117
130
|
# Users may also define their own custom types, as long as they respond
|
@@ -272,7 +272,7 @@ module ActiveRecord
|
|
272
272
|
# or saved. If +autosave+ is +false+ only new records will be returned,
|
273
273
|
# unless the parent is/was a new record itself.
|
274
274
|
def associated_records_to_validate_or_save(association, new_record, autosave)
|
275
|
-
if new_record
|
275
|
+
if new_record || custom_validation_context?
|
276
276
|
association && association.target
|
277
277
|
elsif autosave
|
278
278
|
association.target.find_all(&:changed_for_autosave?)
|
@@ -304,7 +304,7 @@ module ActiveRecord
|
|
304
304
|
def validate_single_association(reflection)
|
305
305
|
association = association_instance_get(reflection.name)
|
306
306
|
record = association && association.reader
|
307
|
-
association_valid?(reflection, record) if record
|
307
|
+
association_valid?(reflection, record) if record && (record.changed_for_autosave? || custom_validation_context?)
|
308
308
|
end
|
309
309
|
|
310
310
|
# Validate the associated records if <tt>:validate</tt> or
|
@@ -324,7 +324,7 @@ module ActiveRecord
|
|
324
324
|
def association_valid?(reflection, record, index = nil)
|
325
325
|
return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
|
326
326
|
|
327
|
-
context = validation_context
|
327
|
+
context = validation_context if custom_validation_context?
|
328
328
|
|
329
329
|
unless valid = record.valid?(context)
|
330
330
|
if reflection.options[:autosave]
|
@@ -382,10 +382,14 @@ module ActiveRecord
|
|
382
382
|
if association = association_instance_get(reflection.name)
|
383
383
|
autosave = reflection.options[:autosave]
|
384
384
|
|
385
|
+
# By saving the instance variable in a local variable,
|
386
|
+
# we make the whole callback re-entrant.
|
387
|
+
new_record_before_save = @new_record_before_save
|
388
|
+
|
385
389
|
# reconstruct the scope now that we know the owner's id
|
386
390
|
association.reset_scope
|
387
391
|
|
388
|
-
if records = associated_records_to_validate_or_save(association,
|
392
|
+
if records = associated_records_to_validate_or_save(association, new_record_before_save, autosave)
|
389
393
|
if autosave
|
390
394
|
records_to_destroy = records.select(&:marked_for_destruction?)
|
391
395
|
records_to_destroy.each { |record| association.destroy(record) }
|
@@ -397,7 +401,7 @@ module ActiveRecord
|
|
397
401
|
|
398
402
|
saved = true
|
399
403
|
|
400
|
-
if autosave != false && (
|
404
|
+
if autosave != false && (new_record_before_save || record.new_record?)
|
401
405
|
if autosave
|
402
406
|
saved = association.insert_record(record, false)
|
403
407
|
elsif !reflection.nested?
|
@@ -412,7 +416,7 @@ module ActiveRecord
|
|
412
416
|
saved = record.save(validate: false)
|
413
417
|
end
|
414
418
|
|
415
|
-
raise
|
419
|
+
raise(RecordInvalid.new(association.owner)) unless saved
|
416
420
|
end
|
417
421
|
end
|
418
422
|
end
|
@@ -457,10 +461,16 @@ module ActiveRecord
|
|
457
461
|
# If the record is new or it has changed, returns true.
|
458
462
|
def record_changed?(reflection, record, key)
|
459
463
|
record.new_record? ||
|
460
|
-
|
464
|
+
association_foreign_key_changed?(reflection, record, key) ||
|
461
465
|
record.will_save_change_to_attribute?(reflection.foreign_key)
|
462
466
|
end
|
463
467
|
|
468
|
+
def association_foreign_key_changed?(reflection, record, key)
|
469
|
+
return false if reflection.through_reflection?
|
470
|
+
|
471
|
+
record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key
|
472
|
+
end
|
473
|
+
|
464
474
|
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
|
465
475
|
#
|
466
476
|
# In addition, it will destroy the association if it was marked for destruction.
|
@@ -489,6 +499,10 @@ module ActiveRecord
|
|
489
499
|
end
|
490
500
|
end
|
491
501
|
|
502
|
+
def custom_validation_context?
|
503
|
+
validation_context && [:create, :update].exclude?(validation_context)
|
504
|
+
end
|
505
|
+
|
492
506
|
def _ensure_no_duplicate_errors
|
493
507
|
errors.messages.each_key do |attribute|
|
494
508
|
errors[attribute].uniq!
|
data/lib/active_record/base.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require "thread"
|
4
4
|
require "concurrent/map"
|
5
5
|
require "monitor"
|
6
|
+
require "weakref"
|
6
7
|
|
7
8
|
module ActiveRecord
|
8
9
|
# Raised when a connection could not be obtained within the connection
|
@@ -19,6 +20,26 @@ module ActiveRecord
|
|
19
20
|
end
|
20
21
|
|
21
22
|
module ConnectionAdapters
|
23
|
+
module AbstractPool # :nodoc:
|
24
|
+
def get_schema_cache(connection)
|
25
|
+
@schema_cache ||= SchemaCache.new(connection)
|
26
|
+
@schema_cache.connection = connection
|
27
|
+
@schema_cache
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_schema_cache(cache)
|
31
|
+
@schema_cache = cache
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class NullPool # :nodoc:
|
36
|
+
include ConnectionAdapters::AbstractPool
|
37
|
+
|
38
|
+
def initialize
|
39
|
+
@schema_cache = nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
22
43
|
# Connection pool base class for managing Active Record database
|
23
44
|
# connections.
|
24
45
|
#
|
@@ -294,23 +315,59 @@ module ActiveRecord
|
|
294
315
|
@frequency = frequency
|
295
316
|
end
|
296
317
|
|
318
|
+
@mutex = Mutex.new
|
319
|
+
@pools = {}
|
320
|
+
@threads = {}
|
321
|
+
|
322
|
+
class << self
|
323
|
+
def register_pool(pool, frequency) # :nodoc:
|
324
|
+
@mutex.synchronize do
|
325
|
+
unless @threads[frequency]&.alive?
|
326
|
+
@threads[frequency] = spawn_thread(frequency)
|
327
|
+
end
|
328
|
+
@pools[frequency] ||= []
|
329
|
+
@pools[frequency] << WeakRef.new(pool)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
private
|
334
|
+
|
335
|
+
def spawn_thread(frequency)
|
336
|
+
Thread.new(frequency) do |t|
|
337
|
+
running = true
|
338
|
+
while running
|
339
|
+
sleep t
|
340
|
+
@mutex.synchronize do
|
341
|
+
@pools[frequency].select!(&:weakref_alive?)
|
342
|
+
@pools[frequency].each do |p|
|
343
|
+
p.reap
|
344
|
+
p.flush
|
345
|
+
rescue WeakRef::RefError
|
346
|
+
end
|
347
|
+
|
348
|
+
if @pools[frequency].empty?
|
349
|
+
@pools.delete(frequency)
|
350
|
+
@threads.delete(frequency)
|
351
|
+
running = false
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
297
359
|
def run
|
298
360
|
return unless frequency && frequency > 0
|
299
|
-
|
300
|
-
loop do
|
301
|
-
sleep t
|
302
|
-
p.reap
|
303
|
-
p.flush
|
304
|
-
end
|
305
|
-
}
|
361
|
+
self.class.register_pool(pool, frequency)
|
306
362
|
end
|
307
363
|
end
|
308
364
|
|
309
365
|
include MonitorMixin
|
310
366
|
include QueryCache::ConnectionPoolConfiguration
|
367
|
+
include ConnectionAdapters::AbstractPool
|
311
368
|
|
312
369
|
attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
|
313
|
-
attr_reader :spec, :
|
370
|
+
attr_reader :spec, :size, :reaper
|
314
371
|
|
315
372
|
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
|
316
373
|
# object which describes database connection information (e.g. adapter,
|
@@ -379,7 +436,7 @@ module ActiveRecord
|
|
379
436
|
# #connection can be called any number of times; the connection is
|
380
437
|
# held in a cache keyed by a thread.
|
381
438
|
def connection
|
382
|
-
@thread_cached_conns[connection_cache_key(
|
439
|
+
@thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
|
383
440
|
end
|
384
441
|
|
385
442
|
# Returns true if there is an open connection being used for the current thread.
|
@@ -388,7 +445,7 @@ module ActiveRecord
|
|
388
445
|
# #connection or #with_connection methods. Connections obtained through
|
389
446
|
# #checkout will not be detected by #active_connection?
|
390
447
|
def active_connection?
|
391
|
-
@thread_cached_conns[connection_cache_key(
|
448
|
+
@thread_cached_conns[connection_cache_key(current_thread)]
|
392
449
|
end
|
393
450
|
|
394
451
|
# Signal that the thread is finished with the current connection.
|
@@ -423,6 +480,21 @@ module ActiveRecord
|
|
423
480
|
synchronize { @connections.any? }
|
424
481
|
end
|
425
482
|
|
483
|
+
# Returns an array containing the connections currently in the pool.
|
484
|
+
# Access to the array does not require synchronization on the pool because
|
485
|
+
# the array is newly created and not retained by the pool.
|
486
|
+
#
|
487
|
+
# However; this method bypasses the ConnectionPool's thread-safe connection
|
488
|
+
# access pattern. A returned connection may be owned by another thread,
|
489
|
+
# unowned, or by happen-stance owned by the calling thread.
|
490
|
+
#
|
491
|
+
# Calling methods on a connection without ownership is subject to the
|
492
|
+
# thread-safety guarantees of the underlying method. Many of the methods
|
493
|
+
# on connection adapter classes are inherently multi-thread unsafe.
|
494
|
+
def connections
|
495
|
+
synchronize { @connections.dup }
|
496
|
+
end
|
497
|
+
|
426
498
|
# Disconnects all connections in the pool, and clears the pool.
|
427
499
|
#
|
428
500
|
# Raises:
|
@@ -578,6 +650,7 @@ module ActiveRecord
|
|
578
650
|
# or a thread dies unexpectedly.
|
579
651
|
def reap
|
580
652
|
stale_connections = synchronize do
|
653
|
+
return unless @connections
|
581
654
|
@connections.select do |conn|
|
582
655
|
conn.in_use? && !conn.owner.alive?
|
583
656
|
end.each do |conn|
|
@@ -602,6 +675,7 @@ module ActiveRecord
|
|
602
675
|
return if minimum_idle.nil?
|
603
676
|
|
604
677
|
idle_connections = synchronize do
|
678
|
+
return unless @connections
|
605
679
|
@connections.select do |conn|
|
606
680
|
!conn.in_use? && conn.seconds_idle >= minimum_idle
|
607
681
|
end.each do |conn|
|
@@ -668,6 +742,10 @@ module ActiveRecord
|
|
668
742
|
thread
|
669
743
|
end
|
670
744
|
|
745
|
+
def current_thread
|
746
|
+
@lock_thread || Thread.current
|
747
|
+
end
|
748
|
+
|
671
749
|
# Take control of all existing connections so a "group" action such as
|
672
750
|
# reload/disconnect can be performed safely. It is no longer enough to
|
673
751
|
# wrap it in +synchronize+ because some pool's actions are allowed
|
@@ -809,7 +887,7 @@ module ActiveRecord
|
|
809
887
|
|
810
888
|
def new_connection
|
811
889
|
Base.send(spec.adapter_method, spec.config).tap do |conn|
|
812
|
-
conn.
|
890
|
+
conn.check_version
|
813
891
|
end
|
814
892
|
end
|
815
893
|
|
@@ -946,6 +1024,26 @@ module ActiveRecord
|
|
946
1024
|
ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
|
947
1025
|
end
|
948
1026
|
|
1027
|
+
def prevent_writes # :nodoc:
|
1028
|
+
Thread.current[:prevent_writes]
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
def prevent_writes=(prevent_writes) # :nodoc:
|
1032
|
+
Thread.current[:prevent_writes] = prevent_writes
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
# Prevent writing to the database regardless of role.
|
1036
|
+
#
|
1037
|
+
# In some cases you may want to prevent writes to the database
|
1038
|
+
# even if you are on a database that can write. `while_preventing_writes`
|
1039
|
+
# will prevent writes to the database for the duration of the block.
|
1040
|
+
def while_preventing_writes(enabled = true)
|
1041
|
+
original, self.prevent_writes = self.prevent_writes, enabled
|
1042
|
+
yield
|
1043
|
+
ensure
|
1044
|
+
self.prevent_writes = original
|
1045
|
+
end
|
1046
|
+
|
949
1047
|
def connection_pool_list
|
950
1048
|
owner_to_pool.values.compact
|
951
1049
|
end
|