activerecord 7.2.3 → 8.0.0.beta1
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.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +192 -1261
- data/README.rdoc +2 -2
- data/lib/active_record/associations/alias_tracker.rb +4 -6
- data/lib/active_record/associations/association.rb +25 -5
- data/lib/active_record/associations/belongs_to_association.rb +2 -18
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/collection_association.rb +4 -4
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +4 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +50 -32
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods.rb +19 -24
- data/lib/active_record/attributes.rb +26 -37
- data/lib/active_record/autosave_association.rb +81 -49
- data/lib/active_record/base.rb +2 -2
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
- 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 +31 -75
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +14 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +2 -6
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +27 -9
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +27 -57
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +28 -58
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -15
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -16
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +12 -14
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +51 -9
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +44 -101
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -13
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +60 -22
- 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_handling.rb +29 -11
- data/lib/active_record/core.rb +15 -60
- data/lib/active_record/counter_cache.rb +1 -1
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -3
- 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 +35 -29
- 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 +12 -13
- data/lib/active_record/errors.rb +16 -8
- data/lib/active_record/fixture_set/table_row.rb +2 -19
- data/lib/active_record/fixtures.rb +0 -1
- data/lib/active_record/future_result.rb +14 -10
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/insert_all.rb +1 -1
- data/lib/active_record/marshalling.rb +1 -4
- data/lib/active_record/migration/command_recorder.rb +22 -5
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +36 -35
- data/lib/active_record/model_schema.rb +1 -1
- data/lib/active_record/nested_attributes.rb +4 -6
- data/lib/active_record/persistence.rb +128 -130
- data/lib/active_record/query_cache.rb +5 -4
- data/lib/active_record/query_logs.rb +98 -44
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +10 -10
- data/lib/active_record/railtie.rb +5 -6
- data/lib/active_record/railties/databases.rake +1 -2
- data/lib/active_record/reflection.rb +9 -7
- 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 +55 -55
- data/lib/active_record/relation/delegation.rb +25 -14
- data/lib/active_record/relation/finder_methods.rb +31 -32
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +0 -2
- 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 +5 -0
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +90 -91
- data/lib/active_record/relation/record_fetch_warning.rb +2 -2
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +2 -8
- data/lib/active_record/relation.rb +77 -76
- data/lib/active_record/result.rb +68 -7
- data/lib/active_record/sanitization.rb +7 -6
- data/lib/active_record/schema_dumper.rb +16 -29
- 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 +6 -7
- data/lib/active_record/statement_cache.rb +12 -12
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +24 -15
- data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +0 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
- data/lib/active_record/test_fixtures.rb +12 -0
- data/lib/active_record/testing/query_assertions.rb +2 -2
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transactions.rb +1 -3
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +16 -1
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/crud.rb +0 -2
- data/lib/arel/delete_manager.rb +0 -5
- data/lib/arel/nodes/delete_statement.rb +2 -4
- data/lib/arel/nodes/update_statement.rb +2 -4
- data/lib/arel/select_manager.rb +2 -6
- data/lib/arel/update_manager.rb +0 -5
- data/lib/arel/visitors/dot.rb +0 -2
- data/lib/arel/visitors/sqlite.rb +0 -25
- data/lib/arel/visitors/to_sql.rb +1 -3
- metadata +14 -11
|
@@ -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
|
-
#
|
|
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,7 +349,6 @@ module ActiveRecord
|
|
|
345
349
|
@lock.synchronize do
|
|
346
350
|
return false unless @raw_connection
|
|
347
351
|
@raw_connection.query ";"
|
|
348
|
-
verified!
|
|
349
352
|
end
|
|
350
353
|
true
|
|
351
354
|
rescue PG::Error
|
|
@@ -406,7 +409,7 @@ module ActiveRecord
|
|
|
406
409
|
end
|
|
407
410
|
|
|
408
411
|
def set_standard_conforming_strings
|
|
409
|
-
internal_execute("SET standard_conforming_strings = on")
|
|
412
|
+
internal_execute("SET standard_conforming_strings = on", "SCHEMA")
|
|
410
413
|
end
|
|
411
414
|
|
|
412
415
|
def supports_ddl_transactions?
|
|
@@ -480,6 +483,7 @@ module ActiveRecord
|
|
|
480
483
|
# Set to +:cascade+ to drop dependent objects as well.
|
|
481
484
|
# Defaults to false.
|
|
482
485
|
def disable_extension(name, force: false)
|
|
486
|
+
_schema, name = name.to_s.split(".").values_at(-2, -1)
|
|
483
487
|
internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
|
|
484
488
|
reload_type_map
|
|
485
489
|
}
|
|
@@ -494,7 +498,19 @@ module ActiveRecord
|
|
|
494
498
|
end
|
|
495
499
|
|
|
496
500
|
def extensions
|
|
497
|
-
|
|
501
|
+
query = <<~SQL
|
|
502
|
+
SELECT
|
|
503
|
+
pg_extension.extname,
|
|
504
|
+
n.nspname AS schema
|
|
505
|
+
FROM pg_extension
|
|
506
|
+
JOIN pg_namespace n ON pg_extension.extnamespace = n.oid
|
|
507
|
+
SQL
|
|
508
|
+
|
|
509
|
+
internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.map do |row|
|
|
510
|
+
name, schema = row[0], row[1]
|
|
511
|
+
schema = nil if schema == current_schema
|
|
512
|
+
[schema, name].compact.join(".")
|
|
513
|
+
end
|
|
498
514
|
end
|
|
499
515
|
|
|
500
516
|
# Returns a list of defined enum types, and their values.
|
|
@@ -504,7 +520,7 @@ module ActiveRecord
|
|
|
504
520
|
type.typname AS name,
|
|
505
521
|
type.OID AS oid,
|
|
506
522
|
n.nspname AS schema,
|
|
507
|
-
|
|
523
|
+
string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
|
|
508
524
|
FROM pg_enum AS enum
|
|
509
525
|
JOIN pg_type AS type ON (type.oid = enum.enumtypid)
|
|
510
526
|
JOIN pg_namespace n ON type.typnamespace = n.oid
|
|
@@ -559,16 +575,18 @@ module ActiveRecord
|
|
|
559
575
|
end
|
|
560
576
|
|
|
561
577
|
# Rename an existing enum type to something else.
|
|
562
|
-
def rename_enum(name, options
|
|
578
|
+
def rename_enum(name, **options)
|
|
563
579
|
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
|
564
580
|
|
|
565
581
|
exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
|
|
566
582
|
end
|
|
567
583
|
|
|
568
584
|
# Add enum value to an existing enum type.
|
|
569
|
-
def add_enum_value(type_name, value, options
|
|
585
|
+
def add_enum_value(type_name, value, **options)
|
|
570
586
|
before, after = options.values_at(:before, :after)
|
|
571
|
-
sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE
|
|
587
|
+
sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE"
|
|
588
|
+
sql << " IF NOT EXISTS" if options[:if_not_exists]
|
|
589
|
+
sql << " '#{value}'"
|
|
572
590
|
|
|
573
591
|
if before && after
|
|
574
592
|
raise ArgumentError, "Cannot have both :before and :after at the same time"
|
|
@@ -582,7 +600,7 @@ module ActiveRecord
|
|
|
582
600
|
end
|
|
583
601
|
|
|
584
602
|
# Rename enum value on an existing enum type.
|
|
585
|
-
def rename_enum_value(type_name, options
|
|
603
|
+
def rename_enum_value(type_name, **options)
|
|
586
604
|
unless database_version >= 10_00_00 # >= 10.0
|
|
587
605
|
raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
|
|
588
606
|
end
|
|
@@ -613,11 +631,7 @@ module ActiveRecord
|
|
|
613
631
|
# Returns the version of the connected PostgreSQL server.
|
|
614
632
|
def get_database_version # :nodoc:
|
|
615
633
|
with_raw_connection do |conn|
|
|
616
|
-
|
|
617
|
-
if version == 0
|
|
618
|
-
raise ActiveRecord::ConnectionFailed, "Could not determine PostgreSQL version"
|
|
619
|
-
end
|
|
620
|
-
version
|
|
634
|
+
conn.server_version
|
|
621
635
|
end
|
|
622
636
|
end
|
|
623
637
|
alias :postgresql_version :database_version
|
|
@@ -657,8 +671,8 @@ module ActiveRecord
|
|
|
657
671
|
m.register_type "int4", Type::Integer.new(limit: 4)
|
|
658
672
|
m.register_type "int8", Type::Integer.new(limit: 8)
|
|
659
673
|
m.register_type "oid", OID::Oid.new
|
|
660
|
-
m.register_type "float4", Type::Float.new
|
|
661
|
-
m.
|
|
674
|
+
m.register_type "float4", Type::Float.new
|
|
675
|
+
m.alias_type "float8", "float4"
|
|
662
676
|
m.register_type "text", Type::Text.new
|
|
663
677
|
register_class_with_limit m, "varchar", Type::String
|
|
664
678
|
m.alias_type "char", "varchar"
|
|
@@ -782,7 +796,7 @@ module ActiveRecord
|
|
|
782
796
|
|
|
783
797
|
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
|
784
798
|
when nil
|
|
785
|
-
if exception.message.match?(/connection is closed/i)
|
|
799
|
+
if exception.message.match?(/connection is closed/i)
|
|
786
800
|
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
|
787
801
|
elsif exception.is_a?(PG::ConnectionBad)
|
|
788
802
|
# libpq message style always ends with a newline; the pg gem's internal
|
|
@@ -846,9 +860,8 @@ module ActiveRecord
|
|
|
846
860
|
def load_additional_types(oids = nil)
|
|
847
861
|
initializer = OID::TypeMapInitializer.new(type_map)
|
|
848
862
|
load_types_queries(initializer, oids) do |query|
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
end
|
|
863
|
+
records = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
|
|
864
|
+
initializer.run(records)
|
|
852
865
|
end
|
|
853
866
|
end
|
|
854
867
|
|
|
@@ -869,73 +882,6 @@ module ActiveRecord
|
|
|
869
882
|
|
|
870
883
|
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
|
|
871
884
|
|
|
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
885
|
# Annoyingly, the code for prepared statements whose return value may
|
|
940
886
|
# have changed is FEATURE_NOT_SUPPORTED.
|
|
941
887
|
#
|
|
@@ -945,8 +891,7 @@ module ActiveRecord
|
|
|
945
891
|
#
|
|
946
892
|
# Check here for more details:
|
|
947
893
|
# 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
|
|
894
|
+
def is_cached_plan_failure?(pgerror)
|
|
950
895
|
pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
|
|
951
896
|
pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
|
|
952
897
|
rescue
|
|
@@ -1025,16 +970,16 @@ module ActiveRecord
|
|
|
1025
970
|
variables = @config.fetch(:variables, {}).stringify_keys
|
|
1026
971
|
|
|
1027
972
|
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
|
|
1028
|
-
internal_execute("SET intervalstyle = iso_8601")
|
|
973
|
+
internal_execute("SET intervalstyle = iso_8601", "SCHEMA")
|
|
1029
974
|
|
|
1030
975
|
# SET statements from :variables config hash
|
|
1031
976
|
# https://www.postgresql.org/docs/current/static/sql-set.html
|
|
1032
977
|
variables.map do |k, v|
|
|
1033
978
|
if v == ":default" || v == :default
|
|
1034
979
|
# Sets the value to the global or compile default
|
|
1035
|
-
internal_execute("SET SESSION #{k} TO DEFAULT")
|
|
980
|
+
internal_execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
|
|
1036
981
|
elsif !v.nil?
|
|
1037
|
-
internal_execute("SET SESSION #{k} TO #{quote(v)}")
|
|
982
|
+
internal_execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
|
|
1038
983
|
end
|
|
1039
984
|
end
|
|
1040
985
|
|
|
@@ -1055,9 +1000,9 @@ module ActiveRecord
|
|
|
1055
1000
|
# If using Active Record's time zone support configure the connection
|
|
1056
1001
|
# to return TIMESTAMP WITH ZONE types in UTC.
|
|
1057
1002
|
if default_timezone == :utc
|
|
1058
|
-
|
|
1003
|
+
raw_execute("SET SESSION timezone TO 'UTC'", "SCHEMA")
|
|
1059
1004
|
else
|
|
1060
|
-
|
|
1005
|
+
raw_execute("SET SESSION timezone TO DEFAULT", "SCHEMA")
|
|
1061
1006
|
end
|
|
1062
1007
|
end
|
|
1063
1008
|
|
|
@@ -1124,9 +1069,8 @@ module ActiveRecord
|
|
|
1124
1069
|
AND castsource = #{quote column.sql_type}::regtype
|
|
1125
1070
|
)
|
|
1126
1071
|
SQL
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
end
|
|
1072
|
+
result = internal_execute(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
|
|
1073
|
+
result.getvalue(0, 0)
|
|
1130
1074
|
end
|
|
1131
1075
|
end
|
|
1132
1076
|
end
|
|
@@ -1182,9 +1126,8 @@ module ActiveRecord
|
|
|
1182
1126
|
FROM pg_type as t
|
|
1183
1127
|
WHERE t.typname IN (%s)
|
|
1184
1128
|
SQL
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
end
|
|
1129
|
+
result = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
|
|
1130
|
+
coders = result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
|
1188
1131
|
|
|
1189
1132
|
map = PG::TypeMapByOid.new
|
|
1190
1133
|
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
|
|
@@ -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", 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 really a Result type, raw_execute already return an ActiveRecord::Result
|
|
118
|
+
# and 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,10 @@ 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
|
+
column.default
|
|
141
|
+
end
|
|
166
142
|
end
|
|
167
143
|
end
|
|
168
144
|
end
|
|
@@ -50,19 +50,6 @@ 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
|
-
|
|
66
53
|
def quote_string(s)
|
|
67
54
|
::SQLite3::Database.quote(s)
|
|
68
55
|
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,6 +5,19 @@ 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
|
|
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
22
|
schema_type(column) == :integer
|
|
10
23
|
end
|
|
@@ -74,7 +74,6 @@ module ActiveRecord
|
|
|
74
74
|
Base.pluralize_table_names ? table.pluralize : table
|
|
75
75
|
end
|
|
76
76
|
table = strip_table_name_prefix_and_suffix(table)
|
|
77
|
-
options = options.slice(*fk.options.keys)
|
|
78
77
|
fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
|
79
78
|
fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
|
|
80
79
|
end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
|
@@ -83,6 +82,10 @@ module ActiveRecord
|
|
|
83
82
|
alter_table(from_table, foreign_keys)
|
|
84
83
|
end
|
|
85
84
|
|
|
85
|
+
def virtual_table_exists?(table_name)
|
|
86
|
+
query_values(data_source_sql(table_name, type: "VIRTUAL TABLE"), "SCHEMA").any?
|
|
87
|
+
end
|
|
88
|
+
|
|
86
89
|
def check_constraints(table_name)
|
|
87
90
|
table_sql = query_value(<<-SQL, "SCHEMA")
|
|
88
91
|
SELECT sql
|
|
@@ -177,7 +180,8 @@ module ActiveRecord
|
|
|
177
180
|
scope = quoted_scope(name, type: type)
|
|
178
181
|
scope[:type] ||= "'table','view'"
|
|
179
182
|
|
|
180
|
-
sql = +"SELECT name FROM
|
|
183
|
+
sql = +"SELECT name FROM pragma_table_list WHERE schema <> 'temp'"
|
|
184
|
+
sql << " AND name NOT IN ('sqlite_sequence', 'sqlite_schema')"
|
|
181
185
|
sql << " AND name = #{scope[:name]}" if scope[:name]
|
|
182
186
|
sql << " AND type IN (#{scope[:type]})"
|
|
183
187
|
sql
|
|
@@ -190,6 +194,8 @@ module ActiveRecord
|
|
|
190
194
|
"'table'"
|
|
191
195
|
when "VIEW"
|
|
192
196
|
"'view'"
|
|
197
|
+
when "VIRTUAL TABLE"
|
|
198
|
+
"'virtual'"
|
|
193
199
|
end
|
|
194
200
|
scope = {}
|
|
195
201
|
scope[:name] = quote(name) if name
|