activerecord 7.1.1 → 7.1.5

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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +496 -0
  3. data/README.rdoc +1 -0
  4. data/lib/active_record/associations/association.rb +2 -1
  5. data/lib/active_record/associations/belongs_to_association.rb +4 -4
  6. data/lib/active_record/associations/collection_association.rb +4 -4
  7. data/lib/active_record/associations/has_many_association.rb +2 -2
  8. data/lib/active_record/associations/has_one_association.rb +2 -2
  9. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  10. data/lib/active_record/associations/join_dependency.rb +10 -12
  11. data/lib/active_record/associations/preloader/association.rb +4 -1
  12. data/lib/active_record/associations.rb +21 -15
  13. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  14. data/lib/active_record/attribute_methods/dirty.rb +15 -11
  15. data/lib/active_record/attribute_methods/read.rb +3 -3
  16. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
  17. data/lib/active_record/attribute_methods/write.rb +3 -3
  18. data/lib/active_record/attribute_methods.rb +47 -7
  19. data/lib/active_record/autosave_association.rb +5 -2
  20. data/lib/active_record/callbacks.rb +2 -2
  21. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +19 -9
  22. data/lib/active_record/connection_adapters/abstract/database_statements.rb +5 -3
  23. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +8 -4
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +27 -19
  26. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +37 -13
  27. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +2 -1
  28. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +3 -0
  29. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -10
  30. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +4 -1
  31. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  32. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  33. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -6
  34. data/lib/active_record/connection_adapters/postgresql_adapter.rb +32 -33
  35. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -3
  36. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -2
  37. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +1 -0
  38. data/lib/active_record/connection_adapters/trilogy_adapter.rb +25 -21
  39. data/lib/active_record/connection_handling.rb +1 -1
  40. data/lib/active_record/core.rb +49 -10
  41. data/lib/active_record/counter_cache.rb +7 -3
  42. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
  43. data/lib/active_record/database_configurations/hash_config.rb +6 -2
  44. data/lib/active_record/delegated_type.rb +7 -7
  45. data/lib/active_record/destroy_association_async_job.rb +1 -1
  46. data/lib/active_record/encryption/encryptable_record.rb +7 -1
  47. data/lib/active_record/encryption/encrypted_attribute_type.rb +6 -2
  48. data/lib/active_record/encryption/extended_deterministic_queries.rb +0 -15
  49. data/lib/active_record/encryption/scheme.rb +8 -4
  50. data/lib/active_record/encryption.rb +2 -0
  51. data/lib/active_record/enum.rb +6 -9
  52. data/lib/active_record/errors.rb +5 -4
  53. data/lib/active_record/fixtures.rb +16 -0
  54. data/lib/active_record/future_result.rb +10 -0
  55. data/lib/active_record/gem_version.rb +1 -1
  56. data/lib/active_record/insert_all.rb +3 -3
  57. data/lib/active_record/internal_metadata.rb +1 -1
  58. data/lib/active_record/locking/optimistic.rb +1 -1
  59. data/lib/active_record/marshalling.rb +4 -1
  60. data/lib/active_record/message_pack.rb +1 -1
  61. data/lib/active_record/middleware/database_selector.rb +1 -1
  62. data/lib/active_record/migration/compatibility.rb +14 -0
  63. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  64. data/lib/active_record/migration.rb +9 -5
  65. data/lib/active_record/model_schema.rb +12 -7
  66. data/lib/active_record/nested_attributes.rb +16 -5
  67. data/lib/active_record/normalization.rb +8 -0
  68. data/lib/active_record/persistence.rb +6 -5
  69. data/lib/active_record/promise.rb +1 -1
  70. data/lib/active_record/query_cache.rb +1 -1
  71. data/lib/active_record/query_logs_formatter.rb +1 -1
  72. data/lib/active_record/railtie.rb +14 -14
  73. data/lib/active_record/railties/controller_runtime.rb +2 -1
  74. data/lib/active_record/railties/databases.rake +7 -7
  75. data/lib/active_record/reflection.rb +21 -3
  76. data/lib/active_record/relation/calculations.rb +28 -1
  77. data/lib/active_record/relation/delegation.rb +1 -1
  78. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
  79. data/lib/active_record/relation/query_methods.rb +21 -7
  80. data/lib/active_record/relation.rb +30 -5
  81. data/lib/active_record/result.rb +1 -1
  82. data/lib/active_record/runtime_registry.rb +15 -1
  83. data/lib/active_record/schema_migration.rb +1 -1
  84. data/lib/active_record/secure_token.rb +1 -1
  85. data/lib/active_record/tasks/database_tasks.rb +35 -13
  86. data/lib/active_record/test_fixtures.rb +1 -0
  87. data/lib/active_record/timestamp.rb +3 -1
  88. data/lib/active_record.rb +2 -2
  89. data/lib/arel/nodes/homogeneous_in.rb +1 -1
  90. data/lib/arel/tree_manager.rb +5 -1
  91. data/lib/arel/visitors/to_sql.rb +2 -1
  92. metadata +14 -13
