activerecord 8.0.0.beta1 → 8.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +102 -2
  3. data/lib/active_record/association_relation.rb +1 -0
  4. data/lib/active_record/associations/association.rb +9 -5
  5. data/lib/active_record/associations/has_many_through_association.rb +7 -1
  6. data/lib/active_record/associations.rb +28 -16
  7. data/lib/active_record/attribute_methods/primary_key.rb +2 -7
  8. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
  9. data/lib/active_record/callbacks.rb +1 -1
  10. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +26 -8
  11. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  12. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +6 -1
  13. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +8 -2
  14. data/lib/active_record/connection_adapters/abstract_adapter.rb +0 -1
  15. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +8 -4
  16. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  17. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  18. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -10
  19. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +6 -12
  20. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +2 -1
  21. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +11 -10
  22. data/lib/active_record/connection_adapters/postgresql_adapter.rb +12 -10
  23. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +1 -1
  24. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +0 -2
  25. data/lib/active_record/connection_adapters.rb +0 -56
  26. data/lib/active_record/core.rb +43 -14
  27. data/lib/active_record/database_configurations/database_config.rb +4 -0
  28. data/lib/active_record/database_configurations/hash_config.rb +8 -0
  29. data/lib/active_record/enum.rb +9 -22
  30. data/lib/active_record/fixtures.rb +0 -1
  31. data/lib/active_record/gem_version.rb +1 -1
  32. data/lib/active_record/locking/optimistic.rb +1 -1
  33. data/lib/active_record/log_subscriber.rb +5 -11
  34. data/lib/active_record/marshalling.rb +4 -1
  35. data/lib/active_record/migration/command_recorder.rb +5 -5
  36. data/lib/active_record/migration.rb +0 -5
  37. data/lib/active_record/model_schema.rb +2 -3
  38. data/lib/active_record/query_cache.rb +0 -4
  39. data/lib/active_record/query_logs.rb +5 -11
  40. data/lib/active_record/querying.rb +2 -2
  41. data/lib/active_record/railtie.rb +2 -25
  42. data/lib/active_record/railties/databases.rake +2 -17
  43. data/lib/active_record/reflection.rb +14 -19
  44. data/lib/active_record/relation/calculations.rb +24 -28
  45. data/lib/active_record/relation/predicate_builder.rb +8 -0
  46. data/lib/active_record/relation/query_methods.rb +76 -38
  47. data/lib/active_record/relation.rb +8 -1
  48. data/lib/active_record/result.rb +10 -9
  49. data/lib/active_record/table_metadata.rb +1 -3
  50. data/lib/active_record/tasks/database_tasks.rb +26 -34
  51. data/lib/active_record/testing/query_assertions.rb +2 -2
  52. data/lib/active_record.rb +0 -45
  53. data/lib/arel/table.rb +3 -7
  54. data/lib/arel/visitors/sqlite.rb +25 -0
  55. metadata +9 -10
  56. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e4bbfe90a91599557f1804b34ccb34ca5db162d8ffb8c61a7bf87c3db518b16
4
- data.tar.gz: a84a3f17fe43e75642b2d293df3916062ef9fcc77cc6485f4b8e9183bb90f234
3
+ metadata.gz: 5b6b5ce7951d63889da24f22f9635d27371d6fc733e400d1510cd0ffe27f669e
4
+ data.tar.gz: 68f851f76e82b3969bc3a7125b3700ce3623ed7cf1717064f26202b4a5c062cd
5
5
  SHA512:
