activerecord 4.2.0 → 6.0.0
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 +612 -971
- data/MIT-LICENSE +4 -2
- data/README.rdoc +13 -12
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +267 -248
- data/lib/active_record/association_relation.rb +24 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +135 -56
- data/lib/active_record/associations/association_scope.rb +103 -131
- data/lib/active_record/associations/belongs_to_association.rb +67 -54
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
- data/lib/active_record/associations/builder/association.rb +27 -40
- data/lib/active_record/associations/builder/belongs_to.rb +69 -55
- data/lib/active_record/associations/builder/collection_association.rb +10 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +60 -70
- data/lib/active_record/associations/builder/has_many.rb +8 -4
- data/lib/active_record/associations/builder/has_one.rb +46 -5
- data/lib/active_record/associations/builder/singular_association.rb +16 -10
- data/lib/active_record/associations/collection_association.rb +138 -274
- data/lib/active_record/associations/collection_proxy.rb +252 -151
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +35 -83
- data/lib/active_record/associations/has_many_through_association.rb +62 -80
- data/lib/active_record/associations/has_one_association.rb +62 -49
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +38 -80
- 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 +138 -162
- data/lib/active_record/associations/preloader/association.rb +90 -119
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +92 -94
- data/lib/active_record/associations/singular_association.rb +18 -45
- data/lib/active_record/associations/through_association.rb +48 -23
- data/lib/active_record/associations.rb +1737 -1596
- data/lib/active_record/attribute_assignment.rb +56 -183
- data/lib/active_record/attribute_decorators.rb +39 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +15 -5
- data/lib/active_record/attribute_methods/dirty.rb +174 -134
- data/lib/active_record/attribute_methods/primary_key.rb +91 -83
- data/lib/active_record/attribute_methods/query.rb +6 -5
- data/lib/active_record/attribute_methods/read.rb +20 -76
- data/lib/active_record/attribute_methods/serialization.rb +40 -20
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -36
- data/lib/active_record/attribute_methods/write.rb +33 -55
- data/lib/active_record/attribute_methods.rb +124 -143
- data/lib/active_record/attributes.rb +214 -74
- data/lib/active_record/autosave_association.rb +115 -46
- data/lib/active_record/base.rb +60 -49
- data/lib/active_record/callbacks.rb +100 -74
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +796 -290
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +247 -108
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +171 -53
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +366 -227
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +706 -222
- data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -87
- data/lib/active_record/connection_adapters/abstract_adapter.rb +468 -194
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +535 -597
- data/lib/active_record/connection_adapters/column.rb +56 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -195
- data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +65 -115
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
- 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 +5 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
- 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 +67 -51
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
- 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 +23 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +474 -286
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -363
- data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -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 +137 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +288 -359
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +176 -41
- data/lib/active_record/core.rb +266 -233
- data/lib/active_record/counter_cache.rb +68 -50
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +164 -88
- data/lib/active_record/errors.rb +189 -53
- data/lib/active_record/explain.rb +23 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +226 -495
- data/lib/active_record/gem_version.rb +4 -2
- data/lib/active_record/inheritance.rb +158 -112
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +123 -29
- data/lib/active_record/internal_metadata.rb +53 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +91 -98
- data/lib/active_record/locking/pessimistic.rb +18 -6
- data/lib/active_record/log_subscriber.rb +76 -33
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +177 -90
- data/lib/active_record/migration/compatibility.rb +244 -0
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/migration.rb +634 -288
- data/lib/active_record/model_schema.rb +314 -112
- data/lib/active_record/nested_attributes.rb +266 -214
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +559 -124
- data/lib/active_record/query_cache.rb +19 -23
- data/lib/active_record/querying.rb +43 -29
- data/lib/active_record/railtie.rb +148 -47
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- 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 +338 -202
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +460 -299
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +207 -55
- data/lib/active_record/relation/calculations.rb +269 -248
- data/lib/active_record/relation/delegation.rb +70 -80
- data/lib/active_record/relation/finder_methods.rb +279 -255
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +83 -69
- data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -25
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -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 +53 -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 +116 -92
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +574 -391
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -16
- data/lib/active_record/relation/where_clause.rb +190 -0
- data/lib/active_record/relation/where_clause_factory.rb +33 -0
- data/lib/active_record/relation.rb +518 -340
- data/lib/active_record/result.rb +79 -42
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +144 -121
- data/lib/active_record/schema.rb +21 -24
- data/lib/active_record/schema_dumper.rb +112 -93
- data/lib/active_record/schema_migration.rb +24 -20
- data/lib/active_record/scoping/default.rb +101 -84
- data/lib/active_record/scoping/named.rb +86 -33
- data/lib/active_record/scoping.rb +45 -26
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +73 -36
- data/lib/active_record/store.rb +127 -42
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +75 -0
- data/lib/active_record/tasks/database_tasks.rb +309 -99
- data/lib/active_record/tasks/mysql_database_tasks.rb +58 -88
- data/lib/active_record/tasks/postgresql_database_tasks.rb +82 -31
- data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +86 -40
- data/lib/active_record/touch_later.rb +66 -0
- data/lib/active_record/transactions.rb +215 -139
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +129 -0
- data/lib/active_record/type/date.rb +4 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
- 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 +30 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +15 -17
- data/lib/active_record/type/unsigned_integer.rb +9 -7
- data/lib/active_record/type.rb +78 -23
- data/lib/active_record/type_caster/connection.rb +34 -0
- data/lib/active_record/type_caster/map.rb +20 -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 +13 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +43 -46
- data/lib/active_record/validations.rb +39 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +43 -21
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -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 +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -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 +18 -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 +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -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 +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -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 +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -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 +68 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +51 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -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 +42 -37
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -8
- data/lib/rails/generators/active_record/migration.rb +31 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +166 -60
- 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 -149
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- 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/infinity.rb +0 -13
- 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 -30
- data/lib/active_record/type/decimal.rb +0 -40
- 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 -55
- 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 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,4 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/migration/join_table"
|
4
|
+
require "active_support/core_ext/string/access"
|
5
|
+
require "active_support/deprecation"
|
6
|
+
require "digest/sha2"
|
2
7
|
|
3
8
|
module ActiveRecord
|
4
9
|
module ConnectionAdapters # :nodoc:
|
@@ -12,9 +17,41 @@ module ActiveRecord
|
|
12
17
|
{}
|
13
18
|
end
|
14
19
|
|
20
|
+
def table_options(table_name)
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the table comment that's stored in database metadata.
|
25
|
+
def table_comment(table_name)
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
15
29
|
# Truncates a table alias according to the limits of the current adapter.
|
16
30
|
def table_alias_for(table_name)
|
17
|
-
table_name[0...table_alias_length].tr(
|
31
|
+
table_name[0...table_alias_length].tr(".", "_")
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the relation names useable to back Active Record models.
|
35
|
+
# For most adapters this means all #tables and #views.
|
36
|
+
def data_sources
|
37
|
+
query_values(data_source_sql, "SCHEMA")
|
38
|
+
rescue NotImplementedError
|
39
|
+
tables | views
|
40
|
+
end
|
41
|
+
|
42
|
+
# Checks to see if the data source +name+ exists on the database.
|
43
|
+
#
|
44
|
+
# data_source_exists?(:ebooks)
|
45
|
+
#
|
46
|
+
def data_source_exists?(name)
|
47
|
+
query_values(data_source_sql(name), "SCHEMA").any? if name.present?
|
48
|
+
rescue NotImplementedError
|
49
|
+
data_sources.include?(name.to_s)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns an array of table names defined in the database.
|
53
|
+
def tables
|
54
|
+
query_values(data_source_sql(type: "BASE TABLE"), "SCHEMA")
|
18
55
|
end
|
19
56
|
|
20
57
|
# Checks to see if the table +table_name+ exists on the database.
|
@@ -22,11 +59,30 @@ module ActiveRecord
|
|
22
59
|
# table_exists?(:developers)
|
23
60
|
#
|
24
61
|
def table_exists?(table_name)
|
62
|
+
query_values(data_source_sql(table_name, type: "BASE TABLE"), "SCHEMA").any? if table_name.present?
|
63
|
+
rescue NotImplementedError
|
25
64
|
tables.include?(table_name.to_s)
|
26
65
|
end
|
27
66
|
|
67
|
+
# Returns an array of view names defined in the database.
|
68
|
+
def views
|
69
|
+
query_values(data_source_sql(type: "VIEW"), "SCHEMA")
|
70
|
+
end
|
71
|
+
|
72
|
+
# Checks to see if the view +view_name+ exists on the database.
|
73
|
+
#
|
74
|
+
# view_exists?(:ebooks)
|
75
|
+
#
|
76
|
+
def view_exists?(view_name)
|
77
|
+
query_values(data_source_sql(view_name, type: "VIEW"), "SCHEMA").any? if view_name.present?
|
78
|
+
rescue NotImplementedError
|
79
|
+
views.include?(view_name.to_s)
|
80
|
+
end
|
81
|
+
|
28
82
|
# Returns an array of indexes for the given table.
|
29
|
-
|
83
|
+
def indexes(table_name)
|
84
|
+
raise NotImplementedError, "#indexes is not implemented"
|
85
|
+
end
|
30
86
|
|
31
87
|
# Checks to see if an index exists on a table for a given index definition.
|
32
88
|
#
|
@@ -44,18 +100,21 @@ module ActiveRecord
|
|
44
100
|
#
|
45
101
|
def index_exists?(table_name, column_name, options = {})
|
46
102
|
column_names = Array(column_name).map(&:to_s)
|
47
|
-
index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, column: column_names)
|
48
103
|
checks = []
|
49
|
-
checks << lambda { |i| i.
|
50
|
-
checks << lambda { |i| i.columns == column_names }
|
104
|
+
checks << lambda { |i| Array(i.columns) == column_names }
|
51
105
|
checks << lambda { |i| i.unique } if options[:unique]
|
106
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
|
52
107
|
|
53
108
|
indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
|
54
109
|
end
|
55
110
|
|
56
|
-
# Returns an array of Column objects for the table specified by +table_name+.
|
57
|
-
|
58
|
-
|
111
|
+
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
112
|
+
def columns(table_name)
|
113
|
+
table_name = table_name.to_s
|
114
|
+
column_definitions(table_name).map do |field|
|
115
|
+
new_column_from_field(table_name, field)
|
116
|
+
end
|
117
|
+
end
|
59
118
|
|
60
119
|
# Checks to see if a column exists in a given table.
|
61
120
|
#
|
@@ -71,21 +130,29 @@ module ActiveRecord
|
|
71
130
|
# column_exists?(:suppliers, :name, :string, null: false)
|
72
131
|
# column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
|
73
132
|
#
|
74
|
-
def column_exists?(table_name, column_name, type = nil, options
|
133
|
+
def column_exists?(table_name, column_name, type = nil, **options)
|
75
134
|
column_name = column_name.to_s
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
135
|
+
checks = []
|
136
|
+
checks << lambda { |c| c.name == column_name }
|
137
|
+
checks << lambda { |c| c.type == type.to_sym rescue nil } if type
|
138
|
+
column_options_keys.each do |attr|
|
139
|
+
checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
|
140
|
+
end
|
141
|
+
|
142
|
+
columns(table_name).any? { |c| checks.all? { |check| check[c] } }
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns just a table's primary key
|
146
|
+
def primary_key(table_name)
|
147
|
+
pk = primary_keys(table_name)
|
148
|
+
pk = pk.first unless pk.size > 1
|
149
|
+
pk
|
83
150
|
end
|
84
151
|
|
85
152
|
# Creates a new table with the name +table_name+. +table_name+ may either
|
86
153
|
# be a String or a Symbol.
|
87
154
|
#
|
88
|
-
# There are two ways to work with
|
155
|
+
# There are two ways to work with #create_table. You can use the block
|
89
156
|
# form or the regular form, like this:
|
90
157
|
#
|
91
158
|
# === Block form
|
@@ -117,13 +184,18 @@ module ActiveRecord
|
|
117
184
|
# The +options+ hash can include the following keys:
|
118
185
|
# [<tt>:id</tt>]
|
119
186
|
# Whether to automatically add a primary key column. Defaults to true.
|
120
|
-
# Join tables for
|
187
|
+
# Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
|
188
|
+
#
|
189
|
+
# A Symbol can be used to specify the type of the generated primary key column.
|
121
190
|
# [<tt>:primary_key</tt>]
|
122
191
|
# The name of the primary key, if one is to be added automatically.
|
123
|
-
# Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
|
192
|
+
# Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
|
193
|
+
#
|
194
|
+
# If an array is passed, a composite primary key will be created.
|
124
195
|
#
|
125
196
|
# Note that Active Record models will automatically detect their
|
126
|
-
# primary key. This can be avoided by using
|
197
|
+
# primary key. This can be avoided by using
|
198
|
+
# {self.primary_key=}[rdoc-ref:AttributeMethods::PrimaryKey::ClassMethods#primary_key=] on the model
|
127
199
|
# to define the key explicitly.
|
128
200
|
#
|
129
201
|
# [<tt>:options</tt>]
|
@@ -134,19 +206,22 @@ module ActiveRecord
|
|
134
206
|
# Set to true to drop the table before creating it.
|
135
207
|
# Set to +:cascade+ to drop dependent objects as well.
|
136
208
|
# Defaults to false.
|
209
|
+
# [<tt>:if_not_exists</tt>]
|
210
|
+
# Set to true to avoid raising an error when the table already exists.
|
211
|
+
# Defaults to false.
|
137
212
|
# [<tt>:as</tt>]
|
138
213
|
# SQL to use to generate the table. When this option is used, the block is
|
139
214
|
# ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
|
140
215
|
#
|
141
216
|
# ====== Add a backend specific option to the generated SQL (MySQL)
|
142
217
|
#
|
143
|
-
# create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=
|
218
|
+
# create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
|
144
219
|
#
|
145
220
|
# generates:
|
146
221
|
#
|
147
222
|
# CREATE TABLE suppliers (
|
148
|
-
# id
|
149
|
-
# ) ENGINE=InnoDB DEFAULT CHARSET=
|
223
|
+
# id bigint auto_increment PRIMARY KEY
|
224
|
+
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
150
225
|
#
|
151
226
|
# ====== Rename the primary key column
|
152
227
|
#
|
@@ -157,22 +232,52 @@ module ActiveRecord
|
|
157
232
|
# generates:
|
158
233
|
#
|
159
234
|
# CREATE TABLE objects (
|
160
|
-
# guid
|
235
|
+
# guid bigint auto_increment PRIMARY KEY,
|
161
236
|
# name varchar(80)
|
162
237
|
# )
|
163
238
|
#
|
239
|
+
# ====== Change the primary key column type
|
240
|
+
#
|
241
|
+
# create_table(:tags, id: :string) do |t|
|
242
|
+
# t.column :label, :string
|
243
|
+
# end
|
244
|
+
#
|
245
|
+
# generates:
|
246
|
+
#
|
247
|
+
# CREATE TABLE tags (
|
248
|
+
# id varchar PRIMARY KEY,
|
249
|
+
# label varchar
|
250
|
+
# )
|
251
|
+
#
|
252
|
+
# ====== Create a composite primary key
|
253
|
+
#
|
254
|
+
# create_table(:orders, primary_key: [:product_id, :client_id]) do |t|
|
255
|
+
# t.belongs_to :product
|
256
|
+
# t.belongs_to :client
|
257
|
+
# end
|
258
|
+
#
|
259
|
+
# generates:
|
260
|
+
#
|
261
|
+
# CREATE TABLE order (
|
262
|
+
# product_id bigint NOT NULL,
|
263
|
+
# client_id bigint NOT NULL
|
264
|
+
# );
|
265
|
+
#
|
266
|
+
# ALTER TABLE ONLY "orders"
|
267
|
+
# ADD CONSTRAINT orders_pkey PRIMARY KEY (product_id, client_id);
|
268
|
+
#
|
164
269
|
# ====== Do not add a primary key column
|
165
270
|
#
|
166
271
|
# create_table(:categories_suppliers, id: false) do |t|
|
167
|
-
# t.column :category_id, :
|
168
|
-
# t.column :supplier_id, :
|
272
|
+
# t.column :category_id, :bigint
|
273
|
+
# t.column :supplier_id, :bigint
|
169
274
|
# end
|
170
275
|
#
|
171
276
|
# generates:
|
172
277
|
#
|
173
278
|
# CREATE TABLE categories_suppliers (
|
174
|
-
# category_id
|
175
|
-
# supplier_id
|
279
|
+
# category_id bigint,
|
280
|
+
# supplier_id bigint
|
176
281
|
# )
|
177
282
|
#
|
178
283
|
# ====== Create a temporary table based on a query
|
@@ -186,25 +291,45 @@ module ActiveRecord
|
|
186
291
|
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
187
292
|
#
|
188
293
|
# See also TableDefinition#column for details on how to create columns.
|
189
|
-
def create_table(table_name, options
|
190
|
-
td = create_table_definition
|
294
|
+
def create_table(table_name, **options)
|
295
|
+
td = create_table_definition(table_name, options)
|
191
296
|
|
192
297
|
if options[:id] != false && !options[:as]
|
193
298
|
pk = options.fetch(:primary_key) do
|
194
299
|
Base.get_primary_key table_name.to_s.singularize
|
195
300
|
end
|
196
301
|
|
197
|
-
|
302
|
+
if pk.is_a?(Array)
|
303
|
+
td.primary_keys pk
|
304
|
+
else
|
305
|
+
td.primary_key pk, options.fetch(:id, :primary_key), options.except(:comment)
|
306
|
+
end
|
198
307
|
end
|
199
308
|
|
200
309
|
yield td if block_given?
|
201
310
|
|
202
|
-
if options[:force]
|
203
|
-
drop_table(table_name, options)
|
311
|
+
if options[:force]
|
312
|
+
drop_table(table_name, options.merge(if_exists: true))
|
204
313
|
end
|
205
314
|
|
206
315
|
result = execute schema_creation.accept td
|
207
|
-
|
316
|
+
|
317
|
+
unless supports_indexes_in_create?
|
318
|
+
td.indexes.each do |column_name, index_options|
|
319
|
+
add_index(table_name, column_name, index_options)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
if supports_comments? && !supports_comments_in_create?
|
324
|
+
if table_comment = options[:comment].presence
|
325
|
+
change_table_comment(table_name, table_comment)
|
326
|
+
end
|
327
|
+
|
328
|
+
td.columns.each do |column|
|
329
|
+
change_column_comment(table_name, column.name, column.comment) if column.comment.present?
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
208
333
|
result
|
209
334
|
end
|
210
335
|
|
@@ -214,9 +339,9 @@ module ActiveRecord
|
|
214
339
|
# # Creates a table called 'assemblies_parts' with no id.
|
215
340
|
# create_join_table(:assemblies, :parts)
|
216
341
|
#
|
217
|
-
# You can pass
|
342
|
+
# You can pass an +options+ hash which can include the following keys:
|
218
343
|
# [<tt>:table_name</tt>]
|
219
|
-
# Sets the table name overriding the default
|
344
|
+
# Sets the table name, overriding the default.
|
220
345
|
# [<tt>:column_options</tt>]
|
221
346
|
# Any extra options you want appended to the columns definition.
|
222
347
|
# [<tt>:options</tt>]
|
@@ -227,7 +352,7 @@ module ActiveRecord
|
|
227
352
|
# Set to true to drop the table before creating it.
|
228
353
|
# Defaults to false.
|
229
354
|
#
|
230
|
-
# Note that
|
355
|
+
# Note that #create_join_table does not create any indices by default; you can use
|
231
356
|
# its block form to do so yourself:
|
232
357
|
#
|
233
358
|
# create_join_table :products, :categories do |t|
|
@@ -242,31 +367,30 @@ module ActiveRecord
|
|
242
367
|
# generates:
|
243
368
|
#
|
244
369
|
# CREATE TABLE assemblies_parts (
|
245
|
-
# assembly_id
|
246
|
-
# part_id
|
370
|
+
# assembly_id bigint NOT NULL,
|
371
|
+
# part_id bigint NOT NULL,
|
247
372
|
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
248
373
|
#
|
249
|
-
def create_join_table(table_1, table_2,
|
374
|
+
def create_join_table(table_1, table_2, column_options: {}, **options)
|
250
375
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
251
376
|
|
252
|
-
column_options
|
253
|
-
column_options.reverse_merge!(null: false)
|
377
|
+
column_options.reverse_merge!(null: false, index: false)
|
254
378
|
|
255
|
-
|
379
|
+
t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
|
256
380
|
|
257
381
|
create_table(join_table_name, options.merge!(id: false)) do |td|
|
258
|
-
td.
|
259
|
-
td.
|
382
|
+
td.references t1_ref, column_options
|
383
|
+
td.references t2_ref, column_options
|
260
384
|
yield td if block_given?
|
261
385
|
end
|
262
386
|
end
|
263
387
|
|
264
388
|
# Drops the join table specified by the given arguments.
|
265
|
-
# See
|
389
|
+
# See #create_join_table for details.
|
266
390
|
#
|
267
391
|
# Although this command ignores the block if one is given, it can be helpful
|
268
392
|
# to provide one in a migration's +change+ method so it can be reverted.
|
269
|
-
# In that case, the block will be used by create_join_table.
|
393
|
+
# In that case, the block will be used by #create_join_table.
|
270
394
|
def drop_join_table(table_1, table_2, options = {})
|
271
395
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
272
396
|
drop_table(join_table_name)
|
@@ -284,10 +408,12 @@ module ActiveRecord
|
|
284
408
|
# [<tt>:bulk</tt>]
|
285
409
|
# Set this to true to make this a bulk alter query, such as
|
286
410
|
#
|
287
|
-
# ALTER TABLE `users` ADD COLUMN age INT
|
411
|
+
# ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...
|
288
412
|
#
|
289
413
|
# Defaults to false.
|
290
414
|
#
|
415
|
+
# Only supported on the MySQL and PostgreSQL adapter, ignored elsewhere.
|
416
|
+
#
|
291
417
|
# ====== Add a column
|
292
418
|
#
|
293
419
|
# change_table(:suppliers) do |t|
|
@@ -312,7 +438,7 @@ module ActiveRecord
|
|
312
438
|
# t.references :company
|
313
439
|
# end
|
314
440
|
#
|
315
|
-
# Creates a <tt>company_id(
|
441
|
+
# Creates a <tt>company_id(bigint)</tt> column.
|
316
442
|
#
|
317
443
|
# ====== Add a polymorphic foreign key column
|
318
444
|
#
|
@@ -320,7 +446,7 @@ module ActiveRecord
|
|
320
446
|
# t.belongs_to :company, polymorphic: true
|
321
447
|
# end
|
322
448
|
#
|
323
|
-
# Creates <tt>company_type(varchar)</tt> and <tt>company_id(
|
449
|
+
# Creates <tt>company_type(varchar)</tt> and <tt>company_id(bigint)</tt> columns.
|
324
450
|
#
|
325
451
|
# ====== Remove a column
|
326
452
|
#
|
@@ -341,7 +467,7 @@ module ActiveRecord
|
|
341
467
|
# t.remove_index :company_id
|
342
468
|
# end
|
343
469
|
#
|
344
|
-
# See also Table for details on all of the various column
|
470
|
+
# See also Table for details on all of the various column transformations.
|
345
471
|
def change_table(table_name, options = {})
|
346
472
|
if supports_bulk_alter? && options[:bulk]
|
347
473
|
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
|
@@ -365,17 +491,101 @@ module ActiveRecord
|
|
365
491
|
# [<tt>:force</tt>]
|
366
492
|
# Set to +:cascade+ to drop dependent objects as well.
|
367
493
|
# Defaults to false.
|
494
|
+
# [<tt>:if_exists</tt>]
|
495
|
+
# Set to +true+ to only drop the table if it exists.
|
496
|
+
# Defaults to false.
|
368
497
|
#
|
369
498
|
# Although this command ignores most +options+ and the block if one is given,
|
370
499
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
371
|
-
# In that case, +options+ and the block will be used by create_table.
|
500
|
+
# In that case, +options+ and the block will be used by #create_table.
|
372
501
|
def drop_table(table_name, options = {})
|
373
|
-
execute "DROP TABLE #{quote_table_name(table_name)}"
|
502
|
+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
374
503
|
end
|
375
504
|
|
376
|
-
#
|
377
|
-
#
|
378
|
-
|
505
|
+
# Add a new +type+ column named +column_name+ to +table_name+.
|
506
|
+
#
|
507
|
+
# The +type+ parameter is normally one of the migrations native types,
|
508
|
+
# which is one of the following:
|
509
|
+
# <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
|
510
|
+
# <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
|
511
|
+
# <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
|
512
|
+
# <tt>:binary</tt>, <tt>:boolean</tt>.
|
513
|
+
#
|
514
|
+
# You may use a type not in this list as long as it is supported by your
|
515
|
+
# database (for example, "polygon" in MySQL), but this will not be database
|
516
|
+
# agnostic and should usually be avoided.
|
517
|
+
#
|
518
|
+
# Available options are (none of these exists by default):
|
519
|
+
# * <tt>:limit</tt> -
|
520
|
+
# Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
|
521
|
+
# and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
|
522
|
+
# This option is ignored by some backends.
|
523
|
+
# * <tt>:default</tt> -
|
524
|
+
# The column's default value. Use +nil+ for +NULL+.
|
525
|
+
# * <tt>:null</tt> -
|
526
|
+
# Allows or disallows +NULL+ values in the column.
|
527
|
+
# * <tt>:precision</tt> -
|
528
|
+
# Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
|
529
|
+
# <tt>:datetime</tt>, and <tt>:time</tt> columns.
|
530
|
+
# * <tt>:scale</tt> -
|
531
|
+
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
532
|
+
# * <tt>:collation</tt> -
|
533
|
+
# Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column. If not specified, the
|
534
|
+
# column will have the same collation as the table.
|
535
|
+
# * <tt>:comment</tt> -
|
536
|
+
# Specifies the comment for the column. This option is ignored by some backends.
|
537
|
+
#
|
538
|
+
# Note: The precision is the total number of significant digits,
|
539
|
+
# and the scale is the number of digits that can be stored following
|
540
|
+
# the decimal point. For example, the number 123.45 has a precision of 5
|
541
|
+
# and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
|
542
|
+
# range from -999.99 to 999.99.
|
543
|
+
#
|
544
|
+
# Please be aware of different RDBMS implementations behavior with
|
545
|
+
# <tt>:decimal</tt> columns:
|
546
|
+
# * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
|
547
|
+
# <tt>:precision</tt>, and makes no comments about the requirements of
|
548
|
+
# <tt>:precision</tt>.
|
549
|
+
# * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
|
550
|
+
# Default is (10,0).
|
551
|
+
# * PostgreSQL: <tt>:precision</tt> [1..infinity],
|
552
|
+
# <tt>:scale</tt> [0..infinity]. No default.
|
553
|
+
# * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
|
554
|
+
# but the maximum supported <tt>:precision</tt> is 16. No default.
|
555
|
+
# * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
|
556
|
+
# Default is (38,0).
|
557
|
+
# * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
|
558
|
+
# Default unknown.
|
559
|
+
# * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
|
560
|
+
# Default (38,0).
|
561
|
+
#
|
562
|
+
# == Examples
|
563
|
+
#
|
564
|
+
# add_column(:users, :picture, :binary, limit: 2.megabytes)
|
565
|
+
# # ALTER TABLE "users" ADD "picture" blob(2097152)
|
566
|
+
#
|
567
|
+
# add_column(:articles, :status, :string, limit: 20, default: 'draft', null: false)
|
568
|
+
# # ALTER TABLE "articles" ADD "status" varchar(20) DEFAULT 'draft' NOT NULL
|
569
|
+
#
|
570
|
+
# add_column(:answers, :bill_gates_money, :decimal, precision: 15, scale: 2)
|
571
|
+
# # ALTER TABLE "answers" ADD "bill_gates_money" decimal(15,2)
|
572
|
+
#
|
573
|
+
# add_column(:measurements, :sensor_reading, :decimal, precision: 30, scale: 20)
|
574
|
+
# # ALTER TABLE "measurements" ADD "sensor_reading" decimal(30,20)
|
575
|
+
#
|
576
|
+
# # While :scale defaults to zero on most databases, it
|
577
|
+
# # probably wouldn't hurt to include it.
|
578
|
+
# add_column(:measurements, :huge_integer, :decimal, precision: 30)
|
579
|
+
# # ALTER TABLE "measurements" ADD "huge_integer" decimal(30)
|
580
|
+
#
|
581
|
+
# # Defines a column that stores an array of a type.
|
582
|
+
# add_column(:users, :skills, :text, array: true)
|
583
|
+
# # ALTER TABLE "users" ADD "skills" text[]
|
584
|
+
#
|
585
|
+
# # Defines a column with a database-specific type.
|
586
|
+
# add_column(:shapes, :triangle, 'polygon')
|
587
|
+
# # ALTER TABLE "shapes" ADD "triangle" polygon
|
588
|
+
def add_column(table_name, column_name, type, **options)
|
379
589
|
at = create_alter_table table_name
|
380
590
|
at.add_column(column_name, type, options)
|
381
591
|
execute schema_creation.accept at
|
@@ -398,9 +608,10 @@ module ActiveRecord
|
|
398
608
|
#
|
399
609
|
# The +type+ and +options+ parameters will be ignored if present. It can be helpful
|
400
610
|
# to provide these in a migration's +change+ method so it can be reverted.
|
401
|
-
# In that case, +type+ and +options+ will be used by add_column.
|
611
|
+
# In that case, +type+ and +options+ will be used by #add_column.
|
612
|
+
# Indexes on the column are automatically removed.
|
402
613
|
def remove_column(table_name, column_name, type = nil, options = {})
|
403
|
-
execute "ALTER TABLE #{quote_table_name(table_name)}
|
614
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, options)}"
|
404
615
|
end
|
405
616
|
|
406
617
|
# Changes the column's definition according to the new options.
|
@@ -422,11 +633,16 @@ module ActiveRecord
|
|
422
633
|
#
|
423
634
|
# change_column_default(:users, :email, nil)
|
424
635
|
#
|
425
|
-
|
636
|
+
# Passing a hash containing +:from+ and +:to+ will make this change
|
637
|
+
# reversible in migration:
|
638
|
+
#
|
639
|
+
# change_column_default(:posts, :state, from: nil, to: "draft")
|
640
|
+
#
|
641
|
+
def change_column_default(table_name, column_name, default_or_changes)
|
426
642
|
raise NotImplementedError, "change_column_default is not implemented"
|
427
643
|
end
|
428
644
|
|
429
|
-
# Sets or removes a
|
645
|
+
# Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
|
430
646
|
# indicates whether the value can be +NULL+. For example
|
431
647
|
#
|
432
648
|
# change_column_null(:users, :nickname, false)
|
@@ -438,7 +654,7 @@ module ActiveRecord
|
|
438
654
|
# allows them to be +NULL+ (drops the constraint).
|
439
655
|
#
|
440
656
|
# The method accepts an optional fourth argument to replace existing
|
441
|
-
#
|
657
|
+
# <tt>NULL</tt>s with some other value. Use that one when enabling the
|
442
658
|
# constraint if needed, since otherwise those rows would not be valid.
|
443
659
|
#
|
444
660
|
# Please note the fourth argument does not set a column's default.
|
@@ -492,6 +708,8 @@ module ActiveRecord
|
|
492
708
|
#
|
493
709
|
# CREATE INDEX by_name ON accounts(name(10))
|
494
710
|
#
|
711
|
+
# ====== Creating an index with specific key lengths for multiple keys
|
712
|
+
#
|
495
713
|
# add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
|
496
714
|
#
|
497
715
|
# generates:
|
@@ -508,7 +726,7 @@ module ActiveRecord
|
|
508
726
|
#
|
509
727
|
# CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
|
510
728
|
#
|
511
|
-
# Note: MySQL
|
729
|
+
# Note: MySQL only supports index order from 8.0.1 onwards (earlier versions accepted the syntax but ignored it).
|
512
730
|
#
|
513
731
|
# ====== Creating a partial index
|
514
732
|
#
|
@@ -518,6 +736,8 @@ module ActiveRecord
|
|
518
736
|
#
|
519
737
|
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
|
520
738
|
#
|
739
|
+
# Note: Partial indexes are only supported for PostgreSQL and SQLite 3.8.0+.
|
740
|
+
#
|
521
741
|
# ====== Creating an index with a specific method
|
522
742
|
#
|
523
743
|
# add_index(:developers, :name, using: 'btree')
|
@@ -529,6 +749,19 @@ module ActiveRecord
|
|
529
749
|
#
|
530
750
|
# Note: only supported by PostgreSQL and MySQL
|
531
751
|
#
|
752
|
+
# ====== Creating an index with a specific operator class
|
753
|
+
#
|
754
|
+
# add_index(:developers, :name, using: 'gist', opclass: :gist_trgm_ops)
|
755
|
+
# # CREATE INDEX developers_on_name ON developers USING gist (name gist_trgm_ops) -- PostgreSQL
|
756
|
+
#
|
757
|
+
# add_index(:developers, [:name, :city], using: 'gist', opclass: { city: :gist_trgm_ops })
|
758
|
+
# # CREATE INDEX developers_on_name_and_city ON developers USING gist (name, city gist_trgm_ops) -- PostgreSQL
|
759
|
+
#
|
760
|
+
# add_index(:developers, [:name, :city], using: 'gist', opclass: :gist_trgm_ops)
|
761
|
+
# # CREATE INDEX developers_on_name_and_city ON developers USING gist (name gist_trgm_ops, city gist_trgm_ops) -- PostgreSQL
|
762
|
+
#
|
763
|
+
# Note: only supported by PostgreSQL
|
764
|
+
#
|
532
765
|
# ====== Creating an index with a specific type
|
533
766
|
#
|
534
767
|
# add_index(:developers, :name, type: :fulltext)
|
@@ -537,7 +770,18 @@ module ActiveRecord
|
|
537
770
|
#
|
538
771
|
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
|
539
772
|
#
|
540
|
-
# Note: only supported by MySQL.
|
773
|
+
# Note: only supported by MySQL.
|
774
|
+
#
|
775
|
+
# ====== Creating an index with a specific algorithm
|
776
|
+
#
|
777
|
+
# add_index(:developers, :name, algorithm: :concurrently)
|
778
|
+
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
|
779
|
+
#
|
780
|
+
# Note: only supported by PostgreSQL.
|
781
|
+
#
|
782
|
+
# Concurrently adding an index is not supported in a transaction.
|
783
|
+
#
|
784
|
+
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
541
785
|
def add_index(table_name, column_name, options = {})
|
542
786
|
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
|
543
787
|
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
|
@@ -545,15 +789,15 @@ module ActiveRecord
|
|
545
789
|
|
546
790
|
# Removes the given index from the table.
|
547
791
|
#
|
548
|
-
# Removes the +
|
792
|
+
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
549
793
|
#
|
550
|
-
# remove_index :accounts, :
|
794
|
+
# remove_index :accounts, :branch_id
|
551
795
|
#
|
552
|
-
# Removes the index
|
796
|
+
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
553
797
|
#
|
554
798
|
# remove_index :accounts, column: :branch_id
|
555
799
|
#
|
556
|
-
# Removes the index
|
800
|
+
# Removes the index on +branch_id+ and +party_id+ in the +accounts+ table if exactly one such index exists.
|
557
801
|
#
|
558
802
|
# remove_index :accounts, column: [:branch_id, :party_id]
|
559
803
|
#
|
@@ -561,11 +805,17 @@ module ActiveRecord
|
|
561
805
|
#
|
562
806
|
# remove_index :accounts, name: :by_branch_party
|
563
807
|
#
|
808
|
+
# Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
|
809
|
+
#
|
810
|
+
# remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
|
811
|
+
#
|
812
|
+
# Note: only supported by PostgreSQL.
|
813
|
+
#
|
814
|
+
# Concurrently removing an index is not supported in a transaction.
|
815
|
+
#
|
816
|
+
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
564
817
|
def remove_index(table_name, options = {})
|
565
|
-
|
566
|
-
end
|
567
|
-
|
568
|
-
def remove_index!(table_name, index_name) #:nodoc:
|
818
|
+
index_name = index_name_for_remove(table_name, options)
|
569
819
|
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
570
820
|
end
|
571
821
|
|
@@ -576,10 +826,9 @@ module ActiveRecord
|
|
576
826
|
# rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
|
577
827
|
#
|
578
828
|
def rename_index(table_name, old_name, new_name)
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
# this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
|
829
|
+
validate_index_length!(table_name, new_name)
|
830
|
+
|
831
|
+
# this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
|
583
832
|
old_index_def = indexes(table_name).detect { |i| i.name == old_name }
|
584
833
|
return unless old_index_def
|
585
834
|
add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
|
@@ -596,70 +845,101 @@ module ActiveRecord
|
|
596
845
|
raise ArgumentError, "You must specify the index name"
|
597
846
|
end
|
598
847
|
else
|
599
|
-
index_name(table_name,
|
848
|
+
index_name(table_name, index_name_options(options))
|
600
849
|
end
|
601
850
|
end
|
602
851
|
|
603
852
|
# Verifies the existence of an index with a given name.
|
604
|
-
|
605
|
-
# The default argument is returned if the underlying implementation does not define the indexes method,
|
606
|
-
# as there's no way to determine the correct answer in that case.
|
607
|
-
def index_name_exists?(table_name, index_name, default)
|
608
|
-
return default unless respond_to?(:indexes)
|
853
|
+
def index_name_exists?(table_name, index_name)
|
609
854
|
index_name = index_name.to_s
|
610
855
|
indexes(table_name).detect { |i| i.name == index_name }
|
611
856
|
end
|
612
857
|
|
613
|
-
# Adds a reference.
|
614
|
-
#
|
615
|
-
# a
|
616
|
-
#
|
617
|
-
#
|
618
|
-
# ====== Create a user_id integer column
|
858
|
+
# Adds a reference. The reference column is a bigint by default,
|
859
|
+
# the <tt>:type</tt> option can be used to specify a different type.
|
860
|
+
# Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
|
861
|
+
# #add_reference and #add_belongs_to are acceptable.
|
619
862
|
#
|
620
|
-
#
|
863
|
+
# The +options+ hash can include the following keys:
|
864
|
+
# [<tt>:type</tt>]
|
865
|
+
# The reference column type. Defaults to +:bigint+.
|
866
|
+
# [<tt>:index</tt>]
|
867
|
+
# Add an appropriate index. Defaults to true.
|
868
|
+
# See #add_index for usage of this option.
|
869
|
+
# [<tt>:foreign_key</tt>]
|
870
|
+
# Add an appropriate foreign key constraint. Defaults to false.
|
871
|
+
# [<tt>:polymorphic</tt>]
|
872
|
+
# Whether an additional +_type+ column should be added. Defaults to false.
|
873
|
+
# [<tt>:null</tt>]
|
874
|
+
# Whether the column allows nulls. Defaults to true.
|
875
|
+
#
|
876
|
+
# ====== Create a user_id bigint column without an index
|
877
|
+
#
|
878
|
+
# add_reference(:products, :user, index: false)
|
621
879
|
#
|
622
880
|
# ====== Create a user_id string column
|
623
881
|
#
|
624
882
|
# add_reference(:products, :user, type: :string)
|
625
883
|
#
|
626
|
-
# ====== Create
|
884
|
+
# ====== Create supplier_id, supplier_type columns
|
885
|
+
#
|
886
|
+
# add_reference(:products, :supplier, polymorphic: true)
|
887
|
+
#
|
888
|
+
# ====== Create a supplier_id column with a unique index
|
889
|
+
#
|
890
|
+
# add_reference(:products, :supplier, index: { unique: true })
|
891
|
+
#
|
892
|
+
# ====== Create a supplier_id column with a named index
|
627
893
|
#
|
628
|
-
#
|
894
|
+
# add_reference(:products, :supplier, index: { name: "my_supplier_index" })
|
629
895
|
#
|
630
|
-
# ====== Create a supplier_id
|
896
|
+
# ====== Create a supplier_id column and appropriate foreign key
|
631
897
|
#
|
632
|
-
# add_reference(:products, :supplier,
|
898
|
+
# add_reference(:products, :supplier, foreign_key: true)
|
633
899
|
#
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
add_index(table_name, polymorphic ? %w[type id].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
|
900
|
+
# ====== Create a supplier_id column and a foreign key to the firms table
|
901
|
+
#
|
902
|
+
# add_reference(:products, :supplier, foreign_key: {to_table: :firms})
|
903
|
+
#
|
904
|
+
def add_reference(table_name, ref_name, **options)
|
905
|
+
ReferenceDefinition.new(ref_name, options).add_to(update_table_definition(table_name, self))
|
641
906
|
end
|
642
907
|
alias :add_belongs_to :add_reference
|
643
908
|
|
644
909
|
# Removes the reference(s). Also removes a +type+ column if one exists.
|
645
|
-
#
|
910
|
+
# #remove_reference and #remove_belongs_to are acceptable.
|
646
911
|
#
|
647
912
|
# ====== Remove the reference
|
648
913
|
#
|
649
|
-
# remove_reference(:products, :user, index:
|
914
|
+
# remove_reference(:products, :user, index: false)
|
650
915
|
#
|
651
916
|
# ====== Remove polymorphic reference
|
652
917
|
#
|
653
918
|
# remove_reference(:products, :supplier, polymorphic: true)
|
654
919
|
#
|
655
|
-
|
920
|
+
# ====== Remove the reference with a foreign key
|
921
|
+
#
|
922
|
+
# remove_reference(:products, :user, foreign_key: true)
|
923
|
+
#
|
924
|
+
def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
|
925
|
+
if foreign_key
|
926
|
+
reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
|
927
|
+
if foreign_key.is_a?(Hash)
|
928
|
+
foreign_key_options = foreign_key
|
929
|
+
else
|
930
|
+
foreign_key_options = { to_table: reference_name }
|
931
|
+
end
|
932
|
+
foreign_key_options[:column] ||= "#{ref_name}_id"
|
933
|
+
remove_foreign_key(table_name, foreign_key_options)
|
934
|
+
end
|
935
|
+
|
656
936
|
remove_column(table_name, "#{ref_name}_id")
|
657
|
-
remove_column(table_name, "#{ref_name}_type") if
|
937
|
+
remove_column(table_name, "#{ref_name}_type") if polymorphic
|
658
938
|
end
|
659
939
|
alias :remove_belongs_to :remove_reference
|
660
940
|
|
661
941
|
# Returns an array of foreign keys for the given table.
|
662
|
-
# The foreign keys are represented as
|
942
|
+
# The foreign keys are represented as ForeignKeyDefinition objects.
|
663
943
|
def foreign_keys(table_name)
|
664
944
|
raise NotImplementedError, "foreign_keys is not implemented"
|
665
945
|
end
|
@@ -668,8 +948,8 @@ module ActiveRecord
|
|
668
948
|
# +to_table+ contains the referenced primary key.
|
669
949
|
#
|
670
950
|
# The foreign key will be named after the following pattern: <tt>fk_rails_<identifier></tt>.
|
671
|
-
# +identifier+ is a 10 character long
|
672
|
-
# the <tt>:name</tt> option.
|
951
|
+
# +identifier+ is a 10 character long string which is deterministically generated from the
|
952
|
+
# +from_table+ and +column+. A custom name can be specified with the <tt>:name</tt> option.
|
673
953
|
#
|
674
954
|
# ====== Creating a simple foreign key
|
675
955
|
#
|
@@ -677,7 +957,7 @@ module ActiveRecord
|
|
677
957
|
#
|
678
958
|
# generates:
|
679
959
|
#
|
680
|
-
# ALTER TABLE "articles" ADD CONSTRAINT
|
960
|
+
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
|
681
961
|
#
|
682
962
|
# ====== Creating a foreign key on a specific column
|
683
963
|
#
|
@@ -693,7 +973,7 @@ module ActiveRecord
|
|
693
973
|
#
|
694
974
|
# generates:
|
695
975
|
#
|
696
|
-
# ALTER TABLE "articles" ADD CONSTRAINT
|
976
|
+
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE
|
697
977
|
#
|
698
978
|
# The +options+ hash can include the following keys:
|
699
979
|
# [<tt>:column</tt>]
|
@@ -703,28 +983,25 @@ module ActiveRecord
|
|
703
983
|
# [<tt>:name</tt>]
|
704
984
|
# The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
|
705
985
|
# [<tt>:on_delete</tt>]
|
706
|
-
# Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade
|
986
|
+
# Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
|
707
987
|
# [<tt>:on_update</tt>]
|
708
|
-
# Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade
|
988
|
+
# Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
|
989
|
+
# [<tt>:validate</tt>]
|
990
|
+
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
709
991
|
def add_foreign_key(from_table, to_table, options = {})
|
710
992
|
return unless supports_foreign_keys?
|
711
993
|
|
712
|
-
options
|
713
|
-
|
714
|
-
options = {
|
715
|
-
column: options[:column],
|
716
|
-
primary_key: options[:primary_key],
|
717
|
-
name: foreign_key_name(from_table, options),
|
718
|
-
on_delete: options[:on_delete],
|
719
|
-
on_update: options[:on_update]
|
720
|
-
}
|
994
|
+
options = foreign_key_options(from_table, to_table, options)
|
721
995
|
at = create_alter_table from_table
|
722
996
|
at.add_foreign_key to_table, options
|
723
997
|
|
724
998
|
execute schema_creation.accept(at)
|
725
999
|
end
|
726
1000
|
|
727
|
-
# Removes the given foreign key from the table.
|
1001
|
+
# Removes the given foreign key from the table. Any option parameters provided
|
1002
|
+
# will be used to re-add the foreign key in case of a migration rollback.
|
1003
|
+
# It is recommended that you provide any options used when creating the foreign
|
1004
|
+
# key so that the migration can be reverted properly.
|
728
1005
|
#
|
729
1006
|
# Removes the foreign key on +accounts.branch_id+.
|
730
1007
|
#
|
@@ -734,28 +1011,22 @@ module ActiveRecord
|
|
734
1011
|
#
|
735
1012
|
# remove_foreign_key :accounts, column: :owner_id
|
736
1013
|
#
|
1014
|
+
# Removes the foreign key on +accounts.owner_id+.
|
1015
|
+
#
|
1016
|
+
# remove_foreign_key :accounts, to_table: :owners
|
1017
|
+
#
|
737
1018
|
# Removes the foreign key named +special_fk_name+ on the +accounts+ table.
|
738
1019
|
#
|
739
1020
|
# remove_foreign_key :accounts, name: :special_fk_name
|
740
1021
|
#
|
741
|
-
|
1022
|
+
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
|
1023
|
+
# with an addition of
|
1024
|
+
# [<tt>:to_table</tt>]
|
1025
|
+
# The name of the table that contains the referenced primary key.
|
1026
|
+
def remove_foreign_key(from_table, to_table = nil, **options)
|
742
1027
|
return unless supports_foreign_keys?
|
743
1028
|
|
744
|
-
|
745
|
-
options = options_or_to_table
|
746
|
-
else
|
747
|
-
options = { column: foreign_key_column_for(options_or_to_table) }
|
748
|
-
end
|
749
|
-
|
750
|
-
fk_name_to_delete = options.fetch(:name) do
|
751
|
-
fk_to_delete = foreign_keys(from_table).detect {|fk| fk.column == options[:column].to_s }
|
752
|
-
|
753
|
-
if fk_to_delete
|
754
|
-
fk_to_delete.name
|
755
|
-
else
|
756
|
-
raise ArgumentError, "Table '#{from_table}' has no foreign key on column '#{options[:column]}'"
|
757
|
-
end
|
758
|
-
end
|
1029
|
+
fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
|
759
1030
|
|
760
1031
|
at = create_alter_table from_table
|
761
1032
|
at.drop_foreign_key fk_name_to_delete
|
@@ -763,52 +1034,71 @@ module ActiveRecord
|
|
763
1034
|
execute schema_creation.accept(at)
|
764
1035
|
end
|
765
1036
|
|
1037
|
+
# Checks to see if a foreign key exists on a table for a given foreign key definition.
|
1038
|
+
#
|
1039
|
+
# # Checks to see if a foreign key exists.
|
1040
|
+
# foreign_key_exists?(:accounts, :branches)
|
1041
|
+
#
|
1042
|
+
# # Checks to see if a foreign key on a specified column exists.
|
1043
|
+
# foreign_key_exists?(:accounts, column: :owner_id)
|
1044
|
+
#
|
1045
|
+
# # Checks to see if a foreign key with a custom name exists.
|
1046
|
+
# foreign_key_exists?(:accounts, name: "special_fk_name")
|
1047
|
+
#
|
1048
|
+
def foreign_key_exists?(from_table, to_table = nil, **options)
|
1049
|
+
foreign_key_for(from_table, to_table: to_table, **options).present?
|
1050
|
+
end
|
1051
|
+
|
766
1052
|
def foreign_key_column_for(table_name) # :nodoc:
|
767
|
-
|
1053
|
+
name = strip_table_name_prefix_and_suffix(table_name)
|
1054
|
+
"#{name.singularize}_id"
|
768
1055
|
end
|
769
1056
|
|
770
|
-
def
|
771
|
-
|
1057
|
+
def foreign_key_options(from_table, to_table, options) # :nodoc:
|
1058
|
+
options = options.dup
|
1059
|
+
options[:column] ||= foreign_key_column_for(to_table)
|
1060
|
+
options[:name] ||= foreign_key_name(from_table, options)
|
1061
|
+
options
|
1062
|
+
end
|
772
1063
|
|
773
|
-
|
774
|
-
|
775
|
-
|
1064
|
+
def dump_schema_information # :nodoc:
|
1065
|
+
versions = schema_migration.all_versions
|
1066
|
+
insert_versions_sql(versions) if versions.any?
|
776
1067
|
end
|
777
1068
|
|
778
|
-
|
779
|
-
|
780
|
-
def initialize_schema_migrations_table
|
781
|
-
ActiveRecord::SchemaMigration.create_table
|
1069
|
+
def internal_string_options_for_primary_key # :nodoc:
|
1070
|
+
{ primary_key: true }
|
782
1071
|
end
|
783
1072
|
|
784
|
-
def assume_migrated_upto_version(version, migrations_paths =
|
785
|
-
|
1073
|
+
def assume_migrated_upto_version(version, migrations_paths = nil)
|
1074
|
+
unless migrations_paths.nil?
|
1075
|
+
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
1076
|
+
Passing migrations_paths to #assume_migrated_upto_version is deprecated and will be removed in Rails 6.1.
|
1077
|
+
MSG
|
1078
|
+
end
|
1079
|
+
|
786
1080
|
version = version.to_i
|
787
|
-
sm_table = quote_table_name(
|
1081
|
+
sm_table = quote_table_name(schema_migration.table_name)
|
788
1082
|
|
789
|
-
migrated =
|
790
|
-
|
791
|
-
versions = Dir[*paths].map do |filename|
|
792
|
-
filename.split('/').last.split('_').first.to_i
|
793
|
-
end
|
1083
|
+
migrated = migration_context.get_all_versions
|
1084
|
+
versions = migration_context.migrations.map(&:version)
|
794
1085
|
|
795
1086
|
unless migrated.include?(version)
|
796
|
-
execute "INSERT INTO #{sm_table} (version) VALUES (
|
1087
|
+
execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
|
797
1088
|
end
|
798
1089
|
|
799
|
-
|
800
|
-
|
801
|
-
if
|
802
|
-
raise "Duplicate migration #{
|
803
|
-
elsif v < version
|
804
|
-
execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
|
805
|
-
inserted << v
|
1090
|
+
inserting = (versions - migrated).select { |v| v < version }
|
1091
|
+
if inserting.any?
|
1092
|
+
if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
|
1093
|
+
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
|
806
1094
|
end
|
1095
|
+
execute insert_versions_sql(inserting)
|
807
1096
|
end
|
808
1097
|
end
|
809
1098
|
|
810
|
-
def type_to_sql(type, limit
|
811
|
-
|
1099
|
+
def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) # :nodoc:
|
1100
|
+
type = type.to_sym if type
|
1101
|
+
if native = native_database_types[type]
|
812
1102
|
column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
|
813
1103
|
|
814
1104
|
if type == :decimal # ignore limit, use precision and scale
|
@@ -824,6 +1114,12 @@ module ActiveRecord
|
|
824
1114
|
raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
|
825
1115
|
end
|
826
1116
|
|
1117
|
+
elsif [:datetime, :timestamp, :time, :interval].include?(type) && precision ||= native[:precision]
|
1118
|
+
if (0..6) === precision
|
1119
|
+
column_type_sql << "(#{precision})"
|
1120
|
+
else
|
1121
|
+
raise ArgumentError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6"
|
1122
|
+
end
|
827
1123
|
elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
|
828
1124
|
column_type_sql << "(#{limit})"
|
829
1125
|
end
|
@@ -835,22 +1131,27 @@ module ActiveRecord
|
|
835
1131
|
end
|
836
1132
|
|
837
1133
|
# Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
|
838
|
-
#
|
1134
|
+
# PostgreSQL, MySQL, and Oracle override this for custom DISTINCT syntax - they
|
839
1135
|
# require the order columns appear in the SELECT.
|
840
1136
|
#
|
841
1137
|
# columns_for_distinct("posts.id", ["posts.created_at desc"])
|
842
|
-
|
1138
|
+
#
|
1139
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
843
1140
|
columns
|
844
1141
|
end
|
845
1142
|
|
846
|
-
include TimestampDefaultDeprecation
|
847
1143
|
# Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
|
848
|
-
# Additional options (like
|
1144
|
+
# Additional options (like +:null+) are forwarded to #add_column.
|
849
1145
|
#
|
850
|
-
# add_timestamps(:suppliers, null:
|
1146
|
+
# add_timestamps(:suppliers, null: true)
|
851
1147
|
#
|
852
1148
|
def add_timestamps(table_name, options = {})
|
853
|
-
|
1149
|
+
options[:null] = false if options[:null].nil?
|
1150
|
+
|
1151
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
1152
|
+
options[:precision] = 6
|
1153
|
+
end
|
1154
|
+
|
854
1155
|
add_column table_name, :created_at, :datetime, options
|
855
1156
|
add_column table_name, :updated_at, :datetime, options
|
856
1157
|
end
|
@@ -868,16 +1169,15 @@ module ActiveRecord
|
|
868
1169
|
Table.new(table_name, base)
|
869
1170
|
end
|
870
1171
|
|
871
|
-
def add_index_options(table_name, column_name,
|
872
|
-
column_names =
|
873
|
-
index_name = index_name(table_name, column: column_names)
|
1172
|
+
def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
|
1173
|
+
column_names = index_column_names(column_name)
|
874
1174
|
|
875
|
-
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
|
1175
|
+
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type, :opclass)
|
876
1176
|
|
877
|
-
index_type = options[:unique] ? "UNIQUE" : ""
|
878
1177
|
index_type = options[:type].to_s if options.key?(:type)
|
1178
|
+
index_type ||= options[:unique] ? "UNIQUE" : ""
|
879
1179
|
index_name = options[:name].to_s if options.key?(:name)
|
880
|
-
|
1180
|
+
index_name ||= index_name(table_name, column_names)
|
881
1181
|
|
882
1182
|
if options.key?(:algorithm)
|
883
1183
|
algorithm = index_algorithms.fetch(options[:algorithm]) {
|
@@ -891,63 +1191,109 @@ module ActiveRecord
|
|
891
1191
|
index_options = options[:where] ? " WHERE #{options[:where]}" : ""
|
892
1192
|
end
|
893
1193
|
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
if table_exists?(table_name) && index_name_exists?(table_name, index_name, false)
|
1194
|
+
validate_index_length!(table_name, index_name, options.fetch(:internal, false))
|
1195
|
+
|
1196
|
+
if data_source_exists?(table_name) && index_name_exists?(table_name, index_name)
|
898
1197
|
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
|
899
1198
|
end
|
900
1199
|
index_columns = quoted_columns_for_index(column_names, options).join(", ")
|
901
1200
|
|
902
|
-
[index_name, index_type, index_columns, index_options, algorithm, using]
|
1201
|
+
[index_name, index_type, index_columns, index_options, algorithm, using, comment]
|
903
1202
|
end
|
904
1203
|
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
1204
|
+
def options_include_default?(options)
|
1205
|
+
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
|
1206
|
+
end
|
1207
|
+
|
1208
|
+
# Changes the comment for a table or removes it if +nil+.
|
1209
|
+
#
|
1210
|
+
# Passing a hash containing +:from+ and +:to+ will make this change
|
1211
|
+
# reversible in migration:
|
1212
|
+
#
|
1213
|
+
# change_table_comment(:posts, from: "old_comment", to: "new_comment")
|
1214
|
+
def change_table_comment(table_name, comment_or_changes)
|
1215
|
+
raise NotImplementedError, "#{self.class} does not support changing table comments"
|
1216
|
+
end
|
915
1217
|
|
916
|
-
|
1218
|
+
# Changes the comment for a column or removes it if +nil+.
|
1219
|
+
#
|
1220
|
+
# Passing a hash containing +:from+ and +:to+ will make this change
|
1221
|
+
# reversible in migration:
|
1222
|
+
#
|
1223
|
+
# change_column_comment(:posts, :state, from: "old_comment", to: "new_comment")
|
1224
|
+
def change_column_comment(table_name, column_name, comment_or_changes)
|
1225
|
+
raise NotImplementedError, "#{self.class} does not support changing column comments"
|
1226
|
+
end
|
1227
|
+
|
1228
|
+
def create_schema_dumper(options) # :nodoc:
|
1229
|
+
SchemaDumper.create(self, options)
|
1230
|
+
end
|
1231
|
+
|
1232
|
+
private
|
1233
|
+
def column_options_keys
|
1234
|
+
[:limit, :precision, :scale, :default, :null, :collation, :comment]
|
917
1235
|
end
|
918
1236
|
|
919
|
-
|
920
|
-
|
921
|
-
|
1237
|
+
def add_index_sort_order(quoted_columns, **options)
|
1238
|
+
orders = options_for_index_columns(options[:order])
|
1239
|
+
quoted_columns.each do |name, column|
|
1240
|
+
column << " #{orders[name].upcase}" if orders[name].present?
|
1241
|
+
end
|
1242
|
+
end
|
1243
|
+
|
1244
|
+
def options_for_index_columns(options)
|
1245
|
+
if options.is_a?(Hash)
|
1246
|
+
options.symbolize_keys
|
1247
|
+
else
|
1248
|
+
Hash.new { |hash, column| hash[column] = options }
|
1249
|
+
end
|
1250
|
+
end
|
922
1251
|
|
923
|
-
|
1252
|
+
# Overridden by the MySQL adapter for supporting index lengths and by
|
1253
|
+
# the PostgreSQL adapter for supporting operator classes.
|
1254
|
+
def add_options_for_index_columns(quoted_columns, **options)
|
924
1255
|
if supports_index_sort_order?
|
925
|
-
|
1256
|
+
quoted_columns = add_index_sort_order(quoted_columns, options)
|
926
1257
|
end
|
927
1258
|
|
928
|
-
|
1259
|
+
quoted_columns
|
929
1260
|
end
|
930
1261
|
|
931
|
-
def
|
932
|
-
|
1262
|
+
def quoted_columns_for_index(column_names, **options)
|
1263
|
+
return [column_names] if column_names.is_a?(String)
|
1264
|
+
|
1265
|
+
quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
|
1266
|
+
add_options_for_index_columns(quoted_columns, options).values
|
933
1267
|
end
|
934
1268
|
|
935
1269
|
def index_name_for_remove(table_name, options = {})
|
936
|
-
|
1270
|
+
return options[:name] if can_remove_index_by_name?(options)
|
937
1271
|
|
938
|
-
|
939
|
-
if options.is_a?(Hash) && options.has_key?(:name)
|
940
|
-
options_without_column = options.dup
|
941
|
-
options_without_column.delete :column
|
942
|
-
index_name_without_column = index_name(table_name, options_without_column)
|
1272
|
+
checks = []
|
943
1273
|
|
944
|
-
|
945
|
-
|
1274
|
+
if options.is_a?(Hash)
|
1275
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
|
1276
|
+
column_names = index_column_names(options[:column])
|
1277
|
+
else
|
1278
|
+
column_names = index_column_names(options)
|
1279
|
+
end
|
946
1280
|
|
947
|
-
|
1281
|
+
if column_names.present?
|
1282
|
+
checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
|
948
1283
|
end
|
949
1284
|
|
950
|
-
|
1285
|
+
raise ArgumentError, "No name or columns specified" if checks.none?
|
1286
|
+
|
1287
|
+
matching_indexes = indexes(table_name).select { |i| checks.all? { |check| check[i] } }
|
1288
|
+
|
1289
|
+
if matching_indexes.count > 1
|
1290
|
+
raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
|
1291
|
+
"Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
|
1292
|
+
elsif matching_indexes.none?
|
1293
|
+
raise ArgumentError, "No indexes found on #{table_name} with the options provided."
|
1294
|
+
else
|
1295
|
+
matching_indexes.first.name
|
1296
|
+
end
|
951
1297
|
end
|
952
1298
|
|
953
1299
|
def rename_table_indexes(table_name, new_name)
|
@@ -972,20 +1318,158 @@ module ActiveRecord
|
|
972
1318
|
end
|
973
1319
|
end
|
974
1320
|
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
end
|
1321
|
+
def schema_creation
|
1322
|
+
SchemaCreation.new(self)
|
1323
|
+
end
|
979
1324
|
|
980
|
-
|
981
|
-
|
982
|
-
|
1325
|
+
def create_table_definition(*args)
|
1326
|
+
TableDefinition.new(self, *args)
|
1327
|
+
end
|
983
1328
|
|
984
|
-
|
985
|
-
|
986
|
-
|
1329
|
+
def create_alter_table(name)
|
1330
|
+
AlterTable.new create_table_definition(name)
|
1331
|
+
end
|
1332
|
+
|
1333
|
+
def fetch_type_metadata(sql_type)
|
1334
|
+
cast_type = lookup_cast_type(sql_type)
|
1335
|
+
SqlTypeMetadata.new(
|
1336
|
+
sql_type: sql_type,
|
1337
|
+
type: cast_type.type,
|
1338
|
+
limit: cast_type.limit,
|
1339
|
+
precision: cast_type.precision,
|
1340
|
+
scale: cast_type.scale,
|
1341
|
+
)
|
1342
|
+
end
|
1343
|
+
|
1344
|
+
def index_column_names(column_names)
|
1345
|
+
if column_names.is_a?(String) && /\W/.match?(column_names)
|
1346
|
+
column_names
|
1347
|
+
else
|
1348
|
+
Array(column_names)
|
1349
|
+
end
|
1350
|
+
end
|
1351
|
+
|
1352
|
+
def index_name_options(column_names)
|
1353
|
+
if column_names.is_a?(String) && /\W/.match?(column_names)
|
1354
|
+
column_names = column_names.scan(/\w+/).join("_")
|
1355
|
+
end
|
1356
|
+
|
1357
|
+
{ column: column_names }
|
1358
|
+
end
|
1359
|
+
|
1360
|
+
def strip_table_name_prefix_and_suffix(table_name)
|
1361
|
+
prefix = Base.table_name_prefix
|
1362
|
+
suffix = Base.table_name_suffix
|
1363
|
+
table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
|
1364
|
+
end
|
1365
|
+
|
1366
|
+
def foreign_key_name(table_name, options)
|
1367
|
+
options.fetch(:name) do
|
1368
|
+
identifier = "#{table_name}_#{options.fetch(:column)}_fk"
|
1369
|
+
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
|
1370
|
+
|
1371
|
+
"fk_rails_#{hashed_identifier}"
|
1372
|
+
end
|
1373
|
+
end
|
1374
|
+
|
1375
|
+
def foreign_key_for(from_table, **options)
|
1376
|
+
return unless supports_foreign_keys?
|
1377
|
+
foreign_keys(from_table).detect { |fk| fk.defined_for?(options) }
|
1378
|
+
end
|
1379
|
+
|
1380
|
+
def foreign_key_for!(from_table, to_table: nil, **options)
|
1381
|
+
foreign_key_for(from_table, to_table: to_table, **options) ||
|
1382
|
+
raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
1383
|
+
end
|
1384
|
+
|
1385
|
+
def extract_foreign_key_action(specifier)
|
1386
|
+
case specifier
|
1387
|
+
when "CASCADE"; :cascade
|
1388
|
+
when "SET NULL"; :nullify
|
1389
|
+
when "RESTRICT"; :restrict
|
1390
|
+
end
|
1391
|
+
end
|
1392
|
+
|
1393
|
+
def validate_index_length!(table_name, new_name, internal = false)
|
1394
|
+
max_index_length = internal ? index_name_length : allowed_index_name_length
|
1395
|
+
|
1396
|
+
if new_name.length > max_index_length
|
1397
|
+
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
|
1398
|
+
end
|
1399
|
+
end
|
1400
|
+
|
1401
|
+
def extract_new_default_value(default_or_changes)
|
1402
|
+
if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
|
1403
|
+
default_or_changes[:to]
|
1404
|
+
else
|
1405
|
+
default_or_changes
|
1406
|
+
end
|
1407
|
+
end
|
1408
|
+
alias :extract_new_comment_value :extract_new_default_value
|
1409
|
+
|
1410
|
+
def can_remove_index_by_name?(options)
|
1411
|
+
options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
|
1412
|
+
end
|
1413
|
+
|
1414
|
+
def bulk_change_table(table_name, operations)
|
1415
|
+
sql_fragments = []
|
1416
|
+
non_combinable_operations = []
|
1417
|
+
|
1418
|
+
operations.each do |command, args|
|
1419
|
+
table, arguments = args.shift, args
|
1420
|
+
method = :"#{command}_for_alter"
|
1421
|
+
|
1422
|
+
if respond_to?(method, true)
|
1423
|
+
sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
|
1424
|
+
sql_fragments << sqls
|
1425
|
+
non_combinable_operations.concat(procs)
|
1426
|
+
else
|
1427
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
1428
|
+
non_combinable_operations.each(&:call)
|
1429
|
+
sql_fragments = []
|
1430
|
+
non_combinable_operations = []
|
1431
|
+
send(command, table, *arguments)
|
1432
|
+
end
|
1433
|
+
end
|
1434
|
+
|
1435
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
1436
|
+
non_combinable_operations.each(&:call)
|
1437
|
+
end
|
1438
|
+
|
1439
|
+
def add_column_for_alter(table_name, column_name, type, options = {})
|
1440
|
+
td = create_table_definition(table_name)
|
1441
|
+
cd = td.new_column_definition(column_name, type, options)
|
1442
|
+
schema_creation.accept(AddColumnDefinition.new(cd))
|
1443
|
+
end
|
1444
|
+
|
1445
|
+
def remove_column_for_alter(table_name, column_name, type = nil, options = {})
|
1446
|
+
"DROP COLUMN #{quote_column_name(column_name)}"
|
1447
|
+
end
|
1448
|
+
|
1449
|
+
def remove_columns_for_alter(table_name, *column_names)
|
1450
|
+
column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
|
1451
|
+
end
|
1452
|
+
|
1453
|
+
def insert_versions_sql(versions)
|
1454
|
+
sm_table = quote_table_name(schema_migration.table_name)
|
1455
|
+
|
1456
|
+
if versions.is_a?(Array)
|
1457
|
+
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
|
1458
|
+
sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
|
1459
|
+
sql << ";\n\n"
|
1460
|
+
sql
|
1461
|
+
else
|
1462
|
+
"INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
|
1463
|
+
end
|
1464
|
+
end
|
1465
|
+
|
1466
|
+
def data_source_sql(name = nil, type: nil)
|
1467
|
+
raise NotImplementedError
|
1468
|
+
end
|
1469
|
+
|
1470
|
+
def quoted_scope(name = nil, type: nil)
|
1471
|
+
raise NotImplementedError
|
987
1472
|
end
|
988
|
-
end
|
989
1473
|
end
|
990
1474
|
end
|
991
1475
|
end
|