activerecord 7.1.1 → 7.1.2

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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +78 -0
  3. data/README.rdoc +1 -0
  4. data/lib/active_record/associations.rb +14 -14
  5. data/lib/active_record/attribute_methods/dirty.rb +12 -8
  6. data/lib/active_record/callbacks.rb +2 -2
  7. data/lib/active_record/connection_adapters/abstract/database_statements.rb +4 -2
  8. data/lib/active_record/connection_adapters/abstract_adapter.rb +13 -4
  9. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +3 -0
  10. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +4 -1
  11. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  12. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -5
  13. data/lib/active_record/connection_adapters/postgresql_adapter.rb +32 -33
  14. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -3
  15. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +1 -0
  16. data/lib/active_record/core.rb +4 -3
  17. data/lib/active_record/encryption/extended_deterministic_queries.rb +0 -15
  18. data/lib/active_record/enum.rb +6 -9
  19. data/lib/active_record/gem_version.rb +1 -1
  20. data/lib/active_record/insert_all.rb +3 -3
  21. data/lib/active_record/internal_metadata.rb +1 -1
  22. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  23. data/lib/active_record/migration.rb +6 -3
  24. data/lib/active_record/model_schema.rb +5 -5
  25. data/lib/active_record/nested_attributes.rb +3 -3
  26. data/lib/active_record/normalization.rb +8 -0
  27. data/lib/active_record/promise.rb +1 -1
  28. data/lib/active_record/railties/databases.rake +5 -5
  29. data/lib/active_record/reflection.rb +12 -0
  30. data/lib/active_record/schema_migration.rb +1 -1
  31. data/lib/arel/nodes/homogeneous_in.rb +1 -1
  32. metadata +10 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3595eee8065caff3100f00f5ce54d1b3a34935759766cb16b4d8229a57ebc97e
4
- data.tar.gz: 5fbab431bcbc3b56cae5b2d8400c893071c72ccc7feea3eb6d9343418c650ec6
3
+ metadata.gz: dba30f1c9ab82b5a3e78337bcdb364d613e04a20e815c4b1f6952e1360d8e602
4
+ data.tar.gz: dc7470a1bc6823f31ec4c3b678df945ac4a32a2a1a642943909f47318af205f6
5
5
  SHA512:
6
- metadata.gz: c75c3da922f3b0089112bb07c115787bedc1bb57a6e05aa04df893262554e09bf81e71898ac7ed24b1c80a00071818e14267cbffc2bb2b789755867b85f76d78
7
- data.tar.gz: 5f6684e9a76e3a2a7156b687d4ac0701b9ddd840edd9c671f51031c534bdbd1e920b2aad65a479299383cb8555346a585e3167c353f33f95ecf8e05115438205
6
+ metadata.gz: b85f866d527785cad076c222228e1b6ee39122b383663cc1fe81dcad6eb152c52dfb46ad86dee471f01525c5e074a382fbe5360fd99e78e7886d7169c2c60091
7
+ data.tar.gz: 5291b47487b7bdb83885687b2c6ca90fe5295ed0c017317585bbb712c125c28f66c4b3d5c7cceb0e041bd25be069854c365222179bad6dc360b9156e5f3753ae
data/CHANGELOG.md CHANGED
@@ -1,3 +1,77 @@
1
+ ## Rails 7.1.2 (November 10, 2023) ##
2
+
3
+ * Fix renaming primary key index when renaming a table with a UUID primary key
4
+ in PostgreSQL.
5
+
6
+ *fatkodima*
7
+
8
+ * Fix `where(field: values)` queries when `field` is a serialized attribute
9
+ (for example, when `field` uses `ActiveRecord::Base.serialize` or is a JSON
10
+ column).
11
+
12
+ *João Alves*
13
+
14
+ * Prevent marking broken connections as verified.
15
+
16
+ *Daniel Colson*
17
+
18
+ * Don't mark Float::INFINITY as changed when reassigning it
19
+
20
+ When saving a record with a float infinite value, it shouldn't mark as changed
21
+
22
+ *Maicol Bentancor*
23
+
24
+ * `ActiveRecord::Base.table_name` now returns `nil` instead of raising
25
+ "undefined method `abstract_class?` for Object:Class".
26
+
27
+ *a5-stable*
28
+
29
+ * Fix upserting for custom `:on_duplicate` and `:unique_by` consisting of all
30
+ inserts keys.
31
+
32
+ *fatkodima*
33
+
34
+ * Fixed an [issue](https://github.com/rails/rails/issues/49809) where saving a
35
+ record could innappropriately `dup` its attributes.
36
+
37
+ *Jonathan Hefner*
38
+
39
+ * Dump schema only for a specific db for rollback/up/down tasks for multiple dbs.
40
+
41
+ *fatkodima*
42
+
43
+ * Fix `NoMethodError` when casting a PostgreSQL `money` value that uses a
44
+ comma as its radix point and has no leading currency symbol. For example,
45
+ when casting `"3,50"`.
46
+
47
+ *Andreas Reischuck* and *Jonathan Hefner*
48
+
49
+ * Re-enable support for using `enum` with non-column-backed attributes.
50
+ Non-column-backed attributes must be previously declared with an explicit
51
+ type. For example:
52
+
53
+ ```ruby
54
+ class Post < ActiveRecord::Base
55
+ attribute :topic, :string
56
+ enum topic: %i[science tech engineering math]
57
+ end
58
+ ```
59
+
60
+ *Jonathan Hefner*
61
+
62
+ * Raise on `foreign_key:` being passed as an array in associations
63
+
64
+ *Nikita Vasilevsky*
65
+
66
+ * Return back maximum allowed PostgreSQL table name to 63 characters.
67
+
68
+ *fatkodima*
69
+
70
+ * Fix detecting `IDENTITY` columns for PostgreSQL < 10.
71
+
72
+ *fatkodima*
73
+
74
+
1
75
  ## Rails 7.1.1 (October 11, 2023) ##
2
76
 
3
77
  * Fix auto populating IDENTITY columns for PostgreSQL.
@@ -165,6 +239,10 @@
165
239
 
166
240
  *Adam Hess*
167
241
 
242
+ * Deprecate aliasing non-attributes with `alias_attribute`.
243
+
244
+ *Ian Candy*
245
+
168
246
  * Fix unscope is not working in specific case
169
247
 
170
248
  Before:
data/README.rdoc CHANGED
@@ -166,6 +166,7 @@ Active Record is an implementation of the object-relational mapping (ORM)
166
166
  pattern[https://www.martinfowler.com/eaaCatalog/activeRecord.html] by the same
167
167
  name described by Martin Fowler:
168
168
 
169
+ >>>
169
170
  "An object that wraps a row in a database table or view,
170
171
  encapsulates the database access, and adds domain logic on that data."
171
172
 
@@ -1339,7 +1339,7 @@ module ActiveRecord
1339
1339
  # Returns a Relation of all of the associated objects, forcing a database read.
1340
1340
  # An empty Relation is returned if none are found.
1341
1341
  #
1342
- # === Example
1342
+ # ==== Example
1343
1343
  #
1344
1344
  # class Firm < ActiveRecord::Base
1345
1345
  # has_many :clients
@@ -1369,7 +1369,7 @@ module ActiveRecord
1369
1369
  #
1370
1370
  # The declaration can also include an +options+ hash to specialize the behavior of the association.
1371
1371
  #
1372
- # === Scopes
1372
+ # ==== Scopes
1373
1373
  #
1374
1374
  # You can pass a second argument +scope+ as a callable (i.e. proc or
1375
1375
  # lambda) to retrieve a specific set of records or customize the generated
@@ -1380,7 +1380,7 @@ module ActiveRecord
1380
1380
  # has_many :employees, -> { joins(:address) }
1381
1381
  # has_many :posts, ->(blog) { where("max_post_length > ?", blog.max_post_length) }
1382
1382
  #
1383
- # === Extensions
1383
+ # ==== Extensions
1384
1384
  #
1385
1385
  # The +extension+ argument allows you to pass a block into a has_many
1386
1386
  # association. This is useful for adding new finders, creators, and other
@@ -1394,7 +1394,7 @@ module ActiveRecord
1394
1394
  # end
1395
1395
  # end
1396
1396
  #
1397
- # === Options
1397
+ # ==== Options
1398
1398
  # [+:class_name+]
1399
1399
  # Specify the class name of the association. Use it only if that name can't be inferred
1400
1400
  # from the association name. So <tt>has_many :products</tt> will by default be linked
@@ -1556,7 +1556,7 @@ module ActiveRecord
1556
1556
  # [<tt>reset_association</tt>]
1557
1557
  # Unloads the associated object. The next access will query it from the database.
1558
1558
  #
1559
- # === Example
1559
+ # ==== Example
1560
1560
  #
1561
1561
  # class Account < ActiveRecord::Base
1562
1562
  # has_one :beneficiary
@@ -1575,7 +1575,7 @@ module ActiveRecord
1575
1575
  # account.reload_beneficiary
1576
1576
  # account.reset_beneficiary
1577
1577
  #
1578
- # === Scopes
1578
+ # ==== Scopes
1579
1579
  #
1580
1580
  # You can pass a second argument +scope+ as a callable (i.e. proc or
1581
1581
  # lambda) to retrieve a specific record or customize the generated query
@@ -1586,7 +1586,7 @@ module ActiveRecord
1586
1586
  # has_one :employer, -> { joins(:company) }
1587
1587
  # has_one :latest_post, ->(blog) { where("created_at > ?", blog.enabled_at) }
1588
1588
  #
1589
- # === Options
1589
+ # ==== Options
1590
1590
  #
1591
1591
  # The declaration can also include an +options+ hash to specialize the behavior of the association.
1592
1592
  #
@@ -1745,7 +1745,7 @@ module ActiveRecord
1745
1745
  # [<tt>association_previously_changed?</tt>]
1746
1746
  # Returns true if the previous save updated the association to reference a new associate object.
1747
1747
  #
1748
- # === Example
1748
+ # ==== Example
1749
1749
  #
1750
1750
  # class Post < ActiveRecord::Base
1751
1751
  # belongs_to :author
@@ -1766,7 +1766,7 @@ module ActiveRecord
1766
1766
  # post.author_changed?
1767
1767
  # post.author_previously_changed?
1768
1768
  #
1769
- # === Scopes
1769
+ # ==== Scopes
1770
1770
  #
1771
1771
  # You can pass a second argument +scope+ as a callable (i.e. proc or
1772
1772
  # lambda) to retrieve a specific record or customize the generated query
@@ -1777,7 +1777,7 @@ module ActiveRecord
1777
1777
  # belongs_to :user, -> { joins(:friends) }
1778
1778
  # belongs_to :level, ->(game) { where("game_level > ?", game.current_level) }
1779
1779
  #
1780
- # === Options
1780
+ # ==== Options
1781
1781
  #
1782
1782
  # The declaration can also include an +options+ hash to specialize the behavior of the association.
1783
1783
  #
@@ -1964,7 +1964,7 @@ module ActiveRecord
1964
1964
  # Returns a Relation of all of the associated objects, forcing a database read.
1965
1965
  # An empty Relation is returned if none are found.
1966
1966
  #
1967
- # === Example
1967
+ # ==== Example
1968
1968
  #
1969
1969
  # class Developer < ActiveRecord::Base
1970
1970
  # has_and_belongs_to_many :projects
@@ -1993,7 +1993,7 @@ module ActiveRecord
1993
1993
  #
1994
1994
  # The declaration may include an +options+ hash to specialize the behavior of the association.
1995
1995
  #
1996
- # === Scopes
1996
+ # ==== Scopes
1997
1997
  #
1998
1998
  # You can pass a second argument +scope+ as a callable (i.e. proc or
1999
1999
  # lambda) to retrieve a specific set of records or customize the generated
@@ -2005,7 +2005,7 @@ module ActiveRecord
2005
2005
  # where("default_category = ?", post.default_category)
2006
2006
  # }
2007
2007
  #
2008
- # === Extensions
2008
+ # ==== Extensions
2009
2009
  #
2010
2010
  # The +extension+ argument allows you to pass a block into a
2011
2011
  # has_and_belongs_to_many association. This is useful for adding new
@@ -2020,7 +2020,7 @@ module ActiveRecord
2020
2020
  # end
2021
2021
  # end
2022
2022
  #
2023
- # === Options
2023
+ # ==== Options
2024
2024
  #
2025
2025
  # [+:class_name+]
2026
2026
  # Specify the class name of the association. Use it only if that name can't be inferred
@@ -76,11 +76,13 @@ module ActiveRecord
76
76
  #
77
77
  # ==== Options
78
78
  #
79
- # +from+ When passed, this method will return false unless the original
80
- # value is equal to the given option
79
+ # [+from+]
80
+ # When specified, this method will return false unless the original
81
+ # value is equal to the given value.
81
82
  #
82
- # +to+ When passed, this method will return false unless the value was
83
- # changed to the given value
83
+ # [+to+]
84
+ # When specified, this method will return false unless the value will be
85
+ # changed to the given value.
84
86
  def saved_change_to_attribute?(attr_name, **options)
85
87
  mutations_before_last_save.changed?(attr_name.to_s, **options)
86
88
  end
@@ -126,11 +128,13 @@ module ActiveRecord
126
128
  #
127
129
  # ==== Options
128
130
  #
129
- # +from+ When passed, this method will return false unless the original
130
- # value is equal to the given option
131
+ # [+from+]
132
+ # When specified, this method will return false unless the original
133
+ # value is equal to the given value.
131
134
  #
132
- # +to+ When passed, this method will return false unless the value will be
133
- # changed to the given value
135
+ # [+to+]
136
+ # When specified, this method will return false unless the value will be
137
+ # changed to the given value.
134
138
  def will_save_change_to_attribute?(attr_name, **options)
135
139
  mutations_from_database.changed?(attr_name.to_s, **options)
136
140
  end
@@ -241,8 +241,8 @@ module ActiveRecord
241
241
  #
242
242
  # config.active_record.run_after_transaction_callbacks_in_order_defined = false
243
243
  #
244
- # If +true+ (the default from \Rails 7.1), callbacks are executed in the order they
245
- # are defined, just like the example above. If +false+, the order is reversed, so
244
+ # When set to +true+ (the default from \Rails 7.1), callbacks are executed in the order they
245
+ # are defined, just like the example above. When set to +false+, the order is reversed, so
246
246
  # +do_something_else+ is executed before +log_children+.
247
247
  #
248
248
  # == \Transactions
@@ -189,8 +189,10 @@ module ActiveRecord
189
189
  def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
190
190
  sql, binds = to_sql_and_binds(arel, binds)
191
191
  value = exec_insert(sql, name, binds, pk, sequence_name, returning: returning)
192
- return id_value if id_value
193
- returning.nil? ? last_inserted_id(value) : returning_column_values(value)
192
+
193
+ return returning_column_values(value) unless returning.nil?
194
+
195
+ id_value || last_inserted_id(value)
194
196
  end
195
197
  alias create insert
196
198
 
@@ -970,7 +970,11 @@ module ActiveRecord
970
970
  # If +allow_retry+ is true, a connection-related exception will
971
971
  # cause an automatic reconnect and re-run of the block, up to
972
972
  # the connection's configured +connection_retries+ setting
973
- # and the configured +retry_deadline+ limit.
973
+ # and the configured +retry_deadline+ limit. (Note that when
974
+ # +allow_retry+ is true, it's possible to return without having marked
975
+ # the connection as verified. If the block is guaranteed to exercise the
976
+ # connection, consider calling `verified!` to avoid needless
977
+ # verification queries in subsequent calls.)
974
978
  #
975
979
  # If +materialize_transactions+ is false, the block will be run without
976
980
  # ensuring virtual transactions have been materialized in the DB
@@ -1021,9 +1025,7 @@ module ActiveRecord
1021
1025
  end
1022
1026
 
1023
1027
  begin
1024
- result = yield @raw_connection
1025
- @verified = true
1026
- result
1028
+ yield @raw_connection
1027
1029
  rescue => original_exception
1028
1030
  translated_exception = translate_exception_class(original_exception, nil, nil)
1029
1031
  invalidate_transaction(translated_exception)
@@ -1058,6 +1060,13 @@ module ActiveRecord
1058
1060
  end
1059
1061
  end
1060
1062
 
1063
+ # Mark the connection as verified. Call this inside a
1064
+ # `with_raw_connection` block only when the block is guaranteed to
1065
+ # exercise the raw connection.
1066
+ def verified!
1067
+ @verified = true
1068
+ end
1069
+
1061
1070
  def retryable_connection_error?(exception)
1062
1071
  exception.is_a?(ConnectionNotEstablished) || exception.is_a?(ConnectionFailed)
1063
1072
  end
@@ -98,6 +98,7 @@ module ActiveRecord
98
98
  with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
99
99
  sync_timezone_changes(conn)
100
100
  result = conn.query(sql)
101
+ verified!
101
102
  handle_warnings(sql)
102
103
  result
103
104
  end
@@ -126,6 +127,8 @@ module ActiveRecord
126
127
  result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
127
128
  stmt.execute(*type_casted_binds)
128
129
  end
130
+ verified!
131
+ result
129
132
  rescue ::Mysql2::Error => e
130
133
  if cache_stmt
131
134
  @statements.delete(sql)
@@ -16,7 +16,9 @@ module ActiveRecord
16
16
 
17
17
  log(sql, name) do
18
18
  with_raw_connection do |conn|
19
- conn.async_exec(sql).map_types!(@type_map_for_results).values
19
+ result = conn.async_exec(sql).map_types!(@type_map_for_results).values
20
+ verified!
21
+ result
20
22
  end
21
23
  end
22
24
  end
@@ -51,6 +53,7 @@ module ActiveRecord
51
53
  log(sql, name, async: async) do
52
54
  with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
53
55
  result = conn.async_exec(sql)
56
+ verified!
54
57
  handle_warnings(result)
55
58
  result
56
59
  end
@@ -27,9 +27,10 @@ module ActiveRecord
27
27
  value = value.sub(/^\((.+)\)$/, '-\1') # (4)
28
28
  case value
29
29
  when /^-?\D*+[\d,]+\.\d{2}$/ # (1)
30
- value.gsub!(/[^-\d.]/, "")
30
+ value.delete!("^-0-9.")
31
31
  when /^-?\D*+[\d.]+,\d{2}$/ # (2)
32
- value.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
32
+ value.delete!("^-0-9,")
33
+ value.tr!(",", ".")
33
34
  end
34
35
 
35
36
  super(value)
@@ -341,7 +341,7 @@ module ActiveRecord
341
341
  JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
342
342
  WHERE t.oid = #{quote(quote_table_name(table))}::regclass
343
343
  AND cons.contype = 'p'
344
- AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
344
+ AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate|gen_random_uuid'
345
345
  SQL
346
346
  end
347
347
 
@@ -385,11 +385,18 @@ module ActiveRecord
385
385
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
386
386
  pk, seq = pk_and_sequence_for(new_name)
387
387
  if pk
388
- idx = "#{table_name}_pkey"
389
- new_idx = "#{new_name}_pkey"
388
+ # PostgreSQL automatically creates an index for PRIMARY KEY with name consisting of
389
+ # truncated table name and "_pkey" suffix fitting into max_identifier_length number of characters.
390
+ max_pkey_prefix = max_identifier_length - "_pkey".size
391
+ idx = "#{table_name[0, max_pkey_prefix]}_pkey"
392
+ new_idx = "#{new_name[0, max_pkey_prefix]}_pkey"
390
393
  execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
391
- if seq && seq.identifier == "#{table_name}_#{pk}_seq"
392
- new_seq = "#{new_name}_#{pk}_seq"
394
+
395
+ # PostgreSQL automatically creates a sequence for PRIMARY KEY with name consisting of
396
+ # truncated table name and "#{primary_key}_seq" suffix fitting into max_identifier_length number of characters.
397
+ max_seq_prefix = max_identifier_length - "_#{pk}_seq".size
398
+ if seq && seq.identifier == "#{table_name[0, max_seq_prefix]}_#{pk}_seq"
399
+ new_seq = "#{new_name[0, max_seq_prefix]}_#{pk}_seq"
393
400
  execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
394
401
  end
395
402
  end
@@ -108,10 +108,11 @@ module ActiveRecord
108
108
  # but significantly increases the risk of data loss if the database
109
109
  # crashes. As a result, this should not be used in production
110
110
  # environments. If you would like all created tables to be unlogged in
111
- # the test environment you can add the following line to your test.rb
112
- # file:
111
+ # the test environment you can add the following to your test.rb file:
113
112
  #
114
- # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
113
+ # ActiveSupport.on_load(:active_record_postgresqladapter) do
114
+ # self.create_unlogged_tables = true
115
+ # end
115
116
  class_attribute :create_unlogged_tables, default: false
116
117
 
117
118
  ##
@@ -277,6 +278,10 @@ module ActiveRecord
277
278
  database_version >= 12_00_00 # >= 12.0
278
279
  end
279
280
 
281
+ def supports_identity_columns? # :nodoc:
282
+ database_version >= 10_00_00 # >= 10.0
283
+ end
284
+
280
285
  def supports_nulls_not_distinct?
281
286
  database_version >= 15_00_00 # >= 15.0
282
287
  end
@@ -535,7 +540,7 @@ module ActiveRecord
535
540
  END
536
541
  $$;
537
542
  SQL
538
- internal_exec_query(query)
543
+ internal_exec_query(query).tap { reload_type_map }
539
544
  end
540
545
 
541
546
  # Drops an enum type.
@@ -551,7 +556,7 @@ module ActiveRecord
551
556
  query = <<~SQL
552
557
  DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
553
558
  SQL
554
- internal_exec_query(query)
559
+ internal_exec_query(query).tap { reload_type_map }
555
560
  end
556
561
 
557
562
  # Rename an existing enum type to something else.
@@ -596,14 +601,6 @@ module ActiveRecord
596
601
  @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
597
602
  end
598
603
 
599
- # Returns the maximum length of a table name.
600
- def table_name_length
601
- # PostgreSQL automatically creates an index for PRIMARY KEY with name consisting of
602
- # truncated table name and "_pkey" suffix fitting into max_identifier_length number of characters.
603
- # We allow smaller table names to be able to correctly rename this index when renaming the table.
604
- max_identifier_length - "_pkey".length
605
- end
606
-
607
604
  # Set the authorized user for this session
608
605
  def session_auth=(user)
609
606
  clear_cache!
@@ -894,7 +891,9 @@ module ActiveRecord
894
891
  type_casted_binds = type_casted_binds(binds)
895
892
  log(sql, name, binds, type_casted_binds, async: async) do
896
893
  with_raw_connection do |conn|
897
- conn.exec_params(sql, type_casted_binds)
894
+ result = conn.exec_params(sql, type_casted_binds)
895
+ verified!
896
+ result
898
897
  end
899
898
  end
900
899
  end
@@ -904,12 +903,14 @@ module ActiveRecord
904
903
 
905
904
  update_typemap_for_default_timezone
906
905
 
907
- stmt_key = prepare_statement(sql, binds)
908
- type_casted_binds = type_casted_binds(binds)
909
-
910
906
  with_raw_connection do |conn|
907
+ stmt_key = prepare_statement(sql, binds, conn)
908
+ type_casted_binds = type_casted_binds(binds)
909
+
911
910
  log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
912
- conn.exec_prepared(stmt_key, type_casted_binds)
911
+ result = conn.exec_prepared(stmt_key, type_casted_binds)
912
+ verified!
913
+ result
913
914
  end
914
915
  end
915
916
  rescue ActiveRecord::StatementInvalid => e
@@ -957,22 +958,20 @@ module ActiveRecord
957
958
 
958
959
  # Prepare the statement if it hasn't been prepared, return
959
960
  # the statement key.
960
- def prepare_statement(sql, binds)
961
- with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
962
- sql_key = sql_key(sql)
963
- unless @statements.key? sql_key
964
- nextkey = @statements.next_key
965
- begin
966
- conn.prepare nextkey, sql
967
- rescue => e
968
- raise translate_exception_class(e, sql, binds)
969
- end
970
- # Clear the queue
971
- conn.get_last_result
972
- @statements[sql_key] = nextkey
961
+ def prepare_statement(sql, binds, conn)
962
+ sql_key = sql_key(sql)
963
+ unless @statements.key? sql_key
964
+ nextkey = @statements.next_key
965
+ begin
966
+ conn.prepare nextkey, sql
967
+ rescue => e
968
+ raise translate_exception_class(e, sql, binds)
973
969
  end
974
- @statements[sql_key]
970
+ # Clear the queue
971
+ conn.get_last_result
972
+ @statements[sql_key] = nextkey
975
973
  end
974
+ @statements[sql_key]
976
975
  end
977
976
 
978
977
  # Connects to a PostgreSQL server and sets up the adapter depending on the
@@ -1076,7 +1075,7 @@ module ActiveRecord
1076
1075
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
1077
1076
  pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
1078
1077
  c.collname, col_description(a.attrelid, a.attnum) AS comment,
1079
- a.attidentity AS identity,
1078
+ #{supports_identity_columns? ? 'attidentity' : quote('')} AS identity,
1080
1079
  #{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
1081
1080
  FROM pg_attribute a
1082
1081
  LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
@@ -50,6 +50,7 @@ module ActiveRecord
50
50
  stmt.bind_params(type_casted_binds)
51
51
  records = stmt.to_a
52
52
  end
53
+ verified!
53
54
 
54
55
  build_result(columns: cols, rows: records)
55
56
  end
@@ -76,7 +77,9 @@ module ActiveRecord
76
77
  def begin_db_transaction # :nodoc:
77
78
  log("begin transaction", "TRANSACTION") do
78
79
  with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
79
- conn.transaction
80
+ result = conn.transaction
81
+ verified!
82
+ result
80
83
  end
81
84
  end
82
85
  end
@@ -112,7 +115,9 @@ module ActiveRecord
112
115
  def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: false)
113
116
  log(sql, name, async: async) do
114
117
  with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
115
- conn.execute(sql)
118
+ result = conn.execute(sql)
119
+ verified!
120
+ result
116
121
  end
117
122
  end
118
123
  end
@@ -133,7 +138,9 @@ module ActiveRecord
133
138
 
134
139
  log(sql, name) do
135
140
  with_raw_connection do |conn|
136
- conn.execute_batch2(sql)
141
+ result = conn.execute_batch2(sql)
142
+ verified!
143
+ result
137
144
  end
138
145
  end
139
146
  end
@@ -46,6 +46,7 @@ module ActiveRecord
46
46
  with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
47
47
  sync_timezone_changes(conn)
48
48
  result = conn.query(sql)
49
+ verified!
49
50
  handle_warnings(sql)
50
51
  result
51
52
  end
@@ -15,9 +15,10 @@ module ActiveRecord
15
15
  ##
16
16
  # :singleton-method:
17
17
  #
18
- # Accepts a logger conforming to the interface of Log4r which is then
19
- # passed on to any new database connections made and which can be
20
- # retrieved on both a class and instance level by calling +logger+.
18
+ # Accepts a logger conforming to the interface of Log4r or the default
19
+ # Ruby +Logger+ class, which is then passed on to any new database
20
+ # connections made. You can retrieve this logger by calling +logger+ on
21
+ # either an Active Record model class or an Active Record model instance.
21
22
  class_attribute :logger, instance_writer: false
22
23
 
23
24
  class_attribute :_destroy_association_async_job, instance_accessor: false, default: "ActiveRecord::DestroyAssociationAsyncJob"
@@ -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
@@ -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}'. 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
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  module VERSION
10
10
  MAJOR = 7
