activerecord 8.0.0 → 8.1.2
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 +703 -248
- data/README.rdoc +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +6 -4
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/belongs_to_association.rb +18 -2
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +17 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +3 -3
- data/lib/active_record/associations/collection_proxy.rb +22 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations.rb +159 -21
- data/lib/active_record/attribute_methods/primary_key.rb +2 -1
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +17 -4
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +10 -2
- data/lib/active_record/attribute_methods.rb +23 -18
- data/lib/active_record/attributes.rb +40 -26
- data/lib/active_record/autosave_association.rb +22 -12
- data/lib/active_record/base.rb +3 -4
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +19 -18
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +458 -108
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +55 -40
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -9
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +31 -35
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +89 -23
- data/lib/active_record/connection_adapters/abstract/transaction.rb +25 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -64
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +57 -20
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +33 -4
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +66 -15
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -3
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +26 -17
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +8 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -33
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +67 -31
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +82 -49
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +38 -10
- data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -23
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +14 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +5 -12
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +65 -36
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -3
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +2 -2
- data/lib/active_record/connection_adapters.rb +1 -0
- data/lib/active_record/connection_handling.rb +15 -10
- data/lib/active_record/core.rb +44 -12
- data/lib/active_record/counter_cache.rb +34 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
- data/lib/active_record/database_configurations/database_config.rb +5 -1
- data/lib/active_record/database_configurations/hash_config.rb +59 -9
- data/lib/active_record/database_configurations/url_config.rb +13 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +19 -19
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
- data/lib/active_record/encryption/encryptor.rb +39 -25
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +37 -20
- data/lib/active_record/errors.rb +23 -7
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +2 -2
- data/lib/active_record/future_result.rb +3 -3
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +12 -7
- data/lib/active_record/locking/optimistic.rb +7 -0
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +2 -6
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +19 -3
- data/lib/active_record/migration/compatibility.rb +34 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +31 -21
- data/lib/active_record/model_schema.rb +36 -10
- data/lib/active_record/nested_attributes.rb +2 -0
- data/lib/active_record/persistence.rb +34 -3
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +7 -7
- data/lib/active_record/querying.rb +4 -4
- data/lib/active_record/railtie.rb +35 -6
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +24 -20
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +35 -0
- data/lib/active_record/relation/batches.rb +25 -11
- data/lib/active_record/relation/calculations.rb +54 -38
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +42 -25
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -9
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +7 -7
- data/lib/active_record/relation/predicate_builder.rb +9 -7
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +43 -32
- data/lib/active_record/relation/spawn_methods.rb +6 -6
- data/lib/active_record/relation/where_clause.rb +10 -11
- data/lib/active_record/relation.rb +43 -19
- data/lib/active_record/result.rb +44 -21
- data/lib/active_record/runtime_registry.rb +42 -58
- data/lib/active_record/sanitization.rb +2 -0
- data/lib/active_record/schema_dumper.rb +42 -22
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +47 -18
- data/lib/active_record/statement_cache.rb +15 -11
- data/lib/active_record/store.rb +44 -19
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +5 -20
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +44 -45
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +27 -2
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +39 -16
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +11 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record.rb +71 -6
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/select_manager.rb +13 -4
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +6 -22
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +16 -15
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
|
@@ -100,7 +100,8 @@ module ActiveRecord
|
|
|
100
100
|
db_config = resolve_config_for_connection(database_key)
|
|
101
101
|
|
|
102
102
|
self.connection_class = true
|
|
103
|
-
|
|
103
|
+
shard = shard.to_sym unless shard.is_a? Integer
|
|
104
|
+
connections << connection_handler.establish_connection(db_config, owner_name: self, role: role, shard: shard)
|
|
104
105
|
end
|
|
105
106
|
end
|
|
106
107
|
|
|
@@ -172,9 +173,11 @@ module ActiveRecord
|
|
|
172
173
|
prevent_writes = true if role == ActiveRecord.reading_role
|
|
173
174
|
|
|
174
175
|
append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes)
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
begin
|
|
177
|
+
yield
|
|
178
|
+
ensure
|
|
179
|
+
connected_to_stack.pop
|
|
180
|
+
end
|
|
178
181
|
end
|
|
179
182
|
|
|
180
183
|
# Passes the block to +connected_to+ for every +shard+ the
|
|
@@ -301,7 +304,7 @@ module ActiveRecord
|
|
|
301
304
|
|
|
302
305
|
# Checkouts a connection from the pool, yield it and then check it back in.
|
|
303
306
|
# If a connection was already leased via #lease_connection or a parent call to
|
|
304
|
-
# #with_connection, that same connection is
|
|
307
|
+
# #with_connection, that same connection is yielded.
|
|
305
308
|
# If #lease_connection is called inside the block, the connection won't be checked
|
|
306
309
|
# back in.
|
|
307
310
|
# If #connection is called inside the block, the connection won't be checked back in
|
|
@@ -395,11 +398,13 @@ module ActiveRecord
|
|
|
395
398
|
prevent_writes = true if role == ActiveRecord.reading_role
|
|
396
399
|
|
|
397
400
|
append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
401
|
+
begin
|
|
402
|
+
return_value = yield
|
|
403
|
+
return_value.load if return_value.is_a? ActiveRecord::Relation
|
|
404
|
+
return_value
|
|
405
|
+
ensure
|
|
406
|
+
self.connected_to_stack.pop
|
|
407
|
+
end
|
|
403
408
|
end
|
|
404
409
|
|
|
405
410
|
def append_to_connected_to_stack(entry)
|
data/lib/active_record/core.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "active_support/core_ext/enumerable"
|
|
4
|
-
require "active_support/core_ext/module/delegation"
|
|
5
4
|
require "active_support/parameter_filter"
|
|
6
5
|
require "concurrent/map"
|
|
7
6
|
|
|
@@ -112,7 +111,7 @@ module ActiveRecord
|
|
|
112
111
|
# Post.attributes_for_inspect = [:id, :title]
|
|
113
112
|
# Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!">"
|
|
114
113
|
#
|
|
115
|
-
# When set to
|
|
114
|
+
# When set to +:all+ inspect will list all the record's attributes:
|
|
116
115
|
#
|
|
117
116
|
# Post.attributes_for_inspect = :all
|
|
118
117
|
# Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
|
@@ -202,6 +201,17 @@ module ActiveRecord
|
|
|
202
201
|
false
|
|
203
202
|
end
|
|
204
203
|
|
|
204
|
+
# Intended to behave like `.current_preventing_writes` given the class name as input.
|
|
205
|
+
# See PoolConfig and ConnectionHandler::ConnectionDescriptor.
|
|
206
|
+
def self.preventing_writes?(class_name) # :nodoc:
|
|
207
|
+
connected_to_stack.reverse_each do |hash|
|
|
208
|
+
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
|
|
209
|
+
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].any? { |klass| klass.name == class_name }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
false
|
|
213
|
+
end
|
|
214
|
+
|
|
205
215
|
def self.connected_to_stack # :nodoc:
|
|
206
216
|
if connected_to_stack = ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack]
|
|
207
217
|
connected_to_stack
|
|
@@ -266,7 +276,7 @@ module ActiveRecord
|
|
|
266
276
|
return super if StatementCache.unsupported_value?(id)
|
|
267
277
|
|
|
268
278
|
cached_find_by([primary_key], [id]) ||
|
|
269
|
-
raise(RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}", name, primary_key, id))
|
|
279
|
+
raise(RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id.inspect}", name, primary_key, id))
|
|
270
280
|
end
|
|
271
281
|
|
|
272
282
|
def find_by(*args) # :nodoc:
|
|
@@ -347,6 +357,8 @@ module ActiveRecord
|
|
|
347
357
|
def filter_attributes=(filter_attributes)
|
|
348
358
|
@inspection_filter = nil
|
|
349
359
|
@filter_attributes = filter_attributes
|
|
360
|
+
|
|
361
|
+
FilterAttributeHandler.sensitive_attribute_was_declared(self, filter_attributes)
|
|
350
362
|
end
|
|
351
363
|
|
|
352
364
|
def inspection_filter # :nodoc:
|
|
@@ -440,7 +452,7 @@ module ActiveRecord
|
|
|
440
452
|
where(wheres).limit(1)
|
|
441
453
|
}
|
|
442
454
|
|
|
443
|
-
statement.execute(values.flatten, connection
|
|
455
|
+
statement.execute(values.flatten, connection).then do |r|
|
|
444
456
|
r.first
|
|
445
457
|
rescue TypeError
|
|
446
458
|
raise ActiveRecord::StatementInvalid
|
|
@@ -589,7 +601,7 @@ module ActiveRecord
|
|
|
589
601
|
#
|
|
590
602
|
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
|
591
603
|
# topic.slice(:title, :author_name)
|
|
592
|
-
# => { "title" => "Budget", "author_name" => "Jason" }
|
|
604
|
+
# # => { "title" => "Budget", "author_name" => "Jason" }
|
|
593
605
|
#
|
|
594
606
|
#--
|
|
595
607
|
# Implemented by ActiveModel::Access#slice.
|
|
@@ -603,7 +615,7 @@ module ActiveRecord
|
|
|
603
615
|
#
|
|
604
616
|
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
|
605
617
|
# topic.values_at(:title, :author_name)
|
|
606
|
-
# => ["Budget", "Jason"]
|
|
618
|
+
# # => ["Budget", "Jason"]
|
|
607
619
|
#
|
|
608
620
|
#--
|
|
609
621
|
# Implemented by ActiveModel::Access#values_at.
|
|
@@ -630,7 +642,7 @@ module ActiveRecord
|
|
|
630
642
|
def hash
|
|
631
643
|
id = self.id
|
|
632
644
|
|
|
633
|
-
if primary_key_values_present?
|
|
645
|
+
if self.class.composite_primary_key? ? primary_key_values_present? : id
|
|
634
646
|
self.class.hash ^ id.hash
|
|
635
647
|
else
|
|
636
648
|
super
|
|
@@ -680,12 +692,14 @@ module ActiveRecord
|
|
|
680
692
|
# Sets the record to strict_loading mode. This will raise an error
|
|
681
693
|
# if the record tries to lazily load an association.
|
|
682
694
|
#
|
|
695
|
+
# NOTE: Strict loading is disabled during validation in order to let the record validate its association.
|
|
696
|
+
#
|
|
683
697
|
# user = User.first
|
|
684
698
|
# user.strict_loading! # => true
|
|
685
699
|
# user.address.city
|
|
686
|
-
# => ActiveRecord::StrictLoadingViolationError
|
|
700
|
+
# # => ActiveRecord::StrictLoadingViolationError
|
|
687
701
|
# user.comments.to_a
|
|
688
|
-
# => ActiveRecord::StrictLoadingViolationError
|
|
702
|
+
# # => ActiveRecord::StrictLoadingViolationError
|
|
689
703
|
#
|
|
690
704
|
# ==== Parameters
|
|
691
705
|
#
|
|
@@ -705,7 +719,7 @@ module ActiveRecord
|
|
|
705
719
|
# user.address.city # => "Tatooine"
|
|
706
720
|
# user.comments.to_a # => [#<Comment:0x00...]
|
|
707
721
|
# user.comments.first.ratings.to_a
|
|
708
|
-
# => ActiveRecord::StrictLoadingViolationError
|
|
722
|
+
# # => ActiveRecord::StrictLoadingViolationError
|
|
709
723
|
def strict_loading!(value = true, mode: :all)
|
|
710
724
|
unless [:all, :n_plus_one_only].include?(mode)
|
|
711
725
|
raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only] but #{mode.inspect} was provided."
|
|
@@ -727,11 +741,29 @@ module ActiveRecord
|
|
|
727
741
|
@strict_loading_mode == :all
|
|
728
742
|
end
|
|
729
743
|
|
|
730
|
-
#
|
|
744
|
+
# Prevents records from being written to the database:
|
|
745
|
+
#
|
|
746
|
+
# customer = Customer.new
|
|
747
|
+
# customer.readonly!
|
|
748
|
+
# customer.save # raises ActiveRecord::ReadOnlyRecord
|
|
749
|
+
#
|
|
750
|
+
# customer = Customer.first
|
|
751
|
+
# customer.readonly!
|
|
752
|
+
# customer.update(name: 'New Name') # raises ActiveRecord::ReadOnlyRecord
|
|
753
|
+
#
|
|
754
|
+
# Read-only records cannot be deleted from the database either:
|
|
731
755
|
#
|
|
732
756
|
# customer = Customer.first
|
|
733
757
|
# customer.readonly!
|
|
734
|
-
# customer.
|
|
758
|
+
# customer.destroy # raises ActiveRecord::ReadOnlyRecord
|
|
759
|
+
#
|
|
760
|
+
# Please, note that the objects themselves are still mutable in memory:
|
|
761
|
+
#
|
|
762
|
+
# customer = Customer.new
|
|
763
|
+
# customer.readonly!
|
|
764
|
+
# customer.name = 'New Name' # OK
|
|
765
|
+
#
|
|
766
|
+
# but you won't be able to persist the changes.
|
|
735
767
|
def readonly!
|
|
736
768
|
@readonly = true
|
|
737
769
|
end
|
|
@@ -17,7 +17,7 @@ module ActiveRecord
|
|
|
17
17
|
#
|
|
18
18
|
# ==== Parameters
|
|
19
19
|
#
|
|
20
|
-
# * +id+ - The id of the object you wish to reset a counter on.
|
|
20
|
+
# * +id+ - The id of the object you wish to reset a counter on or an array of ids.
|
|
21
21
|
# * +counters+ - One or more association counters to reset. Association name or counter name can be given.
|
|
22
22
|
# * <tt>:touch</tt> - Touch timestamp columns when updating.
|
|
23
23
|
# Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
|
|
@@ -28,13 +28,25 @@ module ActiveRecord
|
|
|
28
28
|
# # For the Post with id #1, reset the comments_count
|
|
29
29
|
# Post.reset_counters(1, :comments)
|
|
30
30
|
#
|
|
31
|
-
# #
|
|
31
|
+
# # For posts with ids #1 and #2, reset the comments_count
|
|
32
|
+
# Post.reset_counters([1, 2], :comments)
|
|
33
|
+
#
|
|
34
|
+
# # Like above, but also touch the updated_at and/or updated_on
|
|
32
35
|
# # attributes.
|
|
33
36
|
# Post.reset_counters(1, :comments, touch: true)
|
|
34
37
|
def reset_counters(id, *counters, touch: nil)
|
|
35
|
-
|
|
38
|
+
ids = if composite_primary_key?
|
|
39
|
+
if id.first.is_a?(Array)
|
|
40
|
+
id
|
|
41
|
+
else
|
|
42
|
+
[id]
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
Array(id)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
updates = Hash.new { |h, k| h[k] = {} }
|
|
36
49
|
|
|
37
|
-
updates = {}
|
|
38
50
|
counters.each do |counter_association|
|
|
39
51
|
has_many_association = _reflect_on_association(counter_association)
|
|
40
52
|
unless has_many_association
|
|
@@ -48,14 +60,22 @@ module ActiveRecord
|
|
|
48
60
|
has_many_association = has_many_association.through_reflection
|
|
49
61
|
end
|
|
50
62
|
|
|
63
|
+
counter_association = counter_association.to_sym
|
|
51
64
|
foreign_key = has_many_association.foreign_key.to_s
|
|
52
65
|
child_class = has_many_association.klass
|
|
53
66
|
reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
|
|
54
67
|
counter_name = reflection.counter_cache_column
|
|
55
68
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
counts =
|
|
70
|
+
unscoped
|
|
71
|
+
.joins(counter_association)
|
|
72
|
+
.where(primary_key => ids)
|
|
73
|
+
.group(primary_key)
|
|
74
|
+
.count(:all)
|
|
75
|
+
|
|
76
|
+
ids.each do |id|
|
|
77
|
+
updates[id].merge!(counter_name => counts[id] || 0)
|
|
78
|
+
end
|
|
59
79
|
end
|
|
60
80
|
|
|
61
81
|
if touch
|
|
@@ -63,10 +83,15 @@ module ActiveRecord
|
|
|
63
83
|
names = Array.wrap(names)
|
|
64
84
|
options = names.extract_options!
|
|
65
85
|
touch_updates = touch_attributes_with_time(*names, **options)
|
|
66
|
-
|
|
86
|
+
|
|
87
|
+
updates.each_value do |record_updates|
|
|
88
|
+
record_updates.merge!(touch_updates)
|
|
89
|
+
end
|
|
67
90
|
end
|
|
68
91
|
|
|
69
|
-
|
|
92
|
+
updates.each do |id, record_updates|
|
|
93
|
+
unscoped.where(primary_key => [id]).update_all(record_updates)
|
|
94
|
+
end
|
|
70
95
|
|
|
71
96
|
true
|
|
72
97
|
end
|
|
@@ -81,7 +81,9 @@ module ActiveRecord
|
|
|
81
81
|
|
|
82
82
|
def resolved_adapter
|
|
83
83
|
adapter = uri.scheme && @uri.scheme.tr("-", "_")
|
|
84
|
-
adapter
|
|
84
|
+
if adapter && ActiveRecord.protocol_adapters[adapter]
|
|
85
|
+
adapter = ActiveRecord.protocol_adapters[adapter]
|
|
86
|
+
end
|
|
85
87
|
adapter
|
|
86
88
|
end
|
|
87
89
|
|
|
@@ -38,6 +38,7 @@ module ActiveRecord
|
|
|
38
38
|
def initialize(env_name, name, configuration_hash)
|
|
39
39
|
super(env_name, name)
|
|
40
40
|
@configuration_hash = configuration_hash.symbolize_keys.freeze
|
|
41
|
+
validate_configuration!
|
|
41
42
|
end
|
|
42
43
|
|
|
43
44
|
# Determines whether a database configuration is for a replica / readonly
|
|
@@ -69,16 +70,35 @@ module ActiveRecord
|
|
|
69
70
|
@configuration_hash = configuration_hash.merge(database: database).freeze
|
|
70
71
|
end
|
|
71
72
|
|
|
72
|
-
def
|
|
73
|
-
(
|
|
73
|
+
def max_connections
|
|
74
|
+
max_connections = configuration_hash.fetch(:max_connections) {
|
|
75
|
+
configuration_hash.fetch(:pool, 5)
|
|
76
|
+
}&.to_i
|
|
77
|
+
max_connections if max_connections && max_connections >= 0
|
|
74
78
|
end
|
|
75
79
|
|
|
80
|
+
def min_connections
|
|
81
|
+
(configuration_hash[:min_connections] || 0).to_i
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
alias :pool :max_connections
|
|
85
|
+
deprecate pool: :max_connections, deprecator: ActiveRecord.deprecator
|
|
86
|
+
|
|
76
87
|
def min_threads
|
|
77
88
|
(configuration_hash[:min_threads] || 0).to_i
|
|
78
89
|
end
|
|
79
90
|
|
|
80
91
|
def max_threads
|
|
81
|
-
(configuration_hash[:max_threads] ||
|
|
92
|
+
(configuration_hash[:max_threads] || (max_connections || 5).clamp(0, 5)).to_i
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def max_age
|
|
96
|
+
v = configuration_hash[:max_age]&.to_i
|
|
97
|
+
if v && v > 0
|
|
98
|
+
v
|
|
99
|
+
else
|
|
100
|
+
Float::INFINITY
|
|
101
|
+
end
|
|
82
102
|
end
|
|
83
103
|
|
|
84
104
|
def query_cache
|
|
@@ -93,10 +113,8 @@ module ActiveRecord
|
|
|
93
113
|
(configuration_hash[:checkout_timeout] || 5).to_f
|
|
94
114
|
end
|
|
95
115
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def reaping_frequency
|
|
99
|
-
configuration_hash.fetch(:reaping_frequency, 60)&.to_f
|
|
116
|
+
def reaping_frequency # :nodoc:
|
|
117
|
+
configuration_hash.fetch(:reaping_frequency, default_reaping_frequency)&.to_f
|
|
100
118
|
end
|
|
101
119
|
|
|
102
120
|
def idle_timeout
|
|
@@ -104,6 +122,11 @@ module ActiveRecord
|
|
|
104
122
|
timeout if timeout > 0
|
|
105
123
|
end
|
|
106
124
|
|
|
125
|
+
def keepalive
|
|
126
|
+
keepalive = (configuration_hash[:keepalive] || 600).to_f
|
|
127
|
+
keepalive if keepalive > 0
|
|
128
|
+
end
|
|
129
|
+
|
|
107
130
|
def adapter
|
|
108
131
|
configuration_hash[:adapter]&.to_s
|
|
109
132
|
end
|
|
@@ -146,7 +169,7 @@ module ActiveRecord
|
|
|
146
169
|
#
|
|
147
170
|
# If the config option is set that will be used. Otherwise Rails will generate
|
|
148
171
|
# the filename from the database config name.
|
|
149
|
-
def schema_dump(format =
|
|
172
|
+
def schema_dump(format = schema_format)
|
|
150
173
|
if configuration_hash.key?(:schema_dump)
|
|
151
174
|
if config = configuration_hash[:schema_dump]
|
|
152
175
|
config
|
|
@@ -158,6 +181,12 @@ module ActiveRecord
|
|
|
158
181
|
end
|
|
159
182
|
end
|
|
160
183
|
|
|
184
|
+
def schema_format # :nodoc:
|
|
185
|
+
format = configuration_hash.fetch(:schema_format, ActiveRecord.schema_format).to_sym
|
|
186
|
+
raise "Invalid schema format" unless [:ruby, :sql].include?(format)
|
|
187
|
+
format
|
|
188
|
+
end
|
|
189
|
+
|
|
161
190
|
def database_tasks? # :nodoc:
|
|
162
191
|
!replica? && !!configuration_hash.fetch(:database_tasks, true)
|
|
163
192
|
end
|
|
@@ -168,13 +197,34 @@ module ActiveRecord
|
|
|
168
197
|
|
|
169
198
|
private
|
|
170
199
|
def schema_file_type(format)
|
|
171
|
-
case format
|
|
200
|
+
case format.to_sym
|
|
172
201
|
when :ruby
|
|
173
202
|
"schema.rb"
|
|
174
203
|
when :sql
|
|
175
204
|
"structure.sql"
|
|
176
205
|
end
|
|
177
206
|
end
|
|
207
|
+
|
|
208
|
+
def default_reaping_frequency
|
|
209
|
+
# Reap every 20 seconds by default, but run more often as necessary to
|
|
210
|
+
# meet other configured timeouts.
|
|
211
|
+
[20, idle_timeout, max_age, keepalive].compact.min
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def validate_configuration!
|
|
215
|
+
if configuration_hash[:pool] && configuration_hash[:max_connections]
|
|
216
|
+
pool_val = configuration_hash[:pool].to_i
|
|
217
|
+
max_conn_val = configuration_hash[:max_connections].to_i
|
|
218
|
+
|
|
219
|
+
if pool_val != max_conn_val
|
|
220
|
+
raise "Ambiguous configuration: 'pool' (#{pool_val}) and 'max_connections' (#{max_conn_val}) are set to different values. Prefer just 'max_connections'."
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
if configuration_hash[:pool] && configuration_hash[:min_connections]
|
|
225
|
+
raise "Ambiguous configuration: when setting 'min_connections', use 'max_connections' instead of 'pool'."
|
|
226
|
+
end
|
|
227
|
+
end
|
|
178
228
|
end
|
|
179
229
|
end
|
|
180
230
|
end
|
|
@@ -47,9 +47,8 @@ module ActiveRecord
|
|
|
47
47
|
@configuration_hash[:schema_dump] = false
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
end
|
|
50
|
+
query_cache = parse_query_cache
|
|
51
|
+
@configuration_hash[:query_cache] = query_cache unless query_cache.nil?
|
|
53
52
|
|
|
54
53
|
to_boolean!(@configuration_hash, :replica)
|
|
55
54
|
to_boolean!(@configuration_hash, :database_tasks)
|
|
@@ -58,6 +57,17 @@ module ActiveRecord
|
|
|
58
57
|
end
|
|
59
58
|
|
|
60
59
|
private
|
|
60
|
+
def parse_query_cache
|
|
61
|
+
case value = @configuration_hash[:query_cache]
|
|
62
|
+
when /\A\d+\z/
|
|
63
|
+
value.to_i
|
|
64
|
+
when "false"
|
|
65
|
+
false
|
|
66
|
+
else
|
|
67
|
+
value
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
61
71
|
def to_boolean!(configuration_hash, key)
|
|
62
72
|
if configuration_hash[key].is_a?(String)
|
|
63
73
|
configuration_hash[key] = configuration_hash[key] != "false"
|
|
@@ -36,9 +36,11 @@ module ActiveRecord
|
|
|
36
36
|
# to respond to `sharded?`. To implement this define the following in an
|
|
37
37
|
# initializer:
|
|
38
38
|
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
39
|
+
# ActiveSupport.on_load(:active_record_database_configurations) do
|
|
40
|
+
# ActiveRecord::DatabaseConfigurations.register_db_config_handler do |env_name, name, url, config|
|
|
41
|
+
# next unless config.key?(:vitess)
|
|
42
|
+
# VitessConfig.new(env_name, name, config)
|
|
43
|
+
# end
|
|
42
44
|
# end
|
|
43
45
|
#
|
|
44
46
|
# Note: applications must handle the condition in which custom config should be
|
|
@@ -306,4 +308,6 @@ module ActiveRecord
|
|
|
306
308
|
url
|
|
307
309
|
end
|
|
308
310
|
end
|
|
311
|
+
|
|
312
|
+
ActiveSupport.run_load_hooks(:active_record_database_configurations, DatabaseConfigurations)
|
|
309
313
|
end
|
|
@@ -181,16 +181,16 @@ module ActiveRecord
|
|
|
181
181
|
# delegated_type :entryable, types: %w[ Message Comment ], dependent: :destroy
|
|
182
182
|
# end
|
|
183
183
|
#
|
|
184
|
-
#
|
|
185
|
-
#
|
|
186
|
-
# Entry.messages
|
|
187
|
-
#
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
# Entry.comments
|
|
191
|
-
#
|
|
192
|
-
#
|
|
193
|
-
#
|
|
184
|
+
# @entry.entryable_class # => Message or Comment
|
|
185
|
+
# @entry.entryable_name # => "message" or "comment"
|
|
186
|
+
# Entry.messages # => Entry.where(entryable_type: "Message")
|
|
187
|
+
# @entry.message? # => true when entryable_type == "Message"
|
|
188
|
+
# @entry.message # => returns the message record, when entryable_type == "Message", otherwise nil
|
|
189
|
+
# @entry.message_id # => returns entryable_id, when entryable_type == "Message", otherwise nil
|
|
190
|
+
# Entry.comments # => Entry.where(entryable_type: "Comment")
|
|
191
|
+
# @entry.comment? # => true when entryable_type == "Comment"
|
|
192
|
+
# @entry.comment # => returns the comment record, when entryable_type == "Comment", otherwise nil
|
|
193
|
+
# @entry.comment_id # => returns entryable_id, when entryable_type == "Comment", otherwise nil
|
|
194
194
|
#
|
|
195
195
|
# You can also declare namespaced types:
|
|
196
196
|
#
|
|
@@ -199,25 +199,25 @@ module ActiveRecord
|
|
|
199
199
|
# end
|
|
200
200
|
#
|
|
201
201
|
# Entry.access_notice_messages
|
|
202
|
-
# entry.access_notice_message
|
|
203
|
-
# entry.access_notice_message?
|
|
202
|
+
# @entry.access_notice_message
|
|
203
|
+
# @entry.access_notice_message?
|
|
204
204
|
#
|
|
205
|
-
#
|
|
205
|
+
# ==== Options
|
|
206
206
|
#
|
|
207
207
|
# The +options+ are passed directly to the +belongs_to+ call, so this is where you declare +dependent+ etc.
|
|
208
208
|
# The following options can be included to specialize the behavior of the delegated type convenience methods.
|
|
209
209
|
#
|
|
210
|
-
# [
|
|
210
|
+
# [+:foreign_key+]
|
|
211
211
|
# Specify the foreign key used for the convenience methods. By default this is guessed to be the passed
|
|
212
212
|
# +role+ with an "_id" suffix. So a class that defines a
|
|
213
213
|
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_id" as
|
|
214
214
|
# the default <tt>:foreign_key</tt>.
|
|
215
|
-
# [
|
|
215
|
+
# [+:foreign_type+]
|
|
216
216
|
# Specify the column used to store the associated object's type. By default this is inferred to be the passed
|
|
217
217
|
# +role+ with a "_type" suffix. A class that defines a
|
|
218
218
|
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_type" as
|
|
219
219
|
# the default <tt>:foreign_type</tt>.
|
|
220
|
-
# [
|
|
220
|
+
# [+:primary_key+]
|
|
221
221
|
# Specify the method that returns the primary key of associated object used for the convenience methods.
|
|
222
222
|
# By default this is +id+.
|
|
223
223
|
#
|
|
@@ -226,10 +226,10 @@ module ActiveRecord
|
|
|
226
226
|
# delegated_type :entryable, types: %w[ Message Comment ], primary_key: :uuid, foreign_key: :entryable_uuid
|
|
227
227
|
# end
|
|
228
228
|
#
|
|
229
|
-
#
|
|
230
|
-
#
|
|
229
|
+
# @entry.message_uuid # => returns entryable_uuid, when entryable_type == "Message", otherwise nil
|
|
230
|
+
# @entry.comment_uuid # => returns entryable_uuid, when entryable_type == "Comment", otherwise nil
|
|
231
231
|
def delegated_type(role, types:, **options)
|
|
232
|
-
belongs_to role, options.delete(:scope), **options
|
|
232
|
+
belongs_to role, options.delete(:scope), **options, polymorphic: true
|
|
233
233
|
define_delegated_type_methods role, types: types, options: options
|
|
234
234
|
end
|
|
235
235
|
|