6
- metadata.gz: 8e0c9d489c0baade86104d8f1f2be0595c1eb3b2a9f1f02328cc3de9d941c8d46302daf74547b2a2c508bd03f418312b0557e1274b14ada1d6bb2f2ae3fc17d5
7
- data.tar.gz: '04599d4f24baede9df806c5b0abc1627bb468f365581acd71ee30ef058faeadd3de22074f4752727633df01b8578258cffdab7e709a7e849e332af2de08b3f2d'
6
+ metadata.gz: ef1e91d706ec3c79e71b2e59780a831a63fe82ab45a8e123748c5d073bc120c3d9506d3e8b43b68aa77dc90a5f564b8ef45cb1d5abc19e008127757966bf32dc
7
+ data.tar.gz: 35e7644e3037334b724042c5f26539c2489c579fede36d461e3661cf04ca79b5c8ca0b3e3c29d8edfc9eab3de15af0b8dcee33a7d88f2f4985c46acfce26a324
data/CHANGELOG.md CHANGED
@@ -1,3 +1,103 @@
1
+ ## Rails 8.0.0.rc2 (October 30, 2024) ##
2
+
3
+ * NULLS NOT DISTINCT works with UNIQUE CONSTRAINT as well as UNIQUE INDEX.
4
+
5
+ *Ryuta Kamizono*
6
+
7
+ * The `db:prepare` task no longer loads seeds when a non-primary database is created.
8
+
9
+ Previously, the `db:prepare` task would load seeds whenever a new database
10
+ is created, leading to potential loss of data if a database is added to an
11
+ existing environment.
12
+
13
+ Introduces a new database config property `seeds` to control whether seeds
14
+ are loaded during `db:prepare` which defaults to `true` for primary database
15
+ configs and `false` otherwise.
16
+
17
+ Fixes #53348.
18
+
19
+ *Mike Dalessio*
20
+
21
+ * `PG::UnableToSend: no connection to the server` is now retryable as a connection-related exception
22
+
23
+ *Kazuma Watanabe*
24
+
25
+ * Fix strict loading propagation even if statement cache is not used.
26
+
27
+ *Ryuta Kamizono*
28
+
29
+ * Allow `rename_enum` accepts two from/to name arguments as `rename_table` does so.
30
+
31
+ *Ryuta Kamizono*
32
+
33
+
34
+ ## Rails 8.0.0.rc1 (October 19, 2024) ##
35
+
36
+ * Remove deprecated support to setting `ENV["SCHEMA_CACHE"]`.
37
+
38
+ *Rafael Mendonça França*
39
+
40
+ * Remove deprecated support to passing a database name to `cache_dump_filename`.
41
+
42
+ *Rafael Mendonça França*
43
+
44
+ * Remove deprecated `ActiveRecord::ConnectionAdapters::ConnectionPool#connection`.
45
+
46
+ *Rafael Mendonça França*
47
+
48
+ * Remove deprecated `config.active_record.sqlite3_deprecated_warning`.
49
+
50
+ *Rafael Mendonça França*
51
+
52
+ * Remove deprecated `config.active_record.warn_on_records_fetched_greater_than`.
53
+
54
+ *Rafael Mendonça França*
55
+
56
+ * Remove deprecated support for defining `enum` with keyword arguments.
57
+
58
+ *Rafael Mendonça França*
59
+
60
+ * Remove deprecated support to finding database adapters that aren't registered to Active Record.
61
+
62
+ *Rafael Mendonça França*
63
+
64
+ * Remove deprecated `config.active_record.allow_deprecated_singular_associations_name`.
65
+
66
+ *Rafael Mendonça França*
67
+
68
+ * Remove deprecated `config.active_record.commit_transaction_on_non_local_return`.
69
+
70
+ *Rafael Mendonça França*
71
+
72
+ * Fix incorrect SQL query when passing an empty hash to `ActiveRecord::Base.insert`.
73
+
74
+ *David Stosik*
75
+
76
+ * Allow to save records with polymorphic join tables that have `inverse_of`
77
+ specified.
78
+
79
+ *Markus Doits*
80
+
81
+ * Fix association scopes applying on the incorrect join when using a polymorphic `has_many through:`.
82
+
83
+ *Joshua Young*
84
+
85
+ * Allow `ActiveRecord::Base#pluck` to accept hash arguments with symbol and string values.
86
+
87
+ ```ruby
88
+ Post.joins(:comments).pluck(:id, comments: :id)
89
+ Post.joins(:comments).pluck("id", "comments" => "id")
90
+ ```
91
+
92
+ *Joshua Young*
93
+
94
+ * Make Float distinguish between `float4` and `float8` in PostgreSQL.
95
+
96
+ Fixes #52742
97
+
98
+ *Ryota Kitazawa*, *Takayuki Nagatomi*
99
+
100
+
1
101
  ## Rails 8.0.0.beta1 (September 26, 2024) ##
2
102
 
3
103
  * Allow `drop_table` to accept an array of table names.
@@ -15,9 +115,9 @@
15
115
 
16
116
  *Ariel Rzezak*
17
117
 
18
- * When running `db:migrate` on a fresh database, load the database schema before running migrations.
118
+ * When running `db:migrate` on a fresh database, load the databases schemas before running migrations.
19
119
 
