activerecord 8.0.0.beta1 → 8.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
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
  #