activerecord 5.2.8 → 7.0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1393 -587
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +10 -9
- 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 +122 -47
- data/lib/active_record/associations/association_scope.rb +24 -24
- data/lib/active_record/associations/belongs_to_association.rb +67 -49
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -7
- data/lib/active_record/associations/builder/association.rb +52 -23
- data/lib/active_record/associations/builder/belongs_to.rb +44 -61
- data/lib/active_record/associations/builder/collection_association.rb +17 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +10 -3
- data/lib/active_record/associations/builder/has_one.rb +35 -3
- data/lib/active_record/associations/builder/singular_association.rb +5 -3
- data/lib/active_record/associations/collection_association.rb +59 -50
- data/lib/active_record/associations/collection_proxy.rb +32 -23
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +27 -14
- data/lib/active_record/associations/has_many_through_association.rb +26 -19
- data/lib/active_record/associations/has_one_association.rb +52 -37
- data/lib/active_record/associations/has_one_through_association.rb +6 -6
- data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +97 -62
- data/lib/active_record/associations/preloader/association.rb +220 -60
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +85 -40
- data/lib/active_record/associations/preloader.rb +44 -105
- data/lib/active_record/associations/singular_association.rb +9 -17
- data/lib/active_record/associations/through_association.rb +4 -4
- data/lib/active_record/associations.rb +207 -66
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +19 -8
- data/lib/active_record/attribute_methods/dirty.rb +141 -47
- data/lib/active_record/attribute_methods/primary_key.rb +22 -27
- data/lib/active_record/attribute_methods/query.rb +6 -10
- data/lib/active_record/attribute_methods/read.rb +15 -55
- data/lib/active_record/attribute_methods/serialization.rb +77 -18
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +16 -18
- data/lib/active_record/attribute_methods/write.rb +18 -37
- data/lib/active_record/attribute_methods.rb +90 -153
- data/lib/active_record/attributes.rb +38 -12
- data/lib/active_record/autosave_association.rb +50 -50
- data/lib/active_record/base.rb +23 -18
- data/lib/active_record/callbacks.rb +159 -44
- data/lib/active_record/coders/yaml_column.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +92 -464
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -51
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +209 -164
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +38 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +103 -82
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +140 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -94
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +16 -5
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +456 -159
- data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
- data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -162
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +311 -327
- data/lib/active_record/connection_adapters/column.rb +33 -11
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +113 -45
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +71 -5
- 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 +25 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +143 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +63 -22
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +53 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +56 -63
- 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 +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +54 -16
- 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/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/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +26 -12
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -52
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +128 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +149 -113
- 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 +386 -182
- data/lib/active_record/connection_adapters/schema_cache.rb +161 -22
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +65 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +92 -26
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +251 -204
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +53 -0
- data/lib/active_record/connection_handling.rb +292 -38
- data/lib/active_record/core.rb +385 -158
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +100 -0
- data/lib/active_record/database_configurations/database_config.rb +83 -0
- data/lib/active_record/database_configurations/hash_config.rb +154 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +256 -0
- data/lib/active_record/delegated_type.rb +250 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +4 -5
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +130 -51
- data/lib/active_record/errors.rb +129 -23
- data/lib/active_record/explain.rb +10 -6
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +22 -15
- 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 +187 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +206 -490
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +104 -37
- data/lib/active_record/insert_all.rb +278 -0
- data/lib/active_record/integration.rb +69 -18
- data/lib/active_record/internal_metadata.rb +24 -9
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +41 -26
- data/lib/active_record/locking/pessimistic.rb +18 -8
- data/lib/active_record/log_subscriber.rb +46 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector.rb +82 -0
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +246 -64
- data/lib/active_record/migration/join_table.rb +1 -2
- data/lib/active_record/migration.rb +266 -187
- data/lib/active_record/model_schema.rb +165 -52
- data/lib/active_record/nested_attributes.rb +17 -19
- data/lib/active_record/no_touching.rb +11 -4
- data/lib/active_record/null_relation.rb +2 -7
- data/lib/active_record/persistence.rb +467 -92
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +51 -24
- data/lib/active_record/railtie.rb +224 -57
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +31 -36
- data/lib/active_record/railties/databases.rake +369 -101
- data/lib/active_record/readonly_attributes.rb +15 -0
- data/lib/active_record/reflection.rb +170 -137
- data/lib/active_record/relation/batches/batch_enumerator.rb +44 -14
- data/lib/active_record/relation/batches.rb +46 -37
- data/lib/active_record/relation/calculations.rb +168 -96
- data/lib/active_record/relation/delegation.rb +37 -52
- data/lib/active_record/relation/finder_methods.rb +79 -58
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +50 -51
- 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 +11 -10
- 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 -46
- data/lib/active_record/relation/query_attribute.rb +9 -10
- data/lib/active_record/relation/query_methods.rb +685 -208
- data/lib/active_record/relation/record_fetch_warning.rb +9 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -10
- data/lib/active_record/relation/where_clause.rb +108 -64
- data/lib/active_record/relation.rb +515 -151
- data/lib/active_record/result.rb +78 -42
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +29 -44
- data/lib/active_record/schema.rb +37 -31
- data/lib/active_record/schema_dumper.rb +74 -23
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +62 -17
- data/lib/active_record/scoping/named.rb +17 -32
- data/lib/active_record/scoping.rb +70 -41
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +6 -4
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +49 -6
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +13 -17
- data/lib/active_record/table_metadata.rb +42 -43
- data/lib/active_record/tasks/database_tasks.rb +352 -94
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +41 -39
- 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 +287 -0
- data/lib/active_record/timestamp.rb +44 -34
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +67 -128
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +34 -19
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +7 -4
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +17 -21
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +9 -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 +2 -3
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +39 -31
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +209 -32
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +33 -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 +48 -0
- data/lib/arel/delete_manager.rb +32 -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/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +48 -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 +44 -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/filter.rb +10 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +45 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -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 +46 -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 +71 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +258 -0
- data/lib/arel/select_manager.rb +276 -0
- data/lib/arel/table.rb +117 -0
- data/lib/arel/tree_manager.rb +60 -0
- data/lib/arel/update_manager.rb +48 -0
- data/lib/arel/visitors/dot.rb +298 -0
- data/lib/arel/visitors/mysql.rb +99 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +955 -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 +55 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -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
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +162 -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,8 +1,7 @@
|
|
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
|
-
require "
|
4
|
+
require "openssl"
|
6
5
|
|
7
6
|
module ActiveRecord
|
8
7
|
module ConnectionAdapters # :nodoc:
|
@@ -30,7 +29,7 @@ module ActiveRecord
|
|
30
29
|
table_name[0...table_alias_length].tr(".", "_")
|
31
30
|
end
|
32
31
|
|
33
|
-
# Returns the relation names
|
32
|
+
# Returns the relation names usable to back Active Record models.
|
34
33
|
# For most adapters this means all #tables and #views.
|
35
34
|
def data_sources
|
36
35
|
query_values(data_source_sql, "SCHEMA")
|
@@ -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
|
|
@@ -121,6 +124,9 @@ module ActiveRecord
|
|
121
124
|
# column_exists?(:suppliers, :name)
|
122
125
|
#
|
123
126
|
# # Check a column exists of a particular type
|
127
|
+
# #
|
128
|
+
# # This works for standard non-casted types (eg. string) but is unreliable
|
129
|
+
# # for types that may get cast to something else (eg. char, bigint).
|
124
130
|
# column_exists?(:suppliers, :name, :string)
|
125
131
|
#
|
126
132
|
# # Check a column exists with a specific definition
|
@@ -129,11 +135,11 @@ module ActiveRecord
|
|
129
135
|
# column_exists?(:suppliers, :name, :string, null: false)
|
130
136
|
# column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
|
131
137
|
#
|
132
|
-
def column_exists?(table_name, column_name, type = nil, options
|
138
|
+
def column_exists?(table_name, column_name, type = nil, **options)
|
133
139
|
column_name = column_name.to_s
|
134
140
|
checks = []
|
135
141
|
checks << lambda { |c| c.name == column_name }
|
136
|
-
checks << lambda { |c| c.type == type } if type
|
142
|
+
checks << lambda { |c| c.type == type.to_sym rescue nil } if type
|
137
143
|
column_options_keys.each do |attr|
|
138
144
|
checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
|
139
145
|
end
|
@@ -205,19 +211,22 @@ module ActiveRecord
|
|
205
211
|
# Set to true to drop the table before creating it.
|
206
212
|
# Set to +:cascade+ to drop dependent objects as well.
|
207
213
|
# Defaults to false.
|
214
|
+
# [<tt>:if_not_exists</tt>]
|
215
|
+
# Set to true to avoid raising an error when the table already exists.
|
216
|
+
# Defaults to false.
|
208
217
|
# [<tt>:as</tt>]
|
209
218
|
# SQL to use to generate the table. When this option is used, the block is
|
210
219
|
# ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
|
211
220
|
#
|
212
221
|
# ====== Add a backend specific option to the generated SQL (MySQL)
|
213
222
|
#
|
214
|
-
# create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=
|
223
|
+
# create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
|
215
224
|
#
|
216
225
|
# generates:
|
217
226
|
#
|
218
227
|
# CREATE TABLE suppliers (
|
219
228
|
# id bigint auto_increment PRIMARY KEY
|
220
|
-
# ) ENGINE=InnoDB DEFAULT CHARSET=
|
229
|
+
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
221
230
|
#
|
222
231
|
# ====== Rename the primary key column
|
223
232
|
#
|
@@ -287,37 +296,44 @@ module ActiveRecord
|
|
287
296
|
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
288
297
|
#
|
289
298
|
# See also TableDefinition#column for details on how to create columns.
|
290
|
-
def create_table(table_name,
|
291
|
-
td = create_table_definition
|
299
|
+
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
|
300
|
+
td = create_table_definition(table_name, **extract_table_options!(options))
|
292
301
|
|
293
|
-
if
|
294
|
-
pk =
|
295
|
-
|
302
|
+
if id && !td.as
|
303
|
+
pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
|
304
|
+
|
305
|
+
if id.is_a?(Hash)
|
306
|
+
options.merge!(id.except(:type))
|
307
|
+
id = id.fetch(:type, :primary_key)
|
296
308
|
end
|
297
309
|
|
298
310
|
if pk.is_a?(Array)
|
299
311
|
td.primary_keys pk
|
300
312
|
else
|
301
|
-
td.primary_key pk,
|
313
|
+
td.primary_key pk, id, **options
|
302
314
|
end
|
303
315
|
end
|
304
316
|
|
305
317
|
yield td if block_given?
|
306
318
|
|
307
|
-
if
|
308
|
-
drop_table(table_name,
|
319
|
+
if force
|
320
|
+
drop_table(table_name, force: force, if_exists: true)
|
321
|
+
else
|
322
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
309
323
|
end
|
310
324
|
|
311
325
|
result = execute schema_creation.accept td
|
312
326
|
|
313
327
|
unless supports_indexes_in_create?
|
314
328
|
td.indexes.each do |column_name, index_options|
|
315
|
-
add_index(table_name, column_name, index_options)
|
329
|
+
add_index(table_name, column_name, **index_options, if_not_exists: td.if_not_exists)
|
316
330
|
end
|
317
331
|
end
|
318
332
|
|
319
333
|
if supports_comments? && !supports_comments_in_create?
|
320
|
-
|
334
|
+
if table_comment = td.comment.presence
|
335
|
+
change_table_comment(table_name, table_comment)
|
336
|
+
end
|
321
337
|
|
322
338
|
td.columns.each do |column|
|
323
339
|
change_column_comment(table_name, column.name, column.comment) if column.comment.present?
|
@@ -372,9 +388,9 @@ module ActiveRecord
|
|
372
388
|
|
373
389
|
t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
|
374
390
|
|
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
|
391
|
+
create_table(join_table_name, **options.merge!(id: false)) do |td|
|
392
|
+
td.references t1_ref, **column_options
|
393
|
+
td.references t2_ref, **column_options
|
378
394
|
yield td if block_given?
|
379
395
|
end
|
380
396
|
end
|
@@ -385,7 +401,7 @@ module ActiveRecord
|
|
385
401
|
# Although this command ignores the block if one is given, it can be helpful
|
386
402
|
# to provide one in a migration's +change+ method so it can be reverted.
|
387
403
|
# In that case, the block will be used by #create_join_table.
|
388
|
-
def drop_join_table(table_1, table_2, options
|
404
|
+
def drop_join_table(table_1, table_2, **options)
|
389
405
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
390
406
|
drop_table(join_table_name)
|
391
407
|
end
|
@@ -414,6 +430,12 @@ module ActiveRecord
|
|
414
430
|
# t.column :name, :string, limit: 60
|
415
431
|
# end
|
416
432
|
#
|
433
|
+
# ====== Change type of a column
|
434
|
+
#
|
435
|
+
# change_table(:suppliers) do |t|
|
436
|
+
# t.change :metadata, :json
|
437
|
+
# end
|
438
|
+
#
|
417
439
|
# ====== Add 2 integer columns
|
418
440
|
#
|
419
441
|
# change_table(:suppliers) do |t|
|
@@ -462,7 +484,7 @@ module ActiveRecord
|
|
462
484
|
# end
|
463
485
|
#
|
464
486
|
# See also Table for details on all of the various column transformations.
|
465
|
-
def change_table(table_name, options
|
487
|
+
def change_table(table_name, **options)
|
466
488
|
if supports_bulk_alter? && options[:bulk]
|
467
489
|
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
|
468
490
|
yield update_table_definition(table_name, recorder)
|
@@ -492,38 +514,53 @@ module ActiveRecord
|
|
492
514
|
# Although this command ignores most +options+ and the block if one is given,
|
493
515
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
494
516
|
# In that case, +options+ and the block will be used by #create_table.
|
495
|
-
def drop_table(table_name, options
|
517
|
+
def drop_table(table_name, **options)
|
518
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
496
519
|
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
497
520
|
end
|
498
521
|
|
499
522
|
# Add a new +type+ column named +column_name+ to +table_name+.
|
500
523
|
#
|
524
|
+
# See {ActiveRecord::ConnectionAdapters::TableDefinition.column}[rdoc-ref:ActiveRecord::ConnectionAdapters::TableDefinition#column].
|
525
|
+
#
|
501
526
|
# The +type+ parameter is normally one of the migrations native types,
|
502
527
|
# which is one of the following:
|
503
528
|
# <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
|
504
529
|
# <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
|
505
530
|
# <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
|
506
|
-
# <tt>:binary</tt>, <tt>:boolean</tt>.
|
531
|
+
# <tt>:binary</tt>, <tt>:blob</tt>, <tt>:boolean</tt>.
|
507
532
|
#
|
508
533
|
# You may use a type not in this list as long as it is supported by your
|
509
534
|
# database (for example, "polygon" in MySQL), but this will not be database
|
510
535
|
# agnostic and should usually be avoided.
|
511
536
|
#
|
512
537
|
# Available options are (none of these exists by default):
|
538
|
+
# * <tt>:comment</tt> -
|
539
|
+
# Specifies the comment for the column. This option is ignored by some backends.
|
540
|
+
# * <tt>:collation</tt> -
|
541
|
+
# Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column.
|
542
|
+
# If not specified, the column will have the same collation as the table.
|
543
|
+
# * <tt>:default</tt> -
|
544
|
+
# The column's default value. Use +nil+ for +NULL+.
|
513
545
|
# * <tt>:limit</tt> -
|
514
546
|
# 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
|
547
|
+
# and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, <tt>:blob</tt>, and <tt>:integer</tt> columns.
|
516
548
|
# This option is ignored by some backends.
|
517
|
-
# * <tt>:default</tt> -
|
518
|
-
# The column's default value. Use +nil+ for +NULL+.
|
519
549
|
# * <tt>:null</tt> -
|
520
550
|
# Allows or disallows +NULL+ values in the column.
|
521
551
|
# * <tt>:precision</tt> -
|
522
|
-
# Specifies the precision for the <tt>:decimal</tt
|
552
|
+
# Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
|
553
|
+
# <tt>:datetime</tt>, and <tt>:time</tt> columns.
|
523
554
|
# * <tt>:scale</tt> -
|
524
555
|
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
556
|
+
# * <tt>:collation</tt> -
|
557
|
+
# Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column. If not specified, the
|
558
|
+
# column will have the same collation as the table.
|
525
559
|
# * <tt>:comment</tt> -
|
526
560
|
# Specifies the comment for the column. This option is ignored by some backends.
|
561
|
+
# * <tt>:if_not_exists</tt> -
|
562
|
+
# Specifies if the column already exists to not try to re-add it. This will avoid
|
563
|
+
# duplicate column errors.
|
527
564
|
#
|
528
565
|
# Note: The precision is the total number of significant digits,
|
529
566
|
# and the scale is the number of digits that can be stored following
|
@@ -544,8 +581,6 @@ module ActiveRecord
|
|
544
581
|
# but the maximum supported <tt>:precision</tt> is 16. No default.
|
545
582
|
# * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
|
546
583
|
# Default is (38,0).
|
547
|
-
# * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
|
548
|
-
# Default unknown.
|
549
584
|
# * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
|
550
585
|
# Default (38,0).
|
551
586
|
#
|
@@ -575,21 +610,43 @@ module ActiveRecord
|
|
575
610
|
# # Defines a column with a database-specific type.
|
576
611
|
# add_column(:shapes, :triangle, 'polygon')
|
577
612
|
# # ALTER TABLE "shapes" ADD "triangle" polygon
|
578
|
-
|
613
|
+
#
|
614
|
+
# # Ignores the method call if the column exists
|
615
|
+
# add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
|
616
|
+
def add_column(table_name, column_name, type, **options)
|
617
|
+
return if options[:if_not_exists] == true && column_exists?(table_name, column_name)
|
618
|
+
|
619
|
+
if supports_datetime_with_precision?
|
620
|
+
if type == :datetime && !options.key?(:precision)
|
621
|
+
options[:precision] = 6
|
622
|
+
end
|
623
|
+
end
|
624
|
+
|
579
625
|
at = create_alter_table table_name
|
580
|
-
at.add_column(column_name, type, options)
|
626
|
+
at.add_column(column_name, type, **options)
|
581
627
|
execute schema_creation.accept at
|
582
628
|
end
|
583
629
|
|
630
|
+
def add_columns(table_name, *column_names, type:, **options) # :nodoc:
|
631
|
+
column_names.each do |column_name|
|
632
|
+
add_column(table_name, column_name, type, **options)
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
584
636
|
# Removes the given columns from the table definition.
|
585
637
|
#
|
586
638
|
# remove_columns(:suppliers, :qualification, :experience)
|
587
639
|
#
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
640
|
+
# +type+ and other column options can be passed to make migration reversible.
|
641
|
+
#
|
642
|
+
# remove_columns(:suppliers, :qualification, :experience, type: :string, null: false)
|
643
|
+
def remove_columns(table_name, *column_names, type: nil, **options)
|
644
|
+
if column_names.empty?
|
645
|
+
raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
|
592
646
|
end
|
647
|
+
|
648
|
+
remove_column_fragments = remove_columns_for_alter(table_name, *column_names, type: type, **options)
|
649
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_fragments.join(', ')}"
|
593
650
|
end
|
594
651
|
|
595
652
|
# Removes the column from the table definition.
|
@@ -599,8 +656,18 @@ module ActiveRecord
|
|
599
656
|
# The +type+ and +options+ parameters will be ignored if present. It can be helpful
|
600
657
|
# to provide these in a migration's +change+ method so it can be reverted.
|
601
658
|
# In that case, +type+ and +options+ will be used by #add_column.
|
602
|
-
|
603
|
-
|
659
|
+
# Depending on the database you're using, indexes using this column may be
|
660
|
+
# automatically removed or modified to remove this column from the index.
|
661
|
+
#
|
662
|
+
# If the options provided include an +if_exists+ key, it will be used to check if the
|
663
|
+
# column does not exist. This will silently ignore the migration rather than raising
|
664
|
+
# if the column was already used.
|
665
|
+
#
|
666
|
+
# remove_column(:suppliers, :qualification, if_exists: true)
|
667
|
+
def remove_column(table_name, column_name, type = nil, **options)
|
668
|
+
return if options[:if_exists] == true && !column_exists?(table_name, column_name)
|
669
|
+
|
670
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, **options)}"
|
604
671
|
end
|
605
672
|
|
606
673
|
# Changes the column's definition according to the new options.
|
@@ -609,7 +676,7 @@ module ActiveRecord
|
|
609
676
|
# change_column(:suppliers, :name, :string, limit: 80)
|
610
677
|
# change_column(:accounts, :description, :text)
|
611
678
|
#
|
612
|
-
def change_column(table_name, column_name, type, options
|
679
|
+
def change_column(table_name, column_name, type, **options)
|
613
680
|
raise NotImplementedError, "change_column is not implemented"
|
614
681
|
end
|
615
682
|
|
@@ -671,7 +738,17 @@ module ActiveRecord
|
|
671
738
|
#
|
672
739
|
# generates:
|
673
740
|
#
|
674
|
-
# CREATE INDEX
|
741
|
+
# CREATE INDEX index_suppliers_on_name ON suppliers(name)
|
742
|
+
#
|
743
|
+
# ====== Creating a index which already exists
|
744
|
+
#
|
745
|
+
# add_index(:suppliers, :name, if_not_exists: true)
|
746
|
+
#
|
747
|
+
# generates:
|
748
|
+
#
|
749
|
+
# CREATE INDEX IF NOT EXISTS index_suppliers_on_name ON suppliers(name)
|
750
|
+
#
|
751
|
+
# Note: Not supported by MySQL.
|
675
752
|
#
|
676
753
|
# ====== Creating a unique index
|
677
754
|
#
|
@@ -679,7 +756,7 @@ module ActiveRecord
|
|
679
756
|
#
|
680
757
|
# generates:
|
681
758
|
#
|
682
|
-
# CREATE UNIQUE INDEX
|
759
|
+
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id)
|
683
760
|
#
|
684
761
|
# ====== Creating a named index
|
685
762
|
#
|
@@ -705,11 +782,11 @@ module ActiveRecord
|
|
705
782
|
#
|
706
783
|
# CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
|
707
784
|
#
|
708
|
-
# Note:
|
785
|
+
# Note: only supported by MySQL
|
709
786
|
#
|
710
787
|
# ====== Creating an index with a sort order (desc or asc, asc is the default)
|
711
788
|
#
|
712
|
-
# add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
|
789
|
+
# add_index(:accounts, [:branch_id, :party_id, :surname], name: 'by_branch_desc_party', order: {branch_id: :desc, party_id: :asc})
|
713
790
|
#
|
714
791
|
# generates:
|
715
792
|
#
|
@@ -725,7 +802,7 @@ module ActiveRecord
|
|
725
802
|
#
|
726
803
|
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
|
727
804
|
#
|
728
|
-
# Note: Partial indexes are only supported for PostgreSQL and SQLite
|
805
|
+
# Note: Partial indexes are only supported for PostgreSQL and SQLite.
|
729
806
|
#
|
730
807
|
# ====== Creating an index with a specific method
|
731
808
|
#
|
@@ -760,9 +837,22 @@ module ActiveRecord
|
|
760
837
|
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
|
761
838
|
#
|
762
839
|
# Note: only supported by MySQL.
|
763
|
-
|
764
|
-
|
765
|
-
|
840
|
+
#
|
841
|
+
# ====== Creating an index with a specific algorithm
|
842
|
+
#
|
843
|
+
# add_index(:developers, :name, algorithm: :concurrently)
|
844
|
+
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
|
845
|
+
#
|
846
|
+
# Note: only supported by PostgreSQL.
|
847
|
+
#
|
848
|
+
# Concurrently adding an index is not supported in a transaction.
|
849
|
+
#
|
850
|
+
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
851
|
+
def add_index(table_name, column_name, **options)
|
852
|
+
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
853
|
+
|
854
|
+
create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
|
855
|
+
execute schema_creation.accept(create_index)
|
766
856
|
end
|
767
857
|
|
768
858
|
# Removes the given index from the table.
|
@@ -783,8 +873,29 @@ module ActiveRecord
|
|
783
873
|
#
|
784
874
|
# remove_index :accounts, name: :by_branch_party
|
785
875
|
#
|
786
|
-
|
787
|
-
|
876
|
+
# Removes the index on +branch_id+ named +by_branch_party+ in the +accounts+ table.
|
877
|
+
#
|
878
|
+
# remove_index :accounts, :branch_id, name: :by_branch_party
|
879
|
+
#
|
880
|
+
# Checks if the index exists before trying to remove it. Will silently ignore indexes that
|
881
|
+
# don't exist.
|
882
|
+
#
|
883
|
+
# remove_index :accounts, if_exists: true
|
884
|
+
#
|
885
|
+
# Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
|
886
|
+
#
|
887
|
+
# remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
|
888
|
+
#
|
889
|
+
# Note: only supported by PostgreSQL.
|
890
|
+
#
|
891
|
+
# Concurrently removing an index is not supported in a transaction.
|
892
|
+
#
|
893
|
+
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
894
|
+
def remove_index(table_name, column_name = nil, **options)
|
895
|
+
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
896
|
+
|
897
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
898
|
+
|
788
899
|
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
789
900
|
end
|
790
901
|
|
@@ -795,6 +906,8 @@ module ActiveRecord
|
|
795
906
|
# rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
|
796
907
|
#
|
797
908
|
def rename_index(table_name, old_name, new_name)
|
909
|
+
old_name = old_name.to_s
|
910
|
+
new_name = new_name.to_s
|
798
911
|
validate_index_length!(table_name, new_name)
|
799
912
|
|
800
913
|
# this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
|
@@ -804,7 +917,7 @@ module ActiveRecord
|
|
804
917
|
remove_index(table_name, name: old_name)
|
805
918
|
end
|
806
919
|
|
807
|
-
def index_name(table_name, options)
|
920
|
+
def index_name(table_name, options) # :nodoc:
|
808
921
|
if Hash === options
|
809
922
|
if options[:column]
|
810
923
|
"index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
|
@@ -836,23 +949,25 @@ module ActiveRecord
|
|
836
949
|
# Add an appropriate index. Defaults to true.
|
837
950
|
# See #add_index for usage of this option.
|
838
951
|
# [<tt>:foreign_key</tt>]
|
839
|
-
# Add an appropriate foreign key constraint. Defaults to false
|
952
|
+
# Add an appropriate foreign key constraint. Defaults to false, pass true
|
953
|
+
# to add. In case the join table can't be inferred from the association
|
954
|
+
# pass <tt>:to_table</tt> with the appropriate table name.
|
840
955
|
# [<tt>:polymorphic</tt>]
|
841
956
|
# Whether an additional +_type+ column should be added. Defaults to false.
|
842
957
|
# [<tt>:null</tt>]
|
843
958
|
# Whether the column allows nulls. Defaults to true.
|
844
959
|
#
|
845
|
-
# ====== Create a user_id bigint column
|
960
|
+
# ====== Create a user_id bigint column without an index
|
846
961
|
#
|
847
|
-
# add_reference(:products, :user)
|
962
|
+
# add_reference(:products, :user, index: false)
|
848
963
|
#
|
849
964
|
# ====== Create a user_id string column
|
850
965
|
#
|
851
966
|
# add_reference(:products, :user, type: :string)
|
852
967
|
#
|
853
|
-
# ====== Create supplier_id, supplier_type columns
|
968
|
+
# ====== Create supplier_id, supplier_type columns
|
854
969
|
#
|
855
|
-
# add_reference(:products, :supplier, polymorphic: true
|
970
|
+
# add_reference(:products, :supplier, polymorphic: true)
|
856
971
|
#
|
857
972
|
# ====== Create a supplier_id column with a unique index
|
858
973
|
#
|
@@ -868,10 +983,10 @@ module ActiveRecord
|
|
868
983
|
#
|
869
984
|
# ====== Create a supplier_id column and a foreign key to the firms table
|
870
985
|
#
|
871
|
-
# add_reference(:products, :supplier, foreign_key: {to_table: :firms})
|
986
|
+
# add_reference(:products, :supplier, foreign_key: { to_table: :firms })
|
872
987
|
#
|
873
988
|
def add_reference(table_name, ref_name, **options)
|
874
|
-
ReferenceDefinition.new(ref_name, options).add_to(update_table_definition(table_name, self))
|
989
|
+
ReferenceDefinition.new(ref_name, **options).add_to(update_table_definition(table_name, self))
|
875
990
|
end
|
876
991
|
alias :add_belongs_to :add_reference
|
877
992
|
|
@@ -880,7 +995,7 @@ module ActiveRecord
|
|
880
995
|
#
|
881
996
|
# ====== Remove the reference
|
882
997
|
#
|
883
|
-
# remove_reference(:products, :user, index:
|
998
|
+
# remove_reference(:products, :user, index: false)
|
884
999
|
#
|
885
1000
|
# ====== Remove polymorphic reference
|
886
1001
|
#
|
@@ -888,7 +1003,7 @@ module ActiveRecord
|
|
888
1003
|
#
|
889
1004
|
# ====== Remove the reference with a foreign key
|
890
1005
|
#
|
891
|
-
# remove_reference(:products, :user,
|
1006
|
+
# remove_reference(:products, :user, foreign_key: true)
|
892
1007
|
#
|
893
1008
|
def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
|
894
1009
|
if foreign_key
|
@@ -899,7 +1014,7 @@ module ActiveRecord
|
|
899
1014
|
foreign_key_options = { to_table: reference_name }
|
900
1015
|
end
|
901
1016
|
foreign_key_options[:column] ||= "#{ref_name}_id"
|
902
|
-
remove_foreign_key(table_name, foreign_key_options)
|
1017
|
+
remove_foreign_key(table_name, **foreign_key_options)
|
903
1018
|
end
|
904
1019
|
|
905
1020
|
remove_column(table_name, "#{ref_name}_id")
|
@@ -928,6 +1043,10 @@ module ActiveRecord
|
|
928
1043
|
#
|
929
1044
|
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
|
930
1045
|
#
|
1046
|
+
# ====== Creating a foreign key, ignoring method call if the foreign key exists
|
1047
|
+
#
|
1048
|
+
# add_foreign_key(:articles, :authors, if_not_exists: true)
|
1049
|
+
#
|
931
1050
|
# ====== Creating a foreign key on a specific column
|
932
1051
|
#
|
933
1052
|
# add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
|
@@ -955,10 +1074,17 @@ module ActiveRecord
|
|
955
1074
|
# Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
|
956
1075
|
# [<tt>:on_update</tt>]
|
957
1076
|
# Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
|
1077
|
+
# [<tt>:if_not_exists</tt>]
|
1078
|
+
# Specifies if the foreign key already exists to not try to re-add it. This will avoid
|
1079
|
+
# duplicate column errors.
|
958
1080
|
# [<tt>:validate</tt>]
|
959
|
-
# (
|
960
|
-
|
1081
|
+
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
1082
|
+
# [<tt>:deferrable</tt>]
|
1083
|
+
# (PostgreSQL only) Specify whether or not the foreign key should be deferrable. Valid values are booleans or
|
1084
|
+
# +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
|
1085
|
+
def add_foreign_key(from_table, to_table, **options)
|
961
1086
|
return unless supports_foreign_keys?
|
1087
|
+
return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table)
|
962
1088
|
|
963
1089
|
options = foreign_key_options(from_table, to_table, options)
|
964
1090
|
at = create_alter_table from_table
|
@@ -980,15 +1106,28 @@ module ActiveRecord
|
|
980
1106
|
#
|
981
1107
|
# remove_foreign_key :accounts, column: :owner_id
|
982
1108
|
#
|
1109
|
+
# Removes the foreign key on +accounts.owner_id+.
|
1110
|
+
#
|
1111
|
+
# remove_foreign_key :accounts, to_table: :owners
|
1112
|
+
#
|
983
1113
|
# Removes the foreign key named +special_fk_name+ on the +accounts+ table.
|
984
1114
|
#
|
985
1115
|
# remove_foreign_key :accounts, name: :special_fk_name
|
986
1116
|
#
|
987
|
-
#
|
988
|
-
|
1117
|
+
# Checks if the foreign key exists before trying to remove it. Will silently ignore indexes that
|
1118
|
+
# don't exist.
|
1119
|
+
#
|
1120
|
+
# remove_foreign_key :accounts, :branches, if_exists: true
|
1121
|
+
#
|
1122
|
+
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
|
1123
|
+
# with an addition of
|
1124
|
+
# [<tt>:to_table</tt>]
|
1125
|
+
# The name of the table that contains the referenced primary key.
|
1126
|
+
def remove_foreign_key(from_table, to_table = nil, **options)
|
989
1127
|
return unless supports_foreign_keys?
|
1128
|
+
return if options[:if_exists] == true && !foreign_key_exists?(from_table, to_table)
|
990
1129
|
|
991
|
-
fk_name_to_delete = foreign_key_for!(from_table,
|
1130
|
+
fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
|
992
1131
|
|
993
1132
|
at = create_alter_table from_table
|
994
1133
|
at.drop_foreign_key fk_name_to_delete
|
@@ -1007,14 +1146,12 @@ module ActiveRecord
|
|
1007
1146
|
# # Checks to see if a foreign key with a custom name exists.
|
1008
1147
|
# foreign_key_exists?(:accounts, name: "special_fk_name")
|
1009
1148
|
#
|
1010
|
-
def foreign_key_exists?(from_table,
|
1011
|
-
foreign_key_for(from_table,
|
1149
|
+
def foreign_key_exists?(from_table, to_table = nil, **options)
|
1150
|
+
foreign_key_for(from_table, to_table: to_table, **options).present?
|
1012
1151
|
end
|
1013
1152
|
|
1014
1153
|
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
|
1154
|
+
name = strip_table_name_prefix_and_suffix(table_name)
|
1018
1155
|
"#{name.singularize}_id"
|
1019
1156
|
end
|
1020
1157
|
|
@@ -1025,8 +1162,62 @@ module ActiveRecord
|
|
1025
1162
|
options
|
1026
1163
|
end
|
1027
1164
|
|
1028
|
-
|
1029
|
-
|
1165
|
+
# Returns an array of check constraints for the given table.
|
1166
|
+
# The check constraints are represented as CheckConstraintDefinition objects.
|
1167
|
+
def check_constraints(table_name)
|
1168
|
+
raise NotImplementedError
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
# Adds a new check constraint to the table. +expression+ is a String
|
1172
|
+
# representation of verifiable boolean condition.
|
1173
|
+
#
|
1174
|
+
# add_check_constraint :products, "price > 0", name: "price_check"
|
1175
|
+
#
|
1176
|
+
# generates:
|
1177
|
+
#
|
1178
|
+
# ALTER TABLE "products" ADD CONSTRAINT price_check CHECK (price > 0)
|
1179
|
+
#
|
1180
|
+
# The +options+ hash can include the following keys:
|
1181
|
+
# [<tt>:name</tt>]
|
1182
|
+
# The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
|
1183
|
+
# [<tt>:validate</tt>]
|
1184
|
+
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
1185
|
+
def add_check_constraint(table_name, expression, **options)
|
1186
|
+
return unless supports_check_constraints?
|
1187
|
+
|
1188
|
+
options = check_constraint_options(table_name, expression, options)
|
1189
|
+
at = create_alter_table(table_name)
|
1190
|
+
at.add_check_constraint(expression, options)
|
1191
|
+
|
1192
|
+
execute schema_creation.accept(at)
|
1193
|
+
end
|
1194
|
+
|
1195
|
+
def check_constraint_options(table_name, expression, options) # :nodoc:
|
1196
|
+
options = options.dup
|
1197
|
+
options[:name] ||= check_constraint_name(table_name, expression: expression, **options)
|
1198
|
+
options
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
# Removes the given check constraint from the table.
|
1202
|
+
#
|
1203
|
+
# remove_check_constraint :products, name: "price_check"
|
1204
|
+
#
|
1205
|
+
# The +expression+ parameter will be ignored if present. It can be helpful
|
1206
|
+
# to provide this in a migration's +change+ method so it can be reverted.
|
1207
|
+
# In that case, +expression+ will be used by #add_check_constraint.
|
1208
|
+
def remove_check_constraint(table_name, expression = nil, **options)
|
1209
|
+
return unless supports_check_constraints?
|
1210
|
+
|
1211
|
+
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
1212
|
+
|
1213
|
+
at = create_alter_table(table_name)
|
1214
|
+
at.drop_check_constraint(chk_name_to_delete)
|
1215
|
+
|
1216
|
+
execute schema_creation.accept(at)
|
1217
|
+
end
|
1218
|
+
|
1219
|
+
def dump_schema_information # :nodoc:
|
1220
|
+
versions = schema_migration.all_versions
|
1030
1221
|
insert_versions_sql(versions) if versions.any?
|
1031
1222
|
end
|
1032
1223
|
|
@@ -1034,15 +1225,12 @@ module ActiveRecord
|
|
1034
1225
|
{ primary_key: true }
|
1035
1226
|
end
|
1036
1227
|
|
1037
|
-
def assume_migrated_upto_version(version
|
1038
|
-
migrations_paths = Array(migrations_paths)
|
1228
|
+
def assume_migrated_upto_version(version)
|
1039
1229
|
version = version.to_i
|
1040
|
-
sm_table = quote_table_name(
|
1230
|
+
sm_table = quote_table_name(schema_migration.table_name)
|
1041
1231
|
|
1042
|
-
migrated =
|
1043
|
-
versions = migration_context.
|
1044
|
-
migration_context.parse_migration_filename(file).first.to_i
|
1045
|
-
end
|
1232
|
+
migrated = migration_context.get_all_versions
|
1233
|
+
versions = migration_context.migrations.map(&:version)
|
1046
1234
|
|
1047
1235
|
unless migrated.include?(version)
|
1048
1236
|
execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
|
@@ -1053,13 +1241,7 @@ module ActiveRecord
|
|
1053
1241
|
if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
|
1054
1242
|
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
|
1055
1243
|
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
|
1244
|
+
execute insert_versions_sql(inserting)
|
1063
1245
|
end
|
1064
1246
|
end
|
1065
1247
|
|
@@ -1085,7 +1267,7 @@ module ActiveRecord
|
|
1085
1267
|
if (0..6) === precision
|
1086
1268
|
column_type_sql << "(#{precision})"
|
1087
1269
|
else
|
1088
|
-
raise
|
1270
|
+
raise ArgumentError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6"
|
1089
1271
|
end
|
1090
1272
|
elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
|
1091
1273
|
column_type_sql << "(#{limit})"
|
@@ -1107,61 +1289,90 @@ module ActiveRecord
|
|
1107
1289
|
columns
|
1108
1290
|
end
|
1109
1291
|
|
1292
|
+
def distinct_relation_for_primary_key(relation) # :nodoc:
|
1293
|
+
values = columns_for_distinct(
|
1294
|
+
visitor.compile(relation.table[relation.primary_key]),
|
1295
|
+
relation.order_values
|
1296
|
+
)
|
1297
|
+
|
1298
|
+
limited = relation.reselect(values).distinct!
|
1299
|
+
limited_ids = select_rows(limited.arel, "SQL").map(&:last)
|
1300
|
+
|
1301
|
+
if limited_ids.empty?
|
1302
|
+
relation.none!
|
1303
|
+
else
|
1304
|
+
relation.where!(relation.primary_key => limited_ids)
|
1305
|
+
end
|
1306
|
+
|
1307
|
+
relation.limit_value = relation.offset_value = nil
|
1308
|
+
relation
|
1309
|
+
end
|
1310
|
+
|
1110
1311
|
# Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
|
1111
1312
|
# Additional options (like +:null+) are forwarded to #add_column.
|
1112
1313
|
#
|
1113
1314
|
# add_timestamps(:suppliers, null: true)
|
1114
1315
|
#
|
1115
|
-
def add_timestamps(table_name, options
|
1316
|
+
def add_timestamps(table_name, **options)
|
1116
1317
|
options[:null] = false if options[:null].nil?
|
1117
1318
|
|
1118
|
-
|
1119
|
-
|
1319
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
1320
|
+
options[:precision] = 6
|
1321
|
+
end
|
1322
|
+
|
1323
|
+
add_column table_name, :created_at, :datetime, **options
|
1324
|
+
add_column table_name, :updated_at, :datetime, **options
|
1120
1325
|
end
|
1121
1326
|
|
1122
1327
|
# Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
|
1123
1328
|
#
|
1124
1329
|
# remove_timestamps(:suppliers)
|
1125
1330
|
#
|
1126
|
-
def remove_timestamps(table_name, options
|
1127
|
-
|
1128
|
-
remove_column table_name, :created_at
|
1331
|
+
def remove_timestamps(table_name, **options)
|
1332
|
+
remove_columns table_name, :updated_at, :created_at
|
1129
1333
|
end
|
1130
1334
|
|
1131
|
-
def update_table_definition(table_name, base)
|
1335
|
+
def update_table_definition(table_name, base) # :nodoc:
|
1132
1336
|
Table.new(table_name, base)
|
1133
1337
|
end
|
1134
1338
|
|
1135
|
-
def add_index_options(table_name, column_name,
|
1136
|
-
|
1339
|
+
def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
|
1340
|
+
options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm)
|
1137
1341
|
|
1138
|
-
|
1342
|
+
column_names = index_column_names(column_name)
|
1139
1343
|
|
1140
|
-
|
1141
|
-
index_type ||= options[:unique] ? "UNIQUE" : ""
|
1142
|
-
index_name = options[:name].to_s if options.key?(:name)
|
1344
|
+
index_name = name&.to_s
|
1143
1345
|
index_name ||= index_name(table_name, column_names)
|
1144
1346
|
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1347
|
+
validate_index_length!(table_name, index_name, internal)
|
1348
|
+
|
1349
|
+
index = IndexDefinition.new(
|
1350
|
+
table_name, index_name,
|
1351
|
+
options[:unique],
|
1352
|
+
column_names,
|
1353
|
+
lengths: options[:length] || {},
|
1354
|
+
orders: options[:order] || {},
|
1355
|
+
opclasses: options[:opclass] || {},
|
1356
|
+
where: options[:where],
|
1357
|
+
type: options[:type],
|
1358
|
+
using: options[:using],
|
1359
|
+
comment: options[:comment]
|
1360
|
+
)
|
1361
|
+
|
1362
|
+
[index, index_algorithm(options[:algorithm]), if_not_exists]
|
1363
|
+
end
|
1156
1364
|
|
1157
|
-
|
1365
|
+
def index_algorithm(algorithm) # :nodoc:
|
1366
|
+
index_algorithms.fetch(algorithm) do
|
1367
|
+
raise ArgumentError, "Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}"
|
1368
|
+
end if algorithm
|
1369
|
+
end
|
1158
1370
|
|
1159
|
-
|
1160
|
-
|
1371
|
+
def quoted_columns_for_index(column_names, options) # :nodoc:
|
1372
|
+
quoted_columns = column_names.each_with_object({}) do |name, result|
|
1373
|
+
result[name.to_sym] = quote_column_name(name).dup
|
1161
1374
|
end
|
1162
|
-
|
1163
|
-
|
1164
|
-
[index_name, index_type, index_columns, index_options, algorithm, using, comment]
|
1375
|
+
add_options_for_index_columns(quoted_columns, **options).values.join(", ")
|
1165
1376
|
end
|
1166
1377
|
|
1167
1378
|
def options_include_default?(options)
|
@@ -1169,12 +1380,22 @@ module ActiveRecord
|
|
1169
1380
|
end
|
1170
1381
|
|
1171
1382
|
# Changes the comment for a table or removes it if +nil+.
|
1172
|
-
|
1383
|
+
#
|
1384
|
+
# Passing a hash containing +:from+ and +:to+ will make this change
|
1385
|
+
# reversible in migration:
|
1386
|
+
#
|
1387
|
+
# change_table_comment(:posts, from: "old_comment", to: "new_comment")
|
1388
|
+
def change_table_comment(table_name, comment_or_changes)
|
1173
1389
|
raise NotImplementedError, "#{self.class} does not support changing table comments"
|
1174
1390
|
end
|
1175
1391
|
|
1176
1392
|
# Changes the comment for a column or removes it if +nil+.
|
1177
|
-
|
1393
|
+
#
|
1394
|
+
# Passing a hash containing +:from+ and +:to+ will make this change
|
1395
|
+
# reversible in migration:
|
1396
|
+
#
|
1397
|
+
# change_column_comment(:posts, :state, from: "old_comment", to: "new_comment")
|
1398
|
+
def change_column_comment(table_name, column_name, comment_or_changes)
|
1178
1399
|
raise NotImplementedError, "#{self.class} does not support changing column comments"
|
1179
1400
|
end
|
1180
1401
|
|
@@ -1206,32 +1427,27 @@ module ActiveRecord
|
|
1206
1427
|
# the PostgreSQL adapter for supporting operator classes.
|
1207
1428
|
def add_options_for_index_columns(quoted_columns, **options)
|
1208
1429
|
if supports_index_sort_order?
|
1209
|
-
quoted_columns = add_index_sort_order(quoted_columns, options)
|
1430
|
+
quoted_columns = add_index_sort_order(quoted_columns, **options)
|
1210
1431
|
end
|
1211
1432
|
|
1212
1433
|
quoted_columns
|
1213
1434
|
end
|
1214
1435
|
|
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)
|
1436
|
+
def index_name_for_remove(table_name, column_name, options)
|
1437
|
+
return options[:name] if can_remove_index_by_name?(column_name, options)
|
1224
1438
|
|
1225
1439
|
checks = []
|
1226
1440
|
|
1227
|
-
if options.
|
1228
|
-
|
1229
|
-
column_names =
|
1441
|
+
if !options.key?(:name) && expression_column_name?(column_name)
|
1442
|
+
options[:name] = index_name(table_name, column_name)
|
1443
|
+
column_names = []
|
1230
1444
|
else
|
1231
|
-
column_names = index_column_names(options)
|
1445
|
+
column_names = index_column_names(column_name || options[:column])
|
1232
1446
|
end
|
1233
1447
|
|
1234
|
-
if
|
1448
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
|
1449
|
+
|
1450
|
+
if column_names.present? && !(options.key?(:name) && expression_column_name?(column_names))
|
1235
1451
|
checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
|
1236
1452
|
end
|
1237
1453
|
|
@@ -1275,14 +1491,18 @@ module ActiveRecord
|
|
1275
1491
|
SchemaCreation.new(self)
|
1276
1492
|
end
|
1277
1493
|
|
1278
|
-
def create_table_definition(
|
1279
|
-
TableDefinition.new(
|
1494
|
+
def create_table_definition(name, **options)
|
1495
|
+
TableDefinition.new(self, name, **options)
|
1280
1496
|
end
|
1281
1497
|
|
1282
1498
|
def create_alter_table(name)
|
1283
1499
|
AlterTable.new create_table_definition(name)
|
1284
1500
|
end
|
1285
1501
|
|
1502
|
+
def extract_table_options!(options)
|
1503
|
+
options.extract!(:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation)
|
1504
|
+
end
|
1505
|
+
|
1286
1506
|
def fetch_type_metadata(sql_type)
|
1287
1507
|
cast_type = lookup_cast_type(sql_type)
|
1288
1508
|
SqlTypeMetadata.new(
|
@@ -1295,7 +1515,7 @@ module ActiveRecord
|
|
1295
1515
|
end
|
1296
1516
|
|
1297
1517
|
def index_column_names(column_names)
|
1298
|
-
if
|
1518
|
+
if expression_column_name?(column_names)
|
1299
1519
|
column_names
|
1300
1520
|
else
|
1301
1521
|
Array(column_names)
|
@@ -1303,30 +1523,41 @@ module ActiveRecord
|
|
1303
1523
|
end
|
1304
1524
|
|
1305
1525
|
def index_name_options(column_names)
|
1306
|
-
if
|
1526
|
+
if expression_column_name?(column_names)
|
1307
1527
|
column_names = column_names.scan(/\w+/).join("_")
|
1308
1528
|
end
|
1309
1529
|
|
1310
1530
|
{ column: column_names }
|
1311
1531
|
end
|
1312
1532
|
|
1533
|
+
# Try to identify whether the given column name is an expression
|
1534
|
+
def expression_column_name?(column_name)
|
1535
|
+
column_name.is_a?(String) && /\W/.match?(column_name)
|
1536
|
+
end
|
1537
|
+
|
1538
|
+
def strip_table_name_prefix_and_suffix(table_name)
|
1539
|
+
prefix = Base.table_name_prefix
|
1540
|
+
suffix = Base.table_name_suffix
|
1541
|
+
table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
|
1542
|
+
end
|
1543
|
+
|
1313
1544
|
def foreign_key_name(table_name, options)
|
1314
1545
|
options.fetch(:name) do
|
1315
1546
|
identifier = "#{table_name}_#{options.fetch(:column)}_fk"
|
1316
|
-
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
|
1547
|
+
hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
|
1317
1548
|
|
1318
1549
|
"fk_rails_#{hashed_identifier}"
|
1319
1550
|
end
|
1320
1551
|
end
|
1321
1552
|
|
1322
|
-
def foreign_key_for(from_table,
|
1553
|
+
def foreign_key_for(from_table, **options)
|
1323
1554
|
return unless supports_foreign_keys?
|
1324
|
-
foreign_keys(from_table).detect { |fk| fk.defined_for?
|
1555
|
+
foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
|
1325
1556
|
end
|
1326
1557
|
|
1327
|
-
def foreign_key_for!(from_table,
|
1328
|
-
foreign_key_for(from_table,
|
1329
|
-
raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{
|
1558
|
+
def foreign_key_for!(from_table, to_table: nil, **options)
|
1559
|
+
foreign_key_for(from_table, to_table: to_table, **options) ||
|
1560
|
+
raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
1330
1561
|
end
|
1331
1562
|
|
1332
1563
|
def extract_foreign_key_action(specifier)
|
@@ -1337,11 +1568,30 @@ module ActiveRecord
|
|
1337
1568
|
end
|
1338
1569
|
end
|
1339
1570
|
|
1340
|
-
def
|
1341
|
-
|
1571
|
+
def check_constraint_name(table_name, **options)
|
1572
|
+
options.fetch(:name) do
|
1573
|
+
expression = options.fetch(:expression)
|
1574
|
+
identifier = "#{table_name}_#{expression}_chk"
|
1575
|
+
hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
|
1342
1576
|
|
1343
|
-
|
1344
|
-
|
1577
|
+
"chk_rails_#{hashed_identifier}"
|
1578
|
+
end
|
1579
|
+
end
|
1580
|
+
|
1581
|
+
def check_constraint_for(table_name, **options)
|
1582
|
+
return unless supports_check_constraints?
|
1583
|
+
chk_name = check_constraint_name(table_name, **options)
|
1584
|
+
check_constraints(table_name).detect { |chk| chk.name == chk_name }
|
1585
|
+
end
|
1586
|
+
|
1587
|
+
def check_constraint_for!(table_name, expression: nil, **options)
|
1588
|
+
check_constraint_for(table_name, expression: expression, **options) ||
|
1589
|
+
raise(ArgumentError, "Table '#{table_name}' has no check constraint for #{expression || options}")
|
1590
|
+
end
|
1591
|
+
|
1592
|
+
def validate_index_length!(table_name, new_name, internal = false)
|
1593
|
+
if new_name.length > index_name_length
|
1594
|
+
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
|
1345
1595
|
end
|
1346
1596
|
end
|
1347
1597
|
|
@@ -1352,30 +1602,77 @@ module ActiveRecord
|
|
1352
1602
|
default_or_changes
|
1353
1603
|
end
|
1354
1604
|
end
|
1605
|
+
alias :extract_new_comment_value :extract_new_default_value
|
1606
|
+
|
1607
|
+
def can_remove_index_by_name?(column_name, options)
|
1608
|
+
column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
|
1609
|
+
end
|
1610
|
+
|
1611
|
+
def bulk_change_table(table_name, operations)
|
1612
|
+
sql_fragments = []
|
1613
|
+
non_combinable_operations = []
|
1614
|
+
|
1615
|
+
operations.each do |command, args|
|
1616
|
+
table, arguments = args.shift, args
|
1617
|
+
method = :"#{command}_for_alter"
|
1618
|
+
|
1619
|
+
if respond_to?(method, true)
|
1620
|
+
sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
|
1621
|
+
sql_fragments << sqls
|
1622
|
+
non_combinable_operations.concat(procs)
|
1623
|
+
else
|
1624
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
1625
|
+
non_combinable_operations.each(&:call)
|
1626
|
+
sql_fragments = []
|
1627
|
+
non_combinable_operations = []
|
1628
|
+
send(command, table, *arguments)
|
1629
|
+
end
|
1630
|
+
end
|
1355
1631
|
|
1356
|
-
|
1357
|
-
|
1632
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
1633
|
+
non_combinable_operations.each(&:call)
|
1358
1634
|
end
|
1359
1635
|
|
1360
|
-
def add_column_for_alter(table_name, column_name, type, options
|
1636
|
+
def add_column_for_alter(table_name, column_name, type, **options)
|
1361
1637
|
td = create_table_definition(table_name)
|
1362
|
-
cd = td.new_column_definition(column_name, type, options)
|
1638
|
+
cd = td.new_column_definition(column_name, type, **options)
|
1363
1639
|
schema_creation.accept(AddColumnDefinition.new(cd))
|
1364
1640
|
end
|
1365
1641
|
|
1366
|
-
def
|
1642
|
+
def rename_column_sql(table_name, column_name, new_column_name)
|
1643
|
+
"RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
|
1644
|
+
end
|
1645
|
+
|
1646
|
+
def remove_column_for_alter(table_name, column_name, type = nil, **options)
|
1367
1647
|
"DROP COLUMN #{quote_column_name(column_name)}"
|
1368
1648
|
end
|
1369
1649
|
|
1370
|
-
def remove_columns_for_alter(table_name, *column_names)
|
1650
|
+
def remove_columns_for_alter(table_name, *column_names, **options)
|
1371
1651
|
column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
|
1372
1652
|
end
|
1373
1653
|
|
1654
|
+
def add_timestamps_for_alter(table_name, **options)
|
1655
|
+
options[:null] = false if options[:null].nil?
|
1656
|
+
|
1657
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
1658
|
+
options[:precision] = 6
|
1659
|
+
end
|
1660
|
+
|
1661
|
+
[
|
1662
|
+
add_column_for_alter(table_name, :created_at, :datetime, **options),
|
1663
|
+
add_column_for_alter(table_name, :updated_at, :datetime, **options)
|
1664
|
+
]
|
1665
|
+
end
|
1666
|
+
|
1667
|
+
def remove_timestamps_for_alter(table_name, **options)
|
1668
|
+
remove_columns_for_alter(table_name, :updated_at, :created_at)
|
1669
|
+
end
|
1670
|
+
|
1374
1671
|
def insert_versions_sql(versions)
|
1375
|
-
sm_table = quote_table_name(
|
1672
|
+
sm_table = quote_table_name(schema_migration.table_name)
|
1376
1673
|
|
1377
1674
|
if versions.is_a?(Array)
|
1378
|
-
sql = "INSERT INTO #{sm_table} (version) VALUES\n"
|
1675
|
+
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
|
1379
1676
|
sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
|
1380
1677
|
sql << ";\n\n"
|
1381
1678
|
sql
|