activerecord 5.1.7 → 5.2.4.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 +4 -4
- data/CHANGELOG.md +556 -685
- 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 +40 -63
- data/lib/active_record/associations/alias_tracker.rb +19 -27
- data/lib/active_record/associations/association.rb +41 -37
- data/lib/active_record/associations/association_scope.rb +38 -50
- data/lib/active_record/associations/belongs_to_association.rb +27 -8
- 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 +12 -4
- data/lib/active_record/associations/builder/collection_association.rb +3 -3
- 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 +59 -47
- data/lib/active_record/associations/collection_proxy.rb +20 -49
- data/lib/active_record/associations/foreign_association.rb +2 -0
- data/lib/active_record/associations/has_many_association.rb +12 -1
- data/lib/active_record/associations/has_many_through_association.rb +36 -30
- data/lib/active_record/associations/has_one_association.rb +12 -1
- data/lib/active_record/associations/has_one_through_association.rb +13 -8
- data/lib/active_record/associations/join_dependency.rb +48 -93
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -63
- 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 +18 -38
- 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/singular_association.rb +14 -16
- data/lib/active_record/associations/through_association.rb +26 -11
- 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 +30 -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 +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 +21 -9
- data/lib/active_record/attributes.rb +6 -5
- data/lib/active_record/autosave_association.rb +35 -19
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/callbacks.rb +8 -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 +12 -8
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +139 -41
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +174 -33
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +15 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -31
- 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 +64 -6
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +152 -81
- data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
- data/lib/active_record/connection_adapters/abstract_adapter.rb +84 -97
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +92 -165
- 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 +13 -2
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +47 -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.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -0
- 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 -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 +18 -0
- 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 +233 -111
- 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 +57 -73
- 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 +22 -0
- 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 +81 -94
- 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 +10 -3
- 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 +42 -3
- 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 +5 -3
- 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.rb +189 -139
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +47 -9
- data/lib/active_record/migration/join_table.rb +2 -0
- 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 +167 -16
- data/lib/active_record/query_cache.rb +6 -8
- data/lib/active_record/querying.rb +4 -2
- 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 +46 -36
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +108 -194
- data/lib/active_record/relation.rb +120 -214
- data/lib/active_record/relation/batches.rb +20 -5
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/calculations.rb +45 -19
- data/lib/active_record/relation/delegation.rb +45 -27
- data/lib/active_record/relation/finder_methods.rb +75 -76
- 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 +128 -99
- 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 -68
- 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 +9 -8
- data/lib/active_record/scoping/default.rb +8 -9
- data/lib/active_record/scoping/named.rb +23 -7
- data/lib/active_record/secure_token.rb +2 -0
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +23 -13
- 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 +25 -14
- 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 +6 -6
- data/lib/active_record/touch_later.rb +2 -0
- data/lib/active_record/transactions.rb +33 -28
- 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 +2 -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 +35 -5
- 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 +23 -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 -122
- data/lib/active_record/attribute_set.rb +0 -113
- 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/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
|
module ActiveRecord
|
2
4
|
module AttributeMethods
|
3
5
|
module TimeZoneConversion
|
@@ -54,17 +56,13 @@ module ActiveRecord
|
|
54
56
|
extend ActiveSupport::Concern
|
55
57
|
|
56
58
|
included do
|
57
|
-
mattr_accessor :time_zone_aware_attributes, instance_writer: false
|
58
|
-
self.time_zone_aware_attributes = false
|
59
|
-
|
60
|
-
class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false
|
61
|
-
self.skip_time_zone_conversion_for_attributes = []
|
59
|
+
mattr_accessor :time_zone_aware_attributes, instance_writer: false, default: false
|
62
60
|
|
63
|
-
class_attribute :
|
64
|
-
|
61
|
+
class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false, default: []
|
62
|
+
class_attribute :time_zone_aware_types, instance_writer: false, default: [ :datetime, :time ]
|
65
63
|
end
|
66
64
|
|
67
|
-
module ClassMethods
|
65
|
+
module ClassMethods # :nodoc:
|
68
66
|
private
|
69
67
|
|
70
68
|
def inherited(subclass)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module AttributeMethods
|
3
5
|
module Write
|
@@ -7,17 +9,19 @@ module ActiveRecord
|
|
7
9
|
attribute_method_suffix "="
|
8
10
|
end
|
9
11
|
|
10
|
-
module ClassMethods
|
12
|
+
module ClassMethods # :nodoc:
|
11
13
|
private
|
12
14
|
|
13
15
|
def define_method_attribute=(name)
|
14
16
|
safe_name = name.unpack("h*".freeze).first
|
15
17
|
ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
|
18
|
+
sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
|
16
19
|
|
17
20
|
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
|
18
21
|
def __temp__#{safe_name}=(value)
|
19
22
|
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
|
20
|
-
|
23
|
+
#{sync_with_transaction_state}
|
24
|
+
_write_attribute(name, value)
|
21
25
|
end
|
22
26
|
alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
|
23
27
|
undef_method :__temp__#{safe_name}=
|
@@ -35,21 +39,29 @@ module ActiveRecord
|
|
35
39
|
attr_name.to_s
|
36
40
|
end
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
|
42
|
+
primary_key = self.class.primary_key
|
43
|
+
name = primary_key if name == "id".freeze && primary_key
|
44
|
+
sync_with_transaction_state if name == primary_key
|
45
|
+
_write_attribute(name, value)
|
41
46
|
end
|
42
47
|
|
43
|
-
|
44
|
-
|
45
|
-
|
48
|
+
# This method exists to avoid the expensive primary_key check internally, without
|
49
|
+
# breaking compatibility with the write_attribute API
|
50
|
+
def _write_attribute(attr_name, value) # :nodoc:
|
51
|
+
@attributes.write_from_user(attr_name.to_s, value)
|
46
52
|
value
|
47
53
|
end
|
48
54
|
|
49
55
|
private
|
56
|
+
def write_attribute_without_type_cast(attr_name, value)
|
57
|
+
name = attr_name.to_s
|
58
|
+
@attributes.write_cast_value(name, value)
|
59
|
+
value
|
60
|
+
end
|
61
|
+
|
50
62
|
# Handle *= for method_missing.
|
51
63
|
def attribute=(attribute_name, value)
|
52
|
-
|
64
|
+
_write_attribute(attribute_name, value)
|
53
65
|
end
|
54
66
|
end
|
55
67
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_model/attribute/user_provided_default"
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
# See ActiveRecord::Attributes::ClassMethods for documentation
|
@@ -6,8 +8,7 @@ module ActiveRecord
|
|
6
8
|
extend ActiveSupport::Concern
|
7
9
|
|
8
10
|
included do
|
9
|
-
class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false # :internal:
|
10
|
-
self.attributes_to_define_after_schema_loads = {}
|
11
|
+
class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false, default: {} # :internal:
|
11
12
|
end
|
12
13
|
|
13
14
|
module ClassMethods
|
@@ -249,14 +250,14 @@ module ActiveRecord
|
|
249
250
|
if value == NO_DEFAULT_PROVIDED
|
250
251
|
default_attribute = _default_attributes[name].with_type(type)
|
251
252
|
elsif from_user
|
252
|
-
default_attribute = Attribute::UserProvidedDefault.new(
|
253
|
+
default_attribute = ActiveModel::Attribute::UserProvidedDefault.new(
|
253
254
|
name,
|
254
255
|
value,
|
255
256
|
type,
|
256
257
|
_default_attributes.fetch(name.to_s) { nil },
|
257
258
|
)
|
258
259
|
else
|
259
|
-
default_attribute = Attribute.from_database(name, value, type)
|
260
|
+
default_attribute = ActiveModel::Attribute.from_database(name, value, type)
|
260
261
|
end
|
261
262
|
_default_attributes[name] = default_attribute
|
262
263
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
# = Active Record Autosave Association
|
3
5
|
#
|
@@ -140,8 +142,7 @@ module ActiveRecord
|
|
140
142
|
|
141
143
|
included do
|
142
144
|
Associations::Builder::Association.extensions << AssociationBuilderExtension
|
143
|
-
mattr_accessor :index_nested_attribute_errors, instance_writer: false
|
144
|
-
self.index_nested_attribute_errors = false
|
145
|
+
mattr_accessor :index_nested_attribute_errors, instance_writer: false, default: false
|
145
146
|
end
|
146
147
|
|
147
148
|
module ClassMethods # :nodoc:
|
@@ -216,13 +217,7 @@ module ActiveRecord
|
|
216
217
|
method = :validate_single_association
|
217
218
|
end
|
218
219
|
|
219
|
-
define_non_cyclic_method(validation_method)
|
220
|
-
send(method, reflection)
|
221
|
-
# TODO: remove the following line as soon as the return value of
|
222
|
-
# callbacks is ignored, that is, returning `false` does not
|
223
|
-
# display a deprecation warning or halts the callback chain.
|
224
|
-
true
|
225
|
-
end
|
220
|
+
define_non_cyclic_method(validation_method) { send(method, reflection) }
|
226
221
|
validate validation_method
|
227
222
|
after_validation :_ensure_no_duplicate_errors
|
228
223
|
end
|
@@ -277,7 +272,7 @@ module ActiveRecord
|
|
277
272
|
# or saved. If +autosave+ is +false+ only new records will be returned,
|
278
273
|
# unless the parent is/was a new record itself.
|
279
274
|
def associated_records_to_validate_or_save(association, new_record, autosave)
|
280
|
-
if new_record
|
275
|
+
if new_record || custom_validation_context?
|
281
276
|
association && association.target
|
282
277
|
elsif autosave
|
283
278
|
association.target.find_all(&:changed_for_autosave?)
|
@@ -309,7 +304,7 @@ module ActiveRecord
|
|
309
304
|
def validate_single_association(reflection)
|
310
305
|
association = association_instance_get(reflection.name)
|
311
306
|
record = association && association.reader
|
312
|
-
association_valid?(reflection, record) if record
|
307
|
+
association_valid?(reflection, record) if record && (record.changed_for_autosave? || custom_validation_context?)
|
313
308
|
end
|
314
309
|
|
315
310
|
# Validate the associated records if <tt>:validate</tt> or
|
@@ -329,7 +324,7 @@ module ActiveRecord
|
|
329
324
|
def association_valid?(reflection, record, index = nil)
|
330
325
|
return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
|
331
326
|
|
332
|
-
context = validation_context
|
327
|
+
context = validation_context if custom_validation_context?
|
333
328
|
|
334
329
|
unless valid = record.valid?(context)
|
335
330
|
if reflection.options[:autosave]
|
@@ -369,7 +364,6 @@ module ActiveRecord
|
|
369
364
|
# association whether or not the parent was a new record before saving.
|
370
365
|
def before_save_collection_association
|
371
366
|
@new_record_before_save = new_record?
|
372
|
-
true
|
373
367
|
end
|
374
368
|
|
375
369
|
def after_save_collection_association
|
@@ -388,10 +382,14 @@ module ActiveRecord
|
|
388
382
|
if association = association_instance_get(reflection.name)
|
389
383
|
autosave = reflection.options[:autosave]
|
390
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
|
+
|
391
389
|
# reconstruct the scope now that we know the owner's id
|
392
|
-
association.reset_scope
|
390
|
+
association.reset_scope
|
393
391
|
|
394
|
-
if records = associated_records_to_validate_or_save(association,
|
392
|
+
if records = associated_records_to_validate_or_save(association, new_record_before_save, autosave)
|
395
393
|
if autosave
|
396
394
|
records_to_destroy = records.select(&:marked_for_destruction?)
|
397
395
|
records_to_destroy.each { |record| association.destroy(record) }
|
@@ -403,11 +401,16 @@ module ActiveRecord
|
|
403
401
|
|
404
402
|
saved = true
|
405
403
|
|
406
|
-
if autosave != false && (
|
404
|
+
if autosave != false && (new_record_before_save || record.new_record?)
|
407
405
|
if autosave
|
408
406
|
saved = association.insert_record(record, false)
|
409
|
-
|
410
|
-
association.insert_record(record)
|
407
|
+
elsif !reflection.nested?
|
408
|
+
association_saved = association.insert_record(record)
|
409
|
+
|
410
|
+
if reflection.validate?
|
411
|
+
errors.add(reflection.name) unless association_saved
|
412
|
+
saved = association_saved
|
413
|
+
end
|
411
414
|
end
|
412
415
|
elsif autosave
|
413
416
|
saved = record.save(validate: false)
|
@@ -442,6 +445,9 @@ module ActiveRecord
|
|
442
445
|
if (autosave && record.changed_for_autosave?) || new_record? || record_changed?(reflection, record, key)
|
443
446
|
unless reflection.through_reflection
|
444
447
|
record[reflection.foreign_key] = key
|
448
|
+
if inverse_reflection = reflection.inverse_of
|
449
|
+
record.association(inverse_reflection.name).loaded!
|
450
|
+
end
|
445
451
|
end
|
446
452
|
|
447
453
|
saved = record.save(validate: !autosave)
|
@@ -455,10 +461,16 @@ module ActiveRecord
|
|
455
461
|
# If the record is new or it has changed, returns true.
|
456
462
|
def record_changed?(reflection, record, key)
|
457
463
|
record.new_record? ||
|
458
|
-
|
464
|
+
association_foreign_key_changed?(reflection, record, key) ||
|
459
465
|
record.will_save_change_to_attribute?(reflection.foreign_key)
|
460
466
|
end
|
461
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
|
+
|
462
474
|
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
|
463
475
|
#
|
464
476
|
# In addition, it will destroy the association if it was marked for destruction.
|
@@ -487,6 +499,10 @@ module ActiveRecord
|
|
487
499
|
end
|
488
500
|
end
|
489
501
|
|
502
|
+
def custom_validation_context?
|
503
|
+
validation_context && [:create, :update].exclude?(validation_context)
|
504
|
+
end
|
505
|
+
|
490
506
|
def _ensure_no_duplicate_errors
|
491
507
|
errors.messages.each_key do |attribute|
|
492
508
|
errors[attribute].uniq!
|
data/lib/active_record/base.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
# = Active Record \Callbacks
|
3
5
|
#
|
@@ -96,9 +98,9 @@ module ActiveRecord
|
|
96
98
|
# == Types of callbacks
|
97
99
|
#
|
98
100
|
# There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
|
99
|
-
# inline methods (using a proc)
|
101
|
+
# inline methods (using a proc). Method references and callback objects
|
100
102
|
# are the recommended approaches, inline methods using a proc are sometimes appropriate (such as for
|
101
|
-
# creating mix-ins)
|
103
|
+
# creating mix-ins).
|
102
104
|
#
|
103
105
|
# The method reference callbacks work by specifying a protected or private method available in the object, like this:
|
104
106
|
#
|
@@ -230,7 +232,7 @@ module ActiveRecord
|
|
230
232
|
#
|
231
233
|
# For example:
|
232
234
|
#
|
233
|
-
# class Topic
|
235
|
+
# class Topic < ActiveRecord::Base
|
234
236
|
# has_many :children
|
235
237
|
#
|
236
238
|
# after_save :log_children
|
@@ -238,7 +240,7 @@ module ActiveRecord
|
|
238
240
|
#
|
239
241
|
# private
|
240
242
|
#
|
241
|
-
# def
|
243
|
+
# def log_children
|
242
244
|
# # Child processing
|
243
245
|
# end
|
244
246
|
#
|
@@ -255,7 +257,7 @@ module ActiveRecord
|
|
255
257
|
#
|
256
258
|
# For example:
|
257
259
|
#
|
258
|
-
# class Topic
|
260
|
+
# class Topic < ActiveRecord::Base
|
259
261
|
# has_many :children
|
260
262
|
#
|
261
263
|
# after_commit :log_children
|
@@ -263,7 +265,7 @@ module ActiveRecord
|
|
263
265
|
#
|
264
266
|
# private
|
265
267
|
#
|
266
|
-
# def
|
268
|
+
# def log_children
|
267
269
|
# # Child processing
|
268
270
|
# end
|
269
271
|
#
|
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module CollectionCacheKey
|
3
5
|
def collection_cache_key(collection = all, timestamp_column = :updated_at) # :nodoc:
|
4
|
-
query_signature = Digest
|
6
|
+
query_signature = ActiveSupport::Digest.hexdigest(collection.to_sql)
|
5
7
|
key = "#{collection.model_name.cache_key}/query-#{query_signature}"
|
6
8
|
|
7
9
|
if collection.loaded? || collection.distinct_value
|
@@ -10,24 +12,26 @@ module ActiveRecord
|
|
10
12
|
timestamp = collection.max_by(×tamp_column)._read_attribute(timestamp_column)
|
11
13
|
end
|
12
14
|
else
|
13
|
-
|
14
|
-
|
15
|
+
if collection.eager_loading?
|
16
|
+
collection = collection.send(:apply_join_dependency)
|
17
|
+
end
|
18
|
+
column_type = type_for_attribute(timestamp_column)
|
19
|
+
column = connection.column_name_from_arel_node(collection.arel_attribute(timestamp_column))
|
15
20
|
select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
|
16
21
|
|
17
22
|
if collection.has_limit_or_offset?
|
18
|
-
query = collection.
|
19
|
-
query.select_values = [column]
|
23
|
+
query = collection.select("#{column} AS collection_cache_key_timestamp")
|
20
24
|
subquery_alias = "subquery_for_cache_key"
|
21
|
-
subquery_column = "#{subquery_alias}
|
25
|
+
subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
|
22
26
|
subquery = query.arel.as(subquery_alias)
|
23
|
-
arel = Arel::SelectManager.new(
|
27
|
+
arel = Arel::SelectManager.new(subquery).project(select_values % subquery_column)
|
24
28
|
else
|
25
29
|
query = collection.unscope(:order)
|
26
30
|
query.select_values = [select_values % column]
|
27
31
|
arel = query.arel
|
28
32
|
end
|
29
33
|
|
30
|
-
result = connection.select_one(arel, nil
|
34
|
+
result = connection.select_one(arel, nil)
|
31
35
|
|
32
36
|
if result.blank?
|
33
37
|
size = 0
|
@@ -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
|
@@ -270,7 +267,7 @@ module ActiveRecord
|
|
270
267
|
# Connections must be leased while holding the main pool mutex. This is
|
271
268
|
# an internal subclass that also +.leases+ returned connections while
|
272
269
|
# still in queue's critical section (queue synchronizes with the same
|
273
|
-
#
|
270
|
+
# <tt>@lock</tt> as the main pool) so that a returned connection is already
|
274
271
|
# leased and there is no need to re-enter synchronized block.
|
275
272
|
class ConnectionLeasingQueue < Queue # :nodoc:
|
276
273
|
include BiasableQueue
|
@@ -283,12 +280,12 @@ module ActiveRecord
|
|
283
280
|
end
|
284
281
|
end
|
285
282
|
|
286
|
-
# Every +frequency+ seconds, the reaper will call +reap+
|
287
|
-
# A reaper instantiated with a
|
288
|
-
# connection pool.
|
283
|
+
# Every +frequency+ seconds, the reaper will call +reap+ and +flush+ on
|
284
|
+
# +pool+. A reaper instantiated with a zero frequency will never reap
|
285
|
+
# the connection pool.
|
289
286
|
#
|
290
|
-
# Configure the frequency by setting
|
291
|
-
#
|
287
|
+
# Configure the frequency by setting +reaping_frequency+ in your database
|
288
|
+
# yaml file (default 60 seconds).
|
292
289
|
class Reaper
|
293
290
|
attr_reader :pool, :frequency
|
294
291
|
|
@@ -298,11 +295,12 @@ module ActiveRecord
|
|
298
295
|
end
|
299
296
|
|
300
297
|
def run
|
301
|
-
return unless frequency
|
298
|
+
return unless frequency && frequency > 0
|
302
299
|
Thread.new(frequency, pool) { |t, p|
|
303
300
|
loop do
|
304
301
|
sleep t
|
305
302
|
p.reap
|
303
|
+
p.flush
|
306
304
|
end
|
307
305
|
}
|
308
306
|
end
|
@@ -312,7 +310,7 @@ module ActiveRecord
|
|
312
310
|
include QueryCache::ConnectionPoolConfiguration
|
313
311
|
|
314
312
|
attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
|
315
|
-
attr_reader :spec, :
|
313
|
+
attr_reader :spec, :size, :reaper
|
316
314
|
|
317
315
|
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
|
318
316
|
# object which describes database connection information (e.g. adapter,
|
@@ -326,8 +324,10 @@ module ActiveRecord
|
|
326
324
|
@spec = spec
|
327
325
|
|
328
326
|
@checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
|
329
|
-
@
|
330
|
-
|
327
|
+
if @idle_timeout = spec.config.fetch(:idle_timeout, 300)
|
328
|
+
@idle_timeout = @idle_timeout.to_f
|
329
|
+
@idle_timeout = nil if @idle_timeout <= 0
|
330
|
+
end
|
331
331
|
|
332
332
|
# default max pool size to 5
|
333
333
|
@size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
|
@@ -340,7 +340,7 @@ module ActiveRecord
|
|
340
340
|
# then that +thread+ does indeed own that +conn+. However, an absence of a such
|
341
341
|
# mapping does not mean that the +thread+ doesn't own the said connection. In
|
342
342
|
# that case +conn.owner+ attr should be consulted.
|
343
|
-
# Access and modification of
|
343
|
+
# Access and modification of <tt>@thread_cached_conns</tt> does not require
|
344
344
|
# synchronization.
|
345
345
|
@thread_cached_conns = Concurrent::Map.new(initial_capacity: @size)
|
346
346
|
|
@@ -357,6 +357,12 @@ module ActiveRecord
|
|
357
357
|
@available = ConnectionLeasingQueue.new self
|
358
358
|
|
359
359
|
@lock_thread = false
|
360
|
+
|
361
|
+
# +reaping_frequency+ is configurable mostly for historical reasons, but it could
|
362
|
+
# also be useful if someone wants a very low +idle_timeout+.
|
363
|
+
reaping_frequency = spec.config.fetch(:reaping_frequency, 60)
|
364
|
+
@reaper = Reaper.new(self, reaping_frequency && reaping_frequency.to_f)
|
365
|
+
@reaper.run
|
360
366
|
end
|
361
367
|
|
362
368
|
def lock_thread=(lock_thread)
|
@@ -373,7 +379,7 @@ module ActiveRecord
|
|
373
379
|
# #connection can be called any number of times; the connection is
|
374
380
|
# held in a cache keyed by a thread.
|
375
381
|
def connection
|
376
|
-
@thread_cached_conns[connection_cache_key(
|
382
|
+
@thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
|
377
383
|
end
|
378
384
|
|
379
385
|
# Returns true if there is an open connection being used for the current thread.
|
@@ -382,7 +388,7 @@ module ActiveRecord
|
|
382
388
|
# #connection or #with_connection methods. Connections obtained through
|
383
389
|
# #checkout will not be detected by #active_connection?
|
384
390
|
def active_connection?
|
385
|
-
@thread_cached_conns[connection_cache_key(
|
391
|
+
@thread_cached_conns[connection_cache_key(current_thread)]
|
386
392
|
end
|
387
393
|
|
388
394
|
# Signal that the thread is finished with the current connection.
|
@@ -417,6 +423,21 @@ module ActiveRecord
|
|
417
423
|
synchronize { @connections.any? }
|
418
424
|
end
|
419
425
|
|
426
|
+
# Returns an array containing the connections currently in the pool.
|
427
|
+
# Access to the array does not require synchronization on the pool because
|
428
|
+
# the array is newly created and not retained by the pool.
|
429
|
+
#
|
430
|
+
# However; this method bypasses the ConnectionPool's thread-safe connection
|
431
|
+
# access pattern. A returned connection may be owned by another thread,
|
432
|
+
# unowned, or by happen-stance owned by the calling thread.
|
433
|
+
#
|
434
|
+
# Calling methods on a connection without ownership is subject to the
|
435
|
+
# thread-safety guarantees of the underlying method. Many of the methods
|
436
|
+
# on connection adapter classes are inherently multi-thread unsafe.
|
437
|
+
def connections
|
438
|
+
synchronize { @connections.dup }
|
439
|
+
end
|
440
|
+
|
420
441
|
# Disconnects all connections in the pool, and clears the pool.
|
421
442
|
#
|
422
443
|
# Raises:
|
@@ -449,6 +470,21 @@ module ActiveRecord
|
|
449
470
|
disconnect(false)
|
450
471
|
end
|
451
472
|
|
473
|
+
# Discards all connections in the pool (even if they're currently
|
474
|
+
# leased!), along with the pool itself. Any further interaction with the
|
475
|
+
# pool (except #spec and #schema_cache) is undefined.
|
476
|
+
#
|
477
|
+
# See AbstractAdapter#discard!
|
478
|
+
def discard! # :nodoc:
|
479
|
+
synchronize do
|
480
|
+
return if @connections.nil? # already discarded
|
481
|
+
@connections.each do |conn|
|
482
|
+
conn.discard!
|
483
|
+
end
|
484
|
+
@connections = @available = @thread_cached_conns = nil
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
452
488
|
# Clears the cache which maps classes and re-connects connections that
|
453
489
|
# require reloading.
|
454
490
|
#
|
@@ -574,6 +610,35 @@ module ActiveRecord
|
|
574
610
|
end
|
575
611
|
end
|
576
612
|
|
613
|
+
# Disconnect all connections that have been idle for at least
|
614
|
+
# +minimum_idle+ seconds. Connections currently checked out, or that were
|
615
|
+
# checked in less than +minimum_idle+ seconds ago, are unaffected.
|
616
|
+
def flush(minimum_idle = @idle_timeout)
|
617
|
+
return if minimum_idle.nil?
|
618
|
+
|
619
|
+
idle_connections = synchronize do
|
620
|
+
@connections.select do |conn|
|
621
|
+
!conn.in_use? && conn.seconds_idle >= minimum_idle
|
622
|
+
end.each do |conn|
|
623
|
+
conn.lease
|
624
|
+
|
625
|
+
@available.delete conn
|
626
|
+
@connections.delete conn
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
630
|
+
idle_connections.each do |conn|
|
631
|
+
conn.disconnect!
|
632
|
+
end
|
633
|
+
end
|
634
|
+
|
635
|
+
# Disconnect all currently idle connections. Connections currently checked
|
636
|
+
# out are unaffected.
|
637
|
+
def flush!
|
638
|
+
reap
|
639
|
+
flush(-1)
|
640
|
+
end
|
641
|
+
|
577
642
|
def num_waiting_in_queue # :nodoc:
|
578
643
|
@available.num_waiting
|
579
644
|
end
|
@@ -618,6 +683,10 @@ module ActiveRecord
|
|
618
683
|
thread
|
619
684
|
end
|
620
685
|
|
686
|
+
def current_thread
|
687
|
+
@lock_thread || Thread.current
|
688
|
+
end
|
689
|
+
|
621
690
|
# Take control of all existing connections so a "group" action such as
|
622
691
|
# reload/disconnect can be performed safely. It is no longer enough to
|
623
692
|
# wrap it in +synchronize+ because some pool's actions are allowed
|
@@ -681,7 +750,7 @@ module ActiveRecord
|
|
681
750
|
# this block can't be easily moved into attempt_to_checkout_all_existing_connections's
|
682
751
|
# rescue block, because doing so would put it outside of synchronize section, without
|
683
752
|
# being in a critical section thread_report might become inaccurate
|
684
|
-
msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds"
|
753
|
+
msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds".dup
|
685
754
|
|
686
755
|
thread_report = []
|
687
756
|
@connections.each do |conn|
|
@@ -736,10 +805,10 @@ module ActiveRecord
|
|
736
805
|
# Implementation detail: the connection returned by +acquire_connection+
|
737
806
|
# will already be "+connection.lease+ -ed" to the current thread.
|
738
807
|
def acquire_connection(checkout_timeout)
|
739
|
-
# NOTE: we rely on
|
808
|
+
# NOTE: we rely on <tt>@available.poll</tt> and +try_to_checkout_new_connection+ to
|
740
809
|
# +conn.lease+ the returned connection (and to do this in a +synchronized+
|
741
810
|
# section). This is not the cleanest implementation, as ideally we would
|
742
|
-
# <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to
|
811
|
+
# <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to <tt>@available.poll</tt>
|
743
812
|
# and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
|
744
813
|
# of the said methods and avoid an additional +synchronize+ overhead.
|
745
814
|
if conn = @available.poll || try_to_checkout_new_connection
|
@@ -763,7 +832,7 @@ module ActiveRecord
|
|
763
832
|
end
|
764
833
|
end
|
765
834
|
|
766
|
-
# If the pool is not at a
|
835
|
+
# If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
|
767
836
|
# to the DB is done outside main synchronized section.
|
768
837
|
#--
|
769
838
|
# Implementation constraint: a newly established connection returned by this
|
@@ -829,7 +898,7 @@ module ActiveRecord
|
|
829
898
|
# end
|
830
899
|
#
|
831
900
|
# class Book < ActiveRecord::Base
|
832
|
-
# establish_connection
|
901
|
+
# establish_connection :library_db
|
833
902
|
# end
|
834
903
|
#
|
835
904
|
# class ScaryBook < Book
|
@@ -861,17 +930,41 @@ module ActiveRecord
|
|
861
930
|
# All Active Record models use this handler to determine the connection pool that they
|
862
931
|
# should use.
|
863
932
|
#
|
864
|
-
# The ConnectionHandler class is not coupled with the Active models, as it has no
|
933
|
+
# The ConnectionHandler class is not coupled with the Active models, as it has no knowledge
|
865
934
|
# about the model. The model needs to pass a specification name to the handler,
|
866
|
-
# in order to
|
935
|
+
# in order to look up the correct connection pool.
|
867
936
|
class ConnectionHandler
|
868
|
-
def
|
869
|
-
|
870
|
-
|
937
|
+
def self.create_owner_to_pool # :nodoc:
|
938
|
+
Concurrent::Map.new(initial_capacity: 2) do |h, k|
|
939
|
+
# Discard the parent's connection pools immediately; we have no need
|
940
|
+
# of them
|
941
|
+
discard_unowned_pools(h)
|
942
|
+
|
871
943
|
h[k] = Concurrent::Map.new(initial_capacity: 2)
|
872
944
|
end
|
873
945
|
end
|
874
946
|
|
947
|
+
def self.unowned_pool_finalizer(pid_map) # :nodoc:
|
948
|
+
lambda do |_|
|
949
|
+
discard_unowned_pools(pid_map)
|
950
|
+
end
|
951
|
+
end
|
952
|
+
|
953
|
+
def self.discard_unowned_pools(pid_map) # :nodoc:
|
954
|
+
pid_map.each do |pid, pools|
|
955
|
+
pools.values.compact.each(&:discard!) unless pid == Process.pid
|
956
|
+
end
|
957
|
+
end
|
958
|
+
|
959
|
+
def initialize
|
960
|
+
# These caches are keyed by spec.name (ConnectionSpecification#name).
|
961
|
+
@owner_to_pool = ConnectionHandler.create_owner_to_pool
|
962
|
+
|
963
|
+
# Backup finalizer: if the forked child never needed a pool, the above
|
964
|
+
# early discard has not occurred
|
965
|
+
ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
|
966
|
+
end
|
967
|
+
|
875
968
|
def connection_pool_list
|
876
969
|
owner_to_pool.values.compact
|
877
970
|
end
|
@@ -923,6 +1016,13 @@ module ActiveRecord
|
|
923
1016
|
connection_pool_list.each(&:disconnect!)
|
924
1017
|
end
|
925
1018
|
|
1019
|
+
# Disconnects all currently idle connections.
|
1020
|
+
#
|
1021
|
+
# See ConnectionPool#flush! for details.
|
1022
|
+
def flush_idle_connections!
|
1023
|
+
connection_pool_list.each(&:flush!)
|
1024
|
+
end
|
1025
|
+
|
926
1026
|
# Locate the connection of the nearest super class. This can be an
|
927
1027
|
# active or defined connection: if it is the latter, it will be
|
928
1028
|
# opened and set as the active connection for the class it was defined
|
@@ -930,9 +1030,7 @@ module ActiveRecord
|
|
930
1030
|
def retrieve_connection(spec_name) #:nodoc:
|
931
1031
|
pool = retrieve_connection_pool(spec_name)
|
932
1032
|
raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found." unless pool
|
933
|
-
|
934
|
-
raise ConnectionNotEstablished, "No connection for '#{spec_name}' in connection pool" unless conn
|
935
|
-
conn
|
1033
|
+
pool.connection
|
936
1034
|
end
|
937
1035
|
|
938
1036
|
# Returns true if a connection that's accessible to this class has
|