20
- *Andrew Novoselac*
120
+ *Andrew Novoselac*, *Marek Kasztelnik*
21
121
 
22
122
  * Fix an issue where `.left_outer_joins` used with multiple associations that have
23
123
  the same child association but different parents does not join all parents.
@@ -43,6 +43,7 @@ module ActiveRecord
43
43
  def exec_queries
44
44
  super do |record|
45
45
  @association.set_inverse_instance_from_queries(record)
46
+ @association.set_strict_loading(record)
46
47
  yield record if block_given?
47
48
  end
48
49
  end
@@ -120,6 +120,14 @@ module ActiveRecord
120
120
  @association_scope = nil
121
121
  end
122
122
 
123
+ def set_strict_loading(record)
124
+ if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many
125
+ record.strict_loading!
126
+ else
127
+ record.strict_loading!(false, mode: owner.strict_loading_mode)
128
+ end
129
+ end
130
+
123
131
  # Set the inverse association, if possible
124
132
  def set_inverse_instance(record)
125
133
  if inverse = inverse_association_for(record)
@@ -260,11 +268,7 @@ module ActiveRecord
260
268
  klass.with_connection do |c|
261
269
  sc.execute(binds, c, async: async) do |record|
262
270
  set_inverse_instance(record)
263
- if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many
264
- record.strict_loading!
265
- else
266
- record.strict_loading!(false, mode: owner.strict_loading_mode)
267
- end
271
+ set_strict_loading(record)
268
272
  end
269
273
  end
270
274
  end
@@ -93,7 +93,13 @@ module ActiveRecord
93
93
  @through_scope = scope
94
94
  record = super
95
95
 
96
- inverse = source_reflection.inverse_of
96
+ inverse =
97
+ if source_reflection.polymorphic?
98
+ source_reflection.polymorphic_inverse_of(record.class)
99
+ else
100
+ source_reflection.inverse_of
101
+ end
102
+
97
103
  if inverse
98
104
  if inverse.collection?
99
105
  record.send(inverse.name) << build_through_record(record)
@@ -559,7 +559,7 @@ module ActiveRecord
559
559
  # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
560
560
  # @group.avatars.delete(@group.avatars.last) # so would this
561
561
  #
562
- # == Setting Inverses
562
+ # === Setting Inverses
563
563
  #
564
564
  # If you are using a #belongs_to on the join model, it is a good idea to set the
565
565
  # <tt>:inverse_of</tt> option on the #belongs_to, which will mean that the following example
@@ -1221,8 +1221,11 @@ module ActiveRecord
1221
1221
  # If you are going to modify the association (rather than just read from it), then it is
1222
1222
  # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
1223
1223
  # join model. This allows associated records to be built which will automatically create
1224
- # the appropriate join model records when they are saved. (See the 'Association Join Models'
1225
- # and 'Setting Inverses' sections above.)
1224
+ # the appropriate join model records when they are saved. See
1225
+ # {Association Join Models}[rdoc-ref:Associations::ClassMethods@Association+Join+Models]
1226
+ # and {Setting Inverses}[rdoc-ref:Associations::ClassMethods@Setting+Inverses] for
1227
+ # more detail.
1228
+ #
1226
1229
  # [+:disable_joins+]
1227
1230
  # Specifies whether joins should be skipped for an association. If set to true, two or more queries
1228
1231
  # will be generated. Note that in some cases, if order or limit is applied, it will be done in-memory
@@ -1251,7 +1254,8 @@ module ActiveRecord
1251
1254
  # [+:inverse_of+]
1252
1255
  # Specifies the name of the #belongs_to association on the associated object
1253
1256
  # that is the inverse of this #has_many association.
1254
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1257
+ # See {Bi-directional associations}[rdoc-ref:Associations::ClassMethods@Bi-directional+associations]
1258
+ # for more detail.
1255
1259
  # [+:extend+]
1256
1260
  # Specifies a module or array of modules that will be extended into the association object returned.
1257
1261
  # Useful for defining methods on associations, especially when they should be shared between multiple
@@ -1300,10 +1304,12 @@ module ActiveRecord
1300
1304
  Reflection.add_reflection self, name, reflection
1301
1305
  end
1302
1306
 
