activerecord 7.0.0.alpha2 → 7.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +504 -10
  3. data/lib/active_record/associations/association.rb +2 -8
  4. data/lib/active_record/associations/builder/collection_association.rb +9 -2
  5. data/lib/active_record/associations/collection_association.rb +10 -2
  6. data/lib/active_record/associations/preloader/association.rb +68 -48
  7. data/lib/active_record/associations/preloader/batch.rb +3 -6
  8. data/lib/active_record/associations/preloader/through_association.rb +19 -9
  9. data/lib/active_record/associations/preloader.rb +14 -24
  10. data/lib/active_record/associations/through_association.rb +2 -2
  11. data/lib/active_record/associations.rb +16 -3
  12. data/lib/active_record/asynchronous_queries_tracker.rb +3 -0
  13. data/lib/active_record/attribute_methods/dirty.rb +9 -1
  14. data/lib/active_record/attribute_methods.rb +7 -5
  15. data/lib/active_record/autosave_association.rb +3 -3
  16. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +6 -26
  17. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -2
  18. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +18 -5
  19. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
  21. data/lib/active_record/connection_adapters/abstract/quoting.rb +33 -70
  22. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -0
  23. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +9 -2
  24. data/lib/active_record/connection_adapters/abstract/transaction.rb +12 -19
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +37 -8
  26. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1 -0
  27. data/lib/active_record/connection_adapters/column.rb +4 -0
  28. data/lib/active_record/connection_adapters/mysql/database_statements.rb +2 -2
  29. data/lib/active_record/connection_adapters/mysql/quoting.rb +23 -24
  30. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
  31. data/lib/active_record/connection_adapters/pool_config.rb +7 -5
  32. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  33. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -44
  34. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  35. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +18 -1
  36. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  37. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -4
  38. data/lib/active_record/connection_adapters/postgresql_adapter.rb +48 -5
  39. data/lib/active_record/connection_adapters/schema_cache.rb +3 -1
  40. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +2 -2
  41. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +15 -16
  42. data/lib/active_record/connection_handling.rb +31 -19
  43. data/lib/active_record/core.rb +13 -24
  44. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  45. data/lib/active_record/database_configurations/database_config.rb +0 -9
  46. data/lib/active_record/database_configurations/hash_config.rb +40 -8
  47. data/lib/active_record/database_configurations.rb +2 -27
  48. data/lib/active_record/delegated_type.rb +19 -0
  49. data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
  50. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +1 -2
  51. data/lib/active_record/encryption/message_serializer.rb +11 -1
  52. data/lib/active_record/encryption/scheme.rb +1 -1
  53. data/lib/active_record/enum.rb +8 -1
  54. data/lib/active_record/errors.rb +1 -1
  55. data/lib/active_record/explain_registry.rb +11 -6
  56. data/lib/active_record/fixture_set/table_row.rb +1 -1
  57. data/lib/active_record/fixtures.rb +1 -9
  58. data/lib/active_record/future_result.rb +2 -2
  59. data/lib/active_record/gem_version.rb +1 -1
  60. data/lib/active_record/insert_all.rb +52 -15
  61. data/lib/active_record/integration.rb +3 -2
  62. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  63. data/lib/active_record/locking/pessimistic.rb +9 -3
  64. data/lib/active_record/log_subscriber.rb +8 -1
  65. data/lib/active_record/middleware/shard_selector.rb +60 -0
  66. data/lib/active_record/model_schema.rb +1 -28
  67. data/lib/active_record/nested_attributes.rb +11 -10
  68. data/lib/active_record/no_touching.rb +1 -1
  69. data/lib/active_record/persistence.rb +99 -21
  70. data/lib/active_record/query_logs.rb +18 -83
  71. data/lib/active_record/railtie.rb +11 -1
  72. data/lib/active_record/railties/databases.rake +4 -91
  73. data/lib/active_record/reflection.rb +22 -6
  74. data/lib/active_record/relation/calculations.rb +1 -10
  75. data/lib/active_record/relation/finder_methods.rb +0 -13
  76. data/lib/active_record/relation/query_methods.rb +5 -14
  77. data/lib/active_record/relation/record_fetch_warning.rb +5 -7
  78. data/lib/active_record/relation/where_clause.rb +2 -15
  79. data/lib/active_record/relation.rb +11 -13
  80. data/lib/active_record/result.rb +0 -5
  81. data/lib/active_record/runtime_registry.rb +10 -12
  82. data/lib/active_record/schema_dumper.rb +7 -0
  83. data/lib/active_record/scoping.rb +34 -22
  84. data/lib/active_record/suppressor.rb +11 -15
  85. data/lib/active_record/tasks/database_tasks.rb +18 -44
  86. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -1
  87. data/lib/active_record/validations/uniqueness.rb +1 -1
  88. data/lib/active_record.rb +41 -33
  89. data/lib/arel/crud.rb +12 -2
  90. data/lib/arel/delete_manager.rb +16 -0
  91. data/lib/arel/filter_predications.rb +9 -0
  92. data/lib/arel/nodes/delete_statement.rb +5 -1
  93. data/lib/arel/nodes/filter.rb +10 -0
  94. data/lib/arel/nodes/function.rb +1 -0
  95. data/lib/arel/nodes/update_statement.rb +5 -1
  96. data/lib/arel/nodes.rb +1 -0
  97. data/lib/arel/predications.rb +10 -2
  98. data/lib/arel/update_manager.rb +16 -0
  99. data/lib/arel/visitors/mysql.rb +2 -1
  100. data/lib/arel/visitors/to_sql.rb +15 -0
  101. data/lib/arel.rb +1 -0
  102. metadata +14 -10
