activerecord 7.2.0 → 7.2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +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?
|