activerecord 8.0.0.beta1 → 8.0.0.rc2

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +102 -2
  3. data/lib/active_record/association_relation.rb +1 -0
  4. data/lib/active_record/associations/association.rb +9 -5
  5. data/lib/active_record/associations/has_many_through_association.rb +7 -1
  6. data/lib/active_record/associations.rb +28 -16
  7. data/lib/active_record/attribute_methods/primary_key.rb +2 -7
  8. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
  9. data/lib/active_record/callbacks.rb +1 -1
  10. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +26 -8
  11. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  12. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +6 -1
  13. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +8 -2
  14. data/lib/active_record/connection_adapters/abstract_adapter.rb +0 -1
  15. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +8 -4
  16. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  17. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  18. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -10
  19. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +6 -12
  20. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +2 -1
  21. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +11 -10
  22. data/lib/active_record/connection_adapters/postgresql_adapter.rb +12 -10
  23. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +1 -1
  24. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +0 -2
  25. data/lib/active_record/connection_adapters.rb +0 -56
  26. data/lib/active_record/core.rb +43 -14
  27. data/lib/active_record/database_configurations/database_config.rb +4 -0
  28. data/lib/active_record/database_configurations/hash_config.rb +8 -0
  29. data/lib/active_record/enum.rb +9 -22
  30. data/lib/active_record/fixtures.rb +0 -1
  31. data/lib/active_record/gem_version.rb +1 -1
  32. data/lib/active_record/locking/optimistic.rb +1 -1
  33. data/lib/active_record/log_subscriber.rb +5 -11
  34. data/lib/active_record/marshalling.rb +4 -1
  35. data/lib/active_record/migration/command_recorder.rb +5 -5
  36. data/lib/active_record/migration.rb +0 -5
  37. data/lib/active_record/model_schema.rb +2 -3
  38. data/lib/active_record/query_cache.rb +0 -4
  39. data/lib/active_record/query_logs.rb +5 -11
  40. data/lib/active_record/querying.rb +2 -2
  41. data/lib/active_record/railtie.rb +2 -25
  42. data/lib/active_record/railties/databases.rake +2 -17
  43. data/lib/active_record/reflection.rb +14 -19
  44. data/lib/active_record/relation/calculations.rb +24 -28
  45. data/lib/active_record/relation/predicate_builder.rb +8 -0
  46. data/lib/active_record/relation/query_methods.rb +76 -38
  47. data/lib/active_record/relation.rb +8 -1
  48. data/lib/active_record/result.rb +10 -9
  49. data/lib/active_record/table_metadata.rb +1 -3
  50. data/lib/active_record/tasks/database_tasks.rb +26 -34
  51. data/lib/active_record/testing/query_assertions.rb +2 -2
  52. data/lib/active_record.rb +0 -45
  53. data/lib/arel/table.rb +3 -7
  54. data/lib/arel/visitors/sqlite.rb +25 -0
  55. metadata +9 -10
  56. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -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 if Rails.env.production?
72
+ ActiveRecord::Base.attributes_for_inspect = :all
73
73
  end
74
74
 
75
75
  runner do
@@ -184,30 +184,6 @@ To keep using the current cache store, you can turn off cache versioning entirel
184
184
  end
185
185
  end
186
186
 
187
- initializer "active_record.warn_on_records_fetched_greater_than" do
188
- if config.active_record.warn_on_records_fetched_greater_than
189
- ActiveRecord.deprecator.warn <<~MSG.squish
190
- `config.active_record.warn_on_records_fetched_greater_than` is deprecated and will be
191
- removed in Rails 8.0.
192
- Please subscribe to `sql.active_record` notifications and access the row count field to
193
- detect large result set sizes.
194
- MSG
195
- ActiveSupport.on_load(:active_record) do
196
- require "active_record/relation/record_fetch_warning"
197
- end
198
- end
199
- end
200
-
201
- initializer "active_record.sqlite3_deprecated_warning" do
202
- if config.active_record.key?(:sqlite3_production_warning)
203
- config.active_record.delete(:sqlite3_production_warning)
204
- ActiveRecord.deprecator.warn <<~MSG.squish
205
- The `config.active_record.sqlite3_production_warning` configuration no longer has any effect
206
- and can be safely removed.
207
- MSG
208
- end
209
- end
210
-
211
187
  initializer "active_record.sqlite3_adapter_strict_strings_by_default" do