1303
- # Specifies a one-to-one association with another class. This method should only be used
1304
- # if the other class contains the foreign key. If the current class contains the foreign key,
1305
- # then you should use #belongs_to instead. See also ActiveRecord::Associations::ClassMethods's overview
1306
- # on when to use #has_one and when to use #belongs_to.
1307
+ # Specifies a one-to-one association with another class. This method
1308
+ # should only be used if the other class contains the foreign key. If
1309
+ # the current class contains the foreign key, then you should use
1310
+ # #belongs_to instead. See {Is it a belongs_to or has_one
1311
+ # association?}[rdoc-ref:Associations::ClassMethods@Is+it+a+-23belongs_to+or+-23has_one+association-3F]
1312
+ # for more detail on when to use #has_one and when to use #belongs_to.
1307
1313
  #
1308
1314
  # The following methods for retrieval and query of a single associated object will be added:
1309
1315
  #
@@ -1418,8 +1424,10 @@ module ActiveRecord
1418
1424
  # If you are going to modify the association (rather than just read from it), then it is
1419
1425
  # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
1420
1426
  # join model. This allows associated records to be built which will automatically create
1421
- # the appropriate join model records when they are saved. (See the 'Association Join Models'
1422
- # and 'Setting Inverses' sections above.)
1427
+ # the appropriate join model records when they are saved. See
1428
+ # {Association Join Models}[rdoc-ref:Associations::ClassMethods@Association+Join+Models]
1429
+ # and {Setting Inverses}[rdoc-ref:Associations::ClassMethods@Setting+Inverses] for
1430
+ # more detail.
1423
1431
  # [+:disable_joins+]
1424
1432
  # Specifies whether joins should be skipped for an association. If set to true, two or more queries
1425
1433
  # will be generated. Note that in some cases, if order or limit is applied, it will be done in-memory
@@ -1457,7 +1465,8 @@ module ActiveRecord
1457
1465
  # [+:inverse_of+]
1458
1466
  # Specifies the name of the #belongs_to association on the associated object
1459
1467
  # that is the inverse of this #has_one association.
1460
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1468
+ # See {Bi-directional associations}[rdoc-ref:Associations::ClassMethods@Bi-directional+associations]
1469
+ # for more detail.
1461
1470
  # [+:required+]
1462
1471
  # When set to +true+, the association will also have its presence validated.
1463
1472
  # This will validate the association itself, not the id. You can use
@@ -1491,10 +1500,12 @@ module ActiveRecord
1491
1500
  Reflection.add_reflection self, name, reflection
1492
1501
  end
1493
1502
 
1494
- # Specifies a one-to-one association with another class. This method should only be used
1495
- # if this class contains the foreign key. If the other class contains the foreign key,
1496
- # then you should use #has_one instead. See also ActiveRecord::Associations::ClassMethods's overview
1497
- # on when to use #has_one and when to use #belongs_to.
1503
+ # Specifies a one-to-one association with another class. This method
1504
+ # should only be used if this class contains the foreign key. If the
1505
+ # other class contains the foreign key, then you should use #has_one
1506
+ # instead. See {Is it a belongs_to or has_one
1507
+ # association?}[rdoc-ref:Associations::ClassMethods@Is+it+a+-23belongs_to+or+-23has_one+association-3F]
1508
+ # for more detail on when to use #has_one and when to use #belongs_to.
1498
1509
  #
1499
1510
  # Methods will be added for retrieval and query for a single associated object, for which
1500
1511
  # this object holds an id:
@@ -1636,7 +1647,8 @@ module ActiveRecord
1636
1647
  # [+:inverse_of+]
1637
1648
  # Specifies the name of the #has_one or #has_many association on the associated
1638
1649
  # object that is the inverse of this #belongs_to association.
1639
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1650
+ # See {Bi-directional associations}[rdoc-ref:Associations::ClassMethods@Bi-directional+associations]
1651
+ # for more detail.
1640
1652
  # [+:optional+]
1641
1653
  # When set to +true+, the association will not have its presence validated.
1642
1654
  # [+:required+]
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "set"
4
-
5
3
  module ActiveRecord
6
4
  module AttributeMethods
7
5
  # = Active Record Attribute Methods Primary Key
@@ -89,10 +87,9 @@ module ActiveRecord
89
87
  @composite_primary_key
90
88
  end
91
89
 
92
- # Returns a quoted version of the primary key name, used to construct
93
- # SQL statements.
90
+ # Returns a quoted version of the primary key name.
94
91
  def quoted_primary_key
95
- @quoted_primary_key ||= adapter_class.quote_column_name(primary_key)
92
+ adapter_class.quote_column_name(primary_key)
96
93
  end
