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.
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?