212
188
  config.after_initialize do
213
189
  if config.active_record.sqlite3_adapter_strict_strings_by_default
@@ -312,6 +288,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
312
288
  initializer "active_record.set_executor_hooks" do
313
289
  ActiveRecord::QueryCache.install_executor_hooks
314
290
  ActiveRecord::AsynchronousQueriesTracker.install_executor_hooks
291
+ ActiveRecord::ConnectionAdapters::ConnectionPool.install_executor_hooks
315
292
  end
316
293
 
317
294
  initializer "active_record.add_watchable_files" do |app|
@@ -87,22 +87,7 @@ db_namespace = namespace :db do
87
87
 
88
88
  desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
89
89
  task migrate: :load_config do
90
- db_configs = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env)
91
-
92
- if db_configs.size == 1 && db_configs.first.primary?
93
- ActiveRecord::Tasks::DatabaseTasks.migrate
94
- else
95
- mapped_versions = ActiveRecord::Tasks::DatabaseTasks.db_configs_with_versions
96
-
97
- mapped_versions.sort.each do |version, db_configs|
98
- db_configs.each do |db_config|
99
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection(db_config) do
100
- ActiveRecord::Tasks::DatabaseTasks.migrate(version)
101
- end
102
- end
103
- end
104
- end
105
-
90
+ ActiveRecord::Tasks::DatabaseTasks.migrate_all
106
91
  db_namespace["_dump"].invoke
107
92
  end
108
93
 
@@ -176,7 +161,7 @@ db_namespace = namespace :db do
176
161
  end
177
162
 
178
163
  # desc 'Resets your database using your migrations for the current environment'
179
- task reset: ["db:drop", "db:create", "db:migrate"]
164
+ task reset: ["db:drop", "db:create", "db:schema:dump", "db:migrate"]
180
165
 
181
166
  desc 'Run the "up" for a given migration VERSION.'
182
167
  task up: :load_config do
@@ -198,7 +198,7 @@ module ActiveRecord
198
198
  end
199
199
 
200
200
  def join_scope(table, foreign_table, foreign_klass)
201
- predicate_builder = predicate_builder(table)
201
+ predicate_builder = klass.predicate_builder.with(TableMetadata.new(klass, table))
202
202
  scope_chain_items = join_scopes(table, predicate_builder)
203
203
  klass_scope = klass_join_scope(table, predicate_builder)
204
204
 
@@ -224,7 +224,7 @@ module ActiveRecord
224
224
  klass_scope
225
225
  end
226
226
 
227
- def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
227
+ def join_scopes(table, predicate_builder = nil, klass = self.klass, record = nil) # :nodoc:
228
228
  if scope
229
229
  [scope_for(build_scope(table, predicate_builder, klass), record)]
230
230
  else
@@ -232,7 +232,7 @@ module ActiveRecord
232
232
  end
233
233
  end
234
234
 
235
- def klass_join_scope(table, predicate_builder) # :nodoc:
235
+ def klass_join_scope(table, predicate_builder = nil) # :nodoc:
236
236
  relation = build_scope(table, predicate_builder)
237
237
  klass.scope_for_association(relation)
238
238
  end
@@ -333,12 +333,8 @@ module ActiveRecord
333
333
  collect_join_chain
334
334
  end
335
335
 
336
- def build_scope(table, predicate_builder = predicate_builder(table), klass = self.klass)
337
- Relation.create(
338
- klass,
339
- table: table,
340
- predicate_builder: predicate_builder
341
- )
336
+ def build_scope(table, predicate_builder = nil, klass = self.klass)
337
+ Relation.create(klass, table:, predicate_builder:)
342
338
  end
343
339
 
344
340
  def strict_loading?
@@ -357,10 +353,6 @@ module ActiveRecord
357
353
  end
358
354
 
359
355
  private
360
- def predicate_builder(table)
361
- PredicateBuilder.new(TableMetadata.new(klass, table))
362
- end
363
-
364
356
  def primary_key(klass)
365
357
  klass.primary_key || raise(UnknownPrimaryKey.new(klass))
366
358
  end
@@ -531,9 +523,9 @@ module ActiveRecord
531
523
  @association_foreign_key = nil
532
524
  @association_primary_key = nil
533
525
  if options[:query_constraints]