@@ -571,7 +571,6 @@ module ActiveRecord
571
571
  @columns_hash.each do |name, column|
572
572
  type = connection.lookup_cast_type_from_column(column)
573
573
  type = _convert_type_from_options(type)
574
- warn_if_deprecated_type(column)
575
574
  define_attribute(
576
575
  name,
577
576
  type,
@@ -595,7 +594,7 @@ module ActiveRecord
595
594
  @schema_loaded = false
596
595
  @attribute_names = nil
597
596
  @yaml_encoder = nil
598
- direct_descendants.each do |descendant|
597
+ subclasses.each do |descendant|
599
598
  descendant.send(:reload_schema_from_cache)
600
599
  end
601
600
  end
@@ -630,32 +629,6 @@ module ActiveRecord
630
629
  type
631
630
  end
632
631
  end
633
-
634
- def warn_if_deprecated_type(column)
635
- return if attributes_to_define_after_schema_loads.key?(column.name)
636
- return unless column.respond_to?(:array?)
637
-
638
- if column.array?
639
- array_arguments = ", array: true"
640
- else
641
- array_arguments = ""
642
- end
643
-
644
- if column.sql_type.start_with?("interval")
645
- precision_arguments = column.precision.presence && ", precision: #{column.precision}"
646
- ActiveSupport::Deprecation.warn(<<~WARNING)
647
- The behavior of the `:interval` type will be changing in Rails 7.0
648
- to return an `ActiveSupport::Duration` object. If you'd like to keep
649
- the old behavior, you can add this line to #{self.name} model:
650
-
651
- attribute :#{column.name}, :string#{precision_arguments}#{array_arguments}
652
-
653
- If you'd like the new behavior today, you can add this line:
654
-
655
- attribute :#{column.name}, :interval#{precision_arguments}#{array_arguments}
656
- WARNING
657
- end
658
- end
659
632
  end
660
633
  end
661
634
  end
@@ -245,18 +245,19 @@ module ActiveRecord
245
245
  #
246
246
  # === Validating the presence of a parent model
247
247
  #
248
- # If you want to validate that a child record is associated with a parent
249
- # record, you can use the +validates_presence_of+ method and the +:inverse_of+
250
- # key as this example illustrates:
251
- #
252
- # class Member < ActiveRecord::Base
253
- # has_many :posts, inverse_of: :member
254
- # accepts_nested_attributes_for :posts
248
+ # The +belongs_to+ association validates the presence of the parent model
249
+ # by default. You can disable this behavior by specifying <code>optional: true</code>.
250
+ # This can be used, for example, when conditionally validating the presence
251
+ # of the parent model:
252
+ #
253
+ # class Veterinarian < ActiveRecord::Base
254
+ # has_many :patients, inverse_of: :veterinarian
255
+ # accepts_nested_attributes_for :patients
255
256
  # end
256
257
  #
257
- # class Post < ActiveRecord::Base
258
- # belongs_to :member, inverse_of: :posts
259
- # validates_presence_of :member
258
+ # class Patient < ActiveRecord::Base
259
+ # belongs_to :veterinarian, inverse_of: :patients, optional: true
260
+ # validates :veterinarian, presence: true, unless: -> { awaiting_intake }
260
261
  # end
261
262
  #
262
263
  # Note that if you do not specify the +:inverse_of+ option, then
@@ -39,7 +39,7 @@ module ActiveRecord
39
39
 
40
40
  private
41
41
  def klasses
42
- Thread.current[:no_touching_classes] ||= []
42
+ ActiveSupport::IsolatedExecutionState[:active_record_no_touching_classes] ||= []
43
43
  end
44
44
  end
45
45
 
@@ -63,8 +63,8 @@ module ActiveRecord
63
63
  # go through Active Record's type casting and serialization.
64
64
  #
65
65
  # See <tt>ActiveRecord::Persistence#insert_all</tt> for documentation.
66
- def insert(attributes, returning: nil, unique_by: nil)
67
- insert_all([ attributes ], returning: returning, unique_by: unique_by)
66
+ def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
67
+ insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
68
68
  end
69
69
 
70
70
  # Inserts multiple records into the database in a single SQL INSERT
@@ -110,6 +110,17 @@ module ActiveRecord
110
110
  # unique_by: %i[ author_id name ]
111
111
  # unique_by: :index_books_on_isbn
112
112
  #
113
+ # [:record_timestamps]
114
+ # By default, automatic setting of timestamp columns is controlled by
115
+ # the model's <tt>record_timestamps</tt> config, matching typical
116
+ # behavior.
117
+ #
118
+ # To override this and force automatic setting of timestamp columns one
119
+ # way or the other, pass <tt>:record_timestamps</tt>:
120
+ #
121
+ # record_timestamps: true # Always set timestamps automatically
122
+ # record_timestamps: false # Never set timestamps automatically
123
+ #
113
124
  # Because it relies on the index information from the database
114
125
  # <tt>:unique_by</tt> is recommended to be paired with
115
126
  # Active Record's schema_cache.
@@ -131,8 +142,8 @@ module ActiveRecord
131
142
  # { id: 1, title: "Rework" },
132
143
  # { id: 2, title: "Eloquent Ruby" }
133
144
  # ])
134
- def insert_all(attributes, returning: nil, unique_by: nil)
135
- InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by).execute
145
+ def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
146
+ InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
136
147
  end
