activerecord 7.2.2.1 → 8.0.5
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 +523 -677
- data/README.rdoc +2 -2
- data/lib/active_record/association_relation.rb +1 -0
- data/lib/active_record/associations/alias_tracker.rb +6 -4
- data/lib/active_record/associations/association.rb +34 -10
- data/lib/active_record/associations/belongs_to_association.rb +18 -2
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/collection_association.rb +10 -8
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
- data/lib/active_record/associations/join_dependency.rb +2 -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/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +34 -4
- 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 +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
- data/lib/active_record/attribute_methods.rb +24 -19
- data/lib/active_record/attributes.rb +37 -26
- data/lib/active_record/autosave_association.rb +91 -39
- data/lib/active_record/base.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +34 -25
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +55 -47
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +91 -44
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +40 -10
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +92 -70
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +35 -46
- data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -9
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +50 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +84 -94
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -9
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -43
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -24
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +60 -17
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +61 -99
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +80 -100
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -0
- 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 +10 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +63 -17
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -18
- data/lib/active_record/connection_adapters.rb +0 -56
- data/lib/active_record/connection_handling.rb +35 -9
- data/lib/active_record/core.rb +57 -22
- data/lib/active_record/counter_cache.rb +1 -1
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
- data/lib/active_record/database_configurations/database_config.rb +4 -0
- data/lib/active_record/database_configurations/hash_config.rb +16 -2
- data/lib/active_record/delegated_type.rb +18 -18
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
- data/lib/active_record/encryption/encryptor.rb +37 -28
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/enum.rb +22 -34
- data/lib/active_record/errors.rb +16 -8
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +0 -2
- data/lib/active_record/future_result.rb +15 -9
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/insert_all.rb +3 -3
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +5 -11
- data/lib/active_record/migration/command_recorder.rb +32 -12
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +40 -43
- data/lib/active_record/model_schema.rb +3 -4
- data/lib/active_record/nested_attributes.rb +4 -6
- data/lib/active_record/persistence.rb +128 -130
- data/lib/active_record/query_logs.rb +106 -50
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +12 -12
- data/lib/active_record/railtie.rb +4 -28
- data/lib/active_record/railties/databases.rake +11 -34
- data/lib/active_record/reflection.rb +18 -21
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +132 -72
- data/lib/active_record/relation/calculations.rb +71 -65
- data/lib/active_record/relation/delegation.rb +25 -14
- data/lib/active_record/relation/finder_methods.rb +32 -31
- 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 +2 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +13 -0
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +121 -70
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation/where_clause.rb +9 -3
- data/lib/active_record/relation.rb +95 -67
- data/lib/active_record/result.rb +66 -4
- data/lib/active_record/sanitization.rb +7 -6
- data/lib/active_record/schema_dumper.rb +34 -11
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +7 -6
- data/lib/active_record/statement_cache.rb +14 -14
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +1 -3
- data/lib/active_record/tasks/database_tasks.rb +69 -60
- data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +9 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +12 -0
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transactions.rb +8 -7
- data/lib/active_record/type/serialized.rb +5 -0
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +22 -49
- 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 +2 -0
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/node.rb +1 -1
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/predications.rb +1 -3
- data/lib/arel/select_manager.rb +6 -2
- data/lib/arel/table.rb +3 -7
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -0
- data/lib/arel/visitors/to_sql.rb +3 -1
- metadata +11 -15
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
|
@@ -25,7 +25,7 @@ module ActiveRecord
|
|
|
25
25
|
#
|
|
26
26
|
# The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
|
|
27
27
|
#
|
|
28
|
-
# Options
|
|
28
|
+
# ==== Options
|
|
29
29
|
#
|
|
30
30
|
# * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
|
|
31
31
|
# the default is to connect to localhost.
|
|
@@ -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,10 @@ 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
|
+
|
|
287
291
|
def index_algorithms
|
|
288
292
|
{ concurrently: "CONCURRENTLY" }
|
|
289
293
|
end
|
|
@@ -345,6 +349,7 @@ module ActiveRecord
|
|
|
345
349
|
@lock.synchronize do
|
|
346
350
|
return false unless @raw_connection
|
|
347
351
|
@raw_connection.query ";"
|
|
352
|
+
verified!
|
|
348
353
|
end
|
|
349
354
|
true
|
|
350
355
|
rescue PG::Error
|
|
@@ -376,6 +381,11 @@ module ActiveRecord
|
|
|
376
381
|
end
|
|
377
382
|
end
|
|
378
383
|
|
|
384
|
+
def clear_cache!(new_connection: false)
|
|
385
|
+
super
|
|
386
|
+
@schema_search_path = nil if new_connection
|
|
387
|
+
end
|
|
388
|
+
|
|
379
389
|
# Disconnects from the database if already connected. Otherwise, this
|
|
380
390
|
# method does nothing.
|
|
381
391
|
def disconnect!
|
|
@@ -405,7 +415,7 @@ module ActiveRecord
|
|
|
405
415
|
end
|
|
406
416
|
|
|
407
417
|
def set_standard_conforming_strings
|
|
408
|
-
internal_execute("SET standard_conforming_strings = on")
|
|
418
|
+
internal_execute("SET standard_conforming_strings = on", "SCHEMA")
|
|
409
419
|
end
|
|
410
420
|
|
|
411
421
|
def supports_ddl_transactions?
|
|
@@ -479,6 +489,7 @@ module ActiveRecord
|
|
|
479
489
|
# Set to +:cascade+ to drop dependent objects as well.
|
|
480
490
|
# Defaults to false.
|
|
481
491
|
def disable_extension(name, force: false)
|
|
492
|
+
_schema, name = name.to_s.split(".").values_at(-2, -1)
|
|
482
493
|
internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
|
|
483
494
|
reload_type_map
|
|
484
495
|
}
|
|
@@ -493,7 +504,19 @@ module ActiveRecord
|
|
|
493
504
|
end
|
|
494
505
|
|
|
495
506
|
def extensions
|
|
496
|
-
|
|
507
|
+
query = <<~SQL
|
|
508
|
+
SELECT
|
|
509
|
+
pg_extension.extname,
|
|
510
|
+
n.nspname AS schema
|
|
511
|
+
FROM pg_extension
|
|
512
|
+
JOIN pg_namespace n ON pg_extension.extnamespace = n.oid
|
|
513
|
+
SQL
|
|
514
|
+
|
|
515
|
+
internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.map do |row|
|
|
516
|
+
name, schema = row[0], row[1]
|
|
517
|
+
schema = nil if schema == current_schema
|
|
518
|
+
[schema, name].compact.join(".")
|
|
519
|
+
end
|
|
497
520
|
end
|
|
498
521
|
|
|
499
522
|
# Returns a list of defined enum types, and their values.
|
|
@@ -503,7 +526,7 @@ module ActiveRecord
|
|
|
503
526
|
type.typname AS name,
|
|
504
527
|
type.OID AS oid,
|
|
505
528
|
n.nspname AS schema,
|
|
506
|
-
|
|
529
|
+
array_agg(enum.enumlabel ORDER BY enum.enumsortorder) AS value
|
|
507
530
|
FROM pg_enum AS enum
|
|
508
531
|
JOIN pg_type AS type ON (type.oid = enum.enumtypid)
|
|
509
532
|
JOIN pg_namespace n ON type.typnamespace = n.oid
|
|
@@ -558,30 +581,34 @@ module ActiveRecord
|
|
|
558
581
|
end
|
|
559
582
|
|
|
560
583
|
# Rename an existing enum type to something else.
|
|
561
|
-
def rename_enum(name,
|
|
562
|
-
|
|
584
|
+
def rename_enum(name, new_name = nil, **options)
|
|
585
|
+
new_name ||= options.fetch(:to) do
|
|
586
|
+
raise ArgumentError, "rename_enum requires two from/to name positional arguments."
|
|
587
|
+
end
|
|
563
588
|
|
|
564
|
-
exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{
|
|
589
|
+
exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}").tap { reload_type_map }
|
|
565
590
|
end
|
|
566
591
|
|
|
567
592
|
# Add enum value to an existing enum type.
|
|
568
|
-
def add_enum_value(type_name, value, options
|
|
593
|
+
def add_enum_value(type_name, value, **options)
|
|
569
594
|
before, after = options.values_at(:before, :after)
|
|
570
|
-
sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE
|
|
595
|
+
sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE"
|
|
596
|
+
sql << " IF NOT EXISTS" if options[:if_not_exists]
|
|
597
|
+
sql << " #{quote(value)}"
|
|
571
598
|
|
|
572
599
|
if before && after
|
|
573
600
|
raise ArgumentError, "Cannot have both :before and :after at the same time"
|
|
574
601
|
elsif before
|
|
575
|
-
sql << " BEFORE
|
|
602
|
+
sql << " BEFORE #{quote(before)}"
|
|
576
603
|
elsif after
|
|
577
|
-
sql << " AFTER
|
|
604
|
+
sql << " AFTER #{quote(after)}"
|
|
578
605
|
end
|
|
579
606
|
|
|
580
607
|
execute(sql).tap { reload_type_map }
|
|
581
608
|
end
|
|
582
609
|
|
|
583
610
|
# Rename enum value on an existing enum type.
|
|
584
|
-
def rename_enum_value(type_name, options
|
|
611
|
+
def rename_enum_value(type_name, **options)
|
|
585
612
|
unless database_version >= 10_00_00 # >= 10.0
|
|
586
613
|
raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
|
|
587
614
|
end
|
|
@@ -589,12 +616,12 @@ module ActiveRecord
|
|
|
589
616
|
from = options.fetch(:from) { raise ArgumentError, ":from is required" }
|
|
590
617
|
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
|
591
618
|
|
|
592
|
-
execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE
|
|
619
|
+
execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE #{quote(from)} TO #{quote(to)}").tap {
|
|
593
620
|
reload_type_map
|
|
594
621
|
}
|
|
595
622
|
end
|
|
596
623
|
|
|
597
|
-
# Returns the configured supported identifier length supported by PostgreSQL
|
|
624
|
+
# Returns the configured maximum supported identifier length supported by PostgreSQL
|
|
598
625
|
def max_identifier_length
|
|
599
626
|
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
|
600
627
|
end
|
|
@@ -612,7 +639,11 @@ module ActiveRecord
|
|
|
612
639
|
# Returns the version of the connected PostgreSQL server.
|
|
613
640
|
def get_database_version # :nodoc:
|
|
614
641
|
with_raw_connection do |conn|
|
|
615
|
-
conn.server_version
|
|
642
|
+
version = conn.server_version
|
|
643
|
+
if version == 0
|
|
644
|
+
raise ActiveRecord::ConnectionFailed, "Could not determine PostgreSQL version"
|
|
645
|
+
end
|
|
646
|
+
version
|
|
616
647
|
end
|
|
617
648
|
end
|
|
618
649
|
alias :postgresql_version :database_version
|
|
@@ -841,9 +872,8 @@ module ActiveRecord
|
|
|
841
872
|
def load_additional_types(oids = nil)
|
|
842
873
|
initializer = OID::TypeMapInitializer.new(type_map)
|
|
843
874
|
load_types_queries(initializer, oids) do |query|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
end
|
|
875
|
+
records = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
|
|
876
|
+
initializer.run(records)
|
|
847
877
|
end
|
|
848
878
|
end
|
|
849
879
|
|
|
@@ -864,73 +894,6 @@ module ActiveRecord
|
|
|
864
894
|
|
|
865
895
|
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
|
|
866
896
|
|
|
867
|
-
def execute_and_clear(sql, name, binds, prepare: false, async: false, allow_retry: false, materialize_transactions: true)
|
|
868
|
-
sql = transform_query(sql)
|
|
869
|
-
check_if_write_query(sql)
|
|
870
|
-
|
|
871
|
-
if !prepare || without_prepared_statement?(binds)
|
|
872
|
-
result = exec_no_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
|
873
|
-
else
|
|
874
|
-
result = exec_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
|
875
|
-
end
|
|
876
|
-
begin
|
|
877
|
-
ret = yield result
|
|
878
|
-
ensure
|
|
879
|
-
result.clear
|
|
880
|
-
end
|
|
881
|
-
ret
|
|
882
|
-
end
|
|
883
|
-
|
|
884
|
-
def exec_no_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
|
885
|
-
mark_transaction_written_if_write(sql)
|
|
886
|
-
|
|
887
|
-
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
|
888
|
-
# made since we established the connection
|
|
889
|
-
update_typemap_for_default_timezone
|
|
890
|
-
|
|
891
|
-
type_casted_binds = type_casted_binds(binds)
|
|
892
|
-
log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
|
|
893
|
-
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
894
|
-
result = conn.exec_params(sql, type_casted_binds)
|
|
895
|
-
verified!
|
|
896
|
-
notification_payload[:row_count] = result.count
|
|
897
|
-
result
|
|
898
|
-
end
|
|
899
|
-
end
|
|
900
|
-
end
|
|
901
|
-
|
|
902
|
-
def exec_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
|
903
|
-
mark_transaction_written_if_write(sql)
|
|
904
|
-
|
|
905
|
-
update_typemap_for_default_timezone
|
|
906
|
-
|
|
907
|
-
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
908
|
-
stmt_key = prepare_statement(sql, binds, conn)
|
|
909
|
-
type_casted_binds = type_casted_binds(binds)
|
|
910
|
-
|
|
911
|
-
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do |notification_payload|
|
|
912
|
-
result = conn.exec_prepared(stmt_key, type_casted_binds)
|
|
913
|
-
verified!
|
|
914
|
-
notification_payload[:row_count] = result.count
|
|
915
|
-
result
|
|
916
|
-
end
|
|
917
|
-
end
|
|
918
|
-
rescue ActiveRecord::StatementInvalid => e
|
|
919
|
-
raise unless is_cached_plan_failure?(e)
|
|
920
|
-
|
|
921
|
-
# Nothing we can do if we are in a transaction because all commands
|
|
922
|
-
# will raise InFailedSQLTransaction
|
|
923
|
-
if in_transaction?
|
|
924
|
-
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message, connection_pool: @pool)
|
|
925
|
-
else
|
|
926
|
-
@lock.synchronize do
|
|
927
|
-
# outside of transactions we can simply flush this query and retry
|
|
928
|
-
@statements.delete sql_key(sql)
|
|
929
|
-
end
|
|
930
|
-
retry
|
|
931
|
-
end
|
|
932
|
-
end
|
|
933
|
-
|
|
934
897
|
# Annoyingly, the code for prepared statements whose return value may
|
|
935
898
|
# have changed is FEATURE_NOT_SUPPORTED.
|
|
936
899
|
#
|
|
@@ -940,8 +903,7 @@ module ActiveRecord
|
|
|
940
903
|
#
|
|
941
904
|
# Check here for more details:
|
|
942
905
|
# https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
|
|
943
|
-
def is_cached_plan_failure?(
|
|
944
|
-
pgerror = e.cause
|
|
906
|
+
def is_cached_plan_failure?(pgerror)
|
|
945
907
|
pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
|
|
946
908
|
pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
|
|
947
909
|
rescue
|
|
@@ -1020,22 +982,24 @@ module ActiveRecord
|
|
|
1020
982
|
variables = @config.fetch(:variables, {}).stringify_keys
|
|
1021
983
|
|
|
1022
984
|
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
|
|
1023
|
-
internal_execute("SET intervalstyle = iso_8601")
|
|
985
|
+
internal_execute("SET intervalstyle = iso_8601", "SCHEMA")
|
|
1024
986
|
|
|
1025
987
|
# SET statements from :variables config hash
|
|
1026
988
|
# https://www.postgresql.org/docs/current/static/sql-set.html
|
|
1027
989
|
variables.map do |k, v|
|
|
1028
990
|
if v == ":default" || v == :default
|
|
1029
991
|
# Sets the value to the global or compile default
|
|
1030
|
-
internal_execute("SET SESSION #{k} TO DEFAULT")
|
|
992
|
+
internal_execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
|
|
1031
993
|
elsif !v.nil?
|
|
1032
|
-
internal_execute("SET SESSION #{k} TO #{quote(v)}")
|
|
994
|
+
internal_execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
|
|
1033
995
|
end
|
|
1034
996
|
end
|
|
1035
997
|
|
|
1036
998
|
add_pg_encoders
|
|
1037
999
|
add_pg_decoders
|
|
1038
1000
|
|
|
1001
|
+
schema_search_path # populate cache
|
|
1002
|
+
|
|
1039
1003
|
reload_type_map
|
|
1040
1004
|
end
|
|
1041
1005
|
|
|
@@ -1050,9 +1014,9 @@ module ActiveRecord
|
|
|
1050
1014
|
# If using Active Record's time zone support configure the connection
|
|
1051
1015
|
# to return TIMESTAMP WITH ZONE types in UTC.
|
|
1052
1016
|
if default_timezone == :utc
|
|
1053
|
-
|
|
1017
|
+
raw_execute("SET SESSION timezone TO 'UTC'", "SCHEMA")
|
|
1054
1018
|
else
|
|
1055
|
-
|
|
1019
|
+
raw_execute("SET SESSION timezone TO DEFAULT", "SCHEMA")
|
|
1056
1020
|
end
|
|
1057
1021
|
end
|
|
1058
1022
|
|
|
@@ -1119,9 +1083,8 @@ module ActiveRecord
|
|
|
1119
1083
|
AND castsource = #{quote column.sql_type}::regtype
|
|
1120
1084
|
)
|
|
1121
1085
|
SQL
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
end
|
|
1086
|
+
result = internal_execute(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
|
|
1087
|
+
result.getvalue(0, 0)
|
|
1125
1088
|
end
|
|
1126
1089
|
end
|
|
1127
1090
|
end
|
|
@@ -1177,9 +1140,8 @@ module ActiveRecord
|
|
|
1177
1140
|
FROM pg_type as t
|
|
1178
1141
|
WHERE t.typname IN (%s)
|
|
1179
1142
|
SQL
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
end
|
|
1143
|
+
result = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
|
|
1144
|
+
coders = result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
|
1183
1145
|
|
|
1184
1146
|
map = PG::TypeMapByOid.new
|
|
1185
1147
|
coders.each { |coder| map.add_coder(coder) }
|
|
@@ -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,82 @@ 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
|
+
|
|
116
64
|
private
|
|
117
|
-
def
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
65
|
+
def internal_begin_transaction(mode, isolation)
|
|
66
|
+
if isolation
|
|
67
|
+
raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
|
|
68
|
+
raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
internal_execute("BEGIN #{mode} TRANSACTION", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
|
72
|
+
if isolation
|
|
73
|
+
@previous_read_uncommitted = query_value("PRAGMA read_uncommitted")
|
|
74
|
+
internal_execute("PRAGMA read_uncommitted=ON", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
|
125
75
|
end
|
|
126
76
|
end
|
|
127
77
|
|
|
128
|
-
def
|
|
129
|
-
|
|
130
|
-
|
|
78
|
+
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
|
79
|
+
if batch
|
|
80
|
+
raw_connection.execute_batch2(sql)
|
|
81
|
+
elsif prepare
|
|
82
|
+
stmt = @statements[sql] ||= raw_connection.prepare(sql)
|
|
83
|
+
stmt.reset!
|
|
84
|
+
stmt.bind_params(type_casted_binds)
|
|
85
|
+
|
|
86
|
+
result = if stmt.column_count.zero? # No return
|
|
87
|
+
stmt.step
|
|
88
|
+
ActiveRecord::Result.empty
|
|
89
|
+
else
|
|
90
|
+
ActiveRecord::Result.new(stmt.columns, stmt.to_a)
|
|
91
|
+
end
|
|
92
|
+
else
|
|
93
|
+
# Don't cache statements if they are not prepared.
|
|
94
|
+
stmt = raw_connection.prepare(sql)
|
|
95
|
+
begin
|
|
96
|
+
unless binds.nil? || binds.empty?
|
|
97
|
+
stmt.bind_params(type_casted_binds)
|
|
98
|
+
end
|
|
99
|
+
result = if stmt.column_count.zero? # No return
|
|
100
|
+
stmt.step
|
|
101
|
+
ActiveRecord::Result.empty
|
|
102
|
+
else
|
|
103
|
+
ActiveRecord::Result.new(stmt.columns, stmt.to_a)
|
|
104
|
+
end
|
|
105
|
+
ensure
|
|
106
|
+
stmt.close
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
@last_affected_rows = raw_connection.changes
|
|
110
|
+
verified!
|
|
131
111
|
|
|
132
|
-
|
|
112
|
+
notification_payload[:row_count] = result&.length || 0
|
|
113
|
+
result
|
|
133
114
|
end
|
|
134
115
|
|
|
135
|
-
def
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
mark_transaction_written_if_write(sql)
|
|
116
|
+
def cast_result(result)
|
|
117
|
+
# Given that SQLite3 doesn't have a Result type, raw_execute already returns an ActiveRecord::Result
|
|
118
|
+
# so we have nothing to cast here.
|
|
119
|
+
result
|
|
120
|
+
end
|
|
141
121
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
result = conn.execute_batch2(sql)
|
|
145
|
-
verified!
|
|
146
|
-
notification_payload[:row_count] = result.length
|
|
147
|
-
result
|
|
148
|
-
end
|
|
149
|
-
end
|
|
122
|
+
def affected_rows(result)
|
|
123
|
+
@last_affected_rows
|
|
150
124
|
end
|
|
151
125
|
|
|
152
|
-
def
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
fixtures.map { |fixture| build_fixture_sql([fixture], table_name) }
|
|
156
|
-
end.compact
|
|
126
|
+
def execute_batch(statements, name = nil, **kwargs)
|
|
127
|
+
sql = combine_multi_statements(statements)
|
|
128
|
+
raw_execute(sql, name, batch: true, **kwargs)
|
|
157
129
|
end
|
|
158
130
|
|
|
159
131
|
def build_truncate_statement(table_name)
|
|
@@ -163,6 +135,14 @@ module ActiveRecord
|
|
|
163
135
|
def returning_column_values(result)
|
|
164
136
|
result.rows.first
|
|
165
137
|
end
|
|
138
|
+
|
|
139
|
+
def default_insert_value(column)
|
|
140
|
+
if column.default_function
|
|
141
|
+
Arel.sql(column.default_function)
|
|
142
|
+
else
|
|
143
|
+
column.default
|
|
144
|
+
end
|
|
145
|
+
end
|
|
166
146
|
end
|
|
167
147
|
end
|
|
168
148
|
end
|
|
@@ -50,6 +50,19 @@ module ActiveRecord
|
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
def quote(value) # :nodoc:
|
|
54
|
+
case value
|
|
55
|
+
when Numeric
|
|
56
|
+
if value.finite?
|
|
57
|
+
super
|
|
58
|
+
else
|
|
59
|
+
"'#{value}'"
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
super
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
53
66
|
def quote_string(s)
|
|
54
67
|
::SQLite3::Database.quote(s)
|
|
55
68
|
end
|
|
@@ -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
|
|
@@ -5,12 +5,37 @@ module ActiveRecord
|
|
|
5
5
|
module SQLite3
|
|
6
6
|
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
|
|
7
7
|
private
|
|
8
|
+
def virtual_tables(stream)
|
|
9
|
+
virtual_tables = @connection.virtual_tables.reject { |name, _| ignored?(name) }
|
|
10
|
+
if virtual_tables.any?
|
|
11
|
+
stream.puts
|
|
12
|
+
stream.puts " # Virtual tables defined in this database."
|
|
13
|
+
stream.puts " # Note that virtual tables may not work with other database engines. Be careful if changing database."
|
|
14
|
+
virtual_tables.sort.each do |table_name, options|
|
|
15
|
+
module_name, arguments = options
|
|
16
|
+
stream.puts " create_virtual_table #{table_name.inspect}, #{module_name.inspect}, #{arguments.split(", ").inspect}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
8
21
|
def default_primary_key?(column)
|
|
9
|
-
schema_type(column) == :integer
|
|
22
|
+
schema_type(column) == :integer && primary_key_has_autoincrement?
|
|
10
23
|
end
|
|
11
24
|
|
|
12
25
|
def explicit_primary_key_default?(column)
|
|
13
|
-
column.bigint?
|
|
26
|
+
column.bigint? || (column.type == :integer && !primary_key_has_autoincrement?)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def primary_key_has_autoincrement?
|
|
30
|
+
return false unless table_name
|
|
31
|
+
|
|
32
|
+
table_sql = @connection.query_value(<<~SQL, "SCHEMA")
|
|
33
|
+
SELECT sql FROM sqlite_master WHERE name = #{@connection.quote(table_name)} AND type = 'table'
|
|
34
|
+
UNION ALL
|
|
35
|
+
SELECT sql FROM sqlite_temp_master WHERE name = #{@connection.quote(table_name)} AND type = 'table'
|
|
36
|
+
SQL
|
|
37
|
+
|
|
38
|
+
table_sql.to_s.match?(/\bAUTOINCREMENT\b/i)
|
|
14
39
|
end
|
|
15
40
|
|
|
16
41
|
def prepare_column_options(column)
|