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