@@ -65,7 +65,7 @@ module ActiveRecord
65
65
  updates.merge!(touch_updates)
66
66
  end
67
67
 
68
- unscoped.where(primary_key => object.id).update_all(updates) if updates.any?
68
+ unscoped.where(primary_key => [object.id]).update_all(updates) if updates.any?
69
69
 
70
70
  true
71
71
  end
@@ -112,6 +112,7 @@ module ActiveRecord
112
112
  # # `updated_at` = '2016-10-13T09:59:23-05:00'
113
113
  # # WHERE id IN (10, 15)
114
114
  def update_counters(id, counters)
115
+ id = [id] if composite_primary_key? && id.is_a?(Array) && !id[0].is_a?(Array)
115
116
  unscoped.where!(primary_key => id).update_counters(counters)
116
117
  end
117
118
 
@@ -198,8 +199,7 @@ module ActiveRecord
198
199
 
199
200
  if affected_rows > 0
200
201
  each_counter_cached_associations do |association|
201
- foreign_key = association.reflection.foreign_key.to_sym
202
- unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
202
+ unless destroyed_by_association && _foreign_keys_equal?(destroyed_by_association.foreign_key, association.reflection.foreign_key)
203
203
  association.decrement_counters
204
204
  end
205
205
  end
@@ -213,5 +213,9 @@ module ActiveRecord
213
213
  yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
214
214
  end
215
215
  end
216
+
217
+ def _foreign_keys_equal?(fkey1, fkey2)
218
+ fkey1 == fkey2 || Array(fkey1).map(&:to_sym) == Array(fkey2).map(&:to_sym)
219
+ end
216
220
  end
217
221
  end
@@ -46,7 +46,7 @@ module ActiveRecord
46
46
  attr_reader :uri
47
47
 
48
48
  def uri_parser
49
- @uri_parser ||= URI::Parser.new
49
+ @uri_parser ||= URI::RFC2396_Parser.new
50
50
  end
51
51
 
52
52
  # Converts the query parameters of the URI into a hash.
@@ -114,8 +114,12 @@ module ActiveRecord
114
114
  configuration_hash[:schema_cache_path]
115
115
  end
116
116
 
117
- def default_schema_cache_path
118
- "db/schema_cache.yml"
117
+ def default_schema_cache_path(db_dir = "db")
118
+ if primary?
119
+ File.join(db_dir, "schema_cache.yml")
120
+ else
121
+ File.join(db_dir, "#{name}_schema_cache.yml")
122
+ end
119
123
  end
120
124
 
121
125
  def lazy_schema_cache_path
@@ -36,7 +36,7 @@ module ActiveRecord
36
36
  #
37
37
  # Let's look at that entry/message/comment example using delegated types:
38
38
  #
39
- # # Schema: entries[ id, account_id, creator_id, created_at, updated_at, entryable_type, entryable_id ]
39
+ # # Schema: entries[ id, account_id, creator_id, entryable_type, entryable_id, created_at, updated_at ]
40
40
  # class Entry < ApplicationRecord
41
41
  # belongs_to :account
42
42
  # belongs_to :creator
@@ -51,12 +51,12 @@ module ActiveRecord
51
51
  # end
52
52
  # end
53
53
  #
54
- # # Schema: messages[ id, subject, body ]
54
+ # # Schema: messages[ id, subject, body, created_at, updated_at ]
55
55
  # class Message < ApplicationRecord
