activerecord 7.2.3 → 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.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +192 -1261
  3. data/README.rdoc +2 -2
  4. data/lib/active_record/associations/alias_tracker.rb +4 -6
  5. data/lib/active_record/associations/association.rb +25 -5
  6. data/lib/active_record/associations/belongs_to_association.rb +2 -18
  7. data/lib/active_record/associations/builder/association.rb +7 -6
  8. data/lib/active_record/associations/collection_association.rb +4 -4
  9. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  10. data/lib/active_record/associations/has_many_through_association.rb +4 -9
  11. data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
  12. data/lib/active_record/associations/preloader/association.rb +2 -2
  13. data/lib/active_record/associations/singular_association.rb +8 -3
  14. data/lib/active_record/associations.rb +50 -32
  15. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  16. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  17. data/lib/active_record/attribute_methods.rb +19 -24
  18. data/lib/active_record/attributes.rb +26 -37
  19. data/lib/active_record/autosave_association.rb +81 -49
  20. data/lib/active_record/base.rb +2 -2
  21. data/lib/active_record/callbacks.rb +1 -1
  22. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
  23. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  24. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
  25. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +31 -75
  26. data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
  27. data/lib/active_record/connection_adapters/abstract/query_cache.rb +14 -19
  28. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  29. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +2 -6
  30. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +27 -9
  31. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
  32. data/lib/active_record/connection_adapters/abstract_adapter.rb +27 -57
  33. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +28 -58
  34. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -15
  35. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  36. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -45
  37. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
  38. data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -16
  39. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
  40. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  41. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +0 -1
  42. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +12 -14
  43. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
  44. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +51 -9
  45. data/lib/active_record/connection_adapters/postgresql_adapter.rb +44 -101
  46. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  47. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
  48. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -13
  49. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  50. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
  51. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -2
  52. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +60 -22
  53. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
  54. data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -18
  55. data/lib/active_record/connection_handling.rb +29 -11
  56. data/lib/active_record/core.rb +15 -60
  57. data/lib/active_record/counter_cache.rb +1 -1
  58. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -3
  59. data/lib/active_record/delegated_type.rb +18 -18
  60. data/lib/active_record/encryption/config.rb +3 -1
  61. data/lib/active_record/encryption/encryptable_record.rb +5 -5
  62. data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
  63. data/lib/active_record/encryption/encryptor.rb +35 -29
  64. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  65. data/lib/active_record/encryption/scheme.rb +8 -1
  66. data/lib/active_record/enum.rb +12 -13
  67. data/lib/active_record/errors.rb +16 -8
  68. data/lib/active_record/fixture_set/table_row.rb +2 -19
  69. data/lib/active_record/fixtures.rb +0 -1
  70. data/lib/active_record/future_result.rb +14 -10
  71. data/lib/active_record/gem_version.rb +4 -4
  72. data/lib/active_record/insert_all.rb +1 -1
  73. data/lib/active_record/marshalling.rb +1 -4
  74. data/lib/active_record/migration/command_recorder.rb +22 -5
  75. data/lib/active_record/migration/compatibility.rb +5 -2
  76. data/lib/active_record/migration.rb +36 -35
  77. data/lib/active_record/model_schema.rb +1 -1
  78. data/lib/active_record/nested_attributes.rb +4 -6
  79. data/lib/active_record/persistence.rb +128 -130
  80. data/lib/active_record/query_cache.rb +5 -4
  81. data/lib/active_record/query_logs.rb +98 -44
  82. data/lib/active_record/query_logs_formatter.rb +17 -28
  83. data/lib/active_record/querying.rb +10 -10
  84. data/lib/active_record/railtie.rb +5 -6
  85. data/lib/active_record/railties/databases.rake +1 -2
  86. data/lib/active_record/reflection.rb +9 -7
  87. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  88. data/lib/active_record/relation/batches.rb +132 -72
  89. data/lib/active_record/relation/calculations.rb +55 -55
  90. data/lib/active_record/relation/delegation.rb +25 -14
  91. data/lib/active_record/relation/finder_methods.rb +31 -32
  92. data/lib/active_record/relation/merger.rb +8 -8
  93. data/lib/active_record/relation/predicate_builder/association_query_value.rb +0 -2
  94. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  95. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  96. data/lib/active_record/relation/predicate_builder.rb +5 -0
  97. data/lib/active_record/relation/query_attribute.rb +1 -1
  98. data/lib/active_record/relation/query_methods.rb +90 -91
  99. data/lib/active_record/relation/record_fetch_warning.rb +2 -2
  100. data/lib/active_record/relation/spawn_methods.rb +1 -1
  101. data/lib/active_record/relation/where_clause.rb +2 -8
  102. data/lib/active_record/relation.rb +77 -76
  103. data/lib/active_record/result.rb +68 -7
  104. data/lib/active_record/sanitization.rb +7 -6
  105. data/lib/active_record/schema_dumper.rb +16 -29
  106. data/lib/active_record/schema_migration.rb +2 -1
  107. data/lib/active_record/scoping/named.rb +5 -2
  108. data/lib/active_record/secure_token.rb +3 -3
  109. data/lib/active_record/signed_id.rb +6 -7
  110. data/lib/active_record/statement_cache.rb +12 -12
  111. data/lib/active_record/store.rb +7 -3
  112. data/lib/active_record/tasks/database_tasks.rb +24 -15
  113. data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
  114. data/lib/active_record/tasks/postgresql_database_tasks.rb +0 -7
  115. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
  116. data/lib/active_record/test_fixtures.rb +12 -0
  117. data/lib/active_record/testing/query_assertions.rb +2 -2
  118. data/lib/active_record/token_for.rb +1 -1
  119. data/lib/active_record/transactions.rb +1 -3
  120. data/lib/active_record/validations/uniqueness.rb +8 -8
  121. data/lib/active_record.rb +16 -1
  122. data/lib/arel/collectors/bind.rb +1 -1
  123. data/lib/arel/crud.rb +0 -2
  124. data/lib/arel/delete_manager.rb +0 -5
  125. data/lib/arel/nodes/delete_statement.rb +2 -4
  126. data/lib/arel/nodes/update_statement.rb +2 -4
  127. data/lib/arel/select_manager.rb +2 -6
  128. data/lib/arel/update_manager.rb +0 -5
  129. data/lib/arel/visitors/dot.rb +0 -2
  130. data/lib/arel/visitors/sqlite.rb +0 -25
  131. data/lib/arel/visitors/to_sql.rb +1 -3
  132. metadata +14 -11