534
- ActiveRecord.deprecator.warn <<~MSG.squish
535
- Setting `query_constraints:` option on `#{active_record}.#{macro} :#{name}` is deprecated.
536
- To maintain current behavior, use the `foreign_key` option instead.
526
+ raise ConfigurationError, <<~MSG.squish
527
+ Setting `query_constraints:` option on `#{active_record}.#{macro} :#{name}` is not allowed.
528
+ To get the same behavior, use the `foreign_key` option instead.
537
529
  MSG
538
530
  end
539
531
 
@@ -1070,7 +1062,7 @@ module ActiveRecord
1070
1062
  source_reflection.scopes + super
1071
1063
  end
1072
1064
 
1073
- def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
1065
+ def join_scopes(table, predicate_builder = nil, klass = self.klass, record = nil) # :nodoc:
1074
1066
  source_reflection.join_scopes(table, predicate_builder, klass, record) + super
1075
1067
  end
1076
1068
 
@@ -1243,8 +1235,11 @@ module ActiveRecord
1243
1235
  @previous_reflection = previous_reflection
1244
1236
  end
1245
1237
 
1246
- def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
1247
- scopes = @previous_reflection.join_scopes(table, predicate_builder, klass, record) + super
1238
+ def join_scopes(table, predicate_builder = nil, klass = self.klass, record = nil) # :nodoc:
1239
+ scopes = super
1240
+ unless @previous_reflection.through_reflection?
1241
+ scopes += @previous_reflection.join_scopes(table, predicate_builder, klass, record)
1242
+ end
1248
1243
  scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
1249
1244
  end
1250
1245
 
@@ -275,10 +275,14 @@ module ActiveRecord
275
275
  # # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
276
276
  # # => [2, 3]
277
277
  #
278
- # Comment.joins(:person).pluck(:id, person: [:id])
279
- # # SELECT comments.id, people.id FROM comments INNER JOIN people on comments.person_id = people.id
278
+ # Comment.joins(:person).pluck(:id, person: :id)
279
+ # # SELECT comments.id, person.id FROM comments INNER JOIN people person ON person.id = comments.person_id
280
280
  # # => [[1, 2], [2, 2]]
281
281
  #
282
+ # Comment.joins(:person).pluck(:id, person: [:id, :name])
283
+ # # SELECT comments.id, person.id, person.name FROM comments INNER JOIN people person ON person.id = comments.person_id
284
+ # # => [[1, 2, 'David'], [2, 2, 'David']]
285
+ #
282
286
  # Person.pluck(Arel.sql('DATEDIFF(updated_at, created_at)'))
283
287
  # # SELECT DATEDIFF(updated_at, created_at) FROM people
284
288
  # # => ['0', '27761', '173']
@@ -307,8 +311,8 @@ module ActiveRecord
307
311
  relation.pluck(*column_names)
308
312
  else
309
313
  model.disallow_raw_sql!(flattened_args(column_names))
310
- columns = arel_columns(column_names)
311
314
  relation = spawn
315
+ columns = relation.arel_columns(column_names)
312
316
  relation.select_values = columns
313
317
  result = skip_query_cache_if_necessary do
314
318
  if where_clause.contradiction?
@@ -447,10 +451,13 @@ module ActiveRecord
447
451
  end
448
452
 
449
453
  def aggregate_column(column_name)
450
- return column_name if Arel::Expressions === column_name
451
-
452
- arel_column(column_name.to_s) do |name|
453
- column_name == :all ? Arel.sql("*", retryable: true) : Arel.sql(name)
454
+ case column_name
455
+ when Arel::Expressions
456
+ column_name
457
+ when :all
458
+ Arel.star
459
+ else
460
+ arel_column(column_name)
454
461
  end
455
462
  end
456
463
 
@@ -630,27 +637,12 @@ module ActiveRecord
630
637
  end
631
638
 
632
639
  def select_for_count
633
- if select_values.present?
634
- return select_values.first if select_values.one?
635
-
636
- adapter_class = model.adapter_class
637
- select_values.map do |field|
638
- column = if Arel.arel_node?(field)
639
- field
640
- else
641
- arel_column(field.to_s) do |attr_name|
642
- Arel.sql(attr_name)
643
- end
644
- end
645
-
646
- if column.is_a?(Arel::Nodes::SqlLiteral)
647
- column
648
- else
649
- "#{adapter_class.quote_table_name(column.relation.name)}.#{adapter_class.quote_column_name(column.name)}"
650
- end
651
- end.join(", ")
652
- else
640
+ if select_values.empty?
653
641
  :all