56
56
  # include Entryable
57
57
  # end
58
58
  #
59
- # # Schema: comments[ id, content ]
59
+ # # Schema: comments[ id, content, created_at, updated_at ]
60
60
  # class Comment < ApplicationRecord
61
61
  # include Entryable
62
62
  # end
@@ -102,14 +102,14 @@ module ActiveRecord
102
102
  # You create a new record that uses delegated typing by creating the delegator and delegatee at the same time,
103
103
  # like so:
104
104
  #
105
- # Entry.create! entryable: Comment.new(content: "Hello!"), creator: Current.user
105
+ # Entry.create! entryable: Comment.new(content: "Hello!"), creator: Current.user, account: Current.account
106
106
  #
107
107
  # If you need more complicated composition, or you need to perform dependent validation, you should build a factory
108
108
  # method or class to take care of the complicated needs. This could be as simple as:
109
109
  #
110
110
  # class Entry < ApplicationRecord
111
- # def self.create_with_comment(content, creator: Current.user)
112
- # create! entryable: Comment.new(content: content), creator: creator
111
+ # def self.create_with_comment(content, creator: Current.user, account: Current.account)
112
+ # create! entryable: Comment.new(content: content), creator: creator, account: account
113
113
  # end
114
114
  # end
115
115
  #
@@ -138,7 +138,7 @@ module ActiveRecord
138
138
  #
139
139
  # Now you can list a bunch of entries, call <tt>Entry#title</tt>, and polymorphism will provide you with the answer.
140
140
  #
141
- # == Nested Attributes
141
+ # == Nested \Attributes
142
142
  #
143
143
  # Enabling nested attributes on a delegated_type association allows you to
144
144
  # create the entry and message in one go:
@@ -19,7 +19,7 @@ module ActiveRecord
19
19
  )
20
20
  association_model = association_class.constantize
21
21
  owner_class = owner_model_name.constantize
22
- owner = owner_class.find_by(owner_class.primary_key.to_sym => owner_id)
22
+ owner = owner_class.find_by(owner_class.primary_key => [owner_id])
23
23
 
24
24
  if !owner_destroyed?(owner, ensuring_owner_was_method)
25
25
  raise DestroyAssociationAsyncError, "owner record not destroyed"
@@ -144,7 +144,13 @@ module ActiveRecord
144
144
 
145
145
  # Returns whether a given attribute is encrypted or not.
146
146
  def encrypted_attribute?(attribute_name)
147
- ActiveRecord::Encryption.encryptor.encrypted? read_attribute_before_type_cast(attribute_name)
147
+ name = attribute_name.to_s
148
+ name = self.class.attribute_aliases[name] || name
149
+
150
+ return false unless self.class.encrypted_attributes&.include? name.to_sym
151
+
152
+ type = type_for_attribute(name)
153
+ type.encrypted? read_attribute_before_type_cast(name)
148
154
  end
149
155
 
150
156
  # Returns the ciphertext for +attribute_name+.
@@ -44,6 +44,10 @@ module ActiveRecord
44
44
  end
45
45
  end
46
46
 
47
+ def encrypted?(value)
48
+ with_context { encryptor.encrypted? value }
49
+ end
50
+
47
51
  def changed_in_place?(raw_old_value, new_value)
48
52
  old_value = raw_old_value.nil? ? nil : deserialize(raw_old_value)
49
53
  old_value != new_value
@@ -136,11 +140,11 @@ module ActiveRecord
136
140
  end
137
141
 
138
142
  def encryption_options
139
- @encryption_options ||= { key_provider: key_provider, cipher_options: { deterministic: deterministic? } }.compact
143
+ { key_provider: key_provider, cipher_options: { deterministic: deterministic? } }.compact
140
144
  end
141
145
 
142
146
  def decryption_options
143
- @decryption_options ||= { key_provider: key_provider }.compact
147
+ { key_provider: key_provider }.compact
144
148
  end
145
149
 
146
150
  def clean_text_scheme
@@ -28,7 +28,6 @@ module ActiveRecord
28
28
  ActiveRecord::Relation.prepend(RelationQueries)
29
29
  ActiveRecord::Base.include(CoreQueries)
