activerecord 5.2.3 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +898 -532
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +5 -4
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +95 -42
- data/lib/active_record/associations/association_scope.rb +21 -21
- data/lib/active_record/associations/belongs_to_association.rb +50 -46
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -5
- data/lib/active_record/associations/builder/association.rb +23 -21
- data/lib/active_record/associations/builder/belongs_to.rb +29 -59
- data/lib/active_record/associations/builder/collection_association.rb +10 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -2
- data/lib/active_record/associations/builder/has_one.rb +33 -2
- data/lib/active_record/associations/builder/singular_association.rb +3 -1
- data/lib/active_record/associations/collection_association.rb +31 -29
- data/lib/active_record/associations/collection_proxy.rb +25 -21
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +26 -13
- data/lib/active_record/associations/has_many_through_association.rb +27 -28
- data/lib/active_record/associations/has_one_association.rb +43 -31
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +54 -12
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +91 -60
- data/lib/active_record/associations/preloader/association.rb +71 -43
- data/lib/active_record/associations/preloader/through_association.rb +49 -40
- data/lib/active_record/associations/preloader.rb +48 -35
- data/lib/active_record/associations/singular_association.rb +3 -17
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +133 -25
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -40
- data/lib/active_record/attribute_methods/primary_key.rb +20 -25
- data/lib/active_record/attribute_methods/query.rb +4 -8
- data/lib/active_record/attribute_methods/read.rb +14 -56
- data/lib/active_record/attribute_methods/serialization.rb +12 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +18 -34
- data/lib/active_record/attribute_methods.rb +81 -143
- data/lib/active_record/attributes.rb +45 -8
- data/lib/active_record/autosave_association.rb +76 -47
- data/lib/active_record/base.rb +4 -17
- data/lib/active_record/callbacks.rb +158 -43
- data/lib/active_record/coders/yaml_column.rb +1 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +293 -132
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +21 -17
- data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +203 -90
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +381 -146
- data/lib/active_record/connection_adapters/abstract/transaction.rb +155 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -98
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
- data/lib/active_record/connection_adapters/column.rb +30 -12
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +86 -32
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +38 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +222 -112
- data/lib/active_record/connection_adapters/schema_cache.rb +127 -21
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +19 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +175 -187
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_handling.rb +285 -33
- data/lib/active_record/core.rb +308 -100
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +272 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +3 -4
- data/lib/active_record/enum.rb +71 -17
- data/lib/active_record/errors.rb +62 -19
- data/lib/active_record/explain.rb +10 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +197 -481
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +53 -24
- data/lib/active_record/insert_all.rb +208 -0
- data/lib/active_record/integration.rb +67 -17
- data/lib/active_record/internal_metadata.rb +26 -9
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +26 -22
- data/lib/active_record/locking/pessimistic.rb +9 -5
- data/lib/active_record/log_subscriber.rb +34 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +141 -64
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/migration.rb +205 -156
- data/lib/active_record/model_schema.rb +148 -22
- data/lib/active_record/nested_attributes.rb +4 -7
- data/lib/active_record/no_touching.rb +8 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +267 -59
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/querying.rb +40 -23
- data/lib/active_record/railtie.rb +115 -58
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +402 -78
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +113 -101
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +44 -35
- data/lib/active_record/relation/calculations.rb +157 -93
- data/lib/active_record/relation/delegation.rb +35 -50
- data/lib/active_record/relation/finder_methods.rb +65 -40
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +32 -40
- data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -7
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +58 -40
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +487 -199
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +9 -9
- data/lib/active_record/relation/where_clause.rb +108 -58
- data/lib/active_record/relation.rb +375 -104
- data/lib/active_record/result.rb +64 -38
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +22 -41
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +54 -9
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +6 -8
- data/lib/active_record/scoping/named.rb +17 -24
- data/lib/active_record/scoping.rb +8 -9
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +51 -8
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +39 -43
- data/lib/active_record/tasks/database_tasks.rb +276 -81
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +246 -0
- data/lib/active_record/timestamp.rb +43 -32
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +59 -117
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +3 -13
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +10 -5
- data/lib/active_record/type_caster/connection.rb +15 -15
- data/lib/active_record/type_caster/map.rb +8 -8
- data/lib/active_record/validations/associated.rb +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +38 -30
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +13 -12
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +70 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +54 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
- data/lib/rails/generators/active_record/migration.rb +19 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +117 -32
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
- data/lib/active_record/relation/where_clause_factory.rb +0 -34
| @@ -3,22 +3,32 @@ | |
| 3 3 | 
             
            require "thread"
         | 
| 4 4 | 
             
            require "concurrent/map"
         | 
| 5 5 | 
             
            require "monitor"
         | 
| 6 | 
            +
            require "weakref"
         | 
| 6 7 |  | 
| 7 8 | 
             
            module ActiveRecord
         | 
| 8 | 
            -
               | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 9 | 
            +
              module ConnectionAdapters
         | 
| 10 | 
            +
                module AbstractPool # :nodoc:
         | 
| 11 | 
            +
                  def get_schema_cache(connection)
         | 
| 12 | 
            +
                    self.schema_cache ||= SchemaCache.new(connection)
         | 
| 13 | 
            +
                    schema_cache.connection = connection
         | 
| 14 | 
            +
                    schema_cache
         | 
| 15 | 
            +
                  end
         | 
| 13 16 |  | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 17 | 
            +
                  def set_schema_cache(cache)
         | 
| 18 | 
            +
                    self.schema_cache = cache
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                class NullPool # :nodoc:
         | 
| 23 | 
            +
                  include ConnectionAdapters::AbstractPool
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  attr_accessor :schema_cache
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  def owner_name
         | 
| 28 | 
            +
                    nil
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                end
         | 
| 20 31 |  | 
| 21 | 
            -
              module ConnectionAdapters
         | 
| 22 32 | 
             
                # Connection pool base class for managing Active Record database
         | 
| 23 33 | 
             
                # connections.
         | 
| 24 34 | 
             
                #
         | 
| @@ -129,7 +139,7 @@ module ActiveRecord | |
| 129 139 |  | 
| 130 140 | 
             
                    # Remove the head of the queue.
         | 
| 131 141 | 
             
                    #
         | 
| 132 | 
            -
                    # If +timeout+ is not given, remove and return the head the
         | 
