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
| @@ -1,6 +1,5 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require "active_record/migration/join_table"
         | 
| 4 3 | 
             
            require "active_support/core_ext/string/access"
         | 
| 5 4 | 
             
            require "digest/sha2"
         | 
| 6 5 |  | 
| @@ -97,10 +96,14 @@ module ActiveRecord | |
| 97 96 | 
             
                  #   # Check an index with a custom name exists
         | 
| 98 97 | 
             
                  #   index_exists?(:suppliers, :company_id, name: "idx_company_id")
         | 
| 99 98 | 
             
                  #
         | 
| 100 | 
            -
                  def index_exists?(table_name, column_name, options | 
| 101 | 
            -
                    column_names = Array(column_name).map(&:to_s)
         | 
| 99 | 
            +
                  def index_exists?(table_name, column_name, **options)
         | 
| 102 100 | 
             
                    checks = []
         | 
| 103 | 
            -
             | 
| 101 | 
            +
             | 
| 102 | 
            +
                    if column_name.present?
         | 
| 103 | 
            +
                      column_names = Array(column_name).map(&:to_s)
         | 
| 104 | 
            +
                      checks << lambda { |i| Array(i.columns) == column_names }
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
             | 
| 104 107 | 
             
                    checks << lambda { |i| i.unique } if options[:unique]
         | 
| 105 108 | 
             
                    checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
         | 
| 106 109 |  | 
| @@ -129,11 +132,11 @@ module ActiveRecord | |
| 129 132 | 
             
                  #   column_exists?(:suppliers, :name, :string, null: false)
         | 
| 130 133 | 
             
                  #   column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
         | 
| 131 134 | 
             
                  #
         | 
| 132 | 
            -
                  def column_exists?(table_name, column_name, type = nil, options | 
| 135 | 
            +
                  def column_exists?(table_name, column_name, type = nil, **options)
         | 
| 133 136 | 
             
                    column_name = column_name.to_s
         | 
| 134 137 | 
             
                    checks = []
         | 
| 135 138 | 
             
                    checks << lambda { |c| c.name == column_name }
         | 
| 136 | 
            -
                    checks << lambda { |c| c.type == type } if type
         | 
| 139 | 
            +
                    checks << lambda { |c| c.type == type.to_sym rescue nil } if type
         | 
| 137 140 | 
             
                    column_options_keys.each do |attr|
         | 
| 138 141 | 
             
                      checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
         | 
| 139 142 | 
             
                    end
         | 
| @@ -205,19 +208,22 @@ module ActiveRecord | |
| 205 208 | 
             
                  #   Set to true to drop the table before creating it.
         | 
| 206 209 | 
             
                  #   Set to +:cascade+ to drop dependent objects as well.
         | 
| 207 210 | 
             
                  #   Defaults to false.
         | 
| 211 | 
            +
                  # [<tt>:if_not_exists</tt>]
         | 
| 212 | 
            +
                  #   Set to true to avoid raising an error when the table already exists.
         | 
| 213 | 
            +
                  #   Defaults to false.
         | 
| 208 214 | 
             
                  # [<tt>:as</tt>]
         | 
| 209 215 | 
             
                  #   SQL to use to generate the table. When this option is used, the block is
         | 
| 210 216 | 
             
                  #   ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
         | 
| 211 217 | 
             
                  #
         | 
| 212 218 | 
             
                  # ====== Add a backend specific option to the generated SQL (MySQL)
         | 
| 213 219 | 
             
                  #
         | 
| 214 | 
            -
                  #   create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET= | 
| 220 | 
            +
                  #   create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
         | 
| 215 221 | 
             
                  #
         | 
| 216 222 | 
             
                  # generates:
         | 
| 217 223 | 
             
                  #
         | 
| 218 224 | 
             
                  #   CREATE TABLE suppliers (
         | 
| 219 225 | 
             
                  #     id bigint auto_increment PRIMARY KEY
         | 
| 220 | 
            -
                  #   ) ENGINE=InnoDB DEFAULT CHARSET= | 
| 226 | 
            +
                  #   ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
         | 
| 221 227 | 
             
                  #
         | 
| 222 228 | 
             
                  # ====== Rename the primary key column
         | 
| 223 229 | 
             
                  #
         | 
| @@ -287,37 +293,44 @@ module ActiveRecord | |
| 287 293 | 
             
                  #     SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
         | 
| 288 294 | 
             
                  #
         | 
| 289 295 | 
             
                  # See also TableDefinition#column for details on how to create columns.
         | 
| 290 | 
            -
                  def create_table(table_name,  | 
| 291 | 
            -
                    td = create_table_definition | 
| 296 | 
            +
                  def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
         | 
| 297 | 
            +
                    td = create_table_definition(table_name, **extract_table_options!(options))
         | 
| 292 298 |  | 
| 293 | 
            -
                    if  | 
| 294 | 
            -
                      pk =  | 
| 295 | 
            -
             | 
| 299 | 
            +
                    if id && !td.as
         | 
| 300 | 
            +
                      pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
         | 
| 301 | 
            +
             | 
| 302 | 
            +
                      if id.is_a?(Hash)
         | 
| 303 | 
            +
                        options.merge!(id.except(:type))
         | 
| 304 | 
            +
                        id = id.fetch(:type, :primary_key)
         | 
| 296 305 | 
             
                      end
         | 
| 297 306 |  | 
| 298 307 | 
             
                      if pk.is_a?(Array)
         | 
| 299 308 | 
             
                        td.primary_keys pk
         | 
| 300 309 | 
             
                      else
         | 
| 301 | 
            -
                        td.primary_key pk,  | 
| 310 | 
            +
                        td.primary_key pk, id, **options
         | 
| 302 311 | 
             
                      end
         | 
| 303 312 | 
             
                    end
         | 
| 304 313 |  | 
| 305 314 | 
             
                    yield td if block_given?
         | 
| 306 315 |  | 
| 307 | 
            -
                    if  | 
| 308 | 
            -
                      drop_table(table_name,  | 
| 316 | 
            +
                    if force
         | 
| 317 | 
            +
                      drop_table(table_name, force: force, if_exists: true)
         | 
| 318 | 
            +
                    else
         | 
| 319 | 
            +
                      schema_cache.clear_data_source_cache!(table_name.to_s)
         | 
| 309 320 | 
             
                    end
         | 
| 310 321 |  | 
| 311 322 | 
             
                    result = execute schema_creation.accept td
         | 
| 312 323 |  | 
| 313 324 | 
             
                    unless supports_indexes_in_create?
         | 
| 314 325 | 
             
                      td.indexes.each do |column_name, index_options|
         | 
| 315 | 
            -
                        add_index(table_name, column_name, index_options)
         | 
| 326 | 
            +
                        add_index(table_name, column_name, **index_options, if_not_exists: td.if_not_exists)
         | 
| 316 327 | 
             
                      end
         | 
| 317 328 | 
             
                    end
         | 
| 318 329 |  | 
| 319 330 | 
             
                    if supports_comments? && !supports_comments_in_create?
         | 
| 320 | 
            -
                       | 
| 331 | 
            +
                      if table_comment = td.comment.presence
         | 
| 332 | 
            +
                        change_table_comment(table_name, table_comment)
         | 
| 333 | 
            +
                      end
         | 
| 321 334 |  | 
| 322 335 | 
             
                      td.columns.each do |column|
         | 
| 323 336 | 
             
                        change_column_comment(table_name, column.name, column.comment) if column.comment.present?
         | 
| @@ -372,9 +385,9 @@ module ActiveRecord | |
| 372 385 |  | 
| 373 386 | 
             
                    t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
         | 
| 374 387 |  | 
| 375 | 
            -
                    create_table(join_table_name, options.merge!(id: false)) do |td|
         | 
| 376 | 
            -
                      td.references t1_ref, column_options
         | 
| 377 | 
            -
                      td.references t2_ref, column_options
         | 
| 388 | 
            +
                    create_table(join_table_name, **options.merge!(id: false)) do |td|
         | 
| 389 | 
            +
                      td.references t1_ref, **column_options
         | 
| 390 | 
            +
                      td.references t2_ref, **column_options
         | 
| 378 391 | 
             
                      yield td if block_given?
         | 
| 379 392 | 
             
                    end
         | 
| 380 393 | 
             
                  end
         | 
| @@ -385,7 +398,7 @@ module ActiveRecord | |
| 385 398 | 
             
                  # Although this command ignores the block if one is given, it can be helpful
         | 
| 386 399 | 
             
                  # to provide one in a migration's +change+ method so it can be reverted.
         | 
| 387 400 | 
             
                  # In that case, the block will be used by #create_join_table.
         | 
| 388 | 
            -
                  def drop_join_table(table_1, table_2, options | 
| 401 | 
            +
                  def drop_join_table(table_1, table_2, **options)
         | 
| 389 402 | 
             
                    join_table_name = find_join_table_name(table_1, table_2, options)
         | 
| 390 403 | 
             
                    drop_table(join_table_name)
         | 
| 391 404 | 
             
                  end
         | 
| @@ -414,6 +427,12 @@ module ActiveRecord | |
| 414 427 | 
             
                  #     t.column :name, :string, limit: 60
         | 
| 415 428 | 
             
                  #   end
         | 
| 416 429 | 
             
                  #
         | 
| 430 | 
            +
                  # ====== Change type of a column
         | 
| 431 | 
            +
                  #
         | 
| 432 | 
            +
                  #   change_table(:suppliers) do |t|
         | 
| 433 | 
            +
                  #     t.change :metadata, :json
         | 
| 434 | 
            +
                  #   end
         | 
| 435 | 
            +
                  #
         | 
| 417 436 | 
             
                  # ====== Add 2 integer columns
         | 
| 418 437 | 
             
                  #
         | 
| 419 438 | 
             
                  #   change_table(:suppliers) do |t|
         | 
| @@ -462,7 +481,7 @@ module ActiveRecord | |
| 462 481 | 
             
                  #  end
         | 
| 463 482 | 
             
                  #
         | 
| 464 483 | 
             
                  # See also Table for details on all of the various column transformations.
         | 
| 465 | 
            -
                  def change_table(table_name, options | 
| 484 | 
            +
                  def change_table(table_name, **options)
         | 
| 466 485 | 
             
                    if supports_bulk_alter? && options[:bulk]
         | 
| 467 486 | 
             
                      recorder = ActiveRecord::Migration::CommandRecorder.new(self)
         | 
| 468 487 | 
             
                      yield update_table_definition(table_name, recorder)
         | 
| @@ -492,7 +511,8 @@ module ActiveRecord | |
| 492 511 | 
             
                  # Although this command ignores most +options+ and the block if one is given,
         | 
| 493 512 | 
             
                  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
         | 
| 494 513 | 
             
                  # In that case, +options+ and the block will be used by #create_table.
         | 
| 495 | 
            -
                  def drop_table(table_name, options | 
| 514 | 
            +
                  def drop_table(table_name, **options)
         | 
| 515 | 
            +
                    schema_cache.clear_data_source_cache!(table_name.to_s)
         | 
| 496 516 | 
             
                    execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
         | 
| 497 517 | 
             
                  end
         | 
| 498 518 |  | 
| @@ -512,18 +532,25 @@ module ActiveRecord | |
| 512 532 | 
             
                  # Available options are (none of these exists by default):
         | 
| 513 533 | 
             
                  # * <tt>:limit</tt> -
         | 
| 514 534 | 
             
                  #   Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
         | 
| 515 | 
            -
                  #   and number of bytes for <tt>:text</tt>, <tt>:binary</tt | 
| 535 | 
            +
                  #   and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
         | 
| 516 536 | 
             
                  #   This option is ignored by some backends.
         | 
| 517 537 | 
             
                  # * <tt>:default</tt> -
         | 
| 518 538 | 
             
                  #   The column's default value. Use +nil+ for +NULL+.
         | 
| 519 539 | 
             
                  # * <tt>:null</tt> -
         | 
| 520 540 | 
             
                  #   Allows or disallows +NULL+ values in the column.
         | 
| 521 541 | 
             
                  # * <tt>:precision</tt> -
         | 
| 522 | 
            -
                  #   Specifies the precision for the <tt>:decimal</tt | 
| 542 | 
            +
                  #   Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
         | 
| 543 | 
            +
                  #   <tt>:datetime</tt>, and <tt>:time</tt> columns.
         | 
| 523 544 | 
             
                  # * <tt>:scale</tt> -
         | 
| 524 545 | 
             
                  #   Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
         | 
| 546 | 
            +
                  # * <tt>:collation</tt> -
         | 
| 547 | 
            +
                  #   Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column. If not specified, the
         | 
| 548 | 
            +
                  #   column will have the same collation as the table.
         | 
| 525 549 | 
             
                  # * <tt>:comment</tt> -
         | 
| 526 550 | 
             
                  #   Specifies the comment for the column. This option is ignored by some backends.
         | 
| 551 | 
            +
                  # * <tt>:if_not_exists</tt> -
         | 
| 552 | 
            +
                  #   Specifies if the column already exists to not try to re-add it. This will avoid
         | 
| 553 | 
            +
                  #   duplicate column errors.
         | 
| 527 554 | 
             
                  #
         | 
| 528 555 | 
             
                  # Note: The precision is the total number of significant digits,
         | 
| 529 556 | 
             
                  # and the scale is the number of digits that can be stored following
         | 
| @@ -544,8 +571,6 @@ module ActiveRecord | |
| 544 571 | 
             
                  #   but the maximum supported <tt>:precision</tt> is 16. No default.
         | 
| 545 572 | 
             
                  # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
         | 
| 546 573 | 
             
                  #   Default is (38,0).
         | 
| 547 | 
            -
                  # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
         | 
| 548 | 
            -
                  #   Default unknown.
         | 
| 549 574 | 
             
                  # * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
         | 
| 550 575 | 
             
                  #   Default (38,0).
         | 
| 551 576 | 
             
                  #
         | 
| @@ -575,20 +600,37 @@ module ActiveRecord | |
| 575 600 | 
             
                  #  # Defines a column with a database-specific type.
         | 
| 576 601 | 
             
                  #  add_column(:shapes, :triangle, 'polygon')
         | 
| 577 602 | 
             
                  #  # ALTER TABLE "shapes" ADD "triangle" polygon
         | 
| 578 | 
            -
                   | 
| 603 | 
            +
                  #
         | 
| 604 | 
            +
                  #  # Ignores the method call if the column exists
         | 
| 605 | 
            +
                  #  add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
         | 
| 606 | 
            +
                  def add_column(table_name, column_name, type, **options)
         | 
| 607 | 
            +
                    return if options[:if_not_exists] == true && column_exists?(table_name, column_name, type)
         | 
| 608 | 
            +
             | 
| 579 609 | 
             
                    at = create_alter_table table_name
         | 
| 580 | 
            -
                    at.add_column(column_name, type, options)
         | 
| 610 | 
            +
                    at.add_column(column_name, type, **options)
         | 
| 581 611 | 
             
                    execute schema_creation.accept at
         | 
| 582 612 | 
             
                  end
         | 
| 583 613 |  | 
| 614 | 
            +
                  def add_columns(table_name, *column_names, type:, **options) # :nodoc:
         | 
| 615 | 
            +
                    column_names.each do |column_name|
         | 
| 616 | 
            +
                      add_column(table_name, column_name, type, **options)
         | 
| 617 | 
            +
                    end
         | 
| 618 | 
            +
                  end
         | 
| 619 | 
            +
             | 
| 584 620 | 
             
                  # Removes the given columns from the table definition.
         | 
| 585 621 | 
             
                  #
         | 
| 586 622 | 
             
                  #   remove_columns(:suppliers, :qualification, :experience)
         | 
| 587 623 | 
             
                  #
         | 
| 588 | 
            -
                   | 
| 589 | 
            -
             | 
| 624 | 
            +
                  # +type+ and other column options can be passed to make migration reversible.
         | 
| 625 | 
            +
                  #
         | 
| 626 | 
            +
                  #    remove_columns(:suppliers, :qualification, :experience, type: :string, null: false)
         | 
| 627 | 
            +
                  def remove_columns(table_name, *column_names, type: nil, **options)
         | 
| 628 | 
            +
                    if column_names.empty?
         | 
| 629 | 
            +
                      raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
         | 
| 630 | 
            +
                    end
         | 
| 631 | 
            +
             | 
| 590 632 | 
             
                    column_names.each do |column_name|
         | 
| 591 | 
            -
                      remove_column(table_name, column_name)
         | 
| 633 | 
            +
                      remove_column(table_name, column_name, type, **options)
         | 
| 592 634 | 
             
                    end
         | 
| 593 635 | 
             
                  end
         | 
| 594 636 |  | 
| @@ -599,8 +641,17 @@ module ActiveRecord | |
| 599 641 | 
             
                  # The +type+ and +options+ parameters will be ignored if present. It can be helpful
         | 
| 600 642 | 
             
                  # to provide these in a migration's +change+ method so it can be reverted.
         | 
| 601 643 | 
             
                  # In that case, +type+ and +options+ will be used by #add_column.
         | 
| 602 | 
            -
                   | 
| 603 | 
            -
             | 
| 644 | 
            +
                  # Indexes on the column are automatically removed.
         | 
| 645 | 
            +
                  #
         | 
| 646 | 
            +
                  # If the options provided include an +if_exists+ key, it will be used to check if the
         | 
| 647 | 
            +
                  # column does not exist. This will silently ignore the migration rather than raising
         | 
| 648 | 
            +
                  # if the column was already used.
         | 
| 649 | 
            +
                  #
         | 
| 650 | 
            +
                  #   remove_column(:suppliers, :qualification, if_exists: true)
         | 
| 651 | 
            +
                  def remove_column(table_name, column_name, type = nil, **options)
         | 
| 652 | 
            +
                    return if options[:if_exists] == true && !column_exists?(table_name, column_name)
         | 
| 653 | 
            +
             | 
| 654 | 
            +
                    execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, **options)}"
         | 
| 604 655 | 
             
                  end
         | 
| 605 656 |  | 
| 606 657 | 
             
                  # Changes the column's definition according to the new options.
         | 
| @@ -609,7 +660,7 @@ module ActiveRecord | |
| 609 660 | 
             
                  #   change_column(:suppliers, :name, :string, limit: 80)
         | 
| 610 661 | 
             
                  #   change_column(:accounts, :description, :text)
         | 
| 611 662 | 
             
                  #
         | 
| 612 | 
            -
                  def change_column(table_name, column_name, type, options | 
| 663 | 
            +
                  def change_column(table_name, column_name, type, **options)
         | 
| 613 664 | 
             
                    raise NotImplementedError, "change_column is not implemented"
         | 
| 614 665 | 
             
                  end
         | 
| 615 666 |  | 
| @@ -671,7 +722,17 @@ module ActiveRecord | |
| 671 722 | 
             
                  #
         | 
| 672 723 | 
             
                  # generates:
         | 
| 673 724 | 
             
                  #
         | 
| 674 | 
            -
                  #   CREATE INDEX  | 
| 725 | 
            +
                  #   CREATE INDEX index_suppliers_on_name ON suppliers(name)
         | 
| 726 | 
            +
                  #
         | 
| 727 | 
            +
                  # ====== Creating a index which already exists
         | 
| 728 | 
            +
                  #
         | 
| 729 | 
            +
                  #   add_index(:suppliers, :name, if_not_exists: true)
         | 
| 730 | 
            +
                  #
         | 
| 731 | 
            +
                  # generates:
         | 
| 732 | 
            +
                  #
         | 
| 733 | 
            +
                  #   CREATE INDEX IF NOT EXISTS index_suppliers_on_name ON suppliers(name)
         | 
| 734 | 
            +
                  #
         | 
| 735 | 
            +
                  # Note: Not supported by MySQL.
         | 
| 675 736 | 
             
                  #
         | 
| 676 737 | 
             
                  # ====== Creating a unique index
         | 
| 677 738 | 
             
                  #
         | 
| @@ -679,7 +740,7 @@ module ActiveRecord | |
| 679 740 | 
             
                  #
         | 
| 680 741 | 
             
                  # generates:
         | 
| 681 742 | 
             
                  #
         | 
| 682 | 
            -
                  #   CREATE UNIQUE INDEX  | 
| 743 | 
            +
                  #   CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id)
         | 
| 683 744 | 
             
                  #
         | 
| 684 745 | 
             
                  # ====== Creating a named index
         | 
| 685 746 | 
             
                  #
         | 
| @@ -709,7 +770,7 @@ module ActiveRecord | |
| 709 770 | 
             
                  #
         | 
| 710 771 | 
             
                  # ====== Creating an index with a sort order (desc or asc, asc is the default)
         | 
| 711 772 | 
             
                  #
         | 
| 712 | 
            -
                  #   add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
         | 
| 773 | 
            +
                  #   add_index(:accounts, [:branch_id, :party_id, :surname], name: 'by_branch_desc_party', order: {branch_id: :desc, party_id: :asc})
         | 
| 713 774 | 
             
                  #
         | 
| 714 775 | 
             
                  # generates:
         | 
| 715 776 | 
             
                  #
         | 
| @@ -725,7 +786,7 @@ module ActiveRecord | |
| 725 786 | 
             
                  #
         | 
| 726 787 | 
             
                  #   CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
         | 
| 727 788 | 
             
                  #
         | 
| 728 | 
            -
                  # Note: Partial indexes are only supported for PostgreSQL and SQLite | 
| 789 | 
            +
                  # Note: Partial indexes are only supported for PostgreSQL and SQLite.
         | 
| 729 790 | 
             
                  #
         | 
| 730 791 | 
             
                  # ====== Creating an index with a specific method
         | 
| 731 792 | 
             
                  #
         | 
| @@ -760,9 +821,22 @@ module ActiveRecord | |
| 760 821 | 
             
                  #   CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
         | 
| 761 822 | 
             
                  #
         | 
| 762 823 | 
             
                  # Note: only supported by MySQL.
         | 
| 763 | 
            -
                   | 
| 764 | 
            -
             | 
| 765 | 
            -
             | 
| 824 | 
            +
                  #
         | 
| 825 | 
            +
                  # ====== Creating an index with a specific algorithm
         | 
| 826 | 
            +
                  #
         | 
| 827 | 
            +
                  #  add_index(:developers, :name, algorithm: :concurrently)
         | 
| 828 | 
            +
                  #  # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
         | 
| 829 | 
            +
                  #
         | 
| 830 | 
            +
                  # Note: only supported by PostgreSQL.
         | 
| 831 | 
            +
                  #
         | 
| 832 | 
            +
                  # Concurrently adding an index is not supported in a transaction.
         | 
| 833 | 
            +
                  #
         | 
| 834 | 
            +
                  # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
         | 
| 835 | 
            +
                  def add_index(table_name, column_name, **options)
         | 
| 836 | 
            +
                    index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
         | 
| 837 | 
            +
             | 
| 838 | 
            +
                    create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
         | 
| 839 | 
            +
                    execute schema_creation.accept(create_index)
         | 
| 766 840 | 
             
                  end
         | 
| 767 841 |  | 
| 768 842 | 
             
                  # Removes the given index from the table.
         | 
| @@ -783,8 +857,29 @@ module ActiveRecord | |
| 783 857 | 
             
                  #
         | 
| 784 858 | 
             
                  #   remove_index :accounts, name: :by_branch_party
         | 
| 785 859 | 
             
                  #
         | 
| 786 | 
            -
                   | 
| 787 | 
            -
             | 
| 860 | 
            +
                  # Removes the index on +branch_id+ named +by_branch_party+ in the +accounts+ table.
         | 
| 861 | 
            +
                  #
         | 
| 862 | 
            +
                  #   remove_index :accounts, :branch_id, name: :by_branch_party
         | 
| 863 | 
            +
                  #
         | 
| 864 | 
            +
                  # Checks if the index exists before trying to remove it. Will silently ignore indexes that
         | 
| 865 | 
            +
                  # don't exist.
         | 
| 866 | 
            +
                  #
         | 
| 867 | 
            +
                  #   remove_index :accounts, if_exists: true
         | 
| 868 | 
            +
                  #
         | 
| 869 | 
            +
                  # Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
         | 
| 870 | 
            +
                  #
         | 
| 871 | 
            +
                  #   remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
         | 
| 872 | 
            +
                  #
         | 
| 873 | 
            +
                  # Note: only supported by PostgreSQL.
         | 
| 874 | 
            +
                  #
         | 
| 875 | 
            +
                  # Concurrently removing an index is not supported in a transaction.
         | 
| 876 | 
            +
                  #
         | 
| 877 | 
            +
                  # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
         | 
| 878 | 
            +
                  def remove_index(table_name, column_name = nil, **options)
         | 
| 879 | 
            +
                    return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
         | 
| 880 | 
            +
             | 
| 881 | 
            +
                    index_name = index_name_for_remove(table_name, column_name, options)
         | 
| 882 | 
            +
             | 
| 788 883 | 
             
                    execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
         | 
| 789 884 | 
             
                  end
         | 
| 790 885 |  | 
| @@ -795,6 +890,8 @@ module ActiveRecord | |
| 795 890 | 
             
                  #   rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
         | 
| 796 891 | 
             
                  #
         | 
| 797 892 | 
             
                  def rename_index(table_name, old_name, new_name)
         | 
| 893 | 
            +
                    old_name = old_name.to_s
         | 
| 894 | 
            +
                    new_name = new_name.to_s
         | 
| 798 895 | 
             
                    validate_index_length!(table_name, new_name)
         | 
| 799 896 |  | 
| 800 897 | 
             
                    # this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
         | 
| @@ -836,23 +933,25 @@ module ActiveRecord | |
| 836 933 | 
             
                  #   Add an appropriate index. Defaults to true.
         | 
| 837 934 | 
             
                  #   See #add_index for usage of this option.
         | 
| 838 935 | 
             
                  # [<tt>:foreign_key</tt>]
         | 
| 839 | 
            -
                  #   Add an appropriate foreign key constraint. Defaults to false | 
| 936 | 
            +
                  #   Add an appropriate foreign key constraint. Defaults to false, pass true
         | 
| 937 | 
            +
                  #   to add. In case the join table can't be inferred from the association
         | 
| 938 | 
            +
                  #   pass <tt>:to_table</tt> with the appropriate table name.
         | 
| 840 939 | 
             
                  # [<tt>:polymorphic</tt>]
         | 
| 841 940 | 
             
                  #   Whether an additional +_type+ column should be added. Defaults to false.
         | 
| 842 941 | 
             
                  # [<tt>:null</tt>]
         | 
| 843 942 | 
             
                  #   Whether the column allows nulls. Defaults to true.
         | 
| 844 943 | 
             
                  #
         | 
| 845 | 
            -
                  # ====== Create a user_id bigint column
         | 
| 944 | 
            +
                  # ====== Create a user_id bigint column without an index
         | 
| 846 945 | 
             
                  #
         | 
| 847 | 
            -
                  #   add_reference(:products, :user)
         | 
| 946 | 
            +
                  #   add_reference(:products, :user, index: false)
         | 
| 848 947 | 
             
                  #
         | 
| 849 948 | 
             
                  # ====== Create a user_id string column
         | 
| 850 949 | 
             
                  #
         | 
| 851 950 | 
             
                  #   add_reference(:products, :user, type: :string)
         | 
| 852 951 | 
             
                  #
         | 
| 853 | 
            -
                  # ====== Create supplier_id, supplier_type columns | 
| 952 | 
            +
                  # ====== Create supplier_id, supplier_type columns
         | 
| 854 953 | 
             
                  #
         | 
| 855 | 
            -
                  #   add_reference(:products, :supplier, polymorphic: true | 
| 954 | 
            +
                  #   add_reference(:products, :supplier, polymorphic: true)
         | 
| 856 955 | 
             
                  #
         | 
| 857 956 | 
             
                  # ====== Create a supplier_id column with a unique index
         | 
| 858 957 | 
             
                  #
         | 
| @@ -868,10 +967,10 @@ module ActiveRecord | |
| 868 967 | 
             
                  #
         | 
| 869 968 | 
             
                  # ====== Create a supplier_id column and a foreign key to the firms table
         | 
| 870 969 | 
             
                  #
         | 
| 871 | 
            -
                  #   add_reference(:products, :supplier, foreign_key: {to_table: :firms})
         | 
| 970 | 
            +
                  #   add_reference(:products, :supplier, foreign_key: { to_table: :firms })
         | 
| 872 971 | 
             
                  #
         | 
| 873 972 | 
             
                  def add_reference(table_name, ref_name, **options)
         | 
| 874 | 
            -
                    ReferenceDefinition.new(ref_name, options).add_to(update_table_definition(table_name, self))
         | 
| 973 | 
            +
                    ReferenceDefinition.new(ref_name, **options).add_to(update_table_definition(table_name, self))
         | 
| 875 974 | 
             
                  end
         | 
| 876 975 | 
             
                  alias :add_belongs_to :add_reference
         | 
| 877 976 |  | 
| @@ -880,7 +979,7 @@ module ActiveRecord | |
| 880 979 | 
             
                  #
         | 
| 881 980 | 
             
                  # ====== Remove the reference
         | 
| 882 981 | 
             
                  #
         | 
| 883 | 
            -
                  #   remove_reference(:products, :user, index:  | 
| 982 | 
            +
                  #   remove_reference(:products, :user, index: false)
         | 
| 884 983 | 
             
                  #
         | 
| 885 984 | 
             
                  # ====== Remove polymorphic reference
         | 
| 886 985 | 
             
                  #
         | 
| @@ -888,7 +987,7 @@ module ActiveRecord | |
| 888 987 | 
             
                  #
         | 
| 889 988 | 
             
                  # ====== Remove the reference with a foreign key
         | 
| 890 989 | 
             
                  #
         | 
| 891 | 
            -
                  #   remove_reference(:products, :user,  | 
| 990 | 
            +
                  #   remove_reference(:products, :user, foreign_key: true)
         | 
| 892 991 | 
             
                  #
         | 
| 893 992 | 
             
                  def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
         | 
| 894 993 | 
             
                    if foreign_key
         | 
| @@ -899,7 +998,7 @@ module ActiveRecord | |
| 899 998 | 
             
                        foreign_key_options = { to_table: reference_name }
         | 
| 900 999 | 
             
                      end
         | 
| 901 1000 | 
             
                      foreign_key_options[:column] ||= "#{ref_name}_id"
         | 
| 902 | 
            -
                      remove_foreign_key(table_name, foreign_key_options)
         | 
| 1001 | 
            +
                      remove_foreign_key(table_name, **foreign_key_options)
         | 
| 903 1002 | 
             
                    end
         | 
| 904 1003 |  | 
| 905 1004 | 
             
                    remove_column(table_name, "#{ref_name}_id")
         | 
| @@ -956,8 +1055,8 @@ module ActiveRecord | |
| 956 1055 | 
             
                  # [<tt>:on_update</tt>]
         | 
| 957 1056 | 
             
                  #   Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
         | 
| 958 1057 | 
             
                  # [<tt>:validate</tt>]
         | 
| 959 | 
            -
                  #   ( | 
| 960 | 
            -
                  def add_foreign_key(from_table, to_table, options | 
| 1058 | 
            +
                  #   (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
         | 
| 1059 | 
            +
                  def add_foreign_key(from_table, to_table, **options)
         | 
| 961 1060 | 
             
                    return unless supports_foreign_keys?
         | 
| 962 1061 |  | 
| 963 1062 | 
             
                    options = foreign_key_options(from_table, to_table, options)
         | 
| @@ -980,15 +1079,22 @@ module ActiveRecord | |
| 980 1079 | 
             
                  #
         | 
| 981 1080 | 
             
                  #   remove_foreign_key :accounts, column: :owner_id
         | 
| 982 1081 | 
             
                  #
         | 
| 1082 | 
            +
                  # Removes the foreign key on +accounts.owner_id+.
         | 
| 1083 | 
            +
                  #
         | 
| 1084 | 
            +
                  #   remove_foreign_key :accounts, to_table: :owners
         | 
| 1085 | 
            +
                  #
         | 
| 983 1086 | 
             
                  # Removes the foreign key named +special_fk_name+ on the +accounts+ table.
         | 
| 984 1087 | 
             
                  #
         | 
| 985 1088 | 
             
                  #   remove_foreign_key :accounts, name: :special_fk_name
         | 
| 986 1089 | 
             
                  #
         | 
| 987 | 
            -
                  # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key | 
| 988 | 
            -
                   | 
| 1090 | 
            +
                  # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
         | 
| 1091 | 
            +
                  # with an addition of
         | 
| 1092 | 
            +
                  # [<tt>:to_table</tt>]
         | 
| 1093 | 
            +
                  #   The name of the table that contains the referenced primary key.
         | 
| 1094 | 
            +
                  def remove_foreign_key(from_table, to_table = nil, **options)
         | 
| 989 1095 | 
             
                    return unless supports_foreign_keys?
         | 
| 990 1096 |  | 
| 991 | 
            -
                    fk_name_to_delete = foreign_key_for!(from_table,  | 
| 1097 | 
            +
                    fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
         | 
| 992 1098 |  | 
| 993 1099 | 
             
                    at = create_alter_table from_table
         | 
| 994 1100 | 
             
                    at.drop_foreign_key fk_name_to_delete
         | 
| @@ -1007,14 +1113,12 @@ module ActiveRecord | |
| 1007 1113 | 
             
                  #   # Checks to see if a foreign key with a custom name exists.
         | 
| 1008 1114 | 
             
                  #   foreign_key_exists?(:accounts, name: "special_fk_name")
         | 
| 1009 1115 | 
             
                  #
         | 
| 1010 | 
            -
                  def foreign_key_exists?(from_table,  | 
| 1011 | 
            -
                    foreign_key_for(from_table,  | 
| 1116 | 
            +
                  def foreign_key_exists?(from_table, to_table = nil, **options)
         | 
| 1117 | 
            +
                    foreign_key_for(from_table, to_table: to_table, **options).present?
         | 
| 1012 1118 | 
             
                  end
         | 
| 1013 1119 |  | 
| 1014 1120 | 
             
                  def foreign_key_column_for(table_name) # :nodoc:
         | 
| 1015 | 
            -
                     | 
| 1016 | 
            -
                    suffix = Base.table_name_suffix
         | 
| 1017 | 
            -
                    name = table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
         | 
| 1121 | 
            +
                    name = strip_table_name_prefix_and_suffix(table_name)
         | 
| 1018 1122 | 
             
                    "#{name.singularize}_id"
         | 
| 1019 1123 | 
             
                  end
         | 
| 1020 1124 |  | 
| @@ -1025,8 +1129,62 @@ module ActiveRecord | |
| 1025 1129 | 
             
                    options
         | 
| 1026 1130 | 
             
                  end
         | 
| 1027 1131 |  | 
| 1028 | 
            -
                   | 
| 1029 | 
            -
             | 
| 1132 | 
            +
                  # Returns an array of check constraints for the given table.
         | 
| 1133 | 
            +
                  # The check constraints are represented as CheckConstraintDefinition objects.
         | 
| 1134 | 
            +
                  def check_constraints(table_name)
         | 
| 1135 | 
            +
                    raise NotImplementedError
         | 
| 1136 | 
            +
                  end
         | 
| 1137 | 
            +
             | 
| 1138 | 
            +
                  # Adds a new check constraint to the table. +expression+ is a String
         | 
| 1139 | 
            +
                  # representation of verifiable boolean condition.
         | 
| 1140 | 
            +
                  #
         | 
| 1141 | 
            +
                  #   add_check_constraint :products, "price > 0", name: "price_check"
         | 
| 1142 | 
            +
                  #
         | 
| 1143 | 
            +
                  # generates:
         | 
| 1144 | 
            +
                  #
         | 
| 1145 | 
            +
                  #   ALTER TABLE "products" ADD CONSTRAINT price_check CHECK (price > 0)
         | 
| 1146 | 
            +
                  #
         | 
| 1147 | 
            +
                  # The +options+ hash can include the following keys:
         | 
| 1148 | 
            +
                  # [<tt>:name</tt>]
         | 
| 1149 | 
            +
                  #   The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
         | 
| 1150 | 
            +
                  # [<tt>:validate</tt>]
         | 
| 1151 | 
            +
                  #   (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
         | 
| 1152 | 
            +
                  def add_check_constraint(table_name, expression, **options)
         | 
| 1153 | 
            +
                    return unless supports_check_constraints?
         | 
| 1154 | 
            +
             | 
| 1155 | 
            +
                    options = check_constraint_options(table_name, expression, options)
         | 
| 1156 | 
            +
                    at = create_alter_table(table_name)
         | 
| 1157 | 
            +
                    at.add_check_constraint(expression, options)
         | 
| 1158 | 
            +
             | 
| 1159 | 
            +
                    execute schema_creation.accept(at)
         | 
| 1160 | 
            +
                  end
         | 
| 1161 | 
            +
             | 
| 1162 | 
            +
                  def check_constraint_options(table_name, expression, options) # :nodoc:
         | 
| 1163 | 
            +
                    options = options.dup
         | 
| 1164 | 
            +
                    options[:name] ||= check_constraint_name(table_name, expression: expression, **options)
         | 
| 1165 | 
            +
                    options
         | 
| 1166 | 
            +
                  end
         | 
| 1167 | 
            +
             | 
| 1168 | 
            +
                  # Removes the given check constraint from the table.
         | 
| 1169 | 
            +
                  #
         | 
| 1170 | 
            +
                  #   remove_check_constraint :products, name: "price_check"
         | 
| 1171 | 
            +
                  #
         | 
| 1172 | 
            +
                  # The +expression+ parameter will be ignored if present. It can be helpful
         | 
| 1173 | 
            +
                  # to provide this in a migration's +change+ method so it can be reverted.
         | 
| 1174 | 
            +
                  # In that case, +expression+ will be used by #add_check_constraint.
         | 
| 1175 | 
            +
                  def remove_check_constraint(table_name, expression = nil, **options)
         | 
| 1176 | 
            +
                    return unless supports_check_constraints?
         | 
| 1177 | 
            +
             | 
| 1178 | 
            +
                    chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
         | 
| 1179 | 
            +
             | 
| 1180 | 
            +
                    at = create_alter_table(table_name)
         | 
| 1181 | 
            +
                    at.drop_check_constraint(chk_name_to_delete)
         | 
| 1182 | 
            +
             | 
| 1183 | 
            +
                    execute schema_creation.accept(at)
         | 
| 1184 | 
            +
                  end
         | 
| 1185 | 
            +
             | 
| 1186 | 
            +
                  def dump_schema_information # :nodoc:
         | 
| 1187 | 
            +
                    versions = schema_migration.all_versions
         | 
| 1030 1188 | 
             
                    insert_versions_sql(versions) if versions.any?
         | 
| 1031 1189 | 
             
                  end
         | 
| 1032 1190 |  | 
| @@ -1034,15 +1192,12 @@ module ActiveRecord | |
| 1034 1192 | 
             
                    { primary_key: true }
         | 
| 1035 1193 | 
             
                  end
         | 
| 1036 1194 |  | 
| 1037 | 
            -
                  def assume_migrated_upto_version(version | 
| 1038 | 
            -
                    migrations_paths = Array(migrations_paths)
         | 
| 1195 | 
            +
                  def assume_migrated_upto_version(version)
         | 
| 1039 1196 | 
             
                    version = version.to_i
         | 
| 1040 | 
            -
                    sm_table = quote_table_name( | 
| 1197 | 
            +
                    sm_table = quote_table_name(schema_migration.table_name)
         | 
| 1041 1198 |  | 
| 1042 | 
            -
                    migrated =  | 
| 1043 | 
            -
                    versions = migration_context. | 
| 1044 | 
            -
                      migration_context.parse_migration_filename(file).first.to_i
         | 
| 1045 | 
            -
                    end
         | 
| 1199 | 
            +
                    migrated = migration_context.get_all_versions
         | 
| 1200 | 
            +
                    versions = migration_context.migrations.map(&:version)
         | 
| 1046 1201 |  | 
| 1047 1202 | 
             
                    unless migrated.include?(version)
         | 
| 1048 1203 | 
             
                      execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
         | 
| @@ -1053,13 +1208,7 @@ module ActiveRecord | |
| 1053 1208 | 
             
                      if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
         | 
| 1054 1209 | 
             
                        raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
         | 
| 1055 1210 | 
             
                      end
         | 
| 1056 | 
            -
                       | 
| 1057 | 
            -
                        execute insert_versions_sql(inserting)
         | 
| 1058 | 
            -
                      else
         | 
| 1059 | 
            -
                        inserting.each do |v|
         | 
| 1060 | 
            -
                          execute insert_versions_sql(v)
         | 
| 1061 | 
            -
                        end
         | 
| 1062 | 
            -
                      end
         | 
| 1211 | 
            +
                      execute insert_versions_sql(inserting)
         | 
| 1063 1212 | 
             
                    end
         | 
| 1064 1213 | 
             
                  end
         | 
| 1065 1214 |  | 
| @@ -1085,7 +1234,7 @@ module ActiveRecord | |
| 1085 1234 | 
             
                        if (0..6) === precision
         | 
| 1086 1235 | 
             
                          column_type_sql << "(#{precision})"
         | 
| 1087 1236 | 
             
                        else
         | 
| 1088 | 
            -
                          raise | 
| 1237 | 
            +
                          raise ArgumentError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6"
         | 
| 1089 1238 | 
             
                        end
         | 
| 1090 1239 | 
             
                      elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
         | 
| 1091 1240 | 
             
                        column_type_sql << "(#{limit})"
         | 
| @@ -1112,18 +1261,22 @@ module ActiveRecord | |
| 1112 1261 | 
             
                  #
         | 
| 1113 1262 | 
             
                  #   add_timestamps(:suppliers, null: true)
         | 
| 1114 1263 | 
             
                  #
         | 
| 1115 | 
            -
                  def add_timestamps(table_name, options | 
| 1264 | 
            +
                  def add_timestamps(table_name, **options)
         | 
| 1116 1265 | 
             
                    options[:null] = false if options[:null].nil?
         | 
| 1117 1266 |  | 
| 1118 | 
            -
                     | 
| 1119 | 
            -
             | 
| 1267 | 
            +
                    if !options.key?(:precision) && supports_datetime_with_precision?
         | 
| 1268 | 
            +
                      options[:precision] = 6
         | 
| 1269 | 
            +
                    end
         | 
| 1270 | 
            +
             | 
| 1271 | 
            +
                    add_column table_name, :created_at, :datetime, **options
         | 
| 1272 | 
            +
                    add_column table_name, :updated_at, :datetime, **options
         | 
| 1120 1273 | 
             
                  end
         | 
| 1121 1274 |  | 
| 1122 1275 | 
             
                  # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
         | 
| 1123 1276 | 
             
                  #
         | 
| 1124 1277 | 
             
                  #  remove_timestamps(:suppliers)
         | 
| 1125 1278 | 
             
                  #
         | 
| 1126 | 
            -
                  def remove_timestamps(table_name, options | 
| 1279 | 
            +
                  def remove_timestamps(table_name, **options)
         | 
| 1127 1280 | 
             
                    remove_column table_name, :updated_at
         | 
| 1128 1281 | 
             
                    remove_column table_name, :created_at
         | 
| 1129 1282 | 
             
                  end
         | 
| @@ -1132,36 +1285,43 @@ module ActiveRecord | |
| 1132 1285 | 
             
                    Table.new(table_name, base)
         | 
| 1133 1286 | 
             
                  end
         | 
| 1134 1287 |  | 
| 1135 | 
            -
                  def add_index_options(table_name, column_name,  | 
| 1136 | 
            -
                     | 
| 1288 | 
            +
                  def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
         | 
| 1289 | 
            +
                    options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm)
         | 
| 1137 1290 |  | 
| 1138 | 
            -
                     | 
| 1291 | 
            +
                    column_names = index_column_names(column_name)
         | 
| 1139 1292 |  | 
| 1140 | 
            -
                     | 
| 1141 | 
            -
                    index_type ||= options[:unique] ? "UNIQUE" : ""
         | 
| 1142 | 
            -
                    index_name = options[:name].to_s if options.key?(:name)
         | 
| 1293 | 
            +
                    index_name = name&.to_s
         | 
| 1143 1294 | 
             
                    index_name ||= index_name(table_name, column_names)
         | 
| 1144 1295 |  | 
| 1145 | 
            -
                     | 
| 1146 | 
            -
             | 
| 1147 | 
            -
             | 
| 1148 | 
            -
                       | 
| 1149 | 
            -
             | 
| 1150 | 
            -
             | 
| 1151 | 
            -
             | 
| 1152 | 
            -
             | 
| 1153 | 
            -
             | 
| 1154 | 
            -
                       | 
| 1155 | 
            -
             | 
| 1296 | 
            +
                    validate_index_length!(table_name, index_name, internal)
         | 
| 1297 | 
            +
             | 
| 1298 | 
            +
                    index = IndexDefinition.new(
         | 
| 1299 | 
            +
                      table_name, index_name,
         | 
| 1300 | 
            +
                      options[:unique],
         | 
| 1301 | 
            +
                      column_names,
         | 
| 1302 | 
            +
                      lengths: options[:length] || {},
         | 
| 1303 | 
            +
                      orders: options[:order] || {},
         | 
| 1304 | 
            +
                      opclasses: options[:opclass] || {},
         | 
| 1305 | 
            +
                      where: options[:where],
         | 
| 1306 | 
            +
                      type: options[:type],
         | 
| 1307 | 
            +
                      using: options[:using],
         | 
| 1308 | 
            +
                      comment: options[:comment]
         | 
| 1309 | 
            +
                    )
         | 
| 1310 | 
            +
             | 
| 1311 | 
            +
                    [index, index_algorithm(options[:algorithm]), if_not_exists]
         | 
| 1312 | 
            +
                  end
         | 
| 1156 1313 |  | 
| 1157 | 
            -
             | 
| 1314 | 
            +
                  def index_algorithm(algorithm) # :nodoc:
         | 
| 1315 | 
            +
                    index_algorithms.fetch(algorithm) do
         | 
| 1316 | 
            +
                      raise ArgumentError, "Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}"
         | 
| 1317 | 
            +
                    end if algorithm
         | 
| 1318 | 
            +
                  end
         | 
| 1158 1319 |  | 
| 1159 | 
            -
             | 
| 1160 | 
            -
             | 
| 1320 | 
            +
                  def quoted_columns_for_index(column_names, options) # :nodoc:
         | 
| 1321 | 
            +
                    quoted_columns = column_names.each_with_object({}) do |name, result|
         | 
| 1322 | 
            +
                      result[name.to_sym] = quote_column_name(name).dup
         | 
| 1161 1323 | 
             
                    end
         | 
| 1162 | 
            -
                     | 
| 1163 | 
            -
             | 
| 1164 | 
            -
                    [index_name, index_type, index_columns, index_options, algorithm, using, comment]
         | 
| 1324 | 
            +
                    add_options_for_index_columns(quoted_columns, **options).values.join(", ")
         | 
| 1165 1325 | 
             
                  end
         | 
| 1166 1326 |  | 
| 1167 1327 | 
             
                  def options_include_default?(options)
         | 
| @@ -1169,12 +1329,22 @@ module ActiveRecord | |
| 1169 1329 | 
             
                  end
         | 
| 1170 1330 |  | 
| 1171 1331 | 
             
                  # Changes the comment for a table or removes it if +nil+.
         | 
| 1172 | 
            -
                   | 
| 1332 | 
            +
                  #
         | 
| 1333 | 
            +
                  # Passing a hash containing +:from+ and +:to+ will make this change
         | 
| 1334 | 
            +
                  # reversible in migration:
         | 
| 1335 | 
            +
                  #
         | 
| 1336 | 
            +
                  #   change_table_comment(:posts, from: "old_comment", to: "new_comment")
         | 
| 1337 | 
            +
                  def change_table_comment(table_name, comment_or_changes)
         | 
| 1173 1338 | 
             
                    raise NotImplementedError, "#{self.class} does not support changing table comments"
         | 
| 1174 1339 | 
             
                  end
         | 
| 1175 1340 |  | 
| 1176 1341 | 
             
                  # Changes the comment for a column or removes it if +nil+.
         | 
| 1177 | 
            -
                   | 
| 1342 | 
            +
                  #
         | 
| 1343 | 
            +
                  # Passing a hash containing +:from+ and +:to+ will make this change
         | 
| 1344 | 
            +
                  # reversible in migration:
         | 
| 1345 | 
            +
                  #
         | 
| 1346 | 
            +
                  #   change_column_comment(:posts, :state, from: "old_comment", to: "new_comment")
         | 
| 1347 | 
            +
                  def change_column_comment(table_name, column_name, comment_or_changes)
         | 
| 1178 1348 | 
             
                    raise NotImplementedError, "#{self.class} does not support changing column comments"
         | 
| 1179 1349 | 
             
                  end
         | 
| 1180 1350 |  | 
| @@ -1206,30 +1376,19 @@ module ActiveRecord | |
| 1206 1376 | 
             
                    # the PostgreSQL adapter for supporting operator classes.
         | 
| 1207 1377 | 
             
                    def add_options_for_index_columns(quoted_columns, **options)
         | 
| 1208 1378 | 
             
                      if supports_index_sort_order?
         | 
| 1209 | 
            -
                        quoted_columns = add_index_sort_order(quoted_columns, options)
         | 
| 1379 | 
            +
                        quoted_columns = add_index_sort_order(quoted_columns, **options)
         | 
| 1210 1380 | 
             
                      end
         | 
| 1211 1381 |  | 
| 1212 1382 | 
             
                      quoted_columns
         | 
| 1213 1383 | 
             
                    end
         | 
| 1214 1384 |  | 
| 1215 | 
            -
                    def  | 
| 1216 | 
            -
                      return [ | 
| 1217 | 
            -
             | 
| 1218 | 
            -
                      quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
         | 
| 1219 | 
            -
                      add_options_for_index_columns(quoted_columns, options).values
         | 
| 1220 | 
            -
                    end
         | 
| 1221 | 
            -
             | 
| 1222 | 
            -
                    def index_name_for_remove(table_name, options = {})
         | 
| 1223 | 
            -
                      return options[:name] if can_remove_index_by_name?(options)
         | 
| 1385 | 
            +
                    def index_name_for_remove(table_name, column_name, options)
         | 
| 1386 | 
            +
                      return options[:name] if can_remove_index_by_name?(column_name, options)
         | 
| 1224 1387 |  | 
| 1225 1388 | 
             
                      checks = []
         | 
| 1226 1389 |  | 
| 1227 | 
            -
                      if options. | 
| 1228 | 
            -
             | 
| 1229 | 
            -
                        column_names = index_column_names(options[:column])
         | 
| 1230 | 
            -
                      else
         | 
| 1231 | 
            -
                        column_names = index_column_names(options)
         | 
| 1232 | 
            -
                      end
         | 
| 1390 | 
            +
                      checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
         | 
| 1391 | 
            +
                      column_names = index_column_names(column_name || options[:column])
         | 
| 1233 1392 |  | 
| 1234 1393 | 
             
                      if column_names.present?
         | 
| 1235 1394 | 
             
                        checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
         | 
| @@ -1275,14 +1434,18 @@ module ActiveRecord | |
| 1275 1434 | 
             
                      SchemaCreation.new(self)
         | 
| 1276 1435 | 
             
                    end
         | 
| 1277 1436 |  | 
| 1278 | 
            -
                    def create_table_definition( | 
| 1279 | 
            -
                      TableDefinition.new( | 
| 1437 | 
            +
                    def create_table_definition(name, **options)
         | 
| 1438 | 
            +
                      TableDefinition.new(self, name, **options)
         | 
| 1280 1439 | 
             
                    end
         | 
| 1281 1440 |  | 
| 1282 1441 | 
             
                    def create_alter_table(name)
         | 
| 1283 1442 | 
             
                      AlterTable.new create_table_definition(name)
         | 
| 1284 1443 | 
             
                    end
         | 
| 1285 1444 |  | 
| 1445 | 
            +
                    def extract_table_options!(options)
         | 
| 1446 | 
            +
                      options.extract!(:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation)
         | 
| 1447 | 
            +
                    end
         | 
| 1448 | 
            +
             | 
| 1286 1449 | 
             
                    def fetch_type_metadata(sql_type)
         | 
| 1287 1450 | 
             
                      cast_type = lookup_cast_type(sql_type)
         | 
| 1288 1451 | 
             
                      SqlTypeMetadata.new(
         | 
| @@ -1310,6 +1473,12 @@ module ActiveRecord | |
| 1310 1473 | 
             
                      { column: column_names }
         | 
| 1311 1474 | 
             
                    end
         | 
| 1312 1475 |  | 
| 1476 | 
            +
                    def strip_table_name_prefix_and_suffix(table_name)
         | 
| 1477 | 
            +
                      prefix = Base.table_name_prefix
         | 
| 1478 | 
            +
                      suffix = Base.table_name_suffix
         | 
| 1479 | 
            +
                      table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
         | 
| 1480 | 
            +
                    end
         | 
| 1481 | 
            +
             | 
| 1313 1482 | 
             
                    def foreign_key_name(table_name, options)
         | 
| 1314 1483 | 
             
                      options.fetch(:name) do
         | 
| 1315 1484 | 
             
                        identifier = "#{table_name}_#{options.fetch(:column)}_fk"
         | 
| @@ -1319,14 +1488,14 @@ module ActiveRecord | |
| 1319 1488 | 
             
                      end
         | 
| 1320 1489 | 
             
                    end
         | 
| 1321 1490 |  | 
| 1322 | 
            -
                    def foreign_key_for(from_table,  | 
| 1491 | 
            +
                    def foreign_key_for(from_table, **options)
         | 
| 1323 1492 | 
             
                      return unless supports_foreign_keys?
         | 
| 1324 | 
            -
                      foreign_keys(from_table).detect { |fk| fk.defined_for?  | 
| 1493 | 
            +
                      foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
         | 
| 1325 1494 | 
             
                    end
         | 
| 1326 1495 |  | 
| 1327 | 
            -
                    def foreign_key_for!(from_table,  | 
| 1328 | 
            -
                      foreign_key_for(from_table,  | 
| 1329 | 
            -
                        raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{ | 
| 1496 | 
            +
                    def foreign_key_for!(from_table, to_table: nil, **options)
         | 
| 1497 | 
            +
                      foreign_key_for(from_table, to_table: to_table, **options) ||
         | 
| 1498 | 
            +
                        raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
         | 
| 1330 1499 | 
             
                    end
         | 
| 1331 1500 |  | 
| 1332 1501 | 
             
                    def extract_foreign_key_action(specifier)
         | 
| @@ -1337,11 +1506,30 @@ module ActiveRecord | |
| 1337 1506 | 
             
                      end
         | 
| 1338 1507 | 
             
                    end
         | 
| 1339 1508 |  | 
| 1340 | 
            -
                    def  | 
| 1341 | 
            -
                       | 
| 1509 | 
            +
                    def check_constraint_name(table_name, **options)
         | 
| 1510 | 
            +
                      options.fetch(:name) do
         | 
| 1511 | 
            +
                        expression = options.fetch(:expression)
         | 
| 1512 | 
            +
                        identifier = "#{table_name}_#{expression}_chk"
         | 
| 1513 | 
            +
                        hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
         | 
| 1342 1514 |  | 
| 1343 | 
            -
             | 
| 1344 | 
            -
             | 
| 1515 | 
            +
                        "chk_rails_#{hashed_identifier}"
         | 
| 1516 | 
            +
                      end
         | 
| 1517 | 
            +
                    end
         | 
| 1518 | 
            +
             | 
| 1519 | 
            +
                    def check_constraint_for(table_name, **options)
         | 
| 1520 | 
            +
                      return unless supports_check_constraints?
         | 
| 1521 | 
            +
                      chk_name = check_constraint_name(table_name, **options)
         | 
| 1522 | 
            +
                      check_constraints(table_name).detect { |chk| chk.name == chk_name }
         | 
| 1523 | 
            +
                    end
         | 
| 1524 | 
            +
             | 
| 1525 | 
            +
                    def check_constraint_for!(table_name, expression: nil, **options)
         | 
| 1526 | 
            +
                      check_constraint_for(table_name, expression: expression, **options) ||
         | 
| 1527 | 
            +
                        raise(ArgumentError, "Table '#{table_name}' has no check constraint for #{expression || options}")
         | 
| 1528 | 
            +
                    end
         | 
| 1529 | 
            +
             | 
| 1530 | 
            +
                    def validate_index_length!(table_name, new_name, internal = false)
         | 
| 1531 | 
            +
                      if new_name.length > index_name_length
         | 
| 1532 | 
            +
                        raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
         | 
| 1345 1533 | 
             
                      end
         | 
| 1346 1534 | 
             
                    end
         | 
| 1347 1535 |  | 
| @@ -1352,30 +1540,77 @@ module ActiveRecord | |
| 1352 1540 | 
             
                        default_or_changes
         | 
| 1353 1541 | 
             
                      end
         | 
| 1354 1542 | 
             
                    end
         | 
| 1543 | 
            +
                    alias :extract_new_comment_value :extract_new_default_value
         | 
| 1355 1544 |  | 
| 1356 | 
            -
                    def can_remove_index_by_name?(options)
         | 
| 1357 | 
            -
                       | 
| 1545 | 
            +
                    def can_remove_index_by_name?(column_name, options)
         | 
| 1546 | 
            +
                      column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
         | 
| 1358 1547 | 
             
                    end
         | 
| 1359 1548 |  | 
| 1360 | 
            -
                    def  | 
| 1549 | 
            +
                    def bulk_change_table(table_name, operations)
         | 
| 1550 | 
            +
                      sql_fragments = []
         | 
| 1551 | 
            +
                      non_combinable_operations = []
         | 
| 1552 | 
            +
             | 
| 1553 | 
            +
                      operations.each do |command, args|
         | 
| 1554 | 
            +
                        table, arguments = args.shift, args
         | 
| 1555 | 
            +
                        method = :"#{command}_for_alter"
         | 
| 1556 | 
            +
             | 
| 1557 | 
            +
                        if respond_to?(method, true)
         | 
| 1558 | 
            +
                          sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
         | 
| 1559 | 
            +
                          sql_fragments << sqls
         | 
| 1560 | 
            +
                          non_combinable_operations.concat(procs)
         | 
| 1561 | 
            +
                        else
         | 
| 1562 | 
            +
                          execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
         | 
| 1563 | 
            +
                          non_combinable_operations.each(&:call)
         | 
| 1564 | 
            +
                          sql_fragments = []
         | 
| 1565 | 
            +
                          non_combinable_operations = []
         | 
| 1566 | 
            +
                          send(command, table, *arguments)
         | 
| 1567 | 
            +
                        end
         | 
| 1568 | 
            +
                      end
         | 
| 1569 | 
            +
             | 
| 1570 | 
            +
                      execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
         | 
| 1571 | 
            +
                      non_combinable_operations.each(&:call)
         | 
| 1572 | 
            +
                    end
         | 
| 1573 | 
            +
             | 
| 1574 | 
            +
                    def add_column_for_alter(table_name, column_name, type, **options)
         | 
| 1361 1575 | 
             
                      td = create_table_definition(table_name)
         | 
| 1362 | 
            -
                      cd = td.new_column_definition(column_name, type, options)
         | 
| 1576 | 
            +
                      cd = td.new_column_definition(column_name, type, **options)
         | 
| 1363 1577 | 
             
                      schema_creation.accept(AddColumnDefinition.new(cd))
         | 
| 1364 1578 | 
             
                    end
         | 
| 1365 1579 |  | 
| 1366 | 
            -
                    def  | 
| 1580 | 
            +
                    def rename_column_sql(table_name, column_name, new_column_name)
         | 
| 1581 | 
            +
                      "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
         | 
| 1582 | 
            +
                    end
         | 
| 1583 | 
            +
             | 
| 1584 | 
            +
                    def remove_column_for_alter(table_name, column_name, type = nil, **options)
         | 
| 1367 1585 | 
             
                      "DROP COLUMN #{quote_column_name(column_name)}"
         | 
| 1368 1586 | 
             
                    end
         | 
| 1369 1587 |  | 
| 1370 | 
            -
                    def remove_columns_for_alter(table_name, *column_names)
         | 
| 1588 | 
            +
                    def remove_columns_for_alter(table_name, *column_names, **options)
         | 
| 1371 1589 | 
             
                      column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
         | 
| 1372 1590 | 
             
                    end
         | 
| 1373 1591 |  | 
| 1592 | 
            +
                    def add_timestamps_for_alter(table_name, **options)
         | 
| 1593 | 
            +
                      options[:null] = false if options[:null].nil?
         | 
| 1594 | 
            +
             | 
| 1595 | 
            +
                      if !options.key?(:precision) && supports_datetime_with_precision?
         | 
| 1596 | 
            +
                        options[:precision] = 6
         | 
| 1597 | 
            +
                      end
         | 
| 1598 | 
            +
             | 
| 1599 | 
            +
                      [
         | 
| 1600 | 
            +
                        add_column_for_alter(table_name, :created_at, :datetime, **options),
         | 
| 1601 | 
            +
                        add_column_for_alter(table_name, :updated_at, :datetime, **options)
         | 
| 1602 | 
            +
                      ]
         | 
| 1603 | 
            +
                    end
         | 
| 1604 | 
            +
             | 
| 1605 | 
            +
                    def remove_timestamps_for_alter(table_name, **options)
         | 
| 1606 | 
            +
                      remove_columns_for_alter(table_name, :updated_at, :created_at)
         | 
| 1607 | 
            +
                    end
         | 
| 1608 | 
            +
             | 
| 1374 1609 | 
             
                    def insert_versions_sql(versions)
         | 
| 1375 | 
            -
                      sm_table = quote_table_name( | 
| 1610 | 
            +
                      sm_table = quote_table_name(schema_migration.table_name)
         | 
| 1376 1611 |  | 
| 1377 1612 | 
             
                      if versions.is_a?(Array)
         | 
| 1378 | 
            -
                        sql = "INSERT INTO #{sm_table} (version) VALUES\n" | 
| 1613 | 
            +
                        sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
         | 
| 1379 1614 | 
             
                        sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
         | 
| 1380 1615 | 
             
                        sql << ";\n\n"
         | 
| 1381 1616 | 
             
                        sql
         |