30
30
  ActiveRecord::Encryption::EncryptedAttributeType.prepend(ExtendedEncryptableType)
31
- Arel::Nodes::HomogeneousIn.prepend(InWithAdditionalValues)
32
31
  end
33
32
 
34
33
  # When modifying this file run performance tests in
@@ -153,20 +152,6 @@ module ActiveRecord
153
152
  end
154
153
  end
155
154
  end
156
-
157
- module InWithAdditionalValues
158
- def proc_for_binds
159
- -> value { ActiveModel::Attribute.with_cast_value(attribute.name, value, encryption_aware_type_caster) }
160
- end
161
-
162
- def encryption_aware_type_caster
163
- if attribute.type_caster.is_a?(ActiveRecord::Encryption::EncryptedAttributeType)
164
- attribute.type_caster.cast_type
165
- else
166
- attribute.type_caster
167
- end
168
- end
169
- end
170
155
  end
171
156
  end
172
157
  end
@@ -50,7 +50,7 @@ module ActiveRecord
50
50
  end
51
51
 
52
52
  def key_provider
53
- @key_provider ||= @key_provider_param || build_key_provider || default_key_provider
53
+ @key_provider_param || key_provider_from_key || deterministic_key_provider || default_key_provider
54
54
  end
55
55
 
56
56
  def merge(other_scheme)
@@ -80,10 +80,14 @@ module ActiveRecord
80
80
  raise Errors::Configuration, "key_provider: and key: can't be used simultaneously" if @key_provider_param && @key
81
81
  end
82
82
 
83
- def build_key_provider
84
- return DerivedSecretKeyProvider.new(@key) if @key.present?
83
+ def key_provider_from_key
84
+ @key_provider_from_key ||= if @key.present?
85
+ DerivedSecretKeyProvider.new(@key)
86
+ end
87
+ end
85
88
 
86
- if @deterministic
89
+ def deterministic_key_provider
90
+ @deterministic_key_provider ||= if @deterministic
87
91
  DeterministicKeyProvider.new(ActiveRecord::Encryption.config.deterministic_key)
88
92
  end
89
93
  end
@@ -53,4 +53,6 @@ module ActiveRecord
53
53
  Cipher.eager_load!
54
54
  end
55
55
  end
56
+
57
+ ActiveSupport.run_load_hooks :active_record_encryption, Encryption
56
58
  end
@@ -167,15 +167,6 @@ module ActiveRecord
167
167
  base.class_attribute(:defined_enums, instance_writer: false, default: {})
168
168
  end
169
169
 
170
- def load_schema! # :nodoc:
171
- attributes_to_define_after_schema_loads.each do |name, (cast_type, _default)|
172
- unless columns_hash.key?(name)
173
- cast_type = cast_type[type_for_attribute(name)] if Proc === cast_type
174
- raise "Unknown enum attribute '#{name}' for #{self.name}" if Enum::EnumType === cast_type
175
- end
176
- end
177
- end
178
-
179
170
  class EnumType < Type::Value # :nodoc:
180
171
  delegate :type, to: :subtype
181
172
 
@@ -255,6 +246,12 @@ module ActiveRecord
255
246
  detect_enum_conflict!(name, "#{name}=")
256
247
 
257
248
  attribute(name, **options) do |subtype|
249
+ if subtype == ActiveModel::Type.default_value
250
+ raise "Undeclared attribute type for enum '#{name}' in #{self.name}. Enums must be" \
251
+ " backed by a database column or declared with an explicit type" \
252
+ " via `attribute`."
253
+ end
254
+
258
255
  subtype = subtype.subtype if EnumType === subtype
259
256
  EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
260
257
  end
@@ -318,14 +318,15 @@ module ActiveRecord
318
318
  class << self
319
319
  def db_error(db_name)
320
320
  NoDatabaseError.new(<<~MSG)
321
- We could not find your database: #{db_name}. Available database configurations can be found in config/database.yml file.
321
+ We could not find your database: #{db_name}. Available database configurations can be found in config/database.yml.
322
322
 
323
323
  To resolve this error:
324
324
 
325
- - Did you create the database for this app, or delete it? You may need to create your database.
326
- - Has the database name changed? Check your database.yml config has the correct database name.
325
+ - Did you not create the database, or did you delete it? To create the database, run:
327
326
 