11
11
  MINOR = 1
12
- TINY = 1
12
+ TINY = 2
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
@@ -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:
@@ -768,9 +769,11 @@ module ActiveRecord
768
769
  def pending_migrations
769
770
  pending_migrations = []
770
771
 
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
772
+ ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
773
+ ActiveRecord::PendingMigrationConnection.establish_temporary_connection(db_config) do |conn|
774
+ if pending = conn.migration_context.open.pending_migrations
775
+ pending_migrations << pending
776
+ end
774
777
  end
775
778
  end
776
779
 
@@ -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
@@ -467,7 +469,7 @@ module ActiveRecord
467
469
  end
468
470
 
469
471
  # Returns the column object for the named attribute.
470
- # Returns an +ActiveRecord::ConnectionAdapters::NullColumn+ if the
472
+ # Returns an ActiveRecord::ConnectionAdapters::NullColumn if the
471
473
  # named attribute does not exist.
472
474
  #
473
475
  # class Person < ActiveRecord::Base
@@ -627,8 +629,6 @@ module ActiveRecord
627
629
  )
628
630
  alias_attribute :id_value, :id if name == "id"
629
631
  end
630
-
631
- super
632
632
  end
633
633
 
634
634
  # Guesses the table name, but does not decorate it with prefix and suffix information.