642
+ else
643
+ with_connection do |conn|
644
+ arel_columns(select_values).map { |column| conn.visitor.compile(column) }.join(", ")
645
+ end
654
646
  end
655
647
  end
656
648
 
@@ -673,7 +665,11 @@ module ActiveRecord
673
665
  subquery_alias = Arel.sql("subquery_for_count", retryable: true)
674
666
  select_value = operation_over_aggregate_column(column_alias, "count", false)
675
667
 
676
- relation.build_subquery(subquery_alias, select_value)
668
+ if column_name == :all
669
+ relation.unscope(:order).build_subquery(subquery_alias, select_value)
670
+ else
671
+ relation.build_subquery(subquery_alias, select_value)
672
+ end
677
673
  end
678
674
  end
679
675
  end
@@ -72,7 +72,15 @@ module ActiveRecord
72
72
  table.associated_table(table_name, &block).arel_table[column_name]
73
73
  end
74
74
 
75
+ def with(table)
76
+ other = dup
77
+ other.table = table
78
+ other
79
+ end
80
+
75
81
  protected
82
+ attr_writer :table
83
+
76
84
  def expand_from_hash(attributes, &block)
77
85
  return ["1=0"] if attributes.empty?
78
86
 
@@ -498,7 +498,8 @@ module ActiveRecord
498
498
 
499
499
  # Like #with, but modifies relation in place.
500
500
  def with!(*args) # :nodoc:
501
- self.with_values += args
501
+ args = process_with_args(args)
502
+ self.with_values |= args
502
503
  self
503
504
  end
504
505
 
@@ -506,7 +507,7 @@ module ActiveRecord
506
507
  #
507
508
  # Post.with_recursive(post_and_replies: [Post.where(id: 42), Post.joins('JOIN post_and_replies ON posts.in_reply_to_id = post_and_replies.id')])
508
509
  # # => ActiveRecord::Relation
509
- # # WITH post_and_replies AS (
510
+ # # WITH RECURSIVE post_and_replies AS (
510
511
  # # (SELECT * FROM posts WHERE id = 42)
511
512
  # # UNION ALL
512
513
  # # (SELECT * FROM posts JOIN posts_and_replies ON posts.in_reply_to_id = posts_and_replies.id)
@@ -521,7 +522,8 @@ module ActiveRecord
521
522
 
522
523
  # Like #with_recursive but modifies the relation in place.
523
524
  def with_recursive!(*args) # :nodoc:
524
- self.with_values += args
525
+ args = process_with_args(args)
526
+ self.with_values |= args
525
527
  @with_is_recursive = true
526
528
  self
527
529
  end
@@ -1656,6 +1658,22 @@ module ActiveRecord
1656
1658
  self
1657
1659
  end
1658
1660
 
1661
+ protected
1662
+ def arel_columns(columns)
1663
+ columns.flat_map do |field|
1664
+ case field
1665
+ when Symbol, String
1666
+ arel_column(field)
1667
+ when Proc
1668
+ field.call
1669
+ when Hash
1670
+ arel_columns_from_hash(field)
1671
+ else
1672
+ field
1673
+ end
1674
+ end
1675
+ end
1676
+
1659
1677
  private
1660
1678
  def async
1661
1679
  spawn.async!
@@ -1910,12 +1928,26 @@ module ActiveRecord
1910
1928
  end
1911
1929
  end
1912
1930
 
1913
- def build_with_expression_from_value(value)
1931
+ def build_with_expression_from_value(value, nested = false)
1914
1932
  case value
1915
1933
  when Arel::Nodes::SqlLiteral then Arel::Nodes::Grouping.new(value)
1916
- when ActiveRecord::Relation then value.arel
1934
+ when ActiveRecord::Relation
1935
+ if nested
1936
+ value.arel.ast
1937
+ else
1938
+ value.arel
1939
+ end
1917
1940
  when Arel::SelectManager then value
1918
- when Array then value.map { |q| build_with_expression_from_value(q) }.reduce { |result, value| result.union(:all, value) }
1941
+ when Array
1942
+ return build_with_expression_from_value(value.first, false) if value.size == 1
1943
+
1944
+ parts = value.map do |query|
1945
+ build_with_expression_from_value(query, true)
1946
+ end
1947
+
1948
+ parts.reduce do |result, value|
1949
+ Arel::Nodes::UnionAll.new(result, value)
1950
+ end
1919
1951
  else