@@ -28,10 +28,6 @@ module ActiveRecord
28
28
  # * +database+
29
29
  # * +source_location+
30
30
  #
31
- # WARNING: Calculating the +source_location+ of a query can be slow, so you should consider its impact if using it in a production environment.
32
- #
33
- # Also see {config.active_record.verbose_query_logs}[https://guides.rubyonrails.org/debugging_rails_applications.html#verbose-query-logs].
34
- #
35
31
  # Action Controller adds default tags when loaded:
36
32
  #
37
33
  # * +controller+
@@ -76,14 +72,70 @@ module ActiveRecord
76
72
  #
77
73
  # config.active_record.cache_query_log_tags = true
78
74
  module QueryLogs
79
- mattr_accessor :taggings, instance_accessor: false, default: {}
80
- mattr_accessor :tags, instance_accessor: false, default: [ :application ]
81
- mattr_accessor :prepend_comment, instance_accessor: false, default: false
82
- mattr_accessor :cache_query_log_tags, instance_accessor: false, default: false
83
- 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
+
84
111
  thread_mattr_accessor :cached_comment, instance_accessor: false
85
112
 
86
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
+
87
139
  def call(sql, connection) # :nodoc:
88
140
  comment = self.comment(connection)
89
141
 
@@ -100,23 +152,10 @@ module ActiveRecord
100
152
  self.cached_comment = nil
101
153
  end
102
154
 
