activerecord 6.0.6 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +783 -910
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -3
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +22 -14
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +43 -26
- data/lib/active_record/associations/association_scope.rb +11 -15
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +9 -3
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +19 -13
- data/lib/active_record/associations/collection_proxy.rb +12 -5
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -2
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +29 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +63 -49
- data/lib/active_record/associations/preloader/association.rb +13 -5
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +5 -3
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations.rb +114 -11
- data/lib/active_record/attribute_assignment.rb +10 -8
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +1 -11
- data/lib/active_record/attribute_methods/primary_key.rb +6 -2
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -11
- data/lib/active_record/attribute_methods/serialization.rb +11 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
- data/lib/active_record/attribute_methods/write.rb +12 -20
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attributes.rb +32 -7
- data/lib/active_record/autosave_association.rb +47 -30
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +152 -22
- data/lib/active_record/coders/yaml_column.rb +2 -24
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
- data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +110 -30
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
- data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
- data/lib/active_record/connection_adapters/abstract_adapter.rb +49 -72
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -24
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -3
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +12 -53
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
- data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +30 -5
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_handling.rb +210 -71
- data/lib/active_record/core.rb +223 -66
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -40
- data/lib/active_record/database_configurations.rb +124 -85
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/enum.rb +27 -10
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -4
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -2
- data/lib/active_record/fixtures.rb +54 -8
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +34 -5
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +16 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +13 -16
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +26 -8
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +67 -17
- data/lib/active_record/migration.rb +113 -83
- data/lib/active_record/model_schema.rb +88 -13
- data/lib/active_record/nested_attributes.rb +2 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +50 -45
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +11 -6
- data/lib/active_record/railtie.rb +64 -44
- data/lib/active_record/railties/databases.rake +266 -95
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +60 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/calculations.rb +100 -43
- data/lib/active_record/relation/finder_methods.rb +44 -14
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +20 -23
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +57 -33
- data/lib/active_record/relation/query_methods.rb +318 -195
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -7
- data/lib/active_record/relation/where_clause.rb +104 -57
- data/lib/active_record/relation.rb +90 -64
- data/lib/active_record/result.rb +41 -33
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +2 -8
- data/lib/active_record/scoping/named.rb +1 -17
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +2 -2
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +39 -51
- data/lib/active_record/tasks/database_tasks.rb +139 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +36 -33
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +15 -64
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type.rb +8 -1
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +7 -14
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/predications.rb +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors/dot.rb +14 -2
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -4
- data/lib/arel/visitors/to_sql.rb +89 -78
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +5 -13
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
- data/lib/rails/generators/active_record/migration.rb +6 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +28 -30
- data/lib/active_record/advisory_lock_base.rb +0 -18
- data/lib/active_record/attribute_decorators.rb +0 -88
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -203
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -156
- data/lib/arel/visitors/oracle.rb +0 -158
- data/lib/arel/visitors/oracle12.rb +0 -65
- data/lib/arel/visitors/where_sql.rb +0 -22
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_record/migration/join_table"
|
4
3
|
require "active_support/core_ext/string/access"
|
5
|
-
require "active_support/deprecation"
|
6
4
|
require "digest/sha2"
|
7
5
|
|
8
6
|
module ActiveRecord
|
@@ -98,10 +96,14 @@ module ActiveRecord
|
|
98
96
|
# # Check an index with a custom name exists
|
99
97
|
# index_exists?(:suppliers, :company_id, name: "idx_company_id")
|
100
98
|
#
|
101
|
-
def index_exists?(table_name, column_name, options
|
102
|
-
column_names = Array(column_name).map(&:to_s)
|
99
|
+
def index_exists?(table_name, column_name, **options)
|
103
100
|
checks = []
|
104
|
-
|
101
|
+
|
102
|
+
if column_name.present?
|
103
|
+
column_names = Array(column_name).map(&:to_s)
|
104
|
+
checks << lambda { |i| Array(i.columns) == column_names }
|
105
|
+
end
|
106
|
+
|
105
107
|
checks << lambda { |i| i.unique } if options[:unique]
|
106
108
|
checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
|
107
109
|
|
@@ -291,37 +293,42 @@ module ActiveRecord
|
|
291
293
|
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
292
294
|
#
|
293
295
|
# See also TableDefinition#column for details on how to create columns.
|
294
|
-
def create_table(table_name, **options)
|
295
|
-
td = create_table_definition(table_name, **options)
|
296
|
+
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
|
297
|
+
td = create_table_definition(table_name, **extract_table_options!(options))
|
296
298
|
|
297
|
-
if
|
298
|
-
pk =
|
299
|
-
|
299
|
+
if id && !td.as
|
300
|
+
pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
|
301
|
+
|
302
|
+
if id.is_a?(Hash)
|
303
|
+
options.merge!(id.except(:type))
|
304
|
+
id = id.fetch(:type, :primary_key)
|
300
305
|
end
|
301
306
|
|
302
307
|
if pk.is_a?(Array)
|
303
308
|
td.primary_keys pk
|
304
309
|
else
|
305
|
-
td.primary_key pk,
|
310
|
+
td.primary_key pk, id, **options
|
306
311
|
end
|
307
312
|
end
|
308
313
|
|
309
314
|
yield td if block_given?
|
310
315
|
|
311
|
-
if
|
312
|
-
drop_table(table_name,
|
316
|
+
if force
|
317
|
+
drop_table(table_name, force: force, if_exists: true)
|
318
|
+
else
|
319
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
313
320
|
end
|
314
321
|
|
315
322
|
result = execute schema_creation.accept td
|
316
323
|
|
317
324
|
unless supports_indexes_in_create?
|
318
325
|
td.indexes.each do |column_name, index_options|
|
319
|
-
add_index(table_name, column_name, index_options)
|
326
|
+
add_index(table_name, column_name, **index_options, if_not_exists: td.if_not_exists)
|
320
327
|
end
|
321
328
|
end
|
322
329
|
|
323
330
|
if supports_comments? && !supports_comments_in_create?
|
324
|
-
if table_comment =
|
331
|
+
if table_comment = td.comment.presence
|
325
332
|
change_table_comment(table_name, table_comment)
|
326
333
|
end
|
327
334
|
|
@@ -420,6 +427,12 @@ module ActiveRecord
|
|
420
427
|
# t.column :name, :string, limit: 60
|
421
428
|
# end
|
422
429
|
#
|
430
|
+
# ====== Change type of a column
|
431
|
+
#
|
432
|
+
# change_table(:suppliers) do |t|
|
433
|
+
# t.change :metadata, :json
|
434
|
+
# end
|
435
|
+
#
|
423
436
|
# ====== Add 2 integer columns
|
424
437
|
#
|
425
438
|
# change_table(:suppliers) do |t|
|
@@ -499,6 +512,7 @@ module ActiveRecord
|
|
499
512
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
500
513
|
# In that case, +options+ and the block will be used by #create_table.
|
501
514
|
def drop_table(table_name, **options)
|
515
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
502
516
|
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
503
517
|
end
|
504
518
|
|
@@ -534,6 +548,9 @@ module ActiveRecord
|
|
534
548
|
# column will have the same collation as the table.
|
535
549
|
# * <tt>:comment</tt> -
|
536
550
|
# Specifies the comment for the column. This option is ignored by some backends.
|
551
|
+
# * <tt>:if_not_exists</tt> -
|
552
|
+
# Specifies if the column already exists to not try to re-add it. This will avoid
|
553
|
+
# duplicate column errors.
|
537
554
|
#
|
538
555
|
# Note: The precision is the total number of significant digits,
|
539
556
|
# and the scale is the number of digits that can be stored following
|
@@ -554,8 +571,6 @@ module ActiveRecord
|
|
554
571
|
# but the maximum supported <tt>:precision</tt> is 16. No default.
|
555
572
|
# * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
|
556
573
|
# Default is (38,0).
|
557
|
-
# * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
|
558
|
-
# Default unknown.
|
559
574
|
# * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
|
560
575
|
# Default (38,0).
|
561
576
|
#
|
@@ -585,20 +600,37 @@ module ActiveRecord
|
|
585
600
|
# # Defines a column with a database-specific type.
|
586
601
|
# add_column(:shapes, :triangle, 'polygon')
|
587
602
|
# # ALTER TABLE "shapes" ADD "triangle" polygon
|
603
|
+
#
|
604
|
+
# # Ignores the method call if the column exists
|
605
|
+
# add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
|
588
606
|
def add_column(table_name, column_name, type, **options)
|
607
|
+
return if options[:if_not_exists] == true && column_exists?(table_name, column_name, type)
|
608
|
+
|
589
609
|
at = create_alter_table table_name
|
590
610
|
at.add_column(column_name, type, **options)
|
591
611
|
execute schema_creation.accept at
|
592
612
|
end
|
593
613
|
|
614
|
+
def add_columns(table_name, *column_names, type:, **options) # :nodoc:
|
615
|
+
column_names.each do |column_name|
|
616
|
+
add_column(table_name, column_name, type, **options)
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
594
620
|
# Removes the given columns from the table definition.
|
595
621
|
#
|
596
622
|
# remove_columns(:suppliers, :qualification, :experience)
|
597
623
|
#
|
598
|
-
|
599
|
-
|
624
|
+
# +type+ and other column options can be passed to make migration reversible.
|
625
|
+
#
|
626
|
+
# remove_columns(:suppliers, :qualification, :experience, type: :string, null: false)
|
627
|
+
def remove_columns(table_name, *column_names, type: nil, **options)
|
628
|
+
if column_names.empty?
|
629
|
+
raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
|
630
|
+
end
|
631
|
+
|
600
632
|
column_names.each do |column_name|
|
601
|
-
remove_column(table_name, column_name)
|
633
|
+
remove_column(table_name, column_name, type, **options)
|
602
634
|
end
|
603
635
|
end
|
604
636
|
|
@@ -610,7 +642,15 @@ module ActiveRecord
|
|
610
642
|
# to provide these in a migration's +change+ method so it can be reverted.
|
611
643
|
# In that case, +type+ and +options+ will be used by #add_column.
|
612
644
|
# Indexes on the column are automatically removed.
|
645
|
+
#
|
646
|
+
# If the options provided include an +if_exists+ key, it will be used to check if the
|
647
|
+
# column does not exist. This will silently ignore the migration rather than raising
|
648
|
+
# if the column was already used.
|
649
|
+
#
|
650
|
+
# remove_column(:suppliers, :qualification, if_exists: true)
|
613
651
|
def remove_column(table_name, column_name, type = nil, **options)
|
652
|
+
return if options[:if_exists] == true && !column_exists?(table_name, column_name)
|
653
|
+
|
614
654
|
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, **options)}"
|
615
655
|
end
|
616
656
|
|
@@ -620,7 +660,7 @@ module ActiveRecord
|
|
620
660
|
# change_column(:suppliers, :name, :string, limit: 80)
|
621
661
|
# change_column(:accounts, :description, :text)
|
622
662
|
#
|
623
|
-
def change_column(table_name, column_name, type, options
|
663
|
+
def change_column(table_name, column_name, type, **options)
|
624
664
|
raise NotImplementedError, "change_column is not implemented"
|
625
665
|
end
|
626
666
|
|
@@ -682,7 +722,17 @@ module ActiveRecord
|
|
682
722
|
#
|
683
723
|
# generates:
|
684
724
|
#
|
685
|
-
# CREATE INDEX
|
725
|
+
# CREATE INDEX index_suppliers_on_name ON suppliers(name)
|
726
|
+
#
|
727
|
+
# ====== Creating a index which already exists
|
728
|
+
#
|
729
|
+
# add_index(:suppliers, :name, if_not_exists: true)
|
730
|
+
#
|
731
|
+
# generates:
|
732
|
+
#
|
733
|
+
# CREATE INDEX IF NOT EXISTS index_suppliers_on_name ON suppliers(name)
|
734
|
+
#
|
735
|
+
# Note: Not supported by MySQL.
|
686
736
|
#
|
687
737
|
# ====== Creating a unique index
|
688
738
|
#
|
@@ -690,7 +740,7 @@ module ActiveRecord
|
|
690
740
|
#
|
691
741
|
# generates:
|
692
742
|
#
|
693
|
-
# CREATE UNIQUE INDEX
|
743
|
+
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id)
|
694
744
|
#
|
695
745
|
# ====== Creating a named index
|
696
746
|
#
|
@@ -720,7 +770,7 @@ module ActiveRecord
|
|
720
770
|
#
|
721
771
|
# ====== Creating an index with a sort order (desc or asc, asc is the default)
|
722
772
|
#
|
723
|
-
# add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
|
773
|
+
# add_index(:accounts, [:branch_id, :party_id, :surname], name: 'by_branch_desc_party', order: {branch_id: :desc, party_id: :asc})
|
724
774
|
#
|
725
775
|
# generates:
|
726
776
|
#
|
@@ -736,7 +786,7 @@ module ActiveRecord
|
|
736
786
|
#
|
737
787
|
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
|
738
788
|
#
|
739
|
-
# Note: Partial indexes are only supported for PostgreSQL and SQLite
|
789
|
+
# Note: Partial indexes are only supported for PostgreSQL and SQLite.
|
740
790
|
#
|
741
791
|
# ====== Creating an index with a specific method
|
742
792
|
#
|
@@ -782,9 +832,11 @@ module ActiveRecord
|
|
782
832
|
# Concurrently adding an index is not supported in a transaction.
|
783
833
|
#
|
784
834
|
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
785
|
-
def add_index(table_name, column_name, options
|
786
|
-
|
787
|
-
|
835
|
+
def add_index(table_name, column_name, **options)
|
836
|
+
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
837
|
+
|
838
|
+
create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
|
839
|
+
execute schema_creation.accept(create_index)
|
788
840
|
end
|
789
841
|
|
790
842
|
# Removes the given index from the table.
|
@@ -805,6 +857,15 @@ module ActiveRecord
|
|
805
857
|
#
|
806
858
|
# remove_index :accounts, name: :by_branch_party
|
807
859
|
#
|
860
|
+
# Removes the index on +branch_id+ named +by_branch_party+ in the +accounts+ table.
|
861
|
+
#
|
862
|
+
# remove_index :accounts, :branch_id, name: :by_branch_party
|
863
|
+
#
|
864
|
+
# Checks if the index exists before trying to remove it. Will silently ignore indexes that
|
865
|
+
# don't exist.
|
866
|
+
#
|
867
|
+
# remove_index :accounts, if_exists: true
|
868
|
+
#
|
808
869
|
# Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
|
809
870
|
#
|
810
871
|
# remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
|
@@ -814,8 +875,11 @@ module ActiveRecord
|
|
814
875
|
# Concurrently removing an index is not supported in a transaction.
|
815
876
|
#
|
816
877
|
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
817
|
-
def remove_index(table_name,
|
818
|
-
|
878
|
+
def remove_index(table_name, column_name = nil, **options)
|
879
|
+
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
880
|
+
|
881
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
882
|
+
|
819
883
|
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
820
884
|
end
|
821
885
|
|
@@ -826,6 +890,8 @@ module ActiveRecord
|
|
826
890
|
# rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
|
827
891
|
#
|
828
892
|
def rename_index(table_name, old_name, new_name)
|
893
|
+
old_name = old_name.to_s
|
894
|
+
new_name = new_name.to_s
|
829
895
|
validate_index_length!(table_name, new_name)
|
830
896
|
|
831
897
|
# this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
|
@@ -867,7 +933,9 @@ module ActiveRecord
|
|
867
933
|
# Add an appropriate index. Defaults to true.
|
868
934
|
# See #add_index for usage of this option.
|
869
935
|
# [<tt>:foreign_key</tt>]
|
870
|
-
# Add an appropriate foreign key constraint. Defaults to false
|
936
|
+
# Add an appropriate foreign key constraint. Defaults to false, pass true
|
937
|
+
# to add. In case the join table can't be inferred from the association
|
938
|
+
# pass <tt>:to_table</tt> with the appropriate table name.
|
871
939
|
# [<tt>:polymorphic</tt>]
|
872
940
|
# Whether an additional +_type+ column should be added. Defaults to false.
|
873
941
|
# [<tt>:null</tt>]
|
@@ -899,7 +967,7 @@ module ActiveRecord
|
|
899
967
|
#
|
900
968
|
# ====== Create a supplier_id column and a foreign key to the firms table
|
901
969
|
#
|
902
|
-
# add_reference(:products, :supplier, foreign_key: {to_table: :firms})
|
970
|
+
# add_reference(:products, :supplier, foreign_key: { to_table: :firms })
|
903
971
|
#
|
904
972
|
def add_reference(table_name, ref_name, **options)
|
905
973
|
ReferenceDefinition.new(ref_name, **options).add_to(update_table_definition(table_name, self))
|
@@ -1061,6 +1129,60 @@ module ActiveRecord
|
|
1061
1129
|
options
|
1062
1130
|
end
|
1063
1131
|
|
1132
|
+
# Returns an array of check constraints for the given table.
|
1133
|
+
# The check constraints are represented as CheckConstraintDefinition objects.
|
1134
|
+
def check_constraints(table_name)
|
1135
|
+
raise NotImplementedError
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
# Adds a new check constraint to the table. +expression+ is a String
|
1139
|
+
# representation of verifiable boolean condition.
|
1140
|
+
#
|
1141
|
+
# add_check_constraint :products, "price > 0", name: "price_check"
|
1142
|
+
#
|
1143
|
+
# generates:
|
1144
|
+
#
|
1145
|
+
# ALTER TABLE "products" ADD CONSTRAINT price_check CHECK (price > 0)
|
1146
|
+
#
|
1147
|
+
# The +options+ hash can include the following keys:
|
1148
|
+
# [<tt>:name</tt>]
|
1149
|
+
# The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
|
1150
|
+
# [<tt>:validate</tt>]
|
1151
|
+
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
1152
|
+
def add_check_constraint(table_name, expression, **options)
|
1153
|
+
return unless supports_check_constraints?
|
1154
|
+
|
1155
|
+
options = check_constraint_options(table_name, expression, options)
|
1156
|
+
at = create_alter_table(table_name)
|
1157
|
+
at.add_check_constraint(expression, options)
|
1158
|
+
|
1159
|
+
execute schema_creation.accept(at)
|
1160
|
+
end
|
1161
|
+
|
1162
|
+
def check_constraint_options(table_name, expression, options) # :nodoc:
|
1163
|
+
options = options.dup
|
1164
|
+
options[:name] ||= check_constraint_name(table_name, expression: expression, **options)
|
1165
|
+
options
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
# Removes the given check constraint from the table.
|
1169
|
+
#
|
1170
|
+
# remove_check_constraint :products, name: "price_check"
|
1171
|
+
#
|
1172
|
+
# The +expression+ parameter will be ignored if present. It can be helpful
|
1173
|
+
# to provide this in a migration's +change+ method so it can be reverted.
|
1174
|
+
# In that case, +expression+ will be used by #add_check_constraint.
|
1175
|
+
def remove_check_constraint(table_name, expression = nil, **options)
|
1176
|
+
return unless supports_check_constraints?
|
1177
|
+
|
1178
|
+
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
1179
|
+
|
1180
|
+
at = create_alter_table(table_name)
|
1181
|
+
at.drop_check_constraint(chk_name_to_delete)
|
1182
|
+
|
1183
|
+
execute schema_creation.accept(at)
|
1184
|
+
end
|
1185
|
+
|
1064
1186
|
def dump_schema_information # :nodoc:
|
1065
1187
|
versions = schema_migration.all_versions
|
1066
1188
|
insert_versions_sql(versions) if versions.any?
|
@@ -1070,13 +1192,7 @@ module ActiveRecord
|
|
1070
1192
|
{ primary_key: true }
|
1071
1193
|
end
|
1072
1194
|
|
1073
|
-
def assume_migrated_upto_version(version
|
1074
|
-
unless migrations_paths.nil?
|
1075
|
-
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
1076
|
-
Passing migrations_paths to #assume_migrated_upto_version is deprecated and will be removed in Rails 6.1.
|
1077
|
-
MSG
|
1078
|
-
end
|
1079
|
-
|
1195
|
+
def assume_migrated_upto_version(version)
|
1080
1196
|
version = version.to_i
|
1081
1197
|
sm_table = quote_table_name(schema_migration.table_name)
|
1082
1198
|
|
@@ -1169,36 +1285,43 @@ module ActiveRecord
|
|
1169
1285
|
Table.new(table_name, base)
|
1170
1286
|
end
|
1171
1287
|
|
1172
|
-
def add_index_options(table_name, column_name,
|
1173
|
-
|
1288
|
+
def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
|
1289
|
+
options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm)
|
1174
1290
|
|
1175
|
-
|
1291
|
+
column_names = index_column_names(column_name)
|
1176
1292
|
|
1177
|
-
|
1178
|
-
index_type ||= options[:unique] ? "UNIQUE" : ""
|
1179
|
-
index_name = options[:name].to_s if options.key?(:name)
|
1293
|
+
index_name = name&.to_s
|
1180
1294
|
index_name ||= index_name(table_name, column_names)
|
1181
1295
|
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1296
|
+
validate_index_length!(table_name, index_name, internal)
|
1297
|
+
|
1298
|
+
index = IndexDefinition.new(
|
1299
|
+
table_name, index_name,
|
1300
|
+
options[:unique],
|
1301
|
+
column_names,
|
1302
|
+
lengths: options[:length] || {},
|
1303
|
+
orders: options[:order] || {},
|
1304
|
+
opclasses: options[:opclass] || {},
|
1305
|
+
where: options[:where],
|
1306
|
+
type: options[:type],
|
1307
|
+
using: options[:using],
|
1308
|
+
comment: options[:comment]
|
1309
|
+
)
|
1310
|
+
|
1311
|
+
[index, index_algorithm(options[:algorithm]), if_not_exists]
|
1312
|
+
end
|
1193
1313
|
|
1194
|
-
|
1314
|
+
def index_algorithm(algorithm) # :nodoc:
|
1315
|
+
index_algorithms.fetch(algorithm) do
|
1316
|
+
raise ArgumentError, "Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}"
|
1317
|
+
end if algorithm
|
1318
|
+
end
|
1195
1319
|
|
1196
|
-
|
1197
|
-
|
1320
|
+
def quoted_columns_for_index(column_names, options) # :nodoc:
|
1321
|
+
quoted_columns = column_names.each_with_object({}) do |name, result|
|
1322
|
+
result[name.to_sym] = quote_column_name(name).dup
|
1198
1323
|
end
|
1199
|
-
|
1200
|
-
|
1201
|
-
[index_name, index_type, index_columns, index_options, algorithm, using, comment]
|
1324
|
+
add_options_for_index_columns(quoted_columns, **options).values.join(", ")
|
1202
1325
|
end
|
1203
1326
|
|
1204
1327
|
def options_include_default?(options)
|
@@ -1259,24 +1382,13 @@ module ActiveRecord
|
|
1259
1382
|
quoted_columns
|
1260
1383
|
end
|
1261
1384
|
|
1262
|
-
def
|
1263
|
-
return [
|
1264
|
-
|
1265
|
-
quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
|
1266
|
-
add_options_for_index_columns(quoted_columns, **options).values
|
1267
|
-
end
|
1268
|
-
|
1269
|
-
def index_name_for_remove(table_name, options = {})
|
1270
|
-
return options[:name] if can_remove_index_by_name?(options)
|
1385
|
+
def index_name_for_remove(table_name, column_name, options)
|
1386
|
+
return options[:name] if can_remove_index_by_name?(column_name, options)
|
1271
1387
|
|
1272
1388
|
checks = []
|
1273
1389
|
|
1274
|
-
if options.
|
1275
|
-
|
1276
|
-
column_names = index_column_names(options[:column])
|
1277
|
-
else
|
1278
|
-
column_names = index_column_names(options)
|
1279
|
-
end
|
1390
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
|
1391
|
+
column_names = index_column_names(column_name || options[:column])
|
1280
1392
|
|
1281
1393
|
if column_names.present?
|
1282
1394
|
checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
|
@@ -1322,14 +1434,18 @@ module ActiveRecord
|
|
1322
1434
|
SchemaCreation.new(self)
|
1323
1435
|
end
|
1324
1436
|
|
1325
|
-
def create_table_definition(
|
1326
|
-
TableDefinition.new(self,
|
1437
|
+
def create_table_definition(name, **options)
|
1438
|
+
TableDefinition.new(self, name, **options)
|
1327
1439
|
end
|
1328
1440
|
|
1329
1441
|
def create_alter_table(name)
|
1330
1442
|
AlterTable.new create_table_definition(name)
|
1331
1443
|
end
|
1332
1444
|
|
1445
|
+
def extract_table_options!(options)
|
1446
|
+
options.extract!(:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation)
|
1447
|
+
end
|
1448
|
+
|
1333
1449
|
def fetch_type_metadata(sql_type)
|
1334
1450
|
cast_type = lookup_cast_type(sql_type)
|
1335
1451
|
SqlTypeMetadata.new(
|
@@ -1390,11 +1506,30 @@ module ActiveRecord
|
|
1390
1506
|
end
|
1391
1507
|
end
|
1392
1508
|
|
1393
|
-
def
|
1394
|
-
|
1509
|
+
def check_constraint_name(table_name, **options)
|
1510
|
+
options.fetch(:name) do
|
1511
|
+
expression = options.fetch(:expression)
|
1512
|
+
identifier = "#{table_name}_#{expression}_chk"
|
1513
|
+
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
|
1395
1514
|
|
1396
|
-
|
1397
|
-
|
1515
|
+
"chk_rails_#{hashed_identifier}"
|
1516
|
+
end
|
1517
|
+
end
|
1518
|
+
|
1519
|
+
def check_constraint_for(table_name, **options)
|
1520
|
+
return unless supports_check_constraints?
|
1521
|
+
chk_name = check_constraint_name(table_name, **options)
|
1522
|
+
check_constraints(table_name).detect { |chk| chk.name == chk_name }
|
1523
|
+
end
|
1524
|
+
|
1525
|
+
def check_constraint_for!(table_name, expression: nil, **options)
|
1526
|
+
check_constraint_for(table_name, expression: expression, **options) ||
|
1527
|
+
raise(ArgumentError, "Table '#{table_name}' has no check constraint for #{expression || options}")
|
1528
|
+
end
|
1529
|
+
|
1530
|
+
def validate_index_length!(table_name, new_name, internal = false)
|
1531
|
+
if new_name.length > index_name_length
|
1532
|
+
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
|
1398
1533
|
end
|
1399
1534
|
end
|
1400
1535
|
|
@@ -1407,8 +1542,8 @@ module ActiveRecord
|
|
1407
1542
|
end
|
1408
1543
|
alias :extract_new_comment_value :extract_new_default_value
|
1409
1544
|
|
1410
|
-
def can_remove_index_by_name?(options)
|
1411
|
-
|
1545
|
+
def can_remove_index_by_name?(column_name, options)
|
1546
|
+
column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
|
1412
1547
|
end
|
1413
1548
|
|
1414
1549
|
def bulk_change_table(table_name, operations)
|
@@ -1442,6 +1577,10 @@ module ActiveRecord
|
|
1442
1577
|
schema_creation.accept(AddColumnDefinition.new(cd))
|
1443
1578
|
end
|
1444
1579
|
|
1580
|
+
def rename_column_sql(table_name, column_name, new_column_name)
|
1581
|
+
"RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
|
1582
|
+
end
|
1583
|
+
|
1445
1584
|
def remove_column_for_alter(table_name, column_name, type = nil, **options)
|
1446
1585
|
"DROP COLUMN #{quote_column_name(column_name)}"
|
1447
1586
|
end
|