137
148
 
138
149
  # Inserts a single record into the database in a single SQL INSERT
@@ -141,8 +152,8 @@ module ActiveRecord
141
152
  # go through Active Record's type casting and serialization.
142
153
  #
143
154
  # See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
144
- def insert!(attributes, returning: nil)
145
- insert_all!([ attributes ], returning: returning)
155
+ def insert!(attributes, returning: nil, record_timestamps: nil)
156
+ insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
146
157
  end
147
158
 
148
159
  # Inserts multiple records into the database in a single SQL INSERT
@@ -174,6 +185,17 @@ module ActiveRecord
174
185
  # You can also pass an SQL string if you need more control on the return values
175
186
  # (for example, <tt>returning: "id, name as new_name"</tt>).
176
187
  #
188
+ # [:record_timestamps]
189
+ # By default, automatic setting of timestamp columns is controlled by
190
+ # the model's <tt>record_timestamps</tt> config, matching typical
191
+ # behavior.
192
+ #
193
+ # To override this and force automatic setting of timestamp columns one
194
+ # way or the other, pass <tt>:record_timestamps</tt>:
195
+ #
196
+ # record_timestamps: true # Always set timestamps automatically
197
+ # record_timestamps: false # Never set timestamps automatically
198
+ #
177
199
  # ==== Examples
178
200
  #
179
201
  # # Insert multiple records
@@ -188,8 +210,8 @@ module ActiveRecord
188
210
  # { id: 1, title: "Rework", author: "David" },
189
211
  # { id: 1, title: "Eloquent Ruby", author: "Russ" }
190
212
  # ])