@@ -307,7 +307,7 @@ module ActiveRecord
307
307
  # [:allow_destroy]
308
308
  # If true, destroys any members from the attributes hash with a
309
309
  # <tt>_destroy</tt> key and a value that evaluates to +true+
310
- # (e.g. 1, '1', true, or 'true'). This option is off by default.
310
+ # (e.g. 1, '1', true, or 'true'). This option is false by default.
311
311
  # [:reject_if]
312
312
  # Allows you to specify a Proc or a Symbol pointing to a method
313
313
  # that checks whether a record should be built for a certain attribute
@@ -332,11 +332,11 @@ module ActiveRecord
332
332
  # nested attributes are going to be used when an associated record already
333
333
  # exists. In general, an existing record may either be updated with the
334
334
  # new set of attribute values or be replaced by a wholly new record
335
- # containing those values. By default the +:update_only+ option is +false+
335
+ # containing those values. By default the +:update_only+ option is false
336
336
  # and the nested attributes are used to update the existing record only
337
337
  # if they include the record's <tt>:id</tt> value. Otherwise a new
338
338
  # record will be instantiated and used to replace the existing one.
339
- # However if the +:update_only+ option is +true+, the nested attributes
339
+ # However if the +:update_only+ option is true, the nested attributes
340
340
  # are used to update the record's attributes always, regardless of
