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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +515 -2445
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +9 -8
- data/lib/active_record/associations/belongs_to_association.rb +14 -7
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/collection_association.rb +6 -4
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +29 -28
- data/lib/active_record/associations/join_dependency.rb +5 -5
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +2 -1
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +33 -16
- data/lib/active_record/attribute_assignment.rb +1 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +1 -1
- data/lib/active_record/attribute_methods/primary_key.rb +23 -55
- data/lib/active_record/attribute_methods/read.rb +4 -16
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -10
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +60 -71
- data/lib/active_record/attributes.rb +55 -42
- data/lib/active_record/autosave_association.rb +13 -32
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -65
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +159 -74
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
- data/lib/active_record/connection_adapters/abstract_adapter.rb +18 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +32 -6
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -23
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -13
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +107 -75
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +53 -37
- data/lib/active_record/counter_cache.rb +18 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- data/lib/active_record/database_configurations/database_config.rb +15 -4
- data/lib/active_record/database_configurations/hash_config.rb +38 -34
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +24 -0
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +2 -2
- data/lib/active_record/encryption/encrypted_attribute_type.rb +22 -2
- data/lib/active_record/encryption/encryptor.rb +17 -2
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption.rb +0 -2
- data/lib/active_record/enum.rb +10 -1
- data/lib/active_record/errors.rb +16 -11
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +8 -4
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +18 -15
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +7 -6
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/marshalling.rb +1 -4
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +5 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +85 -76
- data/lib/active_record/model_schema.rb +28 -68
- data/lib/active_record/nested_attributes.rb +13 -16
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +30 -352
- data/lib/active_record/query_cache.rb +18 -6
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +50 -62
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +41 -44
- data/lib/active_record/reflection.rb +90 -35
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +94 -61
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +16 -2
- data/lib/active_record/relation/merger.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +196 -57
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +496 -72
- data/lib/active_record/result.rb +31 -44
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +19 -9
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/signed_id.rb +11 -1
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/table_metadata.rb +1 -10
- data/lib/active_record/tasks/database_tasks.rb +76 -70
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +81 -91
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +1 -1
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +68 -0
- data/lib/active_record/transactions.rb +43 -14
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +14 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +149 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/nodes/binary.rb +0 -6
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +4 -3
- data/lib/arel/nodes/sql_literal.rb +7 -0
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- 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
|
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.
|
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 =
|
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 =
|
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
|
90
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
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
|
-
|
589
|
-
|
590
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|
-
|
925
|
+
self.class.with_connection do |connection|
|
926
|
+
returning_columns = self.class._returning_columns_for_insert(connection)
|
1251
927
|
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
928
|
+
returning_values = self.class._insert_record(
|
929
|
+
connection,
|
930
|
+
attributes_with_values(attribute_names),
|
931
|
+
returning_columns
|
932
|
+
)
|
1256
933
|
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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.
|
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
|