191
- def insert_all!(attributes, returning: nil)
192
- InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
213
+ def insert_all!(attributes, returning: nil, record_timestamps: nil)
214
+ InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
193
215
  end
194
216
 
195
217
  # Updates or inserts (upserts) a single record into the database in a
@@ -198,8 +220,8 @@ module ActiveRecord
198
220
  # go through Active Record's type casting and serialization.
199
221
  #
200
222
  # See <tt>ActiveRecord::Persistence#upsert_all</tt> for documentation.
201
- def upsert(attributes, on_duplicate: :update, returning: nil, unique_by: nil)
202
- upsert_all([ attributes ], on_duplicate: on_duplicate, returning: returning, unique_by: unique_by)
223
+ def upsert(attributes, on_duplicate: :update, returning: nil, unique_by: nil, record_timestamps: nil)
224
+ upsert_all([ attributes ], on_duplicate: on_duplicate, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
203
225
  end
204
226
 
205
227
  # Updates or inserts (upserts) multiple records into the database in a
@@ -213,6 +235,10 @@ module ActiveRecord
213
235
  # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
214
236
  # <tt>:returning</tt> (see below).
215
237
  #
238
+ # By default, +upsert_all+ will update all the columns that can be updated when
239
+ # there is a conflict. These are all the columns except primary keys, read-only
240
+ # columns, and columns covered by the optional +unique_by+.
241
+ #
216
242
  # ==== Options
217
243
  #
218
244
  # [:returning]
@@ -246,9 +272,52 @@ module ActiveRecord
246
272
  # Active Record's schema_cache.
247
273
  #
248
274
  # [:on_duplicate]
249
- # Specify a custom SQL for updating rows on conflict.
275
+ # Configure the SQL update sentence that will be used in case of conflict.
276
+ #
277
+ # NOTE: If you use this option you must provide all the columns you want to update
278
+ # by yourself.
279
+ #
280
+ # Example:
281
+ #
282
+ # Commodity.upsert_all(
283
+ # [
284
+ # { id: 2, name: "Copper", price: 4.84 },
285
+ # { id: 4, name: "Gold", price: 1380.87 },
286
+ # { id: 6, name: "Aluminium", price: 0.35 }
287
+ # ],
288
+ # on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
289
+ # )
290
+ #
291
+ # See the related +:update_only+ option. Both options can't be used at the same time.
250
292
  #
251
- # NOTE: in this case you must provide all the columns you want to update by yourself.
293
+ # [:update_only]
294
+ # Provide a list of column names that will be updated in case of conflict. If not provided,
295
+ # +upsert_all+ will update all the columns that can be updated. These are all the columns
296
+ # except primary keys, read-only columns, and columns covered by the optional +unique_by+
297
+ #
298
+ # Example:
299
+ #
300
+ # Commodity.upsert_all(
301
+ # [
302
+ # { id: 2, name: "Copper", price: 4.84 },
303
+ # { id: 4, name: "Gold", price: 1380.87 },
304
+ # { id: 6, name: "Aluminium", price: 0.35 }
305
+ # ],
306
+ # update_only: [:price] # Only prices will be updated
307
+ # )
308
+ #
309
+ # See the related +:on_duplicate+ option. Both options can't be used at the same time.
310
+ #
311
+ # [:record_timestamps]
312
+ # By default, automatic setting of timestamp columns is controlled by
313
+ # the model's <tt>record_timestamps</tt> config, matching typical
314
+ # behavior.
315
+ #
316
+ # To override this and force automatic setting of timestamp columns one
317
+ # way or the other, pass <tt>:record_timestamps</tt>:
318
+ #
319
+ # record_timestamps: true # Always set timestamps automatically
320
+ # record_timestamps: false # Never set timestamps automatically
252
321
  #
253
322
  # ==== Examples
254
323
  #
@@ -261,8 +330,8 @@ module ActiveRecord
261
330
  # ], unique_by: :isbn)
262
331
  #
263
332
  # Book.find_by(isbn: "1").title # => "Eloquent Ruby"
264
- def upsert_all(attributes, on_duplicate: :update, returning: nil, unique_by: nil)
265
- InsertAll.new(self, attributes, on_duplicate: on_duplicate, returning: returning, unique_by: unique_by).execute
333
+ def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
334
+ InsertAll.new(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
266
335
  end
267
336
 
268
337
  # Given an attributes hash, +instantiate+ returns a new instance of
@@ -431,9 +500,8 @@ module ActiveRecord
431
500
  def _update_record(values, constraints) # :nodoc:
432
501
  constraints = constraints.map { |name, value| predicate_builder[name, value] }
433
502
 
434
- if default_scopes?(all_queries: true)
435
- constraints << default_scoped(all_queries: true).where_clause.ast
436
- end
503
+ default_constraint = build_default_constraint
504
+ constraints << default_constraint if default_constraint
437
505
 
438
506
  if current_scope = self.global_current_scope
439
507
  constraints << current_scope.where_clause.ast
@@ -449,9 +517,8 @@ module ActiveRecord
449
517
  def _delete_record(constraints) # :nodoc:
450
518
  constraints = constraints.map { |name, value| predicate_builder[name, value] }
451
519
 
452
- if default_scopes?(all_queries: true)
453
- constraints << default_scoped(all_queries: true).where_clause.ast
454
- end
520
+ default_constraint = build_default_constraint
521
+ constraints << default_constraint if default_constraint
455
522
 
456
523
  if current_scope = self.global_current_scope
457
524
  constraints << current_scope.where_clause.ast
@@ -479,6 +546,16 @@ module ActiveRecord
479
546
  def discriminate_class_for_record(record)
480
547
  self
481
548
  end
549
+
550
+ # Called by +_update_record+ and +_delete_record+
551
+ # to build `where` clause from default scopes.
552
+ # Skips empty scopes.
553
+ def build_default_constraint
554
+ return unless default_scopes?(all_queries: true)
555
+
556
+ default_where_clause = default_scoped(all_queries: true).where_clause
557
+ default_where_clause.ast unless default_where_clause.empty?
558
+ end
482
559
  end
483
560
 
484
561
  # Returns true if this object hasn't been saved yet -- that is, a record
@@ -1035,7 +1112,8 @@ module ActiveRecord
1035
1112
 
1036
1113
  def _raise_record_not_destroyed
1037
1114
  @_association_destroy_exception ||= nil
1038
- raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy the record", self)
1115
+ key = self.class.primary_key
1116
+ raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{send(key)}", self)
1039
1117
  ensure
1040
1118
  @_association_destroy_exception = nil
1041
1119
  end
@@ -38,24 +38,24 @@ module ActiveRecord
38
38
  # tags = [
39
39
  # :application,
40
40
  # {
41
- # custom_tag: ->(context) { context[:controller].controller_name },
41
+ # custom_tag: ->(context) { context[:controller]&.controller_name },
42
42
  # custom_value: -> { Custom.value },
43
43
  # }
44
44
  # ]
45
45
  # ActiveRecord::QueryLogs.tags = tags
46
46
  #
47
- # The QueryLogs +context+ can be manipulated via +update_context+ & +set_context+ methods.
48
- #
49
- # Direct updates to a context value:
50
- #
51
- # ActiveRecord::QueryLogs.update_context(foo: Bar.new)
47
+ # The QueryLogs +context+ can be manipulated via the +ActiveSupport::ExecutionContext.set+ method.
52
48
  #
53
49
  # Temporary updates limited to the execution of a block:
54
50
  #
55
- # ActiveRecord::QueryLogs.set_context(foo: Bar.new) do
51
+ # ActiveSupport::ExecutionContext.set(foo: Bar.new) do
56
52
  # posts = Post.all
57
53
  # end
58
54
  #
55
+ # Direct updates to a context value:
56
+ #
57
+ # ActiveSupport::ExecutionContext[:foo] = Bar.new
58
+ #
59
59
  # Tag comments can be prepended to the query:
60
60
  #
61
61
  # ActiveRecord::QueryLogs.prepend_comment = true
@@ -75,66 +75,22 @@ module ActiveRecord
75
75
  mattr_accessor :cache_query_log_tags, instance_accessor: false, default: false
76
76
  thread_mattr_accessor :cached_comment, instance_accessor: false
77
77
 
78
- class NullObject # :nodoc:
79
- def method_missing(method, *args, &block)
80
- NullObject.new
81
- end
82
-
83
- def nil?
84
- true
85
- end
86
-
87
- private
88
- def respond_to_missing?(method, include_private = false)
89
- true
90
- end
91
- end
92
-
93
78
  class << self
94
- # Updates the context used to construct tags in the SQL comment.
95
- # Resets the cached comment if <tt>cache_query_log_tags</tt> is +true+.
96
- def update_context(**options)
97
- context.merge!(**options.symbolize_keys)
98
- self.cached_comment = nil
99
- end
100
-
101
- # Updates the context used to construct tags in the SQL comment during
102
- # execution of the provided block. Resets the provided keys to their
103
- # previous value once the block exits.
104
- def set_context(**options)
105
- keys = options.keys
106
- previous_context = keys.zip(context.values_at(*keys)).to_h
107
- update_context(**options)
108
- yield if block_given?
109
- ensure
110
- update_context(**previous_context)
111
- end
112
-
113
- # Temporarily tag any query executed within `&block`. Can be nested.
114
- def with_tag(tag, &block)
115
- inline_tags.push(tag)
116
- yield if block_given?
117
- ensure
118
- inline_tags.pop
119
- end
120
-
121
79
  def call(sql) # :nodoc:
122
- parts = self.comments
123
80
  if prepend_comment
124
- parts << sql
81
+ "#{self.comment} #{sql}"
125
82
  else
126
- parts.unshift(sql)
127
- end
128
- parts.join(" ")
83
+ "#{sql} #{self.comment}"
84
+ end.strip
129
85
  end
130
86
 
131
- private
132
- # Returns an array of comments which need to be added to the query, comprised
133
- # of configured and inline tags.
134
- def comments
135
- [ comment, inline_comment ].compact
136
- end
87
+ def clear_cache # :nodoc:
88
+ self.cached_comment = nil
89
+ end
90
+
91
+ ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
137
92
 
93
+ private
138
94
  # Returns an SQL comment +String+ containing the query log tags.
139
95
  # Sets and returns a cached comment if <tt>cache_query_log_tags</tt> is +true+.
140
96
  def comment
@@ -152,30 +108,13 @@ module ActiveRecord
152
108
  end
153
109
  end
154
110
 
155
- # Returns a +String+ containing any inline comments from +with_tag+.
156
- def inline_comment
157
- return nil unless inline_tags.present?
158
- "/*#{escape_sql_comment(inline_tag_content)}*/"
159
- end
160
-
161
- # Return the set of active inline tags from +with_tag+.
162
- def inline_tags
163
- if context[:inline_tags].nil?
164
- context[:inline_tags] = []
165
- else
166
- context[:inline_tags]
167
- end
168
- end
169
-
170
- def context
171
- Thread.current[:active_record_query_log_tags_context] ||= Hash.new { NullObject.new }
172
- end
173
-
174
111
  def escape_sql_comment(content)
175
112
  content.to_s.gsub(%r{ (/ (?: | \g<1>) \*) \+? \s* | \s* (\* (?: | \g<2>) /) }x, "")
176
113
  end
177
114
 
178
115
  def tag_content
116
+ context = ActiveSupport::ExecutionContext.to_h
117
+
179
118
  tags.flat_map { |i| [*i] }.filter_map do |tag|
180
119
  key, handler = tag
181
120
  handler ||= taggings[key]
@@ -194,10 +133,6 @@ module ActiveRecord
194
133
  "#{key}:#{val}" unless val.nil?
195
134
  end.join(",")
196
135
  end
197
-
198
- def inline_tag_content
199
- inline_tags.join
200
- end
201
136
  end
202
137
  end
203
138
  end
@@ -102,6 +102,14 @@ module ActiveRecord
102
102
  end
103
103
  end
104
104
 
105
+ initializer "active_record.shard_selector" do
106
+ if resolver = config.active_record.shard_resolver
107
+ options = config.active_record.shard_selector || {}
108
+
109
+ config.app_middleware.use ActiveRecord::Middleware::ShardSelector, resolver, options
110
+ end
111
+ end
112
+
105
113
  initializer "Check for cache versioning support" do
106
114
  config.after_initialize do |app|
107
115
  ActiveSupport.on_load(:active_record) do
@@ -130,7 +138,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
130
138
  initializer "active_record.check_schema_cache_dump" do
131
139
  check_schema_cache_dump_version = config.active_record.check_schema_cache_dump_version
132
140
 
133
- if config.active_record.use_schema_cache_dump
141
+ if config.active_record.use_schema_cache_dump && !config.active_record.lazily_load_schema_cache
134
142
  config.after_initialize do |app|
135
143
  ActiveSupport.on_load(:active_record) do
136
144
  db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).first
@@ -232,6 +240,8 @@ To keep using the current cache store, you can turn off cache versioning entirel
232
240
  :database_selector,
233
241
  :database_resolver,
234
242
  :database_resolver_context,
243
+ :shard_selector,
244
+ :shard_resolver,
235
245
  :query_log_tags_enabled,
236
246
  :query_log_tags,
237
247
  :cache_query_log_tags,
@@ -332,7 +332,7 @@ db_namespace = namespace :db do
332
332
 
333
333
  desc "Retrieves the current schema version number"
334
334
  task version: :load_config do
335
- puts "Current version: #{ActiveRecord::Base.connection.migration_context.current_version}"
335
+ puts "Current version: #{ActiveRecord::Base.connection.schema_version}"
336
336
  end
337
337
 
338
338
  # desc "Raises an error if there are pending migrations"
@@ -469,14 +469,6 @@ db_namespace = namespace :db do
469
469
  ActiveRecord::Tasks::DatabaseTasks.load_schema_current(ActiveRecord.schema_format, ENV["SCHEMA"])
470
470
  end
471
471
 
472
- task load_if_ruby: ["db:create", :environment] do
473
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
474
- Using `bin/rails db:schema:load_if_ruby` is deprecated and will be removed in Rails 7.0.
475
- Configure the format using `config.active_record.schema_format = :ruby` to use `schema.rb` and run `bin/rails db:schema:load` instead.
476
- MSG
477
- db_namespace["schema:load"].invoke if ActiveRecord.schema_format == :ruby
478
- end
479
-
480
472
  namespace :dump do
481
473
  ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
482
474
  desc "Creates a database schema file (either db/schema.rb or db/structure.sql, depending on `config.active_record.schema_format`) for #{name} database"
@@ -495,7 +487,7 @@ db_namespace = namespace :db do
495
487
  task name => :load_config do
496
488
  original_db_config = ActiveRecord::Base.connection_db_config
497
489
  db_config = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env, name: name)
