activerecord 7.2.1.1 → 8.0.0.beta1

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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +188 -791
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/associations/association.rb +25 -5
  5. data/lib/active_record/associations/builder/association.rb +7 -6
  6. data/lib/active_record/associations/collection_association.rb +10 -8
  7. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  8. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  9. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  10. data/lib/active_record/associations/join_dependency.rb +4 -4
  11. data/lib/active_record/associations/preloader/association.rb +2 -2
  12. data/lib/active_record/associations/singular_association.rb +8 -3
  13. data/lib/active_record/associations.rb +34 -4
  14. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  15. data/lib/active_record/attribute_assignment.rb +9 -1
  16. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
  17. data/lib/active_record/attributes.rb +1 -2
  18. data/lib/active_record/autosave_association.rb +69 -27
  19. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
  20. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  21. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
  22. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +0 -1
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
  24. data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -4
  25. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  26. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  27. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +26 -5
  28. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
  29. data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -25
  30. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +20 -38
  31. data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
  32. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  33. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -45
  34. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
  35. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
  36. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
  37. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  38. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  39. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +0 -1
  40. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +50 -6
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +38 -90
  42. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  43. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
  44. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  45. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
  46. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -1
  47. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +55 -12
  48. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
  49. data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
  50. data/lib/active_record/connection_handling.rb +22 -0
  51. data/lib/active_record/core.rb +14 -7
  52. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
  53. data/lib/active_record/encryption/config.rb +3 -1
  54. data/lib/active_record/encryption/encryptable_record.rb +4 -4
  55. data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
  56. data/lib/active_record/encryption/encryptor.rb +15 -8
  57. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  58. data/lib/active_record/encryption/key_provider.rb +1 -1
  59. data/lib/active_record/encryption/scheme.rb +8 -1
  60. data/lib/active_record/encryption.rb +2 -0
  61. data/lib/active_record/enum.rb +7 -10
  62. data/lib/active_record/errors.rb +13 -5
  63. data/lib/active_record/fixtures.rb +0 -1
  64. data/lib/active_record/future_result.rb +14 -10
  65. data/lib/active_record/gem_version.rb +4 -4
  66. data/lib/active_record/insert_all.rb +1 -1
  67. data/lib/active_record/migration/command_recorder.rb +22 -5
  68. data/lib/active_record/migration/compatibility.rb +5 -2
  69. data/lib/active_record/migration.rb +35 -33
  70. data/lib/active_record/model_schema.rb +2 -3
  71. data/lib/active_record/nested_attributes.rb +11 -2
  72. data/lib/active_record/persistence.rb +128 -130
  73. data/lib/active_record/query_logs.rb +97 -39
  74. data/lib/active_record/query_logs_formatter.rb +17 -28
  75. data/lib/active_record/querying.rb +6 -6
  76. data/lib/active_record/railtie.rb +8 -14
  77. data/lib/active_record/reflection.rb +9 -4
  78. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  79. data/lib/active_record/relation/batches.rb +132 -72
  80. data/lib/active_record/relation/calculations.rb +24 -19
  81. data/lib/active_record/relation/delegation.rb +25 -14
  82. data/lib/active_record/relation/finder_methods.rb +18 -18
  83. data/lib/active_record/relation/merger.rb +8 -8
  84. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  85. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  86. data/lib/active_record/relation/predicate_builder.rb +6 -1
  87. data/lib/active_record/relation/query_methods.rb +58 -37
  88. data/lib/active_record/relation/record_fetch_warning.rb +2 -2
  89. data/lib/active_record/relation/spawn_methods.rb +1 -1
  90. data/lib/active_record/relation.rb +72 -61
  91. data/lib/active_record/result.rb +68 -7
  92. data/lib/active_record/sanitization.rb +7 -6
  93. data/lib/active_record/schema_dumper.rb +5 -0
  94. data/lib/active_record/schema_migration.rb +2 -1
  95. data/lib/active_record/scoping/named.rb +5 -2
  96. data/lib/active_record/statement_cache.rb +12 -12
  97. data/lib/active_record/store.rb +7 -3
  98. data/lib/active_record/tasks/database_tasks.rb +36 -16
  99. data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
  100. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
  101. data/lib/active_record/test_fixtures.rb +12 -0
  102. data/lib/active_record/token_for.rb +1 -1
  103. data/lib/active_record/validations/uniqueness.rb +9 -8
  104. data/lib/active_record.rb +15 -0
  105. data/lib/arel/collectors/bind.rb +1 -1
  106. metadata +10 -10
