activerecord 6.0.3.6 → 6.1.3
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 +944 -698
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -3
- data/lib/active_record.rb +7 -14
- data/lib/active_record/aggregations.rb +5 -5
- data/lib/active_record/association_relation.rb +30 -12
- data/lib/active_record/associations.rb +118 -11
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +44 -28
- data/lib/active_record/associations/association_scope.rb +19 -15
- data/lib/active_record/associations/belongs_to_association.rb +22 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
- data/lib/active_record/associations/builder/association.rb +32 -5
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +19 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -5
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -2
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency.rb +72 -50
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/preloader.rb +11 -5
- data/lib/active_record/associations/preloader/association.rb +51 -25
- data/lib/active_record/associations/preloader/through_association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/attribute_assignment.rb +10 -8
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +1 -11
- data/lib/active_record/attribute_methods/primary_key.rb +6 -2
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -11
- data/lib/active_record/attribute_methods/serialization.rb +11 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
- data/lib/active_record/attribute_methods/write.rb +12 -20
- data/lib/active_record/attributes.rb +33 -8
- data/lib/active_record/autosave_association.rb +57 -40
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +152 -22
- data/lib/active_record/coders/yaml_column.rb +1 -1
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +191 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -23
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +116 -27
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +228 -83
- data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
- data/lib/active_record/connection_adapters/abstract_adapter.rb +54 -72
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +129 -88
- data/lib/active_record/connection_adapters/column.rb +15 -1
- 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/database_statements.rb +23 -25
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +11 -7
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +13 -54
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- 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/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +74 -63
- data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +31 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +37 -4
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +49 -50
- data/lib/active_record/connection_handling.rb +218 -71
- data/lib/active_record/core.rb +245 -61
- data/lib/active_record/database_configurations.rb +124 -85
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -40
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/enum.rb +82 -38
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -4
- 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 +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -2
- data/lib/active_record/fixtures.rb +58 -9
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +35 -6
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +16 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +33 -17
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +27 -8
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/migration.rb +114 -84
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +68 -17
- data/lib/active_record/model_schema.rb +117 -13
- data/lib/active_record/nested_attributes.rb +2 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +50 -45
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +11 -6
- data/lib/active_record/railtie.rb +64 -44
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/databases.rake +276 -99
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +71 -57
- data/lib/active_record/relation.rb +95 -67
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/calculations.rb +101 -44
- data/lib/active_record/relation/delegation.rb +2 -1
- data/lib/active_record/relation/finder_methods.rb +45 -15
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +27 -25
- data/lib/active_record/relation/predicate_builder.rb +61 -38
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +333 -195
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -7
- data/lib/active_record/relation/where_clause.rb +107 -60
- data/lib/active_record/result.rb +41 -33
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +2 -8
- data/lib/active_record/scoping/named.rb +6 -17
- 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 +20 -4
- data/lib/active_record/store.rb +2 -2
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -51
- data/lib/active_record/tasks/database_tasks.rb +140 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +37 -16
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +19 -66
- data/lib/active_record/type.rb +8 -1
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/arel.rb +5 -13
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/predications.rb +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel/visitors/dot.rb +14 -2
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -4
- data/lib/arel/visitors/to_sql.rb +89 -78
- data/lib/rails/generators/active_record/migration.rb +6 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
- 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
- metadata +28 -29
- data/lib/active_record/advisory_lock_base.rb +0 -18
- data/lib/active_record/attribute_decorators.rb +0 -88
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -203
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -156
- data/lib/arel/visitors/oracle.rb +0 -158
- data/lib/arel/visitors/oracle12.rb +0 -65
- data/lib/arel/visitors/where_sql.rb +0 -22
| @@ -67,15 +67,34 @@ module ActiveRecord | |
| 67 67 | 
             
                        end
         | 
| 68 68 |  | 
| 69 69 | 
             
                        def extract_bounds(value)
         | 
| 70 | 
            -
                          from, to = value[1..-2].split(",")
         | 
| 70 | 
            +
                          from, to = value[1..-2].split(",", 2)
         | 