498
- ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, ActiveRecord.schema_format, ENV["SCHEMA"])
490
+ ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config)
499
491
  ensure
500
492
  ActiveRecord::Base.establish_connection(original_db_config) if original_db_config
501
493
  end
@@ -533,63 +525,6 @@ db_namespace = namespace :db do
533
525
  end
534
526
  end
535
527
 
536
- namespace :structure do
537
- desc "Dumps the database structure to db/structure.sql. Specify another file with SCHEMA=db/my_structure.sql"
538
- task dump: :load_config do
539
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
540
- Using `bin/rails db:structure:dump` is deprecated and will be removed in Rails 7.0.
541
- Configure the format using `config.active_record.schema_format = :sql` to use `structure.sql` and run `bin/rails db:schema:dump` instead.
542
- MSG
543
-
544
- db_namespace["schema:dump"].invoke
545
- db_namespace["structure:dump"].reenable
546
- end
547
-
548
- desc "Recreates the databases from the structure.sql file"
549
- task load: [:load_config, :check_protected_environments] do
550
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
551
- Using `bin/rails db:structure:load` is deprecated and will be removed in Rails 7.0.
552
- Configure the format using `config.active_record.schema_format = :sql` to use `structure.sql` and run `bin/rails db:schema:load` instead.
553
- MSG
554
- db_namespace["schema:load"].invoke
555
- end
556
-
557
- task load_if_sql: ["db:create", :environment] do
558
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
559
- Using `bin/rails db:structure:load_if_sql` is deprecated and will be removed in Rails 7.0.
560
- Configure the format using `config.active_record.schema_format = :sql` to use `structure.sql` and run `bin/rails db:schema:load` instead.
561
- MSG
562
- db_namespace["schema:load"].invoke if ActiveRecord.schema_format == :sql
563
- end
564
-
565
- namespace :dump do
566
- ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
567
- desc "Dumps the #{name} database structure to db/structure.sql. Specify another file with SCHEMA=db/my_structure.sql"
568
- task name => :load_config do
569
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
570
- Using `bin/rails db:structure:dump:#{name}` is deprecated and will be removed in Rails 7.0.
571
- Configure the format using `config.active_record.schema_format = :sql` to use `structure.sql` and run `bin/rails db:schema:dump:#{name}` instead.
572
- MSG
573
- db_namespace["schema:dump:#{name}"].invoke
574
- db_namespace["structure:dump:#{name}"].reenable
575
- end
576
- end
577
- end
578
-
579
- namespace :load do
580
- ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
581
- desc "Recreates the #{name} database from the structure.sql file"
582
- task name => :load_config do
583
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
584
- Using `bin/rails db:structure:load:#{name}` is deprecated and will be removed in Rails 7.0.
585
- Configure the format using `config.active_record.schema_format = :sql` to use `structure.sql` and run `bin/rails db:schema:load:#{name}` instead.
586
- MSG
587
- db_namespace["schema:load:#{name}"].invoke
588
- end
589
- end
590
- end
591
- end
592
-
593
528
  namespace :encryption do
