activerecord 7.1.5 → 7.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +515 -2440
  3. data/README.rdoc +15 -15
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +1 -1
  6. data/lib/active_record/associations/alias_tracker.rb +25 -19
  7. data/lib/active_record/associations/association.rb +9 -8
  8. data/lib/active_record/associations/belongs_to_association.rb +14 -7
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +3 -4
  13. data/lib/active_record/associations/builder/has_one.rb +3 -4
  14. data/lib/active_record/associations/collection_association.rb +6 -4
  15. data/lib/active_record/associations/collection_proxy.rb +14 -1
  16. data/lib/active_record/associations/has_many_association.rb +1 -1
  17. data/lib/active_record/associations/join_dependency/join_association.rb +29 -28
  18. data/lib/active_record/associations/join_dependency.rb +5 -5
  19. data/lib/active_record/associations/nested_error.rb +47 -0
  20. data/lib/active_record/associations/preloader/association.rb +2 -1
  21. data/lib/active_record/associations/preloader/branch.rb +7 -1
  22. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  23. data/lib/active_record/associations/singular_association.rb +6 -0
  24. data/lib/active_record/associations/through_association.rb +1 -1
  25. data/lib/active_record/associations.rb +33 -16
  26. data/lib/active_record/attribute_assignment.rb +1 -11
  27. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +1 -1
  29. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  30. data/lib/active_record/attribute_methods/read.rb +4 -16
  31. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  32. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -10
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +60 -71
  35. data/lib/active_record/attributes.rb +55 -42
  36. data/lib/active_record/autosave_association.rb +13 -32
  37. data/lib/active_record/base.rb +2 -3
  38. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
  39. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
  40. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -65
  41. data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
  42. data/lib/active_record/connection_adapters/abstract/query_cache.rb +159 -74
  43. data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
  44. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  45. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +14 -5
  46. data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
  47. data/lib/active_record/connection_adapters/abstract_adapter.rb +18 -46
  48. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +32 -6
  49. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  50. data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
  51. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -1
  52. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
  53. data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -23
  54. data/lib/active_record/connection_adapters/pool_config.rb +7 -6
  55. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
  56. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  57. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  58. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  59. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  60. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
  61. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -13
  62. data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
  63. data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
  64. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  65. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
  66. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
  67. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  68. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  69. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  70. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
  71. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +107 -75
  72. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
  73. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
  74. data/lib/active_record/connection_adapters.rb +121 -0
  75. data/lib/active_record/connection_handling.rb +56 -41
  76. data/lib/active_record/core.rb +53 -37
  77. data/lib/active_record/counter_cache.rb +18 -9
  78. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  79. data/lib/active_record/database_configurations/database_config.rb +15 -4
  80. data/lib/active_record/database_configurations/hash_config.rb +38 -34
  81. data/lib/active_record/database_configurations/url_config.rb +20 -1
  82. data/lib/active_record/database_configurations.rb +1 -1
  83. data/lib/active_record/delegated_type.rb +24 -0
  84. data/lib/active_record/dynamic_matchers.rb +2 -2
  85. data/lib/active_record/encryption/encryptable_record.rb +2 -2
  86. data/lib/active_record/encryption/encrypted_attribute_type.rb +22 -2
  87. data/lib/active_record/encryption/encryptor.rb +17 -2
  88. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  89. data/lib/active_record/encryption/message_serializer.rb +4 -0
  90. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  91. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  92. data/lib/active_record/encryption.rb +0 -2
  93. data/lib/active_record/enum.rb +10 -1
  94. data/lib/active_record/errors.rb +16 -11
  95. data/lib/active_record/explain.rb +13 -24
  96. data/lib/active_record/fixtures.rb +37 -31
  97. data/lib/active_record/future_result.rb +8 -4
  98. data/lib/active_record/gem_version.rb +3 -3
  99. data/lib/active_record/inheritance.rb +4 -2
  100. data/lib/active_record/insert_all.rb +18 -15
  101. data/lib/active_record/integration.rb +4 -1
  102. data/lib/active_record/internal_metadata.rb +48 -34
  103. data/lib/active_record/locking/optimistic.rb +7 -6
  104. data/lib/active_record/log_subscriber.rb +0 -21
  105. data/lib/active_record/marshalling.rb +1 -4
  106. data/lib/active_record/message_pack.rb +1 -1
  107. data/lib/active_record/migration/command_recorder.rb +2 -3
  108. data/lib/active_record/migration/compatibility.rb +5 -3
  109. data/lib/active_record/migration/default_strategy.rb +4 -5
  110. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  111. data/lib/active_record/migration.rb +85 -76
  112. data/lib/active_record/model_schema.rb +28 -68
  113. data/lib/active_record/nested_attributes.rb +13 -16
  114. data/lib/active_record/normalization.rb +3 -7
  115. data/lib/active_record/persistence.rb +30 -352
  116. data/lib/active_record/query_cache.rb +18 -6
  117. data/lib/active_record/query_logs.rb +15 -0
  118. data/lib/active_record/querying.rb +21 -9
  119. data/lib/active_record/railtie.rb +50 -62
  120. data/lib/active_record/railties/controller_runtime.rb +13 -4
  121. data/lib/active_record/railties/databases.rake +41 -44
  122. data/lib/active_record/reflection.rb +90 -35
  123. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  124. data/lib/active_record/relation/batches.rb +3 -3
  125. data/lib/active_record/relation/calculations.rb +94 -61
  126. data/lib/active_record/relation/delegation.rb +8 -11
  127. data/lib/active_record/relation/finder_methods.rb +16 -2
  128. data/lib/active_record/relation/merger.rb +4 -6
  129. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  130. data/lib/active_record/relation/predicate_builder.rb +3 -3
  131. data/lib/active_record/relation/query_methods.rb +196 -57
  132. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  133. data/lib/active_record/relation/spawn_methods.rb +2 -18
  134. data/lib/active_record/relation/where_clause.rb +7 -19
  135. data/lib/active_record/relation.rb +496 -72
  136. data/lib/active_record/result.rb +31 -44
  137. data/lib/active_record/runtime_registry.rb +39 -0
  138. data/lib/active_record/sanitization.rb +24 -19
  139. data/lib/active_record/schema.rb +8 -6
  140. data/lib/active_record/schema_dumper.rb +19 -9
  141. data/lib/active_record/schema_migration.rb +30 -14
  142. data/lib/active_record/signed_id.rb +11 -1
  143. data/lib/active_record/statement_cache.rb +7 -7
  144. data/lib/active_record/table_metadata.rb +1 -10
  145. data/lib/active_record/tasks/database_tasks.rb +76 -70
  146. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  147. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  148. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  149. data/lib/active_record/test_fixtures.rb +81 -91
  150. data/lib/active_record/testing/query_assertions.rb +121 -0
  151. data/lib/active_record/timestamp.rb +1 -1
  152. data/lib/active_record/token_for.rb +22 -12
  153. data/lib/active_record/touch_later.rb +1 -1
  154. data/lib/active_record/transaction.rb +68 -0
  155. data/lib/active_record/transactions.rb +43 -14
  156. data/lib/active_record/translation.rb +0 -2
  157. data/lib/active_record/type/serialized.rb +1 -3
  158. data/lib/active_record/type_caster/connection.rb +4 -4
  159. data/lib/active_record/validations/associated.rb +9 -3
  160. data/lib/active_record/validations/uniqueness.rb +14 -10
  161. data/lib/active_record/validations.rb +4 -1
  162. data/lib/active_record.rb +149 -40
  163. data/lib/arel/alias_predication.rb +1 -1
  164. data/lib/arel/collectors/bind.rb +2 -0
  165. data/lib/arel/collectors/composite.rb +7 -0
  166. data/lib/arel/collectors/sql_string.rb +1 -1
  167. data/lib/arel/collectors/substitute_binds.rb +1 -1
  168. data/lib/arel/nodes/binary.rb +0 -6
  169. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  170. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  171. data/lib/arel/nodes/node.rb +4 -3
  172. data/lib/arel/nodes/sql_literal.rb +7 -0
  173. data/lib/arel/nodes.rb +2 -2
  174. data/lib/arel/predications.rb +1 -1
  175. data/lib/arel/select_manager.rb +1 -1
  176. data/lib/arel/tree_manager.rb +3 -2
  177. data/lib/arel/update_manager.rb +2 -1
  178. data/lib/arel/visitors/dot.rb +1 -0
  179. data/lib/arel/visitors/mysql.rb +9 -4
  180. data/lib/arel/visitors/postgresql.rb +1 -12
  181. data/lib/arel/visitors/to_sql.rb +29 -16
  182. data/lib/arel.rb +7 -3
  183. metadata +20 -15
