activerecord 5.0.7.2 → 6.1.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 +4 -4
- data/CHANGELOG.md +829 -2015
- data/MIT-LICENSE +3 -1
- data/README.rdoc +11 -9
- data/examples/performance.rb +31 -29
- data/examples/simple.rb +5 -3
- data/lib/active_record.rb +37 -29
- data/lib/active_record/aggregations.rb +249 -247
- data/lib/active_record/association_relation.rb +30 -18
- data/lib/active_record/associations.rb +1714 -1596
- data/lib/active_record/associations/alias_tracker.rb +36 -42
- data/lib/active_record/associations/association.rb +143 -68
- data/lib/active_record/associations/association_scope.rb +98 -94
- data/lib/active_record/associations/belongs_to_association.rb +76 -46
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
- data/lib/active_record/associations/builder/association.rb +27 -28
- data/lib/active_record/associations/builder/belongs_to.rb +52 -60
- data/lib/active_record/associations/builder/collection_association.rb +12 -22
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +40 -62
- data/lib/active_record/associations/builder/has_many.rb +10 -2
- data/lib/active_record/associations/builder/has_one.rb +35 -2
- data/lib/active_record/associations/builder/singular_association.rb +5 -1
- data/lib/active_record/associations/collection_association.rb +104 -259
- data/lib/active_record/associations/collection_proxy.rb +169 -125
- data/lib/active_record/associations/foreign_association.rb +22 -0
- data/lib/active_record/associations/has_many_association.rb +46 -31
- data/lib/active_record/associations/has_many_through_association.rb +66 -46
- data/lib/active_record/associations/has_one_association.rb +71 -52
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency.rb +169 -180
- data/lib/active_record/associations/join_dependency/join_association.rb +53 -79
- 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/preloader.rb +97 -104
- data/lib/active_record/associations/preloader/association.rb +109 -97
- data/lib/active_record/associations/preloader/through_association.rb +77 -76
- data/lib/active_record/associations/singular_association.rb +12 -45
- data/lib/active_record/associations/through_association.rb +27 -15
- data/lib/active_record/attribute_assignment.rb +55 -60
- data/lib/active_record/attribute_methods.rb +111 -141
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -9
- data/lib/active_record/attribute_methods/dirty.rb +172 -112
- data/lib/active_record/attribute_methods/primary_key.rb +88 -91
- data/lib/active_record/attribute_methods/query.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +18 -50
- data/lib/active_record/attribute_methods/serialization.rb +38 -10
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -66
- data/lib/active_record/attribute_methods/write.rb +25 -32
- data/lib/active_record/attributes.rb +69 -31
- data/lib/active_record/autosave_association.rb +102 -66
- data/lib/active_record/base.rb +16 -25
- data/lib/active_record/callbacks.rb +202 -43
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +11 -12
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +661 -375
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +14 -38
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +269 -105
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +54 -35
- data/lib/active_record/connection_adapters/abstract/quoting.rb +137 -93
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +155 -113
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -162
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +591 -259
- data/lib/active_record/connection_adapters/abstract/transaction.rb +229 -91
- data/lib/active_record/connection_adapters/abstract_adapter.rb +392 -244
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +457 -582
- data/lib/active_record/connection_adapters/column.rb +55 -13
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +135 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +79 -49
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +66 -56
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +20 -12
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +74 -37
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +39 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -101
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +26 -21
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -6
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -4
- 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 +19 -18
- 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 -5
- data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
- 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 +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +98 -38
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +21 -27
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +426 -324
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +32 -23
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +418 -293
- data/lib/active_record/connection_adapters/schema_cache.rb +135 -18
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +22 -7
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -6
- 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 +282 -290
- data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
- data/lib/active_record/connection_handling.rb +287 -45
- data/lib/active_record/core.rb +385 -181
- data/lib/active_record/counter_cache.rb +60 -28
- data/lib/active_record/database_configurations.rb +272 -0
- 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/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +87 -87
- data/lib/active_record/enum.rb +122 -47
- data/lib/active_record/errors.rb +153 -22
- data/lib/active_record/explain.rb +13 -8
- data/lib/active_record/explain_registry.rb +3 -1
- data/lib/active_record/explain_subscriber.rb +9 -4
- data/lib/active_record/fixture_set/file.rb +20 -22
- 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 +246 -507
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +168 -95
- data/lib/active_record/insert_all.rb +208 -0
- data/lib/active_record/integration.rb +114 -25
- data/lib/active_record/internal_metadata.rb +30 -24
- data/lib/active_record/legacy_yaml_adapter.rb +11 -5
- data/lib/active_record/locking/optimistic.rb +81 -85
- data/lib/active_record/locking/pessimistic.rb +22 -6
- data/lib/active_record/log_subscriber.rb +68 -31
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/migration.rb +439 -342
- data/lib/active_record/migration/command_recorder.rb +152 -98
- data/lib/active_record/migration/compatibility.rb +229 -60
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/model_schema.rb +230 -122
- data/lib/active_record/nested_attributes.rb +213 -203
- data/lib/active_record/no_touching.rb +11 -2
- data/lib/active_record/null_relation.rb +12 -34
- data/lib/active_record/persistence.rb +471 -97
- data/lib/active_record/query_cache.rb +23 -12
- data/lib/active_record/querying.rb +43 -25
- data/lib/active_record/railtie.rb +155 -43
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +507 -195
- data/lib/active_record/readonly_attributes.rb +9 -4
- data/lib/active_record/reflection.rb +245 -269
- data/lib/active_record/relation.rb +475 -324
- data/lib/active_record/relation/batches.rb +125 -72
- data/lib/active_record/relation/batches/batch_enumerator.rb +28 -10
- data/lib/active_record/relation/calculations.rb +267 -171
- data/lib/active_record/relation/delegation.rb +73 -69
- data/lib/active_record/relation/finder_methods.rb +238 -248
- data/lib/active_record/relation/from_clause.rb +7 -9
- data/lib/active_record/relation/merger.rb +95 -77
- data/lib/active_record/relation/predicate_builder.rb +109 -110
- data/lib/active_record/relation/predicate_builder/array_handler.rb +22 -17
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +55 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/query_attribute.rb +33 -2
- data/lib/active_record/relation/query_methods.rb +654 -374
- data/lib/active_record/relation/record_fetch_warning.rb +8 -6
- data/lib/active_record/relation/spawn_methods.rb +15 -14
- data/lib/active_record/relation/where_clause.rb +171 -109
- data/lib/active_record/result.rb +88 -51
- data/lib/active_record/runtime_registry.rb +5 -3
- data/lib/active_record/sanitization.rb +73 -100
- data/lib/active_record/schema.rb +7 -14
- data/lib/active_record/schema_dumper.rb +101 -69
- data/lib/active_record/schema_migration.rb +16 -12
- data/lib/active_record/scoping.rb +20 -20
- data/lib/active_record/scoping/default.rb +92 -95
- data/lib/active_record/scoping/named.rb +39 -30
- data/lib/active_record/secure_token.rb +19 -9
- data/lib/active_record/serialization.rb +7 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +80 -29
- data/lib/active_record/store.rb +122 -42
- data/lib/active_record/suppressor.rb +6 -3
- data/lib/active_record/table_metadata.rb +51 -39
- data/lib/active_record/tasks/database_tasks.rb +332 -115
- data/lib/active_record/tasks/mysql_database_tasks.rb +66 -104
- data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -56
- data/lib/active_record/tasks/sqlite_database_tasks.rb +40 -19
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +246 -0
- data/lib/active_record/timestamp.rb +70 -38
- data/lib/active_record/touch_later.rb +26 -24
- data/lib/active_record/transactions.rb +121 -184
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type.rb +29 -17
- data/lib/active_record/type/adapter_specific_registry.rb +44 -48
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +20 -9
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +12 -1
- data/lib/active_record/type/type_map.rb +14 -17
- data/lib/active_record/type/unsigned_integer.rb +16 -0
- data/lib/active_record/type_caster.rb +4 -2
- data/lib/active_record/type_caster/connection.rb +17 -13
- data/lib/active_record/type_caster/map.rb +10 -6
- data/lib/active_record/validations.rb +8 -5
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +4 -3
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/presence.rb +4 -2
- data/lib/active_record/validations/uniqueness.rb +52 -45
- data/lib/active_record/version.rb +3 -1
- data/lib/arel.rb +54 -0
- 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.rb +70 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/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.rb +13 -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/window_predications.rb +9 -0
- data/lib/rails/generators/active_record.rb +7 -5
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration.rb +22 -3
- data/lib/rails/generators/active_record/migration/migration_generator.rb +38 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +3 -1
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +7 -5
- data/lib/rails/generators/active_record/model/model_generator.rb +41 -25
- 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 → model.rb.tt} +10 -1
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- metadata +141 -57
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- 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 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -20
- data/lib/active_record/attribute.rb +0 -213
- data/lib/active_record/attribute/user_provided_default.rb +0 -28
- data/lib/active_record/attribute_decorators.rb +0 -67
- data/lib/active_record/attribute_mutation_tracker.rb +0 -70
- data/lib/active_record/attribute_set.rb +0 -110
- data/lib/active_record/attribute_set/builder.rb +0 -132
- data/lib/active_record/collection_cache_key.rb +0 -50
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -263
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -22
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -17
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
- data/lib/active_record/relation/where_clause_factory.rb +0 -38
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,105 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters # :nodoc:
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
# We can then redefine how certain data types may be handled in the schema dumper on the
|
7
|
-
# Adapter level by over-writing this code inside the database specific adapters
|
8
|
-
module ColumnDumper
|
9
|
-
def column_spec(column)
|
10
|
-
spec = Hash[prepare_column_options(column).map { |k, v| [k, "#{k}: #{v}"] }]
|
11
|
-
spec[:name] = column.name.inspect
|
12
|
-
spec[:type] = schema_type(column).to_s
|
13
|
-
spec
|
14
|
-
end
|
15
|
-
|
16
|
-
def column_spec_for_primary_key(column)
|
17
|
-
return {} if default_primary_key?(column)
|
18
|
-
spec = { id: schema_type(column).inspect }
|
19
|
-
spec.merge!(prepare_column_options(column).except!(:null))
|
5
|
+
class SchemaDumper < SchemaDumper # :nodoc:
|
6
|
+
def self.create(connection, options)
|
7
|
+
new(connection, options)
|
20
8
|
end
|
21
9
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def prepare_column_options(column)
|
26
|
-
spec = {}
|
27
|
-
|
28
|
-
if limit = schema_limit(column)
|
29
|
-
spec[:limit] = limit
|
10
|
+
private
|
11
|
+
def column_spec(column)
|
12
|
+
[schema_type_with_virtual(column), prepare_column_options(column)]
|
30
13
|
end
|
31
14
|
|
32
|
-
|
33
|
-
spec
|
15
|
+
def column_spec_for_primary_key(column)
|
16
|
+
spec = {}
|
17
|
+
spec[:id] = schema_type(column).inspect unless default_primary_key?(column)
|
18
|
+
spec.merge!(prepare_column_options(column).except!(:null))
|
19
|
+
spec[:default] ||= "nil" if explicit_primary_key_default?(column)
|
20
|
+
spec
|
34
21
|
end
|
35
22
|
|
36
|
-
|
37
|
-
spec
|
23
|
+
def prepare_column_options(column)
|
24
|
+
spec = {}
|
25
|
+
spec[:limit] = schema_limit(column)
|
26
|
+
spec[:precision] = schema_precision(column)
|
27
|
+
spec[:scale] = schema_scale(column)
|
28
|
+
spec[:default] = schema_default(column)
|
29
|
+
spec[:null] = "false" unless column.null
|
30
|
+
spec[:collation] = schema_collation(column)
|
31
|
+
spec[:comment] = column.comment.inspect if column.comment.present?
|
32
|
+
spec.compact!
|
33
|
+
spec
|
38
34
|
end
|
39
35
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
spec[:null] = 'false' unless column.null
|
44
|
-
|
45
|
-
if collation = schema_collation(column)
|
46
|
-
spec[:collation] = collation
|
36
|
+
def default_primary_key?(column)
|
37
|
+
schema_type(column) == :bigint
|
47
38
|
end
|
48
39
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
# Lists the valid migration options
|
55
|
-
def migration_keys
|
56
|
-
[:name, :limit, :precision, :scale, :default, :null, :collation, :comment]
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
40
|
+
def explicit_primary_key_default?(column)
|
41
|
+
false
|
42
|
+
end
|
60
43
|
|
61
|
-
|
62
|
-
|
63
|
-
|
44
|
+
def schema_type_with_virtual(column)
|
45
|
+
if @connection.supports_virtual_columns? && column.virtual?
|
46
|
+
:virtual
|
47
|
+
else
|
48
|
+
schema_type(column)
|
49
|
+
end
|
50
|
+
end
|
64
51
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
52
|
+
def schema_type(column)
|
53
|
+
if column.bigint?
|
54
|
+
:bigint
|
55
|
+
else
|
56
|
+
column.type
|
57
|
+
end
|
70
58
|
end
|
71
|
-
end
|
72
59
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
60
|
+
def schema_limit(column)
|
61
|
+
limit = column.limit unless column.bigint?
|
62
|
+
limit.inspect if limit && limit != @connection.native_database_types[column.type][:limit]
|
63
|
+
end
|
77
64
|
|
78
|
-
|
79
|
-
|
80
|
-
|
65
|
+
def schema_precision(column)
|
66
|
+
column.precision.inspect if column.precision
|
67
|
+
end
|
81
68
|
|
82
|
-
|
83
|
-
|
84
|
-
|
69
|
+
def schema_scale(column)
|
70
|
+
column.scale.inspect if column.scale
|
71
|
+
end
|
85
72
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
73
|
+
def schema_default(column)
|
74
|
+
return unless column.has_default?
|
75
|
+
type = @connection.lookup_cast_type_from_column(column)
|
76
|
+
default = type.deserialize(column.default)
|
77
|
+
if default.nil?
|
78
|
+
schema_expression(column)
|
79
|
+
else
|
80
|
+
type.type_cast_for_schema(default)
|
81
|
+
end
|
93
82
|
end
|
94
|
-
end
|
95
83
|
|
96
|
-
|
97
|
-
|
98
|
-
|
84
|
+
def schema_expression(column)
|
85
|
+
"-> { #{column.default_function.inspect} }" if column.default_function
|
86
|
+
end
|
99
87
|
|
100
|
-
|
101
|
-
|
102
|
-
|
88
|
+
def schema_collation(column)
|
89
|
+
column.collation.inspect if column.collation
|
90
|
+
end
|
103
91
|
end
|
104
92
|
end
|
105
93
|
end
|
@@ -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:
|
@@ -25,12 +26,14 @@ module ActiveRecord
|
|
25
26
|
|
26
27
|
# Truncates a table alias according to the limits of the current adapter.
|
27
28
|
def table_alias_for(table_name)
|
28
|
-
table_name[0...table_alias_length].tr(
|
29
|
+
table_name[0...table_alias_length].tr(".", "_")
|
29
30
|
end
|
30
31
|
|
31
32
|
# Returns the relation names useable to back Active Record models.
|
32
33
|
# For most adapters this means all #tables and #views.
|
33
34
|
def data_sources
|
35
|
+
query_values(data_source_sql, "SCHEMA")
|
36
|
+
rescue NotImplementedError
|
34
37
|
tables | views
|
35
38
|
end
|
36
39
|
|
@@ -39,12 +42,14 @@ module ActiveRecord
|
|
39
42
|
# data_source_exists?(:ebooks)
|
40
43
|
#
|
41
44
|
def data_source_exists?(name)
|
45
|
+
query_values(data_source_sql(name), "SCHEMA").any? if name.present?
|
46
|
+
rescue NotImplementedError
|
42
47
|
data_sources.include?(name.to_s)
|
43
48
|
end
|
44
49
|
|
45
50
|
# Returns an array of table names defined in the database.
|
46
|
-
def tables
|
47
|
-
|
51
|
+
def tables
|
52
|
+
query_values(data_source_sql(type: "BASE TABLE"), "SCHEMA")
|
48
53
|
end
|
49
54
|
|
50
55
|
# Checks to see if the table +table_name+ exists on the database.
|
@@ -52,12 +57,14 @@ module ActiveRecord
|
|
52
57
|
# table_exists?(:developers)
|
53
58
|
#
|
54
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
|
55
62
|
tables.include?(table_name.to_s)
|
56
63
|
end
|
57
64
|
|
58
65
|
# Returns an array of view names defined in the database.
|
59
66
|
def views
|
60
|
-
|
67
|
+
query_values(data_source_sql(type: "VIEW"), "SCHEMA")
|
61
68
|
end
|
62
69
|
|
63
70
|
# Checks to see if the view +view_name+ exists on the database.
|
@@ -65,11 +72,15 @@ module ActiveRecord
|
|
65
72
|
# view_exists?(:ebooks)
|
66
73
|
#
|
67
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
|
68
77
|
views.include?(view_name.to_s)
|
69
78
|
end
|
70
79
|
|
71
80
|
# Returns an array of indexes for the given table.
|
72
|
-
|
81
|
+
def indexes(table_name)
|
82
|
+
raise NotImplementedError, "#indexes is not implemented"
|
83
|
+
end
|
73
84
|
|
74
85
|
# Checks to see if an index exists on a table for a given index definition.
|
75
86
|
#
|
@@ -85,20 +96,26 @@ module ActiveRecord
|
|
85
96
|
# # Check an index with a custom name exists
|
86
97
|
# index_exists?(:suppliers, :company_id, name: "idx_company_id")
|
87
98
|
#
|
88
|
-
def index_exists?(table_name, column_name, options
|
89
|
-
column_names = Array(column_name).map(&:to_s)
|
99
|
+
def index_exists?(table_name, column_name, **options)
|
90
100
|
checks = []
|
91
|
-
|
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
|
+
|
92
107
|
checks << lambda { |i| i.unique } if options[:unique]
|
93
108
|
checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
|
94
109
|
|
95
110
|
indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
|
96
111
|
end
|
97
112
|
|
98
|
-
# Returns an array of Column objects for the table specified by +table_name+.
|
99
|
-
# See the concrete implementation for details on the expected parameter values.
|
113
|
+
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
100
114
|
def columns(table_name)
|
101
|
-
|
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
|
102
119
|
end
|
103
120
|
|
104
121
|
# Checks to see if a column exists in a given table.
|
@@ -115,12 +132,12 @@ module ActiveRecord
|
|
115
132
|
# column_exists?(:suppliers, :name, :string, null: false)
|
116
133
|
# column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
|
117
134
|
#
|
118
|
-
def column_exists?(table_name, column_name, type = nil, options
|
135
|
+
def column_exists?(table_name, column_name, type = nil, **options)
|
119
136
|
column_name = column_name.to_s
|
120
137
|
checks = []
|
121
138
|
checks << lambda { |c| c.name == column_name }
|
122
|
-
checks << lambda { |c| c.type == type } if type
|
123
|
-
|
139
|
+
checks << lambda { |c| c.type == type.to_sym rescue nil } if type
|
140
|
+
column_options_keys.each do |attr|
|
124
141
|
checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
|
125
142
|
end
|
126
143
|
|
@@ -174,7 +191,9 @@ module ActiveRecord
|
|
174
191
|
# A Symbol can be used to specify the type of the generated primary key column.
|
175
192
|
# [<tt>:primary_key</tt>]
|
176
193
|
# The name of the primary key, if one is to be added automatically.
|
177
|
-
# 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.
|
178
197
|
#
|
179
198
|
# Note that Active Record models will automatically detect their
|
180
199
|
# primary key. This can be avoided by using
|
@@ -189,19 +208,22 @@ module ActiveRecord
|
|
189
208
|
# Set to true to drop the table before creating it.
|
190
209
|
# Set to +:cascade+ to drop dependent objects as well.
|
191
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.
|
192
214
|
# [<tt>:as</tt>]
|
193
215
|
# SQL to use to generate the table. When this option is used, the block is
|
194
216
|
# ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
|
195
217
|
#
|
196
218
|
# ====== Add a backend specific option to the generated SQL (MySQL)
|
197
219
|
#
|
198
|
-
# create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=
|
220
|
+
# create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
|
199
221
|
#
|
200
222
|
# generates:
|
201
223
|
#
|
202
224
|
# CREATE TABLE suppliers (
|
203
|
-
# id
|
204
|
-
# ) ENGINE=InnoDB DEFAULT CHARSET=
|
225
|
+
# id bigint auto_increment PRIMARY KEY
|
226
|
+
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
205
227
|
#
|
206
228
|
# ====== Rename the primary key column
|
207
229
|
#
|
@@ -212,7 +234,7 @@ module ActiveRecord
|
|
212
234
|
# generates:
|
213
235
|
#
|
214
236
|
# CREATE TABLE objects (
|
215
|
-
# guid
|
237
|
+
# guid bigint auto_increment PRIMARY KEY,
|
216
238
|
# name varchar(80)
|
217
239
|
# )
|
218
240
|
#
|
@@ -229,18 +251,35 @@ module ActiveRecord
|
|
229
251
|
# label varchar
|
230
252
|
# )
|
231
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
|
+
#
|
232
271
|
# ====== Do not add a primary key column
|
233
272
|
#
|
234
273
|
# create_table(:categories_suppliers, id: false) do |t|
|
235
|
-
# t.column :category_id, :
|
236
|
-
# t.column :supplier_id, :
|
274
|
+
# t.column :category_id, :bigint
|
275
|
+
# t.column :supplier_id, :bigint
|
237
276
|
# end
|
238
277
|
#
|
239
278
|
# generates:
|
240
279
|
#
|
241
280
|
# CREATE TABLE categories_suppliers (
|
242
|
-
# category_id
|
243
|
-
# supplier_id
|
281
|
+
# category_id bigint,
|
282
|
+
# supplier_id bigint
|
244
283
|
# )
|
245
284
|
#
|
246
285
|
# ====== Create a temporary table based on a query
|
@@ -254,37 +293,44 @@ module ActiveRecord
|
|
254
293
|
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
255
294
|
#
|
256
295
|
# See also TableDefinition#column for details on how to create columns.
|
257
|
-
def create_table(table_name,
|
258
|
-
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))
|
259
298
|
|
260
|
-
if
|
261
|
-
pk =
|
262
|
-
|
299
|
+
if id && !td.as
|
300
|
+
pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
|
301
|
+
|
302
|
+
if id.is_a?(Hash)
|
303
|
+
options.merge!(id.except(:type))
|
304
|
+
id = id.fetch(:type, :primary_key)
|
263
305
|
end
|
264
306
|
|
265
307
|
if pk.is_a?(Array)
|
266
308
|
td.primary_keys pk
|
267
309
|
else
|
268
|
-
td.primary_key pk,
|
310
|
+
td.primary_key pk, id, **options
|
269
311
|
end
|
270
312
|
end
|
271
313
|
|
272
314
|
yield td if block_given?
|
273
315
|
|
274
|
-
if
|
275
|
-
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)
|
276
320
|
end
|
277
321
|
|
278
322
|
result = execute schema_creation.accept td
|
279
323
|
|
280
324
|
unless supports_indexes_in_create?
|
281
325
|
td.indexes.each do |column_name, index_options|
|
282
|
-
add_index(table_name, column_name, index_options)
|
326
|
+
add_index(table_name, column_name, **index_options, if_not_exists: td.if_not_exists)
|
283
327
|
end
|
284
328
|
end
|
285
329
|
|
286
330
|
if supports_comments? && !supports_comments_in_create?
|
287
|
-
|
331
|
+
if table_comment = td.comment.presence
|
332
|
+
change_table_comment(table_name, table_comment)
|
333
|
+
end
|
288
334
|
|
289
335
|
td.columns.each do |column|
|
290
336
|
change_column_comment(table_name, column.name, column.comment) if column.comment.present?
|
@@ -300,9 +346,9 @@ module ActiveRecord
|
|
300
346
|
# # Creates a table called 'assemblies_parts' with no id.
|
301
347
|
# create_join_table(:assemblies, :parts)
|
302
348
|
#
|
303
|
-
# You can pass
|
349
|
+
# You can pass an +options+ hash which can include the following keys:
|
304
350
|
# [<tt>:table_name</tt>]
|
305
|
-
# Sets the table name overriding the default
|
351
|
+
# Sets the table name, overriding the default.
|
306
352
|
# [<tt>:column_options</tt>]
|
307
353
|
# Any extra options you want appended to the columns definition.
|
308
354
|
# [<tt>:options</tt>]
|
@@ -328,22 +374,20 @@ module ActiveRecord
|
|
328
374
|
# generates:
|
329
375
|
#
|
330
376
|
# CREATE TABLE assemblies_parts (
|
331
|
-
# assembly_id
|
332
|
-
# part_id
|
377
|
+
# assembly_id bigint NOT NULL,
|
378
|
+
# part_id bigint NOT NULL,
|
333
379
|
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
334
380
|
#
|
335
|
-
def create_join_table(table_1, table_2,
|
381
|
+
def create_join_table(table_1, table_2, column_options: {}, **options)
|
336
382
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
337
383
|
|
338
|
-
column_options
|
339
|
-
column_options.reverse_merge!(null: false)
|
340
|
-
type = column_options.delete(:type) || :integer
|
384
|
+
column_options.reverse_merge!(null: false, index: false)
|
341
385
|
|
342
|
-
|
386
|
+
t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
|
343
387
|
|
344
|
-
create_table(join_table_name, options.merge!(id: false)) do |td|
|
345
|
-
td.
|
346
|
-
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
|
347
391
|
yield td if block_given?
|
348
392
|
end
|
349
393
|
end
|
@@ -354,7 +398,7 @@ module ActiveRecord
|
|
354
398
|
# Although this command ignores the block if one is given, it can be helpful
|
355
399
|
# to provide one in a migration's +change+ method so it can be reverted.
|
356
400
|
# In that case, the block will be used by #create_join_table.
|
357
|
-
def drop_join_table(table_1, table_2, options
|
401
|
+
def drop_join_table(table_1, table_2, **options)
|
358
402
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
359
403
|
drop_table(join_table_name)
|
360
404
|
end
|
@@ -375,12 +419,20 @@ module ActiveRecord
|
|
375
419
|
#
|
376
420
|
# Defaults to false.
|
377
421
|
#
|
422
|
+
# Only supported on the MySQL and PostgreSQL adapter, ignored elsewhere.
|
423
|
+
#
|
378
424
|
# ====== Add a column
|
379
425
|
#
|
380
426
|
# change_table(:suppliers) do |t|
|
381
427
|
# t.column :name, :string, limit: 60
|
382
428
|
# end
|
383
429
|
#
|
430
|
+
# ====== Change type of a column
|
431
|
+
#
|
432
|
+
# change_table(:suppliers) do |t|
|
433
|
+
# t.change :metadata, :json
|
434
|
+
# end
|
435
|
+
#
|
384
436
|
# ====== Add 2 integer columns
|
385
437
|
#
|
386
438
|
# change_table(:suppliers) do |t|
|
@@ -399,7 +451,7 @@ module ActiveRecord
|
|
399
451
|
# t.references :company
|
400
452
|
# end
|
401
453
|
#
|
402
|
-
# Creates a <tt>company_id(
|
454
|
+
# Creates a <tt>company_id(bigint)</tt> column.
|
403
455
|
#
|
404
456
|
# ====== Add a polymorphic foreign key column
|
405
457
|
#
|
@@ -407,7 +459,7 @@ module ActiveRecord
|
|
407
459
|
# t.belongs_to :company, polymorphic: true
|
408
460
|
# end
|
409
461
|
#
|
410
|
-
# Creates <tt>company_type(varchar)</tt> and <tt>company_id(
|
462
|
+
# Creates <tt>company_type(varchar)</tt> and <tt>company_id(bigint)</tt> columns.
|
411
463
|
#
|
412
464
|
# ====== Remove a column
|
413
465
|
#
|
@@ -428,8 +480,8 @@ module ActiveRecord
|
|
428
480
|
# t.remove_index :company_id
|
429
481
|
# end
|
430
482
|
#
|
431
|
-
# See also Table for details on all of the various column
|
432
|
-
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)
|
433
485
|
if supports_bulk_alter? && options[:bulk]
|
434
486
|
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
|
435
487
|
yield update_table_definition(table_name, recorder)
|
@@ -459,7 +511,8 @@ module ActiveRecord
|
|
459
511
|
# Although this command ignores most +options+ and the block if one is given,
|
460
512
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
461
513
|
# In that case, +options+ and the block will be used by #create_table.
|
462
|
-
def drop_table(table_name, options
|
514
|
+
def drop_table(table_name, **options)
|
515
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
463
516
|
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
464
517
|
end
|
465
518
|
|
@@ -478,19 +531,28 @@ module ActiveRecord
|
|
478
531
|
#
|
479
532
|
# Available options are (none of these exists by default):
|
480
533
|
# * <tt>:limit</tt> -
|
481
|
-
# Requests a maximum column length. This is number of characters for a <tt>:string</tt> column
|
482
|
-
# and number of bytes for <tt>:text</tt>, <tt>:binary</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.
|
483
537
|
# * <tt>:default</tt> -
|
484
|
-
# The column's default value. Use nil for NULL
|
538
|
+
# The column's default value. Use +nil+ for +NULL+.
|
485
539
|
# * <tt>:null</tt> -
|
486
|
-
# Allows or disallows +NULL+ values in the column.
|
487
|
-
# have been named <tt>:null_allowed</tt>.
|
540
|
+
# Allows or disallows +NULL+ values in the column.
|
488
541
|
# * <tt>:precision</tt> -
|
489
|
-
# Specifies the precision for the <tt>:decimal</tt
|
542
|
+
# Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
|
543
|
+
# <tt>:datetime</tt>, and <tt>:time</tt> columns.
|
490
544
|
# * <tt>:scale</tt> -
|
491
545
|
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
492
|
-
#
|
493
|
-
#
|
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,
|
494
556
|
# and the scale is the number of digits that can be stored following
|
495
557
|
# the decimal point. For example, the number 123.45 has a precision of 5
|
496
558
|
# and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
|
@@ -509,9 +571,7 @@ module ActiveRecord
|
|
509
571
|
# but the maximum supported <tt>:precision</tt> is 16. No default.
|
510
572
|
# * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
|
511
573
|
# Default is (38,0).
|
512
|
-
# *
|
513
|
-
# Default unknown.
|
514
|
-
# * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
|
574
|
+
# * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
|
515
575
|
# Default (38,0).
|
516
576
|
#
|
517
577
|
# == Examples
|
@@ -533,23 +593,44 @@ module ActiveRecord
|
|
533
593
|
# add_column(:measurements, :huge_integer, :decimal, precision: 30)
|
534
594
|
# # ALTER TABLE "measurements" ADD "huge_integer" decimal(30)
|
535
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
|
+
#
|
536
600
|
# # Defines a column with a database-specific type.
|
537
601
|
# add_column(:shapes, :triangle, 'polygon')
|
538
602
|
# # ALTER TABLE "shapes" ADD "triangle" polygon
|
539
|
-
|
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
|
+
|
540
609
|
at = create_alter_table table_name
|
541
|
-
at.add_column(column_name, type, options)
|
610
|
+
at.add_column(column_name, type, **options)
|
542
611
|
execute schema_creation.accept at
|
543
612
|
end
|
544
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
|
+
|
545
620
|
# Removes the given columns from the table definition.
|
546
621
|
#
|
547
622
|
# remove_columns(:suppliers, :qualification, :experience)
|
548
623
|
#
|
549
|
-
|
550
|
-
|
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
|
+
|
551
632
|
column_names.each do |column_name|
|
552
|
-
remove_column(table_name, column_name)
|
633
|
+
remove_column(table_name, column_name, type, **options)
|
553
634
|
end
|
554
635
|
end
|
555
636
|
|
@@ -559,9 +640,18 @@ module ActiveRecord
|
|
559
640
|
#
|
560
641
|
# The +type+ and +options+ parameters will be ignored if present. It can be helpful
|
561
642
|
# to provide these in a migration's +change+ method so it can be reverted.
|
562
|
-
# In that case, +type+ and +options+ will be used by add_column.
|
563
|
-
|
564
|
-
|
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)}"
|
565
655
|
end
|
566
656
|
|
567
657
|
# Changes the column's definition according to the new options.
|
@@ -570,7 +660,7 @@ module ActiveRecord
|
|
570
660
|
# change_column(:suppliers, :name, :string, limit: 80)
|
571
661
|
# change_column(:accounts, :description, :text)
|
572
662
|
#
|
573
|
-
def change_column(table_name, column_name, type, options
|
663
|
+
def change_column(table_name, column_name, type, **options)
|
574
664
|
raise NotImplementedError, "change_column is not implemented"
|
575
665
|
end
|
576
666
|
|
@@ -632,7 +722,17 @@ module ActiveRecord
|
|
632
722
|
#
|
633
723
|
# generates:
|
634
724
|
#
|
635
|
-
# 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.
|
636
736
|
#
|
637
737
|
# ====== Creating a unique index
|
638
738
|
#
|
@@ -640,7 +740,7 @@ module ActiveRecord
|
|
640
740
|
#
|
641
741
|
# generates:
|
642
742
|
#
|
643
|
-
# CREATE UNIQUE INDEX
|
743
|
+
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id)
|
644
744
|
#
|
645
745
|
# ====== Creating a named index
|
646
746
|
#
|
@@ -670,13 +770,13 @@ module ActiveRecord
|
|
670
770
|
#
|
671
771
|
# ====== Creating an index with a sort order (desc or asc, asc is the default)
|
672
772
|
#
|
673
|
-
# 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})
|
674
774
|
#
|
675
775
|
# generates:
|
676
776
|
#
|
677
777
|
# CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
|
678
778
|
#
|
679
|
-
# Note: MySQL
|
779
|
+
# Note: MySQL only supports index order from 8.0.1 onwards (earlier versions accepted the syntax but ignored it).
|
680
780
|
#
|
681
781
|
# ====== Creating a partial index
|
682
782
|
#
|
@@ -686,7 +786,7 @@ module ActiveRecord
|
|
686
786
|
#
|
687
787
|
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
|
688
788
|
#
|
689
|
-
# Note: Partial indexes are only supported for PostgreSQL and SQLite
|
789
|
+
# Note: Partial indexes are only supported for PostgreSQL and SQLite.
|
690
790
|
#
|
691
791
|
# ====== Creating an index with a specific method
|
692
792
|
#
|
@@ -699,6 +799,19 @@ module ActiveRecord
|
|
699
799
|
#
|
700
800
|
# Note: only supported by PostgreSQL and MySQL
|
701
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
|
+
#
|
702
815
|
# ====== Creating an index with a specific type
|
703
816
|
#
|
704
817
|
# add_index(:developers, :name, type: :fulltext)
|
@@ -708,9 +821,22 @@ module ActiveRecord
|
|
708
821
|
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
|
709
822
|
#
|
710
823
|
# Note: only supported by MySQL.
|
711
|
-
|
712
|
-
|
713
|
-
|
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)
|
714
840
|
end
|
715
841
|
|
716
842
|
# Removes the given index from the table.
|
@@ -731,8 +857,29 @@ module ActiveRecord
|
|
731
857
|
#
|
732
858
|
# remove_index :accounts, name: :by_branch_party
|
733
859
|
#
|
734
|
-
|
735
|
-
|
860
|
+
# Removes the index on +branch_id+ named +by_branch_party+ in the +accounts+ table.
|
861
|
+
#
|
862
|
+
# remove_index :accounts, :branch_id, name: :by_branch_party
|
863
|
+
#
|
864
|
+
# Checks if the index exists before trying to remove it. Will silently ignore indexes that
|
865
|
+
# don't exist.
|
866
|
+
#
|
867
|
+
# remove_index :accounts, if_exists: true
|
868
|
+
#
|
869
|
+
# Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
|
870
|
+
#
|
871
|
+
# remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
|
872
|
+
#
|
873
|
+
# Note: only supported by PostgreSQL.
|
874
|
+
#
|
875
|
+
# Concurrently removing an index is not supported in a transaction.
|
876
|
+
#
|
877
|
+
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
878
|
+
def remove_index(table_name, column_name = nil, **options)
|
879
|
+
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
880
|
+
|
881
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
882
|
+
|
736
883
|
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
737
884
|
end
|
738
885
|
|
@@ -743,9 +890,11 @@ module ActiveRecord
|
|
743
890
|
# rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
|
744
891
|
#
|
745
892
|
def rename_index(table_name, old_name, new_name)
|
893
|
+
old_name = old_name.to_s
|
894
|
+
new_name = new_name.to_s
|
746
895
|
validate_index_length!(table_name, new_name)
|
747
896
|
|
748
|
-
# 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)
|
749
898
|
old_index_def = indexes(table_name).detect { |i| i.name == old_name }
|
750
899
|
return unless old_index_def
|
751
900
|
add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
|
@@ -767,44 +916,42 @@ module ActiveRecord
|
|
767
916
|
end
|
768
917
|
|
769
918
|
# Verifies the existence of an index with a given name.
|
770
|
-
|
771
|
-
# The default argument is returned if the underlying implementation does not define the indexes method,
|
772
|
-
# as there's no way to determine the correct answer in that case.
|
773
|
-
def index_name_exists?(table_name, index_name, default)
|
774
|
-
return default unless respond_to?(:indexes)
|
919
|
+
def index_name_exists?(table_name, index_name)
|
775
920
|
index_name = index_name.to_s
|
776
921
|
indexes(table_name).detect { |i| i.name == index_name }
|
777
922
|
end
|
778
923
|
|
779
|
-
# Adds a reference. The reference column is
|
924
|
+
# Adds a reference. The reference column is a bigint by default,
|
780
925
|
# the <tt>:type</tt> option can be used to specify a different type.
|
781
926
|
# Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
|
782
927
|
# #add_reference and #add_belongs_to are acceptable.
|
783
928
|
#
|
784
929
|
# The +options+ hash can include the following keys:
|
785
930
|
# [<tt>:type</tt>]
|
786
|
-
# The reference column type. Defaults to +:
|
931
|
+
# The reference column type. Defaults to +:bigint+.
|
787
932
|
# [<tt>:index</tt>]
|
788
933
|
# Add an appropriate index. Defaults to true.
|
789
934
|
# See #add_index for usage of this option.
|
790
935
|
# [<tt>:foreign_key</tt>]
|
791
|
-
# Add an appropriate foreign key constraint. Defaults to false
|
936
|
+
# Add an appropriate foreign key constraint. Defaults to false, pass true
|
937
|
+
# to add. In case the join table can't be inferred from the association
|
938
|
+
# pass <tt>:to_table</tt> with the appropriate table name.
|
792
939
|
# [<tt>:polymorphic</tt>]
|
793
940
|
# Whether an additional +_type+ column should be added. Defaults to false.
|
794
941
|
# [<tt>:null</tt>]
|
795
942
|
# Whether the column allows nulls. Defaults to true.
|
796
943
|
#
|
797
|
-
# ====== Create a user_id
|
944
|
+
# ====== Create a user_id bigint column without an index
|
798
945
|
#
|
799
|
-
# add_reference(:products, :user)
|
946
|
+
# add_reference(:products, :user, index: false)
|
800
947
|
#
|
801
948
|
# ====== Create a user_id string column
|
802
949
|
#
|
803
950
|
# add_reference(:products, :user, type: :string)
|
804
951
|
#
|
805
|
-
# ====== Create supplier_id, supplier_type columns
|
952
|
+
# ====== Create supplier_id, supplier_type columns
|
806
953
|
#
|
807
|
-
# add_reference(:products, :supplier, polymorphic: true
|
954
|
+
# add_reference(:products, :supplier, polymorphic: true)
|
808
955
|
#
|
809
956
|
# ====== Create a supplier_id column with a unique index
|
810
957
|
#
|
@@ -820,10 +967,10 @@ module ActiveRecord
|
|
820
967
|
#
|
821
968
|
# ====== Create a supplier_id column and a foreign key to the firms table
|
822
969
|
#
|
823
|
-
# add_reference(:products, :supplier, foreign_key: {to_table: :firms})
|
970
|
+
# add_reference(:products, :supplier, foreign_key: { to_table: :firms })
|
824
971
|
#
|
825
|
-
def add_reference(table_name,
|
826
|
-
ReferenceDefinition.new(
|
972
|
+
def add_reference(table_name, ref_name, **options)
|
973
|
+
ReferenceDefinition.new(ref_name, **options).add_to(update_table_definition(table_name, self))
|
827
974
|
end
|
828
975
|
alias :add_belongs_to :add_reference
|
829
976
|
|
@@ -832,7 +979,7 @@ module ActiveRecord
|
|
832
979
|
#
|
833
980
|
# ====== Remove the reference
|
834
981
|
#
|
835
|
-
# remove_reference(:products, :user, index:
|
982
|
+
# remove_reference(:products, :user, index: false)
|
836
983
|
#
|
837
984
|
# ====== Remove polymorphic reference
|
838
985
|
#
|
@@ -840,7 +987,7 @@ module ActiveRecord
|
|
840
987
|
#
|
841
988
|
# ====== Remove the reference with a foreign key
|
842
989
|
#
|
843
|
-
# remove_reference(:products, :user,
|
990
|
+
# remove_reference(:products, :user, foreign_key: true)
|
844
991
|
#
|
845
992
|
def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
|
846
993
|
if foreign_key
|
@@ -850,6 +997,7 @@ module ActiveRecord
|
|
850
997
|
else
|
851
998
|
foreign_key_options = { to_table: reference_name }
|
852
999
|
end
|
1000
|
+
foreign_key_options[:column] ||= "#{ref_name}_id"
|
853
1001
|
remove_foreign_key(table_name, **foreign_key_options)
|
854
1002
|
end
|
855
1003
|
|
@@ -906,7 +1054,9 @@ module ActiveRecord
|
|
906
1054
|
# Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
|
907
1055
|
# [<tt>:on_update</tt>]
|
908
1056
|
# Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
|
909
|
-
|
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)
|
910
1060
|
return unless supports_foreign_keys?
|
911
1061
|
|
912
1062
|
options = foreign_key_options(from_table, to_table, options)
|
@@ -929,15 +1079,22 @@ module ActiveRecord
|
|
929
1079
|
#
|
930
1080
|
# remove_foreign_key :accounts, column: :owner_id
|
931
1081
|
#
|
1082
|
+
# Removes the foreign key on +accounts.owner_id+.
|
1083
|
+
#
|
1084
|
+
# remove_foreign_key :accounts, to_table: :owners
|
1085
|
+
#
|
932
1086
|
# Removes the foreign key named +special_fk_name+ on the +accounts+ table.
|
933
1087
|
#
|
934
1088
|
# remove_foreign_key :accounts, name: :special_fk_name
|
935
1089
|
#
|
936
|
-
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
|
937
|
-
|
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)
|
938
1095
|
return unless supports_foreign_keys?
|
939
1096
|
|
940
|
-
fk_name_to_delete = foreign_key_for!(from_table,
|
1097
|
+
fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
|
941
1098
|
|
942
1099
|
at = create_alter_table from_table
|
943
1100
|
at.drop_foreign_key fk_name_to_delete
|
@@ -947,33 +1104,21 @@ module ActiveRecord
|
|
947
1104
|
|
948
1105
|
# Checks to see if a foreign key exists on a table for a given foreign key definition.
|
949
1106
|
#
|
950
|
-
# #
|
1107
|
+
# # Checks to see if a foreign key exists.
|
951
1108
|
# foreign_key_exists?(:accounts, :branches)
|
952
1109
|
#
|
953
|
-
# #
|
1110
|
+
# # Checks to see if a foreign key on a specified column exists.
|
954
1111
|
# foreign_key_exists?(:accounts, column: :owner_id)
|
955
1112
|
#
|
956
|
-
# #
|
1113
|
+
# # Checks to see if a foreign key with a custom name exists.
|
957
1114
|
# foreign_key_exists?(:accounts, name: "special_fk_name")
|
958
1115
|
#
|
959
|
-
def foreign_key_exists?(from_table,
|
960
|
-
foreign_key_for(from_table,
|
961
|
-
end
|
962
|
-
|
963
|
-
def foreign_key_for(from_table, options_or_to_table = {}) # :nodoc:
|
964
|
-
return unless supports_foreign_keys?
|
965
|
-
foreign_keys(from_table).detect {|fk| fk.defined_for? options_or_to_table }
|
966
|
-
end
|
967
|
-
|
968
|
-
def foreign_key_for!(from_table, options_or_to_table = {}) # :nodoc:
|
969
|
-
foreign_key_for(from_table, options_or_to_table) or \
|
970
|
-
raise ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}"
|
1116
|
+
def foreign_key_exists?(from_table, to_table = nil, **options)
|
1117
|
+
foreign_key_for(from_table, to_table: to_table, **options).present?
|
971
1118
|
end
|
972
1119
|
|
973
1120
|
def foreign_key_column_for(table_name) # :nodoc:
|
974
|
-
|
975
|
-
suffix = Base.table_name_suffix
|
976
|
-
name = table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
|
1121
|
+
name = strip_table_name_prefix_and_suffix(table_name)
|
977
1122
|
"#{name.singularize}_id"
|
978
1123
|
end
|
979
1124
|
|
@@ -984,68 +1129,90 @@ module ActiveRecord
|
|
984
1129
|
options
|
985
1130
|
end
|
986
1131
|
|
987
|
-
|
988
|
-
|
989
|
-
|
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
|
990
1136
|
end
|
991
1137
|
|
992
|
-
|
993
|
-
|
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?
|
994
1154
|
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
else
|
1001
|
-
"INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
|
1002
|
-
end
|
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)
|
1003
1160
|
end
|
1004
1161
|
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1162
|
+
def check_constraint_options(table_name, expression, options) # :nodoc:
|
1163
|
+
options = options.dup
|
1164
|
+
options[:name] ||= check_constraint_name(table_name, expression: expression, **options)
|
1165
|
+
options
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
# Removes the given check constraint from the table.
|
1169
|
+
#
|
1170
|
+
# remove_check_constraint :products, name: "price_check"
|
1171
|
+
#
|
1172
|
+
# The +expression+ parameter will be ignored if present. It can be helpful
|
1173
|
+
# to provide this in a migration's +change+ method so it can be reverted.
|
1174
|
+
# In that case, +expression+ will be used by #add_check_constraint.
|
1175
|
+
def remove_check_constraint(table_name, expression = nil, **options)
|
1176
|
+
return unless supports_check_constraints?
|
1177
|
+
|
1178
|
+
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
1179
|
+
|
1180
|
+
at = create_alter_table(table_name)
|
1181
|
+
at.drop_check_constraint(chk_name_to_delete)
|
1182
|
+
|
1183
|
+
execute schema_creation.accept(at)
|
1009
1184
|
end
|
1010
1185
|
|
1011
|
-
def
|
1012
|
-
|
1186
|
+
def dump_schema_information # :nodoc:
|
1187
|
+
versions = schema_migration.all_versions
|
1188
|
+
insert_versions_sql(versions) if versions.any?
|
1013
1189
|
end
|
1014
1190
|
|
1015
1191
|
def internal_string_options_for_primary_key # :nodoc:
|
1016
1192
|
{ primary_key: true }
|
1017
1193
|
end
|
1018
1194
|
|
1019
|
-
def assume_migrated_upto_version(version
|
1020
|
-
migrations_paths = Array(migrations_paths)
|
1195
|
+
def assume_migrated_upto_version(version)
|
1021
1196
|
version = version.to_i
|
1022
|
-
sm_table = quote_table_name(
|
1197
|
+
sm_table = quote_table_name(schema_migration.table_name)
|
1023
1198
|
|
1024
|
-
migrated =
|
1025
|
-
versions =
|
1026
|
-
ActiveRecord::Migrator.parse_migration_filename(file).first.to_i
|
1027
|
-
end
|
1199
|
+
migrated = migration_context.get_all_versions
|
1200
|
+
versions = migration_context.migrations.map(&:version)
|
1028
1201
|
|
1029
1202
|
unless migrated.include?(version)
|
1030
1203
|
execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
|
1031
1204
|
end
|
1032
1205
|
|
1033
|
-
inserting = (versions - migrated).select {|v| v < version}
|
1206
|
+
inserting = (versions - migrated).select { |v| v < version }
|
1034
1207
|
if inserting.any?
|
1035
|
-
if (duplicate = inserting.detect {|v| inserting.count(v) > 1})
|
1208
|
+
if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
|
1036
1209
|
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
|
1037
1210
|
end
|
1038
|
-
|
1039
|
-
execute insert_versions_sql(inserting)
|
1040
|
-
else
|
1041
|
-
inserting.each do |v|
|
1042
|
-
execute insert_versions_sql(v)
|
1043
|
-
end
|
1044
|
-
end
|
1211
|
+
execute insert_versions_sql(inserting)
|
1045
1212
|
end
|
1046
1213
|
end
|
1047
1214
|
|
1048
|
-
def type_to_sql(type, limit
|
1215
|
+
def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) # :nodoc:
|
1049
1216
|
type = type.to_sym if type
|
1050
1217
|
if native = native_database_types[type]
|
1051
1218
|
column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
|
@@ -1063,11 +1230,11 @@ module ActiveRecord
|
|
1063
1230
|
raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
|
1064
1231
|
end
|
1065
1232
|
|
1066
|
-
elsif [:datetime, :time].include?(type) && precision ||= native[:precision]
|
1233
|
+
elsif [:datetime, :timestamp, :time, :interval].include?(type) && precision ||= native[:precision]
|
1067
1234
|
if (0..6) === precision
|
1068
1235
|
column_type_sql << "(#{precision})"
|
1069
1236
|
else
|
1070
|
-
raise
|
1237
|
+
raise ArgumentError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6"
|
1071
1238
|
end
|
1072
1239
|
elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
|
1073
1240
|
column_type_sql << "(#{limit})"
|
@@ -1080,7 +1247,7 @@ module ActiveRecord
|
|
1080
1247
|
end
|
1081
1248
|
|
1082
1249
|
# Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
|
1083
|
-
# PostgreSQL, MySQL, and Oracle
|
1250
|
+
# PostgreSQL, MySQL, and Oracle override this for custom DISTINCT syntax - they
|
1084
1251
|
# require the order columns appear in the SELECT.
|
1085
1252
|
#
|
1086
1253
|
# columns_for_distinct("posts.id", ["posts.created_at desc"])
|
@@ -1094,18 +1261,22 @@ module ActiveRecord
|
|
1094
1261
|
#
|
1095
1262
|
# add_timestamps(:suppliers, null: true)
|
1096
1263
|
#
|
1097
|
-
def add_timestamps(table_name, options
|
1264
|
+
def add_timestamps(table_name, **options)
|
1098
1265
|
options[:null] = false if options[:null].nil?
|
1099
1266
|
|
1100
|
-
|
1101
|
-
|
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
|
1102
1273
|
end
|
1103
1274
|
|
1104
1275
|
# Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
|
1105
1276
|
#
|
1106
1277
|
# remove_timestamps(:suppliers)
|
1107
1278
|
#
|
1108
|
-
def remove_timestamps(table_name, options
|
1279
|
+
def remove_timestamps(table_name, **options)
|
1109
1280
|
remove_column table_name, :updated_at
|
1110
1281
|
remove_column table_name, :created_at
|
1111
1282
|
end
|
@@ -1114,38 +1285,43 @@ module ActiveRecord
|
|
1114
1285
|
Table.new(table_name, base)
|
1115
1286
|
end
|
1116
1287
|
|
1117
|
-
def add_index_options(table_name, column_name,
|
1118
|
-
|
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)
|
1119
1290
|
|
1120
|
-
|
1291
|
+
column_names = index_column_names(column_name)
|
1121
1292
|
|
1122
|
-
|
1123
|
-
index_type ||= options[:unique] ? "UNIQUE" : ""
|
1124
|
-
index_name = options[:name].to_s if options.key?(:name)
|
1293
|
+
index_name = name&.to_s
|
1125
1294
|
index_name ||= index_name(table_name, column_names)
|
1126
|
-
max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
|
1127
1295
|
|
1128
|
-
|
1129
|
-
algorithm = index_algorithms.fetch(options[:algorithm]) {
|
1130
|
-
raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
|
1131
|
-
}
|
1132
|
-
end
|
1296
|
+
validate_index_length!(table_name, index_name, internal)
|
1133
1297
|
|
1134
|
-
|
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
|
+
)
|
1135
1310
|
|
1136
|
-
|
1137
|
-
|
1138
|
-
end
|
1311
|
+
[index, index_algorithm(options[:algorithm]), if_not_exists]
|
1312
|
+
end
|
1139
1313
|
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
if
|
1144
|
-
|
1145
|
-
end
|
1146
|
-
index_columns = quoted_columns_for_index(column_names, options).join(", ")
|
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
|
1147
1319
|
|
1148
|
-
|
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
|
1323
|
+
end
|
1324
|
+
add_options_for_index_columns(quoted_columns, **options).values.join(", ")
|
1149
1325
|
end
|
1150
1326
|
|
1151
1327
|
def options_include_default?(options)
|
@@ -1153,63 +1329,73 @@ module ActiveRecord
|
|
1153
1329
|
end
|
1154
1330
|
|
1155
1331
|
# Changes the comment for a table or removes it if +nil+.
|
1156
|
-
|
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)
|
1157
1338
|
raise NotImplementedError, "#{self.class} does not support changing table comments"
|
1158
1339
|
end
|
1159
1340
|
|
1160
1341
|
# Changes the comment for a column or removes it if +nil+.
|
1161
|
-
|
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)
|
1162
1348
|
raise NotImplementedError, "#{self.class} does not support changing column comments"
|
1163
1349
|
end
|
1164
1350
|
|
1165
|
-
|
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
|
1166
1359
|
|
1167
1360
|
def add_index_sort_order(quoted_columns, **options)
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
order = order.symbolize_keys
|
1172
|
-
quoted_columns.each { |name, column| column << " #{order[name].upcase}" if order[name].present? }
|
1173
|
-
when String
|
1174
|
-
quoted_columns.each { |name, column| column << " #{order.upcase}" if order.present? }
|
1175
|
-
end
|
1361
|
+
orders = options_for_index_columns(options[:order])
|
1362
|
+
quoted_columns.each do |name, column|
|
1363
|
+
column << " #{orders[name].upcase}" if orders[name].present?
|
1176
1364
|
end
|
1365
|
+
end
|
1177
1366
|
|
1178
|
-
|
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
|
1179
1373
|
end
|
1180
1374
|
|
1181
|
-
# Overridden by the MySQL adapter for supporting index lengths
|
1375
|
+
# Overridden by the MySQL adapter for supporting index lengths and by
|
1376
|
+
# the PostgreSQL adapter for supporting operator classes.
|
1182
1377
|
def add_options_for_index_columns(quoted_columns, **options)
|
1183
1378
|
if supports_index_sort_order?
|
1184
|
-
quoted_columns = add_index_sort_order(quoted_columns, options)
|
1379
|
+
quoted_columns = add_index_sort_order(quoted_columns, **options)
|
1185
1380
|
end
|
1186
1381
|
|
1187
1382
|
quoted_columns
|
1188
1383
|
end
|
1189
1384
|
|
1190
|
-
def
|
1191
|
-
return [
|
1192
|
-
|
1193
|
-
quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
|
1194
|
-
add_options_for_index_columns(quoted_columns, options).values
|
1195
|
-
end
|
1196
|
-
|
1197
|
-
def index_name_for_remove(table_name, options = {})
|
1198
|
-
return options[:name] if can_remove_index_by_name?(options)
|
1199
|
-
|
1200
|
-
# if the adapter doesn't support the indexes call the best we can do
|
1201
|
-
# is return the default index name for the options provided
|
1202
|
-
return index_name(table_name, options) unless respond_to?(:indexes)
|
1385
|
+
def index_name_for_remove(table_name, column_name, options)
|
1386
|
+
return options[:name] if can_remove_index_by_name?(column_name, options)
|
1203
1387
|
|
1204
1388
|
checks = []
|
1205
1389
|
|
1206
|
-
if options.is_a?(
|
1207
|
-
|
1208
|
-
column_names =
|
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 = []
|
1209
1393
|
else
|
1210
|
-
column_names = index_column_names(options)
|
1394
|
+
column_names = index_column_names(column_name || options[:column])
|
1211
1395
|
end
|
1212
1396
|
|
1397
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
|
1398
|
+
|
1213
1399
|
if column_names.present?
|
1214
1400
|
checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
|
1215
1401
|
end
|
@@ -1250,56 +1436,202 @@ module ActiveRecord
|
|
1250
1436
|
end
|
1251
1437
|
end
|
1252
1438
|
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
end
|
1439
|
+
def schema_creation
|
1440
|
+
SchemaCreation.new(self)
|
1441
|
+
end
|
1257
1442
|
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1443
|
+
def create_table_definition(name, **options)
|
1444
|
+
TableDefinition.new(self, name, **options)
|
1445
|
+
end
|
1261
1446
|
|
1262
|
-
|
1263
|
-
|
1264
|
-
column_names
|
1265
|
-
else
|
1266
|
-
Array(column_names)
|
1447
|
+
def create_alter_table(name)
|
1448
|
+
AlterTable.new create_table_definition(name)
|
1267
1449
|
end
|
1268
|
-
end
|
1269
1450
|
|
1270
|
-
|
1271
|
-
|
1272
|
-
column_names = column_names.scan(/\w+/).join('_')
|
1451
|
+
def extract_table_options!(options)
|
1452
|
+
options.extract!(:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation)
|
1273
1453
|
end
|
1274
1454
|
|
1275
|
-
|
1276
|
-
|
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
|
1277
1478
|
|
1278
|
-
|
1279
|
-
identifier = "#{table_name}_#{options.fetch(:column)}_fk"
|
1280
|
-
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
|
1281
|
-
options.fetch(:name) do
|
1282
|
-
"fk_rails_#{hashed_identifier}"
|
1479
|
+
{ column: column_names }
|
1283
1480
|
end
|
1284
|
-
end
|
1285
1481
|
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
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
|
1289
1486
|
end
|
1290
|
-
end
|
1291
1487
|
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
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
|
1297
1495
|
end
|
1298
|
-
end
|
1299
1496
|
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
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
|
1634
|
+
end
|
1303
1635
|
end
|
1304
1636
|
end
|
1305
1637
|
end
|