594
529
  desc "Generate a set of keys for configuring Active Record encryption in a given environment"
595
530
  task :init do
@@ -615,8 +550,7 @@ db_namespace = namespace :db do
615
550
  should_reconnect = ActiveRecord::Base.connection_pool.active_connection?
616
551
  ActiveRecord::Schema.verbose = false
617
552
  ActiveRecord::Base.configurations.configs_for(env_name: "test").each do |db_config|
618
- filename = ActiveRecord::Tasks::DatabaseTasks.dump_filename(db_config.name)
619
- ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, ActiveRecord.schema_format, filename)
553
+ ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config)
620
554
  end
621
555
  ensure
622
556
  if should_reconnect
@@ -624,15 +558,6 @@ db_namespace = namespace :db do
624
558
  end
625
559
  end
626
560
 
627
- # desc "Recreate the test database from an existent structure.sql file"
628
- task load_structure: %w(db:test:purge) do
629
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
630
- Using `bin/rails db:test:load_structure` is deprecated and will be removed in Rails 7.0.
631
- Configure the format using `config.active_record.schema_format = :sql` to use `structure.sql` and run `bin/rails db:test:load_schema` instead.
632
- MSG
633
- db_namespace["test:load_schema"].invoke
634
- end
635
-
636
561
  # desc "Empty the test database"