97
94
 
98
95
  def reset_primary_key # :nodoc:
@@ -138,7 +135,6 @@ module ActiveRecord
138
135
  elsif value
139
136
  -value.to_s
140
137
  end
141
- @quoted_primary_key = nil
142
138
  @attributes_builder = nil
143
139
  end
144
140
 
@@ -148,7 +144,6 @@ module ActiveRecord
148
144
  base.class_eval do
149
145
  @primary_key = PRIMARY_KEY_NOT_SET
150
146
  @composite_primary_key = false
151
- @quoted_primary_key = nil
152
147
  @attributes_builder = nil
153
148
  end
154
149
  end
@@ -28,7 +28,7 @@ module ActiveRecord
28
28
  elsif value.respond_to?(:infinite?) && value.infinite?
29
29
  value
30
30
  else
31
- map_avoiding_infinite_recursion(super) { |v| cast(v) }
31
+ map(super) { |v| cast(v) }
32
32
  end
33
33
  end
34
34
 
@@ -45,23 +45,13 @@ module ActiveRecord
45
45
  elsif value.respond_to?(:infinite?) && value.infinite?
46
46
  value
47
47
  else
48
- map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
48
+ map(value) { |v| convert_time_to_time_zone(v) }
49
49
  end
50
50
  end
51
51
 
52
52
  def set_time_zone_without_conversion(value)
53
53
  ::Time.zone.local_to_utc(value).try(:in_time_zone) if value
54
54
  end
55
-
56
- def map_avoiding_infinite_recursion(value)
57
- map(value) do |v|
58
- if value.equal?(v)
59
- nil
60
- else
61
- yield(v)
62
- end
63
- end
64
- end
65
55
  end
66
56
 
67
57
  extend ActiveSupport::Concern
@@ -418,7 +418,7 @@ module ActiveRecord
418
418
 
419
419
  def destroy # :nodoc:
420
420
  @_destroy_callback_already_called ||= false
421
- return if @_destroy_callback_already_called
421
+ return true if @_destroy_callback_already_called
422
422
  @_destroy_callback_already_called = true
423
423
  _run_destroy_callbacks { super }
424
424
  rescue RecordNotDestroyed => e
@@ -183,6 +183,31 @@ module ActiveRecord
183
183
  end
184
184
  end
185
185
 
186
+ module ExecutorHooks # :nodoc:
187
+ class << self
188
+ def run
189
+ # noop
190
+ end
191
+
192
+ def complete(_)
193
+ ActiveRecord::Base.connection_handler.each_connection_pool do |pool|
194
+ if (connection = pool.active_connection?)
195
+ transaction = connection.current_transaction
196
+ if transaction.closed? || !transaction.joinable?
197
+ pool.release_connection
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ class << self
206
+ def install_executor_hooks(executor = ActiveSupport::Executor)
207
+ executor.register_hook(ExecutorHooks)
208
+ end
209
+ end
210
+
186
211
  include MonitorMixin
187
212
  prepend QueryCache::ConnectionPoolConfiguration
188
213
  include ConnectionAdapters::AbstractPool
@@ -291,14 +316,6 @@ module ActiveRecord
291
316
  connection_lease.sticky.nil?
292
317
  end
293
318
 
294
- def connection
295
- ActiveRecord.deprecator.warn(<<~MSG)
296
- ActiveRecord::ConnectionAdapters::ConnectionPool#connection is deprecated
297
- and will be removed in Rails 8.0. Use #lease_connection instead.
298
- MSG
299
- lease_connection
300
- end
301
-
302
319
  def pin_connection!(lock_thread) # :nodoc:
303
320
  @pinned_connection ||= (connection_lease&.connection || checkout)
304
321
  @pinned_connections_depth += 1
@@ -332,6 +349,7 @@ module ActiveRecord
332
349
  end
333
350
 
334
351
  if @pinned_connection.nil?
352
+ connection.steal!
335
353
  connection.lock_thread = nil
336
354
  checkin(connection)
337
355
  end
@@ -28,6 +28,7 @@ module ActiveRecord
28
28
  sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
29
29
  sql << o.check_constraint_adds.map { |con| visit_AddCheckConstraint con }.join(" ")
30
30
  sql << o.check_constraint_drops.map { |con| visit_DropCheckConstraint con }.join(" ")
31
+ sql << o.constraint_drops.map { |con| visit_DropConstraint con }.join(" ")
31
32
  end