| 142 | 
            +
                    # If +timeout+ is not given, remove and return the head of the
         | 
| 133 143 | 
             
                    # queue if the number of available elements is strictly
         | 
| 134 144 | 
             
                    # greater than the number of threads currently waiting (that
         | 
| 135 145 | 
             
                    # is, don't jump ahead in line).  Otherwise, return +nil+.
         | 
| @@ -146,7 +156,6 @@ module ActiveRecord | |
| 146 156 | 
             
                    end
         | 
| 147 157 |  | 
| 148 158 | 
             
                    private
         | 
| 149 | 
            -
             | 
| 150 159 | 
             
                      def internal_poll(timeout)
         | 
| 151 160 | 
             
                        no_wait_poll || (timeout && wait_poll(timeout))
         | 
| 152 161 | 
             
                      end
         | 
| @@ -173,7 +182,7 @@ module ActiveRecord | |
| 173 182 | 
             
                        @queue.pop
         | 
| 174 183 | 
             
                      end
         | 
| 175 184 |  | 
| 176 | 
            -
                      # Remove and return the head the queue if the number of
         | 
| 185 | 
            +
                      # Remove and return the head of the queue if the number of
         | 
| 177 186 | 
             
                      # available elements is strictly greater than the number of
         | 
| 178 187 | 
             
                      # threads currently waiting.  Otherwise, return +nil+.
         | 
| 179 188 | 
             
                      def no_wait_poll
         | 
| @@ -185,7 +194,7 @@ module ActiveRecord | |
| 185 194 | 
             
                      def wait_poll(timeout)
         | 
| 186 195 | 
             
                        @num_waiting += 1
         | 
| 187 196 |  | 
| 188 | 
            -
                        t0 =  | 
| 197 | 
            +
                        t0 = Concurrent.monotonic_time
         | 
| 189 198 | 
             
                        elapsed = 0
         | 
| 190 199 | 
             
                        loop do
         | 
| 191 200 | 
             
                          ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
         | 
| @@ -194,7 +203,7 @@ module ActiveRecord | |
| 194 203 |  | 
| 195 204 | 
             
                          return remove if any?
         | 
| 196 205 |  | 
| 197 | 
            -
                          elapsed =  | 
| 206 | 
            +
                          elapsed = Concurrent.monotonic_time - t0
         | 
| 198 207 | 
             
                          if elapsed >= timeout
         | 
| 199 208 | 
             
                            msg = "could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use" %
         | 
| 200 209 | 
             
                              [timeout, elapsed]
         | 
| @@ -294,50 +303,90 @@ module ActiveRecord | |
| 294 303 | 
             
                      @frequency = frequency
         | 
| 295 304 | 
             
                    end
         | 
| 296 305 |  | 
| 306 | 
            +
                    @mutex = Mutex.new
         | 
| 307 | 
            +
                    @pools = {}
         | 
| 308 | 
            +
                    @threads = {}
         | 
| 309 | 
            +
             | 
| 310 | 
            +
                    class << self
         | 
| 311 | 
            +
                      def register_pool(pool, frequency) # :nodoc:
         | 
| 312 | 
            +
                        @mutex.synchronize do
         | 
| 313 | 
            +
                          unless @threads[frequency]&.alive?
         | 
| 314 | 
            +
                            @threads[frequency] = spawn_thread(frequency)
         | 
| 315 | 
            +
                          end
         | 
| 316 | 
            +
                          @pools[frequency] ||= []
         | 
| 317 | 
            +
                          @pools[frequency] << WeakRef.new(pool)
         | 
| 318 | 
            +
                        end
         | 
| 319 | 
            +
                      end
         | 
| 320 | 
            +
             | 
| 321 | 
            +
                      private
         | 
| 322 | 
            +
                        def spawn_thread(frequency)
         | 
| 323 | 
            +
                          Thread.new(frequency) do |t|
         | 
| 324 | 
            +
                            # Advise multi-threaded app servers to ignore this thread for
         | 
| 325 | 
            +
                            # the purposes of fork safety warnings
         | 
| 326 | 
            +
                            Thread.current.thread_variable_set(:fork_safe, true)
         | 
| 327 | 
            +
                            running = true
         | 
| 328 | 
            +
                            while running
         | 
| 329 | 
            +
                              sleep t
         | 
| 330 | 
            +
                              @mutex.synchronize do
         | 
| 331 | 
            +
                                @pools[frequency].select! do |pool|
         | 
| 332 | 
            +
                                  pool.weakref_alive? && !pool.discarded?
         | 
| 333 | 
            +
                                end
         | 
| 334 | 
            +
             | 
| 335 | 
            +
                                @pools[frequency].each do |p|
         | 
| 336 | 
            +
                                  p.reap
         | 
| 337 | 
            +
                                  p.flush
         | 
| 338 | 
            +
                                rescue WeakRef::RefError
         | 
| 339 | 
            +
                                end
         | 
| 340 | 
            +
             | 
| 341 | 
            +
                                if @pools[frequency].empty?
         | 
| 342 | 
            +
                                  @pools.delete(frequency)
         | 
| 343 | 
            +
                                  @threads.delete(frequency)
         | 
| 344 | 
            +
                                  running = false
         | 
| 345 | 
            +
                                end
         | 
| 346 | 
            +
                              end
         | 
| 347 | 
            +
                            end
         | 
| 348 | 
            +
                          end
         | 
| 349 | 
            +
                        end
         | 
| 350 | 
            +
                    end
         | 
| 351 | 
            +
             | 
| 297 352 | 
             
                    def run
         | 
| 298 353 | 
             
                      return unless frequency && frequency > 0
         | 
| 299 | 
            -
                       | 
| 300 | 
            -
                        loop do
         | 
| 301 | 
            -
                          sleep t
         | 
| 302 | 
            -
                          p.reap
         | 
| 303 | 
            -
                          p.flush
         | 
| 304 | 
            -
                        end
         | 
| 305 | 
            -
                      }
         | 
| 354 | 
            +
                      self.class.register_pool(pool, frequency)
         | 
| 306 355 | 
             
                    end
         | 
| 307 356 | 
             
                  end
         | 
| 308 357 |  | 
| 309 358 | 
             
                  include MonitorMixin
         | 
| 310 359 | 
             
                  include QueryCache::ConnectionPoolConfiguration
         | 
