activerecord 7.2.3 → 8.1.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 +612 -1055
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/association.rb +35 -11
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +17 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +1 -1
- data/lib/active_record/associations/collection_proxy.rb +22 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +192 -24
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_methods/primary_key.rb +4 -8
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +16 -3
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
- data/lib/active_record/attributes.rb +3 -0
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/base.rb +1 -2
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +35 -28
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +412 -88
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +137 -75
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +27 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -25
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +32 -35
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +122 -32
- data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
- data/lib/active_record/connection_adapters/abstract_adapter.rb +150 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +63 -52
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +73 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -10
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +14 -33
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +71 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +139 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +78 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +3 -5
- data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +13 -14
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +102 -37
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +38 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -18
- data/lib/active_record/connection_adapters.rb +1 -56
- data/lib/active_record/connection_handling.rb +25 -2
- data/lib/active_record/core.rb +33 -17
- data/lib/active_record/counter_cache.rb +33 -8
- data/lib/active_record/database_configurations/database_config.rb +9 -1
- data/lib/active_record/database_configurations/hash_config.rb +67 -9
- data/lib/active_record/database_configurations/url_config.rb +13 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +8 -8
- data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
- data/lib/active_record/encryption/encryptor.rb +28 -8
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +33 -30
- data/lib/active_record/errors.rb +33 -9
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixtures.rb +2 -4
- data/lib/active_record/future_result.rb +15 -9
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +14 -9
- data/lib/active_record/locking/optimistic.rb +8 -1
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -13
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +45 -12
- data/lib/active_record/migration/compatibility.rb +37 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +48 -42
- data/lib/active_record/model_schema.rb +38 -13
- data/lib/active_record/nested_attributes.rb +6 -6
- data/lib/active_record/persistence.rb +162 -133
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +100 -52
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +8 -8
- data/lib/active_record/railtie.rb +35 -30
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +26 -38
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +53 -21
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +147 -73
- data/lib/active_record/relation/calculations.rb +52 -40
- data/lib/active_record/relation/delegation.rb +25 -15
- data/lib/active_record/relation/finder_methods.rb +40 -24
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -9
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +22 -7
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +140 -86
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation/where_clause.rb +2 -9
- data/lib/active_record/relation.rb +107 -75
- data/lib/active_record/result.rb +109 -24
- data/lib/active_record/runtime_registry.rb +42 -58
- data/lib/active_record/sanitization.rb +9 -6
- data/lib/active_record/schema_dumper.rb +18 -11
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/signed_id.rb +43 -15
- data/lib/active_record/statement_cache.rb +24 -20
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +6 -23
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +85 -85
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +7 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +39 -2
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +37 -16
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +13 -2
- data/lib/active_record/type/serialized.rb +16 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +84 -49
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/crud.rb +6 -11
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/predications.rb +1 -3
- data/lib/arel/select_manager.rb +7 -2
- data/lib/arel/table.rb +3 -7
- data/lib/arel/visitors/dot.rb +0 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +3 -21
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +16 -13
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
module ActiveRecord
|
|
5
4
|
module ConnectionAdapters # :nodoc:
|
|
6
5
|
# Abstract representation of an index definition on a table. Instances of
|
|
@@ -76,7 +75,7 @@ module ActiveRecord
|
|
|
76
75
|
# are typically created by methods in TableDefinition, and added to the
|
|
77
76
|
# +columns+ attribute of said TableDefinition object, in order to be used
|
|
78
77
|
# for generating a number of table creation or table changing SQL statements.
|
|
79
|
-
ColumnDefinition = Struct.new(:name, :type, :options, :sql_type) do # :nodoc:
|
|
78
|
+
ColumnDefinition = Struct.new(:name, :type, :options, :sql_type, :cast_type) do # :nodoc:
|
|
80
79
|
self::OPTION_NAMES = [
|
|
81
80
|
:limit,
|
|
82
81
|
:precision,
|
|
@@ -109,6 +108,10 @@ module ActiveRecord
|
|
|
109
108
|
def aliased_types(name, fallback)
|
|
110
109
|
"timestamp" == name ? :datetime : fallback
|
|
111
110
|
end
|
|
111
|
+
|
|
112
|
+
def fetch_cast_type(connection)
|
|
113
|
+
cast_type
|
|
114
|
+
end
|
|
112
115
|
end
|
|
113
116
|
|
|
114
117
|
AddColumnDefinition = Struct.new(:column) # :nodoc:
|
|
@@ -304,44 +307,32 @@ module ActiveRecord
|
|
|
304
307
|
module ColumnMethods
|
|
305
308
|
extend ActiveSupport::Concern
|
|
306
309
|
|
|
307
|
-
# Appends a primary key definition to the table definition.
|
|
308
|
-
# Can be called multiple times, but this is probably not a good idea.
|
|
309
|
-
def primary_key(name, type = :primary_key, **options)
|
|
310
|
-
column(name, type, **options.merge(primary_key: true))
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
##
|
|
314
|
-
# :method: column
|
|
315
|
-
# :call-seq: column(name, type, **options)
|
|
316
|
-
#
|
|
317
|
-
# Appends a column or columns of a specified type.
|
|
318
|
-
#
|
|
319
|
-
# t.string(:goat)
|
|
320
|
-
# t.string(:goat, :sheep)
|
|
321
|
-
#
|
|
322
|
-
# See TableDefinition#column
|
|
323
|
-
|
|
324
|
-
included do
|
|
325
|
-
define_column_methods :bigint, :binary, :boolean, :date, :datetime, :decimal,
|
|
326
|
-
:float, :integer, :json, :string, :text, :time, :timestamp, :virtual
|
|
327
|
-
|
|
328
|
-
alias :blob :binary
|
|
329
|
-
alias :numeric :decimal
|
|
330
|
-
end
|
|
331
|
-
|
|
332
310
|
class_methods do
|
|
333
|
-
|
|
334
|
-
column_types
|
|
335
|
-
|
|
311
|
+
private
|
|
312
|
+
def define_column_methods(*column_types) # :nodoc:
|
|
313
|
+
column_types.each do |column_type|
|
|
314
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
336
315
|
def #{column_type}(*names, **options)
|
|
337
316
|
raise ArgumentError, "Missing column name(s) for #{column_type}" if names.empty?
|
|
338
317
|
names.each { |name| column(name, :#{column_type}, **options) }
|
|
339
318
|
end
|
|
340
|
-
|
|
319
|
+
RUBY
|
|
320
|
+
end
|
|
341
321
|
end
|
|
342
|
-
end
|
|
343
|
-
private :define_column_methods
|
|
344
322
|
end
|
|
323
|
+
extend ClassMethods
|
|
324
|
+
|
|
325
|
+
# Appends a primary key definition to the table definition.
|
|
326
|
+
# Can be called multiple times, but this is probably not a good idea.
|
|
327
|
+
def primary_key(name, type = :primary_key, **options)
|
|
328
|
+
column(name, type, **options, primary_key: true)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
define_column_methods :bigint, :binary, :boolean, :date, :datetime, :decimal,
|
|
332
|
+
:float, :integer, :json, :string, :text, :time, :timestamp, :virtual
|
|
333
|
+
|
|
334
|
+
alias :blob :binary
|
|
335
|
+
alias :numeric :decimal
|
|
345
336
|
end
|
|
346
337
|
|
|
347
338
|
# = Active Record Connection Adapters \Table \Definition
|
|
@@ -352,7 +343,7 @@ module ActiveRecord
|
|
|
352
343
|
# Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
|
|
353
344
|
# is actually of this type:
|
|
354
345
|
#
|
|
355
|
-
# class SomeMigration < ActiveRecord::Migration[
|
|
346
|
+
# class SomeMigration < ActiveRecord::Migration[8.1]
|
|
356
347
|
# def up
|
|
357
348
|
# create_table :foo do |t|
|
|
358
349
|
# puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
|
|
@@ -626,6 +617,7 @@ module ActiveRecord
|
|
|
626
617
|
attr_reader :adds
|
|
627
618
|
attr_reader :foreign_key_adds, :foreign_key_drops
|
|
628
619
|
attr_reader :check_constraint_adds, :check_constraint_drops
|
|
620
|
+
attr_reader :constraint_drops
|
|
629
621
|
|
|
630
622
|
def initialize(td)
|
|
631
623
|
@td = td
|
|
@@ -634,6 +626,7 @@ module ActiveRecord
|
|
|
634
626
|
@foreign_key_drops = []
|
|
635
627
|
@check_constraint_adds = []
|
|
636
628
|
@check_constraint_drops = []
|
|
629
|
+
@constraint_drops = []
|
|
637
630
|
end
|
|
638
631
|
|
|
639
632
|
def name; @td.name; end
|
|
@@ -654,6 +647,10 @@ module ActiveRecord
|
|
|
654
647
|
@check_constraint_drops << constraint_name
|
|
655
648
|
end
|
|
656
649
|
|
|
650
|
+
def drop_constraint(constraint_name)
|
|
651
|
+
@constraint_drops << constraint_name
|
|
652
|
+
end
|
|
653
|
+
|
|
657
654
|
def add_column(name, type, **options)
|
|
658
655
|
name = name.to_s
|
|
659
656
|
type = type.to_sym
|
|
@@ -760,7 +757,7 @@ module ActiveRecord
|
|
|
760
757
|
# end
|
|
761
758
|
#
|
|
762
759
|
# See {connection.index_exists?}[rdoc-ref:SchemaStatements#index_exists?]
|
|
763
|
-
def index_exists?(column_name, **options)
|
|
760
|
+
def index_exists?(column_name = nil, **options)
|
|
764
761
|
@base.index_exists?(name, column_name, **options)
|
|
765
762
|
end
|
|
766
763
|
|
|
@@ -85,7 +85,8 @@ module ActiveRecord
|
|
|
85
85
|
|
|
86
86
|
def schema_default(column)
|
|
87
87
|
return unless column.has_default?
|
|
88
|
-
|
|
88
|
+
# TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
|
|
89
|
+
type = column.fetch_cast_type(@connection)
|
|
89
90
|
default = type.deserialize(column.default)
|
|
90
91
|
if default.nil?
|
|
91
92
|
schema_expression(column)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "active_support/core_ext/string/access"
|
|
4
|
+
require "active_support/core_ext/string/filters"
|
|
4
5
|
require "openssl"
|
|
5
6
|
|
|
6
7
|
module ActiveRecord
|
|
@@ -99,7 +100,7 @@ module ActiveRecord
|
|
|
99
100
|
# # Check a valid index exists (PostgreSQL only)
|
|
100
101
|
# index_exists?(:suppliers, :company_id, valid: true)
|
|
101
102
|
#
|
|
102
|
-
def index_exists?(table_name, column_name, **options)
|
|
103
|
+
def index_exists?(table_name, column_name = nil, **options)
|
|
103
104
|
indexes(table_name).any? { |i| i.defined_for?(column_name, **options) }
|
|
104
105
|
end
|
|
105
106
|
|
|
@@ -348,11 +349,22 @@ module ActiveRecord
|
|
|
348
349
|
# # Creates a table called 'assemblies_parts' with no id.
|
|
349
350
|
# create_join_table(:assemblies, :parts)
|
|
350
351
|
#
|
|
352
|
+
# # Creates a table called 'paper_boxes_papers' with no id.
|
|
353
|
+
# create_join_table('papers', 'paper_boxes')
|
|
354
|
+
#
|
|
355
|
+
# A duplicate prefix is combined into a single prefix. This is useful for
|
|
356
|
+
# namespaced models like Music::Artist and Music::Record:
|
|
357
|
+
#
|
|
358
|
+
# # Creates a table called 'music_artists_records' with no id.
|
|
359
|
+
# create_join_table('music_artists', 'music_records')
|
|
360
|
+
#
|
|
361
|
+
# See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference]
|
|
362
|
+
# for details of the options you can use in +column_options+. +column_options+
|
|
363
|
+
# will be applied to both columns.
|
|
364
|
+
#
|
|
351
365
|
# You can pass an +options+ hash which can include the following keys:
|
|
352
366
|
# [<tt>:table_name</tt>]
|
|
353
367
|
# Sets the table name, overriding the default.
|
|
354
|
-
# [<tt>:column_options</tt>]
|
|
355
|
-
# Any extra options you want appended to the columns definition.
|
|
356
368
|
# [<tt>:options</tt>]
|
|
357
369
|
# Any extra options you want appended to the table definition.
|
|
358
370
|
# [<tt>:temporary</tt>]
|
|
@@ -369,6 +381,19 @@ module ActiveRecord
|
|
|
369
381
|
# t.index :category_id
|
|
370
382
|
# end
|
|
371
383
|
#
|
|
384
|
+
# ====== Add foreign keys with delete cascade
|
|
385
|
+
#
|
|
386
|
+
# create_join_table(:assemblies, :parts, column_options: { foreign_key: { on_delete: :cascade } })
|
|
387
|
+
#
|
|
388
|
+
# generates:
|
|
389
|
+
#
|
|
390
|
+
# CREATE TABLE assemblies_parts (
|
|
391
|
+
# assembly_id bigint NOT NULL,
|
|
392
|
+
# part_id bigint NOT NULL,
|
|
393
|
+
# CONSTRAINT fk_rails_0d8a572d89 FOREIGN KEY ("assembly_id") REFERENCES "assemblies" ("id") ON DELETE CASCADE,
|
|
394
|
+
# CONSTRAINT fk_rails_ec7b48402b FOREIGN KEY ("part_id") REFERENCES "parts" ("id") ON DELETE CASCADE
|
|
395
|
+
# )
|
|
396
|
+
#
|
|
372
397
|
# ====== Add a backend specific option to the generated SQL (MySQL)
|
|
373
398
|
#
|
|
374
399
|
# create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
|
|
@@ -519,7 +544,7 @@ module ActiveRecord
|
|
|
519
544
|
raise NotImplementedError, "rename_table is not implemented"
|
|
520
545
|
end
|
|
521
546
|
|
|
522
|
-
# Drops a table from the database.
|
|
547
|
+
# Drops a table or tables from the database.
|
|
523
548
|
#
|
|
524
549
|
# [<tt>:force</tt>]
|
|
525
550
|
# Set to +:cascade+ to drop dependent objects as well.
|
|
@@ -530,17 +555,19 @@ module ActiveRecord
|
|
|
530
555
|
#
|
|
531
556
|
# Although this command ignores most +options+ and the block if one is given,
|
|
532
557
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
|
533
|
-
# In that case, +options+ and the block will be used by #create_table.
|
|
534
|
-
def drop_table(
|
|
535
|
-
|
|
536
|
-
|
|
558
|
+
# In that case, +options+ and the block will be used by #create_table except if you provide more than one table which is not supported.
|
|
559
|
+
def drop_table(*table_names, **options)
|
|
560
|
+
table_names.each do |table_name|
|
|
561
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
|
562
|
+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
|
563
|
+
end
|
|
537
564
|
end
|
|
538
565
|
|
|
539
566
|
# Add a new +type+ column named +column_name+ to +table_name+.
|
|
540
567
|
#
|
|
541
568
|
# See {ActiveRecord::ConnectionAdapters::TableDefinition.column}[rdoc-ref:ActiveRecord::ConnectionAdapters::TableDefinition#column].
|
|
542
569
|
#
|
|
543
|
-
# The +type+ parameter is normally one of the
|
|
570
|
+
# The +type+ parameter is normally one of the migration's native types,
|
|
544
571
|
# which is one of the following:
|
|
545
572
|
# <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
|
|
546
573
|
# <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
|
|
@@ -847,6 +874,16 @@ module ActiveRecord
|
|
|
847
874
|
#
|
|
848
875
|
# Note: only supported by PostgreSQL.
|
|
849
876
|
#
|
|
877
|
+
# ====== Creating an index where NULLs are treated equally
|
|
878
|
+
#
|
|
879
|
+
# add_index(:people, :last_name, nulls_not_distinct: true)
|
|
880
|
+
#
|
|
881
|
+
# generates:
|
|
882
|
+
#
|
|
883
|
+
# CREATE INDEX index_people_on_last_name ON people (last_name) NULLS NOT DISTINCT
|
|
884
|
+
#
|
|
885
|
+
# Note: only supported by PostgreSQL version 15.0.0 and greater.
|
|
886
|
+
#
|
|
850
887
|
# ====== Creating an index with a specific method
|
|
851
888
|
#
|
|
852
889
|
# add_index(:developers, :name, using: 'btree')
|
|
@@ -894,6 +931,19 @@ module ActiveRecord
|
|
|
894
931
|
# Concurrently adding an index is not supported in a transaction.
|
|
895
932
|
#
|
|
896
933
|
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
|
934
|
+
#
|
|
935
|
+
# ====== Creating an index that is not used by queries
|
|
936
|
+
#
|
|
937
|
+
# add_index(:developers, :name, enabled: false)
|
|
938
|
+
#
|
|
939
|
+
# generates:
|
|
940
|
+
#
|
|
941
|
+
# CREATE INDEX index_developers_on_name ON developers (name) INVISIBLE -- MySQL
|
|
942
|
+
#
|
|
943
|
+
# CREATE INDEX index_developers_on_name ON developers (name) IGNORED -- MariaDB
|
|
944
|
+
#
|
|
945
|
+
# Note: only supported by MySQL version 8.0.0 and greater, and MariaDB version 10.6.0 and greater.
|
|
946
|
+
#
|
|
897
947
|
def add_index(table_name, column_name, **options)
|
|
898
948
|
create_index = build_create_index_definition(table_name, column_name, **options)
|
|
899
949
|
execute schema_creation.accept(create_index)
|
|
@@ -1154,9 +1204,10 @@ module ActiveRecord
|
|
|
1154
1204
|
# +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
|
|
1155
1205
|
def add_foreign_key(from_table, to_table, **options)
|
|
1156
1206
|
return unless use_foreign_keys?
|
|
1157
|
-
return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
|
|
1158
1207
|
|
|
1159
1208
|
options = foreign_key_options(from_table, to_table, options)
|
|
1209
|
+
return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column, :primary_key))
|
|
1210
|
+
|
|
1160
1211
|
at = create_alter_table from_table
|
|
1161
1212
|
at.add_foreign_key to_table, options
|
|
1162
1213
|
|
|
@@ -1195,7 +1246,7 @@ module ActiveRecord
|
|
|
1195
1246
|
# The name of the table that contains the referenced primary key.
|
|
1196
1247
|
def remove_foreign_key(from_table, to_table = nil, **options)
|
|
1197
1248
|
return unless use_foreign_keys?
|
|
1198
|
-
return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
|
|
1249
|
+
return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table, **options.slice(:column))
|
|
1199
1250
|
|
|
1200
1251
|
fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
|
|
1201
1252
|
|
|
@@ -1316,7 +1367,6 @@ module ActiveRecord
|
|
|
1316
1367
|
execute schema_creation.accept(at)
|
|
1317
1368
|
end
|
|
1318
1369
|
|
|
1319
|
-
|
|
1320
1370
|
# Checks to see if a check constraint exists on a table for a given check constraint definition.
|
|
1321
1371
|
#
|
|
1322
1372
|
# check_constraint_exists?(:products, name: "price_check")
|
|
@@ -1328,7 +1378,14 @@ module ActiveRecord
|
|
|
1328
1378
|
check_constraint_for(table_name, **options).present?
|
|
1329
1379
|
end
|
|
1330
1380
|
|
|
1331
|
-
def
|
|
1381
|
+
def remove_constraint(table_name, constraint_name) # :nodoc:
|
|
1382
|
+
at = create_alter_table(table_name)
|
|
1383
|
+
at.drop_constraint(constraint_name)
|
|
1384
|
+
|
|
1385
|
+
execute schema_creation.accept(at)
|
|
1386
|
+
end
|
|
1387
|
+
|
|
1388
|
+
def dump_schema_versions # :nodoc:
|
|
1332
1389
|
versions = pool.schema_migration.versions
|
|
1333
1390
|
insert_versions_sql(versions) if versions.any?
|
|
1334
1391
|
end
|
|
@@ -1450,7 +1507,7 @@ module ActiveRecord
|
|
|
1450
1507
|
end
|
|
1451
1508
|
|
|
1452
1509
|
def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
|
|
1453
|
-
options.assert_valid_keys(
|
|
1510
|
+
options.assert_valid_keys(valid_index_options)
|
|
1454
1511
|
|
|
1455
1512
|
column_names = index_column_names(column_name)
|
|
1456
1513
|
|
|
@@ -1459,7 +1516,7 @@ module ActiveRecord
|
|
|
1459
1516
|
|
|
1460
1517
|
validate_index_length!(table_name, index_name, internal)
|
|
1461
1518
|
|
|
1462
|
-
index =
|
|
1519
|
+
index = create_index_definition(
|
|
1463
1520
|
table_name, index_name,
|
|
1464
1521
|
options[:unique],
|
|
1465
1522
|
column_names,
|
|
@@ -1514,6 +1571,20 @@ module ActiveRecord
|
|
|
1514
1571
|
raise NotImplementedError, "#{self.class} does not support changing column comments"
|
|
1515
1572
|
end
|
|
1516
1573
|
|
|
1574
|
+
# Enables an index to be used by queries.
|
|
1575
|
+
#
|
|
1576
|
+
# enable_index(:users, :email)
|
|
1577
|
+
def enable_index(table_name, index_name)
|
|
1578
|
+
raise NotImplementedError, "#{self.class} does not support enabling indexes"
|
|
1579
|
+
end
|
|
1580
|
+
|
|
1581
|
+
# Prevents an index from being used by queries.
|
|
1582
|
+
#
|
|
1583
|
+
# disable_index(:users, :email)
|
|
1584
|
+
def disable_index(table_name, index_name)
|
|
1585
|
+
raise NotImplementedError, "#{self.class} does not support disabling indexes"
|
|
1586
|
+
end
|
|
1587
|
+
|
|
1517
1588
|
def create_schema_dumper(options) # :nodoc:
|
|
1518
1589
|
SchemaDumper.create(self, options)
|
|
1519
1590
|
end
|
|
@@ -1533,11 +1604,11 @@ module ActiveRecord
|
|
|
1533
1604
|
non_combinable_operations = []
|
|
1534
1605
|
|
|
1535
1606
|
operations.each do |command, args|
|
|
1536
|
-
|
|
1607
|
+
args.shift # remove table_name
|
|
1537
1608
|
method = :"#{command}_for_alter"
|
|
1538
1609
|
|
|
1539
1610
|
if respond_to?(method, true)
|
|
1540
|
-
sqls, procs = Array(send(method,
|
|
1611
|
+
sqls, procs = Array(send(method, table_name, *args)).partition { |v| v.is_a?(String) }
|
|
1541
1612
|
sql_fragments.concat(sqls)
|
|
1542
1613
|
non_combinable_operations.concat(procs)
|
|
1543
1614
|
else
|
|
@@ -1545,7 +1616,7 @@ module ActiveRecord
|
|
|
1545
1616
|
non_combinable_operations.each(&:call)
|
|
1546
1617
|
sql_fragments = []
|
|
1547
1618
|
non_combinable_operations = []
|
|
1548
|
-
send(command,
|
|
1619
|
+
send(command, table_name, *args)
|
|
1549
1620
|
end
|
|
1550
1621
|
end
|
|
1551
1622
|
|
|
@@ -1580,7 +1651,7 @@ module ActiveRecord
|
|
|
1580
1651
|
name = "idx_on_#{Array(column) * '_'}"
|
|
1581
1652
|
|
|
1582
1653
|
short_limit = max_index_name_size - hashed_identifier.bytesize
|
|
1583
|
-
short_name = name.
|
|
1654
|
+
short_name = name.truncate_bytes(short_limit, omission: nil)
|
|
1584
1655
|
|
|
1585
1656
|
"#{short_name}#{hashed_identifier}"
|
|
1586
1657
|
end
|
|
@@ -1602,6 +1673,10 @@ module ActiveRecord
|
|
|
1602
1673
|
end
|
|
1603
1674
|
end
|
|
1604
1675
|
|
|
1676
|
+
def valid_index_options
|
|
1677
|
+
[:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct]
|
|
1678
|
+
end
|
|
1679
|
+
|
|
1605
1680
|
def options_for_index_columns(options)
|
|
1606
1681
|
if options.is_a?(Hash)
|
|
1607
1682
|
options.symbolize_keys
|
|
@@ -1678,6 +1753,10 @@ module ActiveRecord
|
|
|
1678
1753
|
TableDefinition.new(self, name, **options)
|
|
1679
1754
|
end
|
|
1680
1755
|
|
|
1756
|
+
def create_index_definition(table_name, name, unique, columns, **options)
|
|
1757
|
+
IndexDefinition.new(table_name, name, unique, columns, **options)
|
|
1758
|
+
end
|
|
1759
|
+
|
|
1681
1760
|
def create_alter_table(name)
|
|
1682
1761
|
AlterTable.new create_table_definition(name)
|
|
1683
1762
|
end
|
|
@@ -1740,7 +1819,20 @@ module ActiveRecord
|
|
|
1740
1819
|
|
|
1741
1820
|
def foreign_key_for(from_table, **options)
|
|
1742
1821
|
return unless use_foreign_keys?
|
|
1743
|
-
|
|
1822
|
+
|
|
1823
|
+
keys = foreign_keys(from_table)
|
|
1824
|
+
|
|
1825
|
+
if options[:_skip_column_match]
|
|
1826
|
+
return keys.find { |fk| fk.defined_for?(**options) }
|
|
1827
|
+
end
|
|
1828
|
+
|
|
1829
|
+
if options[:column].nil?
|
|
1830
|
+
default_column = foreign_key_column_for(options[:to_table], "id")
|
|
1831
|
+
matches = keys.select { |fk| fk.column == default_column }
|
|
1832
|
+
keys = matches if matches.any?
|
|
1833
|
+
end
|
|
1834
|
+
|
|
1835
|
+
keys.find { |fk| fk.defined_for?(**options) }
|
|
1744
1836
|
end
|
|
1745
1837
|
|
|
1746
1838
|
def foreign_key_for!(from_table, to_table: nil, **options)
|
|
@@ -1783,13 +1875,19 @@ module ActiveRecord
|
|
|
1783
1875
|
|
|
1784
1876
|
def validate_index_length!(table_name, new_name, internal = false)
|
|
1785
1877
|
if new_name.length > index_name_length
|
|
1786
|
-
raise ArgumentError,
|
|
1878
|
+
raise ArgumentError, <<~MSG.squish
|
|
1879
|
+
Index name '#{new_name}' on table '#{table_name}' is too long (#{new_name.length} characters); the limit
|
|
1880
|
+
is #{index_name_length} characters
|
|
1881
|
+
MSG
|
|
1787
1882
|
end
|
|
1788
1883
|
end
|
|
1789
1884
|
|
|
1790
1885
|
def validate_table_length!(table_name)
|
|
1791
1886
|
if table_name.length > table_name_length
|
|
1792
|
-
raise ArgumentError,
|
|
1887
|
+
raise ArgumentError, <<~MSG.squish
|
|
1888
|
+
Table name '#{table_name}' is too long (#{table_name.length} characters); the limit is
|
|
1889
|
+
#{table_name_length} characters
|
|
1890
|
+
MSG
|
|
1793
1891
|
end
|
|
1794
1892
|
end
|
|
1795
1893
|
|
|
@@ -1851,16 +1949,8 @@ module ActiveRecord
|
|
|
1851
1949
|
end
|
|
1852
1950
|
|
|
1853
1951
|
def insert_versions_sql(versions)
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
if versions.is_a?(Array)
|
|
1857
|
-
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
|
|
1858
|
-
sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
|
|
1859
|
-
sql << ";"
|
|
1860
|
-
sql
|
|
1861
|
-
else
|
|
1862
|
-
"INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
|
|
1863
|
-
end
|
|
1952
|
+
versions_formatter = ActiveRecord.schema_versions_formatter.new(self)
|
|
1953
|
+
versions_formatter.format(versions)
|
|
1864
1954
|
end
|
|
1865
1955
|
|
|
1866
1956
|
def data_source_sql(name = nil, type: nil)
|
|
@@ -112,6 +112,7 @@ module ActiveRecord
|
|
|
112
112
|
def closed?; true; end
|
|
113
113
|
def open?; false; end
|
|
114
114
|
def joinable?; false; end
|
|
115
|
+
def isolation; nil; end
|
|
115
116
|
def add_record(record, _ = true); end
|
|
116
117
|
def restartable?; false; end
|
|
117
118
|
def dirty?; false; end
|
|
@@ -123,6 +124,7 @@ module ActiveRecord
|
|
|
123
124
|
def after_commit; yield; end
|
|
124
125
|
def after_rollback; end
|
|
125
126
|
def user_transaction; ActiveRecord::Transaction::NULL_TRANSACTION; end
|
|
127
|
+
def isolation=(_); end
|
|
126
128
|
end
|
|
127
129
|
|
|
128
130
|
class Transaction # :nodoc:
|
|
@@ -150,6 +152,15 @@ module ActiveRecord
|
|
|
150
152
|
|
|
151
153
|
delegate :invalidate!, :invalidated?, to: :@state
|
|
152
154
|
|
|
155
|
+
# Returns the isolation level if it was explicitly set, nil otherwise
|
|
156
|
+
def isolation
|
|
157
|
+
@isolation_level
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def isolation=(isolation) # :nodoc:
|
|
161
|
+
@isolation_level = isolation
|
|
162
|
+
end
|
|
163
|
+
|
|
153
164
|
def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
|
|
154
165
|
super()
|
|
155
166
|
@connection = connection
|
|
@@ -175,11 +186,11 @@ module ActiveRecord
|
|
|
175
186
|
end
|
|
176
187
|
|
|
177
188
|
def open?
|
|
178
|
-
|
|
189
|
+
!closed?
|
|
179
190
|
end
|
|
180
191
|
|
|
181
192
|
def closed?
|
|
182
|
-
|
|
193
|
+
@state.finalized?
|
|
183
194
|
end
|
|
184
195
|
|
|
185
196
|
def add_record(record, ensure_finalize = true)
|
|
@@ -386,7 +397,7 @@ module ActiveRecord
|
|
|
386
397
|
@parent.state.add_child(@state)
|
|
387
398
|
end
|
|
388
399
|
|
|
389
|
-
delegate :materialize!, :materialized?, :restart, to: :@parent
|
|
400
|
+
delegate :materialize!, :materialized?, :restart, :isolation, to: :@parent
|
|
390
401
|
|
|
391
402
|
def rollback
|
|
392
403
|
@state.rollback!
|
|
@@ -405,6 +416,7 @@ module ActiveRecord
|
|
|
405
416
|
def initialize(connection, savepoint_name, parent_transaction, **options)
|
|
406
417
|
super(connection, **options)
|
|
407
418
|
|
|
419
|
+
@parent_transaction = parent_transaction
|
|
408
420
|
parent_transaction.state.add_child(@state)
|
|
409
421
|
|
|
410
422
|
if isolation_level
|
|
@@ -414,6 +426,15 @@ module ActiveRecord
|
|
|
414
426
|
@savepoint_name = savepoint_name
|
|
415
427
|
end
|
|
416
428
|
|
|
429
|
+
# Delegates to parent transaction's isolation level
|
|
430
|
+
def isolation
|
|
431
|
+
@parent_transaction.isolation
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def isolation=(isolation) # :nodoc:
|
|
435
|
+
@parent_transaction.isolation = isolation
|
|
436
|
+
end
|
|
437
|
+
|
|
417
438
|
def materialize!
|
|
418
439
|
connection.create_savepoint(savepoint_name)
|
|
419
440
|
super
|
|
@@ -448,10 +469,14 @@ module ActiveRecord
|
|
|
448
469
|
# = Active Record Real \Transaction
|
|
449
470
|
class RealTransaction < Transaction
|
|
450
471
|
def materialize!
|
|
451
|
-
if
|
|
452
|
-
|
|
472
|
+
if joinable?
|
|
473
|
+
if isolation_level
|
|
474
|
+
connection.begin_isolated_db_transaction(isolation_level)
|
|
475
|
+
else
|
|
476
|
+
connection.begin_db_transaction
|
|
477
|
+
end
|
|
453
478
|
else
|
|
454
|
-
connection.
|
|
479
|
+
connection.begin_deferred_transaction(isolation_level)
|
|
455
480
|
end
|
|
456
481
|
|
|
457
482
|
super
|
|
@@ -472,13 +497,19 @@ module ActiveRecord
|
|
|
472
497
|
end
|
|
473
498
|
|
|
474
499
|
def rollback
|
|
475
|
-
|
|
500
|
+
if materialized?
|
|
501
|
+
connection.rollback_db_transaction
|
|
502
|
+
connection.reset_isolation_level if isolation_level
|
|
503
|
+
end
|
|
476
504
|
@state.full_rollback!
|
|
477
505
|
@instrumenter.finish(:rollback) if materialized?
|
|
478
506
|
end
|
|
479
507
|
|
|
480
508
|
def commit
|
|
481
|
-
|
|
509
|
+
if materialized?
|
|
510
|
+
connection.commit_db_transaction
|
|
511
|
+
connection.reset_isolation_level if isolation_level
|
|
512
|
+
end
|
|
482
513
|
@state.full_commit!
|
|
483
514
|
@instrumenter.finish(:commit) if materialized?
|
|
484
515
|
end
|
|
@@ -610,6 +641,7 @@ module ActiveRecord
|
|
|
610
641
|
end
|
|
611
642
|
|
|
612
643
|
def within_new_transaction(isolation: nil, joinable: true)
|
|
644
|
+
isolation ||= @connection.pool.pool_transaction_isolation_level
|
|
613
645
|
@connection.lock.synchronize do
|
|
614
646
|
transaction = begin_transaction(isolation: isolation, joinable: joinable)
|
|
615
647
|
begin
|