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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +196 -0
  3. data/lib/active_record/associations/has_many_through_association.rb +7 -1
  4. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  5. data/lib/active_record/associations/join_dependency.rb +5 -5
  6. data/lib/active_record/associations.rb +28 -16
  7. data/lib/active_record/attribute_assignment.rb +9 -1
  8. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
  9. data/lib/active_record/attributes.rb +6 -5
  10. data/lib/active_record/callbacks.rb +1 -1
  11. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +49 -43
  12. data/lib/active_record/connection_adapters/abstract/query_cache.rb +45 -16
  13. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1 -1
  14. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +7 -3
  15. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
  16. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  17. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +2 -0
  18. data/lib/active_record/connection_adapters/postgresql_adapter.rb +3 -3
  19. data/lib/active_record/core.rb +41 -9
  20. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
  21. data/lib/active_record/encryption/encryptable_record.rb +1 -1
  22. data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
  23. data/lib/active_record/encryption/encryptor.rb +1 -1
  24. data/lib/active_record/encryption/key_provider.rb +1 -1
  25. data/lib/active_record/encryption.rb +2 -0
  26. data/lib/active_record/enum.rb +8 -0
  27. data/lib/active_record/gem_version.rb +2 -2
  28. data/lib/active_record/marshalling.rb +4 -1
  29. data/lib/active_record/model_schema.rb +5 -2
  30. data/lib/active_record/nested_attributes.rb +13 -2
  31. data/lib/active_record/query_cache.rb +4 -5
  32. data/lib/active_record/query_logs.rb +1 -1
  33. data/lib/active_record/railtie.rb +7 -12
  34. data/lib/active_record/reflection.rb +15 -8
  35. data/lib/active_record/relation/batches.rb +4 -4
  36. data/lib/active_record/relation/calculations.rb +1 -1
  37. data/lib/active_record/relation/predicate_builder.rb +1 -1
  38. data/lib/active_record/relation/query_methods.rb +39 -24
  39. data/lib/active_record/scoping/named.rb +1 -0
  40. data/lib/active_record/tasks/database_tasks.rb +12 -1
  41. data/lib/active_record/testing/query_assertions.rb +2 -2
  42. data/lib/active_record/validations/uniqueness.rb +1 -0
  43. data/lib/arel/visitors/sqlite.rb +25 -0
  44. 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
- @thread_query_caches = Concurrent::Map.new(initial_capacity: @size)
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, query_cache.dirties = true, true
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, query_cache.dirties = false, true
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
- @thread_query_caches.each_value(&:clear)
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 used.
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
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
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
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
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?
@@ -161,7 +161,7 @@ module ActiveRecord
161
161
  end
162
162
 
163
163
  def valid_primary_key_options
164
- super + [:unsigned]
164
+ super + [:unsigned, :auto_increment]
165
165
  end
166
166
 
167
167
  def create_table_definition(name, **options)
@@ -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
- super || !old_value.nil? && old_value.prefix != new_value.prefix
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.alias_type "float8", "float4"
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
@@ -102,8 +102,20 @@ module ActiveRecord
102
102
 
103
103
  class_attribute :shard_selector, instance_accessor: false, default: nil
104
104
 
105
- # Specifies the attributes that will be included in the output of the #inspect method
106
- class_attribute :attributes_for_inspect, instance_accessor: false, default: [:id]
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}.lease_connection' to establish a connection)"
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 specified by <tt>.attributes_for_inspect</tt> as a nicely formatted string.
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 the full contents of the record as a nicely formatted string.
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(attribute_names)
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 ? attribute_names : self.class.attributes_for_inspect
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
@@ -45,7 +45,7 @@ module ActiveRecord
45
45
  attr_reader :uri
46
46
 
47
47
  def uri_parser
48
- @uri_parser ||= URI::Parser.new
48
+ @uri_parser ||= URI::RFC2396_Parser.new
49
49
  end
50
50
 
51
51
  # Converts the query parameters of the URI into a hash.
@@ -123,7 +123,7 @@ module ActiveRecord
123
123
  end)
124
124
  end
125
125
 
126
- def load_schema!
126
+ def load_schema! # :nodoc:
127
127
  super
128
128
 
129
129
  add_length_validation_for_encrypted_columns if ActiveRecord::Encryption.config.validate_column_size
@@ -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 < ::ActiveRecord::Type::Text
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 a +clean_text+ and returns the result as clean text
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 first key in the list as the active key to perform encryptions
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
@@ -53,4 +53,6 @@ module ActiveRecord
53
53
  Cipher.eager_load!
54
54
  end
55
55
  end
56
+
57
+ ActiveSupport.run_load_hooks :active_record_encryption, Encryption
56
58
  end
@@ -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 " \
@@ -9,8 +9,8 @@ module ActiveRecord
9
9
  module VERSION
10
10
  MAJOR = 7
11
11
  MINOR = 2
12
- TINY = 0
13
- PRE = nil
12
+ TINY = 2
13
+ PRE = "1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -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) && association(reflection.name).loaded?
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
- def load_schema # :nodoc:
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.detect { |record| record.id.to_s == attributes["id"].to_s }
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.detect { |record| record.id.to_s == attributes["id"].to_s }
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(&:enable_query_cache!)
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.path)
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(:active_record) do
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 ||= compute_class(compute_name(class_name))
431
+ @klass ||= _klass(class_name)
432
432
  end
433
433
 
434
- def compute_class(name)
435
- name.constantize
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 compute_name(name) # :nodoc:
439
- active_record.name.demodulize == name ? "::#{name}" : name
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.compute_class(compute_name(class_name))
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 = @previous_reflection.join_scopes(table, predicate_builder, klass, record) + super
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?