637
562
  task purge: %w(load_config check_protected_environments) do
638
563
  ActiveRecord::Base.configurations.configs_for(env_name: "test").each do |db_config|
@@ -660,9 +585,8 @@ db_namespace = namespace :db do
660
585
  task name => "db:test:purge:#{name}" do
661
586
  should_reconnect = ActiveRecord::Base.connection_pool.active_connection?
662
587
  ActiveRecord::Schema.verbose = false
663
- filename = ActiveRecord::Tasks::DatabaseTasks.dump_filename(name)
664
588
  db_config = ActiveRecord::Base.configurations.configs_for(env_name: "test", name: name)
665
- ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, ActiveRecord.schema_format, filename)
589
+ ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config)
666
590
  ensure
667
591
  if should_reconnect
668
592
  ActiveRecord::Base.establish_connection(ActiveRecord::Tasks::DatabaseTasks.env.to_sym)
@@ -670,17 +594,6 @@ db_namespace = namespace :db do
670
594
  end
671
595
  end
672
596
 
673
- # desc "Recreate the #{name} test database from an existent structure.sql file"
674
- namespace :load_structure do
675
- task name => "db:test:purge:#{name}" do
676
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
677
- Using `bin/rails db:test:load_structure:#{name}` is deprecated and will be removed in Rails 7.0.
678
- Configure the format using `config.active_record.schema_format = :sql` to use `structure.sql` and run `bin/rails db:test:load_structure:#{name}` instead.
679
- MSG
680
- db_namespace["test:load_schema:#{name}"].invoke
681
- end
682
- end
683
-
684
597
  # desc "Empty the #{name} test database"
685
598
  namespace :purge do
686
599
  task name => %w(load_config check_protected_environments) do