341
341
  # whether the <tt>:id</tt> is present. The option is ignored for collection
342
342
  # associations.
@@ -49,6 +49,14 @@ module ActiveRecord # :nodoc:
49
49
  # By default, the normalization will not be applied to +nil+ values. This
50
50
  # behavior can be changed with the +:apply_to_nil+ option.
51
51
  #
52
+ # Be aware that if your app was created before Rails 7.1, and your app
53
+ # marshals instances of the targeted model (for example, when caching),
54
+ # then you should set ActiveRecord.marshalling_format_version to +7.1+ or
55
+ # higher via either <tt>config.load_defaults 7.1</tt> or
56
+ # <tt>config.active_record.marshalling_format_version = 7.1</tt>.
57
+ # Otherwise, +Marshal+ may attempt to serialize the normalization +Proc+
58
+ # and raise +TypeError+.
59
+ #
52
60
  # ==== Options
53
61
  #
54
62
  # * +:with+ - Any callable object that accepts the attribute's value as
@@ -31,7 +31,7 @@ module ActiveRecord
31
31
  # Returns a new +ActiveRecord::Promise+ that will apply the passed block
32
32
  # when the value is accessed:
33
33
  #
34
- # Post.async_pluck(:title).then { |title| title.upcase }.value
34
+ # Post.async_pick(:title).then { |title| title.upcase }.value
35
35
  # # => "POST TITLE"