32
33
 
33
34
  def visit_ColumnDefinition(o)
@@ -96,9 +97,11 @@ module ActiveRecord
96
97
  "ADD #{accept(o)}"
97
98
  end
98
99
 
99
- def visit_DropForeignKey(name)
100
+ def visit_DropConstraint(name)
100
101
  "DROP CONSTRAINT #{quote_column_name(name)}"
101
102
  end
103
+ alias :visit_DropForeignKey :visit_DropConstraint
104
+ alias :visit_DropCheckConstraint :visit_DropConstraint
102
105
 
103
106
  def visit_CreateIndexDefinition(o)
104
107
  index = o.index
@@ -127,10 +130,6 @@ module ActiveRecord
127
130
  "ADD #{accept(o)}"
128
131
  end
129
132
 
130
- def visit_DropCheckConstraint(name)
131
- "DROP CONSTRAINT #{quote_column_name(name)}"
132
- end
133
-
134
133
  def quoted_columns(o)
135
134
  String === o.columns ? o.columns : quoted_columns_for_index(o.columns, o.column_options)
136
135
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module ActiveRecord
5
4
  module ConnectionAdapters # :nodoc:
6
5
  # Abstract representation of an index definition on a table. Instances of
@@ -622,6 +621,7 @@ module ActiveRecord
622
621
  attr_reader :adds
623
622
  attr_reader :foreign_key_adds, :foreign_key_drops
624
623
  attr_reader :check_constraint_adds, :check_constraint_drops
624
+ attr_reader :constraint_drops
625
625
 
626
626
  def initialize(td)
627
627
  @td = td
@@ -630,6 +630,7 @@ module ActiveRecord
630
630
  @foreign_key_drops = []
631
631
  @check_constraint_adds = []
632
632
  @check_constraint_drops = []
633
+ @constraint_drops = []
633
634
  end
634
635
 
635
636
  def name; @td.name; end
@@ -650,6 +651,10 @@ module ActiveRecord
650
651
  @check_constraint_drops << constraint_name
651
652
  end
652
653
 
654
+ def drop_constraint(constraint_name)
655
+ @constraint_drops << constraint_name
656
+ end
657
+
653
658
  def add_column(name, type, **options)
654
659
  name = name.to_s
655
660
  type = type.to_sym
@@ -693,7 +693,7 @@ module ActiveRecord
693
693
  #
694
694
  # If the options provided include an +if_exists+ key, it will be used to check if the
695
695
  # column does not exist. This will silently ignore the migration rather than raising
696
- # if the column was already used.
696
+ # if the column was already removed.
697
697
  #
698
698
  # remove_column(:suppliers, :qualification, if_exists: true)
699
699
  def remove_column(table_name, column_name, type = nil, **options)
@@ -1334,7 +1334,6 @@ module ActiveRecord
1334
1334
  execute schema_creation.accept(at)
1335
1335
  end
1336
1336
 
1337
-
1338
1337
  # Checks to see if a check constraint exists on a table for a given check constraint definition.
1339
1338
  #
1340
1339
  # check_constraint_exists?(:products, name: "price_check")
@@ -1346,6 +1345,13 @@ module ActiveRecord
1346
1345
  check_constraint_for(table_name, **options).present?
1347
1346
  end
1348
1347
 
1348
+ def remove_constraint(table_name, constraint_name) # :nodoc:
1349
+ at = create_alter_table(table_name)
1350
+ at.drop_constraint(constraint_name)
1351
+
1352
+ execute schema_creation.accept(at)
1353
+ end
1354
+
1349
1355
  def dump_schema_information # :nodoc:
1350
1356
  versions = pool.schema_migration.versions
1351
1357
  insert_versions_sql(versions) if versions.any?
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "set"
4
3
  require "active_record/connection_adapters/sql_type_metadata"
5
4
  require "active_record/connection_adapters/abstract/schema_dumper"
6
5
  require "active_record/connection_adapters/abstract/schema_creation"
@@ -98,7 +98,7 @@ module ActiveRecord
98
98
  end
99
99
 
100
100
  def supports_index_sort_order?
101
- !mariadb? && database_version >= "8.0.1"
101
+ mariadb? ? database_version >= "10.8.1" : database_version >= "8.0.1"
102
102
  end
103
103
 
104
104
  def supports_expression_index?
@@ -628,7 +628,7 @@ module ActiveRecord
628
628
  end