103
- # Updates the formatter to be what the passed in format is.
104
- def update_formatter(format)
105
- self.tags_formatter =
106
- case format
107
- when :legacy
108
- LegacyFormatter.new
109
- when :sqlcommenter
110
- SQLCommenter.new
111
- else
112
- raise ArgumentError, "Formatter is unsupported: #{formatter}"
113
- end
114
- end
115
-
116
155
  if Thread.respond_to?(:each_caller_location)
117
156
  def query_source_location # :nodoc:
118
157
  Thread.each_caller_location do |location|
119
- frame = LogSubscriber.backtrace_cleaner.clean_frame(location)
158
+ frame = LogSubscriber.backtrace_cleaner.clean_frame(location.path)
120
159
  return frame if frame
121
160
  end
122
161
  nil
@@ -130,6 +169,35 @@ module ActiveRecord
130
169
  ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
131
170
 
132
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
+
133
201
  # Returns an SQL comment +String+ containing the query log tags.
134
202
  # Sets and returns a cached comment if <tt>cache_query_log_tags</tt> is +true+.
135
203
  def comment(connection)
@@ -140,10 +208,6 @@ module ActiveRecord
140
208
  end
141
209
  end
142
210
 
143
- def formatter
144
- self.tags_formatter || self.update_formatter(:legacy)
145
- end
146
-
147
211
  def uncached_comment(connection)
148
212
  content = tag_content(connection)
149
213
 
@@ -169,25 +233,15 @@ module ActiveRecord
169
233
  context = ActiveSupport::ExecutionContext.to_h
170
234
  context[:connection] ||= connection
171
235
 
172
- pairs = tags.flat_map { |i| [*i] }.filter_map do |tag|
173
- key, handler = tag
174
- handler ||= taggings[key]
175
-
176
- val = if handler.nil?
177
- context[key]
178
- elsif handler.respond_to?(:call)
179
- if handler.arity == 0
180
- handler.call
181
- else
182
- handler.call(context)
183
- end
184
- else
185
- handler
186
- end
187
- [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?
188
239
  end
189
- self.formatter.format(pairs)
240
+ @formatter.join(pairs)
190
241
  end
191
242
  end
243
+
244
+ @handlers = rebuild_handlers
245
+ self.tags_formatter = :legacy
192
246
  end
193
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
@@ -46,8 +46,8 @@ module ActiveRecord
46
46
  # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
47
47
  # Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
48
48
  #
49
- # Note that building your own SQL query string from user input {may expose your application to
50
- # injection attacks}[https://guides.rubyonrails.org/security.html#sql-injection].
49
+ # Note that building your own SQL query string from user input may expose your application to
50
+ # injection attacks (https://guides.rubyonrails.org/security.html#sql-injection).
51
51
  def find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
52
52
  result = with_connection do |c|
53
53
  _query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry)
@@ -55,13 +55,11 @@ module ActiveRecord
55
55
  _load_from_sql(result, &block)
56
56
  end
57
57
 
58
- # Same as #find_by_sql 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|
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, 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?
@@ -112,7 +112,7 @@ module ActiveRecord
112
112
  end
113
113
  end
114
114
 
115
- # Same as #count_by_sql but perform the query asynchronously and returns an ActiveRecord::Promise.
115
+ # Same as <tt>#count_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
116
116
  def async_count_by_sql(sql)
117
117
  with_connection do |c|
118
118
  c.select_value(sanitize_sql(sql), "#{name} Count", async: true).then(&:to_i)
@@ -23,8 +23,8 @@ module ActiveRecord
23
23
  config.action_dispatch.rescue_responses.merge!(
24
24
  "ActiveRecord::RecordNotFound" => :not_found,
25
25
  "ActiveRecord::StaleObjectError" => :conflict,
26
- "ActiveRecord::RecordInvalid" => ActionDispatch::Constants::UNPROCESSABLE_CONTENT,
27
- "ActiveRecord::RecordNotSaved" => ActionDispatch::Constants::UNPROCESSABLE_CONTENT
26
+ "ActiveRecord::RecordInvalid" => :unprocessable_entity,
27
+ "ActiveRecord::RecordNotSaved" => :unprocessable_entity
28
28
  )
29
29
 
30
30
  config.active_record.use_schema_cache_dump = true