36
36
  def then(&block)
37
37
  Promise.new(@future_result, @block ? @block >> block : block)
@@ -195,7 +195,7 @@ db_namespace = namespace :db do
195
195
 
196
196
  namespace :up do
197
197
  ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
198
- desc 'Run the "up" on #{name} database for a given migration VERSION.'
198
+ desc "Run the \"up\" on #{name} database for a given migration VERSION."
199
199
  task name => :load_config do
200
200
  raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
201
201
 
@@ -204,7 +204,7 @@ db_namespace = namespace :db do
204
204
  conn.migration_context.run(:up, ActiveRecord::Tasks::DatabaseTasks.target_version)
205
205
  end
206
206
 
207
- db_namespace["_dump"].invoke
207
+ db_namespace["_dump:#{name}"].invoke
208
208
  end
209
209
  end
210
210
  end
@@ -226,7 +226,7 @@ db_namespace = namespace :db do
226
226
 
227
227
  namespace :down do
228
228
  ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
229
- desc 'Run the "down" on #{name} database for a given migration VERSION.'
229
+ desc "Run the \"down\" on #{name} database for a given migration VERSION."
230
230
  task name => :load_config do
231
231
  raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
232
232
 
@@ -235,7 +235,7 @@ db_namespace = namespace :db do
235
235
  conn.migration_context.run(:down, ActiveRecord::Tasks::DatabaseTasks.target_version)