| 71 71 | 
             
                          {
         | 
| 72 | 
            -
                            from:          ( | 
| 73 | 
            -
                            to:            ( | 
| 74 | 
            -
                            exclude_start: ( | 
| 75 | 
            -
                            exclude_end:   ( | 
| 72 | 
            +
                            from:          (from == "" || from == "-infinity") ? infinity(negative: true) : unquote(from),
         | 
| 73 | 
            +
                            to:            (to == "" || to == "infinity") ? infinity : unquote(to),
         | 
| 74 | 
            +
                            exclude_start: value.start_with?("("),
         | 
| 75 | 
            +
                            exclude_end:   value.end_with?(")")
         | 
| 76 76 | 
             
                          }
         | 
| 77 77 | 
             
                        end
         | 
| 78 78 |  | 
| 79 | 
            +
                        # When formatting the bound values of range types, PostgreSQL quotes
         | 
| 80 | 
            +
                        # the bound value using double-quotes in certain conditions. Within
         | 
| 81 | 
            +
                        # a double-quoted string, literal " and \ characters are themselves
         | 
| 82 | 
            +
                        # escaped. In input, PostgreSQL accepts multiple escape styles for "
         | 
| 83 | 
            +
                        # (either \" or "") but in output always uses "".
         | 
| 84 | 
            +
                        # See:
         | 
| 85 | 
            +
                        # * https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-IO
         | 
| 86 | 
            +
                        # * https://www.postgresql.org/docs/current/rowtypes.html#ROWTYPES-IO-SYNTAX
         | 
| 87 | 
            +
                        def unquote(value)
         | 
| 88 | 
            +
                          if value.start_with?('"') && value.end_with?('"')
         | 
| 89 | 
            +
                            unquoted_value = value[1..-2]
         | 
| 90 | 
            +
                            unquoted_value.gsub!('""', '"')
         | 
| 91 | 
            +
                            unquoted_value.gsub!('\\\\', '\\')
         | 
| 92 | 
            +
                            unquoted_value
         | 
| 93 | 
            +
                          else
         | 
| 94 | 
            +
                            value
         | 
| 95 | 
            +
                          end
         | 
| 96 | 
            +
                        end
         | 
| 97 | 
            +
             | 
| 79 98 | 
             
                        def infinity(negative: false)
         | 
| 80 99 | 
             
                          if subtype.respond_to?(:infinity)
         | 
| 81 100 | 
             
                            subtype.infinity(negative: negative)
         | 
| @@ -7,12 +7,22 @@ module ActiveRecord | |
| 7 7 | 
             
                    class Uuid < Type::Value # :nodoc:
         | 
| 8 8 | 
             
                      ACCEPTABLE_UUID = %r{\A(\{)?([a-fA-F0-9]{4}-?){8}(?(1)\}|)\z}
         | 
| 9 9 |  | 
| 10 | 
            -
                       | 
| 10 | 
            +
                      alias :serialize :deserialize
         | 
| 11 11 |  | 
| 12 12 | 
             
                      def type
         | 
| 13 13 | 
             
                        :uuid
         | 
| 14 14 | 
             
                      end
         | 
| 15 15 |  | 
| 16 | 
            +
                      def changed?(old_value, new_value, _new_value_before_type_cast)
         | 
| 17 | 
            +
                        old_value.class != new_value.class ||
         | 
| 18 | 
            +
                          new_value && old_value.casecmp(new_value) != 0
         | 
| 19 | 
            +
                      end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                      def changed_in_place?(raw_old_value, new_value)
         | 
| 22 | 
            +
                        raw_old_value.class != new_value.class ||
         | 
| 23 | 
            +
                          new_value && raw_old_value.casecmp(new_value) != 0
         | 
| 24 | 
            +
                      end
         | 
| 25 | 
            +
             | 
| 16 26 | 
             
                      private
         | 
| 17 27 | 
             
                        def cast_value(value)
         | 
| 18 28 | 
             
                          casted = value.to_s
         | 
| @@ -18,7 +18,7 @@ module ActiveRecord | |
| 18 18 |  | 
| 19 19 | 
             
                    # Quotes strings for use in SQL input.
         | 
| 20 20 | 
             
                    def quote_string(s) #:nodoc:
         | 
| 21 | 
            -
                       | 
| 21 | 
            +
                      PG::Connection.escape(s)
         | 
| 22 22 | 
             
                    end
         | 
| 23 23 |  | 
| 24 24 | 
             
                    # Checks the following cases:
         | 
| @@ -67,8 +67,8 @@ module ActiveRecord | |
| 67 67 | 
             
                      elsif column.type == :uuid && value.is_a?(String) && /\(\)/.match?(value)
         | 
| 68 68 | 
             
                        value # Does not quote function default values for UUID columns
         | 
| 69 69 | 
             
                      elsif column.respond_to?(:array?)
         | 
| 70 | 
            -
                         | 
| 71 | 
            -
                        quote(value)
         | 
| 70 | 
            +
                        type = lookup_cast_type_from_column(column)
         | 
| 71 | 
            +
                        quote(type.serialize(value))
         | 
| 72 72 | 
             
                      else
         | 
| 73 73 | 
             
                        super
         | 
| 74 74 | 
             
                      end
         | 
| @@ -93,7 +93,7 @@ module ActiveRecord | |
| 93 93 | 
             
                          # "table_name"."column_name"::type_name | function(one or no argument)::type_name
         | 
| 94 94 | 
             
                          ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
         | 
| 95 95 | 
             
                        )
         | 
| 96 | 
            -
                        (?:\s+AS | 
| 96 | 
            +
                        (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
         | 
| 97 97 | 
             
                      )
         | 
| 98 98 | 
             
                      (?:\s*,\s*\g<1>)*
         | 
| 99 99 | 
             
                      \z
         | 
| @@ -24,7 +24,7 @@ WARNING: Rails was not able to disable referential integrity. | |
| 24 24 | 
             
            This is most likely caused due to missing permissions.
         | 
| 25 25 | 
             
            Rails needs superuser privileges to disable referential integrity.
         | 
| 26 26 |  | 
| 27 | 
            -
                cause: #{original_exception | 
| 27 | 
            +
                cause: #{original_exception&.message}
         | 
| 28 28 |  | 
| 29 29 | 
             
                        WARNING
         | 
| 30 30 | 
             
                        raise e
         | 
| @@ -3,7 +3,7 @@ | |
| 3 3 | 
             
            module ActiveRecord
         | 
| 4 4 | 
             
              module ConnectionAdapters
         | 
| 5 5 | 
             
                module PostgreSQL
         | 
| 6 | 
            -
                  class SchemaCreation <  | 
| 6 | 
            +
                  class SchemaCreation < SchemaCreation # :nodoc:
         | 
| 7 7 | 
             
                    private
         | 
| 8 8 | 
             
                      def visit_AlterTable(o)
         | 
| 9 9 | 
             
                        super << o.constraint_validations.map { |fk| visit_ValidateConstraint fk }.join(" ")
         | 
| @@ -13,6 +13,10 @@ module ActiveRecord | |
| 13 13 | 
             
                        super.dup.tap { |sql| sql << " NOT VALID" unless o.validate? }
         | 
| 14 14 | 
             
                      end
         | 
| 15 15 |  | 
| 16 | 
            +
                      def visit_CheckConstraintDefinition(o)
         | 
| 17 | 
            +
                        super.dup.tap { |sql| sql << " NOT VALID" unless o.validate? }
         | 
| 18 | 
            +
                      end
         | 
| 19 | 
            +
             | 
| 16 20 | 
             
                      def visit_ValidateConstraint(name)
         | 
| 17 21 | 
             
                        "VALIDATE CONSTRAINT #{quote_column_name(name)}"
         | 
| 18 22 | 
             
                      end
         | 
| @@ -54,7 +54,8 @@ module ActiveRecord | |
| 54 54 | 
             
                      execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
         | 
| 55 55 | 
             
                    end
         | 
| 56 56 |  | 
| 57 | 
            -
                    def drop_table(table_name, options | 
| 57 | 
            +
                    def drop_table(table_name, **options) # :nodoc:
         | 
| 58 | 
            +
                      schema_cache.clear_data_source_cache!(table_name.to_s)
         | 
| 58 59 | 
             
                      execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
         | 
| 59 60 | 
             
                    end
         | 
| 60 61 |  | 
| @@ -211,7 +212,7 @@ module ActiveRecord | |
| 211 212 | 
             
                    end
         | 
| 212 213 |  | 
| 213 214 | 
             
                    # Drops the schema for the given schema name.
         | 
| 214 | 
            -
                    def drop_schema(schema_name, options | 
| 215 | 
            +
                    def drop_schema(schema_name, **options)
         | 
| 215 216 | 
             
                      execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
         | 
| 216 217 | 
             
                    end
         | 
| 217 218 |  | 
| @@ -376,6 +377,8 @@ module ActiveRecord | |
| 376 377 | 
             
                    #   rename_table('octopuses', 'octopi')
         | 
| 377 378 | 
             
                    def rename_table(table_name, new_name)
         | 
| 378 379 | 
             
                      clear_cache!
         | 
| 380 | 
            +
                      schema_cache.clear_data_source_cache!(table_name.to_s)
         | 
| 381 | 
            +
                      schema_cache.clear_data_source_cache!(new_name.to_s)
         | 
| 379 382 | 
             
                      execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
         | 
| 380 383 | 
             
                      pk, seq = pk_and_sequence_for(new_name)
         | 
| 381 384 | 
             
                      if pk
         | 
| @@ -396,9 +399,9 @@ module ActiveRecord | |
| 396 399 | 
             
                      change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
         | 
| 397 400 | 
             
                    end
         | 
| 398 401 |  | 
| 399 | 
            -
                    def change_column(table_name, column_name, type, options | 
| 402 | 
            +
                    def change_column(table_name, column_name, type, **options) #:nodoc:
         | 
| 400 403 | 
             
                      clear_cache!
         | 
| 401 | 
            -
                      sqls, procs = Array(change_column_for_alter(table_name, column_name, type, options)).partition { |v| v.is_a?(String) }
         | 
| 404 | 
            +
                      sqls, procs = Array(change_column_for_alter(table_name, column_name, type, **options)).partition { |v| v.is_a?(String) }
         | 
| 402 405 | 
             
                      execute "ALTER TABLE #{quote_table_name(table_name)} #{sqls.join(", ")}"
         | 
| 403 406 | 
             
                      procs.each(&:call)
         | 
| 404 407 | 
             
                    end
         | 
| @@ -434,21 +437,24 @@ module ActiveRecord | |
| 434 437 | 
             
                    # Renames a column in a table.
         | 
| 435 438 | 
             
                    def rename_column(table_name, column_name, new_column_name) #:nodoc:
         | 
| 436 439 | 
             
                      clear_cache!
         | 
| 437 | 
            -
                      execute | 
| 440 | 
            +
                      execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
         | 
| 438 441 | 
             
                      rename_column_indexes(table_name, column_name, new_column_name)
         | 
| 439 442 | 
             
                    end
         | 
| 440 443 |  | 
| 441 | 
            -
                    def add_index(table_name, column_name, options | 
| 442 | 
            -
                       | 
| 443 | 
            -
             | 
| 444 | 
            -
             | 
| 445 | 
            -
                       | 
| 444 | 
            +
                    def add_index(table_name, column_name, **options) #:nodoc:
         | 
| 445 | 
            +
                      index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
         | 
| 446 | 
            +
             | 
| 447 | 
            +
                      create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
         | 
| 448 | 
            +
                      result = execute schema_creation.accept(create_index)
         | 
| 449 | 
            +
             | 
| 450 | 
            +
                      execute "COMMENT ON INDEX #{quote_column_name(index.name)} IS #{quote(index.comment)}" if index.comment
         | 
| 451 | 
            +
                      result
         | 
| 446 452 | 
             
                    end
         | 
| 447 453 |  | 
| 448 | 
            -
                    def remove_index(table_name,  | 
| 454 | 
            +
                    def remove_index(table_name, column_name = nil, **options) # :nodoc:
         | 
| 449 455 | 
             
                      table = Utils.extract_schema_qualified_name(table_name.to_s)
         | 
| 450 456 |  | 
| 451 | 
            -
                      if options. | 
| 457 | 
            +
                      if options.key?(:name)
         | 
| 452 458 | 
             
                        provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
         | 
| 453 459 |  | 
| 454 460 | 
             
                        options[:name] = provided_index.identifier
         | 
| @@ -459,14 +465,11 @@ module ActiveRecord | |
| 459 465 | 
             
                        end
         | 
| 460 466 | 
             
                      end
         | 
| 461 467 |  | 
| 462 | 
            -
                       | 
| 463 | 
            -
             | 
| 464 | 
            -
             | 
| 465 | 
            -
             | 
| 466 | 
            -
             | 
| 467 | 
            -
                          end
         | 
| 468 | 
            -
                        end
         | 
| 469 | 
            -
                      execute "DROP INDEX #{algorithm} #{quote_table_name(index_to_remove)}"
         | 
| 468 | 
            +
                      return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
         | 
| 469 | 
            +
             | 
| 470 | 
            +
                      index_to_remove = PostgreSQL::Name.new(table.schema, index_name_for_remove(table.to_s, column_name, options))
         | 
| 471 | 
            +
             | 
| 472 | 
            +
                      execute "DROP INDEX #{index_algorithm(options[:algorithm])} #{quote_table_name(index_to_remove)}"
         | 
| 470 473 | 
             
                    end
         | 
| 471 474 |  | 
| 472 475 | 
             
                    # Renames an index of a table. Raises error if length of new
         | 
| @@ -516,6 +519,28 @@ module ActiveRecord | |
| 516 519 | 
             
                      query_values(data_source_sql(table_name, type: "FOREIGN TABLE"), "SCHEMA").any? if table_name.present?
         | 
| 517 520 | 
             
                    end
         | 
| 518 521 |  | 
| 522 | 
            +
                    def check_constraints(table_name) # :nodoc:
         | 
| 523 | 
            +
                      scope = quoted_scope(table_name)
         | 
| 524 | 
            +
             | 
| 525 | 
            +
                      check_info = exec_query(<<-SQL, "SCHEMA")
         | 
| 526 | 
            +
                        SELECT conname, pg_get_constraintdef(c.oid) AS constraintdef, c.convalidated AS valid
         | 
| 527 | 
            +
                        FROM pg_constraint c
         | 
| 528 | 
            +
                        JOIN pg_class t ON c.conrelid = t.oid
         | 
| 529 | 
            +
                        WHERE c.contype = 'c'
         | 
| 530 | 
            +
                          AND t.relname = #{scope[:name]}
         | 
| 531 | 
            +
                      SQL
         | 
| 532 | 
            +
             | 
| 533 | 
            +
                      check_info.map do |row|
         | 
| 534 | 
            +
                        options = {
         | 
| 535 | 
            +
                          name: row["conname"],
         | 
| 536 | 
            +
                          validate: row["valid"]
         | 
| 537 | 
            +
                        }
         | 
| 538 | 
            +
                        expression = row["constraintdef"][/CHECK \({2}(.+)\){2}/, 1]
         | 
| 539 | 
            +
             | 
| 540 | 
            +
                        CheckConstraintDefinition.new(table_name, expression, options)
         | 
| 541 | 
            +
                      end
         | 
| 542 | 
            +
                    end
         | 
| 543 | 
            +
             | 
| 519 544 | 
             
                    # Maps logical Rails types to PostgreSQL-specific data types.
         | 
| 520 545 | 
             
                    def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
         | 
| 521 546 | 
             
                      sql = \
         | 
| @@ -552,13 +577,13 @@ module ActiveRecord | |
| 552 577 | 
             
                    # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
         | 
| 553 578 | 
             
                    # requires that the ORDER BY include the distinct column.
         | 
| 554 579 | 
             
                    def columns_for_distinct(columns, orders) #:nodoc:
         | 
| 555 | 
            -
                      order_columns = orders. | 
| 580 | 
            +
                      order_columns = orders.compact_blank.map { |s|
         | 
| 556 581 | 
             
                        # Convert Arel node to string
         | 
| 557 582 | 
             
                        s = visitor.compile(s) unless s.is_a?(String)
         | 
| 558 583 | 
             
                        # Remove any ASC/DESC modifiers
         | 
| 559 584 | 
             
                        s.gsub(/\s+(?:ASC|DESC)\b/i, "")
         | 
| 560 585 | 
             
                         .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
         | 
| 561 | 
            -
                      }. | 
| 586 | 
            +
                      }.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
         | 
| 562 587 |  | 
| 563 588 | 
             
                      (order_columns << super).join(", ")
         | 
| 564 589 | 
             
                    end
         | 
| @@ -577,8 +602,6 @@ module ActiveRecord | |
| 577 602 | 
             
                    #
         | 
| 578 603 | 
             
                    #   validate_constraint :accounts, :constraint_name
         | 
| 579 604 | 
             
                    def validate_constraint(table_name, constraint_name)
         | 
| 580 | 
            -
                      return unless supports_validate_constraints?
         | 
| 581 | 
            -
             | 
| 582 605 | 
             
                      at = create_alter_table table_name
         | 
| 583 606 | 
             
                      at.validate_constraint constraint_name
         | 
| 584 607 |  | 
| @@ -601,20 +624,29 @@ module ActiveRecord | |
| 601 624 | 
             
                    #
         | 
| 602 625 | 
             
                    # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
         | 
| 603 626 | 
             
                    def validate_foreign_key(from_table, to_table = nil, **options)
         | 
| 604 | 
            -
                      return unless supports_validate_constraints?
         | 
| 605 | 
            -
             | 
| 606 627 | 
             
                      fk_name_to_validate = foreign_key_for!(from_table, to_table: to_table, **options).name
         | 
| 607 628 |  | 
| 608 629 | 
             
                      validate_constraint from_table, fk_name_to_validate
         | 
| 609 630 | 
             
                    end
         | 
| 610 631 |  | 
| 632 | 
            +
                    # Validates the given check constraint.
         | 
| 633 | 
            +
                    #
         | 
| 634 | 
            +
                    #   validate_check_constraint :products, name: "price_check"
         | 
| 635 | 
            +
                    #
         | 
| 636 | 
            +
                    # The +options+ hash accepts the same keys as add_check_constraint[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
         | 
| 637 | 
            +
                    def validate_check_constraint(table_name, **options)
         | 
| 638 | 
            +
                      chk_name_to_validate = check_constraint_for!(table_name, **options).name
         | 
| 639 | 
            +
             | 
| 640 | 
            +
                      validate_constraint table_name, chk_name_to_validate
         | 
| 641 | 
            +
                    end
         | 
| 642 | 
            +
             | 
| 611 643 | 
             
                    private
         | 
| 612 644 | 
             
                      def schema_creation
         | 
| 613 645 | 
             
                        PostgreSQL::SchemaCreation.new(self)
         | 
| 614 646 | 
             
                      end
         | 
| 615 647 |  | 
| 616 | 
            -
                      def create_table_definition( | 
| 617 | 
            -
                        PostgreSQL::TableDefinition.new(self,  | 
| 648 | 
            +
                      def create_table_definition(name, **options)
         | 
| 649 | 
            +
                        PostgreSQL::TableDefinition.new(self, name, **options)
         | 
| 618 650 | 
             
                      end
         | 
| 619 651 |  | 
| 620 652 | 
             
                      def create_alter_table(name)
         | 
| @@ -684,7 +716,7 @@ module ActiveRecord | |
| 684 716 | 
             
                        [super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
         | 
| 685 717 | 
             
                      end
         | 
| 686 718 |  | 
| 687 | 
            -
                      def change_column_for_alter(table_name, column_name, type, options | 
| 719 | 
            +
                      def change_column_for_alter(table_name, column_name, type, **options)
         | 
| 688 720 | 
             
                        td = create_table_definition(table_name)
         | 
| 689 721 | 
             
                        cd = td.new_column_definition(column_name, type, **options)
         | 
| 690 722 | 
             
                        sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
         | 
| @@ -7,6 +7,8 @@ module ActiveRecord | |
| 7 7 | 
             
                  class TypeMetadata < DelegateClass(SqlTypeMetadata)
         | 
| 8 8 | 
             
                    undef to_yaml if method_defined?(:to_yaml)
         | 
| 9 9 |  | 
| 10 | 
            +
                    include Deduplicable
         | 
| 11 | 
            +
             | 
| 10 12 | 
             
                    attr_reader :oid, :fmod
         | 
| 11 13 |  | 
| 12 14 | 
             
                    def initialize(type_metadata, oid: nil, fmod: nil)
         | 
| @@ -29,6 +31,12 @@ module ActiveRecord | |
| 29 31 | 
             
                        oid.hash ^
         | 
| 30 32 | 
             
                        fmod.hash
         | 
| 31 33 | 
             
                    end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    private
         | 
| 36 | 
            +
                      def deduplicated
         | 
| 37 | 
            +
                        __setobj__(__getobj__.deduplicate)
         | 
| 38 | 
            +
                        super
         | 
| 39 | 
            +
                      end
         | 
| 32 40 | 
             
                  end
         | 
| 33 41 | 
             
                end
         | 
| 34 42 | 
             
                PostgreSQLTypeMetadata = PostgreSQL::TypeMetadata
         | 
| @@ -1,17 +1,9 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
            gem "pg", ">= 0.18", "< 2.0"
         | 
| 3 | 
            +
            gem "pg", "~> 1.1"
         | 
| 5 4 | 
             
            require "pg"
         | 
| 6 5 |  | 
| 7 | 
            -
             | 
| 8 | 
            -
            class ::PG::Connection # :nodoc:
         | 
| 9 | 
            -
              unless self.public_method_defined?(:async_exec_params)
         | 
| 10 | 
            -
                remove_method :exec_params
         | 
| 11 | 
            -
                alias exec_params async_exec
         | 
| 12 | 
            -
              end
         | 
| 13 | 
            -
            end
         | 
| 14 | 
            -
             | 
| 6 | 
            +
            require "active_support/core_ext/object/try"
         | 
| 15 7 | 
             
            require "active_record/connection_adapters/abstract_adapter"
         | 
| 16 8 | 
             
            require "active_record/connection_adapters/statement_pool"
         | 
| 17 9 | 
             
            require "active_record/connection_adapters/postgresql/column"
         | 
| @@ -31,9 +23,7 @@ module ActiveRecord | |
| 31 23 | 
             
              module ConnectionHandling # :nodoc:
         | 
| 32 24 | 
             
                # Establishes a connection to the database that's used by all Active Record objects
         | 
| 33 25 | 
             
                def postgresql_connection(config)
         | 
| 34 | 
            -
                  conn_params = config.symbolize_keys
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                  conn_params.delete_if { |_, v| v.nil? }
         | 
| 26 | 
            +
                  conn_params = config.symbolize_keys.compact
         | 
| 37 27 |  | 
| 38 28 | 
             
                  # Map ActiveRecords param names to PGs.
         | 
| 39 29 | 
             
                  conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
         | 
| @@ -43,19 +33,17 @@ module ActiveRecord | |
| 43 33 | 
             
                  valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
         | 
| 44 34 | 
             
                  conn_params.slice!(*valid_conn_param_keys)
         | 
| 45 35 |  | 
| 46 | 
            -
                   | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
                     | 
| 51 | 
            -
                   | 
| 52 | 
            -
                    raise
         | 
| 53 | 
            -
                  end
         | 
| 36 | 
            +
                  ConnectionAdapters::PostgreSQLAdapter.new(
         | 
| 37 | 
            +
                    ConnectionAdapters::PostgreSQLAdapter.new_client(conn_params),
         | 
| 38 | 
            +
                    logger,
         | 
| 39 | 
            +
                    conn_params,
         | 
| 40 | 
            +
                    config,
         | 
| 41 | 
            +
                  )
         | 
| 54 42 | 
             
                end
         | 
| 55 43 | 
             
              end
         | 
| 56 44 |  | 
| 57 45 | 
             
              module ConnectionAdapters
         | 
| 58 | 
            -
                # The PostgreSQL adapter works with the native C (https:// | 
| 46 | 
            +
                # The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
         | 
| 59 47 | 
             
                #
         | 
| 60 48 | 
             
                # Options:
         | 
| 61 49 | 
             
                #
         | 
| @@ -85,6 +73,18 @@ module ActiveRecord | |
| 85 73 | 
             
                class PostgreSQLAdapter < AbstractAdapter
         | 
| 86 74 | 
             
                  ADAPTER_NAME = "PostgreSQL"
         | 
| 87 75 |  | 
| 76 | 
            +
                  class << self
         | 
| 77 | 
            +
                    def new_client(conn_params)
         | 
| 78 | 
            +
                      PG.connect(conn_params)
         | 
| 79 | 
            +
                    rescue ::PG::Error => error
         | 
| 80 | 
            +
                      if conn_params && conn_params[:dbname] && error.message.include?(conn_params[:dbname])
         | 
| 81 | 
            +
                        raise ActiveRecord::NoDatabaseError
         | 
| 82 | 
            +
                      else
         | 
| 83 | 
            +
                        raise ActiveRecord::ConnectionNotEstablished, error.message
         | 
| 84 | 
            +
                      end
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
             | 
| 88 88 | 
             
                  ##
         | 
| 89 89 | 
             
                  # :singleton-method:
         | 
| 90 90 | 
             
                  # PostgreSQL allows the creation of "unlogged" tables, which do not record
         | 
| @@ -176,6 +176,10 @@ module ActiveRecord | |
| 176 176 | 
             
                    true
         | 
| 177 177 | 
             
                  end
         | 
| 178 178 |  | 
| 179 | 
            +
                  def supports_check_constraints?
         | 
| 180 | 
            +
                    true
         | 
| 181 | 
            +
                  end
         | 
| 182 | 
            +
             | 
| 179 183 | 
             
                  def supports_validate_constraints?
         | 
| 180 184 | 
             
                    true
         | 
| 181 185 | 
             
                  end
         | 
| @@ -223,11 +227,7 @@ module ActiveRecord | |
| 223 227 | 
             
                    end
         | 
| 224 228 |  | 
| 225 229 | 
             
                    def next_key
         | 
| 226 | 
            -
                      "a#{@counter  | 
| 227 | 
            -
                    end
         | 
| 228 | 
            -
             | 
| 229 | 
            -
                    def []=(sql, key)
         | 
| 230 | 
            -
                      super.tap { @counter += 1 }
         | 
| 230 | 
            +
                      "a#{@counter += 1}"
         | 
| 231 231 | 
             
                    end
         | 
| 232 232 |  | 
| 233 233 | 
             
                    private
         | 
| @@ -341,11 +341,6 @@ module ActiveRecord | |
| 341 341 | 
             
                    true
         | 
| 342 342 | 
             
                  end
         | 
| 343 343 |  | 
| 344 | 
            -
                  def supports_ranges?
         | 
| 345 | 
            -
                    true
         | 
| 346 | 
            -
                  end
         | 
| 347 | 
            -
                  deprecate :supports_ranges?
         | 
| 348 | 
            -
             | 
| 349 344 | 
             
                  def supports_materialized_views?
         | 
| 350 345 | 
             
                    true
         | 
| 351 346 | 
             
                  end
         | 
| @@ -426,16 +421,6 @@ module ActiveRecord | |
| 426 421 | 
             
                    @use_insert_returning
         | 
| 427 422 | 
             
                  end
         | 
| 428 423 |  | 
| 429 | 
            -
                  def column_name_for_operation(operation, node) # :nodoc:
         | 
| 430 | 
            -
                    OPERATION_ALIASES.fetch(operation) { operation.downcase }
         | 
| 431 | 
            -
                  end
         | 
| 432 | 
            -
             | 
| 433 | 
            -
                  OPERATION_ALIASES = { # :nodoc:
         | 
| 434 | 
            -
                    "maximum" => "max",
         | 
| 435 | 
            -
                    "minimum" => "min",
         | 
| 436 | 
            -
                    "average" => "avg",
         | 
| 437 | 
            -
                  }
         | 
| 438 | 
            -
             | 
| 439 424 | 
             
                  # Returns the version of the connected PostgreSQL server.
         | 
| 440 425 | 
             
                  def get_database_version # :nodoc:
         | 
| 441 426 | 
             
                    @connection.server_version
         | 
| @@ -453,6 +438,7 @@ module ActiveRecord | |
| 453 438 | 
             
                      sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
         | 
| 454 439 | 
             
                    elsif insert.update_duplicates?
         | 
| 455 440 | 
             
                      sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
         | 
| 441 | 
            +
                      sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
         | 
| 456 442 | 
             
                      sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
         | 
| 457 443 | 
             
                    end
         | 
| 458 444 |  | 
| @@ -475,6 +461,7 @@ module ActiveRecord | |
| 475 461 | 
             
                    UNIQUE_VIOLATION      = "23505"
         | 
| 476 462 | 
             
                    SERIALIZATION_FAILURE = "40001"
         | 
| 477 463 | 
             
                    DEADLOCK_DETECTED     = "40P01"
         | 
| 464 | 
            +
                    DUPLICATE_DATABASE    = "42P04"
         | 
| 478 465 | 
             
                    LOCK_NOT_AVAILABLE    = "55P03"
         | 
| 479 466 | 
             
                    QUERY_CANCELED        = "57014"
         | 
| 480 467 |  | 
| @@ -482,6 +469,12 @@ module ActiveRecord | |
| 482 469 | 
             
                      return exception unless exception.respond_to?(:result)
         | 
| 483 470 |  | 
| 484 471 | 
             
                      case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
         | 
| 472 | 
            +
                      when nil
         | 
| 473 | 
            +
                        if exception.message.match?(/connection is closed/i)
         | 
| 474 | 
            +
                          ConnectionNotEstablished.new(exception)
         | 
| 475 | 
            +
                        else
         | 
| 476 | 
            +
                          super
         | 
| 477 | 
            +
                        end
         | 
| 485 478 | 
             
                      when UNIQUE_VIOLATION
         | 
| 486 479 | 
             
                        RecordNotUnique.new(message, sql: sql, binds: binds)
         | 
| 487 480 | 
             
                      when FOREIGN_KEY_VIOLATION
         | 
| @@ -496,6 +489,8 @@ module ActiveRecord | |
| 496 489 | 
             
                        SerializationFailure.new(message, sql: sql, binds: binds)
         | 
| 497 490 | 
             
                      when DEADLOCK_DETECTED
         | 
| 498 491 | 
             
                        Deadlocked.new(message, sql: sql, binds: binds)
         | 
| 492 | 
            +
                      when DUPLICATE_DATABASE
         | 
| 493 | 
            +
                        DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
         | 
| 499 494 | 
             
                      when LOCK_NOT_AVAILABLE
         | 
| 500 495 | 
             
                        LockWaitTimeout.new(message, sql: sql, binds: binds)
         | 
| 501 496 | 
             
                      when QUERY_CANCELED
         | 
| @@ -547,7 +542,7 @@ module ActiveRecord | |
| 547 542 | 
             
                      m.register_type "uuid", OID::Uuid.new
         | 
| 548 543 | 
             
                      m.register_type "xml", OID::Xml.new
         | 
| 549 544 | 
             
                      m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
         | 
| 550 | 
            -
                      m.register_type "macaddr", OID:: | 
| 545 | 
            +
                      m.register_type "macaddr", OID::Macaddr.new
         | 
| 551 546 | 
             
                      m.register_type "citext", OID::SpecializedString.new(:citext)
         | 
| 552 547 | 
             
                      m.register_type "ltree", OID::SpecializedString.new(:ltree)
         | 
| 553 548 | 
             
                      m.register_type "line", OID::SpecializedString.new(:line)
         | 
| @@ -557,11 +552,6 @@ module ActiveRecord | |
| 557 552 | 
             
                      m.register_type "polygon", OID::SpecializedString.new(:polygon)
         | 
| 558 553 | 
             
                      m.register_type "circle", OID::SpecializedString.new(:circle)
         | 
| 559 554 |  | 
| 560 | 
            -
                      m.register_type "interval" do |_, _, sql_type|
         | 
| 561 | 
            -
                        precision = extract_precision(sql_type)
         | 
| 562 | 
            -
                        OID::SpecializedString.new(:interval, precision: precision)
         | 
| 563 | 
            -
                      end
         | 
| 564 | 
            -
             | 
| 565 555 | 
             
                      register_class_with_precision m, "time", Type::Time
         | 
| 566 556 | 
             
                      register_class_with_precision m, "timestamp", OID::DateTime
         | 
| 567 557 |  | 
| @@ -585,6 +575,11 @@ module ActiveRecord | |
| 585 575 | 
             
                        end
         | 
| 586 576 | 
             
                      end
         | 
| 587 577 |  | 
| 578 | 
            +
                      m.register_type "interval" do |*args, sql_type|
         | 
| 579 | 
            +
                        precision = extract_precision(sql_type)
         | 
| 580 | 
            +
                        OID::Interval.new(precision: precision)
         | 
| 581 | 
            +
                      end
         | 
| 582 | 
            +
             | 
| 588 583 | 
             
                      load_additional_types
         | 
| 589 584 | 
             
                    end
         | 
| 590 585 |  | 
| @@ -650,20 +645,22 @@ module ActiveRecord | |
| 650 645 | 
             
                        raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
         | 
| 651 646 | 
             
                      end
         | 
| 652 647 |  | 
| 653 | 
            -
                      if without_prepared_statement?(binds)
         | 
| 654 | 
            -
                        result = exec_no_cache(sql, name, [])
         | 
| 655 | 
            -
                      elsif !prepare
         | 
| 648 | 
            +
                      if !prepare || without_prepared_statement?(binds)
         | 
| 656 649 | 
             
                        result = exec_no_cache(sql, name, binds)
         | 
| 657 650 | 
             
                      else
         | 
| 658 651 | 
             
                        result = exec_cache(sql, name, binds)
         | 
| 659 652 | 
             
                      end
         | 
| 660 | 
            -
                       | 
| 661 | 
            -
             | 
| 653 | 
            +
                      begin
         | 
| 654 | 
            +
                        ret = yield result
         | 
| 655 | 
            +
                      ensure
         | 
| 656 | 
            +
                        result.clear
         | 
| 657 | 
            +
                      end
         | 
| 662 658 | 
             
                      ret
         | 
| 663 659 | 
             
                    end
         | 
| 664 660 |  | 
| 665 661 | 
             
                    def exec_no_cache(sql, name, binds)
         | 
| 666 662 | 
             
                      materialize_transactions
         | 
| 663 | 
            +
                      mark_transaction_written_if_write(sql)
         | 
| 667 664 |  | 
| 668 665 | 
             
                      # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
         | 
| 669 666 | 
             
                      # made since we established the connection
         | 
| @@ -679,6 +676,7 @@ module ActiveRecord | |
| 679 676 |  | 
| 680 677 | 
             
                    def exec_cache(sql, name, binds)
         | 
| 681 678 | 
             
                      materialize_transactions
         | 
| 679 | 
            +
                      mark_transaction_written_if_write(sql)
         | 
| 682 680 | 
             
                      update_typemap_for_default_timezone
         | 
| 683 681 |  | 
| 684 682 | 
             
                      stmt_key = prepare_statement(sql, binds)
         | 
| @@ -714,11 +712,10 @@ module ActiveRecord | |
| 714 712 | 
             
                    #
         | 
| 715 713 | 
             
                    # Check here for more details:
         | 
| 716 714 | 
             
                    # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
         | 
| 717 | 
            -
                    CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
         | 
| 718 715 | 
             
                    def is_cached_plan_failure?(e)
         | 
| 719 716 | 
             
                      pgerror = e.cause
         | 
| 720 | 
            -
                       | 
| 721 | 
            -
             | 
| 717 | 
            +
                      pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
         | 
| 718 | 
            +
                        pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
         | 
| 722 719 | 
             
                    rescue
         | 
| 723 720 | 
             
                      false
         | 
| 724 721 | 
             
                    end
         | 
| @@ -756,7 +753,7 @@ module ActiveRecord | |
| 756 753 | 
             
                    # Connects to a PostgreSQL server and sets up the adapter depending on the
         | 
| 757 754 | 
             
                    # connected server's characteristics.
         | 
| 758 755 | 
             
                    def connect
         | 
| 759 | 
            -
                      @connection =  | 
| 756 | 
            +
                      @connection = self.class.new_client(@connection_parameters)
         | 
| 760 757 | 
             
                      configure_connection
         | 
| 761 758 | 
             
                      add_pg_encoders
         | 
| 762 759 | 
             
                      add_pg_decoders
         | 
| @@ -786,6 +783,9 @@ module ActiveRecord | |
| 786 783 | 
             
                        end
         | 
| 787 784 | 
             
                      end
         | 
| 788 785 |  | 
| 786 | 
            +
                      # Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
         | 
| 787 | 
            +
                      execute("SET intervalstyle = iso_8601", "SCHEMA")
         | 
| 788 | 
            +
             | 
| 789 789 | 
             
                      # SET statements from :variables config hash
         | 
| 790 790 | 
             
                      # https://www.postgresql.org/docs/current/static/sql-set.html
         | 
| 791 791 | 
             
                      variables.map do |k, v|
         | 
| @@ -897,15 +897,12 @@ module ActiveRecord | |
| 897 897 | 
             
                        "oid" => PG::TextDecoder::Integer,
         | 
| 898 898 | 
             
                        "float4" => PG::TextDecoder::Float,
         | 
| 899 899 | 
             
                        "float8" => PG::TextDecoder::Float,
         | 
| 900 | 
            +
                        "numeric" => PG::TextDecoder::Numeric,
         | 
| 900 901 | 
             
                        "bool" => PG::TextDecoder::Boolean,
         | 
| 902 | 
            +
                        "timestamp" => PG::TextDecoder::TimestampUtc,
         | 
| 903 | 
            +
                        "timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
         | 
| 901 904 | 
             
                      }
         | 
| 902 905 |  | 
| 903 | 
            -
                      if defined?(PG::TextDecoder::TimestampUtc)
         | 
| 904 | 
            -
                        # Use native PG encoders available since pg-1.1
         | 
| 905 | 
            -
                        coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
         | 
| 906 | 
            -
                        coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
         | 
| 907 | 
            -
                      end
         | 
| 908 | 
            -
             | 
| 909 906 | 
             
                      known_coder_types = coders_by_name.keys.map { |n| quote(n) }
         | 
| 910 907 | 
             
                      query = <<~SQL % known_coder_types.join(", ")
         | 
| 911 908 | 
             
                        SELECT t.oid, t.typname
         | 
| @@ -922,6 +919,11 @@ module ActiveRecord | |
| 922 919 | 
             
                      coders.each { |coder| map.add_coder(coder) }
         | 
| 923 920 | 
             
                      @connection.type_map_for_results = map
         | 
| 924 921 |  | 
| 922 | 
            +
                      @type_map_for_results = PG::TypeMapByOid.new
         | 
| 923 | 
            +
                      @type_map_for_results.default_type_map = map
         | 
| 924 | 
            +
                      @type_map_for_results.add_coder(PG::TextDecoder::Bytea.new(oid: 17, name: "bytea"))
         | 
| 925 | 
            +
                      @type_map_for_results.add_coder(MoneyDecoder.new(oid: 790, name: "money"))
         | 
| 926 | 
            +
             | 
| 925 927 | 
             
                      # extract timestamp decoder for use in update_typemap_for_default_timezone
         | 
| 926 928 | 
             
                      @timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
         | 
| 927 929 | 
             
                      update_typemap_for_default_timezone
         | 
| @@ -932,6 +934,14 @@ module ActiveRecord | |
| 932 934 | 
             
                      coder_class.new(oid: row["oid"].to_i, name: row["typname"])
         | 
| 933 935 | 
             
                    end
         | 
| 934 936 |  | 
| 937 | 
            +
                    class MoneyDecoder < PG::SimpleDecoder # :nodoc:
         | 
| 938 | 
            +
                      TYPE = OID::Money.new
         | 
| 939 | 
            +
             | 
| 940 | 
            +
                      def decode(value, tuple = nil, field = nil)
         | 
| 941 | 
            +
                        TYPE.deserialize(value)
         | 
| 942 | 
            +
                      end
         | 
| 943 | 
            +
                    end
         | 
| 944 | 
            +
             | 
| 935 945 | 
             
                    ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
         | 
| 936 946 | 
             
                    ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
         | 
| 937 947 | 
             
                    ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
         | 
| @@ -944,6 +954,7 @@ module ActiveRecord | |
| 944 954 | 
             
                    ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
         | 
| 945 955 | 
             
                    ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
         | 
| 946 956 | 
             
                    ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
         | 
| 957 | 
            +
                    ActiveRecord::Type.register(:interval, OID::Interval, adapter: :postgresql)
         | 
| 947 958 | 
             
                    ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
         | 
| 948 959 | 
             
                    ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
         | 
| 949 960 | 
             
                    ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
         |