328
- To create your database, run:\n\n bin/rails db:create
327
+ bin/rails db:create
328
+
329
+ - Has the database name changed? Verify that config/database.yml contains the correct database name.
329
330
  MSG
330
331
  end
331
332
  end
@@ -268,6 +268,8 @@ module ActiveRecord
268
268
  # name: Reginald the Pirate
269
269
  # monkey_id: 1
270
270
  #
271
+ # <code></code>
272
+ #
271
273
  # ### in monkeys.yml
272
274
  #
273
275
  # george:
@@ -285,6 +287,8 @@ module ActiveRecord
285
287
  # name: Reginald the Pirate
286
288
  # monkey: george
287
289
  #
290
+ # <code></code>
291
+ #
288
292
  # ### in monkeys.yml
289
293
  #
290
294
  # george:
@@ -306,6 +310,8 @@ module ActiveRecord
306
310
  #
307
311
  # belongs_to :eater, polymorphic: true
308
312
  #
313
+ # <code></code>
314
+ #
309
315
  # ### in fruits.yml
310
316
  #
311
317
  # apple:
@@ -331,6 +337,8 @@ module ActiveRecord
331
337
  # id: 1
332
338
  # name: George the Monkey
333
339
  #
340
+ # <code></code>
341
+ #
334
342
  # ### in fruits.yml
335
343
  #
336
344
  # apple:
@@ -345,6 +353,8 @@ module ActiveRecord
345
353
  # id: 3
346
354
  # name: grape
347
355
  #
356
+ # <code></code>
357
+ #
348
358
  # ### in fruits_monkeys.yml
349
359
  #
350
360
  # apple_george:
@@ -368,6 +378,8 @@ module ActiveRecord
368
378
  # name: George the Monkey
369
379
  # fruits: apple, orange, grape
370
380
  #
381
+ # <code></code>
382
+ #
371
383
  # ### in fruits.yml
372
384
  #
373
385
  # apple:
@@ -467,6 +479,8 @@ module ActiveRecord
467
479
  # belongs_to :author
468
480
  # end
469
481
  #
482
+ # <code></code>
483
+ #
470
484
  # # books.yml
471
485
  # alices_adventure_in_wonderland:
472
486
  # author_id: <%= ActiveRecord::FixtureSet.identify(:lewis_carroll) %>
@@ -482,6 +496,8 @@ module ActiveRecord
482
496
  # belongs_to :book, query_constraints: [:author_id, :book_id]
483
497
  # end
484
498
  #
499
+ # <code></code>
500
+ #
485
501
  # # book_orders.yml
486
502
  # alices_adventure_in_wonderland_in_books:
487
503
  # author: lewis_carroll
@@ -47,7 +47,17 @@ module ActiveRecord
47
47
 
48
48
  Canceled = Class.new(ActiveRecordError)
49
49
 
50
+ def self.wrap(result)
51
+ case result
52
+ when self, Complete
53
+ result
54
+ else
55
+ Complete.new(result)
56
+ end
57
+ end
58
+
50
59
  delegate :empty?, :to_a, to: :result
60
+ delegate_missing_to :result
51
61
 
52
62
  attr_reader :lock_wait
53
63
 
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  module VERSION
10
10
  MAJOR = 7
11
11
  MINOR = 1
12
- TINY = 1
12
+ TINY = 5
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -23,8 +23,6 @@ module ActiveRecord
23
23
  @keys = @inserts.first.keys
24
24
  end
25
25
 
26
- configure_on_duplicate_update_logic
27
-
28
26
  if model.scope_attributes?
29
27
  @scope_attributes = model.scope_attributes
30
28
  @keys |= @scope_attributes.keys
@@ -35,8 +33,8 @@ module ActiveRecord
35
33
  @returning = false if @returning == []
36
34
 
37
35
  @unique_by = find_unique_index_for(@unique_by)
38
- @on_duplicate = :skip if @on_duplicate == :update && updatable_columns.empty?
39
36
 
37
+ configure_on_duplicate_update_logic
40
38
  ensure_valid_options_for_connection!
41
39
  end
42
40
 
