activerecord 7.0.8 → 7.1.3.4
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 +1554 -1452
- data/MIT-LICENSE +1 -1
- data/README.rdoc +16 -16
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +20 -4
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +15 -9
- data/lib/active_record/associations/collection_proxy.rb +15 -10
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +10 -8
- data/lib/active_record/associations/preloader/association.rb +31 -7
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +313 -217
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +52 -34
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +105 -21
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +55 -9
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +10 -24
- 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 +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +74 -51
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
- 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 +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -124
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +511 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -108
- 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 +22 -143
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
- 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 +18 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- 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 +74 -40
- 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/quoting.rb +10 -6
- 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 +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +361 -60
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +353 -192
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +209 -79
- 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 +262 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +72 -95
- data/lib/active_record/core.rb +175 -153
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +9 -4
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- 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 +42 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
- 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/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +19 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +112 -28
- data/lib/active_record/errors.rb +112 -18
- data/lib/active_record/explain.rb +23 -3
- 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 +29 -8
- data/lib/active_record/fixtures.rb +135 -71
- data/lib/active_record/future_result.rb +31 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +57 -10
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +120 -30
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- 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 +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 +104 -5
- data/lib/active_record/migration/compatibility.rb +139 -5
- data/lib/active_record/migration/default_strategy.rb +23 -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 +219 -111
- data/lib/active_record/model_schema.rb +64 -44
- data/lib/active_record/nested_attributes.rb +24 -6
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +188 -37
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +77 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +109 -47
- data/lib/active_record/railties/controller_runtime.rb +12 -6
- data/lib/active_record/railties/databases.rake +142 -148
- 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 +174 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +187 -63
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +352 -63
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +91 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +24 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +46 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -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 +7 -5
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +10 -1
- data/lib/active_record/tasks/database_tasks.rb +127 -105
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +27 -15
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +36 -10
- 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/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- 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 +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +121 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- 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/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- 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 +48 -12
- 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
|
|
@@ -297,25 +290,10 @@ module ActiveRecord
|
|
297
290
|
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
298
291
|
#
|
299
292
|
# 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
|
-
|
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
|
310
|
-
|
311
|
-
if pk.is_a?(Array)
|
312
|
-
td.primary_keys pk
|
313
|
-
else
|
314
|
-
td.primary_key pk, id, **options
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
|
-
yield td if block_given?
|
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]
|
296
|
+
td = build_create_table_definition(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
|
319
297
|
|
320
298
|
if force
|
321
299
|
drop_table(table_name, force: force, if_exists: true)
|
@@ -323,7 +301,7 @@ module ActiveRecord
|
|
323
301
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
324
302
|
end
|
325
303
|
|
326
|
-
result = execute schema_creation.accept
|
304
|
+
result = execute schema_creation.accept(td)
|
327
305
|
|
328
306
|
unless supports_indexes_in_create?
|
329
307
|
td.indexes.each do |column_name, index_options|
|
@@ -344,6 +322,18 @@ module ActiveRecord
|
|
344
322
|
result
|
345
323
|
end
|
346
324
|
|
325
|
+
# Returns a TableDefinition object containing information about the table that would be created
|
326
|
+
# if the same arguments were passed to #create_table. See #create_table for information about
|
327
|
+
# passing a +table_name+, and other additional options that can be passed.
|
328
|
+
def build_create_table_definition(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
|
329
|
+
table_definition = create_table_definition(table_name, **options.extract!(*valid_table_definition_options, :_skip_validate_options))
|
330
|
+
table_definition.set_primary_key(table_name, id, primary_key, **options.extract!(*valid_primary_key_options, :_skip_validate_options))
|
331
|
+
|
332
|
+
yield table_definition if block_given?
|
333
|
+
|
334
|
+
table_definition
|
335
|
+
end
|
336
|
+
|
347
337
|
# Creates a new join table with the name created using the lexical order of the first two
|
348
338
|
# arguments. These arguments can be a String or a Symbol.
|
349
339
|
#
|
@@ -387,7 +377,7 @@ module ActiveRecord
|
|
387
377
|
|
388
378
|
column_options.reverse_merge!(null: false, index: false)
|
389
379
|
|
390
|
-
t1_ref, t2_ref = [table_1, table_2].map { |t| t
|
380
|
+
t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
|
391
381
|
|
392
382
|
create_table(join_table_name, **options.merge!(id: false)) do |td|
|
393
383
|
td.references t1_ref, **column_options
|
@@ -396,15 +386,33 @@ module ActiveRecord
|
|
396
386
|
end
|
397
387
|
end
|
398
388
|
|
389
|
+
# Builds a TableDefinition object for a join table.
|
390
|
+
#
|
391
|
+
# This definition object contains information about the table that would be created
|
392
|
+
# if the same arguments were passed to #create_join_table. See #create_join_table for
|
393
|
+
# information about what arguments should be passed.
|
394
|
+
def build_create_join_table_definition(table_1, table_2, column_options: {}, **options) # :nodoc:
|
395
|
+
join_table_name = find_join_table_name(table_1, table_2, options)
|
396
|
+
column_options.reverse_merge!(null: false, index: false)
|
397
|
+
|
398
|
+
t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
|
399
|
+
|
400
|
+
build_create_table_definition(join_table_name, **options.merge!(id: false)) do |td|
|
401
|
+
td.references t1_ref, **column_options
|
402
|
+
td.references t2_ref, **column_options
|
403
|
+
yield td if block_given?
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
399
407
|
# Drops the join table specified by the given arguments.
|
400
|
-
# See #create_join_table for details.
|
408
|
+
# See #create_join_table and #drop_table for details.
|
401
409
|
#
|
402
410
|
# Although this command ignores the block if one is given, it can be helpful
|
403
411
|
# to provide one in a migration's +change+ method so it can be reverted.
|
404
412
|
# In that case, the block will be used by #create_join_table.
|
405
413
|
def drop_join_table(table_1, table_2, **options)
|
406
414
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
407
|
-
drop_table(join_table_name)
|
415
|
+
drop_table(join_table_name, **options)
|
408
416
|
end
|
409
417
|
|
410
418
|
# A block for changing columns in +table+.
|
@@ -485,13 +493,13 @@ module ActiveRecord
|
|
485
493
|
# end
|
486
494
|
#
|
487
495
|
# See also Table for details on all of the various column transformations.
|
488
|
-
def change_table(table_name, **options)
|
496
|
+
def change_table(table_name, base = self, **options)
|
489
497
|
if supports_bulk_alter? && options[:bulk]
|
490
498
|
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
|
491
499
|
yield update_table_definition(table_name, recorder)
|
492
500
|
bulk_change_table(table_name, recorder.commands)
|
493
501
|
else
|
494
|
-
yield update_table_definition(table_name,
|
502
|
+
yield update_table_definition(table_name, base)
|
495
503
|
end
|
496
504
|
end
|
497
505
|
|
@@ -499,7 +507,7 @@ module ActiveRecord
|
|
499
507
|
#
|
500
508
|
# rename_table('octopuses', 'octopi')
|
501
509
|
#
|
502
|
-
def rename_table(table_name, new_name)
|
510
|
+
def rename_table(table_name, new_name, **)
|
503
511
|
raise NotImplementedError, "rename_table is not implemented"
|
504
512
|
end
|
505
513
|
|
@@ -554,11 +562,6 @@ module ActiveRecord
|
|
554
562
|
# <tt>:datetime</tt>, and <tt>:time</tt> columns.
|
555
563
|
# * <tt>:scale</tt> -
|
556
564
|
# 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
565
|
# * <tt>:if_not_exists</tt> -
|
563
566
|
# Specifies if the column already exists to not try to re-add it. This will avoid
|
564
567
|
# duplicate column errors.
|
@@ -574,7 +577,7 @@ module ActiveRecord
|
|
574
577
|
# * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
|
575
578
|
# <tt>:precision</tt>, and makes no comments about the requirements of
|
576
579
|
# <tt>:precision</tt>.
|
577
|
-
# * MySQL: <tt>:precision</tt> [1..
|
580
|
+
# * MySQL: <tt>:precision</tt> [1..65], <tt>:scale</tt> [0..30].
|
578
581
|
# Default is (10,0).
|
579
582
|
# * PostgreSQL: <tt>:precision</tt> [1..infinity],
|
580
583
|
# <tt>:scale</tt> [0..infinity]. No default.
|
@@ -615,6 +618,24 @@ module ActiveRecord
|
|
615
618
|
# # Ignores the method call if the column exists
|
616
619
|
# add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
|
617
620
|
def add_column(table_name, column_name, type, **options)
|
621
|
+
add_column_def = build_add_column_definition(table_name, column_name, type, **options)
|
622
|
+
return unless add_column_def
|
623
|
+
|
624
|
+
execute schema_creation.accept(add_column_def)
|
625
|
+
end
|
626
|
+
|
627
|
+
def add_columns(table_name, *column_names, type:, **options) # :nodoc:
|
628
|
+
column_names.each do |column_name|
|
629
|
+
add_column(table_name, column_name, type, **options)
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
# Builds an AlterTable object for adding a column to a table.
|
634
|
+
#
|
635
|
+
# This definition object contains information about the column that would be created
|
636
|
+
# if the same arguments were passed to #add_column. See #add_column for information about
|
637
|
+
# passing a +table_name+, +column_name+, +type+ and other options that can be passed.
|
638
|
+
def build_add_column_definition(table_name, column_name, type, **options) # :nodoc:
|
618
639
|
return if options[:if_not_exists] == true && column_exists?(table_name, column_name)
|
619
640
|
|
620
641
|
if supports_datetime_with_precision?
|
@@ -623,15 +644,9 @@ module ActiveRecord
|
|
623
644
|
end
|
624
645
|
end
|
625
646
|
|
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
|
647
|
+
alter_table = create_alter_table(table_name)
|
648
|
+
alter_table.add_column(column_name, type, **options)
|
649
|
+
alter_table
|
635
650
|
end
|
636
651
|
|
637
652
|
# Removes the given columns from the table definition.
|
@@ -699,6 +714,15 @@ module ActiveRecord
|
|
699
714
|
raise NotImplementedError, "change_column_default is not implemented"
|
700
715
|
end
|
701
716
|
|
717
|
+
# Builds a ChangeColumnDefaultDefinition object.
|
718
|
+
#
|
719
|
+
# This definition object contains information about the column change that would occur
|
720
|
+
# if the same arguments were passed to #change_column_default. See #change_column_default for
|
721
|
+
# information about passing a +table_name+, +column_name+, +type+ and other options that can be passed.
|
722
|
+
def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
|
723
|
+
raise NotImplementedError, "build_change_column_default_definition is not implemented"
|
724
|
+
end
|
725
|
+
|
702
726
|
# Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
|
703
727
|
# indicates whether the value can be +NULL+. For example
|
704
728
|
#
|
@@ -805,6 +829,16 @@ module ActiveRecord
|
|
805
829
|
#
|
806
830
|
# Note: Partial indexes are only supported for PostgreSQL and SQLite.
|
807
831
|
#
|
832
|
+
# ====== Creating an index that includes additional columns
|
833
|
+
#
|
834
|
+
# add_index(:accounts, :branch_id, include: :party_id)
|
835
|
+
#
|
836
|
+
# generates:
|
837
|
+
#
|
838
|
+
# CREATE INDEX index_accounts_on_branch_id ON accounts USING btree(branch_id) INCLUDE (party_id)
|
839
|
+
#
|
840
|
+
# Note: only supported by PostgreSQL.
|
841
|
+
#
|
808
842
|
# ====== Creating an index with a specific method
|
809
843
|
#
|
810
844
|
# add_index(:developers, :name, using: 'btree')
|
@@ -850,12 +884,20 @@ module ActiveRecord
|
|
850
884
|
#
|
851
885
|
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
852
886
|
def add_index(table_name, column_name, **options)
|
853
|
-
|
854
|
-
|
855
|
-
create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
|
887
|
+
create_index = build_create_index_definition(table_name, column_name, **options)
|
856
888
|
execute schema_creation.accept(create_index)
|
857
889
|
end
|
858
890
|
|
891
|
+
# Builds a CreateIndexDefinition object.
|
892
|
+
#
|
893
|
+
# This definition object contains information about the index that would be created
|
894
|
+
# if the same arguments were passed to #add_index. See #add_index for information about
|
895
|
+
# passing a +table_name+, +column_name+, and other additional options that can be passed.
|
896
|
+
def build_create_index_definition(table_name, column_name, **options) # :nodoc:
|
897
|
+
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
898
|
+
CreateIndexDefinition.new(index, algorithm, if_not_exists)
|
899
|
+
end
|
900
|
+
|
859
901
|
# Removes the given index from the table.
|
860
902
|
#
|
861
903
|
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
@@ -921,7 +963,7 @@ module ActiveRecord
|
|
921
963
|
def index_name(table_name, options) # :nodoc:
|
922
964
|
if Hash === options
|
923
965
|
if options[:column]
|
924
|
-
|
966
|
+
generate_index_name(table_name, options[:column])
|
925
967
|
elsif options[:name]
|
926
968
|
options[:name]
|
927
969
|
else
|
@@ -941,7 +983,6 @@ module ActiveRecord
|
|
941
983
|
# Adds a reference. The reference column is a bigint by default,
|
942
984
|
# the <tt>:type</tt> option can be used to specify a different type.
|
943
985
|
# Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
|
944
|
-
# #add_reference and #add_belongs_to are acceptable.
|
945
986
|
#
|
946
987
|
# The +options+ hash can include the following keys:
|
947
988
|
# [<tt>:type</tt>]
|
@@ -987,12 +1028,11 @@ module ActiveRecord
|
|
987
1028
|
# add_reference(:products, :supplier, foreign_key: { to_table: :firms })
|
988
1029
|
#
|
989
1030
|
def add_reference(table_name, ref_name, **options)
|
990
|
-
ReferenceDefinition.new(ref_name, **options).
|
1031
|
+
ReferenceDefinition.new(ref_name, **options).add(table_name, self)
|
991
1032
|
end
|
992
1033
|
alias :add_belongs_to :add_reference
|
993
1034
|
|
994
1035
|
# Removes the reference(s). Also removes a +type+ column if one exists.
|
995
|
-
# #remove_reference and #remove_belongs_to are acceptable.
|
996
1036
|
#
|
997
1037
|
# ====== Remove the reference
|
998
1038
|
#
|
@@ -1007,19 +1047,21 @@ module ActiveRecord
|
|
1007
1047
|
# remove_reference(:products, :user, foreign_key: true)
|
1008
1048
|
#
|
1009
1049
|
def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
|
1050
|
+
conditional_options = options.slice(:if_exists, :if_not_exists)
|
1051
|
+
|
1010
1052
|
if foreign_key
|
1011
1053
|
reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
|
1012
1054
|
if foreign_key.is_a?(Hash)
|
1013
|
-
foreign_key_options = foreign_key
|
1055
|
+
foreign_key_options = foreign_key.merge(conditional_options)
|
1014
1056
|
else
|
1015
|
-
foreign_key_options = { to_table: reference_name }
|
1057
|
+
foreign_key_options = { to_table: reference_name, **conditional_options }
|
1016
1058
|
end
|
1017
1059
|
foreign_key_options[:column] ||= "#{ref_name}_id"
|
1018
1060
|
remove_foreign_key(table_name, **foreign_key_options)
|
1019
1061
|
end
|
1020
1062
|
|
1021
|
-
remove_column(table_name, "#{ref_name}_id")
|
1022
|
-
remove_column(table_name, "#{ref_name}_type") if polymorphic
|
1063
|
+
remove_column(table_name, "#{ref_name}_id", **conditional_options)
|
1064
|
+
remove_column(table_name, "#{ref_name}_type", **conditional_options) if polymorphic
|
1023
1065
|
end
|
1024
1066
|
alias :remove_belongs_to :remove_reference
|
1025
1067
|
|
@@ -1056,6 +1098,16 @@ module ActiveRecord
|
|
1056
1098
|
#
|
1057
1099
|
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")
|
1058
1100
|
#
|
1101
|
+
# ====== Creating a composite foreign key
|
1102
|
+
#
|
1103
|
+
# Assuming "carts" table has "(shop_id, user_id)" as a primary key.
|
1104
|
+
#
|
1105
|
+
# add_foreign_key :orders, :carts, primary_key: [:shop_id, :user_id]
|
1106
|
+
#
|
1107
|
+
# generates:
|
1108
|
+
#
|
1109
|
+
# ALTER TABLE "orders" ADD CONSTRAINT fk_rails_6f5e4cb3a4 FOREIGN KEY ("cart_shop_id", "cart_user_id") REFERENCES "carts" ("shop_id", "user_id")
|
1110
|
+
#
|
1059
1111
|
# ====== Creating a cascading foreign key
|
1060
1112
|
#
|
1061
1113
|
# add_foreign_key :articles, :authors, on_delete: :cascade
|
@@ -1066,9 +1118,11 @@ module ActiveRecord
|
|
1066
1118
|
#
|
1067
1119
|
# The +options+ hash can include the following keys:
|
1068
1120
|
# [<tt>:column</tt>]
|
1069
|
-
# The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt
|
1121
|
+
# The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>.
|
1122
|
+
# Pass an array to create a composite foreign key.
|
1070
1123
|
# [<tt>:primary_key</tt>]
|
1071
1124
|
# The primary key column name on +to_table+. Defaults to +id+.
|
1125
|
+
# Pass an array to create a composite foreign key.
|
1072
1126
|
# [<tt>:name</tt>]
|
1073
1127
|
# The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
|
1074
1128
|
# [<tt>:on_delete</tt>]
|
@@ -1084,8 +1138,8 @@ module ActiveRecord
|
|
1084
1138
|
# (PostgreSQL only) Specify whether or not the foreign key should be deferrable. Valid values are booleans or
|
1085
1139
|
# +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
|
1086
1140
|
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)
|
1141
|
+
return unless use_foreign_keys?
|
1142
|
+
return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
|
1089
1143
|
|
1090
1144
|
options = foreign_key_options(from_table, to_table, options)
|
1091
1145
|
at = create_alter_table from_table
|
@@ -1125,7 +1179,7 @@ module ActiveRecord
|
|
1125
1179
|
# [<tt>:to_table</tt>]
|
1126
1180
|
# The name of the table that contains the referenced primary key.
|
1127
1181
|
def remove_foreign_key(from_table, to_table = nil, **options)
|
1128
|
-
return unless
|
1182
|
+
return unless use_foreign_keys?
|
1129
1183
|
return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
|
1130
1184
|
|
1131
1185
|
fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
|
@@ -1151,15 +1205,33 @@ module ActiveRecord
|
|
1151
1205
|
foreign_key_for(from_table, to_table: to_table, **options).present?
|
1152
1206
|
end
|
1153
1207
|
|
1154
|
-
def foreign_key_column_for(table_name) # :nodoc:
|
1208
|
+
def foreign_key_column_for(table_name, column_name) # :nodoc:
|
1155
1209
|
name = strip_table_name_prefix_and_suffix(table_name)
|
1156
|
-
"#{name.singularize}
|
1210
|
+
"#{name.singularize}_#{column_name}"
|
1157
1211
|
end
|
1158
1212
|
|
1159
1213
|
def foreign_key_options(from_table, to_table, options) # :nodoc:
|
1160
1214
|
options = options.dup
|
1161
|
-
|
1215
|
+
|
1216
|
+
if options[:primary_key].is_a?(Array)
|
1217
|
+
options[:column] ||= options[:primary_key].map do |pk_column|
|
1218
|
+
foreign_key_column_for(to_table, pk_column)
|
1219
|
+
end
|
1220
|
+
else
|
1221
|
+
options[:column] ||= foreign_key_column_for(to_table, "id")
|
1222
|
+
end
|
1223
|
+
|
1162
1224
|
options[:name] ||= foreign_key_name(from_table, options)
|
1225
|
+
|
1226
|
+
if options[:column].is_a?(Array) || options[:primary_key].is_a?(Array)
|
1227
|
+
if Array(options[:primary_key]).size != Array(options[:column]).size
|
1228
|
+
raise ArgumentError, <<~MSG.squish
|
1229
|
+
For composite primary keys, specify :column and :primary_key, where
|
1230
|
+
:column must reference all the :primary_key columns from #{to_table.inspect}
|
1231
|
+
MSG
|
1232
|
+
end
|
1233
|
+
end
|
1234
|
+
|
1163
1235
|
options
|
1164
1236
|
end
|
1165
1237
|
|
@@ -1181,12 +1253,16 @@ module ActiveRecord
|
|
1181
1253
|
# The +options+ hash can include the following keys:
|
1182
1254
|
# [<tt>:name</tt>]
|
1183
1255
|
# The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
|
1256
|
+
# [<tt>:if_not_exists</tt>]
|
1257
|
+
# Silently ignore if the constraint already exists, rather than raise an error.
|
1184
1258
|
# [<tt>:validate</tt>]
|
1185
1259
|
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
1186
|
-
def add_check_constraint(table_name, expression, **options)
|
1260
|
+
def add_check_constraint(table_name, expression, if_not_exists: false, **options)
|
1187
1261
|
return unless supports_check_constraints?
|
1188
1262
|
|
1189
1263
|
options = check_constraint_options(table_name, expression, options)
|
1264
|
+
return if if_not_exists && check_constraint_exists?(table_name, **options)
|
1265
|
+
|
1190
1266
|
at = create_alter_table(table_name)
|
1191
1267
|
at.add_check_constraint(expression, options)
|
1192
1268
|
|
@@ -1199,16 +1275,24 @@ module ActiveRecord
|
|
1199
1275
|
options
|
1200
1276
|
end
|
1201
1277
|
|
1202
|
-
# Removes the given check constraint from the table.
|
1278
|
+
# Removes the given check constraint from the table. Removing a check constraint
|
1279
|
+
# that does not exist will raise an error.
|
1203
1280
|
#
|
1204
1281
|
# remove_check_constraint :products, name: "price_check"
|
1205
1282
|
#
|
1283
|
+
# To silently ignore a non-existent check constraint rather than raise an error,
|
1284
|
+
# use the +if_exists+ option.
|
1285
|
+
#
|
1286
|
+
# remove_check_constraint :products, name: "price_check", if_exists: true
|
1287
|
+
#
|
1206
1288
|
# The +expression+ parameter will be ignored if present. It can be helpful
|
1207
1289
|
# to provide this in a migration's +change+ method so it can be reverted.
|
1208
1290
|
# In that case, +expression+ will be used by #add_check_constraint.
|
1209
|
-
def remove_check_constraint(table_name, expression = nil, **options)
|
1291
|
+
def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
|
1210
1292
|
return unless supports_check_constraints?
|
1211
1293
|
|
1294
|
+
return if if_exists && !check_constraint_exists?(table_name, **options)
|
1295
|
+
|
1212
1296
|
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
1213
1297
|
|
1214
1298
|
at = create_alter_table(table_name)
|
@@ -1217,8 +1301,20 @@ module ActiveRecord
|
|
1217
1301
|
execute schema_creation.accept(at)
|
1218
1302
|
end
|
1219
1303
|
|
1304
|
+
|
1305
|
+
# Checks to see if a check constraint exists on a table for a given check constraint definition.
|
1306
|
+
#
|
1307
|
+
# check_constraint_exists?(:products, name: "price_check")
|
1308
|
+
#
|
1309
|
+
def check_constraint_exists?(table_name, **options)
|
1310
|
+
if !options.key?(:name) && !options.key?(:expression)
|
1311
|
+
raise ArgumentError, "At least one of :name or :expression must be supplied"
|
1312
|
+
end
|
1313
|
+
check_constraint_for(table_name, **options).present?
|
1314
|
+
end
|
1315
|
+
|
1220
1316
|
def dump_schema_information # :nodoc:
|
1221
|
-
versions = schema_migration.
|
1317
|
+
versions = schema_migration.versions
|
1222
1318
|
insert_versions_sql(versions) if versions.any?
|
1223
1319
|
end
|
1224
1320
|
|
@@ -1291,18 +1387,24 @@ module ActiveRecord
|
|
1291
1387
|
end
|
1292
1388
|
|
1293
1389
|
def distinct_relation_for_primary_key(relation) # :nodoc:
|
1390
|
+
primary_key_columns = Array(relation.primary_key).map do |column|
|
1391
|
+
visitor.compile(relation.table[column])
|
1392
|
+
end
|
1393
|
+
|
1294
1394
|
values = columns_for_distinct(
|
1295
|
-
|
1395
|
+
primary_key_columns,
|
1296
1396
|
relation.order_values
|
1297
1397
|
)
|
1298
1398
|
|
1299
1399
|
limited = relation.reselect(values).distinct!
|
1300
|
-
limited_ids = select_rows(limited.arel, "SQL").map
|
1400
|
+
limited_ids = select_rows(limited.arel, "SQL").map do |results|
|
1401
|
+
results.last(Array(relation.primary_key).length) # ignores order values for MySQL and PostgreSQL
|
1402
|
+
end
|
1301
1403
|
|
1302
1404
|
if limited_ids.empty?
|
1303
1405
|
relation.none!
|
1304
1406
|
else
|
1305
|
-
relation.where!(relation.primary_key
|
1407
|
+
relation.where!(**Array(relation.primary_key).zip(limited_ids.transpose).to_h)
|
1306
1408
|
end
|
1307
1409
|
|
1308
1410
|
relation.limit_value = relation.offset_value = nil
|
@@ -1315,14 +1417,8 @@ module ActiveRecord
|
|
1315
1417
|
# add_timestamps(:suppliers, null: true)
|
1316
1418
|
#
|
1317
1419
|
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
|
1420
|
+
fragments = add_timestamps_for_alter(table_name, **options)
|
1421
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(', ')}"
|
1326
1422
|
end
|
1327
1423
|
|
1328
1424
|
# Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
|
@@ -1338,7 +1434,7 @@ module ActiveRecord
|
|
1338
1434
|
end
|
1339
1435
|
|
1340
1436
|
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)
|
1437
|
+
options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct)
|
1342
1438
|
|
1343
1439
|
column_names = index_column_names(column_name)
|
1344
1440
|
|
@@ -1357,6 +1453,8 @@ module ActiveRecord
|
|
1357
1453
|
where: options[:where],
|
1358
1454
|
type: options[:type],
|
1359
1455
|
using: options[:using],
|
1456
|
+
include: options[:include],
|
1457
|
+
nulls_not_distinct: options[:nulls_not_distinct],
|
1360
1458
|
comment: options[:comment]
|
1361
1459
|
)
|
1362
1460
|
|
@@ -1404,7 +1502,79 @@ module ActiveRecord
|
|
1404
1502
|
SchemaDumper.create(self, options)
|
1405
1503
|
end
|
1406
1504
|
|
1505
|
+
def use_foreign_keys?
|
1506
|
+
supports_foreign_keys? && foreign_keys_enabled?
|
1507
|
+
end
|
1508
|
+
|
1509
|
+
# Returns an instance of SchemaCreation, which can be used to visit a schema definition
|
1510
|
+
# object and return DDL.
|
1511
|
+
def schema_creation # :nodoc:
|
1512
|
+
SchemaCreation.new(self)
|
1513
|
+
end
|
1514
|
+
|
1515
|
+
def bulk_change_table(table_name, operations) # :nodoc:
|
1516
|
+
sql_fragments = []
|
1517
|
+
non_combinable_operations = []
|
1518
|
+
|
1519
|
+
operations.each do |command, args|
|
1520
|
+
table, arguments = args.shift, args
|
1521
|
+
method = :"#{command}_for_alter"
|
1522
|
+
|
1523
|
+
if respond_to?(method, true)
|
1524
|
+
sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
|
1525
|
+
sql_fragments.concat(sqls)
|
1526
|
+
non_combinable_operations.concat(procs)
|
1527
|
+
else
|
1528
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
1529
|
+
non_combinable_operations.each(&:call)
|
1530
|
+
sql_fragments = []
|
1531
|
+
non_combinable_operations = []
|
1532
|
+
send(command, table, *arguments)
|
1533
|
+
end
|
1534
|
+
end
|
1535
|
+
|
1536
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
1537
|
+
non_combinable_operations.each(&:call)
|
1538
|
+
end
|
1539
|
+
|
1540
|
+
def valid_table_definition_options # :nodoc:
|
1541
|
+
[:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation]
|
1542
|
+
end
|
1543
|
+
|
1544
|
+
def valid_column_definition_options # :nodoc:
|
1545
|
+
ColumnDefinition::OPTION_NAMES
|
1546
|
+
end
|
1547
|
+
|
1548
|
+
def valid_primary_key_options # :nodoc:
|
1549
|
+
[:limit, :default, :precision]
|
1550
|
+
end
|
1551
|
+
|
1552
|
+
# Returns the maximum length of an index name in bytes.
|
1553
|
+
def max_index_name_size
|
1554
|
+
62
|
1555
|
+
end
|
1556
|
+
|
1407
1557
|
private
|
1558
|
+
def generate_index_name(table_name, column)
|
1559
|
+
name = "index_#{table_name}_on_#{Array(column) * '_and_'}"
|
1560
|
+
return name if name.bytesize <= max_index_name_size
|
1561
|
+
|
1562
|
+
# Fallback to short version, add hash to ensure uniqueness
|
1563
|
+
hashed_identifier = "_" + OpenSSL::Digest::SHA256.hexdigest(name).first(10)
|
1564
|
+
name = "idx_on_#{Array(column) * '_'}"
|
1565
|
+
|
1566
|
+
short_limit = max_index_name_size - hashed_identifier.bytesize
|
1567
|
+
short_name = name.mb_chars.limit(short_limit).to_s
|
1568
|
+
|
1569
|
+
"#{short_name}#{hashed_identifier}"
|
1570
|
+
end
|
1571
|
+
|
1572
|
+
def validate_change_column_null_argument!(value)
|
1573
|
+
unless value == true || value == false
|
1574
|
+
raise ArgumentError, "change_column_null expects a boolean value (true for NULL, false for NOT NULL). Got: #{value.inspect}"
|
1575
|
+
end
|
1576
|
+
end
|
1577
|
+
|
1408
1578
|
def column_options_keys
|
1409
1579
|
[:limit, :precision, :scale, :default, :null, :collation, :comment]
|
1410
1580
|
end
|
@@ -1458,7 +1628,7 @@ module ActiveRecord
|
|
1458
1628
|
|
1459
1629
|
if matching_indexes.count > 1
|
1460
1630
|
raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
|
1461
|
-
|
1631
|
+
"Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
|
1462
1632
|
elsif matching_indexes.none?
|
1463
1633
|
raise ArgumentError, "No indexes found on #{table_name} with the options provided."
|
1464
1634
|
else
|
@@ -1488,10 +1658,6 @@ module ActiveRecord
|
|
1488
1658
|
end
|
1489
1659
|
end
|
1490
1660
|
|
1491
|
-
def schema_creation
|
1492
|
-
SchemaCreation.new(self)
|
1493
|
-
end
|
1494
|
-
|
1495
1661
|
def create_table_definition(name, **options)
|
1496
1662
|
TableDefinition.new(self, name, **options)
|
1497
1663
|
end
|
@@ -1500,8 +1666,12 @@ module ActiveRecord
|
|
1500
1666
|
AlterTable.new create_table_definition(name)
|
1501
1667
|
end
|
1502
1668
|
|
1503
|
-
def
|
1504
|
-
|
1669
|
+
def validate_create_table_options!(options)
|
1670
|
+
unless options[:_skip_validate_options]
|
1671
|
+
options
|
1672
|
+
.except(:_uses_legacy_table_name, :_skip_validate_options)
|
1673
|
+
.assert_valid_keys(valid_table_definition_options, valid_primary_key_options)
|
1674
|
+
end
|
1505
1675
|
end
|
1506
1676
|
|
1507
1677
|
def fetch_type_metadata(sql_type)
|
@@ -1544,7 +1714,8 @@ module ActiveRecord
|
|
1544
1714
|
|
1545
1715
|
def foreign_key_name(table_name, options)
|
1546
1716
|
options.fetch(:name) do
|
1547
|
-
|
1717
|
+
columns = Array(options.fetch(:column)).map(&:to_s)
|
1718
|
+
identifier = "#{table_name}_#{columns * '_and_'}_fk"
|
1548
1719
|
hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
|
1549
1720
|
|
1550
1721
|
"fk_rails_#{hashed_identifier}"
|
@@ -1552,7 +1723,7 @@ module ActiveRecord
|
|
1552
1723
|
end
|
1553
1724
|
|
1554
1725
|
def foreign_key_for(from_table, **options)
|
1555
|
-
return unless
|
1726
|
+
return unless use_foreign_keys?
|
1556
1727
|
foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
|
1557
1728
|
end
|
1558
1729
|
|
@@ -1569,6 +1740,10 @@ module ActiveRecord
|
|
1569
1740
|
end
|
1570
1741
|
end
|
1571
1742
|
|
1743
|
+
def foreign_keys_enabled?
|
1744
|
+
@config.fetch(:foreign_keys, true)
|
1745
|
+
end
|
1746
|
+
|
1572
1747
|
def check_constraint_name(table_name, **options)
|
1573
1748
|
options.fetch(:name) do
|
1574
1749
|
expression = options.fetch(:expression)
|
@@ -1582,7 +1757,7 @@ module ActiveRecord
|
|
1582
1757
|
def check_constraint_for(table_name, **options)
|
1583
1758
|
return unless supports_check_constraints?
|
1584
1759
|
chk_name = check_constraint_name(table_name, **options)
|
1585
|
-
check_constraints(table_name).detect { |chk| chk.name
|
1760
|
+
check_constraints(table_name).detect { |chk| chk.defined_for?(name: chk_name, **options) }
|
1586
1761
|
end
|
1587
1762
|
|
1588
1763
|
def check_constraint_for!(table_name, expression: nil, **options)
|
@@ -1596,6 +1771,12 @@ module ActiveRecord
|
|
1596
1771
|
end
|
1597
1772
|
end
|
1598
1773
|
|
1774
|
+
def validate_table_length!(table_name)
|
1775
|
+
if table_name.length > table_name_length
|
1776
|
+
raise ArgumentError, "Table name '#{table_name}' is too long; the limit is #{table_name_length} characters"
|
1777
|
+
end
|
1778
|
+
end
|
1779
|
+
|
1599
1780
|
def extract_new_default_value(default_or_changes)
|
1600
1781
|
if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
|
1601
1782
|
default_or_changes[:to]
|
@@ -1609,29 +1790,8 @@ module ActiveRecord
|
|
1609
1790
|
column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
|
1610
1791
|
end
|
1611
1792
|
|
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)
|
1793
|
+
def reference_name_for_table(table_name)
|
1794
|
+
table_name.to_s.singularize
|
1635
1795
|
end
|
1636
1796
|
|
1637
1797
|
def add_column_for_alter(table_name, column_name, type, **options)
|
@@ -1640,6 +1800,11 @@ module ActiveRecord
|
|
1640
1800
|
schema_creation.accept(AddColumnDefinition.new(cd))
|
1641
1801
|
end
|
1642
1802
|
|
1803
|
+
def change_column_default_for_alter(table_name, column_name, default_or_changes)
|
1804
|
+
cd = build_change_column_default_definition(table_name, column_name, default_or_changes)
|
1805
|
+
schema_creation.accept(cd)
|
1806
|
+
end
|
1807
|
+
|
1643
1808
|
def rename_column_sql(table_name, column_name, new_column_name)
|
1644
1809
|
"RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
|
1645
1810
|
end
|
@@ -1674,8 +1839,8 @@ module ActiveRecord
|
|
1674
1839
|
|
1675
1840
|
if versions.is_a?(Array)
|
1676
1841
|
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
|
1677
|
-
sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
|
1678
|
-
sql << "
|
1842
|
+
sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
|
1843
|
+
sql << ";"
|
1679
1844
|
sql
|
1680
1845
|
else
|
1681
1846
|
"INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
|