| 360 | 
            +
                  include ConnectionAdapters::AbstractPool
         | 
| 311 361 |  | 
| 312 | 
            -
                  attr_accessor :automatic_reconnect, :checkout_timeout | 
| 313 | 
            -
                  attr_reader : | 
| 362 | 
            +
                  attr_accessor :automatic_reconnect, :checkout_timeout
         | 
| 363 | 
            +
                  attr_reader :db_config, :size, :reaper, :pool_config, :owner_name
         | 
| 314 364 |  | 
| 315 | 
            -
                   | 
| 365 | 
            +
                  delegate :schema_cache, :schema_cache=, to: :pool_config
         | 
| 366 | 
            +
             | 
| 367 | 
            +
                  # Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
         | 
| 316 368 | 
             
                  # object which describes database connection information (e.g. adapter,
         | 
| 317 369 | 
             
                  # host name, username, password, etc), as well as the maximum size for
         | 
| 318 370 | 
             
                  # this ConnectionPool.
         | 
| 319 371 | 
             
                  #
         | 
| 320 372 | 
             
                  # The default ConnectionPool maximum size is 5.
         | 
| 321 | 
            -
                  def initialize( | 
| 373 | 
            +
                  def initialize(pool_config)
         | 
| 322 374 | 
             
                    super()
         | 
| 323 375 |  | 
| 324 | 
            -
                    @ | 
| 325 | 
            -
             | 
| 326 | 
            -
                    @ | 
| 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
         | 
| 376 | 
            +
                    @pool_config = pool_config
         | 
| 377 | 
            +
                    @db_config = pool_config.db_config
         | 
| 378 | 
            +
                    @owner_name = pool_config.connection_specification_name
         | 
| 331 379 |  | 
| 332 | 
            -
                     | 
| 333 | 
            -
                    @ | 
| 380 | 
            +
                    @checkout_timeout = db_config.checkout_timeout
         | 
| 381 | 
            +
                    @idle_timeout = db_config.idle_timeout
         | 
| 382 | 
            +
                    @size = db_config.pool
         | 
| 334 383 |  | 
| 335 384 | 
             
                    # This variable tracks the cache of threads mapped to reserved connections, with the
         | 
| 336 385 | 
             
                    # sole purpose of speeding up the +connection+ method. It is not the authoritative
         | 
| 337 386 | 
             
                    # registry of which thread owns which connection. Connection ownership is tracked by
         | 
| 338 387 | 
             
                    # the +connection.owner+ attr on each +connection+ instance.
         | 
| 339 388 | 
             
                    # The invariant works like this: if there is mapping of <tt>thread => conn</tt>,
         | 
| 340 | 
            -
                    # then that +thread+ does indeed own that +conn+. However, an absence of  | 
| 389 | 
            +
                    # then that +thread+ does indeed own that +conn+. However, an absence of such
         | 
| 341 390 | 
             
                    # mapping does not mean that the +thread+ doesn't own the said connection. In
         | 
| 342 391 | 
             
                    # that case +conn.owner+ attr should be consulted.
         | 
| 343 392 | 
             
                    # Access and modification of <tt>@thread_cached_conns</tt> does not require
         | 
| @@ -358,10 +407,7 @@ module ActiveRecord | |
| 358 407 |  | 
| 359 408 | 
             
                    @lock_thread = false
         | 
| 360 409 |  | 
| 361 | 
            -
                     | 
| 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)
         | 
| 410 | 
            +
                    @reaper = Reaper.new(self, db_config.reaping_frequency)
         | 
| 365 411 | 
             
                    @reaper.run
         | 
| 366 412 | 
             
                  end
         | 
| 367 413 |  | 
| @@ -379,7 +425,7 @@ module ActiveRecord | |
| 379 425 | 
             
                  # #connection can be called any number of times; the connection is
         | 
| 380 426 | 
             
                  # held in a cache keyed by a thread.
         | 
| 381 427 | 
             
                  def connection
         | 