@@ -69,7 +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
+ ActiveRecord::Base.attributes_for_inspect = :all if Rails.env.production?
73
73
  end
74
74
 
75
75
  runner do
@@ -312,7 +312,6 @@ To keep using the current cache store, you can turn off cache versioning entirel
312
312
  initializer "active_record.set_executor_hooks" do
313
313
  ActiveRecord::QueryCache.install_executor_hooks
314
314
  ActiveRecord::AsynchronousQueriesTracker.install_executor_hooks
315
- ActiveRecord::ConnectionAdapters::ConnectionPool.install_executor_hooks
316
315
  end
317
316
 
318
317
  initializer "active_record.add_watchable_files" do |app|
@@ -390,7 +389,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
390
389
  config.after_initialize do
391
390
  if app.config.active_record.query_log_tags_enabled
392
391
  ActiveRecord.query_transformers << ActiveRecord::QueryLogs
393
- ActiveRecord::QueryLogs.taggings.merge!(
392
+ ActiveRecord::QueryLogs.taggings = ActiveRecord::QueryLogs.taggings.merge(
394
393
  application: Rails.application.class.name.split("::").first,
395
394
  pid: -> { Process.pid.to_s },
396
395
  socket: ->(context) { context[:connection].pool.db_config.socket },
@@ -405,7 +404,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
405
404
  end
406
405
 
407
406
  if app.config.active_record.query_log_tags_format
408
- 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
409
408
  end
410
409
 
411
410
  if app.config.active_record.cache_query_log_tags
@@ -473,8 +473,7 @@ db_namespace = namespace :db do
473
473
 
474
474
  desc "Load a database schema file (either db/schema.rb or db/structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`) into the database"
475
475
  task load: [:load_config, :check_protected_environments] do
476
- schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
477
- ActiveRecord::Tasks::DatabaseTasks.load_schema_current(schema_format, ENV["SCHEMA"])
476
+ ActiveRecord::Tasks::DatabaseTasks.load_schema_current(ActiveRecord.schema_format, ENV["SCHEMA"])
478
477
  end
479
478
 
480
479
  namespace :dump do
@@ -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
 
@@ -1239,10 +1244,7 @@ module ActiveRecord
1239
1244
  end
1240
1245
 
1241
1246
  def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
1242
- scopes = super
1243
- unless @previous_reflection.through_reflection?
1244
- scopes += @previous_reflection.join_scopes(table, predicate_builder, klass, record)
1245
- end
1247
+ scopes = @previous_reflection.join_scopes(table, predicate_builder, klass, record) + super
1246
1248
  scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
1247
1249
  end
1248
1250
 
@@ -5,11 +5,12 @@ module ActiveRecord
5
5
  class BatchEnumerator
6
6
  include Enumerable
7
7
 
8
- def initialize(of: 1000, start: nil, finish: nil, relation:, order: :asc, use_ranges: nil) # :nodoc:
8
+ def initialize(of: 1000, start: nil, finish: nil, relation:, cursor:, order: :asc, use_ranges: nil) # :nodoc:
9
9
  @of = of
10
10
  @relation = relation
11
11
  @start = start
12
12
  @finish = finish
13
+ @cursor = cursor
13
14
  @order = order
14
15
  @use_ranges = use_ranges
15
16
  end
@@ -52,7 +53,7 @@ module ActiveRecord
52
53
  def each_record(&block)
53
54
  return to_enum(:each_record) unless block_given?
54
55
 
55
- @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true, order: @order).each do |relation|
56
+ @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true, cursor: @cursor, order: @order).each do |relation|
56
57
  relation.records.each(&block)
57
58
  end
58
59
  end
@@ -105,7 +106,7 @@ module ActiveRecord
105
106
  # relation.update_all(awesome: true)
106
107
  # end
107
108
  def each(&block)
108
- enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false, order: @order, use_ranges: @use_ranges)
109
+ enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false, cursor: @cursor, order: @order, use_ranges: @use_ranges)
109
110
  return enum.each(&block) if block_given?
110
111
  enum
111
112
  end