activerecord 7.1.3.4 → 7.2.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +514 -2126
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +9 -8
- data/lib/active_record/associations/belongs_to_association.rb +18 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/collection_association.rb +4 -2
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/has_many_association.rb +3 -3
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +5 -7
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +2 -1
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +34 -11
- data/lib/active_record/attribute_assignment.rb +1 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +1 -1
- data/lib/active_record/attribute_methods/primary_key.rb +23 -55
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods.rb +87 -58
- data/lib/active_record/attributes.rb +55 -42
- data/lib/active_record/autosave_association.rb +14 -30
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -58
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +161 -75
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +22 -9
- data/lib/active_record/connection_adapters/abstract/transaction.rb +65 -60
- data/lib/active_record/connection_adapters/abstract_adapter.rb +33 -61
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +69 -19
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +16 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +109 -77
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +59 -38
- data/lib/active_record/counter_cache.rb +23 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
- data/lib/active_record/database_configurations/database_config.rb +15 -4
- data/lib/active_record/database_configurations/hash_config.rb +44 -36
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +30 -6
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +2 -2
- data/lib/active_record/encryption/encrypted_attribute_type.rb +24 -4
- data/lib/active_record/encryption/encryptor.rb +17 -2
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +8 -4
- data/lib/active_record/enum.rb +11 -2
- data/lib/active_record/errors.rb +16 -11
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +17 -4
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +18 -15
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/marshalling.rb +1 -1
- data/lib/active_record/message_pack.rb +2 -2
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +11 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +85 -76
- data/lib/active_record/model_schema.rb +34 -69
- data/lib/active_record/nested_attributes.rb +11 -3
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +32 -354
- data/lib/active_record/query_cache.rb +18 -6
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/query_logs_formatter.rb +1 -1
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +52 -64
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +41 -44
- data/lib/active_record/reflection.rb +98 -37
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +94 -61
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +16 -2
- data/lib/active_record/relation/merger.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +197 -44
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +500 -66
- data/lib/active_record/result.rb +32 -45
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +19 -9
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/signed_id.rb +11 -1
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/table_metadata.rb +1 -10
- data/lib/active_record/tasks/database_tasks.rb +70 -42
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +82 -91
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +131 -0
- data/lib/active_record/transactions.rb +70 -14
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +14 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +149 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/nodes/binary.rb +0 -6
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +4 -3
- data/lib/arel/nodes/sql_literal.rb +7 -0
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/to_sql.rb +31 -17
- data/lib/arel.rb +7 -3
- metadata +17 -12
@@ -112,7 +112,7 @@ module ActiveRecord
|
|
112
112
|
|
113
113
|
orders = {}
|
114
114
|
opclasses = {}
|
115
|
-
include_columns = include ? include.split(",").map(
|
115
|
+
include_columns = include ? include.split(",").map { |c| Utils.unquote_identifier(c.strip.gsub('""', '"')) } : []
|
116
116
|
|
117
117
|
if indkey.include?(0)
|
118
118
|
columns = expressions
|
@@ -209,8 +209,16 @@ module ActiveRecord
|
|
209
209
|
end
|
210
210
|
|
211
211
|
# Creates a schema for the given schema name.
|
212
|
-
def create_schema(schema_name)
|
213
|
-
|
212
|
+
def create_schema(schema_name, force: nil, if_not_exists: nil)
|
213
|
+
if force && if_not_exists
|
214
|
+
raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
|
215
|
+
end
|
216
|
+
|
217
|
+
if force
|
218
|
+
drop_schema(schema_name, if_exists: true)
|
219
|
+
end
|
220
|
+
|
221
|
+
execute("CREATE SCHEMA#{' IF NOT EXISTS' if if_not_exists} #{quote_schema_name(schema_name)}")
|
214
222
|
end
|
215
223
|
|
216
224
|
# Drops the schema for the given schema name.
|
@@ -400,7 +408,7 @@ module ActiveRecord
|
|
400
408
|
execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
|
401
409
|
end
|
402
410
|
end
|
403
|
-
rename_table_indexes(table_name, new_name)
|
411
|
+
rename_table_indexes(table_name, new_name, **options)
|
404
412
|
end
|
405
413
|
|
406
414
|
def add_column(table_name, column_name, type, **options) # :nodoc:
|
@@ -522,14 +530,6 @@ module ActiveRecord
|
|
522
530
|
end
|
523
531
|
|
524
532
|
def add_foreign_key(from_table, to_table, **options)
|
525
|
-
if options[:deferrable] == true
|
526
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
527
|
-
`deferrable: true` is deprecated in favor of `deferrable: :immediate`, and will be removed in Rails 7.2.
|
528
|
-
MSG
|
529
|
-
|
530
|
-
options[:deferrable] = :immediate
|
531
|
-
end
|
532
|
-
|
533
533
|
assert_valid_deferrable(options[:deferrable])
|
534
534
|
|
535
535
|
super
|
@@ -690,6 +690,10 @@ module ActiveRecord
|
|
690
690
|
# The constraint name. Defaults to <tt>excl_rails_<identifier></tt>.
|
691
691
|
# [<tt>:deferrable</tt>]
|
692
692
|
# Specify whether or not the exclusion constraint should be deferrable. Valid values are +false+ or +:immediate+ or +:deferred+ to specify the default behavior. Defaults to +false+.
|
693
|
+
# [<tt>:using</tt>]
|
694
|
+
# Specify which index method to use when creating this exclusion constraint (e.g. +:btree+, +:gist+ etc).
|
695
|
+
# [<tt>:where</tt>]
|
696
|
+
# Specify an exclusion constraint on a subset of the table (internally PostgreSQL creates a partial index for this).
|
693
697
|
def add_exclusion_constraint(table_name, expression, **options)
|
694
698
|
options = exclusion_constraint_options(table_name, expression, options)
|
695
699
|
at = create_alter_table(table_name)
|
@@ -20,17 +20,6 @@ require "active_record/connection_adapters/postgresql/type_metadata"
|
|
20
20
|
require "active_record/connection_adapters/postgresql/utils"
|
21
21
|
|
22
22
|
module ActiveRecord
|
23
|
-
module ConnectionHandling # :nodoc:
|
24
|
-
def postgresql_adapter_class
|
25
|
-
ConnectionAdapters::PostgreSQLAdapter
|
26
|
-
end
|
27
|
-
|
28
|
-
# Establishes a connection to the database that's used by all Active Record objects
|
29
|
-
def postgresql_connection(config)
|
30
|
-
postgresql_adapter_class.new(config)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
23
|
module ConnectionAdapters
|
35
24
|
# = Active Record PostgreSQL Adapter
|
36
25
|
#
|
@@ -133,6 +122,15 @@ module ActiveRecord
|
|
133
122
|
# setting, you should immediately run <tt>bin/rails db:migrate</tt> to update the types in your schema.rb.
|
134
123
|
class_attribute :datetime_type, default: :timestamp
|
135
124
|
|
125
|
+
##
|
126
|
+
# :singleton-method:
|
127
|
+
# Toggles automatic decoding of date columns.
|
128
|
+
#
|
129
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date").class #=> String
|
130
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.decode_dates = true
|
131
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date").class #=> Date
|
132
|
+
class_attribute :decode_dates, default: false
|
133
|
+
|
136
134
|
NATIVE_DATABASE_TYPES = {
|
137
135
|
primary_key: "bigserial primary key",
|
138
136
|
string: { name: "character varying" },
|
@@ -290,10 +288,6 @@ module ActiveRecord
|
|
290
288
|
{ concurrently: "CONCURRENTLY" }
|
291
289
|
end
|
292
290
|
|
293
|
-
def return_value_after_insert?(column) # :nodoc:
|
294
|
-
column.auto_populated?
|
295
|
-
end
|
296
|
-
|
297
291
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
298
292
|
def initialize(connection, max)
|
299
293
|
super(max)
|
@@ -342,6 +336,10 @@ module ActiveRecord
|
|
342
336
|
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
343
337
|
end
|
344
338
|
|
339
|
+
def connected?
|
340
|
+
!(@raw_connection.nil? || @raw_connection.finished?)
|
341
|
+
end
|
342
|
+
|
345
343
|
# Is this connection alive and ready for queries?
|
346
344
|
def active?
|
347
345
|
@lock.synchronize do
|
@@ -613,7 +611,9 @@ module ActiveRecord
|
|
613
611
|
|
614
612
|
# Returns the version of the connected PostgreSQL server.
|
615
613
|
def get_database_version # :nodoc:
|
616
|
-
|
614
|
+
with_raw_connection do |conn|
|
615
|
+
conn.server_version
|
616
|
+
end
|
617
617
|
end
|
618
618
|
alias :postgresql_version :database_version
|
619
619
|
|
@@ -889,10 +889,11 @@ module ActiveRecord
|
|
889
889
|
update_typemap_for_default_timezone
|
890
890
|
|
891
891
|
type_casted_binds = type_casted_binds(binds)
|
892
|
-
log(sql, name, binds, type_casted_binds, async: async) do
|
893
|
-
with_raw_connection do |conn|
|
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
894
|
result = conn.exec_params(sql, type_casted_binds)
|
895
895
|
verified!
|
896
|
+
notification_payload[:row_count] = result.count
|
896
897
|
result
|
897
898
|
end
|
898
899
|
end
|
@@ -903,13 +904,14 @@ module ActiveRecord
|
|
903
904
|
|
904
905
|
update_typemap_for_default_timezone
|
905
906
|
|
906
|
-
with_raw_connection do |conn|
|
907
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
907
908
|
stmt_key = prepare_statement(sql, binds, conn)
|
908
909
|
type_casted_binds = type_casted_binds(binds)
|
909
910
|
|
910
|
-
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
|
911
|
+
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do |notification_payload|
|
911
912
|
result = conn.exec_prepared(stmt_key, type_casted_binds)
|
912
913
|
verified!
|
914
|
+
notification_payload[:row_count] = result.count
|
913
915
|
result
|
914
916
|
end
|
915
917
|
end
|
@@ -919,7 +921,7 @@ module ActiveRecord
|
|
919
921
|
# Nothing we can do if we are in a transaction because all commands
|
920
922
|
# will raise InFailedSQLTransaction
|
921
923
|
if in_transaction?
|
922
|
-
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
|
924
|
+
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message, connection_pool: @pool)
|
923
925
|
else
|
924
926
|
@lock.synchronize do
|
925
927
|
# outside of transactions we can simply flush this query and retry
|
@@ -995,6 +997,8 @@ module ActiveRecord
|
|
995
997
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
996
998
|
# This is called by #connect and should not be called manually.
|
997
999
|
def configure_connection
|
1000
|
+
super
|
1001
|
+
|
998
1002
|
if @config[:encoding]
|
999
1003
|
@raw_connection.set_client_encoding(@config[:encoding])
|
1000
1004
|
end
|
@@ -1165,6 +1169,7 @@ module ActiveRecord
|
|
1165
1169
|
"timestamp" => PG::TextDecoder::TimestampUtc,
|
1166
1170
|
"timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
|
1167
1171
|
}
|
1172
|
+
coders_by_name["date"] = PG::TextDecoder::Date if decode_dates
|
1168
1173
|
|
1169
1174
|
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
|
1170
1175
|
query = <<~SQL % known_coder_types.join(", ")
|
@@ -18,70 +18,62 @@ module ActiveRecord
|
|
18
18
|
@cache_path = cache_path
|
19
19
|
end
|
20
20
|
|
21
|
-
def set_schema_cache(cache)
|
22
|
-
@cache = cache
|
23
|
-
end
|
24
|
-
|
25
21
|
def clear!
|
26
22
|
@cache = empty_cache
|
27
23
|
|
28
24
|
nil
|
29
25
|
end
|
30
26
|
|
31
|
-
def load!(
|
32
|
-
cache(
|
27
|
+
def load!(pool)
|
28
|
+
cache(pool)
|
33
29
|
|
34
30
|
self
|
35
31
|
end
|
36
32
|
|
37
|
-
def primary_keys(
|
38
|
-
cache(
|
33
|
+
def primary_keys(pool, table_name)
|
34
|
+
cache(pool).primary_keys(pool, table_name)
|
39
35
|
end
|
40
36
|
|
41
|
-
def data_source_exists?(
|
42
|
-
cache(
|
37
|
+
def data_source_exists?(pool, name)
|
38
|
+
cache(pool).data_source_exists?(pool, name)
|
43
39
|
end
|
44
40
|
|
45
|
-
def add(
|
46
|
-
cache(
|
41
|
+
def add(pool, name)
|
42
|
+
cache(pool).add(pool, name)
|
47
43
|
end
|
48
44
|
|
49
|
-
def data_sources(
|
50
|
-
cache(
|
45
|
+
def data_sources(pool, name)
|
46
|
+
cache(pool).data_source_exists?(pool, name)
|
51
47
|
end
|
52
48
|
|
53
|
-
def columns(
|
54
|
-
cache(
|
49
|
+
def columns(pool, table_name)
|
50
|
+
cache(pool).columns(pool, table_name)
|
55
51
|
end
|
56
52
|
|
57
|
-
def columns_hash(
|
58
|
-
cache(
|
53
|
+
def columns_hash(pool, table_name)
|
54
|
+
cache(pool).columns_hash(pool, table_name)
|
59
55
|
end
|
60
56
|
|
61
|
-
def columns_hash?(
|
62
|
-
cache(
|
57
|
+
def columns_hash?(pool, table_name)
|
58
|
+
cache(pool).columns_hash?(pool, table_name)
|
63
59
|
end
|
64
60
|
|
65
|
-
def indexes(
|
66
|
-
cache(
|
61
|
+
def indexes(pool, table_name)
|
62
|
+
cache(pool).indexes(pool, table_name)
|
67
63
|
end
|
68
64
|
|
69
|
-
def
|
70
|
-
cache(
|
65
|
+
def version(pool)
|
66
|
+
cache(pool).version(pool)
|
71
67
|
end
|
72
68
|
|
73
|
-
def
|
74
|
-
cache(
|
69
|
+
def size(pool)
|
70
|
+
cache(pool).size
|
75
71
|
end
|
76
72
|
|
77
|
-
def
|
78
|
-
cache(connection).size
|
79
|
-
end
|
80
|
-
|
81
|
-
def clear_data_source_cache!(connection, name)
|
73
|
+
def clear_data_source_cache!(pool, name)
|
82
74
|
return if @cache.nil? && !possible_cache_available?
|
83
75
|
|
84
|
-
cache(
|
76
|
+
cache(pool).clear_data_source_cache!(pool, name)
|
85
77
|
end
|
86
78
|
|
87
79
|
def cached?(table_name)
|
@@ -96,9 +88,9 @@ module ActiveRecord
|
|
96
88
|
@cache&.cached?(table_name)
|
97
89
|
end
|
98
90
|
|
99
|
-
def dump_to(
|
91
|
+
def dump_to(pool, filename)
|
100
92
|
fresh_cache = empty_cache
|
101
|
-
fresh_cache.add_all(
|
93
|
+
fresh_cache.add_all(pool)
|
102
94
|
fresh_cache.dump_to(filename)
|
103
95
|
|
104
96
|
@cache = fresh_cache
|
@@ -111,8 +103,8 @@ module ActiveRecord
|
|
111
103
|
new_cache
|
112
104
|
end
|
113
105
|
|
114
|
-
def cache(
|
115
|
-
@cache ||= load_cache(
|
106
|
+
def cache(pool)
|
107
|
+
@cache ||= load_cache(pool) || empty_cache
|
116
108
|
end
|
117
109
|
|
118
110
|
def possible_cache_available?
|
@@ -121,7 +113,7 @@ module ActiveRecord
|
|
121
113
|
File.file?(@cache_path)
|
122
114
|
end
|
123
115
|
|
124
|
-
def load_cache(
|
116
|
+
def load_cache(pool)
|
125
117
|
# Can't load if schema dumps are disabled
|
126
118
|
return unless possible_cache_available?
|
127
119
|
|
@@ -130,11 +122,13 @@ module ActiveRecord
|
|
130
122
|
|
131
123
|
if self.class.check_schema_cache_dump_version
|
132
124
|
begin
|
133
|
-
|
125
|
+
pool.with_connection do |connection|
|
126
|
+
current_version = connection.schema_version
|
134
127
|
|
135
|
-
|
136
|
-
|
137
|
-
|
128
|
+
if new_cache.version(connection) != current_version
|
129
|
+
warn "Ignoring #{@cache_path} because it has expired. The current schema version is #{current_version}, but the one in the schema cache file is #{new_cache.schema_version}."
|
130
|
+
return
|
131
|
+
end
|
138
132
|
end
|
139
133
|
rescue ActiveRecordError => error
|
140
134
|
warn "Failed to validate the schema cache because of #{error.class}: #{error.message}"
|
@@ -147,9 +141,25 @@ module ActiveRecord
|
|
147
141
|
end
|
148
142
|
|
149
143
|
class BoundSchemaReflection
|
150
|
-
|
144
|
+
class FakePool # :nodoc
|
145
|
+
def initialize(connection)
|
146
|
+
@connection = connection
|
147
|
+
end
|
148
|
+
|
149
|
+
def with_connection
|
150
|
+
yield @connection
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class << self
|
155
|
+
def for_lone_connection(abstract_schema_reflection, connection) # :nodoc:
|
156
|
+
new(abstract_schema_reflection, FakePool.new(connection))
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def initialize(abstract_schema_reflection, pool)
|
151
161
|
@schema_reflection = abstract_schema_reflection
|
152
|
-
@
|
162
|
+
@pool = pool
|
153
163
|
end
|
154
164
|
|
155
165
|
def clear!
|
@@ -157,7 +167,7 @@ module ActiveRecord
|
|
157
167
|
end
|
158
168
|
|
159
169
|
def load!
|
160
|
-
@schema_reflection.load!(@
|
170
|
+
@schema_reflection.load!(@pool)
|
161
171
|
end
|
162
172
|
|
163
173
|
def cached?(table_name)
|
@@ -165,72 +175,56 @@ module ActiveRecord
|
|
165
175
|
end
|
166
176
|
|
167
177
|
def primary_keys(table_name)
|
168
|
-
@schema_reflection.primary_keys(@
|
178
|
+
@schema_reflection.primary_keys(@pool, table_name)
|
169
179
|
end
|
170
180
|
|
171
181
|
def data_source_exists?(name)
|
172
|
-
@schema_reflection.data_source_exists?(@
|
182
|
+
@schema_reflection.data_source_exists?(@pool, name)
|
173
183
|
end
|
174
184
|
|
175
185
|
def add(name)
|
176
|
-
@schema_reflection.add(@
|
186
|
+
@schema_reflection.add(@pool, name)
|
177
187
|
end
|
178
188
|
|
179
189
|
def data_sources(name)
|
180
|
-
@schema_reflection.data_sources(@
|
190
|
+
@schema_reflection.data_sources(@pool, name)
|
181
191
|
end
|
182
192
|
|
183
193
|
def columns(table_name)
|
184
|
-
@schema_reflection.columns(@
|
194
|
+
@schema_reflection.columns(@pool, table_name)
|
185
195
|
end
|
186
196
|
|
187
197
|
def columns_hash(table_name)
|
188
|
-
@schema_reflection.columns_hash(@
|
198
|
+
@schema_reflection.columns_hash(@pool, table_name)
|
189
199
|
end
|
190
200
|
|
191
201
|
def columns_hash?(table_name)
|
192
|
-
@schema_reflection.columns_hash?(@
|
202
|
+
@schema_reflection.columns_hash?(@pool, table_name)
|
193
203
|
end
|
194
204
|
|
195
205
|
def indexes(table_name)
|
196
|
-
@schema_reflection.indexes(@
|
197
|
-
end
|
198
|
-
|
199
|
-
def database_version # :nodoc:
|
200
|
-
@schema_reflection.database_version(@connection)
|
206
|
+
@schema_reflection.indexes(@pool, table_name)
|
201
207
|
end
|
202
208
|
|
203
209
|
def version
|
204
|
-
@schema_reflection.version(@
|
210
|
+
@schema_reflection.version(@pool)
|
205
211
|
end
|
206
212
|
|
207
213
|
def size
|
208
|
-
@schema_reflection.size(@
|
214
|
+
@schema_reflection.size(@pool)
|
209
215
|
end
|
210
216
|
|
211
217
|
def clear_data_source_cache!(name)
|
212
|
-
@schema_reflection.clear_data_source_cache!(@
|
218
|
+
@schema_reflection.clear_data_source_cache!(@pool, name)
|
213
219
|
end
|
214
220
|
|
215
221
|
def dump_to(filename)
|
216
|
-
@schema_reflection.dump_to(@
|
222
|
+
@schema_reflection.dump_to(@pool, filename)
|
217
223
|
end
|
218
224
|
end
|
219
225
|
|
220
226
|
# = Active Record Connection Adapters Schema Cache
|
221
227
|
class SchemaCache
|
222
|
-
class << self
|
223
|
-
def new(connection)
|
224
|
-
BoundSchemaReflection.new(SchemaReflection.new(nil), connection)
|
225
|
-
end
|
226
|
-
deprecate new: "use ActiveRecord::ConnectionAdapters::SchemaReflection instead", deprecator: ActiveRecord.deprecator
|
227
|
-
|
228
|
-
def load_from(filename) # :nodoc:
|
229
|
-
BoundSchemaReflection.new(SchemaReflection.new(filename), nil)
|
230
|
-
end
|
231
|
-
deprecate load_from: "use ActiveRecord::ConnectionAdapters::SchemaReflection instead", deprecator: ActiveRecord.deprecator
|
232
|
-
end
|
233
|
-
|
234
228
|
def self._load_from(filename) # :nodoc:
|
235
229
|
return unless File.file?(filename)
|
236
230
|
|
@@ -258,13 +252,12 @@ module ActiveRecord
|
|
258
252
|
end
|
259
253
|
private_class_method :read
|
260
254
|
|
261
|
-
def initialize
|
255
|
+
def initialize # :nodoc:
|
262
256
|
@columns = {}
|
263
257
|
@columns_hash = {}
|
264
258
|
@primary_keys = {}
|
265
259
|
@data_sources = {}
|
266
260
|
@indexes = {}
|
267
|
-
@database_version = nil
|
268
261
|
@version = nil
|
269
262
|
end
|
270
263
|
|
@@ -283,17 +276,15 @@ module ActiveRecord
|
|
283
276
|
coder["data_sources"] = @data_sources.sort.to_h
|
284
277
|
coder["indexes"] = @indexes.sort.to_h
|
285
278
|
coder["version"] = @version
|
286
|
-
coder["database_version"] = @database_version
|
287
279
|
end
|
288
280
|
|
289
|
-
def init_with(coder)
|
281
|
+
def init_with(coder) # :nodoc:
|
290
282
|
@columns = coder["columns"]
|
291
283
|
@columns_hash = coder["columns_hash"]
|
292
284
|
@primary_keys = coder["primary_keys"]
|
293
285
|
@data_sources = coder["data_sources"]
|
294
286
|
@indexes = coder["indexes"] || {}
|
295
287
|
@version = coder["version"]
|
296
|
-
@database_version = coder["database_version"]
|
297
288
|
|
298
289
|
unless coder["deduplicated"]
|
299
290
|
derive_columns_hash_and_deduplicate_values
|
@@ -304,78 +295,85 @@ module ActiveRecord
|
|
304
295
|
@columns.key?(table_name)
|
305
296
|
end
|
306
297
|
|
307
|
-
def primary_keys(
|
298
|
+
def primary_keys(pool, table_name)
|
308
299
|
@primary_keys.fetch(table_name) do
|
309
|
-
|
310
|
-
|
300
|
+
pool.with_connection do |connection|
|
301
|
+
if data_source_exists?(pool, table_name)
|
302
|
+
@primary_keys[deep_deduplicate(table_name)] = deep_deduplicate(connection.primary_key(table_name))
|
303
|
+
end
|
311
304
|
end
|
312
305
|
end
|
313
306
|
end
|
314
307
|
|
315
308
|
# A cached lookup for table existence.
|
316
|
-
def data_source_exists?(
|
309
|
+
def data_source_exists?(pool, name)
|
317
310
|
return if ignored_table?(name)
|
318
|
-
|
311
|
+
|
312
|
+
if @data_sources.empty?
|
313
|
+
tables_to_cache(pool).each do |source|
|
314
|
+
@data_sources[source] = true
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
319
318
|
return @data_sources[name] if @data_sources.key? name
|
320
319
|
|
321
|
-
@data_sources[deep_deduplicate(name)] = connection
|
320
|
+
@data_sources[deep_deduplicate(name)] = pool.with_connection do |connection|
|
321
|
+
connection.data_source_exists?(name)
|
322
|
+
end
|
322
323
|
end
|
323
324
|
|
324
325
|
# Add internal cache for table with +table_name+.
|
325
|
-
def add(
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
326
|
+
def add(pool, table_name)
|
327
|
+
pool.with_connection do
|
328
|
+
if data_source_exists?(pool, table_name)
|
329
|
+
primary_keys(pool, table_name)
|
330
|
+
columns(pool, table_name)
|
331
|
+
columns_hash(pool, table_name)
|
332
|
+
indexes(pool, table_name)
|
333
|
+
end
|
331
334
|
end
|
332
335
|
end
|
333
336
|
|
334
|
-
def data_sources(_connection, name) # :nodoc:
|
335
|
-
@data_sources[name]
|
336
|
-
end
|
337
|
-
deprecate data_sources: :data_source_exists?, deprecator: ActiveRecord.deprecator
|
338
|
-
|
339
337
|
# Get the columns for a table
|
340
|
-
def columns(
|
338
|
+
def columns(pool, table_name)
|
341
339
|
if ignored_table?(table_name)
|
342
|
-
raise ActiveRecord::StatementInvalid
|
340
|
+
raise ActiveRecord::StatementInvalid.new("Table '#{table_name}' doesn't exist", connection_pool: pool)
|
343
341
|
end
|
344
342
|
|
345
343
|
@columns.fetch(table_name) do
|
346
|
-
|
344
|
+
pool.with_connection do |connection|
|
345
|
+
@columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
|
346
|
+
end
|
347
347
|
end
|
348
348
|
end
|
349
349
|
|
350
350
|
# Get the columns for a table as a hash, key is the column name
|
351
351
|
# value is the column object.
|
352
|
-
def columns_hash(
|
352
|
+
def columns_hash(pool, table_name)
|
353
353
|
@columns_hash.fetch(table_name) do
|
354
|
-
@columns_hash[deep_deduplicate(table_name)] = columns(
|
354
|
+
@columns_hash[deep_deduplicate(table_name)] = columns(pool, table_name).index_by(&:name).freeze
|
355
355
|
end
|
356
356
|
end
|
357
357
|
|
358
358
|
# Checks whether the columns hash is already cached for a table.
|
359
|
-
def columns_hash?(
|
359
|
+
def columns_hash?(_pool, table_name)
|
360
360
|
@columns_hash.key?(table_name)
|
361
361
|
end
|
362
362
|
|
363
|
-
def indexes(
|
363
|
+
def indexes(pool, table_name)
|
364
364
|
@indexes.fetch(table_name) do
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
365
|
+
pool.with_connection do |connection|
|
366
|
+
if data_source_exists?(pool, table_name)
|
367
|
+
@indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
|
368
|
+
else
|
369
|
+
[]
|
370
|
+
end
|
369
371
|
end
|
370
372
|
end
|
371
373
|
end
|
372
374
|
|
373
|
-
def
|
374
|
-
@
|
375
|
-
end
|
376
|
-
|
377
|
-
def version(connection)
|
378
|
-
@version ||= connection.schema_version
|
375
|
+
def version(pool)
|
376
|
+
@version ||= pool.with_connection(&:schema_version)
|
379
377
|
end
|
380
378
|
|
381
379
|
def schema_version
|
@@ -395,13 +393,14 @@ module ActiveRecord
|
|
395
393
|
@indexes.delete name
|
396
394
|
end
|
397
395
|
|
398
|
-
def add_all(
|
399
|
-
|
400
|
-
|
401
|
-
|
396
|
+
def add_all(pool) # :nodoc:
|
397
|
+
pool.with_connection do
|
398
|
+
tables_to_cache(pool).each do |table|
|
399
|
+
add(pool, table)
|
400
|
+
end
|
402
401
|
|
403
|
-
|
404
|
-
|
402
|
+
version(pool)
|
403
|
+
end
|
405
404
|
end
|
406
405
|
|
407
406
|
def dump_to(filename)
|
@@ -415,20 +414,22 @@ module ActiveRecord
|
|
415
414
|
end
|
416
415
|
|
417
416
|
def marshal_dump # :nodoc:
|
418
|
-
[@version, @columns, {}, @primary_keys, @data_sources, @indexes
|
417
|
+
[@version, @columns, {}, @primary_keys, @data_sources, @indexes]
|
419
418
|
end
|
420
419
|
|
421
420
|
def marshal_load(array) # :nodoc:
|
422
|
-
@version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes,
|
421
|
+
@version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes, _database_version = array
|
423
422
|
@indexes ||= {}
|
424
423
|
|
425
424
|
derive_columns_hash_and_deduplicate_values
|
426
425
|
end
|
427
426
|
|
428
427
|
private
|
429
|
-
def tables_to_cache(
|
430
|
-
|
431
|
-
|
428
|
+
def tables_to_cache(pool)
|
429
|
+
pool.with_connection do |connection|
|
430
|
+
connection.data_sources.reject do |table|
|
431
|
+
ignored_table?(table)
|
432
|
+
end
|
432
433
|
end
|
433
434
|
end
|
434
435
|
|
@@ -459,12 +460,6 @@ module ActiveRecord
|
|
459
460
|
end
|
460
461
|
end
|
461
462
|
|
462
|
-
def prepare_data_sources(connection)
|
463
|
-
tables_to_cache(connection).each do |source|
|
464
|
-
@data_sources[source] = true
|
465
|
-
end
|
466
|
-
end
|
467
|
-
|
468
463
|
def open(filename)
|
469
464
|
FileUtils.mkdir_p(File.dirname(filename))
|
470
465
|
|