@@ -135,6 +133,8 @@ module ActiveRecord
135
133
  elsif custom_update_sql_provided?
136
134
  @update_sql = on_duplicate
137
135
  @on_duplicate = :update
136
+ elsif @on_duplicate == :update && updatable_columns.empty?
137
+ @on_duplicate = :skip
138
138
  end
139
139
  end
140
140
 
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  # This is enabled by default. To disable this functionality set
11
11
  # `use_metadata_table` to false in your database configuration.
12
12
  class InternalMetadata # :nodoc:
13
- class NullInternalMetadata
13
+ class NullInternalMetadata # :nodoc:
14
14
  end
15
15
 
16
16
  attr_reader :connection, :arel_table
@@ -14,7 +14,7 @@ module ActiveRecord
14
14
  # == Usage
15
15
  #
16
16
  # Active Record supports optimistic locking if the +lock_version+ field is present. Each update to the
17
- # record increments the +lock_version+ column and the locking facilities ensure that records instantiated twice
17
+ # record increments the integer column +lock_version+ and the locking facilities ensure that records instantiated twice
18
18
  # will let the last one saved raise a +StaleObjectError+ if the first was also updated. Example:
19
19
  #
20
20
  # p1 = Person.find(1)
@@ -25,7 +25,10 @@ module ActiveRecord
25
25
  payload = [attributes_for_database, new_record?]
26
26
 
27
27
  cached_associations = self.class.reflect_on_all_associations.select do |reflection|
28
- association_cached?(reflection.name)
28
+ if association_cached?(reflection.name)
29
+ association = association(reflection.name)
30
+ association.loaded? || association.target.present?
31
+ end
29
32
  end
30
33
 
31
34
  unless cached_associations.empty?
@@ -80,7 +80,7 @@ module ActiveRecord
80
80
 
81
81
  def add_cached_associations(record, entry)
82
82
  record.class.reflections.each_value do |reflection|
83
- if record.association_cached?(reflection.name)
83
+ if record.association_cached?(reflection.name) && record.association(reflection.name).loaded?
84
84
  entry << reflection.name << encode(record.association(reflection.name).target)
85
85
  end
86
86
  end
@@ -24,7 +24,7 @@ module ActiveRecord
24
24
  # To use the DatabaseSelector in your application with default settings,
25
25
  # run the provided generator.
26
26
  #
27
- # bin/rails g active_record:multi_db
27
+ # $ bin/rails g active_record:multi_db
28
28
  #
29
29
  # This will create a file named +config/initializers/multi_db.rb+ with the
30
30
  # following contents:
@@ -61,8 +61,10 @@ module ActiveRecord
61
61
  column_name.is_a?(String) && /\W/.match?(column_name)
62
62
  end
63
63
  end
64
+
64
65
  module TableDefinition
65
66
  include LegacyIndexName
67
+
66
68
  def column(name, type, **options)
67
69
  options[:_skip_validate_options] = true
68
70
  super
@@ -78,6 +80,11 @@ module ActiveRecord
78
80
  super
79
81
  end
80
82
 
83
+ def references(*args, **options)
84
+ options[:_skip_validate_options] = true
85
+ super
86
+ end
87
+
81
88
  private
82
89
  def raise_on_if_exist_options(options)
83
90
  end
@@ -95,6 +102,12 @@ module ActiveRecord
95
102
  super
96
103
  end
97
104
 
105
+ def add_reference(table_name, ref_name, **options)
106
+ options[:_skip_validate_options] = true
107
+ super
108
+ end
109
+ alias :add_belongs_to :add_reference
110
+
98
111
  def create_table(table_name, **options)
99
112
  options[:_uses_legacy_table_name] = true
100
113
  options[:_skip_validate_options] = true
@@ -104,6 +117,7 @@ module ActiveRecord
104
117
 
105
118
  def rename_table(table_name, new_name, **options)
106
119
  options[:_uses_legacy_table_name] = true
120
+ options[:_uses_legacy_index_name] = true
107
121
  super
108
122
  end
