activerecord 7.1.1 → 7.1.2

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