@@ -421,10 +421,15 @@ module ActiveRecord
421
421
  # update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
422
422
  # then the existing record will be marked for destruction.
423
423
  def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
424
- options = nested_attributes_options[association_name]
425
424
  if attributes.respond_to?(:permitted?)
426
425
  attributes = attributes.to_h
427
426
  end
427
+
428
+ unless attributes.is_a?(Hash)
429
+ raise ArgumentError, "Hash expected for `#{association_name}` attributes, got #{attributes.class.name}"
430
+ end
431
+
432
+ options = nested_attributes_options[association_name]
428
433
  attributes = attributes.with_indifferent_access
429
434
  existing_record = send(association_name)
430
435
 
@@ -486,7 +491,7 @@ module ActiveRecord
486
491
  end
487
492
 
488
493
  unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
489
- raise ArgumentError, "Hash or Array expected for attribute `#{association_name}`, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
494
+ raise ArgumentError, "Hash or Array expected for `#{association_name}` attributes, got #{attributes_collection.class.name}"
490
495
  end
491
496
 
492
497
  check_record_limit!(options[:limit], attributes_collection)
@@ -509,7 +514,7 @@ module ActiveRecord
509
514
  attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
510
515
  end
511
516
 
512
- attributes_collection.each do |attributes|
517
+ records = attributes_collection.map do |attributes|
513
518
  if attributes.respond_to?(:permitted?)