109
123
 
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class PendingMigrationConnection # :nodoc:
5
+ def self.establish_temporary_connection(db_config, &block)
6
+ pool = ActiveRecord::Base.connection_handler.establish_connection(db_config, owner_name: self)
7
+
8
+ yield pool.connection
9
+ ensure
10
+ ActiveRecord::Base.connection_handler.remove_connection_pool(self.name)
11
+ end
12
+
13
+ def self.primary_class?
14
+ false
15
+ end
16
+
17
+ def self.current_preventing_writes
18
+ false
19
+ end
20
+ end
21
+ end
@@ -7,6 +7,7 @@ require "active_support/core_ext/array/access"
7
7
  require "active_support/core_ext/enumerable"
8
8
  require "active_support/core_ext/module/attribute_accessors"
9
9
  require "active_support/actionable_error"
10
+ require "active_record/migration/pending_migration_connection"
10
11
 
11
12
  module ActiveRecord
12
13
  class MigrationError < ActiveRecordError # :nodoc:
@@ -370,7 +371,8 @@ module ActiveRecord
370
371
  # The \Rails package has several tools to help create and apply migrations.
371
372
  #
372
373
  # To generate a new migration, you can use
373
- # bin/rails generate migration MyNewMigration
374
+ #
375
+ # $ bin/rails generate migration MyNewMigration
374
376
  #
375
377
  # where MyNewMigration is the name of your migration. The generator will
376
378
  # create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
@@ -379,7 +381,7 @@ module ActiveRecord
379
381
  #
380
382
  # There is a special syntactic shortcut to generate migrations that add fields to a table.
381
383
  #
382
- # bin/rails generate migration add_fieldname_to_tablename fieldname:string
384
+ # $ bin/rails generate migration add_fieldname_to_tablename fieldname:string
383
385
  #
384
386
  # This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
385
387
  # class AddFieldnameToTablename < ActiveRecord::Migration[7.1]
@@ -768,9 +770,11 @@ module ActiveRecord
768
770
  def pending_migrations
769
771
  pending_migrations = []
770
772
 
771
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each(env: env) do |connection|
772
- if pending = connection.migration_context.open.pending_migrations
773
- pending_migrations << pending
773
+ ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
774
+ ActiveRecord::PendingMigrationConnection.establish_temporary_connection(db_config) do |conn|
775
+ if pending = conn.migration_context.open.pending_migrations
776
+ pending_migrations << pending
777
+ end
774
778
  end
775
779
  end
776
780
 
@@ -284,8 +284,10 @@ module ActiveRecord
284
284
 
285
285
  # Computes the table name, (re)sets it internally, and returns it.
286
286
  def reset_table_name # :nodoc:
287
- self.table_name = if abstract_class?
288
- superclass == Base ? nil : superclass.table_name
287
+ self.table_name = if self == Base
288
+ nil
289
+ elsif abstract_class?
290
+ superclass.table_name
289
291
  elsif superclass.abstract_class?
290
292
  superclass.table_name || compute_table_name
291
293
  else
@@ -430,8 +432,12 @@ module ActiveRecord
430
432
  end
431
433
 
432
434
  def _returning_columns_for_insert # :nodoc:
433
- @_returning_columns_for_insert ||= columns.filter_map do |c|
434
- c.name if connection.return_value_after_insert?(c)
435
+ @_returning_columns_for_insert ||= begin
436
+ auto_populated_columns = columns.filter_map do |c|
437
+ c.name if connection.return_value_after_insert?(c)
438
+ end
439
+
440
+ auto_populated_columns.empty? ? Array(primary_key) : auto_populated_columns
435
441
  end
436
442
  end
437
443
 
@@ -467,7 +473,7 @@ module ActiveRecord
467
473
  end
468
474
 
469
475
  # Returns the column object for the named attribute.
470
- # Returns an +ActiveRecord::ConnectionAdapters::NullColumn+ if the
476
+ # Returns an ActiveRecord::ConnectionAdapters::NullColumn if the
471
477
  # named attribute does not exist.
472
478
  #
473
479
  # class Person < ActiveRecord::Base
@@ -627,8 +633,7 @@ module ActiveRecord
627
633
  )
628
634
  alias_attribute :id_value, :id if name == "id"
629
635
  end
630
-
631
- super
636
+ _default_attributes # Precompute to cache DB-dependent attribute types
632
637
  end
633
638
 
634
639
  # Guesses the table name, but does not decorate it with prefix and suffix information.