omg-activerecord 8.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +355 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +219 -0
- data/examples/performance.rb +185 -0
- data/examples/simple.rb +15 -0
- data/lib/active_record/aggregations.rb +287 -0
- data/lib/active_record/association_relation.rb +50 -0
- data/lib/active_record/associations/alias_tracker.rb +90 -0
- data/lib/active_record/associations/association.rb +417 -0
- data/lib/active_record/associations/association_scope.rb +175 -0
- data/lib/active_record/associations/belongs_to_association.rb +163 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
- data/lib/active_record/associations/builder/association.rb +170 -0
- data/lib/active_record/associations/builder/belongs_to.rb +160 -0
- data/lib/active_record/associations/builder/collection_association.rb +80 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -0
- data/lib/active_record/associations/builder/has_many.rb +23 -0
- data/lib/active_record/associations/builder/has_one.rb +61 -0
- data/lib/active_record/associations/builder/singular_association.rb +48 -0
- data/lib/active_record/associations/collection_association.rb +535 -0
- data/lib/active_record/associations/collection_proxy.rb +1163 -0
- 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 +40 -0
- data/lib/active_record/associations/has_many_association.rb +167 -0
- data/lib/active_record/associations/has_many_through_association.rb +232 -0
- data/lib/active_record/associations/has_one_association.rb +142 -0
- data/lib/active_record/associations/has_one_through_association.rb +45 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +106 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
- data/lib/active_record/associations/join_dependency.rb +301 -0
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +316 -0
- 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 +150 -0
- data/lib/active_record/associations/preloader.rb +135 -0
- data/lib/active_record/associations/singular_association.rb +76 -0
- data/lib/active_record/associations/through_association.rb +132 -0
- data/lib/active_record/associations.rb +1897 -0
- data/lib/active_record/asynchronous_queries_tracker.rb +64 -0
- data/lib/active_record/attribute_assignment.rb +82 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +106 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +262 -0
- data/lib/active_record/attribute_methods/primary_key.rb +158 -0
- data/lib/active_record/attribute_methods/query.rb +50 -0
- data/lib/active_record/attribute_methods/read.rb +46 -0
- data/lib/active_record/attribute_methods/serialization.rb +232 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +94 -0
- data/lib/active_record/attribute_methods/write.rb +49 -0
- data/lib/active_record/attribute_methods.rb +542 -0
- data/lib/active_record/attributes.rb +307 -0
- data/lib/active_record/autosave_association.rb +586 -0
- data/lib/active_record/base.rb +338 -0
- data/lib/active_record/callbacks.rb +452 -0
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +290 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +210 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +923 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +31 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +747 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +319 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +239 -0
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +24 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +190 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +961 -0
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +106 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1883 -0
- data/lib/active_record/connection_adapters/abstract/transaction.rb +676 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +1218 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1016 -0
- data/lib/active_record/connection_adapters/column.rb +122 -0
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +28 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +95 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +114 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +106 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +106 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +97 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +300 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +196 -0
- 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 +82 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +231 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +91 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +54 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +31 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +74 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +124 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- 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 +125 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +238 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +169 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +392 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +127 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1162 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1182 -0
- data/lib/active_record/connection_adapters/schema_cache.rb +478 -0
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +145 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +116 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +37 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +39 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +221 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +843 -0
- data/lib/active_record/connection_adapters/statement_pool.rb +67 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +69 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +212 -0
- data/lib/active_record/connection_adapters.rb +176 -0
- data/lib/active_record/connection_handling.rb +413 -0
- data/lib/active_record/core.rb +836 -0
- data/lib/active_record/counter_cache.rb +230 -0
- data/lib/active_record/database_configurations/connection_url_resolver.rb +105 -0
- data/lib/active_record/database_configurations/database_config.rb +104 -0
- data/lib/active_record/database_configurations/hash_config.rb +172 -0
- data/lib/active_record/database_configurations/url_config.rb +78 -0
- data/lib/active_record/database_configurations.rb +309 -0
- data/lib/active_record/delegated_type.rb +289 -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 +121 -0
- 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 +70 -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 +184 -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 +177 -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 +159 -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 +107 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +424 -0
- data/lib/active_record/errors.rb +614 -0
- data/lib/active_record/explain.rb +63 -0
- data/lib/active_record/explain_registry.rb +37 -0
- data/lib/active_record/explain_subscriber.rb +34 -0
- data/lib/active_record/fixture_set/file.rb +89 -0
- data/lib/active_record/fixture_set/model_metadata.rb +42 -0
- data/lib/active_record/fixture_set/render_context.rb +19 -0
- data/lib/active_record/fixture_set/table_row.rb +208 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +850 -0
- data/lib/active_record/future_result.rb +182 -0
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +366 -0
- data/lib/active_record/insert_all.rb +328 -0
- data/lib/active_record/integration.rb +209 -0
- data/lib/active_record/internal_metadata.rb +164 -0
- data/lib/active_record/legacy_yaml_adapter.rb +15 -0
- data/lib/active_record/locale/en.yml +48 -0
- data/lib/active_record/locking/optimistic.rb +228 -0
- data/lib/active_record/locking/pessimistic.rb +102 -0
- data/lib/active_record/log_subscriber.rb +149 -0
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +87 -0
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +406 -0
- data/lib/active_record/migration/compatibility.rb +490 -0
- 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 +16 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +1626 -0
- data/lib/active_record/model_schema.rb +635 -0
- data/lib/active_record/nested_attributes.rb +633 -0
- data/lib/active_record/no_touching.rb +65 -0
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +968 -0
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +56 -0
- data/lib/active_record/query_logs.rb +247 -0
- data/lib/active_record/query_logs_formatter.rb +30 -0
- data/lib/active_record/querying.rb +122 -0
- data/lib/active_record/railtie.rb +440 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -0
- data/lib/active_record/railties/controller_runtime.rb +65 -0
- data/lib/active_record/railties/databases.rake +641 -0
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +66 -0
- data/lib/active_record/reflection.rb +1287 -0
- data/lib/active_record/relation/batches/batch_enumerator.rb +115 -0
- data/lib/active_record/relation/batches.rb +491 -0
- data/lib/active_record/relation/calculations.rb +679 -0
- data/lib/active_record/relation/delegation.rb +154 -0
- data/lib/active_record/relation/finder_methods.rb +661 -0
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +192 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +76 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +60 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +24 -0
- data/lib/active_record/relation/predicate_builder.rb +181 -0
- data/lib/active_record/relation/query_attribute.rb +68 -0
- data/lib/active_record/relation/query_methods.rb +2235 -0
- data/lib/active_record/relation/record_fetch_warning.rb +52 -0
- data/lib/active_record/relation/spawn_methods.rb +78 -0
- data/lib/active_record/relation/where_clause.rb +218 -0
- data/lib/active_record/relation.rb +1495 -0
- data/lib/active_record/result.rb +249 -0
- data/lib/active_record/runtime_registry.rb +82 -0
- data/lib/active_record/sanitization.rb +254 -0
- data/lib/active_record/schema.rb +77 -0
- data/lib/active_record/schema_dumper.rb +364 -0
- data/lib/active_record/schema_migration.rb +106 -0
- data/lib/active_record/scoping/default.rb +205 -0
- data/lib/active_record/scoping/named.rb +202 -0
- data/lib/active_record/scoping.rb +136 -0
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +66 -0
- data/lib/active_record/serialization.rb +29 -0
- data/lib/active_record/signed_id.rb +137 -0
- data/lib/active_record/statement_cache.rb +164 -0
- data/lib/active_record/store.rb +299 -0
- data/lib/active_record/suppressor.rb +59 -0
- data/lib/active_record/table_metadata.rb +85 -0
- data/lib/active_record/tasks/database_tasks.rb +681 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +120 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +147 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +89 -0
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +321 -0
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +177 -0
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +70 -0
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +523 -0
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/type/adapter_specific_registry.rb +144 -0
- data/lib/active_record/type/date.rb +9 -0
- data/lib/active_record/type/date_time.rb +9 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +57 -0
- data/lib/active_record/type/internal/timezone.rb +22 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +76 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +35 -0
- data/lib/active_record/type/type_map.rb +58 -0
- data/lib/active_record/type/unsigned_integer.rb +16 -0
- data/lib/active_record/type.rb +83 -0
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +65 -0
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/numericality.rb +36 -0
- data/lib/active_record/validations/presence.rb +45 -0
- data/lib/active_record/validations/uniqueness.rb +295 -0
- data/lib/active_record/validations.rb +101 -0
- data/lib/active_record/version.rb +10 -0
- data/lib/active_record.rb +616 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +33 -0
- data/lib/arel/collectors/bind.rb +31 -0
- data/lib/arel/collectors/composite.rb +46 -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 +19 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +53 -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/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +125 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/bound_sql_literal.rb +65 -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/cte.rb +36 -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/fragments.rb +35 -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 +68 -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/leading_join.rb +8 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/nary.rb +39 -0
- data/lib/arel/nodes/node.rb +161 -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 +32 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +35 -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 +75 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +260 -0
- data/lib/arel/select_manager.rb +276 -0
- data/lib/arel/table.rb +121 -0
- data/lib/arel/tree_manager.rb +65 -0
- data/lib/arel/update_manager.rb +49 -0
- data/lib/arel/visitors/dot.rb +299 -0
- data/lib/arel/visitors/mysql.rb +111 -0
- data/lib/arel/visitors/postgresql.rb +99 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +1033 -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 +73 -0
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +76 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +29 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
- data/lib/rails/generators/active_record/migration.rb +54 -0
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +94 -0
- 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 +22 -0
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
- 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
- data/lib/rails/generators/active_record.rb +19 -0
- metadata +505 -0
@@ -0,0 +1,1883 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/string/access"
|
4
|
+
require "openssl"
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
module ConnectionAdapters # :nodoc:
|
8
|
+
module SchemaStatements
|
9
|
+
include ActiveRecord::Migration::JoinTable
|
10
|
+
|
11
|
+
# Returns a hash of mappings from the abstract data types to the native
|
12
|
+
# database types. See TableDefinition#column for details on the recognized
|
13
|
+
# abstract data types.
|
14
|
+
def native_database_types
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
|
18
|
+
def table_options(table_name)
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the table comment that's stored in database metadata.
|
23
|
+
def table_comment(table_name)
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# Truncates a table alias according to the limits of the current adapter.
|
28
|
+
def table_alias_for(table_name)
|
29
|
+
table_name[0...table_alias_length].tr(".", "_")
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the relation names usable to back Active Record models.
|
33
|
+
# For most adapters this means all #tables and #views.
|
34
|
+
def data_sources
|
35
|
+
query_values(data_source_sql, "SCHEMA")
|
36
|
+
rescue NotImplementedError
|
37
|
+
tables | views
|
38
|
+
end
|
39
|
+
|
40
|
+
# Checks to see if the data source +name+ exists on the database.
|
41
|
+
#
|
42
|
+
# data_source_exists?(:ebooks)
|
43
|
+
#
|
44
|
+
def data_source_exists?(name)
|
45
|
+
query_values(data_source_sql(name), "SCHEMA").any? if name.present?
|
46
|
+
rescue NotImplementedError
|
47
|
+
data_sources.include?(name.to_s)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns an array of table names defined in the database.
|
51
|
+
def tables
|
52
|
+
query_values(data_source_sql(type: "BASE TABLE"), "SCHEMA")
|
53
|
+
end
|
54
|
+
|
55
|
+
# Checks to see if the table +table_name+ exists on the database.
|
56
|
+
#
|
57
|
+
# table_exists?(:developers)
|
58
|
+
#
|
59
|
+
def table_exists?(table_name)
|
60
|
+
query_values(data_source_sql(table_name, type: "BASE TABLE"), "SCHEMA").any? if table_name.present?
|
61
|
+
rescue NotImplementedError
|
62
|
+
tables.include?(table_name.to_s)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns an array of view names defined in the database.
|
66
|
+
def views
|
67
|
+
query_values(data_source_sql(type: "VIEW"), "SCHEMA")
|
68
|
+
end
|
69
|
+
|
70
|
+
# Checks to see if the view +view_name+ exists on the database.
|
71
|
+
#
|
72
|
+
# view_exists?(:ebooks)
|
73
|
+
#
|
74
|
+
def view_exists?(view_name)
|
75
|
+
query_values(data_source_sql(view_name, type: "VIEW"), "SCHEMA").any? if view_name.present?
|
76
|
+
rescue NotImplementedError
|
77
|
+
views.include?(view_name.to_s)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns an array of indexes for the given table.
|
81
|
+
def indexes(table_name)
|
82
|
+
raise NotImplementedError, "#indexes is not implemented"
|
83
|
+
end
|
84
|
+
|
85
|
+
# Checks to see if an index exists on a table for a given index definition.
|
86
|
+
#
|
87
|
+
# # Check an index exists
|
88
|
+
# index_exists?(:suppliers, :company_id)
|
89
|
+
#
|
90
|
+
# # Check an index on multiple columns exists
|
91
|
+
# index_exists?(:suppliers, [:company_id, :company_type])
|
92
|
+
#
|
93
|
+
# # Check a unique index exists
|
94
|
+
# index_exists?(:suppliers, :company_id, unique: true)
|
95
|
+
#
|
96
|
+
# # Check an index with a custom name exists
|
97
|
+
# index_exists?(:suppliers, :company_id, name: "idx_company_id")
|
98
|
+
#
|
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) }
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
107
|
+
def columns(table_name)
|
108
|
+
table_name = table_name.to_s
|
109
|
+
definitions = column_definitions(table_name)
|
110
|
+
definitions.map do |field|
|
111
|
+
new_column_from_field(table_name, field, definitions)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Checks to see if a column exists in a given table.
|
116
|
+
#
|
117
|
+
# # Check a column exists
|
118
|
+
# column_exists?(:suppliers, :name)
|
119
|
+
#
|
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).
|
124
|
+
# column_exists?(:suppliers, :name, :string)
|
125
|
+
#
|
126
|
+
# # Check a column exists with a specific definition
|
127
|
+
# column_exists?(:suppliers, :name, :string, limit: 100)
|
128
|
+
# column_exists?(:suppliers, :name, :string, default: 'default')
|
129
|
+
# column_exists?(:suppliers, :name, :string, null: false)
|
130
|
+
# column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
|
131
|
+
#
|
132
|
+
def column_exists?(table_name, column_name, type = nil, **options)
|
133
|
+
column_name = column_name.to_s
|
134
|
+
checks = []
|
135
|
+
checks << lambda { |c| c.name == column_name }
|
136
|
+
checks << lambda { |c| c.type == type.to_sym rescue nil } if type
|
137
|
+
column_options_keys.each do |attr|
|
138
|
+
checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
|
139
|
+
end
|
140
|
+
|
141
|
+
columns(table_name).any? { |c| checks.all? { |check| check[c] } }
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns just a table's primary key
|
145
|
+
def primary_key(table_name)
|
146
|
+
pk = primary_keys(table_name)
|
147
|
+
pk = pk.first unless pk.size > 1
|
148
|
+
pk
|
149
|
+
end
|
150
|
+
|
151
|
+
# Creates a new table with the name +table_name+. +table_name+ may either
|
152
|
+
# be a String or a Symbol.
|
153
|
+
#
|
154
|
+
# There are two ways to work with #create_table. You can use the block
|
155
|
+
# form or the regular form, like this:
|
156
|
+
#
|
157
|
+
# === Block form
|
158
|
+
#
|
159
|
+
# # create_table() passes a TableDefinition object to the block.
|
160
|
+
# # This form will not only create the table, but also columns for the
|
161
|
+
# # table.
|
162
|
+
#
|
163
|
+
# create_table(:suppliers) do |t|
|
164
|
+
# t.column :name, :string, limit: 60
|
165
|
+
# # Other fields here
|
166
|
+
# end
|
167
|
+
#
|
168
|
+
# === Block form, with shorthand
|
169
|
+
#
|
170
|
+
# # You can also use the column types as method calls, rather than calling the column method.
|
171
|
+
# create_table(:suppliers) do |t|
|
172
|
+
# t.string :name, limit: 60
|
173
|
+
# # Other fields here
|
174
|
+
# end
|
175
|
+
#
|
176
|
+
# === Regular form
|
177
|
+
#
|
178
|
+
# # Creates a table called 'suppliers' with no columns.
|
179
|
+
# create_table(:suppliers)
|
180
|
+
# # Add a column to 'suppliers'.
|
181
|
+
# add_column(:suppliers, :name, :string, {limit: 60})
|
182
|
+
#
|
183
|
+
# The +options+ hash can include the following keys:
|
184
|
+
# [<tt>:id</tt>]
|
185
|
+
# Whether to automatically add a primary key column. Defaults to true.
|
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.
|
187
|
+
#
|
188
|
+
# A Symbol can be used to specify the type of the generated primary key column.
|
189
|
+
# [<tt>:primary_key</tt>]
|
190
|
+
# The name of the primary key, if one is to be added automatically.
|
191
|
+
# Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
|
192
|
+
#
|
193
|
+
# If an array is passed, a composite primary key will be created.
|
194
|
+
#
|
195
|
+
# Note that Active Record models will automatically detect their
|
196
|
+
# primary key. This can be avoided by using
|
197
|
+
# {self.primary_key=}[rdoc-ref:AttributeMethods::PrimaryKey::ClassMethods#primary_key=] on the model
|
198
|
+
# to define the key explicitly.
|
199
|
+
#
|
200
|
+
# [<tt>:options</tt>]
|
201
|
+
# Any extra options you want appended to the table definition.
|
202
|
+
# [<tt>:temporary</tt>]
|
203
|
+
# Make a temporary table.
|
204
|
+
# [<tt>:force</tt>]
|
205
|
+
# Set to true to drop the table before creating it.
|
206
|
+
# Set to +:cascade+ to drop dependent objects as well.
|
207
|
+
# Defaults to false.
|
208
|
+
# [<tt>:if_not_exists</tt>]
|
209
|
+
# Set to true to avoid raising an error when the table already exists.
|
210
|
+
# Defaults to false.
|
211
|
+
# [<tt>:as</tt>]
|
212
|
+
# SQL to use to generate the table. When this option is used, the block is
|
213
|
+
# ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
|
214
|
+
#
|
215
|
+
# ====== Add a backend specific option to the generated SQL (MySQL)
|
216
|
+
#
|
217
|
+
# create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
|
218
|
+
#
|
219
|
+
# generates:
|
220
|
+
#
|
221
|
+
# CREATE TABLE suppliers (
|
222
|
+
# id bigint auto_increment PRIMARY KEY
|
223
|
+
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
224
|
+
#
|
225
|
+
# ====== Rename the primary key column
|
226
|
+
#
|
227
|
+
# create_table(:objects, primary_key: 'guid') do |t|
|
228
|
+
# t.column :name, :string, limit: 80
|
229
|
+
# end
|
230
|
+
#
|
231
|
+
# generates:
|
232
|
+
#
|
233
|
+
# CREATE TABLE objects (
|
234
|
+
# guid bigint auto_increment PRIMARY KEY,
|
235
|
+
# name varchar(80)
|
236
|
+
# )
|
237
|
+
#
|
238
|
+
# ====== Change the primary key column type
|
239
|
+
#
|
240
|
+
# create_table(:tags, id: :string) do |t|
|
241
|
+
# t.column :label, :string
|
242
|
+
# end
|
243
|
+
#
|
244
|
+
# generates:
|
245
|
+
#
|
246
|
+
# CREATE TABLE tags (
|
247
|
+
# id varchar PRIMARY KEY,
|
248
|
+
# label varchar
|
249
|
+
# )
|
250
|
+
#
|
251
|
+
# ====== Create a composite primary key
|
252
|
+
#
|
253
|
+
# create_table(:orders, primary_key: [:product_id, :client_id]) do |t|
|
254
|
+
# t.belongs_to :product
|
255
|
+
# t.belongs_to :client
|
256
|
+
# end
|
257
|
+
#
|
258
|
+
# generates:
|
259
|
+
#
|
260
|
+
# CREATE TABLE orders (
|
261
|
+
# product_id bigint NOT NULL,
|
262
|
+
# client_id bigint NOT NULL
|
263
|
+
# );
|
264
|
+
#
|
265
|
+
# ALTER TABLE ONLY "orders"
|
266
|
+
# ADD CONSTRAINT orders_pkey PRIMARY KEY (product_id, client_id);
|
267
|
+
#
|
268
|
+
# ====== Do not add a primary key column
|
269
|
+
#
|
270
|
+
# create_table(:categories_suppliers, id: false) do |t|
|
271
|
+
# t.column :category_id, :bigint
|
272
|
+
# t.column :supplier_id, :bigint
|
273
|
+
# end
|
274
|
+
#
|
275
|
+
# generates:
|
276
|
+
#
|
277
|
+
# CREATE TABLE categories_suppliers (
|
278
|
+
# category_id bigint,
|
279
|
+
# supplier_id bigint
|
280
|
+
# )
|
281
|
+
#
|
282
|
+
# ====== Create a temporary table based on a query
|
283
|
+
#
|
284
|
+
# create_table(:long_query, temporary: true,
|
285
|
+
# as: "SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id")
|
286
|
+
#
|
287
|
+
# generates:
|
288
|
+
#
|
289
|
+
# CREATE TEMPORARY TABLE long_query AS
|
290
|
+
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
291
|
+
#
|
292
|
+
# See also TableDefinition#column for details on how to create columns.
|
293
|
+
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)
|
294
|
+
validate_create_table_options!(options)
|
295
|
+
validate_table_length!(table_name) unless options[:_uses_legacy_table_name]
|
296
|
+
|
297
|
+
if force && options.key?(:if_not_exists)
|
298
|
+
raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
|
299
|
+
end
|
300
|
+
|
301
|
+
td = build_create_table_definition(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
|
302
|
+
|
303
|
+
if force
|
304
|
+
drop_table(table_name, force: force, if_exists: true)
|
305
|
+
else
|
306
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
307
|
+
end
|
308
|
+
|
309
|
+
result = execute schema_creation.accept(td)
|
310
|
+
|
311
|
+
unless supports_indexes_in_create?
|
312
|
+
td.indexes.each do |column_name, index_options|
|
313
|
+
add_index(table_name, column_name, **index_options, if_not_exists: td.if_not_exists)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
if supports_comments? && !supports_comments_in_create?
|
318
|
+
if table_comment = td.comment.presence
|
319
|
+
change_table_comment(table_name, table_comment)
|
320
|
+
end
|
321
|
+
|
322
|
+
td.columns.each do |column|
|
323
|
+
change_column_comment(table_name, column.name, column.comment) if column.comment.present?
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
result
|
328
|
+
end
|
329
|
+
|
330
|
+
# Returns a TableDefinition object containing information about the table that would be created
|
331
|
+
# if the same arguments were passed to #create_table. See #create_table for information about
|
332
|
+
# passing a +table_name+, and other additional options that can be passed.
|
333
|
+
def build_create_table_definition(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
|
334
|
+
table_definition = create_table_definition(table_name, **options.extract!(*valid_table_definition_options, :_skip_validate_options))
|
335
|
+
table_definition.set_primary_key(table_name, id, primary_key, **options.extract!(*valid_primary_key_options, :_skip_validate_options))
|
336
|
+
|
337
|
+
yield table_definition if block_given?
|
338
|
+
|
339
|
+
table_definition
|
340
|
+
end
|
341
|
+
|
342
|
+
# Creates a new join table with the name created using the lexical order of the first two
|
343
|
+
# arguments. These arguments can be a String or a Symbol.
|
344
|
+
#
|
345
|
+
# # Creates a table called 'assemblies_parts' with no id.
|
346
|
+
# create_join_table(:assemblies, :parts)
|
347
|
+
#
|
348
|
+
# # Creates a table called 'paper_boxes_papers' with no id.
|
349
|
+
# create_join_table('papers', 'paper_boxes')
|
350
|
+
#
|
351
|
+
# A duplicate prefix is combined into a single prefix. This is useful for
|
352
|
+
# namespaced models like Music::Artist and Music::Record:
|
353
|
+
#
|
354
|
+
# # Creates a table called 'music_artists_records' with no id.
|
355
|
+
# create_join_table('music_artists', 'music_records')
|
356
|
+
#
|
357
|
+
# You can pass an +options+ hash which can include the following keys:
|
358
|
+
# [<tt>:table_name</tt>]
|
359
|
+
# Sets the table name, overriding the default.
|
360
|
+
# [<tt>:column_options</tt>]
|
361
|
+
# Any extra options you want appended to the columns definition.
|
362
|
+
# [<tt>:options</tt>]
|
363
|
+
# Any extra options you want appended to the table definition.
|
364
|
+
# [<tt>:temporary</tt>]
|
365
|
+
# Make a temporary table.
|
366
|
+
# [<tt>:force</tt>]
|
367
|
+
# Set to true to drop the table before creating it.
|
368
|
+
# Defaults to false.
|
369
|
+
#
|
370
|
+
# Note that #create_join_table does not create any indices by default; you can use
|
371
|
+
# its block form to do so yourself:
|
372
|
+
#
|
373
|
+
# create_join_table :products, :categories do |t|
|
374
|
+
# t.index :product_id
|
375
|
+
# t.index :category_id
|
376
|
+
# end
|
377
|
+
#
|
378
|
+
# ====== Add a backend specific option to the generated SQL (MySQL)
|
379
|
+
#
|
380
|
+
# create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
|
381
|
+
#
|
382
|
+
# generates:
|
383
|
+
#
|
384
|
+
# CREATE TABLE assemblies_parts (
|
385
|
+
# assembly_id bigint NOT NULL,
|
386
|
+
# part_id bigint NOT NULL,
|
387
|
+
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
388
|
+
#
|
389
|
+
def create_join_table(table_1, table_2, column_options: {}, **options)
|
390
|
+
join_table_name = find_join_table_name(table_1, table_2, options)
|
391
|
+
|
392
|
+
column_options.reverse_merge!(null: false, index: false)
|
393
|
+
|
394
|
+
t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
|
395
|
+
|
396
|
+
create_table(join_table_name, **options.merge!(id: false)) do |td|
|
397
|
+
td.references t1_ref, **column_options
|
398
|
+
td.references t2_ref, **column_options
|
399
|
+
yield td if block_given?
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
# Builds a TableDefinition object for a join table.
|
404
|
+
#
|
405
|
+
# This definition object contains information about the table that would be created
|
406
|
+
# if the same arguments were passed to #create_join_table. See #create_join_table for
|
407
|
+
# information about what arguments should be passed.
|
408
|
+
def build_create_join_table_definition(table_1, table_2, column_options: {}, **options) # :nodoc:
|
409
|
+
join_table_name = find_join_table_name(table_1, table_2, options)
|
410
|
+
column_options.reverse_merge!(null: false, index: false)
|
411
|
+
|
412
|
+
t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
|
413
|
+
|
414
|
+
build_create_table_definition(join_table_name, **options.merge!(id: false)) do |td|
|
415
|
+
td.references t1_ref, **column_options
|
416
|
+
td.references t2_ref, **column_options
|
417
|
+
yield td if block_given?
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
# Drops the join table specified by the given arguments.
|
422
|
+
# See #create_join_table and #drop_table for details.
|
423
|
+
#
|
424
|
+
# Although this command ignores the block if one is given, it can be helpful
|
425
|
+
# to provide one in a migration's +change+ method so it can be reverted.
|
426
|
+
# In that case, the block will be used by #create_join_table.
|
427
|
+
def drop_join_table(table_1, table_2, **options)
|
428
|
+
join_table_name = find_join_table_name(table_1, table_2, options)
|
429
|
+
drop_table(join_table_name, **options)
|
430
|
+
end
|
431
|
+
|
432
|
+
# A block for changing columns in +table+.
|
433
|
+
#
|
434
|
+
# # change_table() yields a Table instance
|
435
|
+
# change_table(:suppliers) do |t|
|
436
|
+
# t.column :name, :string, limit: 60
|
437
|
+
# # Other column alterations here
|
438
|
+
# end
|
439
|
+
#
|
440
|
+
# The +options+ hash can include the following keys:
|
441
|
+
# [<tt>:bulk</tt>]
|
442
|
+
# Set this to true to make this a bulk alter query, such as
|
443
|
+
#
|
444
|
+
# ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...
|
445
|
+
#
|
446
|
+
# Defaults to false.
|
447
|
+
#
|
448
|
+
# Only supported on the MySQL and PostgreSQL adapter, ignored elsewhere.
|
449
|
+
#
|
450
|
+
# ====== Add a column
|
451
|
+
#
|
452
|
+
# change_table(:suppliers) do |t|
|
453
|
+
# t.column :name, :string, limit: 60
|
454
|
+
# end
|
455
|
+
#
|
456
|
+
# ====== Change type of a column
|
457
|
+
#
|
458
|
+
# change_table(:suppliers) do |t|
|
459
|
+
# t.change :metadata, :json
|
460
|
+
# end
|
461
|
+
#
|
462
|
+
# ====== Add 2 integer columns
|
463
|
+
#
|
464
|
+
# change_table(:suppliers) do |t|
|
465
|
+
# t.integer :width, :height, null: false, default: 0
|
466
|
+
# end
|
467
|
+
#
|
468
|
+
# ====== Add created_at/updated_at columns
|
469
|
+
#
|
470
|
+
# change_table(:suppliers) do |t|
|
471
|
+
# t.timestamps
|
472
|
+
# end
|
473
|
+
#
|
474
|
+
# ====== Add a foreign key column
|
475
|
+
#
|
476
|
+
# change_table(:suppliers) do |t|
|
477
|
+
# t.references :company
|
478
|
+
# end
|
479
|
+
#
|
480
|
+
# Creates a <tt>company_id(bigint)</tt> column.
|
481
|
+
#
|
482
|
+
# ====== Add a polymorphic foreign key column
|
483
|
+
#
|
484
|
+
# change_table(:suppliers) do |t|
|
485
|
+
# t.belongs_to :company, polymorphic: true
|
486
|
+
# end
|
487
|
+
#
|
488
|
+
# Creates <tt>company_type(varchar)</tt> and <tt>company_id(bigint)</tt> columns.
|
489
|
+
#
|
490
|
+
# ====== Remove a column
|
491
|
+
#
|
492
|
+
# change_table(:suppliers) do |t|
|
493
|
+
# t.remove :company
|
494
|
+
# end
|
495
|
+
#
|
496
|
+
# ====== Remove several columns
|
497
|
+
#
|
498
|
+
# change_table(:suppliers) do |t|
|
499
|
+
# t.remove :company_id
|
500
|
+
# t.remove :width, :height
|
501
|
+
# end
|
502
|
+
#
|
503
|
+
# ====== Remove an index
|
504
|
+
#
|
505
|
+
# change_table(:suppliers) do |t|
|
506
|
+
# t.remove_index :company_id
|
507
|
+
# end
|
508
|
+
#
|
509
|
+
# See also Table for details on all of the various column transformations.
|
510
|
+
def change_table(table_name, base = self, **options)
|
511
|
+
if supports_bulk_alter? && options[:bulk]
|
512
|
+
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
|
513
|
+
yield update_table_definition(table_name, recorder)
|
514
|
+
bulk_change_table(table_name, recorder.commands)
|
515
|
+
else
|
516
|
+
yield update_table_definition(table_name, base)
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
# Renames a table.
|
521
|
+
#
|
522
|
+
# rename_table('octopuses', 'octopi')
|
523
|
+
#
|
524
|
+
def rename_table(table_name, new_name, **)
|
525
|
+
raise NotImplementedError, "rename_table is not implemented"
|
526
|
+
end
|
527
|
+
|
528
|
+
# Drops a table or tables from the database.
|
529
|
+
#
|
530
|
+
# [<tt>:force</tt>]
|
531
|
+
# Set to +:cascade+ to drop dependent objects as well.
|
532
|
+
# Defaults to false.
|
533
|
+
# [<tt>:if_exists</tt>]
|
534
|
+
# Set to +true+ to only drop the table if it exists.
|
535
|
+
# Defaults to false.
|
536
|
+
#
|
537
|
+
# Although this command ignores most +options+ and the block if one is given,
|
538
|
+
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
539
|
+
# In that case, +options+ and the block will be used by #create_table except if you provide more than one table which is not supported.
|
540
|
+
def drop_table(*table_names, **options)
|
541
|
+
table_names.each do |table_name|
|
542
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
543
|
+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
# Add a new +type+ column named +column_name+ to +table_name+.
|
548
|
+
#
|
549
|
+
# See {ActiveRecord::ConnectionAdapters::TableDefinition.column}[rdoc-ref:ActiveRecord::ConnectionAdapters::TableDefinition#column].
|
550
|
+
#
|
551
|
+
# The +type+ parameter is normally one of the migrations native types,
|
552
|
+
# which is one of the following:
|
553
|
+
# <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
|
554
|
+
# <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
|
555
|
+
# <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
|
556
|
+
# <tt>:binary</tt>, <tt>:blob</tt>, <tt>:boolean</tt>.
|
557
|
+
#
|
558
|
+
# You may use a type not in this list as long as it is supported by your
|
559
|
+
# database (for example, "polygon" in MySQL), but this will not be database
|
560
|
+
# agnostic and should usually be avoided.
|
561
|
+
#
|
562
|
+
# Available options are (none of these exists by default):
|
563
|
+
# * <tt>:comment</tt> -
|
564
|
+
# Specifies the comment for the column. This option is ignored by some backends.
|
565
|
+
# * <tt>:collation</tt> -
|
566
|
+
# Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column.
|
567
|
+
# If not specified, the column will have the same collation as the table.
|
568
|
+
# * <tt>:default</tt> -
|
569
|
+
# The column's default value. Use +nil+ for +NULL+.
|
570
|
+
# * <tt>:limit</tt> -
|
571
|
+
# Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
|
572
|
+
# and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, <tt>:blob</tt>, and <tt>:integer</tt> columns.
|
573
|
+
# This option is ignored by some backends.
|
574
|
+
# * <tt>:null</tt> -
|
575
|
+
# Allows or disallows +NULL+ values in the column.
|
576
|
+
# * <tt>:precision</tt> -
|
577
|
+
# Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
|
578
|
+
# <tt>:datetime</tt>, and <tt>:time</tt> columns.
|
579
|
+
# * <tt>:scale</tt> -
|
580
|
+
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
581
|
+
# * <tt>:if_not_exists</tt> -
|
582
|
+
# Specifies if the column already exists to not try to re-add it. This will avoid
|
583
|
+
# duplicate column errors.
|
584
|
+
#
|
585
|
+
# Note: The precision is the total number of significant digits,
|
586
|
+
# and the scale is the number of digits that can be stored following
|
587
|
+
# the decimal point. For example, the number 123.45 has a precision of 5
|
588
|
+
# and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
|
589
|
+
# range from -999.99 to 999.99.
|
590
|
+
#
|
591
|
+
# Please be aware of different RDBMS implementations behavior with
|
592
|
+
# <tt>:decimal</tt> columns:
|
593
|
+
# * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
|
594
|
+
# <tt>:precision</tt>, and makes no comments about the requirements of
|
595
|
+
# <tt>:precision</tt>.
|
596
|
+
# * MySQL: <tt>:precision</tt> [1..65], <tt>:scale</tt> [0..30].
|
597
|
+
# Default is (10,0).
|
598
|
+
# * PostgreSQL: <tt>:precision</tt> [1..infinity],
|
599
|
+
# <tt>:scale</tt> [0..infinity]. No default.
|
600
|
+
# * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
|
601
|
+
# but the maximum supported <tt>:precision</tt> is 16. No default.
|
602
|
+
# * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
|
603
|
+
# Default is (38,0).
|
604
|
+
# * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
|
605
|
+
# Default (38,0).
|
606
|
+
#
|
607
|
+
# == Examples
|
608
|
+
#
|
609
|
+
# add_column(:users, :picture, :binary, limit: 2.megabytes)
|
610
|
+
# # ALTER TABLE "users" ADD "picture" blob(2097152)
|
611
|
+
#
|
612
|
+
# add_column(:articles, :status, :string, limit: 20, default: 'draft', null: false)
|
613
|
+
# # ALTER TABLE "articles" ADD "status" varchar(20) DEFAULT 'draft' NOT NULL
|
614
|
+
#
|
615
|
+
# add_column(:answers, :bill_gates_money, :decimal, precision: 15, scale: 2)
|
616
|
+
# # ALTER TABLE "answers" ADD "bill_gates_money" decimal(15,2)
|
617
|
+
#
|
618
|
+
# add_column(:measurements, :sensor_reading, :decimal, precision: 30, scale: 20)
|
619
|
+
# # ALTER TABLE "measurements" ADD "sensor_reading" decimal(30,20)
|
620
|
+
#
|
621
|
+
# # While :scale defaults to zero on most databases, it
|
622
|
+
# # probably wouldn't hurt to include it.
|
623
|
+
# add_column(:measurements, :huge_integer, :decimal, precision: 30)
|
624
|
+
# # ALTER TABLE "measurements" ADD "huge_integer" decimal(30)
|
625
|
+
#
|
626
|
+
# # Defines a column that stores an array of a type.
|
627
|
+
# add_column(:users, :skills, :text, array: true)
|
628
|
+
# # ALTER TABLE "users" ADD "skills" text[]
|
629
|
+
#
|
630
|
+
# # Defines a column with a database-specific type.
|
631
|
+
# add_column(:shapes, :triangle, 'polygon')
|
632
|
+
# # ALTER TABLE "shapes" ADD "triangle" polygon
|
633
|
+
#
|
634
|
+
# # Ignores the method call if the column exists
|
635
|
+
# add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
|
636
|
+
def add_column(table_name, column_name, type, **options)
|
637
|
+
add_column_def = build_add_column_definition(table_name, column_name, type, **options)
|
638
|
+
return unless add_column_def
|
639
|
+
|
640
|
+
execute schema_creation.accept(add_column_def)
|
641
|
+
end
|
642
|
+
|
643
|
+
def add_columns(table_name, *column_names, type:, **options) # :nodoc:
|
644
|
+
column_names.each do |column_name|
|
645
|
+
add_column(table_name, column_name, type, **options)
|
646
|
+
end
|
647
|
+
end
|
648
|
+
|
649
|
+
# Builds an AlterTable object for adding a column to a table.
|
650
|
+
#
|
651
|
+
# This definition object contains information about the column that would be created
|
652
|
+
# if the same arguments were passed to #add_column. See #add_column for information about
|
653
|
+
# passing a +table_name+, +column_name+, +type+ and other options that can be passed.
|
654
|
+
def build_add_column_definition(table_name, column_name, type, **options) # :nodoc:
|
655
|
+
return if options[:if_not_exists] == true && column_exists?(table_name, column_name)
|
656
|
+
|
657
|
+
if supports_datetime_with_precision?
|
658
|
+
if type == :datetime && !options.key?(:precision)
|
659
|
+
options[:precision] = 6
|
660
|
+
end
|
661
|
+
end
|
662
|
+
|
663
|
+
alter_table = create_alter_table(table_name)
|
664
|
+
alter_table.add_column(column_name, type, **options)
|
665
|
+
alter_table
|
666
|
+
end
|
667
|
+
|
668
|
+
# Removes the given columns from the table definition.
|
669
|
+
#
|
670
|
+
# remove_columns(:suppliers, :qualification, :experience)
|
671
|
+
#
|
672
|
+
# +type+ and other column options can be passed to make migration reversible.
|
673
|
+
#
|
674
|
+
# remove_columns(:suppliers, :qualification, :experience, type: :string, null: false)
|
675
|
+
def remove_columns(table_name, *column_names, type: nil, **options)
|
676
|
+
if column_names.empty?
|
677
|
+
raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
|
678
|
+
end
|
679
|
+
|
680
|
+
remove_column_fragments = remove_columns_for_alter(table_name, *column_names, type: type, **options)
|
681
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_fragments.join(', ')}"
|
682
|
+
end
|
683
|
+
|
684
|
+
# Removes the column from the table definition.
|
685
|
+
#
|
686
|
+
# remove_column(:suppliers, :qualification)
|
687
|
+
#
|
688
|
+
# The +type+ and +options+ parameters will be ignored if present. It can be helpful
|
689
|
+
# to provide these in a migration's +change+ method so it can be reverted.
|
690
|
+
# In that case, +type+ and +options+ will be used by #add_column.
|
691
|
+
# Depending on the database you're using, indexes using this column may be
|
692
|
+
# automatically removed or modified to remove this column from the index.
|
693
|
+
#
|
694
|
+
# If the options provided include an +if_exists+ key, it will be used to check if the
|
695
|
+
# column does not exist. This will silently ignore the migration rather than raising
|
696
|
+
# if the column was already used.
|
697
|
+
#
|
698
|
+
# remove_column(:suppliers, :qualification, if_exists: true)
|
699
|
+
def remove_column(table_name, column_name, type = nil, **options)
|
700
|
+
return if options[:if_exists] == true && !column_exists?(table_name, column_name)
|
701
|
+
|
702
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, **options)}"
|
703
|
+
end
|
704
|
+
|
705
|
+
# Changes the column's definition according to the new options.
|
706
|
+
# See TableDefinition#column for details of the options you can use.
|
707
|
+
#
|
708
|
+
# change_column(:suppliers, :name, :string, limit: 80)
|
709
|
+
# change_column(:accounts, :description, :text)
|
710
|
+
#
|
711
|
+
def change_column(table_name, column_name, type, **options)
|
712
|
+
raise NotImplementedError, "change_column is not implemented"
|
713
|
+
end
|
714
|
+
|
715
|
+
# Sets a new default value for a column:
|
716
|
+
#
|
717
|
+
# change_column_default(:suppliers, :qualification, 'new')
|
718
|
+
# change_column_default(:accounts, :authorized, 1)
|
719
|
+
#
|
720
|
+
# Setting the default to +nil+ effectively drops the default:
|
721
|
+
#
|
722
|
+
# change_column_default(:users, :email, nil)
|
723
|
+
#
|
724
|
+
# Passing a hash containing +:from+ and +:to+ will make this change
|
725
|
+
# reversible in migration:
|
726
|
+
#
|
727
|
+
# change_column_default(:posts, :state, from: nil, to: "draft")
|
728
|
+
#
|
729
|
+
def change_column_default(table_name, column_name, default_or_changes)
|
730
|
+
raise NotImplementedError, "change_column_default is not implemented"
|
731
|
+
end
|
732
|
+
|
733
|
+
# Builds a ChangeColumnDefaultDefinition object.
|
734
|
+
#
|
735
|
+
# This definition object contains information about the column change that would occur
|
736
|
+
# if the same arguments were passed to #change_column_default. See #change_column_default for
|
737
|
+
# information about passing a +table_name+, +column_name+, +type+ and other options that can be passed.
|
738
|
+
def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
|
739
|
+
raise NotImplementedError, "build_change_column_default_definition is not implemented"
|
740
|
+
end
|
741
|
+
|
742
|
+
# Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
|
743
|
+
# indicates whether the value can be +NULL+. For example
|
744
|
+
#
|
745
|
+
# change_column_null(:users, :nickname, false)
|
746
|
+
#
|
747
|
+
# says nicknames cannot be +NULL+ (adds the constraint), whereas
|
748
|
+
#
|
749
|
+
# change_column_null(:users, :nickname, true)
|
750
|
+
#
|
751
|
+
# allows them to be +NULL+ (drops the constraint).
|
752
|
+
#
|
753
|
+
# The method accepts an optional fourth argument to replace existing
|
754
|
+
# <tt>NULL</tt>s with some other value. Use that one when enabling the
|
755
|
+
# constraint if needed, since otherwise those rows would not be valid.
|
756
|
+
#
|
757
|
+
# Please note the fourth argument does not set a column's default.
|
758
|
+
def change_column_null(table_name, column_name, null, default = nil)
|
759
|
+
raise NotImplementedError, "change_column_null is not implemented"
|
760
|
+
end
|
761
|
+
|
762
|
+
# Renames a column.
|
763
|
+
#
|
764
|
+
# rename_column(:suppliers, :description, :name)
|
765
|
+
#
|
766
|
+
def rename_column(table_name, column_name, new_column_name)
|
767
|
+
raise NotImplementedError, "rename_column is not implemented"
|
768
|
+
end
|
769
|
+
|
770
|
+
# Adds a new index to the table. +column_name+ can be a single Symbol, or
|
771
|
+
# an Array of Symbols.
|
772
|
+
#
|
773
|
+
# The index will be named after the table and the column name(s), unless
|
774
|
+
# you pass <tt>:name</tt> as an option.
|
775
|
+
#
|
776
|
+
# ====== Creating a simple index
|
777
|
+
#
|
778
|
+
# add_index(:suppliers, :name)
|
779
|
+
#
|
780
|
+
# generates:
|
781
|
+
#
|
782
|
+
# CREATE INDEX index_suppliers_on_name ON suppliers(name)
|
783
|
+
#
|
784
|
+
# ====== Creating a index which already exists
|
785
|
+
#
|
786
|
+
# add_index(:suppliers, :name, if_not_exists: true)
|
787
|
+
#
|
788
|
+
# generates:
|
789
|
+
#
|
790
|
+
# CREATE INDEX IF NOT EXISTS index_suppliers_on_name ON suppliers(name)
|
791
|
+
#
|
792
|
+
# Note: Not supported by MySQL.
|
793
|
+
#
|
794
|
+
# ====== Creating a unique index
|
795
|
+
#
|
796
|
+
# add_index(:accounts, [:branch_id, :party_id], unique: true)
|
797
|
+
#
|
798
|
+
# generates:
|
799
|
+
#
|
800
|
+
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id)
|
801
|
+
#
|
802
|
+
# ====== Creating a named index
|
803
|
+
#
|
804
|
+
# add_index(:accounts, [:branch_id, :party_id], unique: true, name: 'by_branch_party')
|
805
|
+
#
|
806
|
+
# generates:
|
807
|
+
#
|
808
|
+
# CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
|
809
|
+
#
|
810
|
+
# ====== Creating an index with specific key length
|
811
|
+
#
|
812
|
+
# add_index(:accounts, :name, name: 'by_name', length: 10)
|
813
|
+
#
|
814
|
+
# generates:
|
815
|
+
#
|
816
|
+
# CREATE INDEX by_name ON accounts(name(10))
|
817
|
+
#
|
818
|
+
# ====== Creating an index with specific key lengths for multiple keys
|
819
|
+
#
|
820
|
+
# add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
|
821
|
+
#
|
822
|
+
# generates:
|
823
|
+
#
|
824
|
+
# CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
|
825
|
+
#
|
826
|
+
# Note: only supported by MySQL
|
827
|
+
#
|
828
|
+
# ====== Creating an index with a sort order (desc or asc, asc is the default)
|
829
|
+
#
|
830
|
+
# add_index(:accounts, [:branch_id, :party_id, :surname], name: 'by_branch_desc_party', order: {branch_id: :desc, party_id: :asc})
|
831
|
+
#
|
832
|
+
# generates:
|
833
|
+
#
|
834
|
+
# CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
|
835
|
+
#
|
836
|
+
# Note: MySQL only supports index order from 8.0.1 onwards (earlier versions accepted the syntax but ignored it).
|
837
|
+
#
|
838
|
+
# ====== Creating a partial index
|
839
|
+
#
|
840
|
+
# add_index(:accounts, [:branch_id, :party_id], unique: true, where: "active")
|
841
|
+
#
|
842
|
+
# generates:
|
843
|
+
#
|
844
|
+
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
|
845
|
+
#
|
846
|
+
# Note: Partial indexes are only supported for PostgreSQL and SQLite.
|
847
|
+
#
|
848
|
+
# ====== Creating an index that includes additional columns
|
849
|
+
#
|
850
|
+
# add_index(:accounts, :branch_id, include: :party_id)
|
851
|
+
#
|
852
|
+
# generates:
|
853
|
+
#
|
854
|
+
# CREATE INDEX index_accounts_on_branch_id ON accounts USING btree(branch_id) INCLUDE (party_id)
|
855
|
+
#
|
856
|
+
# Note: only supported by PostgreSQL.
|
857
|
+
#
|
858
|
+
# ====== Creating an index with a specific method
|
859
|
+
#
|
860
|
+
# add_index(:developers, :name, using: 'btree')
|
861
|
+
#
|
862
|
+
# generates:
|
863
|
+
#
|
864
|
+
# CREATE INDEX index_developers_on_name ON developers USING btree (name) -- PostgreSQL
|
865
|
+
# CREATE INDEX index_developers_on_name USING btree ON developers (name) -- MySQL
|
866
|
+
#
|
867
|
+
# Note: only supported by PostgreSQL and MySQL
|
868
|
+
#
|
869
|
+
# ====== Creating an index with a specific operator class
|
870
|
+
#
|
871
|
+
# add_index(:developers, :name, using: 'gist', opclass: :gist_trgm_ops)
|
872
|
+
# # CREATE INDEX developers_on_name ON developers USING gist (name gist_trgm_ops) -- PostgreSQL
|
873
|
+
#
|
874
|
+
# add_index(:developers, [:name, :city], using: 'gist', opclass: { city: :gist_trgm_ops })
|
875
|
+
# # CREATE INDEX developers_on_name_and_city ON developers USING gist (name, city gist_trgm_ops) -- PostgreSQL
|
876
|
+
#
|
877
|
+
# add_index(:developers, [:name, :city], using: 'gist', opclass: :gist_trgm_ops)
|
878
|
+
# # CREATE INDEX developers_on_name_and_city ON developers USING gist (name gist_trgm_ops, city gist_trgm_ops) -- PostgreSQL
|
879
|
+
#
|
880
|
+
# Note: only supported by PostgreSQL
|
881
|
+
#
|
882
|
+
# ====== Creating an index with a specific type
|
883
|
+
#
|
884
|
+
# add_index(:developers, :name, type: :fulltext)
|
885
|
+
#
|
886
|
+
# generates:
|
887
|
+
#
|
888
|
+
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
|
889
|
+
#
|
890
|
+
# Note: only supported by MySQL.
|
891
|
+
#
|
892
|
+
# ====== Creating an index with a specific algorithm
|
893
|
+
#
|
894
|
+
# add_index(:developers, :name, algorithm: :concurrently)
|
895
|
+
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name) -- PostgreSQL
|
896
|
+
#
|
897
|
+
# add_index(:developers, :name, algorithm: :inplace)
|
898
|
+
# # CREATE INDEX `index_developers_on_name` ON `developers` (`name`) ALGORITHM = INPLACE -- MySQL
|
899
|
+
#
|
900
|
+
# Note: only supported by PostgreSQL and MySQL.
|
901
|
+
#
|
902
|
+
# Concurrently adding an index is not supported in a transaction.
|
903
|
+
#
|
904
|
+
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
905
|
+
def add_index(table_name, column_name, **options)
|
906
|
+
create_index = build_create_index_definition(table_name, column_name, **options)
|
907
|
+
execute schema_creation.accept(create_index)
|
908
|
+
end
|
909
|
+
|
910
|
+
# Builds a CreateIndexDefinition object.
|
911
|
+
#
|
912
|
+
# This definition object contains information about the index that would be created
|
913
|
+
# if the same arguments were passed to #add_index. See #add_index for information about
|
914
|
+
# passing a +table_name+, +column_name+, and other additional options that can be passed.
|
915
|
+
def build_create_index_definition(table_name, column_name, **options) # :nodoc:
|
916
|
+
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
917
|
+
CreateIndexDefinition.new(index, algorithm, if_not_exists)
|
918
|
+
end
|
919
|
+
|
920
|
+
# Removes the given index from the table.
|
921
|
+
#
|
922
|
+
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
923
|
+
#
|
924
|
+
# remove_index :accounts, :branch_id
|
925
|
+
#
|
926
|
+
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
927
|
+
#
|
928
|
+
# remove_index :accounts, column: :branch_id
|
929
|
+
#
|
930
|
+
# Removes the index on +branch_id+ and +party_id+ in the +accounts+ table if exactly one such index exists.
|
931
|
+
#
|
932
|
+
# remove_index :accounts, column: [:branch_id, :party_id]
|
933
|
+
#
|
934
|
+
# Removes the index named +by_branch_party+ in the +accounts+ table.
|
935
|
+
#
|
936
|
+
# remove_index :accounts, name: :by_branch_party
|
937
|
+
#
|
938
|
+
# Removes the index on +branch_id+ named +by_branch_party+ in the +accounts+ table.
|
939
|
+
#
|
940
|
+
# remove_index :accounts, :branch_id, name: :by_branch_party
|
941
|
+
#
|
942
|
+
# Checks if the index exists before trying to remove it. Will silently ignore indexes that
|
943
|
+
# don't exist.
|
944
|
+
#
|
945
|
+
# remove_index :accounts, if_exists: true
|
946
|
+
#
|
947
|
+
# Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
|
948
|
+
#
|
949
|
+
# remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
|
950
|
+
#
|
951
|
+
# Note: only supported by PostgreSQL.
|
952
|
+
#
|
953
|
+
# Concurrently removing an index is not supported in a transaction.
|
954
|
+
#
|
955
|
+
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
956
|
+
def remove_index(table_name, column_name = nil, **options)
|
957
|
+
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
958
|
+
|
959
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
960
|
+
|
961
|
+
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
962
|
+
end
|
963
|
+
|
964
|
+
# Renames an index.
|
965
|
+
#
|
966
|
+
# Rename the +index_people_on_last_name+ index to +index_users_on_last_name+:
|
967
|
+
#
|
968
|
+
# rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
|
969
|
+
#
|
970
|
+
def rename_index(table_name, old_name, new_name)
|
971
|
+
old_name = old_name.to_s
|
972
|
+
new_name = new_name.to_s
|
973
|
+
validate_index_length!(table_name, new_name)
|
974
|
+
|
975
|
+
# this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
|
976
|
+
old_index_def = indexes(table_name).detect { |i| i.name == old_name }
|
977
|
+
return unless old_index_def
|
978
|
+
add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
|
979
|
+
remove_index(table_name, name: old_name)
|
980
|
+
end
|
981
|
+
|
982
|
+
def index_name(table_name, options) # :nodoc:
|
983
|
+
if Hash === options
|
984
|
+
if options[:column]
|
985
|
+
if options[:_uses_legacy_index_name]
|
986
|
+
"index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
|
987
|
+
else
|
988
|
+
generate_index_name(table_name, options[:column])
|
989
|
+
end
|
990
|
+
elsif options[:name]
|
991
|
+
options[:name]
|
992
|
+
else
|
993
|
+
raise ArgumentError, "You must specify the index name"
|
994
|
+
end
|
995
|
+
else
|
996
|
+
index_name(table_name, index_name_options(options))
|
997
|
+
end
|
998
|
+
end
|
999
|
+
|
1000
|
+
# Verifies the existence of an index with a given name.
|
1001
|
+
def index_name_exists?(table_name, index_name)
|
1002
|
+
index_name = index_name.to_s
|
1003
|
+
indexes(table_name).detect { |i| i.name == index_name }
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
# Adds a reference. The reference column is a bigint by default,
|
1007
|
+
# the <tt>:type</tt> option can be used to specify a different type.
|
1008
|
+
# Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
|
1009
|
+
#
|
1010
|
+
# The +options+ hash can include the following keys:
|
1011
|
+
# [<tt>:type</tt>]
|
1012
|
+
# The reference column type. Defaults to +:bigint+.
|
1013
|
+
# [<tt>:index</tt>]
|
1014
|
+
# Add an appropriate index. Defaults to true.
|
1015
|
+
# See #add_index for usage of this option.
|
1016
|
+
# [<tt>:foreign_key</tt>]
|
1017
|
+
# Add an appropriate foreign key constraint. Defaults to false, pass true
|
1018
|
+
# to add. In case the join table can't be inferred from the association
|
1019
|
+
# pass <tt>:to_table</tt> with the appropriate table name.
|
1020
|
+
# [<tt>:polymorphic</tt>]
|
1021
|
+
# Whether an additional +_type+ column should be added. Defaults to false.
|
1022
|
+
# [<tt>:null</tt>]
|
1023
|
+
# Whether the column allows nulls. Defaults to true.
|
1024
|
+
#
|
1025
|
+
# ====== Create a user_id bigint column without an index
|
1026
|
+
#
|
1027
|
+
# add_reference(:products, :user, index: false)
|
1028
|
+
#
|
1029
|
+
# ====== Create a user_id string column
|
1030
|
+
#
|
1031
|
+
# add_reference(:products, :user, type: :string)
|
1032
|
+
#
|
1033
|
+
# ====== Create supplier_id, supplier_type columns
|
1034
|
+
#
|
1035
|
+
# add_reference(:products, :supplier, polymorphic: true)
|
1036
|
+
#
|
1037
|
+
# ====== Create a supplier_id column with a unique index
|
1038
|
+
#
|
1039
|
+
# add_reference(:products, :supplier, index: { unique: true })
|
1040
|
+
#
|
1041
|
+
# ====== Create a supplier_id column with a named index
|
1042
|
+
#
|
1043
|
+
# add_reference(:products, :supplier, index: { name: "my_supplier_index" })
|
1044
|
+
#
|
1045
|
+
# ====== Create a supplier_id column and appropriate foreign key
|
1046
|
+
#
|
1047
|
+
# add_reference(:products, :supplier, foreign_key: true)
|
1048
|
+
#
|
1049
|
+
# ====== Create a supplier_id column and a foreign key to the firms table
|
1050
|
+
#
|
1051
|
+
# add_reference(:products, :supplier, foreign_key: { to_table: :firms })
|
1052
|
+
#
|
1053
|
+
def add_reference(table_name, ref_name, **options)
|
1054
|
+
ReferenceDefinition.new(ref_name, **options).add(table_name, self)
|
1055
|
+
end
|
1056
|
+
alias :add_belongs_to :add_reference
|
1057
|
+
|
1058
|
+
# Removes the reference(s). Also removes a +type+ column if one exists.
|
1059
|
+
#
|
1060
|
+
# ====== Remove the reference
|
1061
|
+
#
|
1062
|
+
# remove_reference(:products, :user, index: false)
|
1063
|
+
#
|
1064
|
+
# ====== Remove polymorphic reference
|
1065
|
+
#
|
1066
|
+
# remove_reference(:products, :supplier, polymorphic: true)
|
1067
|
+
#
|
1068
|
+
# ====== Remove the reference with a foreign key
|
1069
|
+
#
|
1070
|
+
# remove_reference(:products, :user, foreign_key: true)
|
1071
|
+
#
|
1072
|
+
def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
|
1073
|
+
conditional_options = options.slice(:if_exists, :if_not_exists)
|
1074
|
+
|
1075
|
+
if foreign_key
|
1076
|
+
reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
|
1077
|
+
if foreign_key.is_a?(Hash)
|
1078
|
+
foreign_key_options = foreign_key.merge(conditional_options)
|
1079
|
+
else
|
1080
|
+
foreign_key_options = { to_table: reference_name, **conditional_options }
|
1081
|
+
end
|
1082
|
+
foreign_key_options[:column] ||= "#{ref_name}_id"
|
1083
|
+
remove_foreign_key(table_name, **foreign_key_options)
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
remove_column(table_name, "#{ref_name}_id", **conditional_options)
|
1087
|
+
remove_column(table_name, "#{ref_name}_type", **conditional_options) if polymorphic
|
1088
|
+
end
|
1089
|
+
alias :remove_belongs_to :remove_reference
|
1090
|
+
|
1091
|
+
# Returns an array of foreign keys for the given table.
|
1092
|
+
# The foreign keys are represented as ForeignKeyDefinition objects.
|
1093
|
+
def foreign_keys(table_name)
|
1094
|
+
raise NotImplementedError, "foreign_keys is not implemented"
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
# Adds a new foreign key. +from_table+ is the table with the key column,
|
1098
|
+
# +to_table+ contains the referenced primary key.
|
1099
|
+
#
|
1100
|
+
# The foreign key will be named after the following pattern: <tt>fk_rails_<identifier></tt>.
|
1101
|
+
# +identifier+ is a 10 character long string which is deterministically generated from the
|
1102
|
+
# +from_table+ and +column+. A custom name can be specified with the <tt>:name</tt> option.
|
1103
|
+
#
|
1104
|
+
# ====== Creating a simple foreign key
|
1105
|
+
#
|
1106
|
+
# add_foreign_key :articles, :authors
|
1107
|
+
#
|
1108
|
+
# generates:
|
1109
|
+
#
|
1110
|
+
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
|
1111
|
+
#
|
1112
|
+
# ====== Creating a foreign key, ignoring method call if the foreign key exists
|
1113
|
+
#
|
1114
|
+
# add_foreign_key(:articles, :authors, if_not_exists: true)
|
1115
|
+
#
|
1116
|
+
# ====== Creating a foreign key on a specific column
|
1117
|
+
#
|
1118
|
+
# add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
|
1119
|
+
#
|
1120
|
+
# generates:
|
1121
|
+
#
|
1122
|
+
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")
|
1123
|
+
#
|
1124
|
+
# ====== Creating a composite foreign key
|
1125
|
+
#
|
1126
|
+
# Assuming "carts" table has "(shop_id, user_id)" as a primary key.
|
1127
|
+
#
|
1128
|
+
# add_foreign_key :orders, :carts, primary_key: [:shop_id, :user_id]
|
1129
|
+
#
|
1130
|
+
# generates:
|
1131
|
+
#
|
1132
|
+
# ALTER TABLE "orders" ADD CONSTRAINT fk_rails_6f5e4cb3a4 FOREIGN KEY ("cart_shop_id", "cart_user_id") REFERENCES "carts" ("shop_id", "user_id")
|
1133
|
+
#
|
1134
|
+
# ====== Creating a cascading foreign key
|
1135
|
+
#
|
1136
|
+
# add_foreign_key :articles, :authors, on_delete: :cascade
|
1137
|
+
#
|
1138
|
+
# generates:
|
1139
|
+
#
|
1140
|
+
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE
|
1141
|
+
#
|
1142
|
+
# The +options+ hash can include the following keys:
|
1143
|
+
# [<tt>:column</tt>]
|
1144
|
+
# The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>.
|
1145
|
+
# Pass an array to create a composite foreign key.
|
1146
|
+
# [<tt>:primary_key</tt>]
|
1147
|
+
# The primary key column name on +to_table+. Defaults to +id+.
|
1148
|
+
# Pass an array to create a composite foreign key.
|
1149
|
+
# [<tt>:name</tt>]
|
1150
|
+
# The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
|
1151
|
+
# [<tt>:on_delete</tt>]
|
1152
|
+
# Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+, and +:restrict+
|
1153
|
+
# [<tt>:on_update</tt>]
|
1154
|
+
# Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+, and +:restrict+
|
1155
|
+
# [<tt>:if_not_exists</tt>]
|
1156
|
+
# Specifies if the foreign key already exists to not try to re-add it. This will avoid
|
1157
|
+
# duplicate column errors.
|
1158
|
+
# [<tt>:validate</tt>]
|
1159
|
+
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
1160
|
+
# [<tt>:deferrable</tt>]
|
1161
|
+
# (PostgreSQL only) Specify whether or not the foreign key should be deferrable. Valid values are booleans or
|
1162
|
+
# +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
|
1163
|
+
def add_foreign_key(from_table, to_table, **options)
|
1164
|
+
return unless use_foreign_keys?
|
1165
|
+
return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
|
1166
|
+
|
1167
|
+
options = foreign_key_options(from_table, to_table, options)
|
1168
|
+
at = create_alter_table from_table
|
1169
|
+
at.add_foreign_key to_table, options
|
1170
|
+
|
1171
|
+
execute schema_creation.accept(at)
|
1172
|
+
end
|
1173
|
+
|
1174
|
+
# Removes the given foreign key from the table. Any option parameters provided
|
1175
|
+
# will be used to re-add the foreign key in case of a migration rollback.
|
1176
|
+
# It is recommended that you provide any options used when creating the foreign
|
1177
|
+
# key so that the migration can be reverted properly.
|
1178
|
+
#
|
1179
|
+
# Removes the foreign key on +accounts.branch_id+.
|
1180
|
+
#
|
1181
|
+
# remove_foreign_key :accounts, :branches
|
1182
|
+
#
|
1183
|
+
# Removes the foreign key on +accounts.owner_id+.
|
1184
|
+
#
|
1185
|
+
# remove_foreign_key :accounts, column: :owner_id
|
1186
|
+
#
|
1187
|
+
# Removes the foreign key on +accounts.owner_id+.
|
1188
|
+
#
|
1189
|
+
# remove_foreign_key :accounts, to_table: :owners
|
1190
|
+
#
|
1191
|
+
# Removes the foreign key named +special_fk_name+ on the +accounts+ table.
|
1192
|
+
#
|
1193
|
+
# remove_foreign_key :accounts, name: :special_fk_name
|
1194
|
+
#
|
1195
|
+
# Checks if the foreign key exists before trying to remove it. Will silently ignore indexes that
|
1196
|
+
# don't exist.
|
1197
|
+
#
|
1198
|
+
# remove_foreign_key :accounts, :branches, if_exists: true
|
1199
|
+
#
|
1200
|
+
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
|
1201
|
+
# with an addition of
|
1202
|
+
# [<tt>:to_table</tt>]
|
1203
|
+
# The name of the table that contains the referenced primary key.
|
1204
|
+
def remove_foreign_key(from_table, to_table = nil, **options)
|
1205
|
+
return unless use_foreign_keys?
|
1206
|
+
return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
|
1207
|
+
|
1208
|
+
fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
|
1209
|
+
|
1210
|
+
at = create_alter_table from_table
|
1211
|
+
at.drop_foreign_key fk_name_to_delete
|
1212
|
+
|
1213
|
+
execute schema_creation.accept(at)
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
# Checks to see if a foreign key exists on a table for a given foreign key definition.
|
1217
|
+
#
|
1218
|
+
# # Checks to see if a foreign key exists.
|
1219
|
+
# foreign_key_exists?(:accounts, :branches)
|
1220
|
+
#
|
1221
|
+
# # Checks to see if a foreign key on a specified column exists.
|
1222
|
+
# foreign_key_exists?(:accounts, column: :owner_id)
|
1223
|
+
#
|
1224
|
+
# # Checks to see if a foreign key with a custom name exists.
|
1225
|
+
# foreign_key_exists?(:accounts, name: "special_fk_name")
|
1226
|
+
#
|
1227
|
+
def foreign_key_exists?(from_table, to_table = nil, **options)
|
1228
|
+
foreign_key_for(from_table, to_table: to_table, **options).present?
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
def foreign_key_column_for(table_name, column_name) # :nodoc:
|
1232
|
+
name = strip_table_name_prefix_and_suffix(table_name)
|
1233
|
+
"#{name.singularize}_#{column_name}"
|
1234
|
+
end
|
1235
|
+
|
1236
|
+
def foreign_key_options(from_table, to_table, options) # :nodoc:
|
1237
|
+
options = options.dup
|
1238
|
+
|
1239
|
+
if options[:primary_key].is_a?(Array)
|
1240
|
+
options[:column] ||= options[:primary_key].map do |pk_column|
|
1241
|
+
foreign_key_column_for(to_table, pk_column)
|
1242
|
+
end
|
1243
|
+
else
|
1244
|
+
options[:column] ||= foreign_key_column_for(to_table, "id")
|
1245
|
+
end
|
1246
|
+
|
1247
|
+
options[:name] ||= foreign_key_name(from_table, options)
|
1248
|
+
|
1249
|
+
if options[:column].is_a?(Array) || options[:primary_key].is_a?(Array)
|
1250
|
+
if Array(options[:primary_key]).size != Array(options[:column]).size
|
1251
|
+
raise ArgumentError, <<~MSG.squish
|
1252
|
+
For composite primary keys, specify :column and :primary_key, where
|
1253
|
+
:column must reference all the :primary_key columns from #{to_table.inspect}
|
1254
|
+
MSG
|
1255
|
+
end
|
1256
|
+
end
|
1257
|
+
|
1258
|
+
options
|
1259
|
+
end
|
1260
|
+
|
1261
|
+
# Returns an array of check constraints for the given table.
|
1262
|
+
# The check constraints are represented as CheckConstraintDefinition objects.
|
1263
|
+
def check_constraints(table_name)
|
1264
|
+
raise NotImplementedError
|
1265
|
+
end
|
1266
|
+
|
1267
|
+
# Adds a new check constraint to the table. +expression+ is a String
|
1268
|
+
# representation of verifiable boolean condition.
|
1269
|
+
#
|
1270
|
+
# add_check_constraint :products, "price > 0", name: "price_check"
|
1271
|
+
#
|
1272
|
+
# generates:
|
1273
|
+
#
|
1274
|
+
# ALTER TABLE "products" ADD CONSTRAINT price_check CHECK (price > 0)
|
1275
|
+
#
|
1276
|
+
# The +options+ hash can include the following keys:
|
1277
|
+
# [<tt>:name</tt>]
|
1278
|
+
# The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
|
1279
|
+
# [<tt>:if_not_exists</tt>]
|
1280
|
+
# Silently ignore if the constraint already exists, rather than raise an error.
|
1281
|
+
# [<tt>:validate</tt>]
|
1282
|
+
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
1283
|
+
def add_check_constraint(table_name, expression, if_not_exists: false, **options)
|
1284
|
+
return unless supports_check_constraints?
|
1285
|
+
|
1286
|
+
options = check_constraint_options(table_name, expression, options)
|
1287
|
+
return if if_not_exists && check_constraint_exists?(table_name, **options)
|
1288
|
+
|
1289
|
+
at = create_alter_table(table_name)
|
1290
|
+
at.add_check_constraint(expression, options)
|
1291
|
+
|
1292
|
+
execute schema_creation.accept(at)
|
1293
|
+
end
|
1294
|
+
|
1295
|
+
def check_constraint_options(table_name, expression, options) # :nodoc:
|
1296
|
+
options = options.dup
|
1297
|
+
options[:name] ||= check_constraint_name(table_name, expression: expression, **options)
|
1298
|
+
options
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
# Removes the given check constraint from the table. Removing a check constraint
|
1302
|
+
# that does not exist will raise an error.
|
1303
|
+
#
|
1304
|
+
# remove_check_constraint :products, name: "price_check"
|
1305
|
+
#
|
1306
|
+
# To silently ignore a non-existent check constraint rather than raise an error,
|
1307
|
+
# use the +if_exists+ option.
|
1308
|
+
#
|
1309
|
+
# remove_check_constraint :products, name: "price_check", if_exists: true
|
1310
|
+
#
|
1311
|
+
# The +expression+ parameter will be ignored if present. It can be helpful
|
1312
|
+
# to provide this in a migration's +change+ method so it can be reverted.
|
1313
|
+
# In that case, +expression+ will be used by #add_check_constraint.
|
1314
|
+
def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
|
1315
|
+
return unless supports_check_constraints?
|
1316
|
+
|
1317
|
+
return if if_exists && !check_constraint_exists?(table_name, **options)
|
1318
|
+
|
1319
|
+
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
1320
|
+
|
1321
|
+
at = create_alter_table(table_name)
|
1322
|
+
at.drop_check_constraint(chk_name_to_delete)
|
1323
|
+
|
1324
|
+
execute schema_creation.accept(at)
|
1325
|
+
end
|
1326
|
+
|
1327
|
+
|
1328
|
+
# Checks to see if a check constraint exists on a table for a given check constraint definition.
|
1329
|
+
#
|
1330
|
+
# check_constraint_exists?(:products, name: "price_check")
|
1331
|
+
#
|
1332
|
+
def check_constraint_exists?(table_name, **options)
|
1333
|
+
if !options.key?(:name) && !options.key?(:expression)
|
1334
|
+
raise ArgumentError, "At least one of :name or :expression must be supplied"
|
1335
|
+
end
|
1336
|
+
check_constraint_for(table_name, **options).present?
|
1337
|
+
end
|
1338
|
+
|
1339
|
+
def dump_schema_information # :nodoc:
|
1340
|
+
versions = pool.schema_migration.versions
|
1341
|
+
insert_versions_sql(versions) if versions.any?
|
1342
|
+
end
|
1343
|
+
|
1344
|
+
def internal_string_options_for_primary_key # :nodoc:
|
1345
|
+
{ primary_key: true }
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
def assume_migrated_upto_version(version)
|
1349
|
+
version = version.to_i
|
1350
|
+
sm_table = quote_table_name(pool.schema_migration.table_name)
|
1351
|
+
|
1352
|
+
migration_context = pool.migration_context
|
1353
|
+
migrated = migration_context.get_all_versions
|
1354
|
+
versions = migration_context.migrations.map(&:version)
|
1355
|
+
|
1356
|
+
unless migrated.include?(version)
|
1357
|
+
execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
|
1358
|
+
end
|
1359
|
+
|
1360
|
+
inserting = (versions - migrated).select { |v| v < version }
|
1361
|
+
if inserting.any?
|
1362
|
+
if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
|
1363
|
+
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
|
1364
|
+
end
|
1365
|
+
execute insert_versions_sql(inserting)
|
1366
|
+
end
|
1367
|
+
end
|
1368
|
+
|
1369
|
+
def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) # :nodoc:
|
1370
|
+
type = type.to_sym if type
|
1371
|
+
if native = native_database_types[type]
|
1372
|
+
column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
|
1373
|
+
|
1374
|
+
if type == :decimal # ignore limit, use precision and scale
|
1375
|
+
scale ||= native[:scale]
|
1376
|
+
|
1377
|
+
if precision ||= native[:precision]
|
1378
|
+
if scale
|
1379
|
+
column_type_sql << "(#{precision},#{scale})"
|
1380
|
+
else
|
1381
|
+
column_type_sql << "(#{precision})"
|
1382
|
+
end
|
1383
|
+
elsif scale
|
1384
|
+
raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
|
1385
|
+
end
|
1386
|
+
|
1387
|
+
elsif [:datetime, :timestamp, :time, :interval].include?(type) && precision ||= native[:precision]
|
1388
|
+
if (0..6) === precision
|
1389
|
+
column_type_sql << "(#{precision})"
|
1390
|
+
else
|
1391
|
+
raise ArgumentError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6"
|
1392
|
+
end
|
1393
|
+
elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
|
1394
|
+
column_type_sql << "(#{limit})"
|
1395
|
+
end
|
1396
|
+
|
1397
|
+
column_type_sql
|
1398
|
+
else
|
1399
|
+
type.to_s
|
1400
|
+
end
|
1401
|
+
end
|
1402
|
+
|
1403
|
+
# Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
|
1404
|
+
# PostgreSQL, MySQL, and Oracle override this for custom DISTINCT syntax - they
|
1405
|
+
# require the order columns appear in the SELECT.
|
1406
|
+
#
|
1407
|
+
# columns_for_distinct("posts.id", ["posts.created_at desc"])
|
1408
|
+
#
|
1409
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
1410
|
+
columns
|
1411
|
+
end
|
1412
|
+
|
1413
|
+
def distinct_relation_for_primary_key(relation) # :nodoc:
|
1414
|
+
primary_key_columns = Array(relation.primary_key).map do |column|
|
1415
|
+
visitor.compile(relation.table[column])
|
1416
|
+
end
|
1417
|
+
|
1418
|
+
values = columns_for_distinct(
|
1419
|
+
primary_key_columns,
|
1420
|
+
relation.order_values
|
1421
|
+
)
|
1422
|
+
|
1423
|
+
limited = relation.reselect(values).distinct!
|
1424
|
+
limited_ids = select_rows(limited.arel, "SQL").map do |results|
|
1425
|
+
results.last(Array(relation.primary_key).length) # ignores order values for MySQL and PostgreSQL
|
1426
|
+
end
|
1427
|
+
|
1428
|
+
if limited_ids.empty?
|
1429
|
+
relation.none!
|
1430
|
+
else
|
1431
|
+
relation.where!(**Array(relation.primary_key).zip(limited_ids.transpose).to_h)
|
1432
|
+
end
|
1433
|
+
|
1434
|
+
relation.limit_value = relation.offset_value = nil
|
1435
|
+
relation
|
1436
|
+
end
|
1437
|
+
|
1438
|
+
# Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
|
1439
|
+
# Additional options (like +:null+) are forwarded to #add_column.
|
1440
|
+
#
|
1441
|
+
# add_timestamps(:suppliers, null: true)
|
1442
|
+
#
|
1443
|
+
def add_timestamps(table_name, **options)
|
1444
|
+
fragments = add_timestamps_for_alter(table_name, **options)
|
1445
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(', ')}"
|
1446
|
+
end
|
1447
|
+
|
1448
|
+
# Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
|
1449
|
+
#
|
1450
|
+
# remove_timestamps(:suppliers)
|
1451
|
+
#
|
1452
|
+
def remove_timestamps(table_name, **options)
|
1453
|
+
remove_columns table_name, :updated_at, :created_at
|
1454
|
+
end
|
1455
|
+
|
1456
|
+
def update_table_definition(table_name, base) # :nodoc:
|
1457
|
+
Table.new(table_name, base)
|
1458
|
+
end
|
1459
|
+
|
1460
|
+
def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
|
1461
|
+
options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct)
|
1462
|
+
|
1463
|
+
column_names = index_column_names(column_name)
|
1464
|
+
|
1465
|
+
index_name = name&.to_s
|
1466
|
+
index_name ||= index_name(table_name, column_names)
|
1467
|
+
|
1468
|
+
validate_index_length!(table_name, index_name, internal)
|
1469
|
+
|
1470
|
+
index = IndexDefinition.new(
|
1471
|
+
table_name, index_name,
|
1472
|
+
options[:unique],
|
1473
|
+
column_names,
|
1474
|
+
lengths: options[:length] || {},
|
1475
|
+
orders: options[:order] || {},
|
1476
|
+
opclasses: options[:opclass] || {},
|
1477
|
+
where: options[:where],
|
1478
|
+
type: options[:type],
|
1479
|
+
using: options[:using],
|
1480
|
+
include: options[:include],
|
1481
|
+
nulls_not_distinct: options[:nulls_not_distinct],
|
1482
|
+
comment: options[:comment]
|
1483
|
+
)
|
1484
|
+
|
1485
|
+
[index, index_algorithm(options[:algorithm]), if_not_exists]
|
1486
|
+
end
|
1487
|
+
|
1488
|
+
def index_algorithm(algorithm) # :nodoc:
|
1489
|
+
index_algorithms.fetch(algorithm) do
|
1490
|
+
raise ArgumentError, "Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}"
|
1491
|
+
end if algorithm
|
1492
|
+
end
|
1493
|
+
|
1494
|
+
def quoted_columns_for_index(column_names, options) # :nodoc:
|
1495
|
+
quoted_columns = column_names.each_with_object({}) do |name, result|
|
1496
|
+
result[name.to_sym] = quote_column_name(name).dup
|
1497
|
+
end
|
1498
|
+
add_options_for_index_columns(quoted_columns, **options).values.join(", ")
|
1499
|
+
end
|
1500
|
+
|
1501
|
+
def options_include_default?(options)
|
1502
|
+
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
|
1503
|
+
end
|
1504
|
+
|
1505
|
+
# Changes the comment for a table or removes it if +nil+.
|
1506
|
+
#
|
1507
|
+
# Passing a hash containing +:from+ and +:to+ will make this change
|
1508
|
+
# reversible in migration:
|
1509
|
+
#
|
1510
|
+
# change_table_comment(:posts, from: "old_comment", to: "new_comment")
|
1511
|
+
def change_table_comment(table_name, comment_or_changes)
|
1512
|
+
raise NotImplementedError, "#{self.class} does not support changing table comments"
|
1513
|
+
end
|
1514
|
+
|
1515
|
+
# Changes the comment for a column or removes it if +nil+.
|
1516
|
+
#
|
1517
|
+
# Passing a hash containing +:from+ and +:to+ will make this change
|
1518
|
+
# reversible in migration:
|
1519
|
+
#
|
1520
|
+
# change_column_comment(:posts, :state, from: "old_comment", to: "new_comment")
|
1521
|
+
def change_column_comment(table_name, column_name, comment_or_changes)
|
1522
|
+
raise NotImplementedError, "#{self.class} does not support changing column comments"
|
1523
|
+
end
|
1524
|
+
|
1525
|
+
def create_schema_dumper(options) # :nodoc:
|
1526
|
+
SchemaDumper.create(self, options)
|
1527
|
+
end
|
1528
|
+
|
1529
|
+
def use_foreign_keys?
|
1530
|
+
supports_foreign_keys? && foreign_keys_enabled?
|
1531
|
+
end
|
1532
|
+
|
1533
|
+
# Returns an instance of SchemaCreation, which can be used to visit a schema definition
|
1534
|
+
# object and return DDL.
|
1535
|
+
def schema_creation # :nodoc:
|
1536
|
+
SchemaCreation.new(self)
|
1537
|
+
end
|
1538
|
+
|
1539
|
+
def bulk_change_table(table_name, operations) # :nodoc:
|
1540
|
+
sql_fragments = []
|
1541
|
+
non_combinable_operations = []
|
1542
|
+
|
1543
|
+
operations.each do |command, args|
|
1544
|
+
table, arguments = args.shift, args
|
1545
|
+
method = :"#{command}_for_alter"
|
1546
|
+
|
1547
|
+
if respond_to?(method, true)
|
1548
|
+
sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
|
1549
|
+
sql_fragments.concat(sqls)
|
1550
|
+
non_combinable_operations.concat(procs)
|
1551
|
+
else
|
1552
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
1553
|
+
non_combinable_operations.each(&:call)
|
1554
|
+
sql_fragments = []
|
1555
|
+
non_combinable_operations = []
|
1556
|
+
send(command, table, *arguments)
|
1557
|
+
end
|
1558
|
+
end
|
1559
|
+
|
1560
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
1561
|
+
non_combinable_operations.each(&:call)
|
1562
|
+
end
|
1563
|
+
|
1564
|
+
def valid_table_definition_options # :nodoc:
|
1565
|
+
[:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation]
|
1566
|
+
end
|
1567
|
+
|
1568
|
+
def valid_column_definition_options # :nodoc:
|
1569
|
+
ColumnDefinition::OPTION_NAMES
|
1570
|
+
end
|
1571
|
+
|
1572
|
+
def valid_primary_key_options # :nodoc:
|
1573
|
+
[:limit, :default, :precision]
|
1574
|
+
end
|
1575
|
+
|
1576
|
+
# Returns the maximum length of an index name in bytes.
|
1577
|
+
def max_index_name_size
|
1578
|
+
62
|
1579
|
+
end
|
1580
|
+
|
1581
|
+
private
|
1582
|
+
def generate_index_name(table_name, column)
|
1583
|
+
name = "index_#{table_name}_on_#{Array(column) * '_and_'}"
|
1584
|
+
return name if name.bytesize <= max_index_name_size
|
1585
|
+
|
1586
|
+
# Fallback to short version, add hash to ensure uniqueness
|
1587
|
+
hashed_identifier = "_" + OpenSSL::Digest::SHA256.hexdigest(name).first(10)
|
1588
|
+
name = "idx_on_#{Array(column) * '_'}"
|
1589
|
+
|
1590
|
+
short_limit = max_index_name_size - hashed_identifier.bytesize
|
1591
|
+
short_name = name.mb_chars.limit(short_limit).to_s
|
1592
|
+
|
1593
|
+
"#{short_name}#{hashed_identifier}"
|
1594
|
+
end
|
1595
|
+
|
1596
|
+
def validate_change_column_null_argument!(value)
|
1597
|
+
unless value == true || value == false
|
1598
|
+
raise ArgumentError, "change_column_null expects a boolean value (true for NULL, false for NOT NULL). Got: #{value.inspect}"
|
1599
|
+
end
|
1600
|
+
end
|
1601
|
+
|
1602
|
+
def column_options_keys
|
1603
|
+
[:limit, :precision, :scale, :default, :null, :collation, :comment]
|
1604
|
+
end
|
1605
|
+
|
1606
|
+
def add_index_sort_order(quoted_columns, **options)
|
1607
|
+
orders = options_for_index_columns(options[:order])
|
1608
|
+
quoted_columns.each do |name, column|
|
1609
|
+
column << " #{orders[name].upcase}" if orders[name].present?
|
1610
|
+
end
|
1611
|
+
end
|
1612
|
+
|
1613
|
+
def options_for_index_columns(options)
|
1614
|
+
if options.is_a?(Hash)
|
1615
|
+
options.symbolize_keys
|
1616
|
+
else
|
1617
|
+
Hash.new { |hash, column| hash[column] = options }
|
1618
|
+
end
|
1619
|
+
end
|
1620
|
+
|
1621
|
+
# Overridden by the MySQL adapter for supporting index lengths and by
|
1622
|
+
# the PostgreSQL adapter for supporting operator classes.
|
1623
|
+
def add_options_for_index_columns(quoted_columns, **options)
|
1624
|
+
if supports_index_sort_order?
|
1625
|
+
quoted_columns = add_index_sort_order(quoted_columns, **options)
|
1626
|
+
end
|
1627
|
+
|
1628
|
+
quoted_columns
|
1629
|
+
end
|
1630
|
+
|
1631
|
+
def index_name_for_remove(table_name, column_name, options)
|
1632
|
+
return options[:name] if can_remove_index_by_name?(column_name, options)
|
1633
|
+
|
1634
|
+
checks = []
|
1635
|
+
|
1636
|
+
if !options.key?(:name) && expression_column_name?(column_name)
|
1637
|
+
options[:name] = index_name(table_name, column_name)
|
1638
|
+
column_names = []
|
1639
|
+
else
|
1640
|
+
column_names = index_column_names(column_name || options[:column])
|
1641
|
+
end
|
1642
|
+
|
1643
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
|
1644
|
+
|
1645
|
+
if column_names.present? && !(options.key?(:name) && expression_column_name?(column_names))
|
1646
|
+
checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
|
1647
|
+
end
|
1648
|
+
|
1649
|
+
raise ArgumentError, "No name or columns specified" if checks.none?
|
1650
|
+
|
1651
|
+
matching_indexes = indexes(table_name).select { |i| checks.all? { |check| check[i] } }
|
1652
|
+
|
1653
|
+
if matching_indexes.count > 1
|
1654
|
+
raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
|
1655
|
+
"Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
|
1656
|
+
elsif matching_indexes.none?
|
1657
|
+
raise ArgumentError, "No indexes found on #{table_name} with the options provided."
|
1658
|
+
else
|
1659
|
+
matching_indexes.first.name
|
1660
|
+
end
|
1661
|
+
end
|
1662
|
+
|
1663
|
+
def rename_table_indexes(table_name, new_name, **options)
|
1664
|
+
indexes(new_name).each do |index|
|
1665
|
+
generated_index_name = index_name(table_name, column: index.columns, **options)
|
1666
|
+
if generated_index_name == index.name
|
1667
|
+
rename_index new_name, generated_index_name, index_name(new_name, column: index.columns, **options)
|
1668
|
+
end
|
1669
|
+
end
|
1670
|
+
end
|
1671
|
+
|
1672
|
+
def rename_column_indexes(table_name, column_name, new_column_name)
|
1673
|
+
column_name, new_column_name = column_name.to_s, new_column_name.to_s
|
1674
|
+
indexes(table_name).each do |index|
|
1675
|
+
next unless index.columns.include?(new_column_name)
|
1676
|
+
old_columns = index.columns.dup
|
1677
|
+
old_columns[old_columns.index(new_column_name)] = column_name
|
1678
|
+
generated_index_name = index_name(table_name, column: old_columns)
|
1679
|
+
if generated_index_name == index.name
|
1680
|
+
rename_index table_name, generated_index_name, index_name(table_name, column: index.columns)
|
1681
|
+
end
|
1682
|
+
end
|
1683
|
+
end
|
1684
|
+
|
1685
|
+
def create_table_definition(name, **options)
|
1686
|
+
TableDefinition.new(self, name, **options)
|
1687
|
+
end
|
1688
|
+
|
1689
|
+
def create_alter_table(name)
|
1690
|
+
AlterTable.new create_table_definition(name)
|
1691
|
+
end
|
1692
|
+
|
1693
|
+
def validate_create_table_options!(options)
|
1694
|
+
unless options[:_skip_validate_options]
|
1695
|
+
options
|
1696
|
+
.except(:_uses_legacy_table_name, :_skip_validate_options)
|
1697
|
+
.assert_valid_keys(valid_table_definition_options, valid_primary_key_options)
|
1698
|
+
end
|
1699
|
+
end
|
1700
|
+
|
1701
|
+
def fetch_type_metadata(sql_type)
|
1702
|
+
cast_type = lookup_cast_type(sql_type)
|
1703
|
+
SqlTypeMetadata.new(
|
1704
|
+
sql_type: sql_type,
|
1705
|
+
type: cast_type.type,
|
1706
|
+
limit: cast_type.limit,
|
1707
|
+
precision: cast_type.precision,
|
1708
|
+
scale: cast_type.scale,
|
1709
|
+
)
|
1710
|
+
end
|
1711
|
+
|
1712
|
+
def index_column_names(column_names)
|
1713
|
+
if expression_column_name?(column_names)
|
1714
|
+
column_names
|
1715
|
+
else
|
1716
|
+
Array(column_names)
|
1717
|
+
end
|
1718
|
+
end
|
1719
|
+
|
1720
|
+
def index_name_options(column_names)
|
1721
|
+
if expression_column_name?(column_names)
|
1722
|
+
column_names = column_names.scan(/\w+/).join("_")
|
1723
|
+
end
|
1724
|
+
|
1725
|
+
{ column: column_names }
|
1726
|
+
end
|
1727
|
+
|
1728
|
+
# Try to identify whether the given column name is an expression
|
1729
|
+
def expression_column_name?(column_name)
|
1730
|
+
column_name.is_a?(String) && /\W/.match?(column_name)
|
1731
|
+
end
|
1732
|
+
|
1733
|
+
def strip_table_name_prefix_and_suffix(table_name)
|
1734
|
+
prefix = Base.table_name_prefix
|
1735
|
+
suffix = Base.table_name_suffix
|
1736
|
+
table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
|
1737
|
+
end
|
1738
|
+
|
1739
|
+
def foreign_key_name(table_name, options)
|
1740
|
+
options.fetch(:name) do
|
1741
|
+
columns = Array(options.fetch(:column)).map(&:to_s)
|
1742
|
+
identifier = "#{table_name}_#{columns * '_and_'}_fk"
|
1743
|
+
hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
|
1744
|
+
|
1745
|
+
"fk_rails_#{hashed_identifier}"
|
1746
|
+
end
|
1747
|
+
end
|
1748
|
+
|
1749
|
+
def foreign_key_for(from_table, **options)
|
1750
|
+
return unless use_foreign_keys?
|
1751
|
+
foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
|
1752
|
+
end
|
1753
|
+
|
1754
|
+
def foreign_key_for!(from_table, to_table: nil, **options)
|
1755
|
+
foreign_key_for(from_table, to_table: to_table, **options) ||
|
1756
|
+
raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
1757
|
+
end
|
1758
|
+
|
1759
|
+
def extract_foreign_key_action(specifier)
|
1760
|
+
case specifier
|
1761
|
+
when "CASCADE"; :cascade
|
1762
|
+
when "SET NULL"; :nullify
|
1763
|
+
when "RESTRICT"; :restrict
|
1764
|
+
end
|
1765
|
+
end
|
1766
|
+
|
1767
|
+
def foreign_keys_enabled?
|
1768
|
+
@config.fetch(:foreign_keys, true)
|
1769
|
+
end
|
1770
|
+
|
1771
|
+
def check_constraint_name(table_name, **options)
|
1772
|
+
options.fetch(:name) do
|
1773
|
+
expression = options.fetch(:expression)
|
1774
|
+
identifier = "#{table_name}_#{expression}_chk"
|
1775
|
+
hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
|
1776
|
+
|
1777
|
+
"chk_rails_#{hashed_identifier}"
|
1778
|
+
end
|
1779
|
+
end
|
1780
|
+
|
1781
|
+
def check_constraint_for(table_name, **options)
|
1782
|
+
return unless supports_check_constraints?
|
1783
|
+
chk_name = check_constraint_name(table_name, **options)
|
1784
|
+
check_constraints(table_name).detect { |chk| chk.defined_for?(name: chk_name, **options) }
|
1785
|
+
end
|
1786
|
+
|
1787
|
+
def check_constraint_for!(table_name, expression: nil, **options)
|
1788
|
+
check_constraint_for(table_name, expression: expression, **options) ||
|
1789
|
+
raise(ArgumentError, "Table '#{table_name}' has no check constraint for #{expression || options}")
|
1790
|
+
end
|
1791
|
+
|
1792
|
+
def validate_index_length!(table_name, new_name, internal = false)
|
1793
|
+
if new_name.length > index_name_length
|
1794
|
+
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
|
1795
|
+
end
|
1796
|
+
end
|
1797
|
+
|
1798
|
+
def validate_table_length!(table_name)
|
1799
|
+
if table_name.length > table_name_length
|
1800
|
+
raise ArgumentError, "Table name '#{table_name}' is too long; the limit is #{table_name_length} characters"
|
1801
|
+
end
|
1802
|
+
end
|
1803
|
+
|
1804
|
+
def extract_new_default_value(default_or_changes)
|
1805
|
+
if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
|
1806
|
+
default_or_changes[:to]
|
1807
|
+
else
|
1808
|
+
default_or_changes
|
1809
|
+
end
|
1810
|
+
end
|
1811
|
+
alias :extract_new_comment_value :extract_new_default_value
|
1812
|
+
|
1813
|
+
def can_remove_index_by_name?(column_name, options)
|
1814
|
+
column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
|
1815
|
+
end
|
1816
|
+
|
1817
|
+
def reference_name_for_table(table_name)
|
1818
|
+
table_name.to_s.singularize
|
1819
|
+
end
|
1820
|
+
|
1821
|
+
def add_column_for_alter(table_name, column_name, type, **options)
|
1822
|
+
td = create_table_definition(table_name)
|
1823
|
+
cd = td.new_column_definition(column_name, type, **options)
|
1824
|
+
schema_creation.accept(AddColumnDefinition.new(cd))
|
1825
|
+
end
|
1826
|
+
|
1827
|
+
def change_column_default_for_alter(table_name, column_name, default_or_changes)
|
1828
|
+
cd = build_change_column_default_definition(table_name, column_name, default_or_changes)
|
1829
|
+
schema_creation.accept(cd)
|
1830
|
+
end
|
1831
|
+
|
1832
|
+
def rename_column_sql(table_name, column_name, new_column_name)
|
1833
|
+
"RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
|
1834
|
+
end
|
1835
|
+
|
1836
|
+
def remove_column_for_alter(table_name, column_name, type = nil, **options)
|
1837
|
+
"DROP COLUMN #{quote_column_name(column_name)}"
|
1838
|
+
end
|
1839
|
+
|
1840
|
+
def remove_columns_for_alter(table_name, *column_names, **options)
|
1841
|
+
column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
|
1842
|
+
end
|
1843
|
+
|
1844
|
+
def add_timestamps_for_alter(table_name, **options)
|
1845
|
+
options[:null] = false if options[:null].nil?
|
1846
|
+
|
1847
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
1848
|
+
options[:precision] = 6
|
1849
|
+
end
|
1850
|
+
|
1851
|
+
[
|
1852
|
+
add_column_for_alter(table_name, :created_at, :datetime, **options),
|
1853
|
+
add_column_for_alter(table_name, :updated_at, :datetime, **options)
|
1854
|
+
]
|
1855
|
+
end
|
1856
|
+
|
1857
|
+
def remove_timestamps_for_alter(table_name, **options)
|
1858
|
+
remove_columns_for_alter(table_name, :updated_at, :created_at)
|
1859
|
+
end
|
1860
|
+
|
1861
|
+
def insert_versions_sql(versions)
|
1862
|
+
sm_table = quote_table_name(pool.schema_migration.table_name)
|
1863
|
+
|
1864
|
+
if versions.is_a?(Array)
|
1865
|
+
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
|
1866
|
+
sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
|
1867
|
+
sql << ";"
|
1868
|
+
sql
|
1869
|
+
else
|
1870
|
+
"INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
|
1871
|
+
end
|
1872
|
+
end
|
1873
|
+
|
1874
|
+
def data_source_sql(name = nil, type: nil)
|
1875
|
+
raise NotImplementedError
|
1876
|
+
end
|
1877
|
+
|
1878
|
+
def quoted_scope(name = nil, type: nil)
|
1879
|
+
raise NotImplementedError
|
1880
|
+
end
|
1881
|
+
end
|
1882
|
+
end
|
1883
|
+
end
|