514
519
  attributes = attributes.to_h
515
520
  end
@@ -519,12 +524,12 @@ module ActiveRecord
519
524
  unless reject_new_record?(association_name, attributes)
520
525
  association.reader.build(attributes.except(*UNASSIGNABLE_KEYS))
521
526
  end
522
- elsif existing_record = find_record_by_id(existing_records, attributes["id"])
527
+ elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes["id"].to_s }
523
528
  unless call_reject_if(association_name, attributes)
524
529
  # Make sure we are operating on the actual object which is in the association's
525
530
  # proxy_target array (either by finding it, or adding it if not found)
526
531
  # Take into account that the proxy_target may have changed due to callbacks
527
- target_record = find_record_by_id(association.target, attributes["id"])
532
+ target_record = association.target.detect { |record| record.id.to_s == attributes["id"].to_s }
528
533
  if target_record
529
534
  existing_record = target_record
530
535
  else
@@ -532,11 +537,14 @@ module ActiveRecord
532
537
  end
533
538
 
534
539
  assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
540
+ existing_record
535
541
  end
536
542
  else
537
543
  raise_nested_attributes_record_not_found!(association_name, attributes["id"])
538
544
  end
539
545
  end
546
+
547
+ association.nested_attributes_target = records
540
548
  end
541
549
 
542
550
  # Takes in a limit and checks if the attributes_collection has too many
@@ -612,16 +620,5 @@ module ActiveRecord
612
620
  raise RecordNotFound.new("Couldn't find #{model} with ID=#{record_id} for #{self.class.name} with ID=#{id}",
613
621
  model, "id", record_id)
614
622
  end
615
-
616
- def find_record_by_id(records, id)
617
- return if records.empty?
618
-
619
- if records.first.class.composite_primary_key?
620
- id = Array(id).map(&:to_s)
621
- records.find { |record| Array(record.id).map(&:to_s) == id }
622
- else
623
- records.find { |record| record.id.to_s == id.to_s }
624
- end
625
- end
626
623
  end
627
624
  end
@@ -86,10 +86,8 @@ module ActiveRecord # :nodoc:
86
86
  #
