activerecord 6.1.7 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +520 -1385
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +2 -12
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +60 -21
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +37 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +41 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +4 -4
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +46 -36
- data/lib/active_record/associations/collection_proxy.rb +44 -16
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +29 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +23 -15
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +212 -53
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -16
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +15 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +404 -509
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +2 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +47 -27
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +14 -11
- data/lib/active_record/attribute_methods/serialization.rb +174 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -9
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +164 -52
- data/lib/active_record/attributes.rb +51 -49
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- data/lib/active_record/callbacks.rb +18 -34
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +327 -612
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +377 -142
- data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
- data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +345 -166
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
- data/lib/active_record/connection_adapters/pool_config.rb +26 -16
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +401 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +518 -251
- data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +130 -6
- data/lib/active_record/connection_handling.rb +132 -146
- data/lib/active_record/core.rb +276 -251
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -3
- data/lib/active_record/database_configurations/database_config.rb +34 -10
- data/lib/active_record/database_configurations/hash_config.rb +107 -31
- data/lib/active_record/database_configurations/url_config.rb +38 -13
- data/lib/active_record/database_configurations.rb +96 -60
- data/lib/active_record/delegated_type.rb +90 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +3 -3
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +170 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +56 -0
- data/lib/active_record/enum.rb +163 -63
- data/lib/active_record/errors.rb +210 -27
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +179 -112
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +85 -31
- data/lib/active_record/insert_all.rb +148 -32
- data/lib/active_record/integration.rb +14 -10
- data/lib/active_record/internal_metadata.rb +123 -23
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +43 -27
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +41 -29
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +113 -16
- data/lib/active_record/migration/compatibility.rb +235 -46
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +374 -177
- data/lib/active_record/model_schema.rb +143 -159
- data/lib/active_record/nested_attributes.rb +48 -21
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +282 -283
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +189 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +44 -9
- data/lib/active_record/railtie.rb +234 -71
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +189 -256
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +325 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +300 -111
- data/lib/active_record/relation/delegation.rb +33 -22
- data/lib/active_record/relation/finder_methods.rb +123 -52
- data/lib/active_record/relation/merger.rb +26 -19
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +29 -22
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +842 -150
- data/lib/active_record/relation/record_fetch_warning.rb +10 -9
- data/lib/active_record/relation/spawn_methods.rb +7 -6
- data/lib/active_record/relation/where_clause.rb +15 -36
- data/lib/active_record/relation.rb +736 -145
- data/lib/active_record/result.rb +67 -54
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +84 -34
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +90 -31
- data/lib/active_record/schema_migration.rb +74 -23
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +30 -9
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +277 -149
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +173 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +118 -41
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -7
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -7
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +64 -15
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +444 -32
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +7 -2
- data/lib/arel/predications.rb +14 -4
- data/lib/arel/select_manager.rb +11 -5
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +20 -5
- data/lib/arel/visitors/dot.rb +81 -90
- data/lib/arel/visitors/mysql.rb +23 -5
- data/lib/arel/visitors/postgresql.rb +1 -22
- data/lib/arel/visitors/to_sql.rb +170 -36
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +23 -4
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +100 -14
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/string/access"
|
4
|
-
require "
|
4
|
+
require "openssl"
|
5
5
|
|
6
6
|
module ActiveRecord
|
7
7
|
module ConnectionAdapters # :nodoc:
|
@@ -29,7 +29,7 @@ module ActiveRecord
|
|
29
29
|
table_name[0...table_alias_length].tr(".", "_")
|
30
30
|
end
|
31
31
|
|
32
|
-
# Returns the relation names
|
32
|
+
# Returns the relation names usable to back Active Record models.
|
33
33
|
# For most adapters this means all #tables and #views.
|
34
34
|
def data_sources
|
35
35
|
query_values(data_source_sql, "SCHEMA")
|
@@ -96,25 +96,19 @@ module ActiveRecord
|
|
96
96
|
# # Check an index with a custom name exists
|
97
97
|
# index_exists?(:suppliers, :company_id, name: "idx_company_id")
|
98
98
|
#
|
99
|
+
# # Check a valid index exists (PostgreSQL only)
|
100
|
+
# index_exists?(:suppliers, :company_id, valid: true)
|
101
|
+
#
|
99
102
|
def index_exists?(table_name, column_name, **options)
|
100
|
-
|
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
|
-
|
107
|
-
checks << lambda { |i| i.unique } if options[:unique]
|
108
|
-
checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
|
109
|
-
|
110
|
-
indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
|
103
|
+
indexes(table_name).any? { |i| i.defined_for?(column_name, **options) }
|
111
104
|
end
|
112
105
|
|
113
106
|
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
114
107
|
def columns(table_name)
|
115
108
|
table_name = table_name.to_s
|
116
|
-
column_definitions(table_name)
|
117
|
-
|
109
|
+
definitions = column_definitions(table_name)
|
110
|
+
definitions.map do |field|
|
111
|
+
new_column_from_field(table_name, field, definitions)
|
118
112
|
end
|
119
113
|
end
|
120
114
|
|
@@ -124,6 +118,9 @@ module ActiveRecord
|
|
124
118
|
# column_exists?(:suppliers, :name)
|
125
119
|
#
|
126
120
|
# # Check a column exists of a particular type
|
121
|
+
# #
|
122
|
+
# # This works for standard non-casted types (eg. string) but is unreliable
|
123
|
+
# # for types that may get cast to something else (eg. char, bigint).
|
127
124
|
# column_exists?(:suppliers, :name, :string)
|
128
125
|
#
|
129
126
|
# # Check a column exists with a specific definition
|
@@ -260,7 +257,7 @@ module ActiveRecord
|
|
260
257
|
#
|
261
258
|
# generates:
|
262
259
|
#
|
263
|
-
# CREATE TABLE
|
260
|
+
# CREATE TABLE orders (
|
264
261
|
# product_id bigint NOT NULL,
|
265
262
|
# client_id bigint NOT NULL
|
266
263
|
# );
|
@@ -293,25 +290,15 @@ module ActiveRecord
|
|
293
290
|
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
294
291
|
#
|
295
292
|
# See also TableDefinition#column for details on how to create columns.
|
296
|
-
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
|
297
|
-
|
293
|
+
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)
|
294
|
+
validate_create_table_options!(options)
|
295
|
+
validate_table_length!(table_name) unless options[:_uses_legacy_table_name]
|
298
296
|
|
299
|
-
if
|
300
|
-
|
301
|
-
|
302
|
-
if id.is_a?(Hash)
|
303
|
-
options.merge!(id.except(:type))
|
304
|
-
id = id.fetch(:type, :primary_key)
|
305
|
-
end
|
306
|
-
|
307
|
-
if pk.is_a?(Array)
|
308
|
-
td.primary_keys pk
|
309
|
-
else
|
310
|
-
td.primary_key pk, id, **options
|
311
|
-
end
|
297
|
+
if force && options.key?(:if_not_exists)
|
298
|
+
raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
|
312
299
|
end
|
313
300
|
|
314
|
-
|
301
|
+
td = build_create_table_definition(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
|
315
302
|
|
316
303
|
if force
|
317
304
|
drop_table(table_name, force: force, if_exists: true)
|
@@ -319,7 +306,7 @@ module ActiveRecord
|
|
319
306
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
320
307
|
end
|
321
308
|
|
322
|
-
result = execute schema_creation.accept
|
309
|
+
result = execute schema_creation.accept(td)
|
323
310
|
|
324
311
|
unless supports_indexes_in_create?
|
325
312
|
td.indexes.each do |column_name, index_options|
|
@@ -340,6 +327,18 @@ module ActiveRecord
|
|
340
327
|
result
|
341
328
|
end
|
342
329
|
|
330
|
+
# Returns a TableDefinition object containing information about the table that would be created
|
331
|
+
# if the same arguments were passed to #create_table. See #create_table for information about
|
332
|
+
# passing a +table_name+, and other additional options that can be passed.
|
333
|
+
def build_create_table_definition(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
|
334
|
+
table_definition = create_table_definition(table_name, **options.extract!(*valid_table_definition_options, :_skip_validate_options))
|
335
|
+
table_definition.set_primary_key(table_name, id, primary_key, **options.extract!(*valid_primary_key_options, :_skip_validate_options))
|
336
|
+
|
337
|
+
yield table_definition if block_given?
|
338
|
+
|
339
|
+
table_definition
|
340
|
+
end
|
341
|
+
|
343
342
|
# Creates a new join table with the name created using the lexical order of the first two
|
344
343
|
# arguments. These arguments can be a String or a Symbol.
|
345
344
|
#
|
@@ -383,7 +382,7 @@ module ActiveRecord
|
|
383
382
|
|
384
383
|
column_options.reverse_merge!(null: false, index: false)
|
385
384
|
|
386
|
-
t1_ref, t2_ref = [table_1, table_2].map { |t| t
|
385
|
+
t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
|
387
386
|
|
388
387
|
create_table(join_table_name, **options.merge!(id: false)) do |td|
|
389
388
|
td.references t1_ref, **column_options
|
@@ -392,15 +391,33 @@ module ActiveRecord
|
|
392
391
|
end
|
393
392
|
end
|
394
393
|
|
394
|
+
# Builds a TableDefinition object for a join table.
|
395
|
+
#
|
396
|
+
# This definition object contains information about the table that would be created
|
397
|
+
# if the same arguments were passed to #create_join_table. See #create_join_table for
|
398
|
+
# information about what arguments should be passed.
|
399
|
+
def build_create_join_table_definition(table_1, table_2, column_options: {}, **options) # :nodoc:
|
400
|
+
join_table_name = find_join_table_name(table_1, table_2, options)
|
401
|
+
column_options.reverse_merge!(null: false, index: false)
|
402
|
+
|
403
|
+
t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
|
404
|
+
|
405
|
+
build_create_table_definition(join_table_name, **options.merge!(id: false)) do |td|
|
406
|
+
td.references t1_ref, **column_options
|
407
|
+
td.references t2_ref, **column_options
|
408
|
+
yield td if block_given?
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
395
412
|
# Drops the join table specified by the given arguments.
|
396
|
-
# See #create_join_table for details.
|
413
|
+
# See #create_join_table and #drop_table for details.
|
397
414
|
#
|
398
415
|
# Although this command ignores the block if one is given, it can be helpful
|
399
416
|
# to provide one in a migration's +change+ method so it can be reverted.
|
400
417
|
# In that case, the block will be used by #create_join_table.
|
401
418
|
def drop_join_table(table_1, table_2, **options)
|
402
419
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
403
|
-
drop_table(join_table_name)
|
420
|
+
drop_table(join_table_name, **options)
|
404
421
|
end
|
405
422
|
|
406
423
|
# A block for changing columns in +table+.
|
@@ -481,13 +498,13 @@ module ActiveRecord
|
|
481
498
|
# end
|
482
499
|
#
|
483
500
|
# See also Table for details on all of the various column transformations.
|
484
|
-
def change_table(table_name, **options)
|
501
|
+
def change_table(table_name, base = self, **options)
|
485
502
|
if supports_bulk_alter? && options[:bulk]
|
486
503
|
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
|
487
504
|
yield update_table_definition(table_name, recorder)
|
488
505
|
bulk_change_table(table_name, recorder.commands)
|
489
506
|
else
|
490
|
-
yield update_table_definition(table_name,
|
507
|
+
yield update_table_definition(table_name, base)
|
491
508
|
end
|
492
509
|
end
|
493
510
|
|
@@ -495,7 +512,7 @@ module ActiveRecord
|
|
495
512
|
#
|
496
513
|
# rename_table('octopuses', 'octopi')
|
497
514
|
#
|
498
|
-
def rename_table(table_name, new_name)
|
515
|
+
def rename_table(table_name, new_name, **)
|
499
516
|
raise NotImplementedError, "rename_table is not implemented"
|
500
517
|
end
|
501
518
|
|
@@ -518,24 +535,31 @@ module ActiveRecord
|
|
518
535
|
|
519
536
|
# Add a new +type+ column named +column_name+ to +table_name+.
|
520
537
|
#
|
538
|
+
# See {ActiveRecord::ConnectionAdapters::TableDefinition.column}[rdoc-ref:ActiveRecord::ConnectionAdapters::TableDefinition#column].
|
539
|
+
#
|
521
540
|
# The +type+ parameter is normally one of the migrations native types,
|
522
541
|
# which is one of the following:
|
523
542
|
# <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
|
524
543
|
# <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
|
525
544
|
# <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
|
526
|
-
# <tt>:binary</tt>, <tt>:boolean</tt>.
|
545
|
+
# <tt>:binary</tt>, <tt>:blob</tt>, <tt>:boolean</tt>.
|
527
546
|
#
|
528
547
|
# You may use a type not in this list as long as it is supported by your
|
529
548
|
# database (for example, "polygon" in MySQL), but this will not be database
|
530
549
|
# agnostic and should usually be avoided.
|
531
550
|
#
|
532
551
|
# Available options are (none of these exists by default):
|
552
|
+
# * <tt>:comment</tt> -
|
553
|
+
# Specifies the comment for the column. This option is ignored by some backends.
|
554
|
+
# * <tt>:collation</tt> -
|
555
|
+
# Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column.
|
556
|
+
# If not specified, the column will have the same collation as the table.
|
557
|
+
# * <tt>:default</tt> -
|
558
|
+
# The column's default value. Use +nil+ for +NULL+.
|
533
559
|
# * <tt>:limit</tt> -
|
534
560
|
# 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.
|
561
|
+
# and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, <tt>:blob</tt>, and <tt>:integer</tt> columns.
|
536
562
|
# This option is ignored by some backends.
|
537
|
-
# * <tt>:default</tt> -
|
538
|
-
# The column's default value. Use +nil+ for +NULL+.
|
539
563
|
# * <tt>:null</tt> -
|
540
564
|
# Allows or disallows +NULL+ values in the column.
|
541
565
|
# * <tt>:precision</tt> -
|
@@ -543,11 +567,6 @@ module ActiveRecord
|
|
543
567
|
# <tt>:datetime</tt>, and <tt>:time</tt> columns.
|
544
568
|
# * <tt>:scale</tt> -
|
545
569
|
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
546
|
-
# * <tt>:collation</tt> -
|
547
|
-
# Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column. If not specified, the
|
548
|
-
# column will have the same collation as the table.
|
549
|
-
# * <tt>:comment</tt> -
|
550
|
-
# Specifies the comment for the column. This option is ignored by some backends.
|
551
570
|
# * <tt>:if_not_exists</tt> -
|
552
571
|
# Specifies if the column already exists to not try to re-add it. This will avoid
|
553
572
|
# duplicate column errors.
|
@@ -563,7 +582,7 @@ module ActiveRecord
|
|
563
582
|
# * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
|
564
583
|
# <tt>:precision</tt>, and makes no comments about the requirements of
|
565
584
|
# <tt>:precision</tt>.
|
566
|
-
# * MySQL: <tt>:precision</tt> [1..
|
585
|
+
# * MySQL: <tt>:precision</tt> [1..65], <tt>:scale</tt> [0..30].
|
567
586
|
# Default is (10,0).
|
568
587
|
# * PostgreSQL: <tt>:precision</tt> [1..infinity],
|
569
588
|
# <tt>:scale</tt> [0..infinity]. No default.
|
@@ -604,11 +623,10 @@ module ActiveRecord
|
|
604
623
|
# # Ignores the method call if the column exists
|
605
624
|
# add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
|
606
625
|
def add_column(table_name, column_name, type, **options)
|
607
|
-
|
626
|
+
add_column_def = build_add_column_definition(table_name, column_name, type, **options)
|
627
|
+
return unless add_column_def
|
608
628
|
|
609
|
-
|
610
|
-
at.add_column(column_name, type, **options)
|
611
|
-
execute schema_creation.accept at
|
629
|
+
execute schema_creation.accept(add_column_def)
|
612
630
|
end
|
613
631
|
|
614
632
|
def add_columns(table_name, *column_names, type:, **options) # :nodoc:
|
@@ -617,6 +635,25 @@ module ActiveRecord
|
|
617
635
|
end
|
618
636
|
end
|
619
637
|
|
638
|
+
# Builds an AlterTable object for adding a column to a table.
|
639
|
+
#
|
640
|
+
# This definition object contains information about the column that would be created
|
641
|
+
# if the same arguments were passed to #add_column. See #add_column for information about
|
642
|
+
# passing a +table_name+, +column_name+, +type+ and other options that can be passed.
|
643
|
+
def build_add_column_definition(table_name, column_name, type, **options) # :nodoc:
|
644
|
+
return if options[:if_not_exists] == true && column_exists?(table_name, column_name)
|
645
|
+
|
646
|
+
if supports_datetime_with_precision?
|
647
|
+
if type == :datetime && !options.key?(:precision)
|
648
|
+
options[:precision] = 6
|
649
|
+
end
|
650
|
+
end
|
651
|
+
|
652
|
+
alter_table = create_alter_table(table_name)
|
653
|
+
alter_table.add_column(column_name, type, **options)
|
654
|
+
alter_table
|
655
|
+
end
|
656
|
+
|
620
657
|
# Removes the given columns from the table definition.
|
621
658
|
#
|
622
659
|
# remove_columns(:suppliers, :qualification, :experience)
|
@@ -629,9 +666,8 @@ module ActiveRecord
|
|
629
666
|
raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
|
630
667
|
end
|
631
668
|
|
632
|
-
column_names
|
633
|
-
|
634
|
-
end
|
669
|
+
remove_column_fragments = remove_columns_for_alter(table_name, *column_names, type: type, **options)
|
670
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_fragments.join(', ')}"
|
635
671
|
end
|
636
672
|
|
637
673
|
# Removes the column from the table definition.
|
@@ -641,7 +677,8 @@ module ActiveRecord
|
|
641
677
|
# The +type+ and +options+ parameters will be ignored if present. It can be helpful
|
642
678
|
# to provide these in a migration's +change+ method so it can be reverted.
|
643
679
|
# In that case, +type+ and +options+ will be used by #add_column.
|
644
|
-
#
|
680
|
+
# Depending on the database you're using, indexes using this column may be
|
681
|
+
# automatically removed or modified to remove this column from the index.
|
645
682
|
#
|
646
683
|
# If the options provided include an +if_exists+ key, it will be used to check if the
|
647
684
|
# column does not exist. This will silently ignore the migration rather than raising
|
@@ -682,6 +719,15 @@ module ActiveRecord
|
|
682
719
|
raise NotImplementedError, "change_column_default is not implemented"
|
683
720
|
end
|
684
721
|
|
722
|
+
# Builds a ChangeColumnDefaultDefinition object.
|
723
|
+
#
|
724
|
+
# This definition object contains information about the column change that would occur
|
725
|
+
# if the same arguments were passed to #change_column_default. See #change_column_default for
|
726
|
+
# information about passing a +table_name+, +column_name+, +type+ and other options that can be passed.
|
727
|
+
def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
|
728
|
+
raise NotImplementedError, "build_change_column_default_definition is not implemented"
|
729
|
+
end
|
730
|
+
|
685
731
|
# Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
|
686
732
|
# indicates whether the value can be +NULL+. For example
|
687
733
|
#
|
@@ -766,7 +812,7 @@ module ActiveRecord
|
|
766
812
|
#
|
767
813
|
# CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
|
768
814
|
#
|
769
|
-
# Note:
|
815
|
+
# Note: only supported by MySQL
|
770
816
|
#
|
771
817
|
# ====== Creating an index with a sort order (desc or asc, asc is the default)
|
772
818
|
#
|
@@ -788,6 +834,16 @@ module ActiveRecord
|
|
788
834
|
#
|
789
835
|
# Note: Partial indexes are only supported for PostgreSQL and SQLite.
|
790
836
|
#
|
837
|
+
# ====== Creating an index that includes additional columns
|
838
|
+
#
|
839
|
+
# add_index(:accounts, :branch_id, include: :party_id)
|
840
|
+
#
|
841
|
+
# generates:
|
842
|
+
#
|
843
|
+
# CREATE INDEX index_accounts_on_branch_id ON accounts USING btree(branch_id) INCLUDE (party_id)
|
844
|
+
#
|
845
|
+
# Note: only supported by PostgreSQL.
|
846
|
+
#
|
791
847
|
# ====== Creating an index with a specific method
|
792
848
|
#
|
793
849
|
# add_index(:developers, :name, using: 'btree')
|
@@ -825,20 +881,31 @@ module ActiveRecord
|
|
825
881
|
# ====== Creating an index with a specific algorithm
|
826
882
|
#
|
827
883
|
# add_index(:developers, :name, algorithm: :concurrently)
|
828
|
-
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
|
884
|
+
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name) -- PostgreSQL
|
829
885
|
#
|
830
|
-
#
|
886
|
+
# add_index(:developers, :name, algorithm: :inplace)
|
887
|
+
# # CREATE INDEX `index_developers_on_name` ON `developers` (`name`) ALGORITHM = INPLACE -- MySQL
|
888
|
+
#
|
889
|
+
# Note: only supported by PostgreSQL and MySQL.
|
831
890
|
#
|
832
891
|
# Concurrently adding an index is not supported in a transaction.
|
833
892
|
#
|
834
893
|
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
835
894
|
def add_index(table_name, column_name, **options)
|
836
|
-
|
837
|
-
|
838
|
-
create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
|
895
|
+
create_index = build_create_index_definition(table_name, column_name, **options)
|
839
896
|
execute schema_creation.accept(create_index)
|
840
897
|
end
|
841
898
|
|
899
|
+
# Builds a CreateIndexDefinition object.
|
900
|
+
#
|
901
|
+
# This definition object contains information about the index that would be created
|
902
|
+
# if the same arguments were passed to #add_index. See #add_index for information about
|
903
|
+
# passing a +table_name+, +column_name+, and other additional options that can be passed.
|
904
|
+
def build_create_index_definition(table_name, column_name, **options) # :nodoc:
|
905
|
+
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
906
|
+
CreateIndexDefinition.new(index, algorithm, if_not_exists)
|
907
|
+
end
|
908
|
+
|
842
909
|
# Removes the given index from the table.
|
843
910
|
#
|
844
911
|
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
@@ -901,10 +968,14 @@ module ActiveRecord
|
|
901
968
|
remove_index(table_name, name: old_name)
|
902
969
|
end
|
903
970
|
|
904
|
-
def index_name(table_name, options)
|
971
|
+
def index_name(table_name, options) # :nodoc:
|
905
972
|
if Hash === options
|
906
973
|
if options[:column]
|
907
|
-
|
974
|
+
if options[:_uses_legacy_index_name]
|
975
|
+
"index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
|
976
|
+
else
|
977
|
+
generate_index_name(table_name, options[:column])
|
978
|
+
end
|
908
979
|
elsif options[:name]
|
909
980
|
options[:name]
|
910
981
|
else
|
@@ -924,7 +995,6 @@ module ActiveRecord
|
|
924
995
|
# Adds a reference. The reference column is a bigint by default,
|
925
996
|
# the <tt>:type</tt> option can be used to specify a different type.
|
926
997
|
# Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
|
927
|
-
# #add_reference and #add_belongs_to are acceptable.
|
928
998
|
#
|
929
999
|
# The +options+ hash can include the following keys:
|
930
1000
|
# [<tt>:type</tt>]
|
@@ -970,12 +1040,11 @@ module ActiveRecord
|
|
970
1040
|
# add_reference(:products, :supplier, foreign_key: { to_table: :firms })
|
971
1041
|
#
|
972
1042
|
def add_reference(table_name, ref_name, **options)
|
973
|
-
ReferenceDefinition.new(ref_name, **options).
|
1043
|
+
ReferenceDefinition.new(ref_name, **options).add(table_name, self)
|
974
1044
|
end
|
975
1045
|
alias :add_belongs_to :add_reference
|
976
1046
|
|
977
1047
|
# Removes the reference(s). Also removes a +type+ column if one exists.
|
978
|
-
# #remove_reference and #remove_belongs_to are acceptable.
|
979
1048
|
#
|
980
1049
|
# ====== Remove the reference
|
981
1050
|
#
|
@@ -990,19 +1059,21 @@ module ActiveRecord
|
|
990
1059
|
# remove_reference(:products, :user, foreign_key: true)
|
991
1060
|
#
|
992
1061
|
def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
|
1062
|
+
conditional_options = options.slice(:if_exists, :if_not_exists)
|
1063
|
+
|
993
1064
|
if foreign_key
|
994
1065
|
reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
|
995
1066
|
if foreign_key.is_a?(Hash)
|
996
|
-
foreign_key_options = foreign_key
|
1067
|
+
foreign_key_options = foreign_key.merge(conditional_options)
|
997
1068
|
else
|
998
|
-
foreign_key_options = { to_table: reference_name }
|
1069
|
+
foreign_key_options = { to_table: reference_name, **conditional_options }
|
999
1070
|
end
|
1000
1071
|
foreign_key_options[:column] ||= "#{ref_name}_id"
|
1001
1072
|
remove_foreign_key(table_name, **foreign_key_options)
|
1002
1073
|
end
|
1003
1074
|
|
1004
|
-
remove_column(table_name, "#{ref_name}_id")
|
1005
|
-
remove_column(table_name, "#{ref_name}_type") if polymorphic
|
1075
|
+
remove_column(table_name, "#{ref_name}_id", **conditional_options)
|
1076
|
+
remove_column(table_name, "#{ref_name}_type", **conditional_options) if polymorphic
|
1006
1077
|
end
|
1007
1078
|
alias :remove_belongs_to :remove_reference
|
1008
1079
|
|
@@ -1027,6 +1098,10 @@ module ActiveRecord
|
|
1027
1098
|
#
|
1028
1099
|
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
|
1029
1100
|
#
|
1101
|
+
# ====== Creating a foreign key, ignoring method call if the foreign key exists
|
1102
|
+
#
|
1103
|
+
# add_foreign_key(:articles, :authors, if_not_exists: true)
|
1104
|
+
#
|
1030
1105
|
# ====== Creating a foreign key on a specific column
|
1031
1106
|
#
|
1032
1107
|
# add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
|
@@ -1035,6 +1110,16 @@ module ActiveRecord
|
|
1035
1110
|
#
|
1036
1111
|
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")
|
1037
1112
|
#
|
1113
|
+
# ====== Creating a composite foreign key
|
1114
|
+
#
|
1115
|
+
# Assuming "carts" table has "(shop_id, user_id)" as a primary key.
|
1116
|
+
#
|
1117
|
+
# add_foreign_key :orders, :carts, primary_key: [:shop_id, :user_id]
|
1118
|
+
#
|
1119
|
+
# generates:
|
1120
|
+
#
|
1121
|
+
# ALTER TABLE "orders" ADD CONSTRAINT fk_rails_6f5e4cb3a4 FOREIGN KEY ("cart_shop_id", "cart_user_id") REFERENCES "carts" ("shop_id", "user_id")
|
1122
|
+
#
|
1038
1123
|
# ====== Creating a cascading foreign key
|
1039
1124
|
#
|
1040
1125
|
# add_foreign_key :articles, :authors, on_delete: :cascade
|
@@ -1045,19 +1130,28 @@ module ActiveRecord
|
|
1045
1130
|
#
|
1046
1131
|
# The +options+ hash can include the following keys:
|
1047
1132
|
# [<tt>:column</tt>]
|
1048
|
-
# The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt
|
1133
|
+
# The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>.
|
1134
|
+
# Pass an array to create a composite foreign key.
|
1049
1135
|
# [<tt>:primary_key</tt>]
|
1050
1136
|
# The primary key column name on +to_table+. Defaults to +id+.
|
1137
|
+
# Pass an array to create a composite foreign key.
|
1051
1138
|
# [<tt>:name</tt>]
|
1052
1139
|
# The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
|
1053
1140
|
# [<tt>:on_delete</tt>]
|
1054
|
-
# Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade
|
1141
|
+
# Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+, and +:restrict+
|
1055
1142
|
# [<tt>:on_update</tt>]
|
1056
|
-
# Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade
|
1143
|
+
# Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+, and +:restrict+
|
1144
|
+
# [<tt>:if_not_exists</tt>]
|
1145
|
+
# Specifies if the foreign key already exists to not try to re-add it. This will avoid
|
1146
|
+
# duplicate column errors.
|
1057
1147
|
# [<tt>:validate</tt>]
|
1058
1148
|
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
1149
|
+
# [<tt>:deferrable</tt>]
|
1150
|
+
# (PostgreSQL only) Specify whether or not the foreign key should be deferrable. Valid values are booleans or
|
1151
|
+
# +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
|
1059
1152
|
def add_foreign_key(from_table, to_table, **options)
|
1060
|
-
return unless
|
1153
|
+
return unless use_foreign_keys?
|
1154
|
+
return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
|
1061
1155
|
|
1062
1156
|
options = foreign_key_options(from_table, to_table, options)
|
1063
1157
|
at = create_alter_table from_table
|
@@ -1087,12 +1181,18 @@ module ActiveRecord
|
|
1087
1181
|
#
|
1088
1182
|
# remove_foreign_key :accounts, name: :special_fk_name
|
1089
1183
|
#
|
1184
|
+
# Checks if the foreign key exists before trying to remove it. Will silently ignore indexes that
|
1185
|
+
# don't exist.
|
1186
|
+
#
|
1187
|
+
# remove_foreign_key :accounts, :branches, if_exists: true
|
1188
|
+
#
|
1090
1189
|
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
|
1091
1190
|
# with an addition of
|
1092
1191
|
# [<tt>:to_table</tt>]
|
1093
1192
|
# The name of the table that contains the referenced primary key.
|
1094
1193
|
def remove_foreign_key(from_table, to_table = nil, **options)
|
1095
|
-
return unless
|
1194
|
+
return unless use_foreign_keys?
|
1195
|
+
return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
|
1096
1196
|
|
1097
1197
|
fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
|
1098
1198
|
|
@@ -1117,15 +1217,33 @@ module ActiveRecord
|
|
1117
1217
|
foreign_key_for(from_table, to_table: to_table, **options).present?
|
1118
1218
|
end
|
1119
1219
|
|
1120
|
-
def foreign_key_column_for(table_name) # :nodoc:
|
1220
|
+
def foreign_key_column_for(table_name, column_name) # :nodoc:
|
1121
1221
|
name = strip_table_name_prefix_and_suffix(table_name)
|
1122
|
-
"#{name.singularize}
|
1222
|
+
"#{name.singularize}_#{column_name}"
|
1123
1223
|
end
|
1124
1224
|
|
1125
1225
|
def foreign_key_options(from_table, to_table, options) # :nodoc:
|
1126
1226
|
options = options.dup
|
1127
|
-
|
1227
|
+
|
1228
|
+
if options[:primary_key].is_a?(Array)
|
1229
|
+
options[:column] ||= options[:primary_key].map do |pk_column|
|
1230
|
+
foreign_key_column_for(to_table, pk_column)
|
1231
|
+
end
|
1232
|
+
else
|
1233
|
+
options[:column] ||= foreign_key_column_for(to_table, "id")
|
1234
|
+
end
|
1235
|
+
|
1128
1236
|
options[:name] ||= foreign_key_name(from_table, options)
|
1237
|
+
|
1238
|
+
if options[:column].is_a?(Array) || options[:primary_key].is_a?(Array)
|
1239
|
+
if Array(options[:primary_key]).size != Array(options[:column]).size
|
1240
|
+
raise ArgumentError, <<~MSG.squish
|
1241
|
+
For composite primary keys, specify :column and :primary_key, where
|
1242
|
+
:column must reference all the :primary_key columns from #{to_table.inspect}
|
1243
|
+
MSG
|
1244
|
+
end
|
1245
|
+
end
|
1246
|
+
|
1129
1247
|
options
|
1130
1248
|
end
|
1131
1249
|
|
@@ -1147,12 +1265,16 @@ module ActiveRecord
|
|
1147
1265
|
# The +options+ hash can include the following keys:
|
1148
1266
|
# [<tt>:name</tt>]
|
1149
1267
|
# The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
|
1268
|
+
# [<tt>:if_not_exists</tt>]
|
1269
|
+
# Silently ignore if the constraint already exists, rather than raise an error.
|
1150
1270
|
# [<tt>:validate</tt>]
|
1151
1271
|
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
1152
|
-
def add_check_constraint(table_name, expression, **options)
|
1272
|
+
def add_check_constraint(table_name, expression, if_not_exists: false, **options)
|
1153
1273
|
return unless supports_check_constraints?
|
1154
1274
|
|
1155
1275
|
options = check_constraint_options(table_name, expression, options)
|
1276
|
+
return if if_not_exists && check_constraint_exists?(table_name, **options)
|
1277
|
+
|
1156
1278
|
at = create_alter_table(table_name)
|
1157
1279
|
at.add_check_constraint(expression, options)
|
1158
1280
|
|
@@ -1165,16 +1287,24 @@ module ActiveRecord
|
|
1165
1287
|
options
|
1166
1288
|
end
|
1167
1289
|
|
1168
|
-
# Removes the given check constraint from the table.
|
1290
|
+
# Removes the given check constraint from the table. Removing a check constraint
|
1291
|
+
# that does not exist will raise an error.
|
1169
1292
|
#
|
1170
1293
|
# remove_check_constraint :products, name: "price_check"
|
1171
1294
|
#
|
1295
|
+
# To silently ignore a non-existent check constraint rather than raise an error,
|
1296
|
+
# use the +if_exists+ option.
|
1297
|
+
#
|
1298
|
+
# remove_check_constraint :products, name: "price_check", if_exists: true
|
1299
|
+
#
|
1172
1300
|
# The +expression+ parameter will be ignored if present. It can be helpful
|
1173
1301
|
# to provide this in a migration's +change+ method so it can be reverted.
|
1174
1302
|
# In that case, +expression+ will be used by #add_check_constraint.
|
1175
|
-
def remove_check_constraint(table_name, expression = nil, **options)
|
1303
|
+
def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
|
1176
1304
|
return unless supports_check_constraints?
|
1177
1305
|
|
1306
|
+
return if if_exists && !check_constraint_exists?(table_name, **options)
|
1307
|
+
|
1178
1308
|
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
1179
1309
|
|
1180
1310
|
at = create_alter_table(table_name)
|
@@ -1183,8 +1313,20 @@ module ActiveRecord
|
|
1183
1313
|
execute schema_creation.accept(at)
|
1184
1314
|
end
|
1185
1315
|
|
1316
|
+
|
1317
|
+
# Checks to see if a check constraint exists on a table for a given check constraint definition.
|
1318
|
+
#
|
1319
|
+
# check_constraint_exists?(:products, name: "price_check")
|
1320
|
+
#
|
1321
|
+
def check_constraint_exists?(table_name, **options)
|
1322
|
+
if !options.key?(:name) && !options.key?(:expression)
|
1323
|
+
raise ArgumentError, "At least one of :name or :expression must be supplied"
|
1324
|
+
end
|
1325
|
+
check_constraint_for(table_name, **options).present?
|
1326
|
+
end
|
1327
|
+
|
1186
1328
|
def dump_schema_information # :nodoc:
|
1187
|
-
versions = schema_migration.
|
1329
|
+
versions = pool.schema_migration.versions
|
1188
1330
|
insert_versions_sql(versions) if versions.any?
|
1189
1331
|
end
|
1190
1332
|
|
@@ -1194,8 +1336,9 @@ module ActiveRecord
|
|
1194
1336
|
|
1195
1337
|
def assume_migrated_upto_version(version)
|
1196
1338
|
version = version.to_i
|
1197
|
-
sm_table = quote_table_name(schema_migration.table_name)
|
1339
|
+
sm_table = quote_table_name(pool.schema_migration.table_name)
|
1198
1340
|
|
1341
|
+
migration_context = pool.migration_context
|
1199
1342
|
migrated = migration_context.get_all_versions
|
1200
1343
|
versions = migration_context.migrations.map(&:version)
|
1201
1344
|
|
@@ -1256,20 +1399,39 @@ module ActiveRecord
|
|
1256
1399
|
columns
|
1257
1400
|
end
|
1258
1401
|
|
1402
|
+
def distinct_relation_for_primary_key(relation) # :nodoc:
|
1403
|
+
primary_key_columns = Array(relation.primary_key).map do |column|
|
1404
|
+
visitor.compile(relation.table[column])
|
1405
|
+
end
|
1406
|
+
|
1407
|
+
values = columns_for_distinct(
|
1408
|
+
primary_key_columns,
|
1409
|
+
relation.order_values
|
1410
|
+
)
|
1411
|
+
|
1412
|
+
limited = relation.reselect(values).distinct!
|
1413
|
+
limited_ids = select_rows(limited.arel, "SQL").map do |results|
|
1414
|
+
results.last(Array(relation.primary_key).length) # ignores order values for MySQL and PostgreSQL
|
1415
|
+
end
|
1416
|
+
|
1417
|
+
if limited_ids.empty?
|
1418
|
+
relation.none!
|
1419
|
+
else
|
1420
|
+
relation.where!(**Array(relation.primary_key).zip(limited_ids.transpose).to_h)
|
1421
|
+
end
|
1422
|
+
|
1423
|
+
relation.limit_value = relation.offset_value = nil
|
1424
|
+
relation
|
1425
|
+
end
|
1426
|
+
|
1259
1427
|
# Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
|
1260
1428
|
# Additional options (like +:null+) are forwarded to #add_column.
|
1261
1429
|
#
|
1262
1430
|
# add_timestamps(:suppliers, null: true)
|
1263
1431
|
#
|
1264
1432
|
def add_timestamps(table_name, **options)
|
1265
|
-
|
1266
|
-
|
1267
|
-
if !options.key?(:precision) && supports_datetime_with_precision?
|
1268
|
-
options[:precision] = 6
|
1269
|
-
end
|
1270
|
-
|
1271
|
-
add_column table_name, :created_at, :datetime, **options
|
1272
|
-
add_column table_name, :updated_at, :datetime, **options
|
1433
|
+
fragments = add_timestamps_for_alter(table_name, **options)
|
1434
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(', ')}"
|
1273
1435
|
end
|
1274
1436
|
|
1275
1437
|
# Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
|
@@ -1277,16 +1439,15 @@ module ActiveRecord
|
|
1277
1439
|
# remove_timestamps(:suppliers)
|
1278
1440
|
#
|
1279
1441
|
def remove_timestamps(table_name, **options)
|
1280
|
-
|
1281
|
-
remove_column table_name, :created_at
|
1442
|
+
remove_columns table_name, :updated_at, :created_at
|
1282
1443
|
end
|
1283
1444
|
|
1284
|
-
def update_table_definition(table_name, base)
|
1445
|
+
def update_table_definition(table_name, base) # :nodoc:
|
1285
1446
|
Table.new(table_name, base)
|
1286
1447
|
end
|
1287
1448
|
|
1288
1449
|
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)
|
1450
|
+
options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct)
|
1290
1451
|
|
1291
1452
|
column_names = index_column_names(column_name)
|
1292
1453
|
|
@@ -1305,6 +1466,8 @@ module ActiveRecord
|
|
1305
1466
|
where: options[:where],
|
1306
1467
|
type: options[:type],
|
1307
1468
|
using: options[:using],
|
1469
|
+
include: options[:include],
|
1470
|
+
nulls_not_distinct: options[:nulls_not_distinct],
|
1308
1471
|
comment: options[:comment]
|
1309
1472
|
)
|
1310
1473
|
|
@@ -1352,7 +1515,79 @@ module ActiveRecord
|
|
1352
1515
|
SchemaDumper.create(self, options)
|
1353
1516
|
end
|
1354
1517
|
|
1518
|
+
def use_foreign_keys?
|
1519
|
+
supports_foreign_keys? && foreign_keys_enabled?
|
1520
|
+
end
|
1521
|
+
|
1522
|
+
# Returns an instance of SchemaCreation, which can be used to visit a schema definition
|
1523
|
+
# object and return DDL.
|
1524
|
+
def schema_creation # :nodoc:
|
1525
|
+
SchemaCreation.new(self)
|
1526
|
+
end
|
1527
|
+
|
1528
|
+
def bulk_change_table(table_name, operations) # :nodoc:
|
1529
|
+
sql_fragments = []
|
1530
|
+
non_combinable_operations = []
|
1531
|
+
|
1532
|
+
operations.each do |command, args|
|
1533
|
+
table, arguments = args.shift, args
|
1534
|
+
method = :"#{command}_for_alter"
|
1535
|
+
|
1536
|
+
if respond_to?(method, true)
|
1537
|
+
sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
|
1538
|
+
sql_fragments.concat(sqls)
|
1539
|
+
non_combinable_operations.concat(procs)
|
1540
|
+
else
|
1541
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
1542
|
+
non_combinable_operations.each(&:call)
|
1543
|
+
sql_fragments = []
|
1544
|
+
non_combinable_operations = []
|
1545
|
+
send(command, table, *arguments)
|
1546
|
+
end
|
1547
|
+
end
|
1548
|
+
|
1549
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
1550
|
+
non_combinable_operations.each(&:call)
|
1551
|
+
end
|
1552
|
+
|
1553
|
+
def valid_table_definition_options # :nodoc:
|
1554
|
+
[:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation]
|
1555
|
+
end
|
1556
|
+
|
1557
|
+
def valid_column_definition_options # :nodoc:
|
1558
|
+
ColumnDefinition::OPTION_NAMES
|
1559
|
+
end
|
1560
|
+
|
1561
|
+
def valid_primary_key_options # :nodoc:
|
1562
|
+
[:limit, :default, :precision]
|
1563
|
+
end
|
1564
|
+
|
1565
|
+
# Returns the maximum length of an index name in bytes.
|
1566
|
+
def max_index_name_size
|
1567
|
+
62
|
1568
|
+
end
|
1569
|
+
|
1355
1570
|
private
|
1571
|
+
def generate_index_name(table_name, column)
|
1572
|
+
name = "index_#{table_name}_on_#{Array(column) * '_and_'}"
|
1573
|
+
return name if name.bytesize <= max_index_name_size
|
1574
|
+
|
1575
|
+
# Fallback to short version, add hash to ensure uniqueness
|
1576
|
+
hashed_identifier = "_" + OpenSSL::Digest::SHA256.hexdigest(name).first(10)
|
1577
|
+
name = "idx_on_#{Array(column) * '_'}"
|
1578
|
+
|
1579
|
+
short_limit = max_index_name_size - hashed_identifier.bytesize
|
1580
|
+
short_name = name.mb_chars.limit(short_limit).to_s
|
1581
|
+
|
1582
|
+
"#{short_name}#{hashed_identifier}"
|
1583
|
+
end
|
1584
|
+
|
1585
|
+
def validate_change_column_null_argument!(value)
|
1586
|
+
unless value == true || value == false
|
1587
|
+
raise ArgumentError, "change_column_null expects a boolean value (true for NULL, false for NOT NULL). Got: #{value.inspect}"
|
1588
|
+
end
|
1589
|
+
end
|
1590
|
+
|
1356
1591
|
def column_options_keys
|
1357
1592
|
[:limit, :precision, :scale, :default, :null, :collation, :comment]
|
1358
1593
|
end
|
@@ -1387,7 +1622,7 @@ module ActiveRecord
|
|
1387
1622
|
|
1388
1623
|
checks = []
|
1389
1624
|
|
1390
|
-
if !options.key?(:name) &&
|
1625
|
+
if !options.key?(:name) && expression_column_name?(column_name)
|
1391
1626
|
options[:name] = index_name(table_name, column_name)
|
1392
1627
|
column_names = []
|
1393
1628
|
else
|
@@ -1396,7 +1631,7 @@ module ActiveRecord
|
|
1396
1631
|
|
1397
1632
|
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
|
1398
1633
|
|
1399
|
-
if column_names.present?
|
1634
|
+
if column_names.present? && !(options.key?(:name) && expression_column_name?(column_names))
|
1400
1635
|
checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
|
1401
1636
|
end
|
1402
1637
|
|
@@ -1406,7 +1641,7 @@ module ActiveRecord
|
|
1406
1641
|
|
1407
1642
|
if matching_indexes.count > 1
|
1408
1643
|
raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
|
1409
|
-
|
1644
|
+
"Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
|
1410
1645
|
elsif matching_indexes.none?
|
1411
1646
|
raise ArgumentError, "No indexes found on #{table_name} with the options provided."
|
1412
1647
|
else
|
@@ -1414,11 +1649,11 @@ module ActiveRecord
|
|
1414
1649
|
end
|
1415
1650
|
end
|
1416
1651
|
|
1417
|
-
def rename_table_indexes(table_name, new_name)
|
1652
|
+
def rename_table_indexes(table_name, new_name, **options)
|
1418
1653
|
indexes(new_name).each do |index|
|
1419
|
-
generated_index_name = index_name(table_name, column: index.columns)
|
1654
|
+
generated_index_name = index_name(table_name, column: index.columns, **options)
|
1420
1655
|
if generated_index_name == index.name
|
1421
|
-
rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
|
1656
|
+
rename_index new_name, generated_index_name, index_name(new_name, column: index.columns, **options)
|
1422
1657
|
end
|
1423
1658
|
end
|
1424
1659
|
end
|
@@ -1436,10 +1671,6 @@ module ActiveRecord
|
|
1436
1671
|
end
|
1437
1672
|
end
|
1438
1673
|
|
1439
|
-
def schema_creation
|
1440
|
-
SchemaCreation.new(self)
|
1441
|
-
end
|
1442
|
-
|
1443
1674
|
def create_table_definition(name, **options)
|
1444
1675
|
TableDefinition.new(self, name, **options)
|
1445
1676
|
end
|
@@ -1448,8 +1679,12 @@ module ActiveRecord
|
|
1448
1679
|
AlterTable.new create_table_definition(name)
|
1449
1680
|
end
|
1450
1681
|
|
1451
|
-
def
|
1452
|
-
|
1682
|
+
def validate_create_table_options!(options)
|
1683
|
+
unless options[:_skip_validate_options]
|
1684
|
+
options
|
1685
|
+
.except(:_uses_legacy_table_name, :_skip_validate_options)
|
1686
|
+
.assert_valid_keys(valid_table_definition_options, valid_primary_key_options)
|
1687
|
+
end
|
1453
1688
|
end
|
1454
1689
|
|
1455
1690
|
def fetch_type_metadata(sql_type)
|
@@ -1464,7 +1699,7 @@ module ActiveRecord
|
|
1464
1699
|
end
|
1465
1700
|
|
1466
1701
|
def index_column_names(column_names)
|
1467
|
-
if
|
1702
|
+
if expression_column_name?(column_names)
|
1468
1703
|
column_names
|
1469
1704
|
else
|
1470
1705
|
Array(column_names)
|
@@ -1472,13 +1707,18 @@ module ActiveRecord
|
|
1472
1707
|
end
|
1473
1708
|
|
1474
1709
|
def index_name_options(column_names)
|
1475
|
-
if
|
1710
|
+
if expression_column_name?(column_names)
|
1476
1711
|
column_names = column_names.scan(/\w+/).join("_")
|
1477
1712
|
end
|
1478
1713
|
|
1479
1714
|
{ column: column_names }
|
1480
1715
|
end
|
1481
1716
|
|
1717
|
+
# Try to identify whether the given column name is an expression
|
1718
|
+
def expression_column_name?(column_name)
|
1719
|
+
column_name.is_a?(String) && /\W/.match?(column_name)
|
1720
|
+
end
|
1721
|
+
|
1482
1722
|
def strip_table_name_prefix_and_suffix(table_name)
|
1483
1723
|
prefix = Base.table_name_prefix
|
1484
1724
|
suffix = Base.table_name_suffix
|
@@ -1487,15 +1727,16 @@ module ActiveRecord
|
|
1487
1727
|
|
1488
1728
|
def foreign_key_name(table_name, options)
|
1489
1729
|
options.fetch(:name) do
|
1490
|
-
|
1491
|
-
|
1730
|
+
columns = Array(options.fetch(:column)).map(&:to_s)
|
1731
|
+
identifier = "#{table_name}_#{columns * '_and_'}_fk"
|
1732
|
+
hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
|
1492
1733
|
|
1493
1734
|
"fk_rails_#{hashed_identifier}"
|
1494
1735
|
end
|
1495
1736
|
end
|
1496
1737
|
|
1497
1738
|
def foreign_key_for(from_table, **options)
|
1498
|
-
return unless
|
1739
|
+
return unless use_foreign_keys?
|
1499
1740
|
foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
|
1500
1741
|
end
|
1501
1742
|
|
@@ -1512,11 +1753,15 @@ module ActiveRecord
|
|
1512
1753
|
end
|
1513
1754
|
end
|
1514
1755
|
|
1756
|
+
def foreign_keys_enabled?
|
1757
|
+
@config.fetch(:foreign_keys, true)
|
1758
|
+
end
|
1759
|
+
|
1515
1760
|
def check_constraint_name(table_name, **options)
|
1516
1761
|
options.fetch(:name) do
|
1517
1762
|
expression = options.fetch(:expression)
|
1518
1763
|
identifier = "#{table_name}_#{expression}_chk"
|
1519
|
-
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
|
1764
|
+
hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
|
1520
1765
|
|
1521
1766
|
"chk_rails_#{hashed_identifier}"
|
1522
1767
|
end
|
@@ -1525,7 +1770,7 @@ module ActiveRecord
|
|
1525
1770
|
def check_constraint_for(table_name, **options)
|
1526
1771
|
return unless supports_check_constraints?
|
1527
1772
|
chk_name = check_constraint_name(table_name, **options)
|
1528
|
-
check_constraints(table_name).detect { |chk| chk.name
|
1773
|
+
check_constraints(table_name).detect { |chk| chk.defined_for?(name: chk_name, **options) }
|
1529
1774
|
end
|
1530
1775
|
|
1531
1776
|
def check_constraint_for!(table_name, expression: nil, **options)
|
@@ -1539,6 +1784,12 @@ module ActiveRecord
|
|
1539
1784
|
end
|
1540
1785
|
end
|
1541
1786
|
|
1787
|
+
def validate_table_length!(table_name)
|
1788
|
+
if table_name.length > table_name_length
|
1789
|
+
raise ArgumentError, "Table name '#{table_name}' is too long; the limit is #{table_name_length} characters"
|
1790
|
+
end
|
1791
|
+
end
|
1792
|
+
|
1542
1793
|
def extract_new_default_value(default_or_changes)
|
1543
1794
|
if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
|
1544
1795
|
default_or_changes[:to]
|
@@ -1552,29 +1803,8 @@ module ActiveRecord
|
|
1552
1803
|
column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
|
1553
1804
|
end
|
1554
1805
|
|
1555
|
-
def
|
1556
|
-
|
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)
|
1806
|
+
def reference_name_for_table(table_name)
|
1807
|
+
table_name.to_s.singularize
|
1578
1808
|
end
|
1579
1809
|
|
1580
1810
|
def add_column_for_alter(table_name, column_name, type, **options)
|
@@ -1583,6 +1813,11 @@ module ActiveRecord
|
|
1583
1813
|
schema_creation.accept(AddColumnDefinition.new(cd))
|
1584
1814
|
end
|
1585
1815
|
|
1816
|
+
def change_column_default_for_alter(table_name, column_name, default_or_changes)
|
1817
|
+
cd = build_change_column_default_definition(table_name, column_name, default_or_changes)
|
1818
|
+
schema_creation.accept(cd)
|
1819
|
+
end
|
1820
|
+
|
1586
1821
|
def rename_column_sql(table_name, column_name, new_column_name)
|
1587
1822
|
"RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
|
1588
1823
|
end
|
@@ -1613,12 +1848,12 @@ module ActiveRecord
|
|
1613
1848
|
end
|
1614
1849
|
|
1615
1850
|
def insert_versions_sql(versions)
|
1616
|
-
sm_table = quote_table_name(schema_migration.table_name)
|
1851
|
+
sm_table = quote_table_name(pool.schema_migration.table_name)
|
1617
1852
|
|
1618
1853
|
if versions.is_a?(Array)
|
1619
1854
|
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
|
1620
|
-
sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
|
1621
|
-
sql << "
|
1855
|
+
sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
|
1856
|
+
sql << ";"
|
1622
1857
|
sql
|
1623
1858
|
else
|
1624
1859
|
"INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
|