629
629
 
630
630
  def build_insert_sql(insert) # :nodoc:
631
- no_op_column = quote_column_name(insert.keys.first)
631
+ no_op_column = quote_column_name(insert.keys.first) if insert.keys.first
632
632
 
633
633
  # MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
634
634
  # then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
@@ -637,7 +637,9 @@ module ActiveRecord
637
637
  sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
638
638
 
639
639
  if insert.skip_duplicates?
640
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
640
+ if no_op_column
641
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
642
+ end
641
643
  elsif insert.update_duplicates?
642
644
  if insert.raw_update_sql?
643
645
  sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
@@ -651,7 +653,9 @@ module ActiveRecord
651
653
  sql = +"INSERT #{insert.into} #{insert.values_list}"
652
654
 
653
655
  if insert.skip_duplicates?
654
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
656
+ if no_op_column
657
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
658
+ end
655
659
  elsif insert.update_duplicates?
656
660
  sql << " ON DUPLICATE KEY UPDATE "
657
661
  if insert.raw_update_sql?
@@ -65,7 +65,7 @@ module ActiveRecord
65
65
  end
66
66
 
67
67
  def map(value, &block)
68
- value.map { |v| subtype.map(v, &block) }
68
+ value.is_a?(::Array) ? value.map(&block) : subtype.map(value, &block)
69
69
  end
70
70
 
71
71
  def changed_in_place?(raw_old_value, new_value)
@@ -45,12 +45,10 @@ Rails needs superuser privileges to disable referential integrity.
45
45
  BEGIN
46
46
  FOR r IN (
47
47
  SELECT FORMAT(
48
- 'UPDATE pg_constraint SET convalidated=false WHERE conname = ''%I'' AND connamespace::regnamespace = ''%I''::regnamespace; ALTER TABLE %I.%I VALIDATE CONSTRAINT %I;',
48
+ 'UPDATE pg_catalog.pg_constraint SET convalidated=false WHERE conname = ''%1$I'' AND connamespace::regnamespace = ''%2$I''::regnamespace; ALTER TABLE %2$I.%3$I VALIDATE CONSTRAINT %1$I;',
49
49
  constraint_name,
50
50
  table_schema,
51
- table_schema,
52
- table_name,
53
- constraint_name
51
+ table_name
54
52
  ) AS constraint_check
55
53
  FROM information_schema.table_constraints WHERE constraint_type = 'FOREIGN KEY'
56
54
  )
@@ -11,9 +11,7 @@ module ActiveRecord
11
11
  sql = super
12
12
  sql << o.constraint_validations.map { |fk| visit_ValidateConstraint fk }.join(" ")
13
13
  sql << o.exclusion_constraint_adds.map { |con| visit_AddExclusionConstraint con }.join(" ")
14
- sql << o.exclusion_constraint_drops.map { |con| visit_DropExclusionConstraint con }.join(" ")
15
14
  sql << o.unique_constraint_adds.map { |con| visit_AddUniqueConstraint con }.join(" ")
16
- sql << o.unique_constraint_drops.map { |con| visit_DropUniqueConstraint con }.join(" ")
17
15
  end
18
16
 
19
17
  def visit_AddForeignKey(o)
@@ -54,6 +52,7 @@ module ActiveRecord
54
52
  sql = ["CONSTRAINT"]
55
53
  sql << quote_column_name(o.name)
56
54
  sql << "UNIQUE"
55
+ sql << "NULLS NOT DISTINCT" if supports_nulls_not_distinct? && o.nulls_not_distinct
57
56
 
58
57
  if o.using_index
59
58
  sql << "USING INDEX #{quote_column_name(o.using_index)}"
@@ -72,18 +71,10 @@ module ActiveRecord
72
71
  "ADD #{accept(o)}"
73
72
  end
74
73
 
75
- def visit_DropExclusionConstraint(name)
76
- "DROP CONSTRAINT #{quote_column_name(name)}"
77
- end
78
-
79
74
  def visit_AddUniqueConstraint(o)
80
75
  "ADD #{accept(o)}"
81
76
  end
82
77
 
83
- def visit_DropUniqueConstraint(name)
84
- "DROP CONSTRAINT #{quote_column_name(name)}"
85
- end
86
-
87
78
  def visit_ChangeColumnDefinition(o)
88
79
  column = o.column
89
80
  column.sql_type = type_to_sql(column.type, **column.options)