| 382 | 
            -
                    @thread_cached_conns[connection_cache_key( | 
| 428 | 
            +
                    @thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
         | 
| 383 429 | 
             
                  end
         | 
| 384 430 |  | 
| 385 431 | 
             
                  # Returns true if there is an open connection being used for the current thread.
         | 
| @@ -388,7 +434,7 @@ module ActiveRecord | |
| 388 434 | 
             
                  # #connection or #with_connection methods. Connections obtained through
         | 
| 389 435 | 
             
                  # #checkout will not be detected by #active_connection?
         | 
| 390 436 | 
             
                  def active_connection?
         | 
| 391 | 
            -
                    @thread_cached_conns[connection_cache_key( | 
| 437 | 
            +
                    @thread_cached_conns[connection_cache_key(current_thread)]
         | 
| 392 438 | 
             
                  end
         | 
| 393 439 |  | 
| 394 440 | 
             
                  # Signal that the thread is finished with the current connection.
         | 
| @@ -423,12 +469,27 @@ module ActiveRecord | |
| 423 469 | 
             
                    synchronize { @connections.any? }
         | 
| 424 470 | 
             
                  end
         | 
| 425 471 |  | 
| 472 | 
            +
                  # Returns an array containing the connections currently in the pool.
         | 
| 473 | 
            +
                  # Access to the array does not require synchronization on the pool because
         | 
| 474 | 
            +
                  # the array is newly created and not retained by the pool.
         | 
| 475 | 
            +
                  #
         | 
| 476 | 
            +
                  # However; this method bypasses the ConnectionPool's thread-safe connection
         | 
| 477 | 
            +
                  # access pattern. A returned connection may be owned by another thread,
         | 
| 478 | 
            +
                  # unowned, or by happen-stance owned by the calling thread.
         | 
| 479 | 
            +
                  #
         | 
| 480 | 
            +
                  # Calling methods on a connection without ownership is subject to the
         | 
| 481 | 
            +
                  # thread-safety guarantees of the underlying method. Many of the methods
         | 
| 482 | 
            +
                  # on connection adapter classes are inherently multi-thread unsafe.
         | 
| 483 | 
            +
                  def connections
         | 
| 484 | 
            +
                    synchronize { @connections.dup }
         | 
| 485 | 
            +
                  end
         | 
| 486 | 
            +
             | 
| 426 487 | 
             
                  # Disconnects all connections in the pool, and clears the pool.
         | 
| 427 488 | 
             
                  #
         | 
| 428 489 | 
             
                  # Raises:
         | 
| 429 490 | 
             
                  # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
         | 
| 430 491 | 
             
                  #   connections in the pool within a timeout interval (default duration is
         | 
| 431 | 
            -
                  #   <tt>spec. | 
| 492 | 
            +
                  #   <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
         | 
| 432 493 | 
             
                  def disconnect(raise_on_acquisition_timeout = true)
         | 
| 433 494 | 
             
                    with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
         | 
| 434 495 | 
             
                      synchronize do
         | 
| @@ -449,7 +510,7 @@ module ActiveRecord | |
| 449 510 | 
             
                  #
         | 
| 450 511 | 
             
                  # The pool first tries to gain ownership of all connections. If unable to
         | 
| 451 512 | 
             
                  # do so within a timeout interval (default duration is
         | 
| 452 | 
            -
                  # <tt>spec. | 
| 513 | 
            +
                  # <tt>spec.db_config.checkout_timeout * 2</tt> seconds), then the pool is forcefully
         | 
| 453 514 | 
             
                  # disconnected without any regard for other connection owning threads.
         | 
| 454 515 | 
             
                  def disconnect!
         | 
| 455 516 | 
             
                    disconnect(false)
         | 
| @@ -462,7 +523,7 @@ module ActiveRecord | |
| 462 523 | 
             
                  # See AbstractAdapter#discard!
         | 
| 463 524 | 
             
                  def discard! # :nodoc:
         | 
| 464 525 | 
             
                    synchronize do
         | 
| 465 | 
            -
                      return if  | 
| 526 | 
            +
                      return if self.discarded?
         | 
| 466 527 | 
             
                      @connections.each do |conn|
         | 
| 467 528 | 
             
                        conn.discard!
         | 
| 468 529 | 
             
                      end
         | 
| @@ -470,13 +531,17 @@ module ActiveRecord | |
| 470 531 | 
             
                    end
         | 
| 471 532 | 
             
                  end
         | 
| 472 533 |  | 
| 534 | 
            +
                  def discarded? # :nodoc:
         | 
| 535 | 
            +
                    @connections.nil?
         | 
| 536 | 
            +
                  end
         | 
| 537 | 
            +
             | 
| 473 538 | 
             
                  # Clears the cache which maps classes and re-connects connections that
         | 
| 474 539 | 
             
                  # require reloading.
         | 
| 475 540 | 
             
                  #
         | 
| 476 541 | 
             
                  # Raises:
         | 
| 477 542 | 
             
                  # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
         | 
| 478 543 | 
             
                  #   connections in the pool within a timeout interval (default duration is
         | 
| 479 | 
            -
                  #   <tt>spec. | 
| 544 | 
            +
                  #   <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
         | 
| 480 545 | 
             
                  def clear_reloadable_connections(raise_on_acquisition_timeout = true)
         | 
| 481 546 | 
             
                    with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
         | 
| 482 547 | 
             
                      synchronize do
         | 
| @@ -498,7 +563,7 @@ module ActiveRecord | |
| 498 563 | 
             
                  #
         | 
| 499 564 | 
             
                  # The pool first tries to gain ownership of all connections. If unable to
         | 
| 500 565 | 
             
                  # do so within a timeout interval (default duration is
         | 
| 501 | 
            -
                  # <tt>spec. | 
| 566 | 
            +
                  # <tt>spec.db_config.checkout_timeout * 2</tt> seconds), then the pool forcefully
         | 
| 502 567 | 
             
                  # clears the cache and reloads connections without any regard for other
         | 
| 503 568 | 
             
                  # connection owning threads.
         | 
| 504 569 | 
             
                  def clear_reloadable_connections!
         | 
| @@ -578,6 +643,7 @@ module ActiveRecord | |
| 578 643 | 
             
                  # or a thread dies unexpectedly.
         | 
| 579 644 | 
             
                  def reap
         | 
| 580 645 | 
             
                    stale_connections = synchronize do
         | 
| 646 | 
            +
                      return if self.discarded?
         | 
| 581 647 | 
             
                      @connections.select do |conn|
         | 
| 582 648 | 
             
                        conn.in_use? && !conn.owner.alive?
         | 
| 583 649 | 
             
                      end.each do |conn|
         | 
| @@ -602,6 +668,7 @@ module ActiveRecord | |
| 602 668 | 
             
                    return if minimum_idle.nil?
         | 
| 603 669 |  | 
| 604 670 | 
             
                    idle_connections = synchronize do
         | 
| 671 | 
            +
                      return if self.discarded?
         | 
| 605 672 | 
             
                      @connections.select do |conn|
         | 
| 606 673 | 
             
                        !conn.in_use? && conn.seconds_idle >= minimum_idle
         | 
| 607 674 | 
             
                      end.each do |conn|
         | 
| @@ -668,6 +735,10 @@ module ActiveRecord | |
| 668 735 | 
             
                      thread
         | 
| 669 736 | 
             
                    end
         | 
| 670 737 |  | 
| 738 | 
            +
                    def current_thread
         | 
| 739 | 
            +
                      @lock_thread || Thread.current
         | 
| 740 | 
            +
                    end
         | 
| 741 | 
            +
             | 
| 671 742 | 
             
                    # Take control of all existing connections so a "group" action such as
         | 
| 672 743 | 
             
                    # reload/disconnect can be performed safely. It is no longer enough to
         | 
| 673 744 | 
             
                    # wrap it in +synchronize+ because some pool's actions are allowed
         | 
| @@ -686,13 +757,13 @@ module ActiveRecord | |
| 686 757 | 
             
                      end
         | 
| 687 758 |  | 
| 688 759 | 
             
                      newly_checked_out = []
         | 
| 689 | 
            -
                      timeout_time      =  | 
| 760 | 
            +
                      timeout_time      = Concurrent.monotonic_time + (@checkout_timeout * 2)
         | 
| 690 761 |  | 
| 691 762 | 
             
                      @available.with_a_bias_for(Thread.current) do
         | 
| 692 763 | 
             
                        loop do
         | 
| 693 764 | 
             
                          synchronize do
         | 
| 694 765 | 
             
                            return if collected_conns.size == @connections.size && @now_connecting == 0
         | 
| 695 | 
            -
                            remaining_timeout = timeout_time -  | 
| 766 | 
            +
                            remaining_timeout = timeout_time - Concurrent.monotonic_time
         | 
| 696 767 | 
             
                            remaining_timeout = 0 if remaining_timeout < 0
         | 
| 697 768 | 
             
                            conn = checkout_for_exclusive_access(remaining_timeout)
         | 
| 698 769 | 
             
                            collected_conns   << conn
         | 
| @@ -731,7 +802,7 @@ module ActiveRecord | |
| 731 802 | 
             
                      # this block can't be easily moved into attempt_to_checkout_all_existing_connections's
         | 
| 732 803 | 
             
                      # rescue block, because doing so would put it outside of synchronize section, without
         | 
| 733 804 | 
             
                      # being in a critical section thread_report might become inaccurate
         | 
| 734 | 
            -
                      msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds" | 
| 805 | 
            +
                      msg = +"could not obtain ownership of all database connections in #{checkout_timeout} seconds"
         | 
| 735 806 |  | 
| 736 807 | 
             
                      thread_report = []
         | 
| 737 808 | 
             
                      @connections.each do |conn|
         | 
| @@ -808,8 +879,8 @@ module ActiveRecord | |
| 808 879 | 
             
                    alias_method :release, :remove_connection_from_thread_cache
         | 
| 809 880 |  | 
| 810 881 | 
             
                    def new_connection
         | 
| 811 | 
            -
                      Base. | 
| 812 | 
            -
                        conn. | 
| 882 | 
            +
                      Base.public_send(db_config.adapter_method, db_config.configuration_hash).tap do |conn|
         | 
| 883 | 
            +
                        conn.check_version
         | 
| 813 884 | 
             
                      end
         | 
| 814 885 | 
             
                    end
         | 
| 815 886 |  | 
| @@ -912,156 +983,246 @@ module ActiveRecord | |
| 912 983 | 
             
                # should use.
         | 
| 913 984 | 
             
                #
         | 
| 914 985 | 
             
                # The ConnectionHandler class is not coupled with the Active models, as it has no knowledge
         | 
| 915 | 
            -
                # about the model. The model needs to pass a specification name to the handler,
         | 
| 986 | 
            +
                # about the model. The model needs to pass a connection specification name to the handler,
         | 
| 916 987 | 
             
                # in order to look up the correct connection pool.
         | 
| 917 988 | 
             
                class ConnectionHandler
         | 
| 918 | 
            -
                   | 
| 919 | 
            -
             | 
| 920 | 
            -
                      # Discard the parent's connection pools immediately; we have no need
         | 
| 921 | 
            -
                      # of them
         | 
| 922 | 
            -
                      discard_unowned_pools(h)
         | 
| 989 | 
            +
                  FINALIZER = lambda { |_| ActiveSupport::ForkTracker.check! }
         | 
| 990 | 
            +
                  private_constant :FINALIZER
         | 
| 923 991 |  | 
| 924 | 
            -
             | 
| 925 | 
            -
                     | 
| 992 | 
            +
                  def initialize
         | 
| 993 | 
            +
                    # These caches are keyed by pool_config.connection_specification_name (PoolConfig#connection_specification_name).
         | 
| 994 | 
            +
                    @owner_to_pool_manager = Concurrent::Map.new(initial_capacity: 2)
         | 
| 995 | 
            +
             | 
| 996 | 
            +
                    # Backup finalizer: if the forked child skipped Kernel#fork the early discard has not occurred
         | 
| 997 | 
            +
                    ObjectSpace.define_finalizer self, FINALIZER
         | 
| 926 998 | 
             
                  end
         | 
| 927 999 |  | 
| 928 | 
            -
                  def  | 
| 929 | 
            -
                     | 
| 930 | 
            -
                      discard_unowned_pools(pid_map)
         | 
| 931 | 
            -
                    end
         | 
| 1000 | 
            +
                  def prevent_writes # :nodoc:
         | 
| 1001 | 
            +
                    Thread.current[:prevent_writes]
         | 
| 932 1002 | 
             
                  end
         | 
| 933 1003 |  | 
| 934 | 
            -
                  def  | 
| 935 | 
            -
                     | 
| 936 | 
            -
             | 
| 1004 | 
            +
                  def prevent_writes=(prevent_writes) # :nodoc:
         | 
| 1005 | 
            +
                    Thread.current[:prevent_writes] = prevent_writes
         | 
| 1006 | 
            +
                  end
         | 
| 1007 | 
            +
             | 
| 1008 | 
            +
                  # Prevent writing to the database regardless of role.
         | 
| 1009 | 
            +
                  #
         | 
| 1010 | 
            +
                  # In some cases you may want to prevent writes to the database
         | 
| 1011 | 
            +
                  # even if you are on a database that can write. `while_preventing_writes`
         | 
| 1012 | 
            +
                  # will prevent writes to the database for the duration of the block.
         | 
| 1013 | 
            +
                  #
         | 
| 1014 | 
            +
                  # This method does not provide the same protection as a readonly
         | 
| 1015 | 
            +
                  # user and is meant to be a safeguard against accidental writes.
         | 
| 1016 | 
            +
                  #
         | 
| 1017 | 
            +
                  # See `READ_QUERY` for the queries that are blocked by this
         | 
| 1018 | 
            +
                  # method.
         | 
| 1019 | 
            +
                  def while_preventing_writes(enabled = true)
         | 
| 1020 | 
            +
                    unless ActiveRecord::Base.legacy_connection_handling
         | 
| 1021 | 
            +
                      raise NotImplementedError, "`while_preventing_writes` is only available on the connection_handler with legacy_connection_handling"
         | 
| 937 1022 | 
             
                    end
         | 
| 1023 | 
            +
             | 
| 1024 | 
            +
                    original, self.prevent_writes = self.prevent_writes, enabled
         | 
| 1025 | 
            +
                    yield
         | 
| 1026 | 
            +
                  ensure
         | 
| 1027 | 
            +
                    self.prevent_writes = original
         | 
| 938 1028 | 
             
                  end
         | 
| 939 1029 |  | 
| 940 | 
            -
                  def  | 
| 941 | 
            -
                     | 
| 942 | 
            -
             | 
| 1030 | 
            +
                  def connection_pool_names # :nodoc:
         | 
| 1031 | 
            +
                    owner_to_pool_manager.keys
         | 
| 1032 | 
            +
                  end
         | 
| 943 1033 |  | 
| 944 | 
            -
             | 
| 945 | 
            -
                     | 
| 946 | 
            -
                    ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
         | 
| 1034 | 
            +
                  def all_connection_pools
         | 
| 1035 | 
            +
                    owner_to_pool_manager.values.flat_map { |m| m.pool_configs.map(&:pool) }
         | 
| 947 1036 | 
             
                  end
         | 
| 948 1037 |  | 
| 949 | 
            -
                  def connection_pool_list
         | 
| 950 | 
            -
                     | 
| 1038 | 
            +
                  def connection_pool_list(role = ActiveRecord::Base.current_role)
         | 
| 1039 | 
            +
                    owner_to_pool_manager.values.flat_map { |m| m.pool_configs(role).map(&:pool) }
         | 
| 951 1040 | 
             
                  end
         | 
| 952 1041 | 
             
                  alias :connection_pools :connection_pool_list
         | 
| 953 1042 |  | 
| 954 | 
            -
                  def establish_connection(config)
         | 
| 955 | 
            -
                     | 
| 956 | 
            -
                    spec = resolver.spec(config)
         | 
| 1043 | 
            +
                  def establish_connection(config, owner_name: Base.name, role: ActiveRecord::Base.current_role, shard: Base.current_shard)
         | 
| 1044 | 
            +
                    owner_name = config.to_s if config.is_a?(Symbol)
         | 
| 957 1045 |  | 
| 958 | 
            -
                     | 
| 1046 | 
            +
                    pool_config = resolve_pool_config(config, owner_name)
         | 
| 1047 | 
            +
                    db_config = pool_config.db_config
         | 
| 1048 | 
            +
             | 
| 1049 | 
            +
                    # Protects the connection named `ActiveRecord::Base` from being removed
         | 
| 1050 | 
            +
                    # if the user calls `establish_connection :primary`.
         | 
| 1051 | 
            +
                    if owner_to_pool_manager.key?(pool_config.connection_specification_name)
         | 
| 1052 | 
            +
                      remove_connection_pool(pool_config.connection_specification_name, role: role, shard: shard)
         | 
| 1053 | 
            +
                    end
         | 
| 959 1054 |  | 
| 960 1055 | 
             
                    message_bus = ActiveSupport::Notifications.instrumenter
         | 
| 961 | 
            -
                    payload = {
         | 
| 962 | 
            -
             | 
| 963 | 
            -
             | 
| 964 | 
            -
             | 
| 965 | 
            -
                      payload[: | 
| 966 | 
            -
                      payload[:config] = spec.config
         | 
| 1056 | 
            +
                    payload = {}
         | 
| 1057 | 
            +
                    if pool_config
         | 
| 1058 | 
            +
                      payload[:spec_name] = pool_config.connection_specification_name
         | 
| 1059 | 
            +
                      payload[:shard] = shard
         | 
| 1060 | 
            +
                      payload[:config] = db_config.configuration_hash
         | 
| 967 1061 | 
             
                    end
         | 
| 968 1062 |  | 
| 969 | 
            -
                     | 
| 970 | 
            -
                       | 
| 1063 | 
            +
                    if ActiveRecord::Base.legacy_connection_handling
         | 
| 1064 | 
            +
                      owner_to_pool_manager[pool_config.connection_specification_name] ||= LegacyPoolManager.new
         | 
| 1065 | 
            +
                    else
         | 
| 1066 | 
            +
                      owner_to_pool_manager[pool_config.connection_specification_name] ||= PoolManager.new
         | 
| 971 1067 | 
             
                    end
         | 
| 1068 | 
            +
                    pool_manager = get_pool_manager(pool_config.connection_specification_name)
         | 
| 1069 | 
            +
                    pool_manager.set_pool_config(role, shard, pool_config)
         | 
| 972 1070 |  | 
| 973 | 
            -
                     | 
| 1071 | 
            +
                    message_bus.instrument("!connection.active_record", payload) do
         | 
| 1072 | 
            +
                      pool_config.pool
         | 
| 1073 | 
            +
                    end
         | 
| 974 1074 | 
             
                  end
         | 
| 975 1075 |  | 
| 976 1076 | 
             
                  # Returns true if there are any active connections among the connection
         | 
| 977 1077 | 
             
                  # pools that the ConnectionHandler is managing.
         | 
| 978 | 
            -
                  def active_connections?
         | 
| 979 | 
            -
                    connection_pool_list.any?(&:active_connection?)
         | 
| 1078 | 
            +
                  def active_connections?(role = ActiveRecord::Base.current_role)
         | 
| 1079 | 
            +
                    connection_pool_list(role).any?(&:active_connection?)
         | 
| 980 1080 | 
             
                  end
         | 
| 981 1081 |  | 
| 982 1082 | 
             
                  # Returns any connections in use by the current thread back to the pool,
         | 
| 983 1083 | 
             
                  # and also returns connections to the pool cached by threads that are no
         | 
| 984 1084 | 
             
                  # longer alive.
         | 
| 985 | 
            -
                  def clear_active_connections!
         | 
| 986 | 
            -
                    connection_pool_list.each(&:release_connection)
         | 
| 1085 | 
            +
                  def clear_active_connections!(role = ActiveRecord::Base.current_role)
         | 
| 1086 | 
            +
                    connection_pool_list(role).each(&:release_connection)
         | 
| 987 1087 | 
             
                  end
         | 
| 988 1088 |  | 
| 989 1089 | 
             
                  # Clears the cache which maps classes.
         | 
| 990 1090 | 
             
                  #
         | 
| 991 1091 | 
             
                  # See ConnectionPool#clear_reloadable_connections! for details.
         | 
| 992 | 
            -
                  def clear_reloadable_connections!
         | 
| 993 | 
            -
                    connection_pool_list.each(&:clear_reloadable_connections!)
         | 
| 1092 | 
            +
                  def clear_reloadable_connections!(role = ActiveRecord::Base.current_role)
         | 
| 1093 | 
            +
                    connection_pool_list(role).each(&:clear_reloadable_connections!)
         | 
| 994 1094 | 
             
                  end
         | 
| 995 1095 |  | 
| 996 | 
            -
                  def clear_all_connections!
         | 
| 997 | 
            -
                    connection_pool_list.each(&:disconnect!)
         | 
| 1096 | 
            +
                  def clear_all_connections!(role = ActiveRecord::Base.current_role)
         | 
| 1097 | 
            +
                    connection_pool_list(role).each(&:disconnect!)
         | 
| 998 1098 | 
             
                  end
         | 
| 999 1099 |  | 
| 1000 1100 | 
             
                  # Disconnects all currently idle connections.
         | 
| 1001 1101 | 
             
                  #
         | 
| 1002 1102 | 
             
                  # See ConnectionPool#flush! for details.
         | 
| 1003 | 
            -
                  def flush_idle_connections!
         | 
| 1004 | 
            -
                    connection_pool_list.each(&:flush!)
         | 
| 1103 | 
            +
                  def flush_idle_connections!(role = ActiveRecord::Base.current_role)
         | 
| 1104 | 
            +
                    connection_pool_list(role).each(&:flush!)
         | 
| 1005 1105 | 
             
                  end
         | 
| 1006 1106 |  | 
| 1007 1107 | 
             
                  # Locate the connection of the nearest super class. This can be an
         | 
| 1008 1108 | 
             
                  # active or defined connection: if it is the latter, it will be
         | 
| 1009 1109 | 
             
                  # opened and set as the active connection for the class it was defined
         | 
| 1010 1110 | 
             
                  # for (not necessarily the current class).
         | 
| 1011 | 
            -
                  def retrieve_connection(spec_name)  | 
| 1012 | 
            -
                    pool = retrieve_connection_pool(spec_name)
         | 
| 1013 | 
            -
             | 
| 1111 | 
            +
                  def retrieve_connection(spec_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard) # :nodoc:
         | 
| 1112 | 
            +
                    pool = retrieve_connection_pool(spec_name, role: role, shard: shard)
         | 
| 1113 | 
            +
             | 
| 1114 | 
            +
                    unless pool
         | 
| 1115 | 
            +
                      if shard != ActiveRecord::Base.default_shard
         | 
| 1116 | 
            +
                        message = "No connection pool for '#{spec_name}' found for the '#{shard}' shard."
         | 
| 1117 | 
            +
                      elsif ActiveRecord::Base.connection_handler != ActiveRecord::Base.default_connection_handler
         | 
| 1118 | 
            +
                        message = "No connection pool for '#{spec_name}' found for the '#{ActiveRecord::Base.current_role}' role."
         | 
| 1119 | 
            +
                      elsif role != ActiveRecord::Base.default_role
         | 
| 1120 | 
            +
                        message = "No connection pool for '#{spec_name}' found for the '#{role}' role."
         | 
| 1121 | 
            +
                      else
         | 
| 1122 | 
            +
                        message = "No connection pool for '#{spec_name}' found."
         | 
| 1123 | 
            +
                      end
         | 
| 1124 | 
            +
             | 
| 1125 | 
            +
                      raise ConnectionNotEstablished, message
         | 
| 1126 | 
            +
                    end
         | 
| 1127 | 
            +
             | 
| 1014 1128 | 
             
                    pool.connection
         | 
| 1015 1129 | 
             
                  end
         | 
| 1016 1130 |  | 
| 1017 1131 | 
             
                  # Returns true if a connection that's accessible to this class has
         | 
| 1018 1132 | 
             
                  # already been opened.
         | 
| 1019 | 
            -
                  def connected?(spec_name)
         | 
| 1020 | 
            -
                     | 
| 1021 | 
            -
                     | 
| 1133 | 
            +
                  def connected?(spec_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
         | 
| 1134 | 
            +
                    pool = retrieve_connection_pool(spec_name, role: role, shard: shard)
         | 
| 1135 | 
            +
                    pool && pool.connected?
         | 
| 1022 1136 | 
             
                  end
         | 
| 1023 1137 |  | 
| 1024 1138 | 
             
                  # Remove the connection for this class. This will close the active
         | 
| 1025 1139 | 
             
                  # connection and the defined connection (if they exist). The result
         | 
| 1026 1140 | 
             
                  # can be used as an argument for #establish_connection, for easily
         | 
| 1027 1141 | 
             
                  # re-establishing the connection.
         | 
| 1028 | 
            -
                  def remove_connection( | 
| 1029 | 
            -
                     | 
| 1030 | 
            -
             | 
| 1031 | 
            -
             | 
| 1032 | 
            -
             | 
| 1142 | 
            +
                  def remove_connection(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
         | 
| 1143 | 
            +
                    remove_connection_pool(owner, role: role, shard: shard)&.configuration_hash
         | 
| 1144 | 
            +
                  end
         | 
| 1145 | 
            +
                  deprecate remove_connection: "Use #remove_connection_pool, which now returns a DatabaseConfig object instead of a Hash"
         | 
| 1146 | 
            +
             | 
| 1147 | 
            +
                  def remove_connection_pool(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
         | 
| 1148 | 
            +
                    if pool_manager = get_pool_manager(owner)
         | 
| 1149 | 
            +
                      pool_config = pool_manager.remove_pool_config(role, shard)
         | 
| 1150 | 
            +
             | 
| 1151 | 
            +
                      if pool_config
         | 
| 1152 | 
            +
                        pool_config.disconnect!
         | 
| 1153 | 
            +
                        pool_config.db_config
         | 
| 1154 | 
            +
                      end
         | 
| 1033 1155 | 
             
                    end
         | 
| 1034 1156 | 
             
                  end
         | 
| 1035 1157 |  | 
| 1036 | 
            -
                  # Retrieving the connection pool happens a lot, so we cache it in @ | 
| 1158 | 
            +
                  # Retrieving the connection pool happens a lot, so we cache it in @owner_to_pool_manager.
         | 
| 1037 1159 | 
             
                  # This makes retrieving the connection pool O(1) once the process is warm.
         | 
| 1038 1160 | 
             
                  # When a connection is established or removed, we invalidate the cache.
         | 
| 1039 | 
            -
                  def retrieve_connection_pool( | 
| 1040 | 
            -
                     | 
| 1041 | 
            -
             | 
| 1042 | 
            -
                      # which may have been forked.
         | 
| 1043 | 
            -
                      if ancestor_pool = pool_from_any_process_for(spec_name)
         | 
| 1044 | 
            -
                        # A connection was established in an ancestor process that must have
         | 
| 1045 | 
            -
                        # subsequently forked. We can't reuse the connection, but we can copy
         | 
| 1046 | 
            -
                        # the specification and establish a new connection with it.
         | 
| 1047 | 
            -
                        establish_connection(ancestor_pool.spec.to_hash).tap do |pool|
         | 
| 1048 | 
            -
                          pool.schema_cache = ancestor_pool.schema_cache if ancestor_pool.schema_cache
         | 
| 1049 | 
            -
                        end
         | 
| 1050 | 
            -
                      else
         | 
| 1051 | 
            -
                        owner_to_pool[spec_name] = nil
         | 
| 1052 | 
            -
                      end
         | 
| 1053 | 
            -
                    end
         | 
| 1161 | 
            +
                  def retrieve_connection_pool(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
         | 
| 1162 | 
            +
                    pool_config = get_pool_manager(owner)&.get_pool_config(role, shard)
         | 
| 1163 | 
            +
                    pool_config&.pool
         | 
| 1054 1164 | 
             
                  end
         | 
| 1055 1165 |  | 
| 1056 1166 | 
             
                  private
         | 
| 1167 | 
            +
                    attr_reader :owner_to_pool_manager
         | 
| 1057 1168 |  | 
| 1058 | 
            -
                     | 
| 1059 | 
            -
             | 
| 1169 | 
            +
                    # Returns the pool manager for an owner.
         | 
| 1170 | 
            +
                    #
         | 
| 1171 | 
            +
                    # Using `"primary"` to look up the pool manager for `ActiveRecord::Base` is
         | 
| 1172 | 
            +
                    # deprecated in favor of looking it up by `"ActiveRecord::Base"`.
         | 
| 1173 | 
            +
                    #
         | 
| 1174 | 
            +
                    # During the deprecation period, if `"primary"` is passed, the pool manager
         | 
| 1175 | 
            +
                    # for `ActiveRecord::Base` will still be returned.
         | 
| 1176 | 
            +
                    def get_pool_manager(owner)
         | 
| 1177 | 
            +
                      return owner_to_pool_manager[owner] if owner_to_pool_manager.key?(owner)
         | 
| 1178 | 
            +
             | 
| 1179 | 
            +
                      if owner == "primary"
         | 
| 1180 | 
            +
                        ActiveSupport::Deprecation.warn("Using `\"primary\"` as a `connection_specification_name` is deprecated and will be removed in Rails 6.2.0. Please use `ActiveRecord::Base`.")
         | 
| 1181 | 
            +
                        owner_to_pool_manager[Base.name]
         | 
| 1182 | 
            +
                      end
         | 
| 1060 1183 | 
             
                    end
         | 
| 1061 1184 |  | 
| 1062 | 
            -
                     | 
| 1063 | 
            -
             | 
| 1064 | 
            -
             | 
| 1185 | 
            +
                    # Returns an instance of PoolConfig for a given adapter.
         | 
| 1186 | 
            +
                    # Accepts a hash one layer deep that contains all connection information.
         | 
| 1187 | 
            +
                    #
         | 
| 1188 | 
            +
                    # == Example
         | 
| 1189 | 
            +
                    #
         | 
| 1190 | 
            +
                    #   config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
         | 
| 1191 | 
            +
                    #   pool_config = Base.configurations.resolve_pool_config(:production)
         | 
| 1192 | 
            +
                    #   pool_config.db_config.configuration_hash
         | 
| 1193 | 
            +
                    #   # => { host: "localhost", database: "foo", adapter: "sqlite3" }
         | 
| 1194 | 
            +
                    #
         | 
| 1195 | 
            +
                    def resolve_pool_config(config, owner_name)
         | 
| 1196 | 
            +
                      db_config = Base.configurations.resolve(config)
         | 
| 1197 | 
            +
             | 
| 1198 | 
            +
                      raise(AdapterNotSpecified, "database configuration does not specify adapter") unless db_config.adapter
         | 
| 1199 | 
            +
             | 
| 1200 | 
            +
                      # Require the adapter itself and give useful feedback about
         | 
| 1201 | 
            +
                      #   1. Missing adapter gems and
         | 
| 1202 | 
            +
                      #   2. Adapter gems' missing dependencies.
         | 
| 1203 | 
            +
                      path_to_adapter = "active_record/connection_adapters/#{db_config.adapter}_adapter"
         | 
| 1204 | 
            +
                      begin
         | 
| 1205 | 
            +
                        require path_to_adapter
         | 
| 1206 | 
            +
                      rescue LoadError => e
         | 
| 1207 | 
            +
                        # We couldn't require the adapter itself. Raise an exception that
         | 
| 1208 | 
            +
                        # points out config typos and missing gems.
         | 
| 1209 | 
            +
                        if e.path == path_to_adapter
         | 
| 1210 | 
            +
                          # We can assume that a non-builtin adapter was specified, so it's
         | 
| 1211 | 
            +
                          # either misspelled or missing from Gemfile.
         | 
| 1212 | 
            +
                          raise LoadError, "Could not load the '#{db_config.adapter}' Active Record adapter. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile.", e.backtrace
         | 
| 1213 | 
            +
             | 
| 1214 | 
            +
                          # Bubbled up from the adapter require. Prefix the exception message
         | 
| 1215 | 
            +
                          # with some guidance about how to address it and reraise.
         | 
| 1216 | 
            +
                        else
         | 
| 1217 | 
            +
                          raise LoadError, "Error loading the '#{db_config.adapter}' Active Record adapter. Missing a gem it depends on? #{e.message}", e.backtrace
         | 
| 1218 | 
            +
                        end
         | 
| 1219 | 
            +
                      end
         | 
| 1220 | 
            +
             | 
| 1221 | 
            +
                      unless ActiveRecord::Base.respond_to?(db_config.adapter_method)
         | 
| 1222 | 
            +
                        raise AdapterNotFound, "database configuration specifies nonexistent #{db_config.adapter} adapter"
         | 
| 1223 | 
            +
                      end
         | 
| 1224 | 
            +
             | 
| 1225 | 
            +
                      ConnectionAdapters::PoolConfig.new(owner_name, db_config)
         | 
| 1065 1226 | 
             
                    end
         | 
| 1066 1227 | 
             
                end
         | 
| 1067 1228 | 
             
              end
         |