236
236
  end
237
237
 
238
- db_namespace["_dump"].invoke
238
+ db_namespace["_dump:#{name}"].invoke
239
239
  end
240
240
  end
241
241
  end
@@ -269,7 +269,7 @@ db_namespace = namespace :db do
269
269
  conn.migration_context.rollback(step)
270
270
  end
271
271
 
272
- db_namespace["_dump"].invoke
272
+ db_namespace["_dump:#{name}"].invoke
273
273
  end
274
274
  end
275
275
  end
@@ -382,6 +382,7 @@ module ActiveRecord
382
382
  @klass = options[:anonymous_class]
383
383
  @plural_name = active_record.pluralize_table_names ?
384
384
  name.to_s.pluralize : name.to_s
385
+ validate_reflection!
385
386
  end
386
387
 
387
388
  def autosave=(autosave)
@@ -433,6 +434,17 @@ module ActiveRecord
433
434
  def derive_class_name
434
435
  name.to_s.camelize
435
436
  end
437
+
438
+ def validate_reflection!
439
+ return unless options[:foreign_key].is_a?(Array)
440
+
441
+ message = <<~MSG.squish
442
+ Passing #{options[:foreign_key]} array to :foreign_key option
443
+ on the #{active_record}##{name} association is not supported.
444
+ Use the query_constraints: #{options[:foreign_key]} option instead to represent a composite foreign key.
445
+ MSG
446
+ raise ArgumentError, message
447
+ end
436
448
  end