1920
1952
  raise ArgumentError, "Unsupported argument type: `#{value}` #{value.class}"
1921
1953
  end
@@ -1929,38 +1961,43 @@ module ActiveRecord
1929
1961
  ).join_sources.first
1930
1962
  end
1931
1963
 
1932
- def arel_columns(columns)
1933
- columns.flat_map do |field|
1934
- case field
1935
- when Symbol
1936
- arel_column(field.to_s) do |attr_name|
1937
- model.adapter_class.quote_table_name(attr_name)
1964
+ def arel_columns_from_hash(fields)
1965
+ fields.flat_map do |table_name, columns|
1966
+ table_name = table_name.name if table_name.is_a?(Symbol)
1967
+ case columns
1968
+ when Symbol, String
1969
+ arel_column_with_table(table_name, columns.to_s)
1970
+ when Array
1971
+ columns.map do |column|
1972
+ arel_column_with_table(table_name, column.to_s)
1938
1973
  end
1939
- when String
1940
- arel_column(field, &:itself)
1941
- when Proc
1942
- field.call
1943
- when Hash
1944
- arel_columns_from_hash(field)
1945
1974
  else
1946
- field
1975
+ raise TypeError, "Expected Symbol, String or Array, got: #{columns.class}"
1947
1976
  end
1948
1977
  end
1949
1978
  end
1950
1979
 
1980
+ def arel_column_with_table(table_name, column_name)
1981
+ self.references_values |= [Arel.sql(table_name, retryable: true)]
1982
+ predicate_builder.resolve_arel_attribute(table_name, column_name) do
1983
+ lookup_table_klass_from_join_dependencies(table_name)
1984
+ end
1985
+ end
1986
+
1951
1987
  def arel_column(field)
1988
+ field = field.name if is_symbol = field.is_a?(Symbol)
1989
+
1952
1990
  field = model.attribute_aliases[field] || field
1953
1991
  from = from_clause.name || from_clause.value
1954
1992
 
1955
1993
  if model.columns_hash.key?(field) && (!from || table_name_matches?(from))
1956
1994
  table[field]
1957
- elsif field.match?(/\A\w+\.\w+\z/)
1958
- table, column = field.split(".")
1959
- predicate_builder.resolve_arel_attribute(table, column) do
1960
- lookup_table_klass_from_join_dependencies(table)
1961
- end
1962
- else
1995
+ elsif /\A(?<table>(?:\w+\.)?\w+)\.(?<column>\w+)\z/ =~ field
1996
+ arel_column_with_table(table, column)
1997
+ elsif block_given?
1963
1998
  yield field
1999
+ else
2000
+ Arel.sql(is_symbol ? model.adapter_class.quote_table_name(field) : field)
1964
2001
  end
1965
2002
  end
1966
2003
 
@@ -2182,38 +2219,39 @@ module ActiveRecord
2182
2219
  def process_select_args(fields)
2183
2220
  fields.flat_map do |field|
2184
2221
  if field.is_a?(Hash)
2185
- arel_columns_from_hash(field)
2222
+ arel_column_aliases_from_hash(field)
2186
2223
  else
2187
2224
  field
2188
2225
  end
2189
2226
  end
2190
2227
  end
2191
2228
 
2192
- def arel_columns_from_hash(fields)
2229
+ def arel_column_aliases_from_hash(fields)
2193
2230
  fields.flat_map do |key, columns_aliases|
2231
+ table_name = key.is_a?(Symbol) ? key.name : key
2194
2232
  case columns_aliases
2195
2233
  when Hash
2196
2234
  columns_aliases.map do |column, column_alias|
2197
- if values[:joins]&.include?(key)
2198
- references = PredicateBuilder.references({ key.to_s => fields[key] })
2199
- self.references_values |= references unless references.empty?
2200
- end
2201
- arel_column("#{key}.#{column}") do
2202
- predicate_builder.resolve_arel_attribute(key.to_s, column)
2203
- end.as(column_alias.to_s)
2235
+ arel_column_with_table(table_name, column.to_s)
2236
+ .as(model.adapter_class.quote_column_name(column_alias.to_s))
2204
2237
  end
2205
2238
  when Array
2206
2239
  columns_aliases.map do |column|
2207
- arel_column("#{key}.#{column}", &:itself)
2240
+ arel_column_with_table(table_name, column.to_s)
2208
2241
  end
2209
2242
  when String, Symbol
2210
- arel_column(key.to_s) do
2211
- predicate_builder.resolve_arel_attribute(model.table_name, key.to_s)
2212
- end.as(columns_aliases.to_s)
2243
+ arel_column(key)
2244
+ .as(model.adapter_class.quote_column_name(columns_aliases.to_s))
2213
2245
  end
2214
2246
  end
2215
2247
  end
2216
2248
 
2249
+ def process_with_args(args)
2250
+ args.flat_map do |arg|
2251
+ arg.map { |k, v| { k => v } }
2252
+ end
2253
+ end
2254
+
2217
2255
  STRUCTURAL_VALUE_METHODS = (
2218
2256
  Relation::VALUE_METHODS -
2219
2257
  [:extending, :where, :having, :unscope, :references, :annotate, :optimizer_hints]
@@ -74,7 +74,14 @@ module ActiveRecord
74
74
  alias :loaded? :loaded
75
75
  alias :locked? :lock_value
76
76
 
77
- def initialize(model, table: model.arel_table, predicate_builder: model.predicate_builder, values: {})
77
+ def initialize(model, table: nil, predicate_builder: nil, values: {})
78
+ if table
79
+ predicate_builder ||= model.predicate_builder.with(TableMetadata.new(model, table))
80
+ else
81
+ table = model.arel_table
82
+ predicate_builder ||= model.predicate_builder
83
+ end
84
+
78
85
  @model = model
79
86
  @table = table
80
87
  @values = values
@@ -127,9 +127,9 @@ module ActiveRecord
127
127
  # Returns an +Enumerator+ if no block is given.
128
128
  def each(&block)
129
129
  if block_given?
130
- indexed_rows.each(&block)
130
+ hash_rows.each(&block)
131
131
  else
132
- indexed_rows.to_enum { @rows.size }
132
+ hash_rows.to_enum { @rows.size }
133
133
  end
134
134
  end
135
135
 
@@ -191,6 +191,7 @@ module ActiveRecord
191
191
  def initialize_copy(other)
192
192
  @rows = rows.dup
193
193
  @column_types = column_types.dup
194
+ @hash_rows = nil
194
195
  end
195
196
 
196
197
  def freeze # :nodoc:
@@ -212,6 +213,13 @@ module ActiveRecord
212
213
  end
213
214
  end
214
215
 
216
+ def indexed_rows # :nodoc:
217
+ @indexed_rows ||= begin
218
+ columns = column_indexes
219
+ @rows.map { |row| IndexedRow.new(columns, row) }.freeze
220
+ end
221
+ end
222
+
215
223
  private
216
224
  def column_type(name, index, type_overrides)
217
225
  type_overrides.fetch(name) do
@@ -221,13 +229,6 @@ module ActiveRecord
221
229
  end
222
230
  end
223
231
 
224
- def indexed_rows
225
- @indexed_rows ||= begin
226
- columns = column_indexes
227
- @rows.map { |row| IndexedRow.new(columns, row) }.freeze
228
- end
229
- end
230
-
231
232
  def hash_rows
232
233
  # We use transform_values to rows.
233
234
  # This is faster because we avoid any reallocs and avoid hashing entirely.
@@ -69,9 +69,7 @@ module ActiveRecord
69
69
 
70
70
  def predicate_builder
71
71
  if klass
72
- predicate_builder = klass.predicate_builder.dup
73
- predicate_builder.instance_variable_set(:@table, self)
74
- predicate_builder
72
+ klass.predicate_builder.with(self)
75
73
  else
76
74
  PredicateBuilder.new(self)
77
75
  end
@@ -180,7 +180,7 @@ module ActiveRecord
180
180
  each_current_configuration(env) do |db_config|
181
181
  database_initialized = initialize_database(db_config)
182
182
 
183
- seed = true if database_initialized
183
+ seed = true if database_initialized && db_config.seeds?
184
184
  end
185
185
 
186
186
  each_current_environment(env) do |environment|
@@ -240,13 +240,32 @@ module ActiveRecord
240
240
  end
241
241
  end
242
242
 
243
- def migrate(version = nil)
243
+ def migrate_all
244
+ db_configs = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env)
245
+ db_configs.each { |db_config| initialize_database(db_config) }
246
+
247
+ if db_configs.size == 1 && db_configs.first.primary?
248
+ ActiveRecord::Tasks::DatabaseTasks.migrate(skip_initialize: true)
249
+ else
250
+ mapped_versions = ActiveRecord::Tasks::DatabaseTasks.db_configs_with_versions
251
+
252
+ mapped_versions.sort.each do |version, db_configs|
253
+ db_configs.each do |db_config|
254
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection(db_config) do
255
+ ActiveRecord::Tasks::DatabaseTasks.migrate(version, skip_initialize: true)
256
+ end
257
+ end
258
+ end
259
+ end
260
+ end
261
+
262
+ def migrate(version = nil, skip_initialize: false)
244
263
  scope = ENV["SCOPE"]
245
264
  verbose_was, Migration.verbose = Migration.verbose, verbose?
246
265
 
247
266
  check_target_version
248
267
 
249
- initialize_database(migration_connection_pool.db_config)
268
+ initialize_database(migration_connection_pool.db_config) unless skip_initialize
250
269
 
251
270
  migration_connection_pool.migration_context.migrate(target_version) do |migration|
252
271
  if version.blank?
@@ -446,26 +465,10 @@ module ActiveRecord
446
465
  end
447
466
  end
448
467
 
449
- def cache_dump_filename(db_config_or_name, schema_cache_path: nil)
450
- if db_config_or_name.is_a?(DatabaseConfigurations::DatabaseConfig)
451
- schema_cache_path ||
452
- db_config_or_name.schema_cache_path ||
453
- schema_cache_env ||
454
- db_config_or_name.default_schema_cache_path(ActiveRecord::Tasks::DatabaseTasks.db_dir)
455
- else
456
- ActiveRecord.deprecator.warn(<<~MSG.squish)
457
- Passing a database name to `cache_dump_filename` is deprecated and will be removed in Rails 8.0. Pass a
458
- `ActiveRecord::DatabaseConfigurations::DatabaseConfig` object instead.
459
- MSG
460
-
461
- filename = if ActiveRecord::Base.configurations.primary?(db_config_or_name)
462
- "schema_cache.yml"
463
- else
464
- "#{db_config_or_name}_schema_cache.yml"
465
- end
466
-
467
- schema_cache_path || schema_cache_env || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
468
- end
468
+ def cache_dump_filename(db_config, schema_cache_path: nil)
469
+ schema_cache_path ||
470
+ db_config.schema_cache_path ||
471
+ db_config.default_schema_cache_path(ActiveRecord::Tasks::DatabaseTasks.db_dir)
469
472
  end
470
473
 
471
474
  def load_schema_current(format = ActiveRecord.schema_format, file = nil, environment = env)
@@ -536,17 +539,6 @@ module ActiveRecord
536
539
  end
537
540
 
538
541
  private
539
- def schema_cache_env
540
- if ENV["SCHEMA_CACHE"]
541
- ActiveRecord.deprecator.warn(<<~MSG.squish)
542
- Setting `ENV["SCHEMA_CACHE"]` is deprecated and will be removed in Rails 8.0.
543
- Configure the `:schema_cache_path` in the database configuration instead.
544
- MSG
545
-
546
- nil
547
- end
548
- end
549
-
550
542
  def with_temporary_pool(db_config, clobber: false)
551
543
  original_db_config = migration_class.connection_db_config
552
544
  pool = migration_class.connection_handler.establish_connection(db_config, clobber: clobber)
@@ -52,7 +52,7 @@ module ActiveRecord
52
52
  # assert_queries_match(/LIMIT \?/) { Post.first }
53
53
  #
54
54
  # If the +:include_schema+ option is provided, any queries (including schema related)
55
- # that match the matcher are considered.
55
+ # that match the matcher are considered.
56
56
  #
57
57
  # assert_queries_match(/FROM pg_attribute/i, include_schema: true) { Post.columns }
58
58
  #
@@ -80,7 +80,7 @@ module ActiveRecord
80
80
  # assert_no_queries_match(/SELECT/i) { post.comments }
81
81
  #
82
82
  # If the +:include_schema+ option is provided, any queries (including schema related)
83
- # that match the matcher are counted.
83
+ # that match the matcher are counted.
84
84
  #
85
85
  # assert_no_queries_match(/FROM pg_attribute/i, include_schema: true) { Post.columns }
86
86
  #