@@ -248,18 +248,16 @@ module ActiveRecord
248
248
 
249
249
  im = Arel::InsertManager.new(arel_table)
250
250
 
251
- with_connection do |c|
252
- if values.empty?
253
- im.insert(connection.empty_insert_statement_value(primary_key))
254
- else
255
- im.insert(values.transform_keys { |name| arel_table[name] })
256
- end
257
-
258
- connection.insert(
259
- im, "#{self} Create", primary_key || false, primary_key_value,
260
- returning: returning
261
- )
251
+ if values.empty?
252
+ im.insert(connection.empty_insert_statement_value(primary_key))
253
+ else
254
+ im.insert(values.transform_keys { |name| arel_table[name] })
262
255
  end
256
+
257
+ connection.insert(
258
+ im, "#{self} Create", primary_key || false, primary_key_value,
259
+ returning: returning
260
+ )
263
261
  end
264
262
 
265
263
  def _update_record(values, constraints) # :nodoc:
@@ -812,159 +810,159 @@ module ActiveRecord
812
810
  end
813
811
  end
814
812
 
815
- private
816
- def init_internals
817
- super
818
- @_trigger_destroy_callback = @_trigger_update_callback = nil
819
- @previously_new_record = false
820
- end
813
+ private
814
+ def init_internals
815
+ super
816
+ @_trigger_destroy_callback = @_trigger_update_callback = nil
817
+ @previously_new_record = false
818
+ end
821
819
 
822
- def strict_loaded_associations
823
- @association_cache.find_all do |_, assoc|
824
- assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
825
- end.map(&:first)
826
- end
820
+ def strict_loaded_associations
821
+ @association_cache.find_all do |_, assoc|
822
+ assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
823
+ end.map(&:first)
824
+ end
827
825
 
828
- def _find_record(options)
829
- all_queries = options ? options[:all_queries] : nil
830
- base = self.class.all(all_queries: all_queries).preload(strict_loaded_associations)
826
+ def _find_record(options)
827
+ all_queries = options ? options[:all_queries] : nil
828
+ base = self.class.all(all_queries: all_queries).preload(strict_loaded_associations)
831
829
 
832
- if options && options[:lock]
833
- base.lock(options[:lock]).find_by!(_in_memory_query_constraints_hash)
834
- else
835
- base.find_by!(_in_memory_query_constraints_hash)
830
+ if options && options[:lock]
831
+ base.lock(options[:lock]).find_by!(_in_memory_query_constraints_hash)
832
+ else
833
+ base.find_by!(_in_memory_query_constraints_hash)
834
+ end
836
835
  end
837
- end
838
836
 
839
- def _in_memory_query_constraints_hash
840
- if self.class.query_constraints_list.nil?
841
- { @primary_key => id }
842
- else
843
- self.class.query_constraints_list.index_with do |column_name|
844
- attribute(column_name)
837
+ def _in_memory_query_constraints_hash
838
+ if self.class.query_constraints_list.nil?
839
+ { @primary_key => id }
840
+ else
841
+ self.class.query_constraints_list.index_with do |column_name|
842
+ attribute(column_name)
843
+ end
845
844
  end
846
845
  end
847
- end
848
846
 
