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
| @@ -22,8 +22,8 @@ module ActiveRecord | |
| 22 22 | 
             
                    def create_database(name, options = {})
         | 
| 23 23 | 
             
                      options = { encoding: "utf8" }.merge!(options.symbolize_keys)
         | 
| 24 24 |  | 
| 25 | 
            -
                      option_string = options. | 
| 26 | 
            -
                        memo  | 
| 25 | 
            +
                      option_string = options.each_with_object(+"") do |(key, value), memo|
         | 
| 26 | 
            +
                        memo << case key
         | 
| 27 27 | 
             
                                when :owner
         | 
| 28 28 | 
             
                                  " OWNER = \"#{value}\""
         | 
| 29 29 | 
             
                                when :template
         | 
| @@ -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 |  | 
| @@ -68,13 +69,13 @@ module ActiveRecord | |
| 68 69 | 
             
                      table = quoted_scope(table_name)
         | 
| 69 70 | 
             
                      index = quoted_scope(index_name)
         | 
| 70 71 |  | 
| 71 | 
            -
                      query_value( | 
| 72 | 
            +
                      query_value(<<~SQL, "SCHEMA").to_i > 0
         | 
| 72 73 | 
             
                        SELECT COUNT(*)
         | 
| 73 74 | 
             
                        FROM pg_class t
         | 
| 74 75 | 
             
                        INNER JOIN pg_index d ON t.oid = d.indrelid
         | 
| 75 76 | 
             
                        INNER JOIN pg_class i ON d.indexrelid = i.oid
         | 
| 76 77 | 
             
                        LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
         | 
| 77 | 
            -
                        WHERE i.relkind  | 
| 78 | 
            +
                        WHERE i.relkind IN ('i', 'I')
         | 
| 78 79 | 
             
                          AND i.relname = #{index[:name]}
         | 
| 79 80 | 
             
                          AND t.relname = #{table[:name]}
         | 
| 80 81 | 
             
                          AND n.nspname = #{index[:schema]}
         | 
| @@ -85,14 +86,14 @@ module ActiveRecord | |
| 85 86 | 
             
                    def indexes(table_name) # :nodoc:
         | 
| 86 87 | 
             
                      scope = quoted_scope(table_name)
         | 
| 87 88 |  | 
| 88 | 
            -
                      result = query( | 
| 89 | 
            +
                      result = query(<<~SQL, "SCHEMA")
         | 
| 89 90 | 
             
                        SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
         | 
| 90 91 | 
             
                                        pg_catalog.obj_description(i.oid, 'pg_class') AS comment
         | 
| 91 92 | 
             
                        FROM pg_class t
         | 
| 92 93 | 
             
                        INNER JOIN pg_index d ON t.oid = d.indrelid
         | 
| 93 94 | 
             
                        INNER JOIN pg_class i ON d.indexrelid = i.oid
         | 
| 94 95 | 
             
                        LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
         | 
| 95 | 
            -
                        WHERE i.relkind  | 
| 96 | 
            +
                        WHERE i.relkind IN ('i', 'I')
         | 
| 96 97 | 
             
                          AND d.indisprimary = 'f'
         | 
| 97 98 | 
             
                          AND t.relname = #{scope[:name]}
         | 
| 98 99 | 
             
                          AND n.nspname = #{scope[:schema]}
         | 
| @@ -115,7 +116,7 @@ module ActiveRecord | |
| 115 116 | 
             
                        if indkey.include?(0)
         | 
| 116 117 | 
             
                          columns = expressions
         | 
| 117 118 | 
             
                        else
         | 
| 118 | 
            -
                          columns = Hash[query( | 
| 119 | 
            +
                          columns = Hash[query(<<~SQL, "SCHEMA")].values_at(*indkey).compact
         | 
| 119 120 | 
             
                            SELECT a.attnum, a.attname
         | 
| 120 121 | 
             
                            FROM pg_attribute a
         | 
| 121 122 | 
             
                            WHERE a.attrelid = #{oid}
         | 
| @@ -158,7 +159,7 @@ module ActiveRecord | |
| 158 159 | 
             
                    def table_comment(table_name) # :nodoc:
         | 
| 159 160 | 
             
                      scope = quoted_scope(table_name, type: "BASE TABLE")
         | 
| 160 161 | 
             
                      if scope[:name]
         | 
| 161 | 
            -
                        query_value( | 
| 162 | 
            +
                        query_value(<<~SQL, "SCHEMA")
         | 
| 162 163 | 
             
                          SELECT pg_catalog.obj_description(c.oid, 'pg_class')
         | 
| 163 164 | 
             
                          FROM pg_catalog.pg_class c
         | 
| 164 165 | 
             
                            LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
         | 
| @@ -196,7 +197,7 @@ module ActiveRecord | |
| 196 197 |  | 
| 197 198 | 
             
                    # Returns an array of schema names.
         | 
| 198 199 | 
             
                    def schema_names
         | 
| 199 | 
            -
                      query_values( | 
| 200 | 
            +
                      query_values(<<~SQL, "SCHEMA")
         | 
| 200 201 | 
             
                        SELECT nspname
         | 
| 201 202 | 
             
                          FROM pg_namespace
         | 
| 202 203 | 
             
                         WHERE nspname !~ '^pg_.*'
         | 
| @@ -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 |  | 
| @@ -287,7 +288,7 @@ module ActiveRecord | |
| 287 288 | 
             
                        quoted_sequence = quote_table_name(sequence)
         | 
| 288 289 | 
             
                        max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
         | 
| 289 290 | 
             
                        if max_pk.nil?
         | 
| 290 | 
            -
                          if  | 
| 291 | 
            +
                          if database_version >= 100000
         | 
| 291 292 | 
             
                            minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
         | 
| 292 293 | 
             
                          else
         | 
| 293 294 | 
             
                            minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
         | 
| @@ -302,7 +303,7 @@ module ActiveRecord | |
| 302 303 | 
             
                    def pk_and_sequence_for(table) #:nodoc:
         | 
| 303 304 | 
             
                      # First try looking for a sequence with a dependency on the
         | 
| 304 305 | 
             
                      # given table's primary key.
         | 
| 305 | 
            -
                      result = query( | 
| 306 | 
            +
                      result = query(<<~SQL, "SCHEMA")[0]
         | 
| 306 307 | 
             
                        SELECT attr.attname, nsp.nspname, seq.relname
         | 
| 307 308 | 
             
                        FROM pg_class      seq,
         | 
| 308 309 | 
             
                             pg_attribute  attr,
         | 
| @@ -319,10 +320,10 @@ module ActiveRecord | |
| 319 320 | 
             
                          AND cons.contype      = 'p'
         | 
| 320 321 | 
             
                          AND dep.classid       = 'pg_class'::regclass
         | 
| 321 322 | 
             
                          AND dep.refobjid      = #{quote(quote_table_name(table))}::regclass
         | 
| 322 | 
            -
                       | 
| 323 | 
            +
                      SQL
         | 
| 323 324 |  | 
| 324 325 | 
             
                      if result.nil? || result.empty?
         | 
| 325 | 
            -
                        result = query( | 
| 326 | 
            +
                        result = query(<<~SQL, "SCHEMA")[0]
         | 
| 326 327 | 
             
                          SELECT attr.attname, nsp.nspname,
         | 
| 327 328 | 
             
                            CASE
         | 
| 328 329 | 
             
                              WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
         | 
| @@ -339,7 +340,7 @@ module ActiveRecord | |
| 339 340 | 
             
                          WHERE t.oid = #{quote(quote_table_name(table))}::regclass
         | 
| 340 341 | 
             
                            AND cons.contype = 'p'
         | 
| 341 342 | 
             
                            AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
         | 
| 342 | 
            -
                         | 
| 343 | 
            +
                        SQL
         | 
| 343 344 | 
             
                      end
         | 
| 344 345 |  | 
| 345 346 | 
             
                      pk = result.shift
         | 
| @@ -353,7 +354,7 @@ module ActiveRecord | |
| 353 354 | 
             
                    end
         | 
| 354 355 |  | 
| 355 356 | 
             
                    def primary_keys(table_name) # :nodoc:
         | 
| 356 | 
            -
                      query_values( | 
| 357 | 
            +
                      query_values(<<~SQL, "SCHEMA")
         | 
| 357 358 | 
             
                        SELECT a.attname
         | 
| 358 359 | 
             
                          FROM (
         | 
| 359 360 | 
             
                                 SELECT indrelid, indkey, generate_subscripts(indkey, 1) idx
         | 
| @@ -368,31 +369,6 @@ module ActiveRecord | |
| 368 369 | 
             
                      SQL
         | 
| 369 370 | 
             
                    end
         | 
| 370 371 |  | 
| 371 | 
            -
                    def bulk_change_table(table_name, operations)
         | 
| 372 | 
            -
                      sql_fragments = []
         | 
| 373 | 
            -
                      non_combinable_operations = []
         | 
| 374 | 
            -
             | 
| 375 | 
            -
                      operations.each do |command, args|
         | 
| 376 | 
            -
                        table, arguments = args.shift, args
         | 
| 377 | 
            -
                        method = :"#{command}_for_alter"
         | 
| 378 | 
            -
             | 
| 379 | 
            -
                        if respond_to?(method, true)
         | 
| 380 | 
            -
                          sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
         | 
| 381 | 
            -
                          sql_fragments << sqls
         | 
| 382 | 
            -
                          non_combinable_operations.concat(procs)
         | 
| 383 | 
            -
                        else
         | 
| 384 | 
            -
                          execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
         | 
| 385 | 
            -
                          non_combinable_operations.each(&:call)
         | 
| 386 | 
            -
                          sql_fragments = []
         | 
| 387 | 
            -
                          non_combinable_operations = []
         | 
| 388 | 
            -
                          send(command, table, *arguments)
         | 
| 389 | 
            -
                        end
         | 
| 390 | 
            -
                      end
         | 
| 391 | 
            -
             | 
| 392 | 
            -
                      execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
         | 
| 393 | 
            -
                      non_combinable_operations.each(&:call)
         | 
| 394 | 
            -
                    end
         | 
| 395 | 
            -
             | 
| 396 372 | 
             
                    # Renames a table.
         | 
| 397 373 | 
             
                    # Also renames a table's primary key sequence if the sequence name exists and
         | 
| 398 374 | 
             
                    # matches the Active Record default.
         | 
| @@ -401,6 +377,8 @@ module ActiveRecord | |
| 401 377 | 
             
                    #   rename_table('octopuses', 'octopi')
         | 
| 402 378 | 
             
                    def rename_table(table_name, new_name)
         | 
| 403 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)
         | 
| 404 382 | 
             
                      execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
         | 
| 405 383 | 
             
                      pk, seq = pk_and_sequence_for(new_name)
         | 
| 406 384 | 
             
                      if pk
         | 
| @@ -415,15 +393,15 @@ module ActiveRecord | |
| 415 393 | 
             
                      rename_table_indexes(table_name, new_name)
         | 
| 416 394 | 
             
                    end
         | 
| 417 395 |  | 
| 418 | 
            -
                    def add_column(table_name, column_name, type, options | 
| 396 | 
            +
                    def add_column(table_name, column_name, type, **options) #:nodoc:
         | 
| 419 397 | 
             
                      clear_cache!
         | 
| 420 398 | 
             
                      super
         | 
| 421 399 | 
             
                      change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
         | 
| 422 400 | 
             
                    end
         | 
| 423 401 |  | 
| 424 | 
            -
                    def change_column(table_name, column_name, type, options | 
| 402 | 
            +
                    def change_column(table_name, column_name, type, **options) #:nodoc:
         | 
| 425 403 | 
             
                      clear_cache!
         | 
| 426 | 
            -
                      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) }
         | 
| 427 405 | 
             
                      execute "ALTER TABLE #{quote_table_name(table_name)} #{sqls.join(", ")}"
         | 
| 428 406 | 
             
                      procs.each(&:call)
         | 
| 429 407 | 
             
                    end
         | 
| @@ -443,35 +421,40 @@ module ActiveRecord | |
| 443 421 | 
             
                    end
         | 
| 444 422 |  | 
| 445 423 | 
             
                    # Adds comment for given table column or drops it if +comment+ is a +nil+
         | 
| 446 | 
            -
                    def change_column_comment(table_name, column_name,  | 
| 424 | 
            +
                    def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
         | 
| 447 425 | 
             
                      clear_cache!
         | 
| 426 | 
            +
                      comment = extract_new_comment_value(comment_or_changes)
         | 
| 448 427 | 
             
                      execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
         | 
| 449 428 | 
             
                    end
         | 
| 450 429 |  | 
| 451 430 | 
             
                    # Adds comment for given table or drops it if +comment+ is a +nil+
         | 
| 452 | 
            -
                    def change_table_comment(table_name,  | 
| 431 | 
            +
                    def change_table_comment(table_name, comment_or_changes) # :nodoc:
         | 
| 453 432 | 
             
                      clear_cache!
         | 
| 433 | 
            +
                      comment = extract_new_comment_value(comment_or_changes)
         | 
| 454 434 | 
             
                      execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
         | 
| 455 435 | 
             
                    end
         | 
| 456 436 |  | 
| 457 437 | 
             
                    # Renames a column in a table.
         | 
| 458 438 | 
             
                    def rename_column(table_name, column_name, new_column_name) #:nodoc:
         | 
| 459 439 | 
             
                      clear_cache!
         | 
| 460 | 
            -
                      execute | 
| 440 | 
            +
                      execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
         | 
| 461 441 | 
             
                      rename_column_indexes(table_name, column_name, new_column_name)
         | 
| 462 442 | 
             
                    end
         | 
| 463 443 |  | 
| 464 | 
            -
                    def add_index(table_name, column_name, options | 
| 465 | 
            -
                       | 
| 466 | 
            -
             | 
| 467 | 
            -
             | 
| 468 | 
            -
                       | 
| 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
         | 
| 469 452 | 
             
                    end
         | 
| 470 453 |  | 
| 471 | 
            -
                    def remove_index(table_name,  | 
| 454 | 
            +
                    def remove_index(table_name, column_name = nil, **options) # :nodoc:
         | 
| 472 455 | 
             
                      table = Utils.extract_schema_qualified_name(table_name.to_s)
         | 
| 473 456 |  | 
| 474 | 
            -
                      if options. | 
| 457 | 
            +
                      if options.key?(:name)
         | 
| 475 458 | 
             
                        provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
         | 
| 476 459 |  | 
| 477 460 | 
             
                        options[:name] = provided_index.identifier
         | 
| @@ -482,14 +465,11 @@ module ActiveRecord | |
| 482 465 | 
             
                        end
         | 
| 483 466 | 
             
                      end
         | 
| 484 467 |  | 
| 485 | 
            -
                       | 
| 486 | 
            -
             | 
| 487 | 
            -
             | 
| 488 | 
            -
             | 
| 489 | 
            -
             | 
| 490 | 
            -
                          end
         | 
| 491 | 
            -
                        end
         | 
| 492 | 
            -
                      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)}"
         | 
| 493 473 | 
             
                    end
         | 
| 494 474 |  | 
| 495 475 | 
             
                    # Renames an index of a table. Raises error if length of new
         | 
| @@ -502,7 +482,7 @@ module ActiveRecord | |
| 502 482 |  | 
| 503 483 | 
             
                    def foreign_keys(table_name)
         | 
| 504 484 | 
             
                      scope = quoted_scope(table_name)
         | 
| 505 | 
            -
                      fk_info = exec_query( | 
| 485 | 
            +
                      fk_info = exec_query(<<~SQL, "SCHEMA")
         | 
| 506 486 | 
             
                        SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid
         | 
| 507 487 | 
             
                        FROM pg_constraint c
         | 
| 508 488 | 
             
                        JOIN pg_class t1 ON c.conrelid = t1.oid
         | 
| @@ -539,6 +519,28 @@ module ActiveRecord | |
| 539 519 | 
             
                      query_values(data_source_sql(table_name, type: "FOREIGN TABLE"), "SCHEMA").any? if table_name.present?
         | 
| 540 520 | 
             
                    end
         | 
| 541 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 | 
            +
             | 
| 542 544 | 
             
                    # Maps logical Rails types to PostgreSQL-specific data types.
         | 
| 543 545 | 
             
                    def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
         | 
| 544 546 | 
             
                      sql = \
         | 
| @@ -548,21 +550,21 @@ module ActiveRecord | |
| 548 550 | 
             
                          # The hard limit is 1GB, because of a 32-bit size field, and TOAST.
         | 
| 549 551 | 
             
                          case limit
         | 
| 550 552 | 
             
                          when nil, 0..0x3fffffff; super(type)
         | 
| 551 | 
            -
                          else raise | 
| 553 | 
            +
                          else raise ArgumentError, "No binary type has byte size #{limit}. The limit on binary can be at most 1GB - 1byte."
         | 
| 552 554 | 
             
                          end
         | 
| 553 555 | 
             
                        when "text"
         | 
| 554 556 | 
             
                          # PostgreSQL doesn't support limits on text columns.
         | 
| 555 557 | 
             
                          # The hard limit is 1GB, according to section 8.3 in the manual.
         | 
| 556 558 | 
             
                          case limit
         | 
| 557 559 | 
             
                          when nil, 0..0x3fffffff; super(type)
         | 
| 558 | 
            -
                          else raise | 
| 560 | 
            +
                          else raise ArgumentError, "No text type has byte size #{limit}. The limit on text can be at most 1GB - 1byte."
         | 
| 559 561 | 
             
                          end
         | 
| 560 562 | 
             
                        when "integer"
         | 
| 561 563 | 
             
                          case limit
         | 
| 562 564 | 
             
                          when 1, 2; "smallint"
         | 
| 563 565 | 
             
                          when nil, 3, 4; "integer"
         | 
| 564 566 | 
             
                          when 5..8; "bigint"
         | 
| 565 | 
            -
                          else raise | 
| 567 | 
            +
                          else raise ArgumentError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead."
         | 
| 566 568 | 
             
                          end
         | 
| 567 569 | 
             
                        else
         | 
| 568 570 | 
             
                          super
         | 
| @@ -575,13 +577,13 @@ module ActiveRecord | |
| 575 577 | 
             
                    # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
         | 
| 576 578 | 
             
                    # requires that the ORDER BY include the distinct column.
         | 
| 577 579 | 
             
                    def columns_for_distinct(columns, orders) #:nodoc:
         | 
| 578 | 
            -
                      order_columns = orders. | 
| 579 | 
            -
             | 
| 580 | 
            -
             | 
| 581 | 
            -
             | 
| 582 | 
            -
             | 
| 583 | 
            -
             | 
| 584 | 
            -
             | 
| 580 | 
            +
                      order_columns = orders.compact_blank.map { |s|
         | 
| 581 | 
            +
                        # Convert Arel node to string
         | 
| 582 | 
            +
                        s = visitor.compile(s) unless s.is_a?(String)
         | 
| 583 | 
            +
                        # Remove any ASC/DESC modifiers
         | 
| 584 | 
            +
                        s.gsub(/\s+(?:ASC|DESC)\b/i, "")
         | 
| 585 | 
            +
                         .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
         | 
| 586 | 
            +
                      }.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
         | 
| 585 587 |  | 
| 586 588 | 
             
                      (order_columns << super).join(", ")
         | 
| 587 589 | 
             
                    end
         | 
| @@ -600,8 +602,6 @@ module ActiveRecord | |
| 600 602 | 
             
                    #
         | 
| 601 603 | 
             
                    #   validate_constraint :accounts, :constraint_name
         | 
| 602 604 | 
             
                    def validate_constraint(table_name, constraint_name)
         | 
| 603 | 
            -
                      return unless supports_validate_constraints?
         | 
| 604 | 
            -
             | 
| 605 605 | 
             
                      at = create_alter_table table_name
         | 
| 606 606 | 
             
                      at.validate_constraint constraint_name
         | 
| 607 607 |  | 
| @@ -623,21 +623,30 @@ module ActiveRecord | |
| 623 623 | 
             
                    #   validate_foreign_key :accounts, name: :special_fk_name
         | 
| 624 624 | 
             
                    #
         | 
| 625 625 | 
             
                    # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
         | 
| 626 | 
            -
                    def validate_foreign_key(from_table,  | 
| 627 | 
            -
                       | 
| 628 | 
            -
             | 
| 629 | 
            -
                      fk_name_to_validate = foreign_key_for!(from_table, options_or_to_table).name
         | 
| 626 | 
            +
                    def validate_foreign_key(from_table, to_table = nil, **options)
         | 
| 627 | 
            +
                      fk_name_to_validate = foreign_key_for!(from_table, to_table: to_table, **options).name
         | 
| 630 628 |  | 
| 631 629 | 
             
                      validate_constraint from_table, fk_name_to_validate
         | 
| 632 630 | 
             
                    end
         | 
| 633 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 | 
            +
             | 
| 634 643 | 
             
                    private
         | 
| 635 644 | 
             
                      def schema_creation
         | 
| 636 645 | 
             
                        PostgreSQL::SchemaCreation.new(self)
         | 
| 637 646 | 
             
                      end
         | 
| 638 647 |  | 
| 639 | 
            -
                      def create_table_definition( | 
| 640 | 
            -
                        PostgreSQL::TableDefinition.new( | 
| 648 | 
            +
                      def create_table_definition(name, **options)
         | 
| 649 | 
            +
                        PostgreSQL::TableDefinition.new(self, name, **options)
         | 
| 641 650 | 
             
                      end
         | 
| 642 651 |  | 
| 643 652 | 
             
                      def create_alter_table(name)
         | 
| @@ -650,16 +659,19 @@ module ActiveRecord | |
| 650 659 | 
             
                        default_value = extract_value_from_default(default)
         | 
| 651 660 | 
             
                        default_function = extract_default_function(default_value, default)
         | 
| 652 661 |  | 
| 653 | 
            -
                         | 
| 662 | 
            +
                        if match = default_function&.match(/\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z/)
         | 
| 663 | 
            +
                          serial = sequence_name_from_parts(table_name, column_name, match[:suffix]) == match[:sequence_name]
         | 
| 664 | 
            +
                        end
         | 
| 665 | 
            +
             | 
| 666 | 
            +
                        PostgreSQL::Column.new(
         | 
| 654 667 | 
             
                          column_name,
         | 
| 655 668 | 
             
                          default_value,
         | 
| 656 669 | 
             
                          type_metadata,
         | 
| 657 670 | 
             
                          !notnull,
         | 
| 658 | 
            -
                          table_name,
         | 
| 659 671 | 
             
                          default_function,
         | 
| 660 | 
            -
                          collation,
         | 
| 672 | 
            +
                          collation: collation,
         | 
| 661 673 | 
             
                          comment: comment.presence,
         | 
| 662 | 
            -
                           | 
| 674 | 
            +
                          serial: serial
         | 
| 663 675 | 
             
                        )
         | 
| 664 676 | 
             
                      end
         | 
| 665 677 |  | 
| @@ -672,7 +684,23 @@ module ActiveRecord | |
| 672 684 | 
             
                          precision: cast_type.precision,
         | 
| 673 685 | 
             
                          scale: cast_type.scale,
         | 
| 674 686 | 
             
                        )
         | 
| 675 | 
            -
                         | 
| 687 | 
            +
                        PostgreSQL::TypeMetadata.new(simple_type, oid: oid, fmod: fmod)
         | 
| 688 | 
            +
                      end
         | 
| 689 | 
            +
             | 
| 690 | 
            +
                      def sequence_name_from_parts(table_name, column_name, suffix)
         | 
| 691 | 
            +
                        over_length = [table_name, column_name, suffix].sum(&:length) + 2 - max_identifier_length
         | 
| 692 | 
            +
             | 
| 693 | 
            +
                        if over_length > 0
         | 
| 694 | 
            +
                          column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min
         | 
| 695 | 
            +
                          over_length -= column_name.length - column_name_length
         | 
| 696 | 
            +
                          column_name = column_name[0, column_name_length - [over_length, 0].min]
         | 
| 697 | 
            +
                        end
         | 
| 698 | 
            +
             | 
| 699 | 
            +
                        if over_length > 0
         | 
| 700 | 
            +
                          table_name = table_name[0, table_name.length - over_length]
         | 
| 701 | 
            +
                        end
         | 
| 702 | 
            +
             | 
| 703 | 
            +
                        "#{table_name}_#{column_name}_#{suffix}"
         | 
| 676 704 | 
             
                      end
         | 
| 677 705 |  | 
| 678 706 | 
             
                      def extract_foreign_key_action(specifier)
         | 
| @@ -683,14 +711,14 @@ module ActiveRecord | |
| 683 711 | 
             
                        end
         | 
| 684 712 | 
             
                      end
         | 
| 685 713 |  | 
| 686 | 
            -
                      def add_column_for_alter(table_name, column_name, type, options | 
| 714 | 
            +
                      def add_column_for_alter(table_name, column_name, type, **options)
         | 
| 687 715 | 
             
                        return super unless options.key?(:comment)
         | 
| 688 716 | 
             
                        [super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
         | 
| 689 717 | 
             
                      end
         | 
| 690 718 |  | 
| 691 | 
            -
                      def change_column_for_alter(table_name, column_name, type, options | 
| 719 | 
            +
                      def change_column_for_alter(table_name, column_name, type, **options)
         | 
| 692 720 | 
             
                        td = create_table_definition(table_name)
         | 
| 693 | 
            -
                        cd = td.new_column_definition(column_name, type, options)
         | 
| 721 | 
            +
                        cd = td.new_column_definition(column_name, type, **options)
         | 
| 694 722 | 
             
                        sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
         | 
| 695 723 | 
             
                        sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
         | 
| 696 724 | 
             
                        sqls
         | 
| @@ -715,14 +743,6 @@ module ActiveRecord | |
| 715 743 | 
             
                        "ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
         | 
| 716 744 | 
             
                      end
         | 
| 717 745 |  | 
| 718 | 
            -
                      def add_timestamps_for_alter(table_name, options = {})
         | 
| 719 | 
            -
                        [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
         | 
| 720 | 
            -
                      end
         | 
| 721 | 
            -
             | 
| 722 | 
            -
                      def remove_timestamps_for_alter(table_name, options = {})
         | 
| 723 | 
            -
                        [remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
         | 
| 724 | 
            -
                      end
         | 
| 725 | 
            -
             | 
| 726 746 | 
             
                      def add_index_opclass(quoted_columns, **options)
         | 
| 727 747 | 
             
                        opclasses = options_for_index_columns(options[:opclass])
         | 
| 728 748 | 
             
                        quoted_columns.each do |name, column|
         | 
| @@ -731,7 +751,7 @@ module ActiveRecord | |
| 731 751 | 
             
                      end
         | 
| 732 752 |  | 
| 733 753 | 
             
                      def add_options_for_index_columns(quoted_columns, **options)
         | 
| 734 | 
            -
                        quoted_columns = add_index_opclass(quoted_columns, options)
         | 
| 754 | 
            +
                        quoted_columns = add_index_opclass(quoted_columns, **options)
         | 
| 735 755 | 
             
                        super
         | 
| 736 756 | 
             
                      end
         | 
| 737 757 |  | 
| @@ -739,7 +759,7 @@ module ActiveRecord | |
| 739 759 | 
             
                        scope = quoted_scope(name, type: type)
         | 
| 740 760 | 
             
                        scope[:type] ||= "'r','v','m','p','f'" # (r)elation/table, (v)iew, (m)aterialized view, (p)artitioned table, (f)oreign table
         | 
| 741 761 |  | 
| 742 | 
            -
                        sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace" | 
| 762 | 
            +
                        sql = +"SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
         | 
| 743 763 | 
             
                        sql << " WHERE n.nspname = #{scope[:schema]}"
         | 
| 744 764 | 
             
                        sql << " AND c.relname = #{scope[:name]}" if scope[:name]
         | 
| 745 765 | 
             
                        sql << " AND c.relkind IN (#{scope[:type]})"
         | 
| @@ -1,39 +1,44 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            module ActiveRecord
         | 
| 4 | 
            +
              # :stopdoc:
         | 
| 4 5 | 
             
              module ConnectionAdapters
         | 
| 5 | 
            -
                 | 
| 6 | 
            -
                   | 
| 6 | 
            +
                module PostgreSQL
         | 
| 7 | 
            +
                  class TypeMetadata < DelegateClass(SqlTypeMetadata)
         | 
| 8 | 
            +
                    undef to_yaml if method_defined?(:to_yaml)
         | 
| 7 9 |  | 
| 8 | 
            -
             | 
| 10 | 
            +
                    include Deduplicable
         | 
| 9 11 |  | 
| 10 | 
            -
             | 
| 11 | 
            -
                    super(type_metadata)
         | 
| 12 | 
            -
                    @type_metadata = type_metadata
         | 
| 13 | 
            -
                    @oid = oid
         | 
| 14 | 
            -
                    @fmod = fmod
         | 
| 15 | 
            -
                    @array = /\[\]$/.match?(type_metadata.sql_type)
         | 
| 16 | 
            -
                  end
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                  def sql_type
         | 
| 19 | 
            -
                    super.gsub(/\[\]$/, "".freeze)
         | 
| 20 | 
            -
                  end
         | 
| 21 | 
            -
             | 
| 22 | 
            -
                  def ==(other)
         | 
| 23 | 
            -
                    other.is_a?(PostgreSQLTypeMetadata) &&
         | 
| 24 | 
            -
                      attributes_for_hash == other.attributes_for_hash
         | 
| 25 | 
            -
                  end
         | 
| 26 | 
            -
                  alias eql? ==
         | 
| 12 | 
            +
                    attr_reader :oid, :fmod
         | 
| 27 13 |  | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 14 | 
            +
                    def initialize(type_metadata, oid: nil, fmod: nil)
         | 
| 15 | 
            +
                      super(type_metadata)
         | 
| 16 | 
            +
                      @oid = oid
         | 
| 17 | 
            +
                      @fmod = fmod
         | 
| 18 | 
            +
                    end
         | 
| 31 19 |  | 
| 32 | 
            -
             | 
| 20 | 
            +
                    def ==(other)
         | 
| 21 | 
            +
                      other.is_a?(TypeMetadata) &&
         | 
| 22 | 
            +
                        __getobj__ == other.__getobj__ &&
         | 
| 23 | 
            +
                        oid == other.oid &&
         | 
| 24 | 
            +
                        fmod == other.fmod
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
                    alias eql? ==
         | 
| 33 27 |  | 
| 34 | 
            -
                    def  | 
| 35 | 
            -
                       | 
| 28 | 
            +
                    def hash
         | 
| 29 | 
            +
                      TypeMetadata.hash ^
         | 
| 30 | 
            +
                        __getobj__.hash ^
         | 
| 31 | 
            +
                        oid.hash ^
         | 
| 32 | 
            +
                        fmod.hash
         | 
| 36 33 | 
             
                    end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    private
         | 
| 36 | 
            +
                      def deduplicated
         | 
| 37 | 
            +
                        __setobj__(__getobj__.deduplicate)
         | 
| 38 | 
            +
                        super
         | 
| 39 | 
            +
                      end
         | 
| 40 | 
            +
                  end
         | 
| 37 41 | 
             
                end
         | 
| 42 | 
            +
                PostgreSQLTypeMetadata = PostgreSQL::TypeMetadata
         | 
| 38 43 | 
             
              end
         | 
| 39 44 | 
             
            end
         |