87
87
  # User.normalize_value_for(:phone, "+1 (555) 867-5309") # => "5558675309"
88
88
  def normalizes(*names, with:, apply_to_nil: false)
89
- names.each do |name|
90
- attribute(name) do |cast_type|
91
- NormalizedValueType.new(cast_type: cast_type, normalizer: with, normalize_nil: apply_to_nil)
92
- end
89
+ decorate_attributes(names) do |name, cast_type|
90
+ NormalizedValueType.new(cast_type: cast_type, normalizer: with, normalize_nil: apply_to_nil)
93
91
  end
94
92
 
95
93
  self.normalized_attributes += names.map(&:to_sym)
@@ -154,9 +152,7 @@ module ActiveRecord # :nodoc:
154
152
  [self.class, cast_type, normalizer, normalize_nil?].hash
155
153
  end
156
154
 
157
- def inspect
158
- Kernel.instance_method(:inspect).bind_call(self)
159
- end
155
+ define_method(:inspect, Kernel.instance_method(:inspect))
160
156
 
161
157
  private
162
158
  def normalize(value)
@@ -87,282 +87,6 @@ module ActiveRecord
87
87
  end
88
88
  end
89
89
 
90
- # Inserts a single record into the database in a single SQL INSERT
91
- # statement. It does not instantiate any models nor does it trigger
92
- # Active Record callbacks or validations. Though passed values
93
- # go through Active Record's type casting and serialization.
94
- #
95
- # See #insert_all for documentation.
96
- def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
97
- insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
98
- end
99
-
100
- # Inserts multiple records into the database in a single SQL INSERT
101
- # statement. It does not instantiate any models nor does it trigger
102
- # Active Record callbacks or validations. Though passed values
103
- # go through Active Record's type casting and serialization.
104
- #
105
- # The +attributes+ parameter is an Array of Hashes. Every Hash determines
106
- # the attributes for a single row and must have the same keys.
107
- #
108
- # Rows are considered to be unique by every unique index on the table. Any
109
- # duplicate rows are skipped.
110
- # Override with <tt>:unique_by</tt> (see below).
111
- #
112
- # Returns an ActiveRecord::Result with its contents based on
113
- # <tt>:returning</tt> (see below).
114
- #
115
- # ==== Options
116
- #
117
- # [:returning]
118
- # (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
119
- # inserted records, which by default is the primary key.
120
- # Pass <tt>returning: %w[ id name ]</tt> for both id and name
121
- # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
122
- # clause entirely.
123
- #
124
- # You can also pass an SQL string if you need more control on the return values
125
- # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
126
- #
127
- # [:unique_by]
128
- # (PostgreSQL and SQLite only) By default rows are considered to be unique
129
- # by every unique index on the table. Any duplicate rows are skipped.
130
- #
131
- # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
132
- #
133
- # Consider a Book model where no duplicate ISBNs make sense, but if any
134
- # row has an existing id, or is not unique by another unique index,
135
- # ActiveRecord::RecordNotUnique is raised.
136
- #
137
- # Unique indexes can be identified by columns or name:
138
- #
139
- # unique_by: :isbn
140
- # unique_by: %i[ author_id name ]
141
- # unique_by: :index_books_on_isbn
142
- #
143
- # [:record_timestamps]
144
- # By default, automatic setting of timestamp columns is controlled by
145
- # the model's <tt>record_timestamps</tt> config, matching typical
146
- # behavior.
147
- #
148
- # To override this and force automatic setting of timestamp columns one
149
- # way or the other, pass <tt>:record_timestamps</tt>:
150
- #
151
- # record_timestamps: true # Always set timestamps automatically
152
- # record_timestamps: false # Never set timestamps automatically
153
- #
154
- # Because it relies on the index information from the database
155
- # <tt>:unique_by</tt> is recommended to be paired with
156
- # Active Record's schema_cache.
157
- #
158
- # ==== Example
159
- #
160
- # # Insert records and skip inserting any duplicates.
161
- # # Here "Eloquent Ruby" is skipped because its id is not unique.
162
- #
163
- # Book.insert_all([
164
- # { id: 1, title: "Rework", author: "David" },
165
- # { id: 1, title: "Eloquent Ruby", author: "Russ" }
166
- # ])
167
- #
168
- # # insert_all works on chained scopes, and you can use create_with
169
- # # to set default attributes for all inserted records.
170
- #
171
- # author.books.create_with(created_at: Time.now).insert_all([
172
- # { id: 1, title: "Rework" },
173
- # { id: 2, title: "Eloquent Ruby" }
174
- # ])
175
- def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
176
- InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
177
- end
178
-
179
- # Inserts a single record into the database in a single SQL INSERT
180
- # statement. It does not instantiate any models nor does it trigger
181
- # Active Record callbacks or validations. Though passed values
182
- # go through Active Record's type casting and serialization.
183
- #
184
- # See #insert_all! for more.
185
- def insert!(attributes, returning: nil, record_timestamps: nil)
186
- insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
187
- end
188
-
189
- # Inserts multiple records into the database in a single SQL INSERT
190
- # statement. It does not instantiate any models nor does it trigger
191
- # Active Record callbacks or validations. Though passed values
192
- # go through Active Record's type casting and serialization.
193
- #
194
- # The +attributes+ parameter is an Array of Hashes. Every Hash determines
195
- # the attributes for a single row and must have the same keys.
196
- #
197
- # Raises ActiveRecord::RecordNotUnique if any rows violate a
198
- # unique index on the table. In that case, no rows are inserted.
199
- #
200
- # To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
201
- #
202
- # Returns an ActiveRecord::Result with its contents based on
203
- # <tt>:returning</tt> (see below).
204
- #
205
- # ==== Options
206
- #
207
- # [:returning]
208
- # (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
209
- # inserted records, which by default is the primary key.
210
- # Pass <tt>returning: %w[ id name ]</tt> for both id and name
211
- # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
212
- # clause entirely.
213
- #
214
- # You can also pass an SQL string if you need more control on the return values
215
- # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
216
- #
217
- # [:record_timestamps]
218
- # By default, automatic setting of timestamp columns is controlled by
219
- # the model's <tt>record_timestamps</tt> config, matching typical
220
- # behavior.
221
- #
222
- # To override this and force automatic setting of timestamp columns one
223
- # way or the other, pass <tt>:record_timestamps</tt>:
224
- #
225
- # record_timestamps: true # Always set timestamps automatically
226
- # record_timestamps: false # Never set timestamps automatically
227
- #
228
- # ==== Examples
229
- #
230
- # # Insert multiple records
231
- # Book.insert_all!([
232
- # { title: "Rework", author: "David" },
233
- # { title: "Eloquent Ruby", author: "Russ" }
234
- # ])
235
- #
236
- # # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
237
- # # does not have a unique id.
238
- # Book.insert_all!([
239
- # { id: 1, title: "Rework", author: "David" },
240
- # { id: 1, title: "Eloquent Ruby", author: "Russ" }
241
- # ])
242
- def insert_all!(attributes, returning: nil, record_timestamps: nil)
243
- InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
244
- end
245
-
246
- # Updates or inserts (upserts) a single record into the database in a
247
- # single SQL INSERT statement. It does not instantiate any models nor does
248
- # it trigger Active Record callbacks or validations. Though passed values
249
- # go through Active Record's type casting and serialization.
250
- #
251
- # See #upsert_all for documentation.
252
- def upsert(attributes, **kwargs)
253
- upsert_all([ attributes ], **kwargs)
254
- end
255
-
256
- # Updates or inserts (upserts) multiple records into the database in a
257
- # single SQL INSERT statement. It does not instantiate any models nor does
258
- # it trigger Active Record callbacks or validations. Though passed values
259
- # go through Active Record's type casting and serialization.
260
- #
261
- # The +attributes+ parameter is an Array of Hashes. Every Hash determines
262
- # the attributes for a single row and must have the same keys.
263
- #
264
- # Returns an ActiveRecord::Result with its contents based on
265
- # <tt>:returning</tt> (see below).
266
- #
267
- # By default, +upsert_all+ will update all the columns that can be updated when
268
- # there is a conflict. These are all the columns except primary keys, read-only
269
- # columns, and columns covered by the optional +unique_by+.
270
- #
271
- # ==== Options
272
- #
273
- # [:returning]
274
- # (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
275
- # inserted records, which by default is the primary key.
276
- # Pass <tt>returning: %w[ id name ]</tt> for both id and name
277
- # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
278
- # clause entirely.
279
- #
280
- # You can also pass an SQL string if you need more control on the return values
281
- # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
282
- #
283
- # [:unique_by]
284
- # (PostgreSQL and SQLite only) By default rows are considered to be unique
285
- # by every unique index on the table. Any duplicate rows are skipped.
286
- #
287
- # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
288
- #
289
- # Consider a Book model where no duplicate ISBNs make sense, but if any
290
- # row has an existing id, or is not unique by another unique index,
291
- # ActiveRecord::RecordNotUnique is raised.
292
- #
293
- # Unique indexes can be identified by columns or name:
294
- #
295
- # unique_by: :isbn
296
- # unique_by: %i[ author_id name ]
297
- # unique_by: :index_books_on_isbn
298
- #
299
- # Because it relies on the index information from the database
300
- # <tt>:unique_by</tt> is recommended to be paired with
301
- # Active Record's schema_cache.
302
- #
303
- # [:on_duplicate]
304
- # Configure the SQL update sentence that will be used in case of conflict.
305
- #
306
- # NOTE: If you use this option you must provide all the columns you want to update
307
- # by yourself.
308
- #
309
- # Example:
310
- #
311
- # Commodity.upsert_all(
312
- # [
313
- # { id: 2, name: "Copper", price: 4.84 },
314
- # { id: 4, name: "Gold", price: 1380.87 },
315
- # { id: 6, name: "Aluminium", price: 0.35 }
316
- # ],
317
- # on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
318
- # )
319
- #
320
- # See the related +:update_only+ option. Both options can't be used at the same time.
321
- #
322
- # [:update_only]
323
- # Provide a list of column names that will be updated in case of conflict. If not provided,
324
- # +upsert_all+ will update all the columns that can be updated. These are all the columns
325
- # except primary keys, read-only columns, and columns covered by the optional +unique_by+
326
- #
327
- # Example:
328
- #
329
- # Commodity.upsert_all(
330
- # [
331
- # { id: 2, name: "Copper", price: 4.84 },
332
- # { id: 4, name: "Gold", price: 1380.87 },
333
- # { id: 6, name: "Aluminium", price: 0.35 }
334
- # ],
335
- # update_only: [:price] # Only prices will be updated
336
- # )
337
- #
338
- # See the related +:on_duplicate+ option. Both options can't be used at the same time.
339
- #
340
- # [:record_timestamps]
341
- # By default, automatic setting of timestamp columns is controlled by
342
- # the model's <tt>record_timestamps</tt> config, matching typical
343
- # behavior.
344
- #
345
- # To override this and force automatic setting of timestamp columns one
346
- # way or the other, pass <tt>:record_timestamps</tt>:
347
- #
348
- # record_timestamps: true # Always set timestamps automatically
349
- # record_timestamps: false # Never set timestamps automatically
350
- #
351
- # ==== Examples
352
- #
353
- # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
354
- # # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
355
- #
356
- # Book.upsert_all([
357
- # { title: "Rework", author: "David", isbn: "1" },
358
- # { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
359
- # ], unique_by: :isbn)
360
- #
361
- # Book.find_by(isbn: "1").title # => "Eloquent Ruby"
362
- def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
363
- InsertAll.new(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
364
- end
365
-
366
90
  # Given an attributes hash, +instantiate+ returns a new instance of
367
91
  # the appropriate class. Accepts only keys as strings.
368
92
  #
@@ -511,62 +235,7 @@ module ActiveRecord
511
235
  @composite_query_constraints_list ||= query_constraints_list || Array(primary_key)
512
236
  end
513
237
 
514
- # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
515
- # therefore all callbacks and filters are fired off before the object is deleted. This method is
516
- # less efficient than #delete but allows cleanup methods and other actions to be run.
517
- #
518
- # This essentially finds the object (or multiple objects) with the given id, creates a new object
519
- # from the attributes, and then calls destroy on it.
520
- #
521
- # ==== Parameters
522
- #
523
- # * +id+ - This should be the id or an array of ids to be destroyed.
524
- #
525
- # ==== Examples
526
- #
527
- # # Destroy a single object
528
- # Todo.destroy(1)
529
- #
530
- # # Destroy multiple objects
531
- # todos = [1,2,3]
532
- # Todo.destroy(todos)
533
- def destroy(id)
534
- multiple_ids = if composite_primary_key?
535
- id.first.is_a?(Array)
536
- else
537
- id.is_a?(Array)
538
- end
539
-
540
- if multiple_ids
541
- find(id).each(&:destroy)
542
- else
543
- find(id).destroy
544
- end
545
- end
546
-
547
- # Deletes the row with a primary key matching the +id+ argument, using an
548
- # SQL +DELETE+ statement, and returns the number of rows deleted. Active
549
- # Record objects are not instantiated, so the object's callbacks are not
550
- # executed, including any <tt>:dependent</tt> association options.
551
- #
552
- # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
553
- #
554
- # Note: Although it is often much faster than the alternative, #destroy,
555
- # skipping callbacks might bypass business logic in your application
556
- # that ensures referential integrity or performs other essential jobs.
557
- #
558
- # ==== Examples
559
- #
560
- # # Delete a single row
561
- # Todo.delete(1)
562
- #
563
- # # Delete multiple rows
564
- # Todo.delete([2,3,4])
565
- def delete(id_or_array)
566
- delete_by(primary_key => id_or_array)
567
- end
568
-
569
- def _insert_record(values, returning) # :nodoc:
238
+ def _insert_record(connection, values, returning) # :nodoc:
570
239
  primary_key = self.primary_key
571
240
  primary_key_value = nil
572
241
 
@@ -579,16 +248,18 @@ module ActiveRecord
579
248
 
580
249
  im = Arel::InsertManager.new(arel_table)
581
250
 
582
- if values.empty?
583
- im.insert(connection.empty_insert_statement_value(primary_key))
584
- else
585
- im.insert(values.transform_keys { |name| arel_table[name] })
586
- end
251
+ with_connection do |c|
252
+ if values.empty?
253
+ im.insert(connection.empty_insert_statement_value(primary_key))
254
+ else
255
+ im.insert(values.transform_keys { |name| arel_table[name] })
256
+ end
587
257
 
588
- connection.insert(
589
- im, "#{self} Create", primary_key || false, primary_key_value,
590
- returning: returning
591
- )
258
+ connection.insert(
259
+ im, "#{self} Create", primary_key || false, primary_key_value,
260
+ returning: returning
261
+ )
262
+ end
592
263
  end
593
264
 
594
265
  def _update_record(values, constraints) # :nodoc:
@@ -605,7 +276,9 @@ module ActiveRecord
605
276
  um.set(values.transform_keys { |name| arel_table[name] })
606
277
  um.wheres = constraints
607
278
 
608
- connection.update(um, "#{self} Update")
279
+ with_connection do |c|
280
+ c.update(um, "#{self} Update")
281
+ end
609
282
  end
610
283
 
611
284
  def _delete_record(constraints) # :nodoc:
@@ -621,7 +294,9 @@ module ActiveRecord
621
294
  dm = Arel::DeleteManager.new(arel_table)
622
295
  dm.wheres = constraints
623
296
 
624
- connection.delete(dm, "#{self} Destroy")
297
+ with_connection do |c|
298
+ c.delete(dm, "#{self} Destroy")
299
+ end
625
300
  end
626
301
 
627
302
  private
@@ -1067,7 +742,7 @@ module ActiveRecord
1067
742
  # end
1068
743
  #
1069
744
  def reload(options = nil)
1070
- self.class.connection.clear_query_cache
745
+ self.class.connection_pool.clear_query_cache
1071
746
 
1072
747
  fresh_object = if apply_scoping?(options)
1073
748
  _find_record((options || {}).merge(all_queries: true))
@@ -1247,16 +922,19 @@ module ActiveRecord
1247
922
  def _create_record(attribute_names = self.attribute_names)
1248
923
  attribute_names = attributes_for_create(attribute_names)
1249
924
 
1250
- returning_columns = self.class._returning_columns_for_insert
925
+ self.class.with_connection do |connection|
926
+ returning_columns = self.class._returning_columns_for_insert(connection)
1251
927
 
1252
- returning_values = self.class._insert_record(
1253
- attributes_with_values(attribute_names),
1254
- returning_columns
1255
- )
928
+ returning_values = self.class._insert_record(
929
+ connection,
930
+ attributes_with_values(attribute_names),
931
+ returning_columns
932
+ )
1256
933
 
1257
- returning_columns.zip(returning_values).each do |column, value|
1258
- _write_attribute(column, value) if !_read_attribute(column)
1259
- end if returning_values
934
+ returning_columns.zip(returning_values).each do |column, value|
935
+ _write_attribute(column, value) if !_read_attribute(column)
936
+ end if returning_values
937
+ end
1260
938
 
1261
939
  @new_record = false
1262
940
  @previously_new_record = true
@@ -8,7 +8,13 @@ module ActiveRecord
8
8
  # If it's not, it will execute the given block.
9
9
  def cache(&block)
10
10
  if connected? || !configurations.empty?
11
- connection.cache(&block)
11
+ pool = connection_pool
12
+ was_enabled = pool.query_cache_enabled
13
+ begin
14
+ pool.enable_query_cache(&block)
15
+ ensure
16
+ pool.clear_query_cache unless was_enabled
17
+ end
12
18
  else
13
19
  yield
14
20
  end
@@ -16,9 +22,12 @@ module ActiveRecord
16
22
 
17
23
  # Disable the query cache within the block if Active Record is configured.
18
24
  # If it's not, it will execute the given block.
19
- def uncached(&block)
25
+ #
26
+ # Set <tt>dirties: false</tt> to prevent query caches on all connections from being cleared by write operations.
27
+ # (By default, write operations dirty all connections' query caches in case they are replicas whose cache would now be outdated.)
28
+ def uncached(dirties: true, &block)
20
29
  if connected? || !configurations.empty?
21
- connection.uncached(&block)
30
+ connection_pool.disable_query_cache(dirties: dirties, &block)
22
31
  else
23
32
  yield
24
33
  end
@@ -26,14 +35,17 @@ module ActiveRecord
26
35
  end
27
36
 
28
37
  def self.run
29
- ActiveRecord::Base.connection_handler.each_connection_pool.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! }
38
+ ActiveRecord::Base.connection_handler.each_connection_pool.reject(&:query_cache_enabled).each(&:enable_query_cache!)
30
39
  end
