activerecord 7.0.8.7 → 7.2.3
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 +781 -1777
- data/MIT-LICENSE +1 -1
- data/README.rdoc +30 -30
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +40 -9
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +35 -21
- data/lib/active_record/associations/collection_proxy.rb +29 -11
- 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 +21 -14
- data/lib/active_record/associations/has_many_through_association.rb +17 -7
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +4 -3
- data/lib/active_record/associations/join_dependency.rb +10 -10
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +354 -485
- data/lib/active_record/attribute_assignment.rb +0 -4
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +131 -32
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +153 -33
- data/lib/active_record/attributes.rb +96 -71
- data/lib/active_record/autosave_association.rb +81 -39
- data/lib/active_record/base.rb +11 -7
- data/lib/active_record/callbacks.rb +11 -25
- 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 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +343 -91
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +229 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +142 -12
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +539 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +289 -128
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
- data/lib/active_record/connection_adapters/mysql/quoting.rb +60 -55
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +108 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- 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 +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +371 -64
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +374 -203
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -45
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +51 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +298 -113
- 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 +124 -1
- data/lib/active_record/connection_handling.rb +101 -105
- data/lib/active_record/core.rb +273 -178
- data/lib/active_record/counter_cache.rb +69 -35
- data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -3
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +87 -34
- data/lib/active_record/delegated_type.rb +56 -27
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +46 -22
- data/lib/active_record/encryption/encrypted_attribute_type.rb +48 -13
- data/lib/active_record/encryption/encryptor.rb +35 -19
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +130 -28
- data/lib/active_record/errors.rb +154 -34
- data/lib/active_record/explain.rb +21 -12
- 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 +48 -10
- data/lib/active_record/fixtures.rb +167 -97
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +11 -8
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +18 -22
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +106 -8
- data/lib/active_record/migration/compatibility.rb +147 -5
- 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/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +236 -118
- data/lib/active_record/model_schema.rb +90 -102
- data/lib/active_record/nested_attributes.rb +48 -11
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +168 -339
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +96 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +35 -10
- data/lib/active_record/railtie.rb +131 -87
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +147 -155
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +267 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +270 -108
- data/lib/active_record/relation/delegation.rb +30 -19
- data/lib/active_record/relation/finder_methods.rb +97 -21
- data/lib/active_record/relation/merger.rb +6 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -3
- 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 +28 -16
- data/lib/active_record/relation/query_attribute.rb +3 -2
- data/lib/active_record/relation/query_methods.rb +585 -109
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +15 -21
- data/lib/active_record/relation.rb +592 -92
- data/lib/active_record/result.rb +49 -48
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +70 -25
- data/lib/active_record/schema.rb +8 -7
- data/lib/active_record/schema_dumper.rb +90 -23
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +33 -11
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +190 -118
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +23 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +170 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- 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 +108 -24
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type/time.rb +4 -0
- 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 +9 -3
- 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 +61 -11
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +247 -33
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +3 -1
- 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 +2 -0
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +115 -5
- 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 +4 -2
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +7 -3
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +114 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- 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
- metadata +56 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
|
@@ -96,26 +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
|
-
column_name = options[:column] if column_name.nil?
|
|
102
|
-
|
|
103
|
-
if column_name.present?
|
|
104
|
-
column_names = Array(column_name).map(&:to_s)
|
|
105
|
-
checks << lambda { |i| Array(i.columns) == column_names }
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
checks << lambda { |i| i.unique } if options[:unique]
|
|
109
|
-
checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
|
|
110
|
-
|
|
111
|
-
indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
|
|
103
|
+
indexes(table_name).any? { |i| i.defined_for?(column_name, **options) }
|
|
112
104
|
end
|
|
113
105
|
|
|
114
106
|
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
|
115
107
|
def columns(table_name)
|
|
116
108
|
table_name = table_name.to_s
|
|
117
|
-
column_definitions(table_name)
|
|
118
|
-
|
|
109
|
+
definitions = column_definitions(table_name)
|
|
110
|
+
definitions.map do |field|
|
|
111
|
+
new_column_from_field(table_name, field, definitions)
|
|
119
112
|
end
|
|
120
113
|
end
|
|
121
114
|
|
|
@@ -193,6 +186,9 @@ module ActiveRecord
|
|
|
193
186
|
# Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
|
|
194
187
|
#
|
|
195
188
|
# A Symbol can be used to specify the type of the generated primary key column.
|
|
189
|
+
#
|
|
190
|
+
# A Hash can be used to specify the generated primary key column creation options.
|
|
191
|
+
# See {add_column}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_column] for available options.
|
|
196
192
|
# [<tt>:primary_key</tt>]
|
|
197
193
|
# The name of the primary key, if one is to be added automatically.
|
|
198
194
|
# Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
|
|
@@ -297,25 +293,15 @@ module ActiveRecord
|
|
|
297
293
|
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
|
298
294
|
#
|
|
299
295
|
# See also TableDefinition#column for details on how to create columns.
|
|
300
|
-
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
if id && !td.as
|
|
304
|
-
pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
|
|
305
|
-
|
|
306
|
-
if id.is_a?(Hash)
|
|
307
|
-
options.merge!(id.except(:type))
|
|
308
|
-
id = id.fetch(:type, :primary_key)
|
|
309
|
-
end
|
|
296
|
+
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)
|
|
297
|
+
validate_create_table_options!(options)
|
|
298
|
+
validate_table_length!(table_name) unless options[:_uses_legacy_table_name]
|
|
310
299
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
else
|
|
314
|
-
td.primary_key pk, id, **options
|
|
315
|
-
end
|
|
300
|
+
if force && options.key?(:if_not_exists)
|
|
301
|
+
raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
|
|
316
302
|
end
|
|
317
303
|
|
|
318
|
-
|
|
304
|
+
td = build_create_table_definition(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
|
|
319
305
|
|
|
320
306
|
if force
|
|
321
307
|
drop_table(table_name, force: force, if_exists: true)
|
|
@@ -323,7 +309,7 @@ module ActiveRecord
|
|
|
323
309
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
|
324
310
|
end
|
|
325
311
|
|
|
326
|
-
result = execute schema_creation.accept
|
|
312
|
+
result = execute schema_creation.accept(td)
|
|
327
313
|
|
|
328
314
|
unless supports_indexes_in_create?
|
|
329
315
|
td.indexes.each do |column_name, index_options|
|
|
@@ -344,6 +330,18 @@ module ActiveRecord
|
|
|
344
330
|
result
|
|
345
331
|
end
|
|
346
332
|
|
|
333
|
+
# Returns a TableDefinition object containing information about the table that would be created
|
|
334
|
+
# if the same arguments were passed to #create_table. See #create_table for information about
|
|
335
|
+
# passing a +table_name+, and other additional options that can be passed.
|
|
336
|
+
def build_create_table_definition(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
|
|
337
|
+
table_definition = create_table_definition(table_name, **options.extract!(*valid_table_definition_options, :_skip_validate_options))
|
|
338
|
+
table_definition.set_primary_key(table_name, id, primary_key, **options.extract!(*valid_primary_key_options, :_skip_validate_options))
|
|
339
|
+
|
|
340
|
+
yield table_definition if block_given?
|
|
341
|
+
|
|
342
|
+
table_definition
|
|
343
|
+
end
|
|
344
|
+
|
|
347
345
|
# Creates a new join table with the name created using the lexical order of the first two
|
|
348
346
|
# arguments. These arguments can be a String or a Symbol.
|
|
349
347
|
#
|
|
@@ -387,7 +385,7 @@ module ActiveRecord
|
|
|
387
385
|
|
|
388
386
|
column_options.reverse_merge!(null: false, index: false)
|
|
389
387
|
|
|
390
|
-
t1_ref, t2_ref = [table_1, table_2].map { |t| t
|
|
388
|
+
t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
|
|
391
389
|
|
|
392
390
|
create_table(join_table_name, **options.merge!(id: false)) do |td|
|
|
393
391
|
td.references t1_ref, **column_options
|
|
@@ -396,15 +394,33 @@ module ActiveRecord
|
|
|
396
394
|
end
|
|
397
395
|
end
|
|
398
396
|
|
|
397
|
+
# Builds a TableDefinition object for a join table.
|
|
398
|
+
#
|
|
399
|
+
# This definition object contains information about the table that would be created
|
|
400
|
+
# if the same arguments were passed to #create_join_table. See #create_join_table for
|
|
401
|
+
# information about what arguments should be passed.
|
|
402
|
+
def build_create_join_table_definition(table_1, table_2, column_options: {}, **options) # :nodoc:
|
|
403
|
+
join_table_name = find_join_table_name(table_1, table_2, options)
|
|
404
|
+
column_options.reverse_merge!(null: false, index: false)
|
|
405
|
+
|
|
406
|
+
t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
|
|
407
|
+
|
|
408
|
+
build_create_table_definition(join_table_name, **options.merge!(id: false)) do |td|
|
|
409
|
+
td.references t1_ref, **column_options
|
|
410
|
+
td.references t2_ref, **column_options
|
|
411
|
+
yield td if block_given?
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
399
415
|
# Drops the join table specified by the given arguments.
|
|
400
|
-
# See #create_join_table for details.
|
|
416
|
+
# See #create_join_table and #drop_table for details.
|
|
401
417
|
#
|
|
402
418
|
# Although this command ignores the block if one is given, it can be helpful
|
|
403
419
|
# to provide one in a migration's +change+ method so it can be reverted.
|
|
404
420
|
# In that case, the block will be used by #create_join_table.
|
|
405
421
|
def drop_join_table(table_1, table_2, **options)
|
|
406
422
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
|
407
|
-
drop_table(join_table_name)
|
|
423
|
+
drop_table(join_table_name, **options)
|
|
408
424
|
end
|
|
409
425
|
|
|
410
426
|
# A block for changing columns in +table+.
|
|
@@ -485,13 +501,13 @@ module ActiveRecord
|
|
|
485
501
|
# end
|
|
486
502
|
#
|
|
487
503
|
# See also Table for details on all of the various column transformations.
|
|
488
|
-
def change_table(table_name, **options)
|
|
504
|
+
def change_table(table_name, base = self, **options)
|
|
489
505
|
if supports_bulk_alter? && options[:bulk]
|
|
490
506
|
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
|
|
491
507
|
yield update_table_definition(table_name, recorder)
|
|
492
508
|
bulk_change_table(table_name, recorder.commands)
|
|
493
509
|
else
|
|
494
|
-
yield update_table_definition(table_name,
|
|
510
|
+
yield update_table_definition(table_name, base)
|
|
495
511
|
end
|
|
496
512
|
end
|
|
497
513
|
|
|
@@ -499,7 +515,7 @@ module ActiveRecord
|
|
|
499
515
|
#
|
|
500
516
|
# rename_table('octopuses', 'octopi')
|
|
501
517
|
#
|
|
502
|
-
def rename_table(table_name, new_name)
|
|
518
|
+
def rename_table(table_name, new_name, **)
|
|
503
519
|
raise NotImplementedError, "rename_table is not implemented"
|
|
504
520
|
end
|
|
505
521
|
|
|
@@ -554,11 +570,6 @@ module ActiveRecord
|
|
|
554
570
|
# <tt>:datetime</tt>, and <tt>:time</tt> columns.
|
|
555
571
|
# * <tt>:scale</tt> -
|
|
556
572
|
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
|
557
|
-
# * <tt>:collation</tt> -
|
|
558
|
-
# Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column. If not specified, the
|
|
559
|
-
# column will have the same collation as the table.
|
|
560
|
-
# * <tt>:comment</tt> -
|
|
561
|
-
# Specifies the comment for the column. This option is ignored by some backends.
|
|
562
573
|
# * <tt>:if_not_exists</tt> -
|
|
563
574
|
# Specifies if the column already exists to not try to re-add it. This will avoid
|
|
564
575
|
# duplicate column errors.
|
|
@@ -574,7 +585,7 @@ module ActiveRecord
|
|
|
574
585
|
# * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
|
|
575
586
|
# <tt>:precision</tt>, and makes no comments about the requirements of
|
|
576
587
|
# <tt>:precision</tt>.
|
|
577
|
-
# * MySQL: <tt>:precision</tt> [1..
|
|
588
|
+
# * MySQL: <tt>:precision</tt> [1..65], <tt>:scale</tt> [0..30].
|
|
578
589
|
# Default is (10,0).
|
|
579
590
|
# * PostgreSQL: <tt>:precision</tt> [1..infinity],
|
|
580
591
|
# <tt>:scale</tt> [0..infinity]. No default.
|
|
@@ -615,6 +626,24 @@ module ActiveRecord
|
|
|
615
626
|
# # Ignores the method call if the column exists
|
|
616
627
|
# add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
|
|
617
628
|
def add_column(table_name, column_name, type, **options)
|
|
629
|
+
add_column_def = build_add_column_definition(table_name, column_name, type, **options)
|
|
630
|
+
return unless add_column_def
|
|
631
|
+
|
|
632
|
+
execute schema_creation.accept(add_column_def)
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
def add_columns(table_name, *column_names, type:, **options) # :nodoc:
|
|
636
|
+
column_names.each do |column_name|
|
|
637
|
+
add_column(table_name, column_name, type, **options)
|
|
638
|
+
end
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
# Builds an AlterTable object for adding a column to a table.
|
|
642
|
+
#
|
|
643
|
+
# This definition object contains information about the column that would be created
|
|
644
|
+
# if the same arguments were passed to #add_column. See #add_column for information about
|
|
645
|
+
# passing a +table_name+, +column_name+, +type+ and other options that can be passed.
|
|
646
|
+
def build_add_column_definition(table_name, column_name, type, **options) # :nodoc:
|
|
618
647
|
return if options[:if_not_exists] == true && column_exists?(table_name, column_name)
|
|
619
648
|
|
|
620
649
|
if supports_datetime_with_precision?
|
|
@@ -623,15 +652,9 @@ module ActiveRecord
|
|
|
623
652
|
end
|
|
624
653
|
end
|
|
625
654
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
end
|
|
630
|
-
|
|
631
|
-
def add_columns(table_name, *column_names, type:, **options) # :nodoc:
|
|
632
|
-
column_names.each do |column_name|
|
|
633
|
-
add_column(table_name, column_name, type, **options)
|
|
634
|
-
end
|
|
655
|
+
alter_table = create_alter_table(table_name)
|
|
656
|
+
alter_table.add_column(column_name, type, **options)
|
|
657
|
+
alter_table
|
|
635
658
|
end
|
|
636
659
|
|
|
637
660
|
# Removes the given columns from the table definition.
|
|
@@ -662,7 +685,7 @@ module ActiveRecord
|
|
|
662
685
|
#
|
|
663
686
|
# If the options provided include an +if_exists+ key, it will be used to check if the
|
|
664
687
|
# column does not exist. This will silently ignore the migration rather than raising
|
|
665
|
-
# if the column was already
|
|
688
|
+
# if the column was already removed.
|
|
666
689
|
#
|
|
667
690
|
# remove_column(:suppliers, :qualification, if_exists: true)
|
|
668
691
|
def remove_column(table_name, column_name, type = nil, **options)
|
|
@@ -699,6 +722,15 @@ module ActiveRecord
|
|
|
699
722
|
raise NotImplementedError, "change_column_default is not implemented"
|
|
700
723
|
end
|
|
701
724
|
|
|
725
|
+
# Builds a ChangeColumnDefaultDefinition object.
|
|
726
|
+
#
|
|
727
|
+
# This definition object contains information about the column change that would occur
|
|
728
|
+
# if the same arguments were passed to #change_column_default. See #change_column_default for
|
|
729
|
+
# information about passing a +table_name+, +column_name+, +type+ and other options that can be passed.
|
|
730
|
+
def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
|
|
731
|
+
raise NotImplementedError, "build_change_column_default_definition is not implemented"
|
|
732
|
+
end
|
|
733
|
+
|
|
702
734
|
# Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
|
|
703
735
|
# indicates whether the value can be +NULL+. For example
|
|
704
736
|
#
|
|
@@ -805,6 +837,16 @@ module ActiveRecord
|
|
|
805
837
|
#
|
|
806
838
|
# Note: Partial indexes are only supported for PostgreSQL and SQLite.
|
|
807
839
|
#
|
|
840
|
+
# ====== Creating an index that includes additional columns
|
|
841
|
+
#
|
|
842
|
+
# add_index(:accounts, :branch_id, include: :party_id)
|
|
843
|
+
#
|
|
844
|
+
# generates:
|
|
845
|
+
#
|
|
846
|
+
# CREATE INDEX index_accounts_on_branch_id ON accounts USING btree(branch_id) INCLUDE (party_id)
|
|
847
|
+
#
|
|
848
|
+
# Note: only supported by PostgreSQL.
|
|
849
|
+
#
|
|
808
850
|
# ====== Creating an index with a specific method
|
|
809
851
|
#
|
|
810
852
|
# add_index(:developers, :name, using: 'btree')
|
|
@@ -842,20 +884,31 @@ module ActiveRecord
|
|
|
842
884
|
# ====== Creating an index with a specific algorithm
|
|
843
885
|
#
|
|
844
886
|
# add_index(:developers, :name, algorithm: :concurrently)
|
|
845
|
-
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
|
|
887
|
+
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name) -- PostgreSQL
|
|
846
888
|
#
|
|
847
|
-
#
|
|
889
|
+
# add_index(:developers, :name, algorithm: :inplace)
|
|
890
|
+
# # CREATE INDEX `index_developers_on_name` ON `developers` (`name`) ALGORITHM = INPLACE -- MySQL
|
|
891
|
+
#
|
|
892
|
+
# Note: only supported by PostgreSQL and MySQL.
|
|
848
893
|
#
|
|
849
894
|
# Concurrently adding an index is not supported in a transaction.
|
|
850
895
|
#
|
|
851
896
|
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
|
852
897
|
def add_index(table_name, column_name, **options)
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
|
|
898
|
+
create_index = build_create_index_definition(table_name, column_name, **options)
|
|
856
899
|
execute schema_creation.accept(create_index)
|
|
857
900
|
end
|
|
858
901
|
|
|
902
|
+
# Builds a CreateIndexDefinition object.
|
|
903
|
+
#
|
|
904
|
+
# This definition object contains information about the index that would be created
|
|
905
|
+
# if the same arguments were passed to #add_index. See #add_index for information about
|
|
906
|
+
# passing a +table_name+, +column_name+, and other additional options that can be passed.
|
|
907
|
+
def build_create_index_definition(table_name, column_name, **options) # :nodoc:
|
|
908
|
+
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
|
909
|
+
CreateIndexDefinition.new(index, algorithm, if_not_exists)
|
|
910
|
+
end
|
|
911
|
+
|
|
859
912
|
# Removes the given index from the table.
|
|
860
913
|
#
|
|
861
914
|
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
|
@@ -921,7 +974,11 @@ module ActiveRecord
|
|
|
921
974
|
def index_name(table_name, options) # :nodoc:
|
|
922
975
|
if Hash === options
|
|
923
976
|
if options[:column]
|
|
924
|
-
|
|
977
|
+
if options[:_uses_legacy_index_name]
|
|
978
|
+
"index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
|
|
979
|
+
else
|
|
980
|
+
generate_index_name(table_name, options[:column])
|
|
981
|
+
end
|
|
925
982
|
elsif options[:name]
|
|
926
983
|
options[:name]
|
|
927
984
|
else
|
|
@@ -941,7 +998,6 @@ module ActiveRecord
|
|
|
941
998
|
# Adds a reference. The reference column is a bigint by default,
|
|
942
999
|
# the <tt>:type</tt> option can be used to specify a different type.
|
|
943
1000
|
# Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
|
|
944
|
-
# #add_reference and #add_belongs_to are acceptable.
|
|
945
1001
|
#
|
|
946
1002
|
# The +options+ hash can include the following keys:
|
|
947
1003
|
# [<tt>:type</tt>]
|
|
@@ -987,12 +1043,11 @@ module ActiveRecord
|
|
|
987
1043
|
# add_reference(:products, :supplier, foreign_key: { to_table: :firms })
|
|
988
1044
|
#
|
|
989
1045
|
def add_reference(table_name, ref_name, **options)
|
|
990
|
-
ReferenceDefinition.new(ref_name, **options).
|
|
1046
|
+
ReferenceDefinition.new(ref_name, **options).add(table_name, self)
|
|
991
1047
|
end
|
|
992
1048
|
alias :add_belongs_to :add_reference
|
|
993
1049
|
|
|
994
1050
|
# Removes the reference(s). Also removes a +type+ column if one exists.
|
|
995
|
-
# #remove_reference and #remove_belongs_to are acceptable.
|
|
996
1051
|
#
|
|
997
1052
|
# ====== Remove the reference
|
|
998
1053
|
#
|
|
@@ -1007,19 +1062,21 @@ module ActiveRecord
|
|
|
1007
1062
|
# remove_reference(:products, :user, foreign_key: true)
|
|
1008
1063
|
#
|
|
1009
1064
|
def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
|
|
1065
|
+
conditional_options = options.slice(:if_exists, :if_not_exists)
|
|
1066
|
+
|
|
1010
1067
|
if foreign_key
|
|
1011
1068
|
reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
|
|
1012
1069
|
if foreign_key.is_a?(Hash)
|
|
1013
|
-
foreign_key_options = foreign_key
|
|
1070
|
+
foreign_key_options = foreign_key.merge(conditional_options)
|
|
1014
1071
|
else
|
|
1015
|
-
foreign_key_options = { to_table: reference_name }
|
|
1072
|
+
foreign_key_options = { to_table: reference_name, **conditional_options }
|
|
1016
1073
|
end
|
|
1017
1074
|
foreign_key_options[:column] ||= "#{ref_name}_id"
|
|
1018
1075
|
remove_foreign_key(table_name, **foreign_key_options)
|
|
1019
1076
|
end
|
|
1020
1077
|
|
|
1021
|
-
remove_column(table_name, "#{ref_name}_id")
|
|
1022
|
-
remove_column(table_name, "#{ref_name}_type") if polymorphic
|
|
1078
|
+
remove_column(table_name, "#{ref_name}_id", **conditional_options)
|
|
1079
|
+
remove_column(table_name, "#{ref_name}_type", **conditional_options) if polymorphic
|
|
1023
1080
|
end
|
|
1024
1081
|
alias :remove_belongs_to :remove_reference
|
|
1025
1082
|
|
|
@@ -1056,6 +1113,16 @@ module ActiveRecord
|
|
|
1056
1113
|
#
|
|
1057
1114
|
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")
|
|
1058
1115
|
#
|
|
1116
|
+
# ====== Creating a composite foreign key
|
|
1117
|
+
#
|
|
1118
|
+
# Assuming "carts" table has "(shop_id, user_id)" as a primary key.
|
|
1119
|
+
#
|
|
1120
|
+
# add_foreign_key :orders, :carts, primary_key: [:shop_id, :user_id]
|
|
1121
|
+
#
|
|
1122
|
+
# generates:
|
|
1123
|
+
#
|
|
1124
|
+
# ALTER TABLE "orders" ADD CONSTRAINT fk_rails_6f5e4cb3a4 FOREIGN KEY ("cart_shop_id", "cart_user_id") REFERENCES "carts" ("shop_id", "user_id")
|
|
1125
|
+
#
|
|
1059
1126
|
# ====== Creating a cascading foreign key
|
|
1060
1127
|
#
|
|
1061
1128
|
# add_foreign_key :articles, :authors, on_delete: :cascade
|
|
@@ -1066,9 +1133,11 @@ module ActiveRecord
|
|
|
1066
1133
|
#
|
|
1067
1134
|
# The +options+ hash can include the following keys:
|
|
1068
1135
|
# [<tt>:column</tt>]
|
|
1069
|
-
# The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt
|
|
1136
|
+
# The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>.
|
|
1137
|
+
# Pass an array to create a composite foreign key.
|
|
1070
1138
|
# [<tt>:primary_key</tt>]
|
|
1071
1139
|
# The primary key column name on +to_table+. Defaults to +id+.
|
|
1140
|
+
# Pass an array to create a composite foreign key.
|
|
1072
1141
|
# [<tt>:name</tt>]
|
|
1073
1142
|
# The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
|
|
1074
1143
|
# [<tt>:on_delete</tt>]
|
|
@@ -1084,8 +1153,8 @@ module ActiveRecord
|
|
|
1084
1153
|
# (PostgreSQL only) Specify whether or not the foreign key should be deferrable. Valid values are booleans or
|
|
1085
1154
|
# +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
|
|
1086
1155
|
def add_foreign_key(from_table, to_table, **options)
|
|
1087
|
-
return unless
|
|
1088
|
-
return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table)
|
|
1156
|
+
return unless use_foreign_keys?
|
|
1157
|
+
return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
|
|
1089
1158
|
|
|
1090
1159
|
options = foreign_key_options(from_table, to_table, options)
|
|
1091
1160
|
at = create_alter_table from_table
|
|
@@ -1125,7 +1194,7 @@ module ActiveRecord
|
|
|
1125
1194
|
# [<tt>:to_table</tt>]
|
|
1126
1195
|
# The name of the table that contains the referenced primary key.
|
|
1127
1196
|
def remove_foreign_key(from_table, to_table = nil, **options)
|
|
1128
|
-
return unless
|
|
1197
|
+
return unless use_foreign_keys?
|
|
1129
1198
|
return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
|
|
1130
1199
|
|
|
1131
1200
|
fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
|
|
@@ -1151,15 +1220,33 @@ module ActiveRecord
|
|
|
1151
1220
|
foreign_key_for(from_table, to_table: to_table, **options).present?
|
|
1152
1221
|
end
|
|
1153
1222
|
|
|
1154
|
-
def foreign_key_column_for(table_name) # :nodoc:
|
|
1223
|
+
def foreign_key_column_for(table_name, column_name) # :nodoc:
|
|
1155
1224
|
name = strip_table_name_prefix_and_suffix(table_name)
|
|
1156
|
-
"#{name.singularize}
|
|
1225
|
+
"#{name.singularize}_#{column_name}"
|
|
1157
1226
|
end
|
|
1158
1227
|
|
|
1159
1228
|
def foreign_key_options(from_table, to_table, options) # :nodoc:
|
|
1160
1229
|
options = options.dup
|
|
1161
|
-
|
|
1230
|
+
|
|
1231
|
+
if options[:primary_key].is_a?(Array)
|
|
1232
|
+
options[:column] ||= options[:primary_key].map do |pk_column|
|
|
1233
|
+
foreign_key_column_for(to_table, pk_column)
|
|
1234
|
+
end
|
|
1235
|
+
else
|
|
1236
|
+
options[:column] ||= foreign_key_column_for(to_table, "id")
|
|
1237
|
+
end
|
|
1238
|
+
|
|
1162
1239
|
options[:name] ||= foreign_key_name(from_table, options)
|
|
1240
|
+
|
|
1241
|
+
if options[:column].is_a?(Array) || options[:primary_key].is_a?(Array)
|
|
1242
|
+
if Array(options[:primary_key]).size != Array(options[:column]).size
|
|
1243
|
+
raise ArgumentError, <<~MSG.squish
|
|
1244
|
+
For composite primary keys, specify :column and :primary_key, where
|
|
1245
|
+
:column must reference all the :primary_key columns from #{to_table.inspect}
|
|
1246
|
+
MSG
|
|
1247
|
+
end
|
|
1248
|
+
end
|
|
1249
|
+
|
|
1163
1250
|
options
|
|
1164
1251
|
end
|
|
1165
1252
|
|
|
@@ -1181,12 +1268,16 @@ module ActiveRecord
|
|
|
1181
1268
|
# The +options+ hash can include the following keys:
|
|
1182
1269
|
# [<tt>:name</tt>]
|
|
1183
1270
|
# The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
|
|
1271
|
+
# [<tt>:if_not_exists</tt>]
|
|
1272
|
+
# Silently ignore if the constraint already exists, rather than raise an error.
|
|
1184
1273
|
# [<tt>:validate</tt>]
|
|
1185
1274
|
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
|
1186
|
-
def add_check_constraint(table_name, expression, **options)
|
|
1275
|
+
def add_check_constraint(table_name, expression, if_not_exists: false, **options)
|
|
1187
1276
|
return unless supports_check_constraints?
|
|
1188
1277
|
|
|
1189
1278
|
options = check_constraint_options(table_name, expression, options)
|
|
1279
|
+
return if if_not_exists && check_constraint_exists?(table_name, **options)
|
|
1280
|
+
|
|
1190
1281
|
at = create_alter_table(table_name)
|
|
1191
1282
|
at.add_check_constraint(expression, options)
|
|
1192
1283
|
|
|
@@ -1199,16 +1290,24 @@ module ActiveRecord
|
|
|
1199
1290
|
options
|
|
1200
1291
|
end
|
|
1201
1292
|
|
|
1202
|
-
# Removes the given check constraint from the table.
|
|
1293
|
+
# Removes the given check constraint from the table. Removing a check constraint
|
|
1294
|
+
# that does not exist will raise an error.
|
|
1203
1295
|
#
|
|
1204
1296
|
# remove_check_constraint :products, name: "price_check"
|
|
1205
1297
|
#
|
|
1298
|
+
# To silently ignore a non-existent check constraint rather than raise an error,
|
|
1299
|
+
# use the +if_exists+ option.
|
|
1300
|
+
#
|
|
1301
|
+
# remove_check_constraint :products, name: "price_check", if_exists: true
|
|
1302
|
+
#
|
|
1206
1303
|
# The +expression+ parameter will be ignored if present. It can be helpful
|
|
1207
1304
|
# to provide this in a migration's +change+ method so it can be reverted.
|
|
1208
1305
|
# In that case, +expression+ will be used by #add_check_constraint.
|
|
1209
|
-
def remove_check_constraint(table_name, expression = nil, **options)
|
|
1306
|
+
def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
|
|
1210
1307
|
return unless supports_check_constraints?
|
|
1211
1308
|
|
|
1309
|
+
return if if_exists && !check_constraint_exists?(table_name, **options)
|
|
1310
|
+
|
|
1212
1311
|
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
|
1213
1312
|
|
|
1214
1313
|
at = create_alter_table(table_name)
|
|
@@ -1217,8 +1316,20 @@ module ActiveRecord
|
|
|
1217
1316
|
execute schema_creation.accept(at)
|
|
1218
1317
|
end
|
|
1219
1318
|
|
|
1319
|
+
|
|
1320
|
+
# Checks to see if a check constraint exists on a table for a given check constraint definition.
|
|
1321
|
+
#
|
|
1322
|
+
# check_constraint_exists?(:products, name: "price_check")
|
|
1323
|
+
#
|
|
1324
|
+
def check_constraint_exists?(table_name, **options)
|
|
1325
|
+
if !options.key?(:name) && !options.key?(:expression)
|
|
1326
|
+
raise ArgumentError, "At least one of :name or :expression must be supplied"
|
|
1327
|
+
end
|
|
1328
|
+
check_constraint_for(table_name, **options).present?
|
|
1329
|
+
end
|
|
1330
|
+
|
|
1220
1331
|
def dump_schema_information # :nodoc:
|
|
1221
|
-
versions = schema_migration.
|
|
1332
|
+
versions = pool.schema_migration.versions
|
|
1222
1333
|
insert_versions_sql(versions) if versions.any?
|
|
1223
1334
|
end
|
|
1224
1335
|
|
|
@@ -1228,8 +1339,9 @@ module ActiveRecord
|
|
|
1228
1339
|
|
|
1229
1340
|
def assume_migrated_upto_version(version)
|
|
1230
1341
|
version = version.to_i
|
|
1231
|
-
sm_table = quote_table_name(schema_migration.table_name)
|
|
1342
|
+
sm_table = quote_table_name(pool.schema_migration.table_name)
|
|
1232
1343
|
|
|
1344
|
+
migration_context = pool.migration_context
|
|
1233
1345
|
migrated = migration_context.get_all_versions
|
|
1234
1346
|
versions = migration_context.migrations.map(&:version)
|
|
1235
1347
|
|
|
@@ -1291,18 +1403,24 @@ module ActiveRecord
|
|
|
1291
1403
|
end
|
|
1292
1404
|
|
|
1293
1405
|
def distinct_relation_for_primary_key(relation) # :nodoc:
|
|
1406
|
+
primary_key_columns = Array(relation.primary_key).map do |column|
|
|
1407
|
+
visitor.compile(relation.table[column])
|
|
1408
|
+
end
|
|
1409
|
+
|
|
1294
1410
|
values = columns_for_distinct(
|
|
1295
|
-
|
|
1411
|
+
primary_key_columns,
|
|
1296
1412
|
relation.order_values
|
|
1297
1413
|
)
|
|
1298
1414
|
|
|
1299
1415
|
limited = relation.reselect(values).distinct!
|
|
1300
|
-
limited_ids = select_rows(limited.arel, "SQL").map
|
|
1416
|
+
limited_ids = select_rows(limited.arel, "SQL").map do |results|
|
|
1417
|
+
results.last(Array(relation.primary_key).length) # ignores order values for MySQL and PostgreSQL
|
|
1418
|
+
end
|
|
1301
1419
|
|
|
1302
1420
|
if limited_ids.empty?
|
|
1303
1421
|
relation.none!
|
|
1304
1422
|
else
|
|
1305
|
-
relation.where!(relation.primary_key
|
|
1423
|
+
relation.where!(**Array(relation.primary_key).zip(limited_ids.transpose).to_h)
|
|
1306
1424
|
end
|
|
1307
1425
|
|
|
1308
1426
|
relation.limit_value = relation.offset_value = nil
|
|
@@ -1315,14 +1433,8 @@ module ActiveRecord
|
|
|
1315
1433
|
# add_timestamps(:suppliers, null: true)
|
|
1316
1434
|
#
|
|
1317
1435
|
def add_timestamps(table_name, **options)
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
if !options.key?(:precision) && supports_datetime_with_precision?
|
|
1321
|
-
options[:precision] = 6
|
|
1322
|
-
end
|
|
1323
|
-
|
|
1324
|
-
add_column table_name, :created_at, :datetime, **options
|
|
1325
|
-
add_column table_name, :updated_at, :datetime, **options
|
|
1436
|
+
fragments = add_timestamps_for_alter(table_name, **options)
|
|
1437
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(', ')}"
|
|
1326
1438
|
end
|
|
1327
1439
|
|
|
1328
1440
|
# Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
|
|
@@ -1338,7 +1450,7 @@ module ActiveRecord
|
|
|
1338
1450
|
end
|
|
1339
1451
|
|
|
1340
1452
|
def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
|
|
1341
|
-
options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm)
|
|
1453
|
+
options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct)
|
|
1342
1454
|
|
|
1343
1455
|
column_names = index_column_names(column_name)
|
|
1344
1456
|
|
|
@@ -1357,6 +1469,8 @@ module ActiveRecord
|
|
|
1357
1469
|
where: options[:where],
|
|
1358
1470
|
type: options[:type],
|
|
1359
1471
|
using: options[:using],
|
|
1472
|
+
include: options[:include],
|
|
1473
|
+
nulls_not_distinct: options[:nulls_not_distinct],
|
|
1360
1474
|
comment: options[:comment]
|
|
1361
1475
|
)
|
|
1362
1476
|
|
|
@@ -1404,7 +1518,79 @@ module ActiveRecord
|
|
|
1404
1518
|
SchemaDumper.create(self, options)
|
|
1405
1519
|
end
|
|
1406
1520
|
|
|
1521
|
+
def use_foreign_keys?
|
|
1522
|
+
supports_foreign_keys? && foreign_keys_enabled?
|
|
1523
|
+
end
|
|
1524
|
+
|
|
1525
|
+
# Returns an instance of SchemaCreation, which can be used to visit a schema definition
|
|
1526
|
+
# object and return DDL.
|
|
1527
|
+
def schema_creation # :nodoc:
|
|
1528
|
+
SchemaCreation.new(self)
|
|
1529
|
+
end
|
|
1530
|
+
|
|
1531
|
+
def bulk_change_table(table_name, operations) # :nodoc:
|
|
1532
|
+
sql_fragments = []
|
|
1533
|
+
non_combinable_operations = []
|
|
1534
|
+
|
|
1535
|
+
operations.each do |command, args|
|
|
1536
|
+
table, arguments = args.shift, args
|
|
1537
|
+
method = :"#{command}_for_alter"
|
|
1538
|
+
|
|
1539
|
+
if respond_to?(method, true)
|
|
1540
|
+
sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
|
|
1541
|
+
sql_fragments.concat(sqls)
|
|
1542
|
+
non_combinable_operations.concat(procs)
|
|
1543
|
+
else
|
|
1544
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
|
1545
|
+
non_combinable_operations.each(&:call)
|
|
1546
|
+
sql_fragments = []
|
|
1547
|
+
non_combinable_operations = []
|
|
1548
|
+
send(command, table, *arguments)
|
|
1549
|
+
end
|
|
1550
|
+
end
|
|
1551
|
+
|
|
1552
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
|
1553
|
+
non_combinable_operations.each(&:call)
|
|
1554
|
+
end
|
|
1555
|
+
|
|
1556
|
+
def valid_table_definition_options # :nodoc:
|
|
1557
|
+
[:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation]
|
|
1558
|
+
end
|
|
1559
|
+
|
|
1560
|
+
def valid_column_definition_options # :nodoc:
|
|
1561
|
+
ColumnDefinition::OPTION_NAMES
|
|
1562
|
+
end
|
|
1563
|
+
|
|
1564
|
+
def valid_primary_key_options # :nodoc:
|
|
1565
|
+
[:limit, :default, :precision]
|
|
1566
|
+
end
|
|
1567
|
+
|
|
1568
|
+
# Returns the maximum length of an index name in bytes.
|
|
1569
|
+
def max_index_name_size
|
|
1570
|
+
62
|
|
1571
|
+
end
|
|
1572
|
+
|
|
1407
1573
|
private
|
|
1574
|
+
def generate_index_name(table_name, column)
|
|
1575
|
+
name = "index_#{table_name}_on_#{Array(column) * '_and_'}"
|
|
1576
|
+
return name if name.bytesize <= max_index_name_size
|
|
1577
|
+
|
|
1578
|
+
# Fallback to short version, add hash to ensure uniqueness
|
|
1579
|
+
hashed_identifier = "_" + OpenSSL::Digest::SHA256.hexdigest(name).first(10)
|
|
1580
|
+
name = "idx_on_#{Array(column) * '_'}"
|
|
1581
|
+
|
|
1582
|
+
short_limit = max_index_name_size - hashed_identifier.bytesize
|
|
1583
|
+
short_name = name.mb_chars.limit(short_limit).to_s
|
|
1584
|
+
|
|
1585
|
+
"#{short_name}#{hashed_identifier}"
|
|
1586
|
+
end
|
|
1587
|
+
|
|
1588
|
+
def validate_change_column_null_argument!(value)
|
|
1589
|
+
unless value == true || value == false
|
|
1590
|
+
raise ArgumentError, "change_column_null expects a boolean value (true for NULL, false for NOT NULL). Got: #{value.inspect}"
|
|
1591
|
+
end
|
|
1592
|
+
end
|
|
1593
|
+
|
|
1408
1594
|
def column_options_keys
|
|
1409
1595
|
[:limit, :precision, :scale, :default, :null, :collation, :comment]
|
|
1410
1596
|
end
|
|
@@ -1458,7 +1644,7 @@ module ActiveRecord
|
|
|
1458
1644
|
|
|
1459
1645
|
if matching_indexes.count > 1
|
|
1460
1646
|
raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
|
|
1461
|
-
|
|
1647
|
+
"Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
|
|
1462
1648
|
elsif matching_indexes.none?
|
|
1463
1649
|
raise ArgumentError, "No indexes found on #{table_name} with the options provided."
|
|
1464
1650
|
else
|
|
@@ -1466,11 +1652,11 @@ module ActiveRecord
|
|
|
1466
1652
|
end
|
|
1467
1653
|
end
|
|
1468
1654
|
|
|
1469
|
-
def rename_table_indexes(table_name, new_name)
|
|
1655
|
+
def rename_table_indexes(table_name, new_name, **options)
|
|
1470
1656
|
indexes(new_name).each do |index|
|
|
1471
|
-
generated_index_name = index_name(table_name, column: index.columns)
|
|
1657
|
+
generated_index_name = index_name(table_name, column: index.columns, **options)
|
|
1472
1658
|
if generated_index_name == index.name
|
|
1473
|
-
rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
|
|
1659
|
+
rename_index new_name, generated_index_name, index_name(new_name, column: index.columns, **options)
|
|
1474
1660
|
end
|
|
1475
1661
|
end
|
|
1476
1662
|
end
|
|
@@ -1488,10 +1674,6 @@ module ActiveRecord
|
|
|
1488
1674
|
end
|
|
1489
1675
|
end
|
|
1490
1676
|
|
|
1491
|
-
def schema_creation
|
|
1492
|
-
SchemaCreation.new(self)
|
|
1493
|
-
end
|
|
1494
|
-
|
|
1495
1677
|
def create_table_definition(name, **options)
|
|
1496
1678
|
TableDefinition.new(self, name, **options)
|
|
1497
1679
|
end
|
|
@@ -1500,8 +1682,12 @@ module ActiveRecord
|
|
|
1500
1682
|
AlterTable.new create_table_definition(name)
|
|
1501
1683
|
end
|
|
1502
1684
|
|
|
1503
|
-
def
|
|
1504
|
-
|
|
1685
|
+
def validate_create_table_options!(options)
|
|
1686
|
+
unless options[:_skip_validate_options]
|
|
1687
|
+
options
|
|
1688
|
+
.except(:_uses_legacy_table_name, :_skip_validate_options)
|
|
1689
|
+
.assert_valid_keys(valid_table_definition_options, valid_primary_key_options)
|
|
1690
|
+
end
|
|
1505
1691
|
end
|
|
1506
1692
|
|
|
1507
1693
|
def fetch_type_metadata(sql_type)
|
|
@@ -1544,7 +1730,8 @@ module ActiveRecord
|
|
|
1544
1730
|
|
|
1545
1731
|
def foreign_key_name(table_name, options)
|
|
1546
1732
|
options.fetch(:name) do
|
|
1547
|
-
|
|
1733
|
+
columns = Array(options.fetch(:column)).map(&:to_s)
|
|
1734
|
+
identifier = "#{table_name}_#{columns * '_and_'}_fk"
|
|
1548
1735
|
hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
|
|
1549
1736
|
|
|
1550
1737
|
"fk_rails_#{hashed_identifier}"
|
|
@@ -1552,7 +1739,7 @@ module ActiveRecord
|
|
|
1552
1739
|
end
|
|
1553
1740
|
|
|
1554
1741
|
def foreign_key_for(from_table, **options)
|
|
1555
|
-
return unless
|
|
1742
|
+
return unless use_foreign_keys?
|
|
1556
1743
|
foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
|
|
1557
1744
|
end
|
|
1558
1745
|
|
|
@@ -1569,6 +1756,10 @@ module ActiveRecord
|
|
|
1569
1756
|
end
|
|
1570
1757
|
end
|
|
1571
1758
|
|
|
1759
|
+
def foreign_keys_enabled?
|
|
1760
|
+
@config.fetch(:foreign_keys, true)
|
|
1761
|
+
end
|
|
1762
|
+
|
|
1572
1763
|
def check_constraint_name(table_name, **options)
|
|
1573
1764
|
options.fetch(:name) do
|
|
1574
1765
|
expression = options.fetch(:expression)
|
|
@@ -1582,7 +1773,7 @@ module ActiveRecord
|
|
|
1582
1773
|
def check_constraint_for(table_name, **options)
|
|
1583
1774
|
return unless supports_check_constraints?
|
|
1584
1775
|
chk_name = check_constraint_name(table_name, **options)
|
|
1585
|
-
check_constraints(table_name).detect { |chk| chk.name
|
|
1776
|
+
check_constraints(table_name).detect { |chk| chk.defined_for?(name: chk_name, **options) }
|
|
1586
1777
|
end
|
|
1587
1778
|
|
|
1588
1779
|
def check_constraint_for!(table_name, expression: nil, **options)
|
|
@@ -1596,6 +1787,12 @@ module ActiveRecord
|
|
|
1596
1787
|
end
|
|
1597
1788
|
end
|
|
1598
1789
|
|
|
1790
|
+
def validate_table_length!(table_name)
|
|
1791
|
+
if table_name.length > table_name_length
|
|
1792
|
+
raise ArgumentError, "Table name '#{table_name}' is too long; the limit is #{table_name_length} characters"
|
|
1793
|
+
end
|
|
1794
|
+
end
|
|
1795
|
+
|
|
1599
1796
|
def extract_new_default_value(default_or_changes)
|
|
1600
1797
|
if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
|
|
1601
1798
|
default_or_changes[:to]
|
|
@@ -1609,29 +1806,8 @@ module ActiveRecord
|
|
|
1609
1806
|
column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
|
|
1610
1807
|
end
|
|
1611
1808
|
|
|
1612
|
-
def
|
|
1613
|
-
|
|
1614
|
-
non_combinable_operations = []
|
|
1615
|
-
|
|
1616
|
-
operations.each do |command, args|
|
|
1617
|
-
table, arguments = args.shift, args
|
|
1618
|
-
method = :"#{command}_for_alter"
|
|
1619
|
-
|
|
1620
|
-
if respond_to?(method, true)
|
|
1621
|
-
sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
|
|
1622
|
-
sql_fragments << sqls
|
|
1623
|
-
non_combinable_operations.concat(procs)
|
|
1624
|
-
else
|
|
1625
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
|
1626
|
-
non_combinable_operations.each(&:call)
|
|
1627
|
-
sql_fragments = []
|
|
1628
|
-
non_combinable_operations = []
|
|
1629
|
-
send(command, table, *arguments)
|
|
1630
|
-
end
|
|
1631
|
-
end
|
|
1632
|
-
|
|
1633
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
|
1634
|
-
non_combinable_operations.each(&:call)
|
|
1809
|
+
def reference_name_for_table(table_name)
|
|
1810
|
+
table_name.to_s.singularize
|
|
1635
1811
|
end
|
|
1636
1812
|
|
|
1637
1813
|
def add_column_for_alter(table_name, column_name, type, **options)
|
|
@@ -1640,6 +1816,11 @@ module ActiveRecord
|
|
|
1640
1816
|
schema_creation.accept(AddColumnDefinition.new(cd))
|
|
1641
1817
|
end
|
|
1642
1818
|
|
|
1819
|
+
def change_column_default_for_alter(table_name, column_name, default_or_changes)
|
|
1820
|
+
cd = build_change_column_default_definition(table_name, column_name, default_or_changes)
|
|
1821
|
+
schema_creation.accept(cd)
|
|
1822
|
+
end
|
|
1823
|
+
|
|
1643
1824
|
def rename_column_sql(table_name, column_name, new_column_name)
|
|
1644
1825
|
"RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
|
|
1645
1826
|
end
|
|
@@ -1670,12 +1851,12 @@ module ActiveRecord
|
|
|
1670
1851
|
end
|
|
1671
1852
|
|
|
1672
1853
|
def insert_versions_sql(versions)
|
|
1673
|
-
sm_table = quote_table_name(schema_migration.table_name)
|
|
1854
|
+
sm_table = quote_table_name(pool.schema_migration.table_name)
|
|
1674
1855
|
|
|
1675
1856
|
if versions.is_a?(Array)
|
|
1676
1857
|
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
|
|
1677
|
-
sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
|
|
1678
|
-
sql << "
|
|
1858
|
+
sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
|
|
1859
|
+
sql << ";"
|
|
1679
1860
|
sql
|
|
1680
1861
|
else
|
|
1681
1862
|
"INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
|