849
- def apply_scoping?(options)
850
- !(options && options[:unscoped]) &&
851
- (self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
852
- end
847
+ def apply_scoping?(options)
848
+ !(options && options[:unscoped]) &&
849
+ (self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
850
+ end
853
851
 
854
- def _query_constraints_hash
855
- if self.class.query_constraints_list.nil?
856
- { @primary_key => id_in_database }
857
- else
858
- self.class.query_constraints_list.index_with do |column_name|
859
- attribute_in_database(column_name)
852
+ def _query_constraints_hash
853
+ if self.class.query_constraints_list.nil?
854
+ { @primary_key => id_in_database }
855
+ else
856
+ self.class.query_constraints_list.index_with do |column_name|
857
+ attribute_in_database(column_name)
858
+ end
860
859
  end
861
860
  end
862
- end
863
861
 
864
- # A hook to be overridden by association modules.
865
- def destroy_associations
866
- end
867
-
868
- def destroy_row
869
- _delete_row
870
- end
871
-
872
- def _delete_row
873
- self.class._delete_record(_query_constraints_hash)
874
- end
862
+ # A hook to be overridden by association modules.
863
+ def destroy_associations
864
+ end
875
865
 
876
- def _touch_row(attribute_names, time)
877
- time ||= current_time_from_proper_timezone
866
+ def destroy_row
867
+ _delete_row
868
+ end
878
869
 
879
- attribute_names.each do |attr_name|
880
- _write_attribute(attr_name, time)
870
+ def _delete_row
871
+ self.class._delete_record(_query_constraints_hash)
881
872
  end
882
873
 
883
- _update_row(attribute_names, "touch")
884
- end
874
+ def _touch_row(attribute_names, time)
875
+ time ||= current_time_from_proper_timezone
885
876
 
886
- def _update_row(attribute_names, attempted_action = "update")
887
- self.class._update_record(
888
- attributes_with_values(attribute_names),
889
- _query_constraints_hash
890
- )
891
- end
877
+ attribute_names.each do |attr_name|
878
+ _write_attribute(attr_name, time)
879
+ end
892
880
 
893
- def create_or_update(**, &block)
894
- _raise_readonly_record_error if readonly?
895
- return false if destroyed?
896
- result = new_record? ? _create_record(&block) : _update_record(&block)
897
- result != false
898
- end
881
+ _update_row(attribute_names, "touch")
882
+ end
899
883
 
900
- # Updates the associated record with values matching those of the instance attributes.
901
- # Returns the number of affected rows.
902
- def _update_record(attribute_names = self.attribute_names)
903
- attribute_names = attributes_for_update(attribute_names)
884
+ def _update_row(attribute_names, attempted_action = "update")
885
+ self.class._update_record(
886
+ attributes_with_values(attribute_names),
887
+ _query_constraints_hash
888
+ )
889
+ end
904
890
 
905
- if attribute_names.empty?
906
- affected_rows = 0
907
- @_trigger_update_callback = true
908
- else
909
- affected_rows = _update_row(attribute_names)
910
- @_trigger_update_callback = affected_rows == 1
891
+ def create_or_update(**, &block)
892
+ _raise_readonly_record_error if readonly?
893
+ return false if destroyed?
894
+ result = new_record? ? _create_record(&block) : _update_record(&block)
895
+ result != false
911
896
  end
912
897
 
913
- @previously_new_record = false
898
+ # Updates the associated record with values matching those of the instance attributes.
899
+ # Returns the number of affected rows.
900
+ def _update_record(attribute_names = self.attribute_names)
901
+ attribute_names = attributes_for_update(attribute_names)
914
902
 
915
- yield(self) if block_given?
903
+ if attribute_names.empty?
904
+ affected_rows = 0
905
+ @_trigger_update_callback = true
906
+ else
907
+ affected_rows = _update_row(attribute_names)
908
+ @_trigger_update_callback = affected_rows == 1
909
+ end
916
910
 
917
- affected_rows
918
- end
911
+ @previously_new_record = false
919
912
 
920
- # Creates a record with values matching those of the instance attributes
921
- # and returns its id.
922
- def _create_record(attribute_names = self.attribute_names)
923
- attribute_names = attributes_for_create(attribute_names)
913
+ yield(self) if block_given?
924
914
 
925
- self.class.with_connection do |connection|
926
- returning_columns = self.class._returning_columns_for_insert(connection)
915
+ affected_rows
916
+ end
927
917
 
928
- returning_values = self.class._insert_record(
929
- connection,
930
- attributes_with_values(attribute_names),
931
- returning_columns
932
- )
918
+ # Creates a record with values matching those of the instance attributes
919
+ # and returns its id.
920
+ def _create_record(attribute_names = self.attribute_names)
921
+ attribute_names = attributes_for_create(attribute_names)
933
922
 
934
- returning_columns.zip(returning_values).each do |column, value|
935
- _write_attribute(column, value) if !_read_attribute(column)
936
- end if returning_values
937
- end
923
+ self.class.with_connection do |connection|
924
+ returning_columns = self.class._returning_columns_for_insert(connection)
938
925
 
939
- @new_record = false
940
- @previously_new_record = true
926
+ returning_values = self.class._insert_record(
927
+ connection,
928
+ attributes_with_values(attribute_names),
929
+ returning_columns
930
+ )
941
931
 
942
- yield(self) if block_given?
932
+ returning_columns.zip(returning_values).each do |column, value|
933
+ _write_attribute(column, value) if !_read_attribute(column)
934
+ end if returning_values
935
+ end
943
936
 
944
- id
945
- end
937
+ @new_record = false
938
+ @previously_new_record = true
946
939
 
947
- def verify_readonly_attribute(name)
948
- raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
949
- end
940
+ yield(self) if block_given?
950
941
 
951
- def _raise_record_not_destroyed
952
- @_association_destroy_exception ||= nil
953
- key = self.class.primary_key
954
- raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
955
- ensure
956
- @_association_destroy_exception = nil
957
- end
942
+ id
943
+ end
958
944
 
959
- def _raise_readonly_record_error
960
- raise ReadOnlyRecord, "#{self.class} is marked as readonly"
961
- end
945
+ def verify_readonly_attribute(name)
946
+ raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
947
+ end
962
948
 
963
- def _raise_record_not_touched_error
964
- raise ActiveRecordError, <<~MSG.squish
965
- Cannot touch on a new or destroyed record object. Consider using
966
- persisted?, new_record?, or destroyed? before touching.
967
- MSG
968
- end
949
+ def _raise_record_not_destroyed
950
+ @_association_destroy_exception ||= nil
951
+ key = self.class.primary_key
952
+ raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
953
+ ensure
954
+ @_association_destroy_exception = nil
955
+ end
956
+
957
+ def _raise_readonly_record_error
958
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly"
959
+ end
960
+
961
+ def _raise_record_not_touched_error
962
+ raise ActiveRecordError, <<~MSG.squish
963
+ Cannot touch on a new or destroyed record object. Consider using
964
+ persisted?, new_record?, or destroyed? before touching.
965
+ MSG
966
+ end
969
967
  end
970
968
  end
@@ -72,14 +72,70 @@ module ActiveRecord
72
72
  #
73
73
  # config.active_record.cache_query_log_tags = true
74
74
  module QueryLogs
75
- mattr_accessor :taggings, instance_accessor: false, default: {}
76
- mattr_accessor :tags, instance_accessor: false, default: [ :application ]
77
- mattr_accessor :prepend_comment, instance_accessor: false, default: false
78
- mattr_accessor :cache_query_log_tags, instance_accessor: false, default: false
79
- mattr_accessor :tags_formatter, instance_accessor: false
75
+ class GetKeyHandler # :nodoc:
76
+ def initialize(name)
77
+ @name = name
78
+ end
79
+
80
+ def call(context)
81
+ context[@name]
82
+ end
83
+ end
84
+
85
+ class IdentityHandler # :nodoc:
86
+ def initialize(value)
87
+ @value = value
88
+ end
89
+
90
+ def call(_context)
91
+ @value
92
+ end
93
+ end
94
+
95
+ class ZeroArityHandler # :nodoc:
96
+ def initialize(proc)
97
+ @proc = proc
98
+ end
99
+
100
+ def call(_context)
101
+ @proc.call
102
+ end
103
+ end
104
+
105
+ @taggings = {}.freeze
106
+ @tags = [ :application ].freeze
107
+ @prepend_comment = false
108
+ @cache_query_log_tags = false
109
+ @tags_formatter = false
110
+
80
111
  thread_mattr_accessor :cached_comment, instance_accessor: false
81
112
 
82
113
  class << self
114
+ attr_reader :tags, :taggings, :tags_formatter # :nodoc:
115
+ attr_accessor :prepend_comment, :cache_query_log_tags # :nodoc:
116
+
117
+ def taggings=(taggings) # :nodoc:
118
+ @taggings = taggings.freeze
119
+ @handlers = rebuild_handlers
120
+ end
121
+
122
+ def tags=(tags) # :nodoc:
123
+ @tags = tags.freeze
124
+ @handlers = rebuild_handlers
125
+ end
126
+
127
+ def tags_formatter=(format) # :nodoc:
128
+ @formatter = case format
129
+ when :legacy
130
+ LegacyFormatter
131
+ when :sqlcommenter
132
+ SQLCommenter
133
+ else
134
+ raise ArgumentError, "Formatter is unsupported: #{format}"
135
+ end
136
+ @tags_formatter = format
137
+ end
138
+
83
139
  def call(sql, connection) # :nodoc:
84
140
  comment = self.comment(connection)
85
141
 
@@ -96,19 +152,6 @@ module ActiveRecord
96
152
  self.cached_comment = nil
97
153
  end
98
154
 
99
- # Updates the formatter to be what the passed in format is.
100
- def update_formatter(format)
101
- self.tags_formatter =
102
- case format
103
- when :legacy
104
- LegacyFormatter.new
105
- when :sqlcommenter
106
- SQLCommenter.new
107
- else
108
- raise ArgumentError, "Formatter is unsupported: #{formatter}"
109
- end
110
- end
111
-
112
155
  if Thread.respond_to?(:each_caller_location)
113
156
  def query_source_location # :nodoc:
114
157
  Thread.each_caller_location do |location|
@@ -126,6 +169,35 @@ module ActiveRecord
126
169
  ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
127
170
 
128
171
  private
172
+ def rebuild_handlers
173
+ handlers = []
174
+ @tags.each do |i|
175
+ if i.is_a?(Hash)
176
+ i.each do |k, v|
177
+ handlers << [k, build_handler(k, v)]
178
+ end
179
+ else
180
+ handlers << [i, build_handler(i)]
181
+ end
182
+ end
183
+ handlers.sort_by! { |(key, _)| key.to_s }
184
+ end
185
+
186
+ def build_handler(name, handler = nil)
187
+ handler ||= @taggings[name]
188
+ if handler.nil?
189
+ GetKeyHandler.new(name)
190
+ elsif handler.respond_to?(:call)
191
+ if handler.arity == 0
192
+ ZeroArityHandler.new(handler)
193
+ else
194
+ handler
195
+ end
196
+ else
197
+ IdentityHandler.new(handler)
198
+ end
199
+ end
200
+
129
201
  # Returns an SQL comment +String+ containing the query log tags.
130
202
  # Sets and returns a cached comment if <tt>cache_query_log_tags</tt> is +true+.
131
203
  def comment(connection)
@@ -136,10 +208,6 @@ module ActiveRecord
136
208
  end
137
209
  end
138
210
 
139
- def formatter
140
- self.tags_formatter || self.update_formatter(:legacy)
141
- end
142
-
143
211
  def uncached_comment(connection)
144
212
  content = tag_content(connection)
145
213
 
@@ -165,25 +233,15 @@ module ActiveRecord
165
233
  context = ActiveSupport::ExecutionContext.to_h
166
234
  context[:connection] ||= connection
167
235
 
168
- pairs = tags.flat_map { |i| [*i] }.filter_map do |tag|
169
- key, handler = tag
170
- handler ||= taggings[key]
171
-
172
- val = if handler.nil?
173
- context[key]
174
- elsif handler.respond_to?(:call)
175
- if handler.arity == 0
176
- handler.call
177
- else
178
- handler.call(context)
179
- end
180
- else
181
- handler
182
- end
183
- [key, val] unless val.nil?
236
+ pairs = @handlers.filter_map do |(key, handler)|
237
+ val = handler.call(context)
238
+ @formatter.format(key, val) unless val.nil?
184
239
  end
185
- self.formatter.format(pairs)
240
+ @formatter.join(pairs)
186
241
  end
187
242
  end
243
+
244
+ @handlers = rebuild_handlers
245
+ self.tags_formatter = :legacy
188
246
  end
189
247
  end
@@ -2,40 +2,29 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module QueryLogs
5
- class LegacyFormatter # :nodoc:
6
- def initialize
7
- @key_value_separator = ":"
8
- end
9
-
10
- # Formats the key value pairs into a string.
11
- def format(pairs)
12
- pairs.map! do |key, value|
13
- "#{key}#{key_value_separator}#{format_value(value)}"
14
- end.join(",")
15
- end
16
-
17
- private
18
- attr_reader :key_value_separator
19
-
20
- def format_value(value)
21
- value
5
+ module LegacyFormatter # :nodoc:
6
+ class << self
7
+ # Formats the key value pairs into a string.
8
+ def format(key, value)
9
+ "#{key}:#{value}"
22
10
  end
23
- end
24
11
 
25
- class SQLCommenter < LegacyFormatter # :nodoc:
26
- def initialize
27
- @key_value_separator = "="
12
+ def join(pairs)
13
+ pairs.join(",")
14
+ end
28
15
  end
16
+ end
29
17
 
30
- def format(pairs)
31
- pairs.sort_by! { |pair| pair.first.to_s }
32
- super
33
- end
18
+ class SQLCommenter # :nodoc:
19
+ class << self
20
+ def format(key, value)
21
+ "#{key}='#{ERB::Util.url_encode(value)}'"
22
+ end
34
23
 
35
- private
36
- def format_value(value)
37
- "'#{ERB::Util.url_encode(value)}'"
24
+ def join(pairs)
25
+ pairs.join(",")
38
26
  end
27
+ end
39
28
  end
40
29
  end
41
30
  end
@@ -56,12 +56,10 @@ module ActiveRecord
56
56
  end
57
57
 
58
58
  # Same as <tt>#find_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
59
- def async_find_by_sql(sql, binds = [], preparable: nil, &block)
60
- result = with_connection do |c|
61
- _query_by_sql(c, sql, binds, preparable: preparable, async: true)
62
- end
63
-
64
- result.then do |result|
59
+ def async_find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
60
+ with_connection do |c|
61
+ _query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry, async: true)
62
+ end.then do |result|
65
63
  _load_from_sql(result, &block)
66
64
  end
67
65
  end
@@ -71,6 +69,8 @@ module ActiveRecord
71
69
  end
72
70
 
73
71
  def _load_from_sql(result_set, &block) # :nodoc:
72
+ return [] if result_set.empty?
73
+
74
74
  column_types = result_set.column_types
75
75
 
76
76
  unless column_types.empty?
@@ -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 if Rails.env.production?
72
73
  end
73
74
 
74
75
  runner do
@@ -356,16 +357,19 @@ To keep using the current cache store, you can turn off cache versioning entirel
356
357
  end
357
358
 
358
359
  initializer "active_record_encryption.configuration" do |app|
359
- ActiveSupport.on_load(:active_record) do
360
- ActiveRecord::Encryption.configure \
360
+ ActiveSupport.on_load(:active_record_encryption) do
361
+ ActiveRecord::Encryption.configure(
361
362
  primary_key: app.credentials.dig(:active_record_encryption, :primary_key),
362
363
  deterministic_key: app.credentials.dig(:active_record_encryption, :deterministic_key),
363
364
  key_derivation_salt: app.credentials.dig(:active_record_encryption, :key_derivation_salt),
364
365
  **app.config.active_record.encryption
366
+ )
365
367
 
366
368
  auto_filtered_parameters = ActiveRecord::Encryption::AutoFilteredParameters.new(app)
367
369
  auto_filtered_parameters.enable if ActiveRecord::Encryption.config.add_to_filter_parameters
370
+ end
368
371
 
372
+ ActiveSupport.on_load(:active_record) do
369
373
  # Support extended queries for deterministic attributes and validations
370
374
  if ActiveRecord::Encryption.config.extend_queries
371
375
  ActiveRecord::Encryption::ExtendedDeterministicQueries.install_support
@@ -385,7 +389,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
385
389
  config.after_initialize do
386
390
  if app.config.active_record.query_log_tags_enabled
387
391
  ActiveRecord.query_transformers << ActiveRecord::QueryLogs
388
- ActiveRecord::QueryLogs.taggings.merge!(
392
+ ActiveRecord::QueryLogs.taggings = ActiveRecord::QueryLogs.taggings.merge(
389
393
  application: Rails.application.class.name.split("::").first,
390
394
  pid: -> { Process.pid.to_s },
391
395
  socket: ->(context) { context[:connection].pool.db_config.socket },
@@ -400,7 +404,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
400
404
  end
401
405
 
402
406
  if app.config.active_record.query_log_tags_format
403
- ActiveRecord::QueryLogs.update_formatter(app.config.active_record.query_log_tags_format)
407
+ ActiveRecord::QueryLogs.tags_formatter = app.config.active_record.query_log_tags_format
404
408
  end
405
409
 
406
410
  if app.config.active_record.cache_query_log_tags
@@ -432,15 +436,5 @@ To keep using the current cache store, you can turn off cache versioning entirel
432
436
  end
433
437
  end
434
438
  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
439
  end
446
440
  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
 
@@ -562,12 +562,12 @@ module ActiveRecord
562
562
  def foreign_key(infer_from_inverse_of: true)
563
563
  @foreign_key ||= if options[:foreign_key]
564
564
  if options[:foreign_key].is_a?(Array)
565
- options[:foreign_key].map { |fk| fk.to_s.freeze }.freeze
565
+ options[:foreign_key].map { |fk| -fk.to_s.freeze }.freeze
566
566
  else
567
567
  options[:foreign_key].to_s.freeze
568
568
  end
569
569
  elsif options[:query_constraints]
570
- options[:query_constraints].map { |fk| fk.to_s.freeze }.freeze
570
+ options[:query_constraints].map { |fk| -fk.to_s.freeze }.freeze
571
571
  else
572
572
  derived_fk = derive_foreign_key(infer_from_inverse_of: infer_from_inverse_of)
573
573
 
@@ -575,7 +575,12 @@ module ActiveRecord
575
575
  derived_fk = derive_fk_query_constraints(derived_fk)
576
576
  end
577
577
 
578
- derived_fk
578
+ if derived_fk.is_a?(Array)
579
+ derived_fk.map! { |fk| -fk.freeze }
580
+ derived_fk.freeze
581
+ else
582
+ -derived_fk.freeze
583
+ end
579
584
  end
580
585
  end
581
586