activerecord 7.1.5.1 → 7.2.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +515 -2445
  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