activerecord 8.0.2 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +555 -388
- data/README.rdoc +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/belongs_to_association.rb +11 -1
- data/lib/active_record/associations/builder/association.rb +16 -5
- 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 +3 -3
- data/lib/active_record/associations/collection_proxy.rb +22 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/join_dependency.rb +2 -0
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations.rb +159 -21
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +17 -4
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +10 -2
- data/lib/active_record/attribute_methods.rb +1 -1
- data/lib/active_record/attributes.rb +38 -24
- data/lib/active_record/autosave_association.rb +2 -2
- data/lib/active_record/base.rb +2 -4
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +2 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +413 -76
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +55 -40
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -9
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +26 -34
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +89 -23
- data/lib/active_record/connection_adapters/abstract/transaction.rb +25 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +89 -21
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -13
- 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/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +26 -4
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +27 -22
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +26 -17
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
- 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/schema_creation.rb +8 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +8 -21
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +67 -31
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +81 -48
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +25 -9
- data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -27
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +58 -34
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -3
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
- data/lib/active_record/connection_adapters.rb +1 -0
- data/lib/active_record/connection_handling.rb +15 -10
- data/lib/active_record/core.rb +13 -10
- data/lib/active_record/counter_cache.rb +33 -8
- data/lib/active_record/database_configurations/database_config.rb +5 -1
- data/lib/active_record/database_configurations/hash_config.rb +59 -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 +2 -2
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
- data/lib/active_record/encryption/encryptor.rb +39 -25
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +37 -20
- data/lib/active_record/errors.rb +23 -7
- 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/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +2 -2
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +12 -7
- data/lib/active_record/locking/optimistic.rb +7 -0
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +2 -6
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +14 -1
- data/lib/active_record/migration/compatibility.rb +34 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +31 -21
- data/lib/active_record/model_schema.rb +36 -10
- data/lib/active_record/nested_attributes.rb +2 -0
- data/lib/active_record/persistence.rb +34 -3
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +7 -7
- data/lib/active_record/querying.rb +4 -4
- data/lib/active_record/railtie.rb +34 -5
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +23 -19
- 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 +35 -0
- data/lib/active_record/relation/batches.rb +25 -11
- data/lib/active_record/relation/calculations.rb +35 -25
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +41 -24
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -9
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +7 -7
- data/lib/active_record/relation/predicate_builder.rb +9 -7
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +44 -33
- data/lib/active_record/relation/spawn_methods.rb +6 -6
- data/lib/active_record/relation/where_clause.rb +9 -10
- data/lib/active_record/relation.rb +38 -16
- data/lib/active_record/result.rb +44 -21
- data/lib/active_record/runtime_registry.rb +41 -58
- data/lib/active_record/sanitization.rb +2 -0
- data/lib/active_record/schema_dumper.rb +12 -10
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +46 -18
- data/lib/active_record/statement_cache.rb +13 -9
- data/lib/active_record/store.rb +44 -19
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +5 -20
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +44 -45
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +27 -2
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +34 -10
- 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 +15 -2
- data/lib/active_record/type/serialized.rb +11 -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.rb +68 -5
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +1 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/select_manager.rb +13 -4
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +5 -21
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +16 -12
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
|
@@ -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
|
|
|
@@ -186,6 +187,9 @@ module ActiveRecord
|
|
|
186
187
|
# Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
|
|
187
188
|
#
|
|
188
189
|
# A Symbol can be used to specify the type of the generated primary key column.
|
|
190
|
+
#
|
|
191
|
+
# A Hash can be used to specify the generated primary key column creation options.
|
|
192
|
+
# See {add_column}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_column] for available options.
|
|
189
193
|
# [<tt>:primary_key</tt>]
|
|
190
194
|
# The name of the primary key, if one is to be added automatically.
|
|
191
195
|
# Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
|
|
@@ -354,11 +358,13 @@ module ActiveRecord
|
|
|
354
358
|
# # Creates a table called 'music_artists_records' with no id.
|
|
355
359
|
# create_join_table('music_artists', 'music_records')
|
|
356
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
|
+
#
|
|
357
365
|
# You can pass an +options+ hash which can include the following keys:
|
|
358
366
|
# [<tt>:table_name</tt>]
|
|
359
367
|
# Sets the table name, overriding the default.
|
|
360
|
-
# [<tt>:column_options</tt>]
|
|
361
|
-
# Any extra options you want appended to the columns definition.
|
|
362
368
|
# [<tt>:options</tt>]
|
|
363
369
|
# Any extra options you want appended to the table definition.
|
|
364
370
|
# [<tt>:temporary</tt>]
|
|
@@ -375,6 +381,19 @@ module ActiveRecord
|
|
|
375
381
|
# t.index :category_id
|
|
376
382
|
# end
|
|
377
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
|
+
#
|
|
378
397
|
# ====== Add a backend specific option to the generated SQL (MySQL)
|
|
379
398
|
#
|
|
380
399
|
# create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
|
|
@@ -548,7 +567,7 @@ module ActiveRecord
|
|
|
548
567
|
#
|
|
549
568
|
# See {ActiveRecord::ConnectionAdapters::TableDefinition.column}[rdoc-ref:ActiveRecord::ConnectionAdapters::TableDefinition#column].
|
|
550
569
|
#
|
|
551
|
-
# The +type+ parameter is normally one of the
|
|
570
|
+
# The +type+ parameter is normally one of the migration's native types,
|
|
552
571
|
# which is one of the following:
|
|
553
572
|
# <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
|
|
554
573
|
# <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
|
|
@@ -912,6 +931,19 @@ module ActiveRecord
|
|
|
912
931
|
# Concurrently adding an index is not supported in a transaction.
|
|
913
932
|
#
|
|
914
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
|
+
#
|
|
915
947
|
def add_index(table_name, column_name, **options)
|
|
916
948
|
create_index = build_create_index_definition(table_name, column_name, **options)
|
|
917
949
|
execute schema_creation.accept(create_index)
|
|
@@ -1172,9 +1204,10 @@ module ActiveRecord
|
|
|
1172
1204
|
# +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
|
|
1173
1205
|
def add_foreign_key(from_table, to_table, **options)
|
|
1174
1206
|
return unless use_foreign_keys?
|
|
1175
|
-
return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
|
|
1176
1207
|
|
|
1177
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
|
+
|
|
1178
1211
|
at = create_alter_table from_table
|
|
1179
1212
|
at.add_foreign_key to_table, options
|
|
1180
1213
|
|
|
@@ -1213,7 +1246,7 @@ module ActiveRecord
|
|
|
1213
1246
|
# The name of the table that contains the referenced primary key.
|
|
1214
1247
|
def remove_foreign_key(from_table, to_table = nil, **options)
|
|
1215
1248
|
return unless use_foreign_keys?
|
|
1216
|
-
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))
|
|
1217
1250
|
|
|
1218
1251
|
fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
|
|
1219
1252
|
|
|
@@ -1352,7 +1385,7 @@ module ActiveRecord
|
|
|
1352
1385
|
execute schema_creation.accept(at)
|
|
1353
1386
|
end
|
|
1354
1387
|
|
|
1355
|
-
def
|
|
1388
|
+
def dump_schema_versions # :nodoc:
|
|
1356
1389
|
versions = pool.schema_migration.versions
|
|
1357
1390
|
insert_versions_sql(versions) if versions.any?
|
|
1358
1391
|
end
|
|
@@ -1474,7 +1507,7 @@ module ActiveRecord
|
|
|
1474
1507
|
end
|
|
1475
1508
|
|
|
1476
1509
|
def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
|
|
1477
|
-
options.assert_valid_keys(
|
|
1510
|
+
options.assert_valid_keys(valid_index_options)
|
|
1478
1511
|
|
|
1479
1512
|
column_names = index_column_names(column_name)
|
|
1480
1513
|
|
|
@@ -1483,7 +1516,7 @@ module ActiveRecord
|
|
|
1483
1516
|
|
|
1484
1517
|
validate_index_length!(table_name, index_name, internal)
|
|
1485
1518
|
|
|
1486
|
-
index =
|
|
1519
|
+
index = create_index_definition(
|
|
1487
1520
|
table_name, index_name,
|
|
1488
1521
|
options[:unique],
|
|
1489
1522
|
column_names,
|
|
@@ -1538,6 +1571,20 @@ module ActiveRecord
|
|
|
1538
1571
|
raise NotImplementedError, "#{self.class} does not support changing column comments"
|
|
1539
1572
|
end
|
|
1540
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
|
+
|
|
1541
1588
|
def create_schema_dumper(options) # :nodoc:
|
|
1542
1589
|
SchemaDumper.create(self, options)
|
|
1543
1590
|
end
|
|
@@ -1604,7 +1651,7 @@ module ActiveRecord
|
|
|
1604
1651
|
name = "idx_on_#{Array(column) * '_'}"
|
|
1605
1652
|
|
|
1606
1653
|
short_limit = max_index_name_size - hashed_identifier.bytesize
|
|
1607
|
-
short_name = name.
|
|
1654
|
+
short_name = name.truncate_bytes(short_limit, omission: nil)
|
|
1608
1655
|
|
|
1609
1656
|
"#{short_name}#{hashed_identifier}"
|
|
1610
1657
|
end
|
|
@@ -1626,6 +1673,10 @@ module ActiveRecord
|
|
|
1626
1673
|
end
|
|
1627
1674
|
end
|
|
1628
1675
|
|
|
1676
|
+
def valid_index_options
|
|
1677
|
+
[:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct]
|
|
1678
|
+
end
|
|
1679
|
+
|
|
1629
1680
|
def options_for_index_columns(options)
|
|
1630
1681
|
if options.is_a?(Hash)
|
|
1631
1682
|
options.symbolize_keys
|
|
@@ -1702,6 +1753,10 @@ module ActiveRecord
|
|
|
1702
1753
|
TableDefinition.new(self, name, **options)
|
|
1703
1754
|
end
|
|
1704
1755
|
|
|
1756
|
+
def create_index_definition(table_name, name, unique, columns, **options)
|
|
1757
|
+
IndexDefinition.new(table_name, name, unique, columns, **options)
|
|
1758
|
+
end
|
|
1759
|
+
|
|
1705
1760
|
def create_alter_table(name)
|
|
1706
1761
|
AlterTable.new create_table_definition(name)
|
|
1707
1762
|
end
|
|
@@ -1764,7 +1819,20 @@ module ActiveRecord
|
|
|
1764
1819
|
|
|
1765
1820
|
def foreign_key_for(from_table, **options)
|
|
1766
1821
|
return unless use_foreign_keys?
|
|
1767
|
-
|
|
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) }
|
|
1768
1836
|
end
|
|
1769
1837
|
|
|
1770
1838
|
def foreign_key_for!(from_table, to_table: nil, **options)
|
|
@@ -1807,13 +1875,19 @@ module ActiveRecord
|
|
|
1807
1875
|
|
|
1808
1876
|
def validate_index_length!(table_name, new_name, internal = false)
|
|
1809
1877
|
if new_name.length > index_name_length
|
|
1810
|
-
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
|
|
1811
1882
|
end
|
|
1812
1883
|
end
|
|
1813
1884
|
|
|
1814
1885
|
def validate_table_length!(table_name)
|
|
1815
1886
|
if table_name.length > table_name_length
|
|
1816
|
-
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
|
|
1817
1891
|
end
|
|
1818
1892
|
end
|
|
1819
1893
|
|
|
@@ -1875,16 +1949,8 @@ module ActiveRecord
|
|
|
1875
1949
|
end
|
|
1876
1950
|
|
|
1877
1951
|
def insert_versions_sql(versions)
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
if versions.is_a?(Array)
|
|
1881
|
-
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
|
|
1882
|
-
sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
|
|
1883
|
-
sql << ";"
|
|
1884
|
-
sql
|
|
1885
|
-
else
|
|
1886
|
-
"INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
|
|
1887
|
-
end
|
|
1952
|
+
versions_formatter = ActiveRecord.schema_versions_formatter.new(self)
|
|
1953
|
+
versions_formatter.format(versions)
|
|
1888
1954
|
end
|
|
1889
1955
|
|
|
1890
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
|
|
@@ -620,6 +641,7 @@ module ActiveRecord
|
|
|
620
641
|
end
|
|
621
642
|
|
|
622
643
|
def within_new_transaction(isolation: nil, joinable: true)
|
|
644
|
+
isolation ||= @connection.pool.pool_transaction_isolation_level
|
|
623
645
|
@connection.lock.synchronize do
|
|
624
646
|
transaction = begin_transaction(isolation: isolation, joinable: joinable)
|
|
625
647
|
begin
|
|
@@ -5,6 +5,7 @@ require "active_record/connection_adapters/abstract/schema_dumper"
|
|
|
5
5
|
require "active_record/connection_adapters/abstract/schema_creation"
|
|
6
6
|
require "active_support/concurrency/null_lock"
|
|
7
7
|
require "active_support/concurrency/load_interlock_aware_monitor"
|
|
8
|
+
require "active_support/concurrency/thread_monitor"
|
|
8
9
|
require "arel/collectors/bind"
|
|
9
10
|
require "arel/collectors/composite"
|
|
10
11
|
require "arel/collectors/sql_string"
|
|
@@ -42,6 +43,8 @@ module ActiveRecord
|
|
|
42
43
|
|
|
43
44
|
attr_reader :pool
|
|
44
45
|
attr_reader :visitor, :owner, :logger, :lock
|
|
46
|
+
attr_reader :allow_preconnect # :nodoc:
|
|
47
|
+
attr_accessor :pinned # :nodoc:
|
|
45
48
|
alias :in_use? :owner
|
|
46
49
|
|
|
47
50
|
def pool=(value)
|
|
@@ -50,7 +53,11 @@ module ActiveRecord
|
|
|
50
53
|
@pool = value
|
|
51
54
|
end
|
|
52
55
|
|
|
53
|
-
|
|
56
|
+
def allow_preconnect=(value) # :nodoc:
|
|
57
|
+
@lock.synchronize do
|
|
58
|
+
@allow_preconnect = value
|
|
59
|
+
end
|
|
60
|
+
end
|
|
54
61
|
|
|
55
62
|
def self.type_cast_config_to_integer(config)
|
|
56
63
|
if config.is_a?(Integer)
|
|
@@ -119,7 +126,7 @@ module ActiveRecord
|
|
|
119
126
|
|
|
120
127
|
# Opens a database console session.
|
|
121
128
|
def self.dbconsole(config, options = {})
|
|
122
|
-
raise NotImplementedError
|
|
129
|
+
raise NotImplementedError.new("#{self.class} should define `dbconsole` that accepts a db config and options to implement connecting to the db console")
|
|
123
130
|
end
|
|
124
131
|
|
|
125
132
|
def initialize(config_or_deprecated_connection, deprecated_logger = nil, deprecated_connection_options = nil, deprecated_config = nil) # :nodoc:
|
|
@@ -127,6 +134,7 @@ module ActiveRecord
|
|
|
127
134
|
|
|
128
135
|
@raw_connection = nil
|
|
129
136
|
@unconfigured_connection = nil
|
|
137
|
+
@connected_since = nil
|
|
130
138
|
|
|
131
139
|
if config_or_deprecated_connection.is_a?(Hash)
|
|
132
140
|
@config = config_or_deprecated_connection.symbolize_keys
|
|
@@ -139,6 +147,7 @@ module ActiveRecord
|
|
|
139
147
|
# Soft-deprecated for now; we'll probably warn in future.
|
|
140
148
|
|
|
141
149
|
@unconfigured_connection = config_or_deprecated_connection
|
|
150
|
+
@connected_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
142
151
|
@logger = deprecated_logger || ActiveRecord::Base.logger
|
|
143
152
|
if deprecated_config
|
|
144
153
|
@config = (deprecated_config || {}).symbolize_keys
|
|
@@ -150,8 +159,10 @@ module ActiveRecord
|
|
|
150
159
|
end
|
|
151
160
|
|
|
152
161
|
@owner = nil
|
|
162
|
+
@pinned = false
|
|
153
163
|
@pool = ActiveRecord::ConnectionAdapters::NullPool.new
|
|
154
164
|
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
165
|
+
@allow_preconnect = false
|
|
155
166
|
@visitor = arel_visitor
|
|
156
167
|
@statements = build_statement_pool
|
|
157
168
|
self.lock_thread = nil
|
|
@@ -169,6 +180,8 @@ module ActiveRecord
|
|
|
169
180
|
@raw_connection_dirty = false
|
|
170
181
|
@last_activity = nil
|
|
171
182
|
@verified = false
|
|
183
|
+
|
|
184
|
+
@pool_jitter = rand * max_jitter
|
|
172
185
|
end
|
|
173
186
|
|
|
174
187
|
def inspect # :nodoc:
|
|
@@ -182,20 +195,25 @@ module ActiveRecord
|
|
|
182
195
|
@lock =
|
|
183
196
|
case lock_thread
|
|
184
197
|
when Thread
|
|
185
|
-
ActiveSupport::Concurrency::
|
|
198
|
+
ActiveSupport::Concurrency::ThreadMonitor.new
|
|
186
199
|
when Fiber
|
|
187
|
-
|
|
200
|
+
::Monitor.new
|
|
188
201
|
else
|
|
189
202
|
ActiveSupport::Concurrency::NullLock
|
|
190
203
|
end
|
|
191
204
|
end
|
|
192
205
|
|
|
193
|
-
def
|
|
194
|
-
if preventing_writes?
|
|
206
|
+
def ensure_writes_are_allowed(sql) # :nodoc:
|
|
207
|
+
if preventing_writes?
|
|
195
208
|
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
|
196
209
|
end
|
|
197
210
|
end
|
|
198
211
|
|
|
212
|
+
MAX_JITTER = 0.0..1.0 # :nodoc:
|
|
213
|
+
def max_jitter
|
|
214
|
+
(@config[:pool_jitter] || 0.2).to_f.clamp(MAX_JITTER)
|
|
215
|
+
end
|
|
216
|
+
|
|
199
217
|
def replica?
|
|
200
218
|
@config[:replica] || false
|
|
201
219
|
end
|
|
@@ -260,7 +278,11 @@ module ActiveRecord
|
|
|
260
278
|
end
|
|
261
279
|
|
|
262
280
|
def valid_type?(type) # :nodoc:
|
|
263
|
-
|
|
281
|
+
self.class.valid_type?(type)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def native_database_types # :nodoc:
|
|
285
|
+
self.class.native_database_types
|
|
264
286
|
end
|
|
265
287
|
|
|
266
288
|
# this method must only be called while holding connection pool's mutex
|
|
@@ -299,8 +321,12 @@ module ActiveRecord
|
|
|
299
321
|
@pool.schema_cache || (@schema_cache ||= BoundSchemaReflection.for_lone_connection(@pool.schema_reflection, self))
|
|
300
322
|
end
|
|
301
323
|
|
|
324
|
+
def pool_jitter(duration)
|
|
325
|
+
duration * (1.0 - @pool_jitter)
|
|
326
|
+
end
|
|
327
|
+
|
|
302
328
|
# this method must only be called while holding connection pool's mutex
|
|
303
|
-
def expire
|
|
329
|
+
def expire(update_idle = true) # :nodoc:
|
|
304
330
|
if in_use?
|
|
305
331
|
if @owner != ActiveSupport::IsolatedExecutionState.context
|
|
306
332
|
raise ActiveRecordError, "Cannot expire connection, " \
|
|
@@ -308,8 +334,12 @@ module ActiveRecord
|
|
|
308
334
|
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
|
309
335
|
end
|
|
310
336
|
|
|
311
|
-
|
|
312
|
-
|
|
337
|
+
_run_checkin_callbacks do
|
|
338
|
+
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC) if update_idle
|
|
339
|
+
@owner = nil
|
|
340
|
+
enable_lazy_transactions!
|
|
341
|
+
unset_query_cache!
|
|
342
|
+
end
|
|
313
343
|
else
|
|
314
344
|
raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
|
|
315
345
|
end
|
|
@@ -341,6 +371,21 @@ module ActiveRecord
|
|
|
341
371
|
end
|
|
342
372
|
end
|
|
343
373
|
|
|
374
|
+
# Seconds since this connection was established. nil if not
|
|
375
|
+
# connected; infinity if the connection has been explicitly
|
|
376
|
+
# retired.
|
|
377
|
+
def connection_age # :nodoc:
|
|
378
|
+
if @raw_connection && @connected_since
|
|
379
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC) - @connected_since
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# Mark the connection as needing to be retired, as if the age has
|
|
384
|
+
# exceeded the maximum allowed.
|
|
385
|
+
def force_retirement # :nodoc:
|
|
386
|
+
@connected_since &&= -Float::INFINITY
|
|
387
|
+
end
|
|
388
|
+
|
|
344
389
|
def unprepared_statement
|
|
345
390
|
cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
|
|
346
391
|
yield
|
|
@@ -555,6 +600,10 @@ module ActiveRecord
|
|
|
555
600
|
false
|
|
556
601
|
end
|
|
557
602
|
|
|
603
|
+
def supports_disabling_indexes?
|
|
604
|
+
false
|
|
605
|
+
end
|
|
606
|
+
|
|
558
607
|
def return_value_after_insert?(column) # :nodoc:
|
|
559
608
|
column.auto_populated?
|
|
560
609
|
end
|
|
@@ -664,12 +713,15 @@ module ActiveRecord
|
|
|
664
713
|
deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
|
|
665
714
|
|
|
666
715
|
@lock.synchronize do
|
|
716
|
+
@allow_preconnect = false
|
|
717
|
+
|
|
667
718
|
reconnect
|
|
668
719
|
|
|
669
720
|
enable_lazy_transactions!
|
|
670
721
|
@raw_connection_dirty = false
|
|
671
|
-
@last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
722
|
+
@last_activity = @connected_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
672
723
|
@verified = true
|
|
724
|
+
@allow_preconnect = true
|
|
673
725
|
|
|
674
726
|
reset_transaction(restore: restore_transactions) do
|
|
675
727
|
clear_cache!(new_connection: true)
|
|
@@ -702,6 +754,7 @@ module ActiveRecord
|
|
|
702
754
|
clear_cache!(new_connection: true)
|
|
703
755
|
reset_transaction
|
|
704
756
|
@raw_connection_dirty = false
|
|
757
|
+
@connected_since = nil
|
|
705
758
|
end
|
|
706
759
|
end
|
|
707
760
|
|
|
@@ -765,6 +818,7 @@ module ActiveRecord
|
|
|
765
818
|
attempt_configure_connection
|
|
766
819
|
@last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
767
820
|
@verified = true
|
|
821
|
+
@allow_preconnect = true
|
|
768
822
|
return
|
|
769
823
|
end
|
|
770
824
|
|
|
@@ -772,6 +826,7 @@ module ActiveRecord
|
|
|
772
826
|
end
|
|
773
827
|
end
|
|
774
828
|
|
|
829
|
+
@last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
775
830
|
@verified = true
|
|
776
831
|
end
|
|
777
832
|
|
|
@@ -781,8 +836,15 @@ module ActiveRecord
|
|
|
781
836
|
end
|
|
782
837
|
|
|
783
838
|
def clean! # :nodoc:
|
|
784
|
-
|
|
785
|
-
|
|
839
|
+
_run_checkout_callbacks do
|
|
840
|
+
@raw_connection_dirty = false
|
|
841
|
+
@verified = nil
|
|
842
|
+
end
|
|
843
|
+
self
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
def verified? # :nodoc:
|
|
847
|
+
@verified
|
|
786
848
|
end
|
|
787
849
|
|
|
788
850
|
# Provides access to the underlying database driver for this adapter. For
|
|
@@ -870,7 +932,7 @@ module ActiveRecord
|
|
|
870
932
|
def register_class_with_precision(mapping, key, klass, **kwargs) # :nodoc:
|
|
871
933
|
mapping.register_type(key) do |*args|
|
|
872
934
|
precision = extract_precision(args.last)
|
|
873
|
-
klass.new(precision: precision, **kwargs)
|
|
935
|
+
klass.new(precision: precision, **kwargs).freeze
|
|
874
936
|
end
|
|
875
937
|
end
|
|
876
938
|
|
|
@@ -882,6 +944,10 @@ module ActiveRecord
|
|
|
882
944
|
end
|
|
883
945
|
end
|
|
884
946
|
|
|
947
|
+
def valid_type?(type) # :nodoc:
|
|
948
|
+
!native_database_types[type].nil?
|
|
949
|
+
end
|
|
950
|
+
|
|
885
951
|
private
|
|
886
952
|
def initialize_type_map(m)
|
|
887
953
|
register_class_with_limit m, %r(boolean)i, Type::Boolean
|
|
@@ -901,7 +967,7 @@ module ActiveRecord
|
|
|
901
967
|
m.alias_type %r(number)i, "decimal"
|
|
902
968
|
m.alias_type %r(double)i, "float"
|
|
903
969
|
|
|
904
|
-
m.register_type %r(^json)i, Type::Json.new
|
|
970
|
+
m.register_type %r(^json)i, Type::Json.new.freeze
|
|
905
971
|
|
|
906
972
|
m.register_type(%r(decimal)i) do |sql_type|
|
|
907
973
|
scale = extract_scale(sql_type)
|
|
@@ -909,9 +975,9 @@ module ActiveRecord
|
|
|
909
975
|
|
|
910
976
|
if scale == 0
|
|
911
977
|
# FIXME: Remove this class as well
|
|
912
|
-
Type::DecimalWithoutScale.new(precision: precision)
|
|
978
|
+
Type::DecimalWithoutScale.new(precision: precision).freeze
|
|
913
979
|
else
|
|
914
|
-
Type::Decimal.new(precision: precision, scale: scale)
|
|
980
|
+
Type::Decimal.new(precision: precision, scale: scale).freeze
|
|
915
981
|
end
|
|
916
982
|
end
|
|
917
983
|
end
|
|
@@ -919,7 +985,7 @@ module ActiveRecord
|
|
|
919
985
|
def register_class_with_limit(mapping, key, klass)
|
|
920
986
|
mapping.register_type(key) do |*args|
|
|
921
987
|
limit = extract_limit(args.last)
|
|
922
|
-
klass.new(limit: limit)
|
|
988
|
+
klass.new(limit: limit).freeze
|
|
923
989
|
end
|
|
924
990
|
end
|
|
925
991
|
|
|
@@ -1080,7 +1146,7 @@ module ActiveRecord
|
|
|
1080
1146
|
end
|
|
1081
1147
|
|
|
1082
1148
|
def reconnect
|
|
1083
|
-
raise NotImplementedError
|
|
1149
|
+
raise NotImplementedError.new("#{self.class} should define `reconnect` to implement adapter-specific logic for reconnecting to the database")
|
|
1084
1150
|
end
|
|
1085
1151
|
|
|
1086
1152
|
# Returns a raw connection for internal use with methods that are known
|
|
@@ -1131,7 +1197,7 @@ module ActiveRecord
|
|
|
1131
1197
|
active_record_error
|
|
1132
1198
|
end
|
|
1133
1199
|
|
|
1134
|
-
def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block) # :doc:
|
|
1200
|
+
def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, allow_retry: false, &block) # :doc:
|
|
1135
1201
|
instrumenter.instrument(
|
|
1136
1202
|
"sql.active_record",
|
|
1137
1203
|
sql: sql,
|
|
@@ -1139,8 +1205,10 @@ module ActiveRecord
|
|
|
1139
1205
|
binds: binds,
|
|
1140
1206
|
type_casted_binds: type_casted_binds,
|
|
1141
1207
|
async: async,
|
|
1208
|
+
allow_retry: allow_retry,
|
|
1142
1209
|
connection: self,
|
|
1143
1210
|
transaction: current_transaction.user_transaction.presence,
|
|
1211
|
+
affected_rows: 0,
|
|
1144
1212
|
row_count: 0,
|
|
1145
1213
|
&block
|
|
1146
1214
|
)
|
|
@@ -1215,7 +1283,7 @@ module ActiveRecord
|
|
|
1215
1283
|
|
|
1216
1284
|
def attempt_configure_connection
|
|
1217
1285
|
configure_connection
|
|
1218
|
-
rescue
|
|
1286
|
+
rescue Exception # Need to handle things such as Timeout::ExitException
|
|
1219
1287
|
disconnect!
|
|
1220
1288
|
raise
|
|
1221
1289
|
end
|