31
40
 
32
41
  def self.complete(pools)
33
- pools.each { |pool| pool.disable_query_cache! unless pool.discarded? }
42
+ pools.each do |pool|
43
+ pool.disable_query_cache!
44
+ pool.clear_query_cache
45
+ end
34
46
 
35
47
  ActiveRecord::Base.connection_handler.each_connection_pool do |pool|
36
- pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
48
+ pool.release_connection if pool.active_connection? && !pool.lease_connection.transaction_open?
37
49
  end
38
50
  end
39
51
 
@@ -26,6 +26,7 @@ module ActiveRecord
26
26
  # * +socket+
27
27
  # * +db_host+
28
28
  # * +database+
29
+ # * +source_location+
29
30
  #
30
31
  # Action Controller adds default tags when loaded:
31
32
  #
@@ -108,6 +109,20 @@ module ActiveRecord
108
109
  end
109
110
  end
110
111
 
112
+ if Thread.respond_to?(:each_caller_location)
113
+ def query_source_location # :nodoc:
114
+ Thread.each_caller_location do |location|
115
+ frame = LogSubscriber.backtrace_cleaner.clean_frame(location.path)
116
+ return frame if frame
117
+ end
118
+ nil
119
+ end
120
+ else
121
+ def query_source_location # :nodoc:
122
+ LogSubscriber.backtrace_cleaner.clean(caller_locations(1).each).first
123
+ end
124
+ end
125
+
111
126
  ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
112
127
 
113
128
  private