activerecord 7.2.0 → 7.2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +196 -0
- data/lib/active_record/associations/has_many_through_association.rb +7 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +5 -5
- data/lib/active_record/associations.rb +28 -16
- data/lib/active_record/attribute_assignment.rb +9 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
- data/lib/active_record/attributes.rb +6 -5
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +49 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +45 -16
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +7 -3
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +3 -3
- data/lib/active_record/core.rb +41 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +1 -1
- data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
- data/lib/active_record/encryption/encryptor.rb +1 -1
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption.rb +2 -0
- data/lib/active_record/enum.rb +8 -0
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/marshalling.rb +4 -1
- data/lib/active_record/model_schema.rb +5 -2
- data/lib/active_record/nested_attributes.rb +13 -2
- data/lib/active_record/query_cache.rb +4 -5
- data/lib/active_record/query_logs.rb +1 -1
- data/lib/active_record/railtie.rb +7 -12
- data/lib/active_record/reflection.rb +15 -8
- data/lib/active_record/relation/batches.rb +4 -4
- data/lib/active_record/relation/calculations.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +39 -24
- data/lib/active_record/scoping/named.rb +1 -0
- data/lib/active_record/tasks/database_tasks.rb +12 -1
- data/lib/active_record/testing/query_assertions.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +1 -0
- data/lib/arel/visitors/sqlite.rb +25 -0
- metadata +13 -13
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "concurrent/map"
|
4
|
+
require "concurrent/atomic/atomic_fixnum"
|
4
5
|
|
5
6
|
module ActiveRecord
|
6
7
|
module ConnectionAdapters # :nodoc:
|
@@ -35,7 +36,9 @@ module ActiveRecord
|
|
35
36
|
alias_method :enabled?, :enabled
|
36
37
|
alias_method :dirties?, :dirties
|
37
38
|
|
38
|
-
def initialize(max_size)
|
39
|
+
def initialize(version, max_size)
|
40
|
+
@version = version
|
41
|
+
@current_version = version.value
|
39
42
|
@map = {}
|
40
43
|
@max_size = max_size
|
41
44
|
@enabled = false
|
@@ -43,14 +46,17 @@ module ActiveRecord
|
|
43
46
|
end
|
44
47
|
|
45
48
|
def size
|
49
|
+
check_version
|
46
50
|
@map.size
|
47
51
|
end
|
48
52
|
|
49
53
|
def empty?
|
54
|
+
check_version
|
50
55
|
@map.empty?
|
51
56
|
end
|
52
57
|
|
53
58
|
def [](key)
|
59
|
+
check_version
|
54
60
|
return unless @enabled
|
55
61
|
|
56
62
|
if entry = @map.delete(key)
|
@@ -59,6 +65,8 @@ module ActiveRecord
|
|
59
65
|
end
|
60
66
|
|
61
67
|
def compute_if_absent(key)
|
68
|
+
check_version
|
69
|
+
|
62
70
|
return yield unless @enabled
|
63
71
|
|
64
72
|
if entry = @map.delete(key)
|
@@ -76,12 +84,40 @@ module ActiveRecord
|
|
76
84
|
@map.clear
|
77
85
|
self
|
78
86
|
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def check_version
|
90
|
+
if @current_version != @version.value
|
91
|
+
@map.clear
|
92
|
+
@current_version = @version.value
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class QueryCacheRegistry # :nodoc:
|
98
|
+
def initialize
|
99
|
+
@mutex = Mutex.new
|
100
|
+
@map = ConnectionPool::WeakThreadKeyMap.new
|
101
|
+
end
|
102
|
+
|
103
|
+
def compute_if_absent(context)
|
104
|
+
@map[context] || @mutex.synchronize do
|
105
|
+
@map[context] ||= yield
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def clear
|
110
|
+
@map.synchronize do
|
111
|
+
@map.clear
|
112
|
+
end
|
113
|
+
end
|
79
114
|
end
|
80
115
|
|
81
116
|
module ConnectionPoolConfiguration # :nodoc:
|
82
117
|
def initialize(...)
|
83
118
|
super
|
84
|
-
@
|
119
|
+
@query_cache_version = Concurrent::AtomicFixnum.new
|
120
|
+
@thread_query_caches = QueryCacheRegistry.new
|
85
121
|
@query_cache_max_size = \
|
86
122
|
case query_cache = db_config&.query_cache
|
87
123
|
when 0, false
|
@@ -121,11 +157,13 @@ module ActiveRecord
|
|
121
157
|
end
|
122
158
|
|
123
159
|
def enable_query_cache!
|
124
|
-
query_cache.enabled
|
160
|
+
query_cache.enabled = true
|
161
|
+
query_cache.dirties = true
|
125
162
|
end
|
126
163
|
|
127
164
|
def disable_query_cache!
|
128
|
-
query_cache.enabled
|
165
|
+
query_cache.enabled = false
|
166
|
+
query_cache.dirties = true
|
129
167
|
end
|
130
168
|
|
131
169
|
def query_cache_enabled
|
@@ -141,25 +179,16 @@ module ActiveRecord
|
|
141
179
|
# With transactional fixtures, and especially systems test
|
142
180
|
# another thread may use the same connection, but with a different
|
143
181
|
# query cache. So we must clear them all.
|
144
|
-
@
|
145
|
-
else
|
146
|
-
query_cache.clear
|
182
|
+
@query_cache_version.increment
|
147
183
|
end
|
184
|
+
query_cache.clear
|
148
185
|
end
|
149
186
|
|
150
187
|
def query_cache
|
151
188
|
@thread_query_caches.compute_if_absent(ActiveSupport::IsolatedExecutionState.context) do
|
152
|
-
Store.new(@query_cache_max_size)
|
189
|
+
Store.new(@query_cache_version, @query_cache_max_size)
|
153
190
|
end
|
154
191
|
end
|
155
|
-
|
156
|
-
private
|
157
|
-
def prune_thread_cache
|
158
|
-
dead_threads = @thread_query_caches.keys.reject(&:alive?)
|
159
|
-
dead_threads.each do |dead_thread|
|
160
|
-
@thread_query_caches.delete(dead_thread)
|
161
|
-
end
|
162
|
-
end
|
163
192
|
end
|
164
193
|
|
165
194
|
attr_accessor :query_cache
|
@@ -682,7 +682,7 @@ module ActiveRecord
|
|
682
682
|
#
|
683
683
|
# If the options provided include an +if_exists+ key, it will be used to check if the
|
684
684
|
# column does not exist. This will silently ignore the migration rather than raising
|
685
|
-
# if the column was already
|
685
|
+
# if the column was already removed.
|
686
686
|
#
|
687
687
|
# remove_column(:suppliers, :qualification, if_exists: true)
|
688
688
|
def remove_column(table_name, column_name, type = nil, **options)
|
@@ -639,7 +639,7 @@ module ActiveRecord
|
|
639
639
|
end
|
640
640
|
|
641
641
|
def build_insert_sql(insert) # :nodoc:
|
642
|
-
no_op_column = quote_column_name(insert.keys.first)
|
642
|
+
no_op_column = quote_column_name(insert.keys.first) if insert.keys.first
|
643
643
|
|
644
644
|
# MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
|
645
645
|
# then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
|
@@ -648,7 +648,9 @@ module ActiveRecord
|
|
648
648
|
sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
|
649
649
|
|
650
650
|
if insert.skip_duplicates?
|
651
|
-
|
651
|
+
if no_op_column
|
652
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
|
653
|
+
end
|
652
654
|
elsif insert.update_duplicates?
|
653
655
|
if insert.raw_update_sql?
|
654
656
|
sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
|
@@ -662,7 +664,9 @@ module ActiveRecord
|
|
662
664
|
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
663
665
|
|
664
666
|
if insert.skip_duplicates?
|
665
|
-
|
667
|
+
if no_op_column
|
668
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
669
|
+
end
|
666
670
|
elsif insert.update_duplicates?
|
667
671
|
sql << " ON DUPLICATE KEY UPDATE "
|
668
672
|
if insert.raw_update_sql?
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
# TODO: Remove when IPAddr#== compares IPAddr#prefix. See
|
32
32
|
# https://github.com/ruby/ipaddr/issues/21
|
33
33
|
def changed?(old_value, new_value, _new_value_before_type_cast)
|
34
|
-
|
34
|
+
!old_value.eql?(new_value) || !old_value.nil? && old_value.prefix != new_value.prefix
|
35
35
|
end
|
36
36
|
|
37
37
|
def cast_value(value)
|
@@ -255,6 +255,8 @@ module ActiveRecord
|
|
255
255
|
|
256
256
|
# Returns the sequence name for a table's primary key or some other specified key.
|
257
257
|
def default_sequence_name(table_name, pk = "id") # :nodoc:
|
258
|
+
return nil if pk.is_a?(Array)
|
259
|
+
|
258
260
|
result = serial_sequence(table_name, pk)
|
259
261
|
return nil unless result
|
260
262
|
Utils.extract_schema_qualified_name(result).to_s
|
@@ -652,8 +652,8 @@ module ActiveRecord
|
|
652
652
|
m.register_type "int4", Type::Integer.new(limit: 4)
|
653
653
|
m.register_type "int8", Type::Integer.new(limit: 8)
|
654
654
|
m.register_type "oid", OID::Oid.new
|
655
|
-
m.register_type "float4", Type::Float.new
|
656
|
-
m.
|
655
|
+
m.register_type "float4", Type::Float.new(limit: 24)
|
656
|
+
m.register_type "float8", Type::Float.new
|
657
657
|
m.register_type "text", Type::Text.new
|
658
658
|
register_class_with_limit m, "varchar", Type::String
|
659
659
|
m.alias_type "char", "varchar"
|
@@ -777,7 +777,7 @@ module ActiveRecord
|
|
777
777
|
|
778
778
|
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
779
779
|
when nil
|
780
|
-
if exception.message.match?(/connection is closed/i)
|
780
|
+
if exception.message.match?(/connection is closed/i) || exception.message.match?(/no connection to the server/i)
|
781
781
|
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
782
782
|
elsif exception.is_a?(PG::ConnectionBad)
|
783
783
|
# libpq message style always ends with a newline; the pg gem's internal
|
data/lib/active_record/core.rb
CHANGED
@@ -102,8 +102,20 @@ module ActiveRecord
|
|
102
102
|
|
103
103
|
class_attribute :shard_selector, instance_accessor: false, default: nil
|
104
104
|
|
105
|
-
|
106
|
-
|
105
|
+
##
|
106
|
+
# :singleton-method:
|
107
|
+
#
|
108
|
+
# Specifies the attributes that will be included in the output of the
|
109
|
+
# #inspect method:
|
110
|
+
#
|
111
|
+
# Post.attributes_for_inspect = [:id, :title]
|
112
|
+
# Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!">"
|
113
|
+
#
|
114
|
+
# When set to `:all` inspect will list all the record's attributes:
|
115
|
+
#
|
116
|
+
# Post.attributes_for_inspect = :all
|
117
|
+
# Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
118
|
+
class_attribute :attributes_for_inspect, instance_accessor: false, default: :all
|
107
119
|
|
108
120
|
def self.application_record_class? # :nodoc:
|
109
121
|
if ActiveRecord.application_record_class
|
@@ -349,12 +361,12 @@ module ActiveRecord
|
|
349
361
|
|
350
362
|
# Returns a string like 'Post(id:integer, title:string, body:text)'
|
351
363
|
def inspect # :nodoc:
|
352
|
-
if self == Base
|
364
|
+
if self == Base || singleton_class?
|
353
365
|
super
|
354
366
|
elsif abstract_class?
|
355
367
|
"#{super}(abstract)"
|
356
|
-
elsif !connected?
|
357
|
-
"#{super} (call '#{super}.
|
368
|
+
elsif !schema_loaded? && !connected?
|
369
|
+
"#{super} (call '#{super}.load_schema' to load schema informations)"
|
358
370
|
elsif table_exists?
|
359
371
|
attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
|
360
372
|
"#{super}(#{attr_list})"
|
@@ -724,14 +736,28 @@ module ActiveRecord
|
|
724
736
|
self.class.connection_handler
|
725
737
|
end
|
726
738
|
|
727
|
-
# Returns the attributes
|
739
|
+
# Returns the attributes of the record as a nicely formatted string.
|
740
|
+
#
|
741
|
+
# Post.first.inspect
|
742
|
+
# #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
743
|
+
#
|
744
|
+
# The attributes can be limited by setting <tt>.attributes_for_inspect</tt>.
|
745
|
+
#
|
746
|
+
# Post.attributes_for_inspect = [:id, :title]
|
747
|
+
# Post.first.inspect
|
748
|
+
# #=> "#<Post id: 1, title: "Hello, World!">"
|
728
749
|
def inspect
|
729
750
|
inspect_with_attributes(attributes_for_inspect)
|
730
751
|
end
|
731
752
|
|
732
|
-
# Returns
|
753
|
+
# Returns all attributes of the record as a nicely formatted string,
|
754
|
+
# ignoring <tt>.attributes_for_inspect</tt>.
|
755
|
+
#
|
756
|
+
# Post.first.full_inspect
|
757
|
+
# #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
758
|
+
#
|
733
759
|
def full_inspect
|
734
|
-
inspect_with_attributes(
|
760
|
+
inspect_with_attributes(all_attributes_for_inspect)
|
735
761
|
end
|
736
762
|
|
737
763
|
# Takes a PP and prettily prints this record to it, allowing you to get a nice result from <tt>pp record</tt>
|
@@ -823,7 +849,13 @@ module ActiveRecord
|
|
823
849
|
end
|
824
850
|
|
825
851
|
def attributes_for_inspect
|
826
|
-
self.class.attributes_for_inspect == :all ?
|
852
|
+
self.class.attributes_for_inspect == :all ? all_attributes_for_inspect : self.class.attributes_for_inspect
|
853
|
+
end
|
854
|
+
|
855
|
+
def all_attributes_for_inspect
|
856
|
+
return [] unless @attributes
|
857
|
+
|
858
|
+
attribute_names
|
827
859
|
end
|
828
860
|
end
|
829
861
|
end
|
@@ -7,13 +7,13 @@ module ActiveRecord
|
|
7
7
|
# This is the central piece that connects the encryption system with +encrypts+ declarations in the
|
8
8
|
# model classes. Whenever you declare an attribute as encrypted, it configures an +EncryptedAttributeType+
|
9
9
|
# for that attribute.
|
10
|
-
class EncryptedAttributeType < ::
|
10
|
+
class EncryptedAttributeType < ::ActiveModel::Type::Value
|
11
11
|
include ActiveModel::Type::Helpers::Mutable
|
12
12
|
|
13
13
|
attr_reader :scheme, :cast_type
|
14
14
|
|
15
15
|
delegate :key_provider, :downcase?, :deterministic?, :previous_schemes, :with_context, :fixed?, to: :scheme
|
16
|
-
delegate :accessor, to: :cast_type
|
16
|
+
delegate :accessor, :type, to: :cast_type
|
17
17
|
|
18
18
|
# === Options
|
19
19
|
#
|
@@ -46,7 +46,7 @@ module ActiveRecord
|
|
46
46
|
serialize_message build_encrypted_message(clear_text, key_provider: key_provider, cipher_options: cipher_options)
|
47
47
|
end
|
48
48
|
|
49
|
-
# Decrypts
|
49
|
+
# Decrypts an +encrypted_text+ and returns the result as clean text
|
50
50
|
#
|
51
51
|
# === Options
|
52
52
|
#
|
@@ -12,7 +12,7 @@ module ActiveRecord
|
|
12
12
|
@keys = Array(keys)
|
13
13
|
end
|
14
14
|
|
15
|
-
# Returns the
|
15
|
+
# Returns the last key in the list as the active key to perform encryptions
|
16
16
|
#
|
17
17
|
# When +ActiveRecord::Encryption.config.store_key_references+ is true, the key will include
|
18
18
|
# a public tag referencing the key itself. That key will be stored in the public
|
data/lib/active_record/enum.rb
CHANGED
@@ -240,6 +240,7 @@ module ActiveRecord
|
|
240
240
|
|
241
241
|
def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
|
242
242
|
assert_valid_enum_definition_values(values)
|
243
|
+
assert_valid_enum_options(options)
|
243
244
|
# statuses = { }
|
244
245
|
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
245
246
|
name = name.to_s
|
@@ -370,6 +371,13 @@ module ActiveRecord
|
|
370
371
|
end
|
371
372
|
end
|
372
373
|
|
374
|
+
def assert_valid_enum_options(options)
|
375
|
+
invalid_keys = options.keys & %i[_prefix _suffix _scopes _default _instance_methods]
|
376
|
+
unless invalid_keys.empty?
|
377
|
+
raise ArgumentError, "invalid option(s): #{invalid_keys.map(&:inspect).join(", ")}. Valid options are: :prefix, :suffix, :scopes, :default, :instance_methods, and :validate."
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
373
381
|
ENUM_CONFLICT_MESSAGE = \
|
374
382
|
"You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
|
375
383
|
"this will generate a %{type} method \"%{method}\", which is already defined " \
|
@@ -25,7 +25,10 @@ module ActiveRecord
|
|
25
25
|
payload = [attributes_for_database, new_record?]
|
26
26
|
|
27
27
|
cached_associations = self.class.reflect_on_all_associations.select do |reflection|
|
28
|
-
association_cached?(reflection.name)
|
28
|
+
if association_cached?(reflection.name)
|
29
|
+
association = association(reflection.name)
|
30
|
+
association.loaded? || association.target.present?
|
31
|
+
end
|
29
32
|
end
|
30
33
|
|
31
34
|
unless cached_associations.empty?
|
@@ -431,7 +431,6 @@ module ActiveRecord
|
|
431
431
|
end
|
432
432
|
|
433
433
|
def columns
|
434
|
-
load_schema unless @columns
|
435
434
|
@columns ||= columns_hash.values.freeze
|
436
435
|
end
|
437
436
|
|
@@ -531,7 +530,9 @@ module ActiveRecord
|
|
531
530
|
initialize_find_by_cache
|
532
531
|
end
|
533
532
|
|
534
|
-
|
533
|
+
# Load the model's schema information either from the schema cache
|
534
|
+
# or directly from the database.
|
535
|
+
def load_schema
|
535
536
|
return if schema_loaded?
|
536
537
|
@load_schema_monitor.synchronize do
|
537
538
|
return if schema_loaded?
|
@@ -592,6 +593,8 @@ module ActiveRecord
|
|
592
593
|
columns_hash = schema_cache.columns_hash(table_name)
|
593
594
|
columns_hash = columns_hash.except(*ignored_columns) unless ignored_columns.empty?
|
594
595
|
@columns_hash = columns_hash.freeze
|
596
|
+
|
597
|
+
_default_attributes # Precompute to cache DB-dependent attribute types
|
595
598
|
end
|
596
599
|
|
597
600
|
# Guesses the table name, but does not decorate it with prefix and suffix information.
|
@@ -524,12 +524,12 @@ module ActiveRecord
|
|
524
524
|
unless reject_new_record?(association_name, attributes)
|
525
525
|
association.reader.build(attributes.except(*UNASSIGNABLE_KEYS))
|
526
526
|
end
|
527
|
-
elsif existing_record = existing_records
|
527
|
+
elsif existing_record = find_record_by_id(existing_records, attributes["id"])
|
528
528
|
unless call_reject_if(association_name, attributes)
|
529
529
|
# Make sure we are operating on the actual object which is in the association's
|
530
530
|
# proxy_target array (either by finding it, or adding it if not found)
|
531
531
|
# Take into account that the proxy_target may have changed due to callbacks
|
532
|
-
target_record = association.target
|
532
|
+
target_record = find_record_by_id(association.target, attributes["id"])
|
533
533
|
if target_record
|
534
534
|
existing_record = target_record
|
535
535
|
else
|
@@ -620,5 +620,16 @@ module ActiveRecord
|
|
620
620
|
raise RecordNotFound.new("Couldn't find #{model} with ID=#{record_id} for #{self.class.name} with ID=#{id}",
|
621
621
|
model, "id", record_id)
|
622
622
|
end
|
623
|
+
|
624
|
+
def find_record_by_id(records, id)
|
625
|
+
return if records.empty?
|
626
|
+
|
627
|
+
if records.first.class.composite_primary_key?
|
628
|
+
id = Array(id).map(&:to_s)
|
629
|
+
records.find { |record| Array(record.id).map(&:to_s) == id }
|
630
|
+
else
|
631
|
+
records.find { |record| record.id.to_s == id.to_s }
|
632
|
+
end
|
633
|
+
end
|
623
634
|
end
|
624
635
|
end
|
@@ -35,7 +35,10 @@ module ActiveRecord
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def self.run
|
38
|
-
ActiveRecord::Base.connection_handler.each_connection_pool.reject(&:query_cache_enabled).each
|
38
|
+
ActiveRecord::Base.connection_handler.each_connection_pool.reject(&:query_cache_enabled).each do |pool|
|
39
|
+
next if pool.db_config&.query_cache == false
|
40
|
+
pool.enable_query_cache!
|
41
|
+
end
|
39
42
|
end
|
40
43
|
|
41
44
|
def self.complete(pools)
|
@@ -43,10 +46,6 @@ module ActiveRecord
|
|
43
46
|
pool.disable_query_cache!
|
44
47
|
pool.clear_query_cache
|
45
48
|
end
|
46
|
-
|
47
|
-
ActiveRecord::Base.connection_handler.each_connection_pool do |pool|
|
48
|
-
pool.release_connection if pool.active_connection? && !pool.lease_connection.transaction_open?
|
49
|
-
end
|
50
49
|
end
|
51
50
|
|
52
51
|
def self.install_executor_hooks(executor = ActiveSupport::Executor)
|
@@ -112,7 +112,7 @@ module ActiveRecord
|
|
112
112
|
if Thread.respond_to?(:each_caller_location)
|
113
113
|
def query_source_location # :nodoc:
|
114
114
|
Thread.each_caller_location do |location|
|
115
|
-
frame = LogSubscriber.backtrace_cleaner.clean_frame(location
|
115
|
+
frame = LogSubscriber.backtrace_cleaner.clean_frame(location)
|
116
116
|
return frame if frame
|
117
117
|
end
|
118
118
|
nil
|
@@ -69,6 +69,7 @@ module ActiveRecord
|
|
69
69
|
Rails.logger.broadcast_to(console)
|
70
70
|
end
|
71
71
|
ActiveRecord.verbose_query_logs = false
|
72
|
+
ActiveRecord::Base.attributes_for_inspect = :all
|
72
73
|
end
|
73
74
|
|
74
75
|
runner do
|
@@ -311,6 +312,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
311
312
|
initializer "active_record.set_executor_hooks" do
|
312
313
|
ActiveRecord::QueryCache.install_executor_hooks
|
313
314
|
ActiveRecord::AsynchronousQueriesTracker.install_executor_hooks
|
315
|
+
ActiveRecord::ConnectionAdapters::ConnectionPool.install_executor_hooks
|
314
316
|
end
|
315
317
|
|
316
318
|
initializer "active_record.add_watchable_files" do |app|
|
@@ -356,16 +358,19 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
356
358
|
end
|
357
359
|
|
358
360
|
initializer "active_record_encryption.configuration" do |app|
|
359
|
-
ActiveSupport.on_load(:
|
360
|
-
ActiveRecord::Encryption.configure
|
361
|
+
ActiveSupport.on_load(:active_record_encryption) do
|
362
|
+
ActiveRecord::Encryption.configure(
|
361
363
|
primary_key: app.credentials.dig(:active_record_encryption, :primary_key),
|
362
364
|
deterministic_key: app.credentials.dig(:active_record_encryption, :deterministic_key),
|
363
365
|
key_derivation_salt: app.credentials.dig(:active_record_encryption, :key_derivation_salt),
|
364
366
|
**app.config.active_record.encryption
|
367
|
+
)
|
365
368
|
|
366
369
|
auto_filtered_parameters = ActiveRecord::Encryption::AutoFilteredParameters.new(app)
|
367
370
|
auto_filtered_parameters.enable if ActiveRecord::Encryption.config.add_to_filter_parameters
|
371
|
+
end
|
368
372
|
|
373
|
+
ActiveSupport.on_load(:active_record) do
|
369
374
|
# Support extended queries for deterministic attributes and validations
|
370
375
|
if ActiveRecord::Encryption.config.extend_queries
|
371
376
|
ActiveRecord::Encryption::ExtendedDeterministicQueries.install_support
|
@@ -432,15 +437,5 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
432
437
|
end
|
433
438
|
end
|
434
439
|
end
|
435
|
-
|
436
|
-
initializer "active_record.attributes_for_inspect" do |app|
|
437
|
-
ActiveSupport.on_load(:active_record) do
|
438
|
-
if app.config.consider_all_requests_local
|
439
|
-
if app.config.active_record.attributes_for_inspect.nil?
|
440
|
-
ActiveRecord::Base.attributes_for_inspect = :all
|
441
|
-
end
|
442
|
-
end
|
443
|
-
end
|
444
|
-
end
|
445
440
|
end
|
446
441
|
end
|
@@ -79,7 +79,7 @@ module ActiveRecord
|
|
79
79
|
normalized_reflections.stringify_keys
|
80
80
|
end
|
81
81
|
|
82
|
-
def normalized_reflections # :nodoc
|
82
|
+
def normalized_reflections # :nodoc:
|
83
83
|
@__reflections ||= begin
|
84
84
|
ref = {}
|
85
85
|
|
@@ -428,15 +428,19 @@ module ActiveRecord
|
|
428
428
|
# a new association object. Use +build_association+ or +create_association+
|
429
429
|
# instead. This allows plugins to hook into association object creation.
|
430
430
|
def klass
|
431
|
-
@klass ||=
|
431
|
+
@klass ||= _klass(class_name)
|
432
432
|
end
|
433
433
|
|
434
|
-
def
|
435
|
-
name.
|
434
|
+
def _klass(class_name) # :nodoc:
|
435
|
+
if active_record.name.demodulize == class_name
|
436
|
+
return compute_class("::#{class_name}") rescue NameError
|
437
|
+
end
|
438
|
+
|
439
|
+
compute_class(class_name)
|
436
440
|
end
|
437
441
|
|
438
|
-
def
|
439
|
-
|
442
|
+
def compute_class(name)
|
443
|
+
name.constantize
|
440
444
|
end
|
441
445
|
|
442
446
|
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
|
@@ -986,7 +990,7 @@ module ActiveRecord
|
|
986
990
|
end
|
987
991
|
|
988
992
|
def klass
|
989
|
-
@klass ||= delegate_reflection.
|
993
|
+
@klass ||= delegate_reflection._klass(class_name)
|
990
994
|
end
|
991
995
|
|
992
996
|
# Returns the source of the through reflection. It checks both a singularized
|
@@ -1235,7 +1239,10 @@ module ActiveRecord
|
|
1235
1239
|
end
|
1236
1240
|
|
1237
1241
|
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
1238
|
-
scopes =
|
1242
|
+
scopes = super
|
1243
|
+
unless @previous_reflection.through_reflection?
|
1244
|
+
scopes += @previous_reflection.join_scopes(table, predicate_builder, klass, record)
|
1245
|
+
end
|
1239
1246
|
scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
|
1240
1247
|
end
|
1241
1248
|
|
@@ -241,14 +241,14 @@ module ActiveRecord
|
|
241
241
|
raise ArgumentError, ":order must be :asc or :desc or an array consisting of :asc or :desc, got #{order.inspect}"
|
242
242
|
end
|
243
243
|
|
244
|
-
unless block
|
245
|
-
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self, order: order, use_ranges: use_ranges)
|
246
|
-
end
|
247
|
-
|
248
244
|
if arel.orders.present?
|
249
245
|
act_on_ignored_order(error_on_ignore)
|
250
246
|
end
|
251
247
|
|
248
|
+
unless block
|
249
|
+
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self, order: order, use_ranges: use_ranges)
|
250
|
+
end
|
251
|
+
|
252
252
|
batch_limit = of
|
253
253
|
|
254
254
|
if limit_value
|
@@ -307,8 +307,8 @@ module ActiveRecord
|
|
307
307
|
relation.pluck(*column_names)
|
308
308
|
else
|
309
309
|
klass.disallow_raw_sql!(flattened_args(column_names))
|
310
|
-
columns = arel_columns(column_names)
|
311
310
|
relation = spawn
|
311
|
+
columns = relation.arel_columns(column_names)
|
312
312
|
relation.select_values = columns
|
313
313
|
result = skip_query_cache_if_necessary do
|
314
314
|
if where_clause.contradiction?
|