activerecord 6.0.0 → 7.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +996 -594
- data/MIT-LICENSE +1 -1
- data/README.rdoc +34 -34
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +22 -20
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +41 -30
- data/lib/active_record/associations/association.rb +106 -41
- data/lib/active_record/associations/association_scope.rb +30 -21
- data/lib/active_record/associations/belongs_to_association.rb +69 -14
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
- data/lib/active_record/associations/builder/association.rb +39 -6
- data/lib/active_record/associations/builder/belongs_to.rb +47 -17
- data/lib/active_record/associations/builder/collection_association.rb +14 -6
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
- data/lib/active_record/associations/builder/has_many.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +13 -16
- data/lib/active_record/associations/builder/singular_association.rb +7 -3
- data/lib/active_record/associations/collection_association.rb +90 -53
- data/lib/active_record/associations/collection_proxy.rb +54 -19
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +21 -1
- data/lib/active_record/associations/has_many_association.rb +41 -10
- data/lib/active_record/associations/has_many_through_association.rb +29 -12
- data/lib/active_record/associations/has_one_association.rb +33 -9
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/join_dependency.rb +97 -54
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +237 -54
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +51 -17
- data/lib/active_record/associations/preloader.rb +55 -121
- data/lib/active_record/associations/singular_association.rb +16 -4
- data/lib/active_record/associations/through_association.rb +26 -15
- data/lib/active_record/associations.rb +454 -440
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +11 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +75 -34
- data/lib/active_record/attribute_methods/primary_key.rb +53 -31
- data/lib/active_record/attribute_methods/query.rb +31 -22
- data/lib/active_record/attribute_methods/read.rb +16 -17
- data/lib/active_record/attribute_methods/serialization.rb +177 -35
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
- data/lib/active_record/attribute_methods/write.rb +16 -28
- data/lib/active_record/attribute_methods.rb +227 -100
- data/lib/active_record/attributes.rb +94 -56
- data/lib/active_record/autosave_association.rb +119 -73
- data/lib/active_record/base.rb +31 -21
- data/lib/active_record/callbacks.rb +168 -55
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -25
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
- data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
- data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
- data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
- data/lib/active_record/connection_adapters/column.rb +28 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
- data/lib/active_record/connection_adapters/pool_config.rb +83 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -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 +53 -15
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
- 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 +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
- data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +176 -0
- data/lib/active_record/connection_handling.rb +243 -115
- data/lib/active_record/core.rb +481 -199
- data/lib/active_record/counter_cache.rb +69 -32
- data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
- data/lib/active_record/database_configurations/database_config.rb +77 -10
- data/lib/active_record/database_configurations/hash_config.rb +148 -26
- data/lib/active_record/database_configurations/url_config.rb +44 -45
- data/lib/active_record/database_configurations.rb +190 -114
- data/lib/active_record/delegated_type.rb +279 -0
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +38 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +5 -6
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -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 +171 -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 +157 -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 +53 -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_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +224 -73
- data/lib/active_record/errors.rb +254 -36
- data/lib/active_record/explain.rb +30 -17
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +22 -15
- data/lib/active_record/fixture_set/model_metadata.rb +15 -6
- data/lib/active_record/fixture_set/render_context.rb +3 -1
- data/lib/active_record/fixture_set/table_row.rb +88 -16
- data/lib/active_record/fixture_set/table_rows.rb +4 -5
- data/lib/active_record/fixtures.rb +229 -116
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +121 -48
- data/lib/active_record/insert_all.rb +178 -29
- data/lib/active_record/integration.rb +16 -14
- data/lib/active_record/internal_metadata.rb +132 -21
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +64 -33
- data/lib/active_record/locking/pessimistic.rb +21 -8
- data/lib/active_record/log_subscriber.rb +61 -30
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
- data/lib/active_record/middleware/database_selector.rb +25 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +160 -55
- data/lib/active_record/migration/compatibility.rb +286 -43
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -2
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +421 -193
- data/lib/active_record/model_schema.rb +217 -125
- data/lib/active_record/nested_attributes.rb +62 -27
- data/lib/active_record/no_touching.rb +4 -4
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +322 -319
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -15
- data/lib/active_record/query_logs.rb +193 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +54 -14
- data/lib/active_record/railtie.rb +250 -72
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +312 -197
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +45 -3
- data/lib/active_record/reflection.rb +389 -146
- data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
- data/lib/active_record/relation/batches.rb +214 -73
- data/lib/active_record/relation/calculations.rb +379 -124
- data/lib/active_record/relation/delegation.rb +36 -23
- data/lib/active_record/relation/finder_methods.rb +159 -49
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +41 -33
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +79 -53
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +1156 -279
- data/lib/active_record/relation/record_fetch_warning.rb +12 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -9
- data/lib/active_record/relation/where_clause.rb +100 -66
- data/lib/active_record/relation.rb +829 -194
- data/lib/active_record/result.rb +76 -56
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +86 -47
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +140 -33
- data/lib/active_record/schema_migration.rb +74 -29
- data/lib/active_record/scoping/default.rb +73 -19
- data/lib/active_record/scoping/named.rb +10 -28
- data/lib/active_record/scoping.rb +65 -35
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +34 -8
- data/lib/active_record/serialization.rb +11 -4
- data/lib/active_record/signed_id.rb +138 -0
- data/lib/active_record/statement_cache.rb +26 -10
- data/lib/active_record/store.rb +19 -14
- data/lib/active_record/suppressor.rb +15 -17
- data/lib/active_record/table_metadata.rb +46 -36
- data/lib/active_record/tasks/database_tasks.rb +371 -205
- data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +189 -104
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +35 -25
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +31 -27
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +131 -99
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +33 -18
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +11 -6
- data/lib/active_record/type/time.rb +14 -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 +7 -2
- data/lib/active_record/type_caster/connection.rb +4 -5
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -8
- data/lib/active_record/validations/numericality.rb +36 -0
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +88 -18
- data/lib/active_record/validations.rb +15 -8
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +446 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +4 -8
- data/lib/arel/collectors/bind.rb +8 -1
- data/lib/arel/collectors/composite.rb +15 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/crud.rb +30 -22
- data/lib/arel/delete_manager.rb +23 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/binary.rb +82 -9
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +22 -10
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +14 -13
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +68 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +122 -11
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/table_alias.rb +11 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes/update_statement.rb +11 -4
- data/lib/arel/nodes.rb +10 -3
- data/lib/arel/predications.rb +31 -28
- data/lib/arel/select_manager.rb +18 -9
- data/lib/arel/table.rb +21 -10
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +25 -5
- data/lib/arel/visitors/dot.rb +94 -90
- data/lib/arel/visitors/mysql.rb +34 -6
- data/lib/arel/visitors/postgresql.rb +5 -16
- data/lib/arel/visitors/sqlite.rb +25 -1
- data/lib/arel/visitors/to_sql.rb +227 -81
- data/lib/arel/visitors/visitor.rb +2 -3
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +37 -15
- data/lib/rails/generators/active_record/application_record/USAGE +8 -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 +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
- data/lib/rails/generators/active_record/migration.rb +9 -3
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
- 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 +1 -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 +117 -30
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/null_relation.rb +0 -68
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
|
@@ -1,9 +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 "
|
|
6
|
-
require "digest/sha2"
|
|
4
|
+
require "openssl"
|
|
7
5
|
|
|
8
6
|
module ActiveRecord
|
|
9
7
|
module ConnectionAdapters # :nodoc:
|
|
@@ -31,7 +29,7 @@ module ActiveRecord
|
|
|
31
29
|
table_name[0...table_alias_length].tr(".", "_")
|
|
32
30
|
end
|
|
33
31
|
|
|
34
|
-
# Returns the relation names
|
|
32
|
+
# Returns the relation names usable to back Active Record models.
|
|
35
33
|
# For most adapters this means all #tables and #views.
|
|
36
34
|
def data_sources
|
|
37
35
|
query_values(data_source_sql, "SCHEMA")
|
|
@@ -98,21 +96,19 @@ module ActiveRecord
|
|
|
98
96
|
# # Check an index with a custom name exists
|
|
99
97
|
# index_exists?(:suppliers, :company_id, name: "idx_company_id")
|
|
100
98
|
#
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
|
|
107
|
-
|
|
108
|
-
indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
|
|
99
|
+
# # Check a valid index exists (PostgreSQL only)
|
|
100
|
+
# index_exists?(:suppliers, :company_id, valid: true)
|
|
101
|
+
#
|
|
102
|
+
def index_exists?(table_name, column_name, **options)
|
|
103
|
+
indexes(table_name).any? { |i| i.defined_for?(column_name, **options) }
|
|
109
104
|
end
|
|
110
105
|
|
|
111
106
|
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
|
112
107
|
def columns(table_name)
|
|
113
108
|
table_name = table_name.to_s
|
|
114
|
-
column_definitions(table_name)
|
|
115
|
-
|
|
109
|
+
definitions = column_definitions(table_name)
|
|
110
|
+
definitions.map do |field|
|
|
111
|
+
new_column_from_field(table_name, field, definitions)
|
|
116
112
|
end
|
|
117
113
|
end
|
|
118
114
|
|
|
@@ -122,6 +118,9 @@ module ActiveRecord
|
|
|
122
118
|
# column_exists?(:suppliers, :name)
|
|
123
119
|
#
|
|
124
120
|
# # Check a column exists of a particular type
|
|
121
|
+
# #
|
|
122
|
+
# # This works for standard non-casted types (eg. string) but is unreliable
|
|
123
|
+
# # for types that may get cast to something else (eg. char, bigint).
|
|
125
124
|
# column_exists?(:suppliers, :name, :string)
|
|
126
125
|
#
|
|
127
126
|
# # Check a column exists with a specific definition
|
|
@@ -187,6 +186,9 @@ module ActiveRecord
|
|
|
187
186
|
# Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
|
|
188
187
|
#
|
|
189
188
|
# A Symbol can be used to specify the type of the generated primary key column.
|
|
189
|
+
#
|
|
190
|
+
# A Hash can be used to specify the generated primary key column creation options.
|
|
191
|
+
# See {add_column}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_column] for available options.
|
|
190
192
|
# [<tt>:primary_key</tt>]
|
|
191
193
|
# The name of the primary key, if one is to be added automatically.
|
|
192
194
|
# Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
|
|
@@ -258,7 +260,7 @@ module ActiveRecord
|
|
|
258
260
|
#
|
|
259
261
|
# generates:
|
|
260
262
|
#
|
|
261
|
-
# CREATE TABLE
|
|
263
|
+
# CREATE TABLE orders (
|
|
262
264
|
# product_id bigint NOT NULL,
|
|
263
265
|
# client_id bigint NOT NULL
|
|
264
266
|
# );
|
|
@@ -291,37 +293,32 @@ module ActiveRecord
|
|
|
291
293
|
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
|
292
294
|
#
|
|
293
295
|
# See also TableDefinition#column for details on how to create columns.
|
|
294
|
-
def create_table(table_name, **options)
|
|
295
|
-
|
|
296
|
+
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)
|
|
297
|
+
validate_create_table_options!(options)
|
|
298
|
+
validate_table_length!(table_name) unless options[:_uses_legacy_table_name]
|
|
296
299
|
|
|
297
|
-
if
|
|
298
|
-
|
|
299
|
-
Base.get_primary_key table_name.to_s.singularize
|
|
300
|
-
end
|
|
301
|
-
|
|
302
|
-
if pk.is_a?(Array)
|
|
303
|
-
td.primary_keys pk
|
|
304
|
-
else
|
|
305
|
-
td.primary_key pk, options.fetch(:id, :primary_key), options.except(:comment)
|
|
306
|
-
end
|
|
300
|
+
if force && options.key?(:if_not_exists)
|
|
301
|
+
raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
|
|
307
302
|
end
|
|
308
303
|
|
|
309
|
-
|
|
304
|
+
td = build_create_table_definition(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
|
|
310
305
|
|
|
311
|
-
if
|
|
312
|
-
drop_table(table_name,
|
|
306
|
+
if force
|
|
307
|
+
drop_table(table_name, force: force, if_exists: true)
|
|
308
|
+
else
|
|
309
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
|
313
310
|
end
|
|
314
311
|
|
|
315
|
-
result = execute schema_creation.accept
|
|
312
|
+
result = execute schema_creation.accept(td)
|
|
316
313
|
|
|
317
314
|
unless supports_indexes_in_create?
|
|
318
315
|
td.indexes.each do |column_name, index_options|
|
|
319
|
-
add_index(table_name, column_name, index_options)
|
|
316
|
+
add_index(table_name, column_name, **index_options, if_not_exists: td.if_not_exists)
|
|
320
317
|
end
|
|
321
318
|
end
|
|
322
319
|
|
|
323
320
|
if supports_comments? && !supports_comments_in_create?
|
|
324
|
-
if table_comment =
|
|
321
|
+
if table_comment = td.comment.presence
|
|
325
322
|
change_table_comment(table_name, table_comment)
|
|
326
323
|
end
|
|
327
324
|
|
|
@@ -333,6 +330,18 @@ module ActiveRecord
|
|
|
333
330
|
result
|
|
334
331
|
end
|
|
335
332
|
|
|
333
|
+
# Returns a TableDefinition object containing information about the table that would be created
|
|
334
|
+
# if the same arguments were passed to #create_table. See #create_table for information about
|
|
335
|
+
# passing a +table_name+, and other additional options that can be passed.
|
|
336
|
+
def build_create_table_definition(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
|
|
337
|
+
table_definition = create_table_definition(table_name, **options.extract!(*valid_table_definition_options, :_skip_validate_options))
|
|
338
|
+
table_definition.set_primary_key(table_name, id, primary_key, **options.extract!(*valid_primary_key_options, :_skip_validate_options))
|
|
339
|
+
|
|
340
|
+
yield table_definition if block_given?
|
|
341
|
+
|
|
342
|
+
table_definition
|
|
343
|
+
end
|
|
344
|
+
|
|
336
345
|
# Creates a new join table with the name created using the lexical order of the first two
|
|
337
346
|
# arguments. These arguments can be a String or a Symbol.
|
|
338
347
|
#
|
|
@@ -376,24 +385,42 @@ module ActiveRecord
|
|
|
376
385
|
|
|
377
386
|
column_options.reverse_merge!(null: false, index: false)
|
|
378
387
|
|
|
379
|
-
t1_ref, t2_ref = [table_1, table_2].map { |t| t
|
|
388
|
+
t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
|
|
389
|
+
|
|
390
|
+
create_table(join_table_name, **options.merge!(id: false)) do |td|
|
|
391
|
+
td.references t1_ref, **column_options
|
|
392
|
+
td.references t2_ref, **column_options
|
|
393
|
+
yield td if block_given?
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
# Builds a TableDefinition object for a join table.
|
|
398
|
+
#
|
|
399
|
+
# This definition object contains information about the table that would be created
|
|
400
|
+
# if the same arguments were passed to #create_join_table. See #create_join_table for
|
|
401
|
+
# information about what arguments should be passed.
|
|
402
|
+
def build_create_join_table_definition(table_1, table_2, column_options: {}, **options) # :nodoc:
|
|
403
|
+
join_table_name = find_join_table_name(table_1, table_2, options)
|
|
404
|
+
column_options.reverse_merge!(null: false, index: false)
|
|
405
|
+
|
|
406
|
+
t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
|
|
380
407
|
|
|
381
|
-
|
|
382
|
-
td.references t1_ref, column_options
|
|
383
|
-
td.references t2_ref, column_options
|
|
408
|
+
build_create_table_definition(join_table_name, **options.merge!(id: false)) do |td|
|
|
409
|
+
td.references t1_ref, **column_options
|
|
410
|
+
td.references t2_ref, **column_options
|
|
384
411
|
yield td if block_given?
|
|
385
412
|
end
|
|
386
413
|
end
|
|
387
414
|
|
|
388
415
|
# Drops the join table specified by the given arguments.
|
|
389
|
-
# See #create_join_table for details.
|
|
416
|
+
# See #create_join_table and #drop_table for details.
|
|
390
417
|
#
|
|
391
418
|
# Although this command ignores the block if one is given, it can be helpful
|
|
392
419
|
# to provide one in a migration's +change+ method so it can be reverted.
|
|
393
420
|
# In that case, the block will be used by #create_join_table.
|
|
394
|
-
def drop_join_table(table_1, table_2, options
|
|
421
|
+
def drop_join_table(table_1, table_2, **options)
|
|
395
422
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
|
396
|
-
drop_table(join_table_name)
|
|
423
|
+
drop_table(join_table_name, **options)
|
|
397
424
|
end
|
|
398
425
|
|
|
399
426
|
# A block for changing columns in +table+.
|
|
@@ -420,6 +447,12 @@ module ActiveRecord
|
|
|
420
447
|
# t.column :name, :string, limit: 60
|
|
421
448
|
# end
|
|
422
449
|
#
|
|
450
|
+
# ====== Change type of a column
|
|
451
|
+
#
|
|
452
|
+
# change_table(:suppliers) do |t|
|
|
453
|
+
# t.change :metadata, :json
|
|
454
|
+
# end
|
|
455
|
+
#
|
|
423
456
|
# ====== Add 2 integer columns
|
|
424
457
|
#
|
|
425
458
|
# change_table(:suppliers) do |t|
|
|
@@ -468,13 +501,13 @@ module ActiveRecord
|
|
|
468
501
|
# end
|
|
469
502
|
#
|
|
470
503
|
# See also Table for details on all of the various column transformations.
|
|
471
|
-
def change_table(table_name,
|
|
504
|
+
def change_table(table_name, base = self, **options)
|
|
472
505
|
if supports_bulk_alter? && options[:bulk]
|
|
473
506
|
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
|
|
474
507
|
yield update_table_definition(table_name, recorder)
|
|
475
508
|
bulk_change_table(table_name, recorder.commands)
|
|
476
509
|
else
|
|
477
|
-
yield update_table_definition(table_name,
|
|
510
|
+
yield update_table_definition(table_name, base)
|
|
478
511
|
end
|
|
479
512
|
end
|
|
480
513
|
|
|
@@ -482,7 +515,7 @@ module ActiveRecord
|
|
|
482
515
|
#
|
|
483
516
|
# rename_table('octopuses', 'octopi')
|
|
484
517
|
#
|
|
485
|
-
def rename_table(table_name, new_name)
|
|
518
|
+
def rename_table(table_name, new_name, **)
|
|
486
519
|
raise NotImplementedError, "rename_table is not implemented"
|
|
487
520
|
end
|
|
488
521
|
|
|
@@ -498,30 +531,38 @@ module ActiveRecord
|
|
|
498
531
|
# Although this command ignores most +options+ and the block if one is given,
|
|
499
532
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
|
500
533
|
# In that case, +options+ and the block will be used by #create_table.
|
|
501
|
-
def drop_table(table_name, options
|
|
534
|
+
def drop_table(table_name, **options)
|
|
535
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
|
502
536
|
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
|
503
537
|
end
|
|
504
538
|
|
|
505
539
|
# Add a new +type+ column named +column_name+ to +table_name+.
|
|
506
540
|
#
|
|
541
|
+
# See {ActiveRecord::ConnectionAdapters::TableDefinition.column}[rdoc-ref:ActiveRecord::ConnectionAdapters::TableDefinition#column].
|
|
542
|
+
#
|
|
507
543
|
# The +type+ parameter is normally one of the migrations native types,
|
|
508
544
|
# which is one of the following:
|
|
509
545
|
# <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
|
|
510
546
|
# <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
|
|
511
547
|
# <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
|
|
512
|
-
# <tt>:binary</tt>, <tt>:boolean</tt>.
|
|
548
|
+
# <tt>:binary</tt>, <tt>:blob</tt>, <tt>:boolean</tt>.
|
|
513
549
|
#
|
|
514
550
|
# You may use a type not in this list as long as it is supported by your
|
|
515
551
|
# database (for example, "polygon" in MySQL), but this will not be database
|
|
516
552
|
# agnostic and should usually be avoided.
|
|
517
553
|
#
|
|
518
554
|
# Available options are (none of these exists by default):
|
|
555
|
+
# * <tt>:comment</tt> -
|
|
556
|
+
# Specifies the comment for the column. This option is ignored by some backends.
|
|
557
|
+
# * <tt>:collation</tt> -
|
|
558
|
+
# Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column.
|
|
559
|
+
# If not specified, the column will have the same collation as the table.
|
|
560
|
+
# * <tt>:default</tt> -
|
|
561
|
+
# The column's default value. Use +nil+ for +NULL+.
|
|
519
562
|
# * <tt>:limit</tt> -
|
|
520
563
|
# Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
|
|
521
|
-
# and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
|
|
564
|
+
# and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, <tt>:blob</tt>, and <tt>:integer</tt> columns.
|
|
522
565
|
# This option is ignored by some backends.
|
|
523
|
-
# * <tt>:default</tt> -
|
|
524
|
-
# The column's default value. Use +nil+ for +NULL+.
|
|
525
566
|
# * <tt>:null</tt> -
|
|
526
567
|
# Allows or disallows +NULL+ values in the column.
|
|
527
568
|
# * <tt>:precision</tt> -
|
|
@@ -529,11 +570,9 @@ module ActiveRecord
|
|
|
529
570
|
# <tt>:datetime</tt>, and <tt>:time</tt> columns.
|
|
530
571
|
# * <tt>:scale</tt> -
|
|
531
572
|
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
|
532
|
-
# * <tt>:
|
|
533
|
-
# Specifies the
|
|
534
|
-
# column
|
|
535
|
-
# * <tt>:comment</tt> -
|
|
536
|
-
# Specifies the comment for the column. This option is ignored by some backends.
|
|
573
|
+
# * <tt>:if_not_exists</tt> -
|
|
574
|
+
# Specifies if the column already exists to not try to re-add it. This will avoid
|
|
575
|
+
# duplicate column errors.
|
|
537
576
|
#
|
|
538
577
|
# Note: The precision is the total number of significant digits,
|
|
539
578
|
# and the scale is the number of digits that can be stored following
|
|
@@ -546,7 +585,7 @@ module ActiveRecord
|
|
|
546
585
|
# * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
|
|
547
586
|
# <tt>:precision</tt>, and makes no comments about the requirements of
|
|
548
587
|
# <tt>:precision</tt>.
|
|
549
|
-
# * MySQL: <tt>:precision</tt> [1..
|
|
588
|
+
# * MySQL: <tt>:precision</tt> [1..65], <tt>:scale</tt> [0..30].
|
|
550
589
|
# Default is (10,0).
|
|
551
590
|
# * PostgreSQL: <tt>:precision</tt> [1..infinity],
|
|
552
591
|
# <tt>:scale</tt> [0..infinity]. No default.
|
|
@@ -554,8 +593,6 @@ module ActiveRecord
|
|
|
554
593
|
# but the maximum supported <tt>:precision</tt> is 16. No default.
|
|
555
594
|
# * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
|
|
556
595
|
# Default is (38,0).
|
|
557
|
-
# * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
|
|
558
|
-
# Default unknown.
|
|
559
596
|
# * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
|
|
560
597
|
# Default (38,0).
|
|
561
598
|
#
|
|
@@ -585,21 +622,55 @@ module ActiveRecord
|
|
|
585
622
|
# # Defines a column with a database-specific type.
|
|
586
623
|
# add_column(:shapes, :triangle, 'polygon')
|
|
587
624
|
# # ALTER TABLE "shapes" ADD "triangle" polygon
|
|
625
|
+
#
|
|
626
|
+
# # Ignores the method call if the column exists
|
|
627
|
+
# add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
|
|
588
628
|
def add_column(table_name, column_name, type, **options)
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
629
|
+
add_column_def = build_add_column_definition(table_name, column_name, type, **options)
|
|
630
|
+
return unless add_column_def
|
|
631
|
+
|
|
632
|
+
execute schema_creation.accept(add_column_def)
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
def add_columns(table_name, *column_names, type:, **options) # :nodoc:
|
|
636
|
+
column_names.each do |column_name|
|
|
637
|
+
add_column(table_name, column_name, type, **options)
|
|
638
|
+
end
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
# Builds an AlterTable object for adding a column to a table.
|
|
642
|
+
#
|
|
643
|
+
# This definition object contains information about the column that would be created
|
|
644
|
+
# if the same arguments were passed to #add_column. See #add_column for information about
|
|
645
|
+
# passing a +table_name+, +column_name+, +type+ and other options that can be passed.
|
|
646
|
+
def build_add_column_definition(table_name, column_name, type, **options) # :nodoc:
|
|
647
|
+
return if options[:if_not_exists] == true && column_exists?(table_name, column_name)
|
|
648
|
+
|
|
649
|
+
if supports_datetime_with_precision?
|
|
650
|
+
if type == :datetime && !options.key?(:precision)
|
|
651
|
+
options[:precision] = 6
|
|
652
|
+
end
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
alter_table = create_alter_table(table_name)
|
|
656
|
+
alter_table.add_column(column_name, type, **options)
|
|
657
|
+
alter_table
|
|
592
658
|
end
|
|
593
659
|
|
|
594
660
|
# Removes the given columns from the table definition.
|
|
595
661
|
#
|
|
596
662
|
# remove_columns(:suppliers, :qualification, :experience)
|
|
597
663
|
#
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
664
|
+
# +type+ and other column options can be passed to make migration reversible.
|
|
665
|
+
#
|
|
666
|
+
# remove_columns(:suppliers, :qualification, :experience, type: :string, null: false)
|
|
667
|
+
def remove_columns(table_name, *column_names, type: nil, **options)
|
|
668
|
+
if column_names.empty?
|
|
669
|
+
raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
|
|
602
670
|
end
|
|
671
|
+
|
|
672
|
+
remove_column_fragments = remove_columns_for_alter(table_name, *column_names, type: type, **options)
|
|
673
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_fragments.join(', ')}"
|
|
603
674
|
end
|
|
604
675
|
|
|
605
676
|
# Removes the column from the table definition.
|
|
@@ -609,9 +680,18 @@ module ActiveRecord
|
|
|
609
680
|
# The +type+ and +options+ parameters will be ignored if present. It can be helpful
|
|
610
681
|
# to provide these in a migration's +change+ method so it can be reverted.
|
|
611
682
|
# In that case, +type+ and +options+ will be used by #add_column.
|
|
612
|
-
#
|
|
613
|
-
|
|
614
|
-
|
|
683
|
+
# Depending on the database you're using, indexes using this column may be
|
|
684
|
+
# automatically removed or modified to remove this column from the index.
|
|
685
|
+
#
|
|
686
|
+
# If the options provided include an +if_exists+ key, it will be used to check if the
|
|
687
|
+
# column does not exist. This will silently ignore the migration rather than raising
|
|
688
|
+
# if the column was already removed.
|
|
689
|
+
#
|
|
690
|
+
# remove_column(:suppliers, :qualification, if_exists: true)
|
|
691
|
+
def remove_column(table_name, column_name, type = nil, **options)
|
|
692
|
+
return if options[:if_exists] == true && !column_exists?(table_name, column_name)
|
|
693
|
+
|
|
694
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, **options)}"
|
|
615
695
|
end
|
|
616
696
|
|
|
617
697
|
# Changes the column's definition according to the new options.
|
|
@@ -620,7 +700,7 @@ module ActiveRecord
|
|
|
620
700
|
# change_column(:suppliers, :name, :string, limit: 80)
|
|
621
701
|
# change_column(:accounts, :description, :text)
|
|
622
702
|
#
|
|
623
|
-
def change_column(table_name, column_name, type, options
|
|
703
|
+
def change_column(table_name, column_name, type, **options)
|
|
624
704
|
raise NotImplementedError, "change_column is not implemented"
|
|
625
705
|
end
|
|
626
706
|
|
|
@@ -642,6 +722,15 @@ module ActiveRecord
|
|
|
642
722
|
raise NotImplementedError, "change_column_default is not implemented"
|
|
643
723
|
end
|
|
644
724
|
|
|
725
|
+
# Builds a ChangeColumnDefaultDefinition object.
|
|
726
|
+
#
|
|
727
|
+
# This definition object contains information about the column change that would occur
|
|
728
|
+
# if the same arguments were passed to #change_column_default. See #change_column_default for
|
|
729
|
+
# information about passing a +table_name+, +column_name+, +type+ and other options that can be passed.
|
|
730
|
+
def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
|
|
731
|
+
raise NotImplementedError, "build_change_column_default_definition is not implemented"
|
|
732
|
+
end
|
|
733
|
+
|
|
645
734
|
# Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
|
|
646
735
|
# indicates whether the value can be +NULL+. For example
|
|
647
736
|
#
|
|
@@ -682,7 +771,17 @@ module ActiveRecord
|
|
|
682
771
|
#
|
|
683
772
|
# generates:
|
|
684
773
|
#
|
|
685
|
-
# CREATE INDEX
|
|
774
|
+
# CREATE INDEX index_suppliers_on_name ON suppliers(name)
|
|
775
|
+
#
|
|
776
|
+
# ====== Creating a index which already exists
|
|
777
|
+
#
|
|
778
|
+
# add_index(:suppliers, :name, if_not_exists: true)
|
|
779
|
+
#
|
|
780
|
+
# generates:
|
|
781
|
+
#
|
|
782
|
+
# CREATE INDEX IF NOT EXISTS index_suppliers_on_name ON suppliers(name)
|
|
783
|
+
#
|
|
784
|
+
# Note: Not supported by MySQL.
|
|
686
785
|
#
|
|
687
786
|
# ====== Creating a unique index
|
|
688
787
|
#
|
|
@@ -690,7 +789,7 @@ module ActiveRecord
|
|
|
690
789
|
#
|
|
691
790
|
# generates:
|
|
692
791
|
#
|
|
693
|
-
# CREATE UNIQUE INDEX
|
|
792
|
+
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id)
|
|
694
793
|
#
|
|
695
794
|
# ====== Creating a named index
|
|
696
795
|
#
|
|
@@ -716,11 +815,11 @@ module ActiveRecord
|
|
|
716
815
|
#
|
|
717
816
|
# CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
|
|
718
817
|
#
|
|
719
|
-
# Note:
|
|
818
|
+
# Note: only supported by MySQL
|
|
720
819
|
#
|
|
721
820
|
# ====== Creating an index with a sort order (desc or asc, asc is the default)
|
|
722
821
|
#
|
|
723
|
-
# add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
|
|
822
|
+
# add_index(:accounts, [:branch_id, :party_id, :surname], name: 'by_branch_desc_party', order: {branch_id: :desc, party_id: :asc})
|
|
724
823
|
#
|
|
725
824
|
# generates:
|
|
726
825
|
#
|
|
@@ -736,7 +835,17 @@ module ActiveRecord
|
|
|
736
835
|
#
|
|
737
836
|
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
|
|
738
837
|
#
|
|
739
|
-
# Note: Partial indexes are only supported for PostgreSQL and SQLite
|
|
838
|
+
# Note: Partial indexes are only supported for PostgreSQL and SQLite.
|
|
839
|
+
#
|
|
840
|
+
# ====== Creating an index that includes additional columns
|
|
841
|
+
#
|
|
842
|
+
# add_index(:accounts, :branch_id, include: :party_id)
|
|
843
|
+
#
|
|
844
|
+
# generates:
|
|
845
|
+
#
|
|
846
|
+
# CREATE INDEX index_accounts_on_branch_id ON accounts USING btree(branch_id) INCLUDE (party_id)
|
|
847
|
+
#
|
|
848
|
+
# Note: only supported by PostgreSQL.
|
|
740
849
|
#
|
|
741
850
|
# ====== Creating an index with a specific method
|
|
742
851
|
#
|
|
@@ -775,16 +884,29 @@ module ActiveRecord
|
|
|
775
884
|
# ====== Creating an index with a specific algorithm
|
|
776
885
|
#
|
|
777
886
|
# add_index(:developers, :name, algorithm: :concurrently)
|
|
778
|
-
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
|
|
887
|
+
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name) -- PostgreSQL
|
|
779
888
|
#
|
|
780
|
-
#
|
|
889
|
+
# add_index(:developers, :name, algorithm: :inplace)
|
|
890
|
+
# # CREATE INDEX `index_developers_on_name` ON `developers` (`name`) ALGORITHM = INPLACE -- MySQL
|
|
891
|
+
#
|
|
892
|
+
# Note: only supported by PostgreSQL and MySQL.
|
|
781
893
|
#
|
|
782
894
|
# Concurrently adding an index is not supported in a transaction.
|
|
783
895
|
#
|
|
784
896
|
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
|
785
|
-
def add_index(table_name, column_name, options
|
|
786
|
-
|
|
787
|
-
execute
|
|
897
|
+
def add_index(table_name, column_name, **options)
|
|
898
|
+
create_index = build_create_index_definition(table_name, column_name, **options)
|
|
899
|
+
execute schema_creation.accept(create_index)
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
# Builds a CreateIndexDefinition object.
|
|
903
|
+
#
|
|
904
|
+
# This definition object contains information about the index that would be created
|
|
905
|
+
# if the same arguments were passed to #add_index. See #add_index for information about
|
|
906
|
+
# passing a +table_name+, +column_name+, and other additional options that can be passed.
|
|
907
|
+
def build_create_index_definition(table_name, column_name, **options) # :nodoc:
|
|
908
|
+
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
|
909
|
+
CreateIndexDefinition.new(index, algorithm, if_not_exists)
|
|
788
910
|
end
|
|
789
911
|
|
|
790
912
|
# Removes the given index from the table.
|
|
@@ -805,6 +927,15 @@ module ActiveRecord
|
|
|
805
927
|
#
|
|
806
928
|
# remove_index :accounts, name: :by_branch_party
|
|
807
929
|
#
|
|
930
|
+
# Removes the index on +branch_id+ named +by_branch_party+ in the +accounts+ table.
|
|
931
|
+
#
|
|
932
|
+
# remove_index :accounts, :branch_id, name: :by_branch_party
|
|
933
|
+
#
|
|
934
|
+
# Checks if the index exists before trying to remove it. Will silently ignore indexes that
|
|
935
|
+
# don't exist.
|
|
936
|
+
#
|
|
937
|
+
# remove_index :accounts, if_exists: true
|
|
938
|
+
#
|
|
808
939
|
# Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
|
|
809
940
|
#
|
|
810
941
|
# remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
|
|
@@ -814,8 +945,11 @@ module ActiveRecord
|
|
|
814
945
|
# Concurrently removing an index is not supported in a transaction.
|
|
815
946
|
#
|
|
816
947
|
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
|
817
|
-
def remove_index(table_name,
|
|
818
|
-
|
|
948
|
+
def remove_index(table_name, column_name = nil, **options)
|
|
949
|
+
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
|
950
|
+
|
|
951
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
|
952
|
+
|
|
819
953
|
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
|
820
954
|
end
|
|
821
955
|
|
|
@@ -826,6 +960,8 @@ module ActiveRecord
|
|
|
826
960
|
# rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
|
|
827
961
|
#
|
|
828
962
|
def rename_index(table_name, old_name, new_name)
|
|
963
|
+
old_name = old_name.to_s
|
|
964
|
+
new_name = new_name.to_s
|
|
829
965
|
validate_index_length!(table_name, new_name)
|
|
830
966
|
|
|
831
967
|
# this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
|
|
@@ -835,10 +971,14 @@ module ActiveRecord
|
|
|
835
971
|
remove_index(table_name, name: old_name)
|
|
836
972
|
end
|
|
837
973
|
|
|
838
|
-
def index_name(table_name, options)
|
|
974
|
+
def index_name(table_name, options) # :nodoc:
|
|
839
975
|
if Hash === options
|
|
840
976
|
if options[:column]
|
|
841
|
-
|
|
977
|
+
if options[:_uses_legacy_index_name]
|
|
978
|
+
"index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
|
|
979
|
+
else
|
|
980
|
+
generate_index_name(table_name, options[:column])
|
|
981
|
+
end
|
|
842
982
|
elsif options[:name]
|
|
843
983
|
options[:name]
|
|
844
984
|
else
|
|
@@ -858,7 +998,6 @@ module ActiveRecord
|
|
|
858
998
|
# Adds a reference. The reference column is a bigint by default,
|
|
859
999
|
# the <tt>:type</tt> option can be used to specify a different type.
|
|
860
1000
|
# Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
|
|
861
|
-
# #add_reference and #add_belongs_to are acceptable.
|
|
862
1001
|
#
|
|
863
1002
|
# The +options+ hash can include the following keys:
|
|
864
1003
|
# [<tt>:type</tt>]
|
|
@@ -867,7 +1006,9 @@ module ActiveRecord
|
|
|
867
1006
|
# Add an appropriate index. Defaults to true.
|
|
868
1007
|
# See #add_index for usage of this option.
|
|
869
1008
|
# [<tt>:foreign_key</tt>]
|
|
870
|
-
# Add an appropriate foreign key constraint. Defaults to false
|
|
1009
|
+
# Add an appropriate foreign key constraint. Defaults to false, pass true
|
|
1010
|
+
# to add. In case the join table can't be inferred from the association
|
|
1011
|
+
# pass <tt>:to_table</tt> with the appropriate table name.
|
|
871
1012
|
# [<tt>:polymorphic</tt>]
|
|
872
1013
|
# Whether an additional +_type+ column should be added. Defaults to false.
|
|
873
1014
|
# [<tt>:null</tt>]
|
|
@@ -899,15 +1040,14 @@ module ActiveRecord
|
|
|
899
1040
|
#
|
|
900
1041
|
# ====== Create a supplier_id column and a foreign key to the firms table
|
|
901
1042
|
#
|
|
902
|
-
# add_reference(:products, :supplier, foreign_key: {to_table: :firms})
|
|
1043
|
+
# add_reference(:products, :supplier, foreign_key: { to_table: :firms })
|
|
903
1044
|
#
|
|
904
1045
|
def add_reference(table_name, ref_name, **options)
|
|
905
|
-
ReferenceDefinition.new(ref_name, options).
|
|
1046
|
+
ReferenceDefinition.new(ref_name, **options).add(table_name, self)
|
|
906
1047
|
end
|
|
907
1048
|
alias :add_belongs_to :add_reference
|
|
908
1049
|
|
|
909
1050
|
# Removes the reference(s). Also removes a +type+ column if one exists.
|
|
910
|
-
# #remove_reference and #remove_belongs_to are acceptable.
|
|
911
1051
|
#
|
|
912
1052
|
# ====== Remove the reference
|
|
913
1053
|
#
|
|
@@ -922,19 +1062,21 @@ module ActiveRecord
|
|
|
922
1062
|
# remove_reference(:products, :user, foreign_key: true)
|
|
923
1063
|
#
|
|
924
1064
|
def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
|
|
1065
|
+
conditional_options = options.slice(:if_exists, :if_not_exists)
|
|
1066
|
+
|
|
925
1067
|
if foreign_key
|
|
926
1068
|
reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
|
|
927
1069
|
if foreign_key.is_a?(Hash)
|
|
928
|
-
foreign_key_options = foreign_key
|
|
1070
|
+
foreign_key_options = foreign_key.merge(conditional_options)
|
|
929
1071
|
else
|
|
930
|
-
foreign_key_options = { to_table: reference_name }
|
|
1072
|
+
foreign_key_options = { to_table: reference_name, **conditional_options }
|
|
931
1073
|
end
|
|
932
1074
|
foreign_key_options[:column] ||= "#{ref_name}_id"
|
|
933
|
-
remove_foreign_key(table_name, foreign_key_options)
|
|
1075
|
+
remove_foreign_key(table_name, **foreign_key_options)
|
|
934
1076
|
end
|
|
935
1077
|
|
|
936
|
-
remove_column(table_name, "#{ref_name}_id")
|
|
937
|
-
remove_column(table_name, "#{ref_name}_type") if polymorphic
|
|
1078
|
+
remove_column(table_name, "#{ref_name}_id", **conditional_options)
|
|
1079
|
+
remove_column(table_name, "#{ref_name}_type", **conditional_options) if polymorphic
|
|
938
1080
|
end
|
|
939
1081
|
alias :remove_belongs_to :remove_reference
|
|
940
1082
|
|
|
@@ -959,6 +1101,10 @@ module ActiveRecord
|
|
|
959
1101
|
#
|
|
960
1102
|
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
|
|
961
1103
|
#
|
|
1104
|
+
# ====== Creating a foreign key, ignoring method call if the foreign key exists
|
|
1105
|
+
#
|
|
1106
|
+
# add_foreign_key(:articles, :authors, if_not_exists: true)
|
|
1107
|
+
#
|
|
962
1108
|
# ====== Creating a foreign key on a specific column
|
|
963
1109
|
#
|
|
964
1110
|
# add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
|
|
@@ -967,6 +1113,16 @@ module ActiveRecord
|
|
|
967
1113
|
#
|
|
968
1114
|
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")
|
|
969
1115
|
#
|
|
1116
|
+
# ====== Creating a composite foreign key
|
|
1117
|
+
#
|
|
1118
|
+
# Assuming "carts" table has "(shop_id, user_id)" as a primary key.
|
|
1119
|
+
#
|
|
1120
|
+
# add_foreign_key :orders, :carts, primary_key: [:shop_id, :user_id]
|
|
1121
|
+
#
|
|
1122
|
+
# generates:
|
|
1123
|
+
#
|
|
1124
|
+
# ALTER TABLE "orders" ADD CONSTRAINT fk_rails_6f5e4cb3a4 FOREIGN KEY ("cart_shop_id", "cart_user_id") REFERENCES "carts" ("shop_id", "user_id")
|
|
1125
|
+
#
|
|
970
1126
|
# ====== Creating a cascading foreign key
|
|
971
1127
|
#
|
|
972
1128
|
# add_foreign_key :articles, :authors, on_delete: :cascade
|
|
@@ -977,19 +1133,28 @@ module ActiveRecord
|
|
|
977
1133
|
#
|
|
978
1134
|
# The +options+ hash can include the following keys:
|
|
979
1135
|
# [<tt>:column</tt>]
|
|
980
|
-
# The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt
|
|
1136
|
+
# The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>.
|
|
1137
|
+
# Pass an array to create a composite foreign key.
|
|
981
1138
|
# [<tt>:primary_key</tt>]
|
|
982
1139
|
# The primary key column name on +to_table+. Defaults to +id+.
|
|
1140
|
+
# Pass an array to create a composite foreign key.
|
|
983
1141
|
# [<tt>:name</tt>]
|
|
984
1142
|
# The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
|
|
985
1143
|
# [<tt>:on_delete</tt>]
|
|
986
|
-
# Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade
|
|
1144
|
+
# Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+, and +:restrict+
|
|
987
1145
|
# [<tt>:on_update</tt>]
|
|
988
|
-
# Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade
|
|
1146
|
+
# Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+, and +:restrict+
|
|
1147
|
+
# [<tt>:if_not_exists</tt>]
|
|
1148
|
+
# Specifies if the foreign key already exists to not try to re-add it. This will avoid
|
|
1149
|
+
# duplicate column errors.
|
|
989
1150
|
# [<tt>:validate</tt>]
|
|
990
1151
|
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
|
991
|
-
|
|
992
|
-
|
|
1152
|
+
# [<tt>:deferrable</tt>]
|
|
1153
|
+
# (PostgreSQL only) Specify whether or not the foreign key should be deferrable. Valid values are booleans or
|
|
1154
|
+
# +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
|
|
1155
|
+
def add_foreign_key(from_table, to_table, **options)
|
|
1156
|
+
return unless use_foreign_keys?
|
|
1157
|
+
return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
|
|
993
1158
|
|
|
994
1159
|
options = foreign_key_options(from_table, to_table, options)
|
|
995
1160
|
at = create_alter_table from_table
|
|
@@ -1019,12 +1184,18 @@ module ActiveRecord
|
|
|
1019
1184
|
#
|
|
1020
1185
|
# remove_foreign_key :accounts, name: :special_fk_name
|
|
1021
1186
|
#
|
|
1187
|
+
# Checks if the foreign key exists before trying to remove it. Will silently ignore indexes that
|
|
1188
|
+
# don't exist.
|
|
1189
|
+
#
|
|
1190
|
+
# remove_foreign_key :accounts, :branches, if_exists: true
|
|
1191
|
+
#
|
|
1022
1192
|
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
|
|
1023
1193
|
# with an addition of
|
|
1024
1194
|
# [<tt>:to_table</tt>]
|
|
1025
1195
|
# The name of the table that contains the referenced primary key.
|
|
1026
1196
|
def remove_foreign_key(from_table, to_table = nil, **options)
|
|
1027
|
-
return unless
|
|
1197
|
+
return unless use_foreign_keys?
|
|
1198
|
+
return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
|
|
1028
1199
|
|
|
1029
1200
|
fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
|
|
1030
1201
|
|
|
@@ -1049,20 +1220,116 @@ module ActiveRecord
|
|
|
1049
1220
|
foreign_key_for(from_table, to_table: to_table, **options).present?
|
|
1050
1221
|
end
|
|
1051
1222
|
|
|
1052
|
-
def foreign_key_column_for(table_name) # :nodoc:
|
|
1223
|
+
def foreign_key_column_for(table_name, column_name) # :nodoc:
|
|
1053
1224
|
name = strip_table_name_prefix_and_suffix(table_name)
|
|
1054
|
-
"#{name.singularize}
|
|
1225
|
+
"#{name.singularize}_#{column_name}"
|
|
1055
1226
|
end
|
|
1056
1227
|
|
|
1057
1228
|
def foreign_key_options(from_table, to_table, options) # :nodoc:
|
|
1058
1229
|
options = options.dup
|
|
1059
|
-
|
|
1230
|
+
|
|
1231
|
+
if options[:primary_key].is_a?(Array)
|
|
1232
|
+
options[:column] ||= options[:primary_key].map do |pk_column|
|
|
1233
|
+
foreign_key_column_for(to_table, pk_column)
|
|
1234
|
+
end
|
|
1235
|
+
else
|
|
1236
|
+
options[:column] ||= foreign_key_column_for(to_table, "id")
|
|
1237
|
+
end
|
|
1238
|
+
|
|
1060
1239
|
options[:name] ||= foreign_key_name(from_table, options)
|
|
1240
|
+
|
|
1241
|
+
if options[:column].is_a?(Array) || options[:primary_key].is_a?(Array)
|
|
1242
|
+
if Array(options[:primary_key]).size != Array(options[:column]).size
|
|
1243
|
+
raise ArgumentError, <<~MSG.squish
|
|
1244
|
+
For composite primary keys, specify :column and :primary_key, where
|
|
1245
|
+
:column must reference all the :primary_key columns from #{to_table.inspect}
|
|
1246
|
+
MSG
|
|
1247
|
+
end
|
|
1248
|
+
end
|
|
1249
|
+
|
|
1061
1250
|
options
|
|
1062
1251
|
end
|
|
1063
1252
|
|
|
1253
|
+
# Returns an array of check constraints for the given table.
|
|
1254
|
+
# The check constraints are represented as CheckConstraintDefinition objects.
|
|
1255
|
+
def check_constraints(table_name)
|
|
1256
|
+
raise NotImplementedError
|
|
1257
|
+
end
|
|
1258
|
+
|
|
1259
|
+
# Adds a new check constraint to the table. +expression+ is a String
|
|
1260
|
+
# representation of verifiable boolean condition.
|
|
1261
|
+
#
|
|
1262
|
+
# add_check_constraint :products, "price > 0", name: "price_check"
|
|
1263
|
+
#
|
|
1264
|
+
# generates:
|
|
1265
|
+
#
|
|
1266
|
+
# ALTER TABLE "products" ADD CONSTRAINT price_check CHECK (price > 0)
|
|
1267
|
+
#
|
|
1268
|
+
# The +options+ hash can include the following keys:
|
|
1269
|
+
# [<tt>:name</tt>]
|
|
1270
|
+
# The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
|
|
1271
|
+
# [<tt>:if_not_exists</tt>]
|
|
1272
|
+
# Silently ignore if the constraint already exists, rather than raise an error.
|
|
1273
|
+
# [<tt>:validate</tt>]
|
|
1274
|
+
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
|
1275
|
+
def add_check_constraint(table_name, expression, if_not_exists: false, **options)
|
|
1276
|
+
return unless supports_check_constraints?
|
|
1277
|
+
|
|
1278
|
+
options = check_constraint_options(table_name, expression, options)
|
|
1279
|
+
return if if_not_exists && check_constraint_exists?(table_name, **options)
|
|
1280
|
+
|
|
1281
|
+
at = create_alter_table(table_name)
|
|
1282
|
+
at.add_check_constraint(expression, options)
|
|
1283
|
+
|
|
1284
|
+
execute schema_creation.accept(at)
|
|
1285
|
+
end
|
|
1286
|
+
|
|
1287
|
+
def check_constraint_options(table_name, expression, options) # :nodoc:
|
|
1288
|
+
options = options.dup
|
|
1289
|
+
options[:name] ||= check_constraint_name(table_name, expression: expression, **options)
|
|
1290
|
+
options
|
|
1291
|
+
end
|
|
1292
|
+
|
|
1293
|
+
# Removes the given check constraint from the table. Removing a check constraint
|
|
1294
|
+
# that does not exist will raise an error.
|
|
1295
|
+
#
|
|
1296
|
+
# remove_check_constraint :products, name: "price_check"
|
|
1297
|
+
#
|
|
1298
|
+
# To silently ignore a non-existent check constraint rather than raise an error,
|
|
1299
|
+
# use the +if_exists+ option.
|
|
1300
|
+
#
|
|
1301
|
+
# remove_check_constraint :products, name: "price_check", if_exists: true
|
|
1302
|
+
#
|
|
1303
|
+
# The +expression+ parameter will be ignored if present. It can be helpful
|
|
1304
|
+
# to provide this in a migration's +change+ method so it can be reverted.
|
|
1305
|
+
# In that case, +expression+ will be used by #add_check_constraint.
|
|
1306
|
+
def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
|
|
1307
|
+
return unless supports_check_constraints?
|
|
1308
|
+
|
|
1309
|
+
return if if_exists && !check_constraint_exists?(table_name, **options)
|
|
1310
|
+
|
|
1311
|
+
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
|
1312
|
+
|
|
1313
|
+
at = create_alter_table(table_name)
|
|
1314
|
+
at.drop_check_constraint(chk_name_to_delete)
|
|
1315
|
+
|
|
1316
|
+
execute schema_creation.accept(at)
|
|
1317
|
+
end
|
|
1318
|
+
|
|
1319
|
+
|
|
1320
|
+
# Checks to see if a check constraint exists on a table for a given check constraint definition.
|
|
1321
|
+
#
|
|
1322
|
+
# check_constraint_exists?(:products, name: "price_check")
|
|
1323
|
+
#
|
|
1324
|
+
def check_constraint_exists?(table_name, **options)
|
|
1325
|
+
if !options.key?(:name) && !options.key?(:expression)
|
|
1326
|
+
raise ArgumentError, "At least one of :name or :expression must be supplied"
|
|
1327
|
+
end
|
|
1328
|
+
check_constraint_for(table_name, **options).present?
|
|
1329
|
+
end
|
|
1330
|
+
|
|
1064
1331
|
def dump_schema_information # :nodoc:
|
|
1065
|
-
versions = schema_migration.
|
|
1332
|
+
versions = pool.schema_migration.versions
|
|
1066
1333
|
insert_versions_sql(versions) if versions.any?
|
|
1067
1334
|
end
|
|
1068
1335
|
|
|
@@ -1070,16 +1337,11 @@ module ActiveRecord
|
|
|
1070
1337
|
{ primary_key: true }
|
|
1071
1338
|
end
|
|
1072
1339
|
|
|
1073
|
-
def assume_migrated_upto_version(version
|
|
1074
|
-
unless migrations_paths.nil?
|
|
1075
|
-
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
|
1076
|
-
Passing migrations_paths to #assume_migrated_upto_version is deprecated and will be removed in Rails 6.1.
|
|
1077
|
-
MSG
|
|
1078
|
-
end
|
|
1079
|
-
|
|
1340
|
+
def assume_migrated_upto_version(version)
|
|
1080
1341
|
version = version.to_i
|
|
1081
|
-
sm_table = quote_table_name(schema_migration.table_name)
|
|
1342
|
+
sm_table = quote_table_name(pool.schema_migration.table_name)
|
|
1082
1343
|
|
|
1344
|
+
migration_context = pool.migration_context
|
|
1083
1345
|
migrated = migration_context.get_all_versions
|
|
1084
1346
|
versions = migration_context.migrations.map(&:version)
|
|
1085
1347
|
|
|
@@ -1140,65 +1402,92 @@ module ActiveRecord
|
|
|
1140
1402
|
columns
|
|
1141
1403
|
end
|
|
1142
1404
|
|
|
1405
|
+
def distinct_relation_for_primary_key(relation) # :nodoc:
|
|
1406
|
+
primary_key_columns = Array(relation.primary_key).map do |column|
|
|
1407
|
+
visitor.compile(relation.table[column])
|
|
1408
|
+
end
|
|
1409
|
+
|
|
1410
|
+
values = columns_for_distinct(
|
|
1411
|
+
primary_key_columns,
|
|
1412
|
+
relation.order_values
|
|
1413
|
+
)
|
|
1414
|
+
|
|
1415
|
+
limited = relation.reselect(values).distinct!
|
|
1416
|
+
limited_ids = select_rows(limited.arel, "SQL").map do |results|
|
|
1417
|
+
results.last(Array(relation.primary_key).length) # ignores order values for MySQL and PostgreSQL
|
|
1418
|
+
end
|
|
1419
|
+
|
|
1420
|
+
if limited_ids.empty?
|
|
1421
|
+
relation.none!
|
|
1422
|
+
else
|
|
1423
|
+
relation.where!(**Array(relation.primary_key).zip(limited_ids.transpose).to_h)
|
|
1424
|
+
end
|
|
1425
|
+
|
|
1426
|
+
relation.limit_value = relation.offset_value = nil
|
|
1427
|
+
relation
|
|
1428
|
+
end
|
|
1429
|
+
|
|
1143
1430
|
# Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
|
|
1144
1431
|
# Additional options (like +:null+) are forwarded to #add_column.
|
|
1145
1432
|
#
|
|
1146
1433
|
# add_timestamps(:suppliers, null: true)
|
|
1147
1434
|
#
|
|
1148
|
-
def add_timestamps(table_name, options
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
if !options.key?(:precision) && supports_datetime_with_precision?
|
|
1152
|
-
options[:precision] = 6
|
|
1153
|
-
end
|
|
1154
|
-
|
|
1155
|
-
add_column table_name, :created_at, :datetime, options
|
|
1156
|
-
add_column table_name, :updated_at, :datetime, options
|
|
1435
|
+
def add_timestamps(table_name, **options)
|
|
1436
|
+
fragments = add_timestamps_for_alter(table_name, **options)
|
|
1437
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(', ')}"
|
|
1157
1438
|
end
|
|
1158
1439
|
|
|
1159
1440
|
# Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
|
|
1160
1441
|
#
|
|
1161
1442
|
# remove_timestamps(:suppliers)
|
|
1162
1443
|
#
|
|
1163
|
-
def remove_timestamps(table_name, options
|
|
1164
|
-
|
|
1165
|
-
remove_column table_name, :created_at
|
|
1444
|
+
def remove_timestamps(table_name, **options)
|
|
1445
|
+
remove_columns table_name, :updated_at, :created_at
|
|
1166
1446
|
end
|
|
1167
1447
|
|
|
1168
|
-
def update_table_definition(table_name, base)
|
|
1448
|
+
def update_table_definition(table_name, base) # :nodoc:
|
|
1169
1449
|
Table.new(table_name, base)
|
|
1170
1450
|
end
|
|
1171
1451
|
|
|
1172
|
-
def add_index_options(table_name, column_name,
|
|
1173
|
-
|
|
1452
|
+
def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
|
|
1453
|
+
options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct)
|
|
1174
1454
|
|
|
1175
|
-
|
|
1455
|
+
column_names = index_column_names(column_name)
|
|
1176
1456
|
|
|
1177
|
-
|
|
1178
|
-
index_type ||= options[:unique] ? "UNIQUE" : ""
|
|
1179
|
-
index_name = options[:name].to_s if options.key?(:name)
|
|
1457
|
+
index_name = name&.to_s
|
|
1180
1458
|
index_name ||= index_name(table_name, column_names)
|
|
1181
1459
|
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1460
|
+
validate_index_length!(table_name, index_name, internal)
|
|
1461
|
+
|
|
1462
|
+
index = IndexDefinition.new(
|
|
1463
|
+
table_name, index_name,
|
|
1464
|
+
options[:unique],
|
|
1465
|
+
column_names,
|
|
1466
|
+
lengths: options[:length] || {},
|
|
1467
|
+
orders: options[:order] || {},
|
|
1468
|
+
opclasses: options[:opclass] || {},
|
|
1469
|
+
where: options[:where],
|
|
1470
|
+
type: options[:type],
|
|
1471
|
+
using: options[:using],
|
|
1472
|
+
include: options[:include],
|
|
1473
|
+
nulls_not_distinct: options[:nulls_not_distinct],
|
|
1474
|
+
comment: options[:comment]
|
|
1475
|
+
)
|
|
1476
|
+
|
|
1477
|
+
[index, index_algorithm(options[:algorithm]), if_not_exists]
|
|
1478
|
+
end
|
|
1193
1479
|
|
|
1194
|
-
|
|
1480
|
+
def index_algorithm(algorithm) # :nodoc:
|
|
1481
|
+
index_algorithms.fetch(algorithm) do
|
|
1482
|
+
raise ArgumentError, "Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}"
|
|
1483
|
+
end if algorithm
|
|
1484
|
+
end
|
|
1195
1485
|
|
|
1196
|
-
|
|
1197
|
-
|
|
1486
|
+
def quoted_columns_for_index(column_names, options) # :nodoc:
|
|
1487
|
+
quoted_columns = column_names.each_with_object({}) do |name, result|
|
|
1488
|
+
result[name.to_sym] = quote_column_name(name).dup
|
|
1198
1489
|
end
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
[index_name, index_type, index_columns, index_options, algorithm, using, comment]
|
|
1490
|
+
add_options_for_index_columns(quoted_columns, **options).values.join(", ")
|
|
1202
1491
|
end
|
|
1203
1492
|
|
|
1204
1493
|
def options_include_default?(options)
|
|
@@ -1229,7 +1518,79 @@ module ActiveRecord
|
|
|
1229
1518
|
SchemaDumper.create(self, options)
|
|
1230
1519
|
end
|
|
1231
1520
|
|
|
1521
|
+
def use_foreign_keys?
|
|
1522
|
+
supports_foreign_keys? && foreign_keys_enabled?
|
|
1523
|
+
end
|
|
1524
|
+
|
|
1525
|
+
# Returns an instance of SchemaCreation, which can be used to visit a schema definition
|
|
1526
|
+
# object and return DDL.
|
|
1527
|
+
def schema_creation # :nodoc:
|
|
1528
|
+
SchemaCreation.new(self)
|
|
1529
|
+
end
|
|
1530
|
+
|
|
1531
|
+
def bulk_change_table(table_name, operations) # :nodoc:
|
|
1532
|
+
sql_fragments = []
|
|
1533
|
+
non_combinable_operations = []
|
|
1534
|
+
|
|
1535
|
+
operations.each do |command, args|
|
|
1536
|
+
table, arguments = args.shift, args
|
|
1537
|
+
method = :"#{command}_for_alter"
|
|
1538
|
+
|
|
1539
|
+
if respond_to?(method, true)
|
|
1540
|
+
sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
|
|
1541
|
+
sql_fragments.concat(sqls)
|
|
1542
|
+
non_combinable_operations.concat(procs)
|
|
1543
|
+
else
|
|
1544
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
|
1545
|
+
non_combinable_operations.each(&:call)
|
|
1546
|
+
sql_fragments = []
|
|
1547
|
+
non_combinable_operations = []
|
|
1548
|
+
send(command, table, *arguments)
|
|
1549
|
+
end
|
|
1550
|
+
end
|
|
1551
|
+
|
|
1552
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
|
1553
|
+
non_combinable_operations.each(&:call)
|
|
1554
|
+
end
|
|
1555
|
+
|
|
1556
|
+
def valid_table_definition_options # :nodoc:
|
|
1557
|
+
[:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation]
|
|
1558
|
+
end
|
|
1559
|
+
|
|
1560
|
+
def valid_column_definition_options # :nodoc:
|
|
1561
|
+
ColumnDefinition::OPTION_NAMES
|
|
1562
|
+
end
|
|
1563
|
+
|
|
1564
|
+
def valid_primary_key_options # :nodoc:
|
|
1565
|
+
[:limit, :default, :precision]
|
|
1566
|
+
end
|
|
1567
|
+
|
|
1568
|
+
# Returns the maximum length of an index name in bytes.
|
|
1569
|
+
def max_index_name_size
|
|
1570
|
+
62
|
|
1571
|
+
end
|
|
1572
|
+
|
|
1232
1573
|
private
|
|
1574
|
+
def generate_index_name(table_name, column)
|
|
1575
|
+
name = "index_#{table_name}_on_#{Array(column) * '_and_'}"
|
|
1576
|
+
return name if name.bytesize <= max_index_name_size
|
|
1577
|
+
|
|
1578
|
+
# Fallback to short version, add hash to ensure uniqueness
|
|
1579
|
+
hashed_identifier = "_" + OpenSSL::Digest::SHA256.hexdigest(name).first(10)
|
|
1580
|
+
name = "idx_on_#{Array(column) * '_'}"
|
|
1581
|
+
|
|
1582
|
+
short_limit = max_index_name_size - hashed_identifier.bytesize
|
|
1583
|
+
short_name = name.mb_chars.limit(short_limit).to_s
|
|
1584
|
+
|
|
1585
|
+
"#{short_name}#{hashed_identifier}"
|
|
1586
|
+
end
|
|
1587
|
+
|
|
1588
|
+
def validate_change_column_null_argument!(value)
|
|
1589
|
+
unless value == true || value == false
|
|
1590
|
+
raise ArgumentError, "change_column_null expects a boolean value (true for NULL, false for NOT NULL). Got: #{value.inspect}"
|
|
1591
|
+
end
|
|
1592
|
+
end
|
|
1593
|
+
|
|
1233
1594
|
def column_options_keys
|
|
1234
1595
|
[:limit, :precision, :scale, :default, :null, :collation, :comment]
|
|
1235
1596
|
end
|
|
@@ -1253,32 +1614,27 @@ module ActiveRecord
|
|
|
1253
1614
|
# the PostgreSQL adapter for supporting operator classes.
|
|
1254
1615
|
def add_options_for_index_columns(quoted_columns, **options)
|
|
1255
1616
|
if supports_index_sort_order?
|
|
1256
|
-
quoted_columns = add_index_sort_order(quoted_columns, options)
|
|
1617
|
+
quoted_columns = add_index_sort_order(quoted_columns, **options)
|
|
1257
1618
|
end
|
|
1258
1619
|
|
|
1259
1620
|
quoted_columns
|
|
1260
1621
|
end
|
|
1261
1622
|
|
|
1262
|
-
def
|
|
1263
|
-
return [
|
|
1264
|
-
|
|
1265
|
-
quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
|
|
1266
|
-
add_options_for_index_columns(quoted_columns, options).values
|
|
1267
|
-
end
|
|
1268
|
-
|
|
1269
|
-
def index_name_for_remove(table_name, options = {})
|
|
1270
|
-
return options[:name] if can_remove_index_by_name?(options)
|
|
1623
|
+
def index_name_for_remove(table_name, column_name, options)
|
|
1624
|
+
return options[:name] if can_remove_index_by_name?(column_name, options)
|
|
1271
1625
|
|
|
1272
1626
|
checks = []
|
|
1273
1627
|
|
|
1274
|
-
if options.
|
|
1275
|
-
|
|
1276
|
-
column_names =
|
|
1628
|
+
if !options.key?(:name) && expression_column_name?(column_name)
|
|
1629
|
+
options[:name] = index_name(table_name, column_name)
|
|
1630
|
+
column_names = []
|
|
1277
1631
|
else
|
|
1278
|
-
column_names = index_column_names(options)
|
|
1632
|
+
column_names = index_column_names(column_name || options[:column])
|
|
1279
1633
|
end
|
|
1280
1634
|
|
|
1281
|
-
if
|
|
1635
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
|
|
1636
|
+
|
|
1637
|
+
if column_names.present? && !(options.key?(:name) && expression_column_name?(column_names))
|
|
1282
1638
|
checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
|
|
1283
1639
|
end
|
|
1284
1640
|
|
|
@@ -1288,7 +1644,7 @@ module ActiveRecord
|
|
|
1288
1644
|
|
|
1289
1645
|
if matching_indexes.count > 1
|
|
1290
1646
|
raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
|
|
1291
|
-
|
|
1647
|
+
"Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
|
|
1292
1648
|
elsif matching_indexes.none?
|
|
1293
1649
|
raise ArgumentError, "No indexes found on #{table_name} with the options provided."
|
|
1294
1650
|
else
|
|
@@ -1296,11 +1652,11 @@ module ActiveRecord
|
|
|
1296
1652
|
end
|
|
1297
1653
|
end
|
|
1298
1654
|
|
|
1299
|
-
def rename_table_indexes(table_name, new_name)
|
|
1655
|
+
def rename_table_indexes(table_name, new_name, **options)
|
|
1300
1656
|
indexes(new_name).each do |index|
|
|
1301
|
-
generated_index_name = index_name(table_name, column: index.columns)
|
|
1657
|
+
generated_index_name = index_name(table_name, column: index.columns, **options)
|
|
1302
1658
|
if generated_index_name == index.name
|
|
1303
|
-
rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
|
|
1659
|
+
rename_index new_name, generated_index_name, index_name(new_name, column: index.columns, **options)
|
|
1304
1660
|
end
|
|
1305
1661
|
end
|
|
1306
1662
|
end
|
|
@@ -1318,18 +1674,22 @@ module ActiveRecord
|
|
|
1318
1674
|
end
|
|
1319
1675
|
end
|
|
1320
1676
|
|
|
1321
|
-
def
|
|
1322
|
-
|
|
1323
|
-
end
|
|
1324
|
-
|
|
1325
|
-
def create_table_definition(*args)
|
|
1326
|
-
TableDefinition.new(self, *args)
|
|
1677
|
+
def create_table_definition(name, **options)
|
|
1678
|
+
TableDefinition.new(self, name, **options)
|
|
1327
1679
|
end
|
|
1328
1680
|
|
|
1329
1681
|
def create_alter_table(name)
|
|
1330
1682
|
AlterTable.new create_table_definition(name)
|
|
1331
1683
|
end
|
|
1332
1684
|
|
|
1685
|
+
def validate_create_table_options!(options)
|
|
1686
|
+
unless options[:_skip_validate_options]
|
|
1687
|
+
options
|
|
1688
|
+
.except(:_uses_legacy_table_name, :_skip_validate_options)
|
|
1689
|
+
.assert_valid_keys(valid_table_definition_options, valid_primary_key_options)
|
|
1690
|
+
end
|
|
1691
|
+
end
|
|
1692
|
+
|
|
1333
1693
|
def fetch_type_metadata(sql_type)
|
|
1334
1694
|
cast_type = lookup_cast_type(sql_type)
|
|
1335
1695
|
SqlTypeMetadata.new(
|
|
@@ -1342,7 +1702,7 @@ module ActiveRecord
|
|
|
1342
1702
|
end
|
|
1343
1703
|
|
|
1344
1704
|
def index_column_names(column_names)
|
|
1345
|
-
if
|
|
1705
|
+
if expression_column_name?(column_names)
|
|
1346
1706
|
column_names
|
|
1347
1707
|
else
|
|
1348
1708
|
Array(column_names)
|
|
@@ -1350,13 +1710,18 @@ module ActiveRecord
|
|
|
1350
1710
|
end
|
|
1351
1711
|
|
|
1352
1712
|
def index_name_options(column_names)
|
|
1353
|
-
if
|
|
1713
|
+
if expression_column_name?(column_names)
|
|
1354
1714
|
column_names = column_names.scan(/\w+/).join("_")
|
|
1355
1715
|
end
|
|
1356
1716
|
|
|
1357
1717
|
{ column: column_names }
|
|
1358
1718
|
end
|
|
1359
1719
|
|
|
1720
|
+
# Try to identify whether the given column name is an expression
|
|
1721
|
+
def expression_column_name?(column_name)
|
|
1722
|
+
column_name.is_a?(String) && /\W/.match?(column_name)
|
|
1723
|
+
end
|
|
1724
|
+
|
|
1360
1725
|
def strip_table_name_prefix_and_suffix(table_name)
|
|
1361
1726
|
prefix = Base.table_name_prefix
|
|
1362
1727
|
suffix = Base.table_name_suffix
|
|
@@ -1365,16 +1730,17 @@ module ActiveRecord
|
|
|
1365
1730
|
|
|
1366
1731
|
def foreign_key_name(table_name, options)
|
|
1367
1732
|
options.fetch(:name) do
|
|
1368
|
-
|
|
1369
|
-
|
|
1733
|
+
columns = Array(options.fetch(:column)).map(&:to_s)
|
|
1734
|
+
identifier = "#{table_name}_#{columns * '_and_'}_fk"
|
|
1735
|
+
hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
|
|
1370
1736
|
|
|
1371
1737
|
"fk_rails_#{hashed_identifier}"
|
|
1372
1738
|
end
|
|
1373
1739
|
end
|
|
1374
1740
|
|
|
1375
1741
|
def foreign_key_for(from_table, **options)
|
|
1376
|
-
return unless
|
|
1377
|
-
foreign_keys(from_table).detect { |fk| fk.defined_for?(options) }
|
|
1742
|
+
return unless use_foreign_keys?
|
|
1743
|
+
foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
|
|
1378
1744
|
end
|
|
1379
1745
|
|
|
1380
1746
|
def foreign_key_for!(from_table, to_table: nil, **options)
|
|
@@ -1390,11 +1756,40 @@ module ActiveRecord
|
|
|
1390
1756
|
end
|
|
1391
1757
|
end
|
|
1392
1758
|
|
|
1759
|
+
def foreign_keys_enabled?
|
|
1760
|
+
@config.fetch(:foreign_keys, true)
|
|
1761
|
+
end
|
|
1762
|
+
|
|
1763
|
+
def check_constraint_name(table_name, **options)
|
|
1764
|
+
options.fetch(:name) do
|
|
1765
|
+
expression = options.fetch(:expression)
|
|
1766
|
+
identifier = "#{table_name}_#{expression}_chk"
|
|
1767
|
+
hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
|
|
1768
|
+
|
|
1769
|
+
"chk_rails_#{hashed_identifier}"
|
|
1770
|
+
end
|
|
1771
|
+
end
|
|
1772
|
+
|
|
1773
|
+
def check_constraint_for(table_name, **options)
|
|
1774
|
+
return unless supports_check_constraints?
|
|
1775
|
+
chk_name = check_constraint_name(table_name, **options)
|
|
1776
|
+
check_constraints(table_name).detect { |chk| chk.defined_for?(name: chk_name, **options) }
|
|
1777
|
+
end
|
|
1778
|
+
|
|
1779
|
+
def check_constraint_for!(table_name, expression: nil, **options)
|
|
1780
|
+
check_constraint_for(table_name, expression: expression, **options) ||
|
|
1781
|
+
raise(ArgumentError, "Table '#{table_name}' has no check constraint for #{expression || options}")
|
|
1782
|
+
end
|
|
1783
|
+
|
|
1393
1784
|
def validate_index_length!(table_name, new_name, internal = false)
|
|
1394
|
-
|
|
1785
|
+
if new_name.length > index_name_length
|
|
1786
|
+
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
|
|
1787
|
+
end
|
|
1788
|
+
end
|
|
1395
1789
|
|
|
1396
|
-
|
|
1397
|
-
|
|
1790
|
+
def validate_table_length!(table_name)
|
|
1791
|
+
if table_name.length > table_name_length
|
|
1792
|
+
raise ArgumentError, "Table name '#{table_name}' is too long; the limit is #{table_name_length} characters"
|
|
1398
1793
|
end
|
|
1399
1794
|
end
|
|
1400
1795
|
|
|
@@ -1407,56 +1802,61 @@ module ActiveRecord
|
|
|
1407
1802
|
end
|
|
1408
1803
|
alias :extract_new_comment_value :extract_new_default_value
|
|
1409
1804
|
|
|
1410
|
-
def can_remove_index_by_name?(options)
|
|
1411
|
-
|
|
1805
|
+
def can_remove_index_by_name?(column_name, options)
|
|
1806
|
+
column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
|
|
1412
1807
|
end
|
|
1413
1808
|
|
|
1414
|
-
def
|
|
1415
|
-
|
|
1416
|
-
non_combinable_operations = []
|
|
1417
|
-
|
|
1418
|
-
operations.each do |command, args|
|
|
1419
|
-
table, arguments = args.shift, args
|
|
1420
|
-
method = :"#{command}_for_alter"
|
|
1421
|
-
|
|
1422
|
-
if respond_to?(method, true)
|
|
1423
|
-
sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
|
|
1424
|
-
sql_fragments << sqls
|
|
1425
|
-
non_combinable_operations.concat(procs)
|
|
1426
|
-
else
|
|
1427
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
|
1428
|
-
non_combinable_operations.each(&:call)
|
|
1429
|
-
sql_fragments = []
|
|
1430
|
-
non_combinable_operations = []
|
|
1431
|
-
send(command, table, *arguments)
|
|
1432
|
-
end
|
|
1433
|
-
end
|
|
1434
|
-
|
|
1435
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
|
1436
|
-
non_combinable_operations.each(&:call)
|
|
1809
|
+
def reference_name_for_table(table_name)
|
|
1810
|
+
table_name.to_s.singularize
|
|
1437
1811
|
end
|
|
1438
1812
|
|
|
1439
|
-
def add_column_for_alter(table_name, column_name, type, options
|
|
1813
|
+
def add_column_for_alter(table_name, column_name, type, **options)
|
|
1440
1814
|
td = create_table_definition(table_name)
|
|
1441
|
-
cd = td.new_column_definition(column_name, type, options)
|
|
1815
|
+
cd = td.new_column_definition(column_name, type, **options)
|
|
1442
1816
|
schema_creation.accept(AddColumnDefinition.new(cd))
|
|
1443
1817
|
end
|
|
1444
1818
|
|
|
1445
|
-
def
|
|
1819
|
+
def change_column_default_for_alter(table_name, column_name, default_or_changes)
|
|
1820
|
+
cd = build_change_column_default_definition(table_name, column_name, default_or_changes)
|
|
1821
|
+
schema_creation.accept(cd)
|
|
1822
|
+
end
|
|
1823
|
+
|
|
1824
|
+
def rename_column_sql(table_name, column_name, new_column_name)
|
|
1825
|
+
"RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
|
|
1826
|
+
end
|
|
1827
|
+
|
|
1828
|
+
def remove_column_for_alter(table_name, column_name, type = nil, **options)
|
|
1446
1829
|
"DROP COLUMN #{quote_column_name(column_name)}"
|
|
1447
1830
|
end
|
|
1448
1831
|
|
|
1449
|
-
def remove_columns_for_alter(table_name, *column_names)
|
|
1832
|
+
def remove_columns_for_alter(table_name, *column_names, **options)
|
|
1450
1833
|
column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
|
|
1451
1834
|
end
|
|
1452
1835
|
|
|
1836
|
+
def add_timestamps_for_alter(table_name, **options)
|
|
1837
|
+
options[:null] = false if options[:null].nil?
|
|
1838
|
+
|
|
1839
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
|
1840
|
+
options[:precision] = 6
|
|
1841
|
+
end
|
|
1842
|
+
|
|
1843
|
+
[
|
|
1844
|
+
add_column_for_alter(table_name, :created_at, :datetime, **options),
|
|
1845
|
+
add_column_for_alter(table_name, :updated_at, :datetime, **options)
|
|
1846
|
+
]
|
|
1847
|
+
end
|
|
1848
|
+
|
|
1849
|
+
def remove_timestamps_for_alter(table_name, **options)
|
|
1850
|
+
remove_columns_for_alter(table_name, :updated_at, :created_at)
|
|
1851
|
+
end
|
|
1852
|
+
|
|
1453
1853
|
def insert_versions_sql(versions)
|
|
1454
|
-
sm_table = quote_table_name(schema_migration.table_name)
|
|
1854
|
+
sm_table = quote_table_name(pool.schema_migration.table_name)
|
|
1455
1855
|
|
|
1456
1856
|
if versions.is_a?(Array)
|
|
1457
1857
|
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
|
|
1458
|
-
sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
|
|
1459
|
-
sql << "
|
|
1858
|
+
sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
|
|
1859
|
+
sql << ";"
|
|
1460
1860
|
sql
|
|
1461
1861
|
else
|
|
1462
1862
|
"INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
|