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
|
@@ -86,7 +86,7 @@ module ActiveRecord
|
|
|
86
86
|
"-c #{name}=#{value.to_s.gsub(/[ \\]/, '\\\\\0')}" unless value == ":default" || value == :default
|
|
87
87
|
end.join(" ")
|
|
88
88
|
end
|
|
89
|
-
find_cmd_and_exec(
|
|
89
|
+
find_cmd_and_exec(ActiveRecord.database_cli[:postgresql], config.database)
|
|
90
90
|
end
|
|
91
91
|
end
|
|
92
92
|
|
|
@@ -284,6 +284,20 @@ module ActiveRecord
|
|
|
284
284
|
database_version >= 15_00_00 # >= 15.0
|
|
285
285
|
end
|
|
286
286
|
|
|
287
|
+
def supports_native_partitioning? # :nodoc:
|
|
288
|
+
database_version >= 10_00_00 # >= 10.0
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
if PG::Connection.method_defined?(:close_prepared) # pg 1.6.0 & libpq 17
|
|
292
|
+
def supports_close_prepared? # :nodoc:
|
|
293
|
+
database_version >= 17_00_00
|
|
294
|
+
end
|
|
295
|
+
else
|
|
296
|
+
def supports_close_prepared? # :nodoc:
|
|
297
|
+
false
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
287
301
|
def index_algorithms
|
|
288
302
|
{ concurrently: "CONCURRENTLY" }
|
|
289
303
|
end
|
|
@@ -305,8 +319,12 @@ module ActiveRecord
|
|
|
305
319
|
# accessed while holding the connection's lock. (And we
|
|
306
320
|
# don't need the complication of with_raw_connection because
|
|
307
321
|
# a reconnect would invalidate the entire statement pool.)
|
|
308
|
-
if conn = @connection.instance_variable_get(:@raw_connection)
|
|
309
|
-
|
|
322
|
+
if (conn = @connection.instance_variable_get(:@raw_connection)) && conn.status == PG::CONNECTION_OK
|
|
323
|
+
if @connection.supports_close_prepared?
|
|
324
|
+
conn.close_prepared key
|
|
325
|
+
else
|
|
326
|
+
conn.query "DEALLOCATE #{key}"
|
|
327
|
+
end
|
|
310
328
|
end
|
|
311
329
|
rescue PG::Error
|
|
312
330
|
end
|
|
@@ -377,6 +395,11 @@ module ActiveRecord
|
|
|
377
395
|
end
|
|
378
396
|
end
|
|
379
397
|
|
|
398
|
+
def clear_cache!(new_connection: false)
|
|
399
|
+
super
|
|
400
|
+
@schema_search_path = nil if new_connection
|
|
401
|
+
end
|
|
402
|
+
|
|
380
403
|
# Disconnects from the database if already connected. Otherwise, this
|
|
381
404
|
# method does nothing.
|
|
382
405
|
def disconnect!
|
|
@@ -393,10 +416,6 @@ module ActiveRecord
|
|
|
393
416
|
@raw_connection = nil
|
|
394
417
|
end
|
|
395
418
|
|
|
396
|
-
def native_database_types # :nodoc:
|
|
397
|
-
self.class.native_database_types
|
|
398
|
-
end
|
|
399
|
-
|
|
400
419
|
def self.native_database_types # :nodoc:
|
|
401
420
|
@native_database_types ||= begin
|
|
402
421
|
types = NATIVE_DATABASE_TYPES.dup
|
|
@@ -406,7 +425,7 @@ module ActiveRecord
|
|
|
406
425
|
end
|
|
407
426
|
|
|
408
427
|
def set_standard_conforming_strings
|
|
409
|
-
internal_execute("SET standard_conforming_strings = on")
|
|
428
|
+
internal_execute("SET standard_conforming_strings = on", "SCHEMA")
|
|
410
429
|
end
|
|
411
430
|
|
|
412
431
|
def supports_ddl_transactions?
|
|
@@ -480,6 +499,7 @@ module ActiveRecord
|
|
|
480
499
|
# Set to +:cascade+ to drop dependent objects as well.
|
|
481
500
|
# Defaults to false.
|
|
482
501
|
def disable_extension(name, force: false)
|
|
502
|
+
_schema, name = name.to_s.split(".").values_at(-2, -1)
|
|
483
503
|
internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
|
|
484
504
|
reload_type_map
|
|
485
505
|
}
|
|
@@ -494,7 +514,19 @@ module ActiveRecord
|
|
|
494
514
|
end
|
|
495
515
|
|
|
496
516
|
def extensions
|
|
497
|
-
|
|
517
|
+
query = <<~SQL
|
|
518
|
+
SELECT
|
|
519
|
+
pg_extension.extname,
|
|
520
|
+
n.nspname AS schema
|
|
521
|
+
FROM pg_extension
|
|
522
|
+
JOIN pg_namespace n ON pg_extension.extnamespace = n.oid
|
|
523
|
+
SQL
|
|
524
|
+
|
|
525
|
+
internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.map do |row|
|
|
526
|
+
name, schema = row[0], row[1]
|
|
527
|
+
schema = nil if schema == current_schema
|
|
528
|
+
[schema, name].compact.join(".")
|
|
529
|
+
end
|
|
498
530
|
end
|
|
499
531
|
|
|
500
532
|
# Returns a list of defined enum types, and their values.
|
|
@@ -559,30 +591,34 @@ module ActiveRecord
|
|
|
559
591
|
end
|
|
560
592
|
|
|
561
593
|
# Rename an existing enum type to something else.
|
|
562
|
-
def rename_enum(name,
|
|
563
|
-
|
|
594
|
+
def rename_enum(name, new_name = nil, **options)
|
|
595
|
+
new_name ||= options.fetch(:to) do
|
|
596
|
+
raise ArgumentError, "rename_enum requires two from/to name positional arguments."
|
|
597
|
+
end
|
|
564
598
|
|
|
565
|
-
exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{
|
|
599
|
+
exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}").tap { reload_type_map }
|
|
566
600
|
end
|
|
567
601
|
|
|
568
602
|
# Add enum value to an existing enum type.
|
|
569
|
-
def add_enum_value(type_name, value, options
|
|
603
|
+
def add_enum_value(type_name, value, **options)
|
|
570
604
|
before, after = options.values_at(:before, :after)
|
|
571
|
-
sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE
|
|
605
|
+
sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE"
|
|
606
|
+
sql << " IF NOT EXISTS" if options[:if_not_exists]
|
|
607
|
+
sql << " #{quote(value)}"
|
|
572
608
|
|
|
573
609
|
if before && after
|
|
574
610
|
raise ArgumentError, "Cannot have both :before and :after at the same time"
|
|
575
611
|
elsif before
|
|
576
|
-
sql << " BEFORE
|
|
612
|
+
sql << " BEFORE #{quote(before)}"
|
|
577
613
|
elsif after
|
|
578
|
-
sql << " AFTER
|
|
614
|
+
sql << " AFTER #{quote(after)}"
|
|
579
615
|
end
|
|
580
616
|
|
|
581
617
|
execute(sql).tap { reload_type_map }
|
|
582
618
|
end
|
|
583
619
|
|
|
584
620
|
# Rename enum value on an existing enum type.
|
|
585
|
-
def rename_enum_value(type_name, options
|
|
621
|
+
def rename_enum_value(type_name, **options)
|
|
586
622
|
unless database_version >= 10_00_00 # >= 10.0
|
|
587
623
|
raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
|
|
588
624
|
end
|
|
@@ -590,12 +626,12 @@ module ActiveRecord
|
|
|
590
626
|
from = options.fetch(:from) { raise ArgumentError, ":from is required" }
|
|
591
627
|
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
|
592
628
|
|
|
593
|
-
execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE
|
|
629
|
+
execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE #{quote(from)} TO #{quote(to)}").tap {
|
|
594
630
|
reload_type_map
|
|
595
631
|
}
|
|
596
632
|
end
|
|
597
633
|
|
|
598
|
-
# Returns the configured supported identifier length supported by PostgreSQL
|
|
634
|
+
# Returns the configured maximum supported identifier length supported by PostgreSQL
|
|
599
635
|
def max_identifier_length
|
|
600
636
|
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
|
601
637
|
end
|
|
@@ -615,7 +651,7 @@ module ActiveRecord
|
|
|
615
651
|
with_raw_connection do |conn|
|
|
616
652
|
version = conn.server_version
|
|
617
653
|
if version == 0
|
|
618
|
-
raise ActiveRecord::
|
|
654
|
+
raise ActiveRecord::ConnectionNotEstablished, "Could not determine PostgreSQL version"
|
|
619
655
|
end
|
|
620
656
|
version
|
|
621
657
|
end
|
|
@@ -646,8 +682,8 @@ module ActiveRecord
|
|
|
646
682
|
end
|
|
647
683
|
|
|
648
684
|
def check_version # :nodoc:
|
|
649
|
-
if database_version <
|
|
650
|
-
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.
|
|
685
|
+
if database_version < 9_05_00 # < 9.5
|
|
686
|
+
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.5."
|
|
651
687
|
end
|
|
652
688
|
end
|
|
653
689
|
|
|
@@ -771,6 +807,8 @@ module ActiveRecord
|
|
|
771
807
|
NOT_NULL_VIOLATION = "23502"
|
|
772
808
|
FOREIGN_KEY_VIOLATION = "23503"
|
|
773
809
|
UNIQUE_VIOLATION = "23505"
|
|
810
|
+
CHECK_VIOLATION = "23514"
|
|
811
|
+
EXCLUSION_VIOLATION = "23P01"
|
|
774
812
|
SERIALIZATION_FAILURE = "40001"
|
|
775
813
|
DEADLOCK_DETECTED = "40P01"
|
|
776
814
|
DUPLICATE_DATABASE = "42P04"
|
|
@@ -802,6 +840,10 @@ module ActiveRecord
|
|
|
802
840
|
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
803
841
|
when FOREIGN_KEY_VIOLATION
|
|
804
842
|
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
843
|
+
when CHECK_VIOLATION
|
|
844
|
+
CheckViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
845
|
+
when EXCLUSION_VIOLATION
|
|
846
|
+
ExclusionViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
805
847
|
when VALUE_LIMIT_VIOLATION
|
|
806
848
|
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
807
849
|
when NUMERIC_VALUE_OUT_OF_RANGE
|
|
@@ -846,9 +888,8 @@ module ActiveRecord
|
|
|
846
888
|
def load_additional_types(oids = nil)
|
|
847
889
|
initializer = OID::TypeMapInitializer.new(type_map)
|
|
848
890
|
load_types_queries(initializer, oids) do |query|
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
end
|
|
891
|
+
records = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
|
|
892
|
+
initializer.run(records)
|
|
852
893
|
end
|
|
853
894
|
end
|
|
854
895
|
|
|
@@ -869,73 +910,6 @@ module ActiveRecord
|
|
|
869
910
|
|
|
870
911
|
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
|
|
871
912
|
|
|
872
|
-
def execute_and_clear(sql, name, binds, prepare: false, async: false, allow_retry: false, materialize_transactions: true)
|
|
873
|
-
sql = transform_query(sql)
|
|
874
|
-
check_if_write_query(sql)
|
|
875
|
-
|
|
876
|
-
if !prepare || without_prepared_statement?(binds)
|
|
877
|
-
result = exec_no_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
|
878
|
-
else
|
|
879
|
-
result = exec_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
|
880
|
-
end
|
|
881
|
-
begin
|
|
882
|
-
ret = yield result
|
|
883
|
-
ensure
|
|
884
|
-
result.clear
|
|
885
|
-
end
|
|
886
|
-
ret
|
|
887
|
-
end
|
|
888
|
-
|
|
889
|
-
def exec_no_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
|
890
|
-
mark_transaction_written_if_write(sql)
|
|
891
|
-
|
|
892
|
-
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
|
893
|
-
# made since we established the connection
|
|
894
|
-
update_typemap_for_default_timezone
|
|
895
|
-
|
|
896
|
-
type_casted_binds = type_casted_binds(binds)
|
|
897
|
-
log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
|
|
898
|
-
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
899
|
-
result = conn.exec_params(sql, type_casted_binds)
|
|
900
|
-
verified!
|
|
901
|
-
notification_payload[:row_count] = result.count
|
|
902
|
-
result
|
|
903
|
-
end
|
|
904
|
-
end
|
|
905
|
-
end
|
|
906
|
-
|
|
907
|
-
def exec_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
|
908
|
-
mark_transaction_written_if_write(sql)
|
|
909
|
-
|
|
910
|
-
update_typemap_for_default_timezone
|
|
911
|
-
|
|
912
|
-
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
913
|
-
stmt_key = prepare_statement(sql, binds, conn)
|
|
914
|
-
type_casted_binds = type_casted_binds(binds)
|
|
915
|
-
|
|
916
|
-
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do |notification_payload|
|
|
917
|
-
result = conn.exec_prepared(stmt_key, type_casted_binds)
|
|
918
|
-
verified!
|
|
919
|
-
notification_payload[:row_count] = result.count
|
|
920
|
-
result
|
|
921
|
-
end
|
|
922
|
-
end
|
|
923
|
-
rescue ActiveRecord::StatementInvalid => e
|
|
924
|
-
raise unless is_cached_plan_failure?(e)
|
|
925
|
-
|
|
926
|
-
# Nothing we can do if we are in a transaction because all commands
|
|
927
|
-
# will raise InFailedSQLTransaction
|
|
928
|
-
if in_transaction?
|
|
929
|
-
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message, connection_pool: @pool)
|
|
930
|
-
else
|
|
931
|
-
@lock.synchronize do
|
|
932
|
-
# outside of transactions we can simply flush this query and retry
|
|
933
|
-
@statements.delete sql_key(sql)
|
|
934
|
-
end
|
|
935
|
-
retry
|
|
936
|
-
end
|
|
937
|
-
end
|
|
938
|
-
|
|
939
913
|
# Annoyingly, the code for prepared statements whose return value may
|
|
940
914
|
# have changed is FEATURE_NOT_SUPPORTED.
|
|
941
915
|
#
|
|
@@ -945,8 +919,7 @@ module ActiveRecord
|
|
|
945
919
|
#
|
|
946
920
|
# Check here for more details:
|
|
947
921
|
# https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
|
|
948
|
-
def is_cached_plan_failure?(
|
|
949
|
-
pgerror = e.cause
|
|
922
|
+
def is_cached_plan_failure?(pgerror)
|
|
950
923
|
pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
|
|
951
924
|
pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
|
|
952
925
|
rescue
|
|
@@ -1025,22 +998,24 @@ module ActiveRecord
|
|
|
1025
998
|
variables = @config.fetch(:variables, {}).stringify_keys
|
|
1026
999
|
|
|
1027
1000
|
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
|
|
1028
|
-
internal_execute("SET intervalstyle = iso_8601")
|
|
1001
|
+
internal_execute("SET intervalstyle = iso_8601", "SCHEMA")
|
|
1029
1002
|
|
|
1030
1003
|
# SET statements from :variables config hash
|
|
1031
1004
|
# https://www.postgresql.org/docs/current/static/sql-set.html
|
|
1032
1005
|
variables.map do |k, v|
|
|
1033
1006
|
if v == ":default" || v == :default
|
|
1034
1007
|
# Sets the value to the global or compile default
|
|
1035
|
-
internal_execute("SET SESSION #{k} TO DEFAULT")
|
|
1008
|
+
internal_execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
|
|
1036
1009
|
elsif !v.nil?
|
|
1037
|
-
internal_execute("SET SESSION #{k} TO #{quote(v)}")
|
|
1010
|
+
internal_execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
|
|
1038
1011
|
end
|
|
1039
1012
|
end
|
|
1040
1013
|
|
|
1041
1014
|
add_pg_encoders
|
|
1042
1015
|
add_pg_decoders
|
|
1043
1016
|
|
|
1017
|
+
schema_search_path # populate cache
|
|
1018
|
+
|
|
1044
1019
|
reload_type_map
|
|
1045
1020
|
end
|
|
1046
1021
|
|
|
@@ -1055,9 +1030,9 @@ module ActiveRecord
|
|
|
1055
1030
|
# If using Active Record's time zone support configure the connection
|
|
1056
1031
|
# to return TIMESTAMP WITH ZONE types in UTC.
|
|
1057
1032
|
if default_timezone == :utc
|
|
1058
|
-
|
|
1033
|
+
raw_execute("SET SESSION timezone TO 'UTC'", "SCHEMA")
|
|
1059
1034
|
else
|
|
1060
|
-
|
|
1035
|
+
raw_execute("SET SESSION timezone TO DEFAULT", "SCHEMA")
|
|
1061
1036
|
end
|
|
1062
1037
|
end
|
|
1063
1038
|
|
|
@@ -1124,9 +1099,8 @@ module ActiveRecord
|
|
|
1124
1099
|
AND castsource = #{quote column.sql_type}::regtype
|
|
1125
1100
|
)
|
|
1126
1101
|
SQL
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
end
|
|
1102
|
+
result = internal_execute(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
|
|
1103
|
+
result.getvalue(0, 0)
|
|
1130
1104
|
end
|
|
1131
1105
|
end
|
|
1132
1106
|
end
|
|
@@ -1182,9 +1156,8 @@ module ActiveRecord
|
|
|
1182
1156
|
FROM pg_type as t
|
|
1183
1157
|
WHERE t.typname IN (%s)
|
|
1184
1158
|
SQL
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
end
|
|
1159
|
+
result = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
|
|
1160
|
+
coders = result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
|
1188
1161
|
|
|
1189
1162
|
map = PG::TypeMapByOid.new
|
|
1190
1163
|
coders.each { |coder| map.add_coder(coder) }
|
|
@@ -271,10 +271,10 @@ module ActiveRecord
|
|
|
271
271
|
end
|
|
272
272
|
|
|
273
273
|
def encode_with(coder) # :nodoc:
|
|
274
|
-
coder["columns"] = @columns.sort.to_h
|
|
274
|
+
coder["columns"] = @columns.sort.to_h.transform_values { _1.sort_by(&:name) }
|
|
275
275
|
coder["primary_keys"] = @primary_keys.sort.to_h
|
|
276
276
|
coder["data_sources"] = @data_sources.sort.to_h
|
|
277
|
-
coder["indexes"] = @indexes.sort.to_h
|
|
277
|
+
coder["indexes"] = @indexes.sort.to_h.transform_values { _1.sort_by(&:name) }
|
|
278
278
|
coder["version"] = @version
|
|
279
279
|
end
|
|
280
280
|
|
|
@@ -434,9 +434,7 @@ module ActiveRecord
|
|
|
434
434
|
end
|
|
435
435
|
|
|
436
436
|
def ignored_table?(table_name)
|
|
437
|
-
ActiveRecord.
|
|
438
|
-
ignored === table_name
|
|
439
|
-
end
|
|
437
|
+
ActiveRecord.schema_cache_ignored_table?(table_name)
|
|
440
438
|
end
|
|
441
439
|
|
|
442
440
|
def derive_columns_hash_and_deduplicate_values
|
|
@@ -46,7 +46,9 @@ module ActiveRecord
|
|
|
46
46
|
def ==(other)
|
|
47
47
|
other.is_a?(Column) &&
|
|
48
48
|
super &&
|
|
49
|
-
auto_increment? == other.auto_increment?
|
|
49
|
+
auto_increment? == other.auto_increment? &&
|
|
50
|
+
rowid == other.rowid &&
|
|
51
|
+
generated_type == other.generated_type
|
|
50
52
|
end
|
|
51
53
|
alias :eql? :==
|
|
52
54
|
|
|
@@ -54,8 +56,12 @@ module ActiveRecord
|
|
|
54
56
|
Column.hash ^
|
|
55
57
|
super.hash ^
|
|
56
58
|
auto_increment?.hash ^
|
|
57
|
-
rowid.hash
|
|
59
|
+
rowid.hash ^
|
|
60
|
+
virtual?.hash
|
|
58
61
|
end
|
|
62
|
+
|
|
63
|
+
protected
|
|
64
|
+
attr_reader :generated_type
|
|
59
65
|
end
|
|
60
66
|
end
|
|
61
67
|
end
|
|
@@ -21,87 +21,24 @@ module ActiveRecord
|
|
|
21
21
|
SQLite3::ExplainPrettyPrinter.new.pp(result)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def
|
|
25
|
-
|
|
26
|
-
check_if_write_query(sql)
|
|
27
|
-
|
|
28
|
-
mark_transaction_written_if_write(sql)
|
|
29
|
-
|
|
30
|
-
type_casted_binds = type_casted_binds(binds)
|
|
31
|
-
|
|
32
|
-
log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
|
|
33
|
-
with_raw_connection do |conn|
|
|
34
|
-
# Don't cache statements if they are not prepared
|
|
35
|
-
unless prepare
|
|
36
|
-
stmt = conn.prepare(sql)
|
|
37
|
-
begin
|
|
38
|
-
cols = stmt.columns
|
|
39
|
-
unless without_prepared_statement?(binds)
|
|
40
|
-
stmt.bind_params(type_casted_binds)
|
|
41
|
-
end
|
|
42
|
-
records = stmt.to_a
|
|
43
|
-
ensure
|
|
44
|
-
stmt.close
|
|
45
|
-
end
|
|
46
|
-
else
|
|
47
|
-
stmt = @statements[sql] ||= conn.prepare(sql)
|
|
48
|
-
cols = stmt.columns
|
|
49
|
-
stmt.reset!
|
|
50
|
-
stmt.bind_params(type_casted_binds)
|
|
51
|
-
records = stmt.to_a
|
|
52
|
-
end
|
|
53
|
-
verified!
|
|
54
|
-
|
|
55
|
-
result = build_result(columns: cols, rows: records)
|
|
56
|
-
notification_payload[:row_count] = result.length
|
|
57
|
-
result
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def exec_delete(sql, name = "SQL", binds = []) # :nodoc:
|
|
63
|
-
internal_exec_query(sql, name, binds)
|
|
64
|
-
@raw_connection.changes
|
|
24
|
+
def begin_deferred_transaction(isolation = nil) # :nodoc:
|
|
25
|
+
internal_begin_transaction(:deferred, isolation)
|
|
65
26
|
end
|
|
66
|
-
alias :exec_update :exec_delete
|
|
67
27
|
|
|
68
28
|
def begin_isolated_db_transaction(isolation) # :nodoc:
|
|
69
|
-
|
|
70
|
-
raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
|
|
71
|
-
|
|
72
|
-
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
|
73
|
-
ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted] = conn.get_first_value("PRAGMA read_uncommitted")
|
|
74
|
-
conn.read_uncommitted = true
|
|
75
|
-
begin_db_transaction
|
|
76
|
-
end
|
|
29
|
+
internal_begin_transaction(:deferred, isolation)
|
|
77
30
|
end
|
|
78
31
|
|
|
79
32
|
def begin_db_transaction # :nodoc:
|
|
80
|
-
|
|
81
|
-
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
|
82
|
-
result = conn.transaction
|
|
83
|
-
verified!
|
|
84
|
-
result
|
|
85
|
-
end
|
|
86
|
-
end
|
|
33
|
+
internal_begin_transaction(:immediate, nil)
|
|
87
34
|
end
|
|
88
35
|
|
|
89
36
|
def commit_db_transaction # :nodoc:
|
|
90
|
-
|
|
91
|
-
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
|
92
|
-
conn.commit
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
reset_read_uncommitted
|
|
37
|
+
internal_execute("COMMIT TRANSACTION", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
|
96
38
|
end
|
|
97
39
|
|
|
98
40
|
def exec_rollback_db_transaction # :nodoc:
|
|
99
|
-
|
|
100
|
-
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
|
101
|
-
conn.rollback
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
reset_read_uncommitted
|
|
41
|
+
internal_execute("ROLLBACK TRANSACTION", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
|
105
42
|
end
|
|
106
43
|
|
|
107
44
|
# https://stackoverflow.com/questions/17574784
|
|
@@ -113,47 +50,102 @@ module ActiveRecord
|
|
|
113
50
|
HIGH_PRECISION_CURRENT_TIMESTAMP
|
|
114
51
|
end
|
|
115
52
|
|
|
53
|
+
def execute(...) # :nodoc:
|
|
54
|
+
# SQLite3Adapter was refactored to use ActiveRecord::Result internally
|
|
55
|
+
# but for backward compatibility we have to keep returning arrays of hashes here
|
|
56
|
+
super&.to_a
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def reset_isolation_level # :nodoc:
|
|
60
|
+
internal_execute("PRAGMA read_uncommitted=#{@previous_read_uncommitted}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
|
61
|
+
@previous_read_uncommitted = nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def default_insert_value(column) # :nodoc:
|
|
65
|
+
if column.default_function
|
|
66
|
+
Arel.sql(column.default_function)
|
|
67
|
+
else
|
|
68
|
+
column.default
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
116
72
|
private
|
|
117
|
-
def
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
73
|
+
def internal_begin_transaction(mode, isolation)
|
|
74
|
+
if isolation
|
|
75
|
+
raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
|
|
76
|
+
raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
internal_execute("BEGIN #{mode} TRANSACTION", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
|
80
|
+
if isolation
|
|
81
|
+
@previous_read_uncommitted = query_value("PRAGMA read_uncommitted")
|
|
82
|
+
internal_execute("PRAGMA read_uncommitted=ON", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
|
125
83
|
end
|
|
126
84
|
end
|
|
127
85
|
|
|
128
|
-
def
|
|
129
|
-
|
|
130
|
-
|
|
86
|
+
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
|
87
|
+
total_changes_before_query = raw_connection.total_changes
|
|
88
|
+
affected_rows = nil
|
|
131
89
|
|
|
132
|
-
|
|
133
|
-
|
|
90
|
+
if batch
|
|
91
|
+
raw_connection.execute_batch2(sql)
|
|
92
|
+
else
|
|
93
|
+
stmt = if prepare
|
|
94
|
+
@statements[sql] ||= raw_connection.prepare(sql)
|
|
95
|
+
@statements[sql].reset!
|
|
96
|
+
else
|
|
97
|
+
# Don't cache statements if they are not prepared.
|
|
98
|
+
raw_connection.prepare(sql)
|
|
99
|
+
end
|
|
100
|
+
begin
|
|
101
|
+
unless binds.nil? || binds.empty?
|
|
102
|
+
stmt.bind_params(type_casted_binds)
|
|
103
|
+
end
|
|
104
|
+
result = if stmt.column_count.zero? # No return
|
|
105
|
+
stmt.step
|
|
134
106
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
107
|
+
affected_rows = if raw_connection.total_changes > total_changes_before_query
|
|
108
|
+
raw_connection.changes
|
|
109
|
+
else
|
|
110
|
+
0
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
ActiveRecord::Result.empty(affected_rows: affected_rows)
|
|
114
|
+
else
|
|
115
|
+
rows = stmt.to_a
|
|
138
116
|
|
|
139
|
-
|
|
140
|
-
|
|
117
|
+
affected_rows = if raw_connection.total_changes > total_changes_before_query
|
|
118
|
+
raw_connection.changes
|
|
119
|
+
else
|
|
120
|
+
0
|
|
121
|
+
end
|
|
141
122
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
notification_payload[:row_count] = result.length
|
|
147
|
-
result
|
|
123
|
+
ActiveRecord::Result.new(stmt.columns, rows, stmt.types.map { |t| type_map.lookup(t) }, affected_rows: affected_rows)
|
|
124
|
+
end
|
|
125
|
+
ensure
|
|
126
|
+
stmt.close unless prepare
|
|
148
127
|
end
|
|
149
128
|
end
|
|
129
|
+
verified!
|
|
130
|
+
|
|
131
|
+
notification_payload[:affected_rows] = affected_rows
|
|
132
|
+
notification_payload[:row_count] = result&.length || 0
|
|
133
|
+
result
|
|
150
134
|
end
|
|
151
135
|
|
|
152
|
-
def
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
136
|
+
def cast_result(result)
|
|
137
|
+
# Given that SQLite3 doesn't have a Result type, raw_execute already returns an ActiveRecord::Result
|
|
138
|
+
# so we have nothing to cast here.
|
|
139
|
+
result
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def affected_rows(result)
|
|
143
|
+
result.affected_rows
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def execute_batch(statements, name = nil, **kwargs)
|
|
147
|
+
sql = combine_multi_statements(statements)
|
|
148
|
+
raw_execute(sql, name, batch: true, **kwargs)
|
|
157
149
|
end
|
|
158
150
|
|
|
159
151
|
def build_truncate_statement(table_name)
|
|
@@ -5,12 +5,6 @@ module ActiveRecord
|
|
|
5
5
|
module SQLite3
|
|
6
6
|
class SchemaCreation < SchemaCreation # :nodoc:
|
|
7
7
|
private
|
|
8
|
-
def visit_AddForeignKey(o)
|
|
9
|
-
super.dup.tap do |sql|
|
|
10
|
-
sql << " DEFERRABLE INITIALLY #{o.options[:deferrable].to_s.upcase}" if o.deferrable
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
8
|
def visit_ForeignKeyDefinition(o)
|
|
15
9
|
super.dup.tap do |sql|
|
|
16
10
|
sql << " DEFERRABLE INITIALLY #{o.deferrable.to_s.upcase}" if o.deferrable
|