437
449
 
438
450
  # Holds all the metadata about an aggregation as it was specified in the
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  # number is inserted in to the schema migrations table so it doesn't need
7
7
  # to be executed the next time.
8
8
  class SchemaMigration # :nodoc:
9
- class NullSchemaMigration
9
+ class NullSchemaMigration # :nodoc:
10
10
  end
11
11
 
12
12
  attr_reader :connection, :arel_table
@@ -48,7 +48,7 @@ module Arel # :nodoc: all
48
48
  end
49
49
 
50
50
  def proc_for_binds
51
- -> value { ActiveModel::Attribute.with_cast_value(attribute.name, value, attribute.type_caster) }
51
+ -> value { ActiveModel::Attribute.with_cast_value(attribute.name, value, ActiveModel::Type.default_value) }
52
52
  end
53
53
 
54
54
  def fetch_attribute(&block)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.1.1
4
+ version: 7.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-11 00:00:00.000000000 Z
11
+ date: 2023-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 7.1.1
19
+ version: 7.1.2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 7.1.1
26
+ version: 7.1.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activemodel
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 7.1.1
33
+ version: 7.1.2
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 7.1.1
40
+ version: 7.1.2
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: timeout
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -279,6 +279,7 @@ files:
279
279
  - lib/active_record/migration/default_strategy.rb
280
280
  - lib/active_record/migration/execution_strategy.rb
281
281
  - lib/active_record/migration/join_table.rb
282
+ - lib/active_record/migration/pending_migration_connection.rb
282
283
  - lib/active_record/model_schema.rb
283
284
  - lib/active_record/nested_attributes.rb
284
285
  - lib/active_record/no_touching.rb
@@ -469,10 +470,10 @@ licenses:
469
470
  - MIT
470
471
  metadata:
471
472
  bug_tracker_uri: https://github.com/rails/rails/issues
472
- changelog_uri: https://github.com/rails/rails/blob/v7.1.1/activerecord/CHANGELOG.md
473
- documentation_uri: https://api.rubyonrails.org/v7.1.1/
473
+ changelog_uri: https://github.com/rails/rails/blob/v7.1.2/activerecord/CHANGELOG.md
474
+ documentation_uri: https://api.rubyonrails.org/v7.1.2/
474
475
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
475
- source_code_uri: https://github.com/rails/rails/tree/v7.1.1/activerecord
476
+ source_code_uri: https://github.com/rails/rails/tree/v7.1.2/activerecord
476
477
  rubygems_mfa_required: 'true'
477
478
  post_install_message:
478
479
  rdoc_options: