activerecord 7.0.8.7 → 7.1.5.1
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 +1795 -1424
- data/MIT-LICENSE +1 -1
- data/README.rdoc +16 -16
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +20 -4
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +19 -13
- data/lib/active_record/associations/collection_proxy.rb +15 -10
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +10 -10
- data/lib/active_record/associations/preloader/association.rb +31 -7
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +319 -217
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +21 -8
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +145 -21
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +59 -10
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +10 -24
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +80 -50
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +296 -127
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +511 -92
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +244 -121
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +19 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +106 -55
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +74 -40
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -6
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +364 -61
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +353 -192
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +211 -81
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +72 -95
- data/lib/active_record/core.rb +181 -154
- data/lib/active_record/counter_cache.rb +52 -27
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +28 -14
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +15 -10
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +42 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +23 -8
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +112 -28
- data/lib/active_record/errors.rb +112 -18
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +135 -71
- data/lib/active_record/future_result.rb +40 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +57 -10
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +120 -30
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +104 -5
- data/lib/active_record/migration/compatibility.rb +145 -5
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +219 -111
- data/lib/active_record/model_schema.rb +69 -44
- data/lib/active_record/nested_attributes.rb +37 -8
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +188 -37
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +4 -22
- data/lib/active_record/query_logs.rb +77 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +107 -45
- data/lib/active_record/railties/controller_runtime.rb +12 -6
- data/lib/active_record/railties/databases.rake +144 -150
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +181 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +187 -63
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +371 -68
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +103 -37
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +24 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +46 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +7 -5
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +10 -1
- data/lib/active_record/tasks/database_tasks.rb +152 -108
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_fixtures.rb +114 -96
- data/lib/active_record/timestamp.rb +30 -16
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +36 -10
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +122 -17
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +5 -1
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +83 -18
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +46 -10
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -5,6 +5,10 @@ module ActiveRecord
|
|
5
5
|
module CounterCache
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
|
+
included do
|
9
|
+
class_attribute :_counter_cache_columns, instance_accessor: false, default: []
|
10
|
+
end
|
11
|
+
|
8
12
|
module ClassMethods
|
9
13
|
# Resets one or more counter caches to their correct value using an SQL
|
10
14
|
# count query. This is useful when adding new counter caches, or if the
|
@@ -29,6 +33,7 @@ module ActiveRecord
|
|
29
33
|
def reset_counters(id, *counters, touch: nil)
|
30
34
|
object = find(id)
|
31
35
|
|
36
|
+
updates = {}
|
32
37
|
counters.each do |counter_association|
|
33
38
|
has_many_association = _reflect_on_association(counter_association)
|
34
39
|
unless has_many_association
|
@@ -47,19 +52,21 @@ module ActiveRecord
|
|
47
52
|
reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
|
48
53
|
counter_name = reflection.counter_cache_column
|
49
54
|
|
50
|
-
|
51
|
-
|
52
|
-
if
|
53
|
-
|
54
|
-
names = Array.wrap(names)
|
55
|
-
options = names.extract_options!
|
56
|
-
touch_updates = touch_attributes_with_time(*names, **options)
|
57
|
-
updates.merge!(touch_updates)
|
58
|
-
end
|
55
|
+
count_was = object.send(counter_name)
|
56
|
+
count = object.send(counter_association).count(:all)
|
57
|
+
updates[counter_name] = count if count != count_was
|
58
|
+
end
|
59
59
|
|
60
|
-
|
60
|
+
if touch
|
61
|
+
names = touch if touch != true
|
62
|
+
names = Array.wrap(names)
|
63
|
+
options = names.extract_options!
|
64
|
+
touch_updates = touch_attributes_with_time(*names, **options)
|
65
|
+
updates.merge!(touch_updates)
|
61
66
|
end
|
62
67
|
|
68
|
+
unscoped.where(primary_key => [object.id]).update_all(updates) if updates.any?
|
69
|
+
|
63
70
|
true
|
64
71
|
end
|
65
72
|
|
@@ -80,31 +87,32 @@ module ActiveRecord
|
|
80
87
|
#
|
81
88
|
# ==== Examples
|
82
89
|
#
|
83
|
-
# # For the Post with id of 5, decrement the
|
84
|
-
# # increment the
|
85
|
-
# Post.update_counters 5,
|
90
|
+
# # For the Post with id of 5, decrement the comments_count by 1, and
|
91
|
+
# # increment the actions_count by 1
|
92
|
+
# Post.update_counters 5, comments_count: -1, actions_count: 1
|
86
93
|
# # Executes the following SQL:
|
87
94
|
# # UPDATE posts
|
88
|
-
# # SET
|
89
|
-
# #
|
95
|
+
# # SET comments_count = COALESCE(comments_count, 0) - 1,
|
96
|
+
# # actions_count = COALESCE(actions_count, 0) + 1
|
90
97
|
# # WHERE id = 5
|
91
98
|
#
|
92
|
-
# # For the Posts with id of 10 and 15, increment the
|
93
|
-
# Post.update_counters [10, 15],
|
99
|
+
# # For the Posts with id of 10 and 15, increment the comments_count by 1
|
100
|
+
# Post.update_counters [10, 15], comments_count: 1
|
94
101
|
# # Executes the following SQL:
|
95
102
|
# # UPDATE posts
|
96
|
-
# # SET
|
103
|
+
# # SET comments_count = COALESCE(comments_count, 0) + 1
|
97
104
|
# # WHERE id IN (10, 15)
|
98
105
|
#
|
99
|
-
# # For the Posts with id of 10 and 15, increment the
|
106
|
+
# # For the Posts with id of 10 and 15, increment the comments_count by 1
|
100
107
|
# # and update the updated_at value for each counter.
|
101
|
-
# Post.update_counters [10, 15],
|
108
|
+
# Post.update_counters [10, 15], comments_count: 1, touch: true
|
102
109
|
# # Executes the following SQL:
|
103
110
|
# # UPDATE posts
|
104
|
-
# # SET
|
111
|
+
# # SET comments_count = COALESCE(comments_count, 0) + 1,
|
105
112
|
# # `updated_at` = '2016-10-13T09:59:23-05:00'
|
106
113
|
# # WHERE id IN (10, 15)
|
107
114
|
def update_counters(id, counters)
|
115
|
+
id = [id] if composite_primary_key? && id.is_a?(Array) && !id[0].is_a?(Array)
|
108
116
|
unscoped.where!(primary_key => id).update_counters(counters)
|
109
117
|
end
|
110
118
|
|
@@ -119,6 +127,7 @@ module ActiveRecord
|
|
119
127
|
#
|
120
128
|
# * +counter_name+ - The name of the field that should be incremented.
|
121
129
|
# * +id+ - The id of the object that should be incremented or an array of ids.
|
130
|
+
# * <tt>:by</tt> - The amount by which to increment the value. Defaults to +1+.
|
122
131
|
# * <tt>:touch</tt> - Touch timestamp columns when updating.
|
123
132
|
# Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
|
124
133
|
# touch that column or an array of symbols to touch just those ones.
|
@@ -129,10 +138,14 @@ module ActiveRecord
|
|
129
138
|
# DiscussionBoard.increment_counter(:posts_count, 5)
|
130
139
|
#
|
131
140
|
# # Increment the posts_count column for the record with an id of 5
|
141
|
+
# # by a specific amount.
|
142
|
+
# DiscussionBoard.increment_counter(:posts_count, 5, by: 3)
|
143
|
+
#
|
144
|
+
# # Increment the posts_count column for the record with an id of 5
|
132
145
|
# # and update the updated_at value.
|
133
146
|
# DiscussionBoard.increment_counter(:posts_count, 5, touch: true)
|
134
|
-
def increment_counter(counter_name, id, touch: nil)
|
135
|
-
update_counters(id, counter_name =>
|
147
|
+
def increment_counter(counter_name, id, by: 1, touch: nil)
|
148
|
+
update_counters(id, counter_name => by, touch: touch)
|
136
149
|
end
|
137
150
|
|
138
151
|
# Decrement a numeric field by one, via a direct SQL update.
|
@@ -144,6 +157,7 @@ module ActiveRecord
|
|
144
157
|
#
|
145
158
|
# * +counter_name+ - The name of the field that should be decremented.
|
146
159
|
# * +id+ - The id of the object that should be decremented or an array of ids.
|
160
|
+
# * <tt>:by</tt> - The amount by which to decrement the value. Defaults to +1+.
|
147
161
|
# * <tt>:touch</tt> - Touch timestamp columns when updating.
|
148
162
|
# Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
|
149
163
|
# touch that column or an array of symbols to touch just those ones.
|
@@ -154,10 +168,18 @@ module ActiveRecord
|
|
154
168
|
# DiscussionBoard.decrement_counter(:posts_count, 5)
|
155
169
|
#
|
156
170
|
# # Decrement the posts_count column for the record with an id of 5
|
171
|
+
# by a specific amount.
|
172
|
+
# DiscussionBoard.decrement_counter(:posts_count, 5, by: 3)
|
173
|
+
#
|
174
|
+
# # Decrement the posts_count column for the record with an id of 5
|
157
175
|
# # and update the updated_at value.
|
158
176
|
# DiscussionBoard.decrement_counter(:posts_count, 5, touch: true)
|
159
|
-
def decrement_counter(counter_name, id, touch: nil)
|
160
|
-
update_counters(id, counter_name => -
|
177
|
+
def decrement_counter(counter_name, id, by: 1, touch: nil)
|
178
|
+
update_counters(id, counter_name => -by, touch: touch)
|
179
|
+
end
|
180
|
+
|
181
|
+
def counter_cache_column?(name) # :nodoc:
|
182
|
+
_counter_cache_columns.include?(name)
|
161
183
|
end
|
162
184
|
end
|
163
185
|
|
@@ -177,8 +199,7 @@ module ActiveRecord
|
|
177
199
|
|
178
200
|
if affected_rows > 0
|
179
201
|
each_counter_cached_associations do |association|
|
180
|
-
foreign_key
|
181
|
-
unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
|
202
|
+
unless destroyed_by_association && _foreign_keys_equal?(destroyed_by_association.foreign_key, association.reflection.foreign_key)
|
182
203
|
association.decrement_counters
|
183
204
|
end
|
184
205
|
end
|
@@ -192,5 +213,9 @@ module ActiveRecord
|
|
192
213
|
yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
|
193
214
|
end
|
194
215
|
end
|
216
|
+
|
217
|
+
def _foreign_keys_equal?(fkey1, fkey2)
|
218
|
+
fkey1 == fkey2 || Array(fkey1).map(&:to_sym) == Array(fkey2).map(&:to_sym)
|
219
|
+
end
|
195
220
|
end
|
196
221
|
end
|
@@ -3,13 +3,11 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
class DatabaseConfigurations
|
5
5
|
# ActiveRecord::Base.configurations will return either a HashConfig or
|
6
|
-
# UrlConfig respectively. It will never return a DatabaseConfig object,
|
6
|
+
# UrlConfig respectively. It will never return a +DatabaseConfig+ object,
|
7
7
|
# as this is the parent class for the types of database configuration objects.
|
8
8
|
class DatabaseConfig # :nodoc:
|
9
9
|
attr_reader :env_name, :name
|
10
10
|
|
11
|
-
attr_accessor :owner_name
|
12
|
-
|
13
11
|
def initialize(env_name, name)
|
14
12
|
@env_name = env_name
|
15
13
|
@name = name
|
@@ -19,6 +17,10 @@ module ActiveRecord
|
|
19
17
|
"#{adapter}_connection"
|
20
18
|
end
|
21
19
|
|
20
|
+
def adapter_class_method
|
21
|
+
"#{adapter}_adapter_class"
|
22
|
+
end
|
23
|
+
|
22
24
|
def host
|
23
25
|
raise NotImplementedError
|
24
26
|
end
|
@@ -51,6 +53,10 @@ module ActiveRecord
|
|
51
53
|
raise NotImplementedError
|
52
54
|
end
|
53
55
|
|
56
|
+
def query_cache
|
57
|
+
raise NotImplementedError
|
58
|
+
end
|
59
|
+
|
54
60
|
def checkout_timeout
|
55
61
|
raise NotImplementedError
|
56
62
|
end
|
@@ -2,7 +2,9 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
class DatabaseConfigurations
|
5
|
-
#
|
5
|
+
# = Active Record Database Hash Config
|
6
|
+
#
|
7
|
+
# A +HashConfig+ object is created for each database configuration entry that
|
6
8
|
# is created from a hash.
|
7
9
|
#
|
8
10
|
# A hash config:
|
@@ -14,19 +16,23 @@ module ActiveRecord
|
|
14
16
|
# #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
|
15
17
|
# @env_name="development", @name="primary", @config={database: "db_name"}>
|
16
18
|
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# * <tt>:env_name</tt> - The Rails environment, i.e. "development".
|
20
|
-
# * <tt>:name</tt> - The db config name. In a standard two-tier
|
21
|
-
# database configuration this will default to "primary". In a multiple
|
22
|
-
# database three-tier database configuration this corresponds to the name
|
23
|
-
# used in the second tier, for example "primary_readonly".
|
24
|
-
# * <tt>:config</tt> - The config hash. This is the hash that contains the
|
25
|
-
# database adapter, name, and other important information for database
|
26
|
-
# connections.
|
19
|
+
# See ActiveRecord::DatabaseConfigurations for more info.
|
27
20
|
class HashConfig < DatabaseConfig
|
28
21
|
attr_reader :configuration_hash
|
29
22
|
|
23
|
+
|
24
|
+
# Initialize a new +HashConfig+ object
|
25
|
+
#
|
26
|
+
# ==== Options
|
27
|
+
#
|
28
|
+
# * <tt>:env_name</tt> - The \Rails environment, i.e. "development".
|
29
|
+
# * <tt>:name</tt> - The db config name. In a standard two-tier
|
30
|
+
# database configuration this will default to "primary". In a multiple
|
31
|
+
# database three-tier database configuration this corresponds to the name
|
32
|
+
# used in the second tier, for example "primary_readonly".
|
33
|
+
# * <tt>:config</tt> - The config hash. This is the hash that contains the
|
34
|
+
# database adapter, name, and other important information for database
|
35
|
+
# connections.
|
30
36
|
def initialize(env_name, name, configuration_hash)
|
31
37
|
super(env_name, name)
|
32
38
|
@configuration_hash = configuration_hash.symbolize_keys.freeze
|
@@ -74,6 +80,10 @@ module ActiveRecord
|
|
74
80
|
(configuration_hash[:max_threads] || pool).to_i
|
75
81
|
end
|
76
82
|
|
83
|
+
def query_cache
|
84
|
+
configuration_hash[:query_cache]
|
85
|
+
end
|
86
|
+
|
77
87
|
def max_queue
|
78
88
|
max_threads * 4
|
79
89
|
end
|
@@ -104,8 +114,12 @@ module ActiveRecord
|
|
104
114
|
configuration_hash[:schema_cache_path]
|
105
115
|
end
|
106
116
|
|
107
|
-
def default_schema_cache_path
|
108
|
-
|
117
|
+
def default_schema_cache_path(db_dir = "db")
|
118
|
+
if primary?
|
119
|
+
File.join(db_dir, "schema_cache.yml")
|
120
|
+
else
|
121
|
+
File.join(db_dir, "#{name}_schema_cache.yml")
|
122
|
+
end
|
109
123
|
end
|
110
124
|
|
111
125
|
def lazy_schema_cache_path
|
@@ -122,7 +136,7 @@ module ActiveRecord
|
|
122
136
|
# If +configuration_hash[:schema_dump]+ is set to +false+ or +nil+
|
123
137
|
# the schema will not be dumped.
|
124
138
|
#
|
125
|
-
# If the config option is set that will be used. Otherwise Rails
|
139
|
+
# If the config option is set that will be used. Otherwise \Rails
|
126
140
|
# will generate the filename from the database config name.
|
127
141
|
def schema_dump(format = ActiveRecord.schema_format)
|
128
142
|
if configuration_hash.key?(:schema_dump)
|
@@ -2,7 +2,9 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
class DatabaseConfigurations
|
5
|
-
#
|
5
|
+
# = Active Record Database Url Config
|
6
|
+
#
|
7
|
+
# A +UrlConfig+ object is created for each database configuration
|
6
8
|
# entry that is created from a URL. This can either be a URL string
|
7
9
|
# or a hash with a URL in place of the config hash.
|
8
10
|
#
|
@@ -17,20 +19,24 @@ module ActiveRecord
|
|
17
19
|
# @config={adapter: "postgresql", database: "foo", host: "localhost"},
|
18
20
|
# @url="postgres://localhost/foo">
|
19
21
|
#
|
20
|
-
#
|
22
|
+
# See ActiveRecord::DatabaseConfigurations for more info.
|
21
23
|
#
|
22
|
-
# * <tt>:env_name</tt> - The Rails environment, i.e. "development".
|
23
|
-
# * <tt>:name</tt> - The db config name. In a standard two-tier
|
24
|
-
# database configuration this will default to "primary". In a multiple
|
25
|
-
# database three-tier database configuration this corresponds to the name
|
26
|
-
# used in the second tier, for example "primary_readonly".
|
27
|
-
# * <tt>:url</tt> - The database URL.
|
28
|
-
# * <tt>:config</tt> - The config hash. This is the hash that contains the
|
29
|
-
# database adapter, name, and other important information for database
|
30
|
-
# connections.
|
31
24
|
class UrlConfig < HashConfig
|
32
25
|
attr_reader :url
|
33
26
|
|
27
|
+
# Initialize a new +UrlConfig+ object
|
28
|
+
#
|
29
|
+
# ==== Options
|
30
|
+
#
|
31
|
+
# * <tt>:env_name</tt> - The \Rails environment, i.e. "development".
|
32
|
+
# * <tt>:name</tt> - The db config name. In a standard two-tier
|
33
|
+
# database configuration this will default to "primary". In a multiple
|
34
|
+
# database three-tier database configuration this corresponds to the name
|
35
|
+
# used in the second tier, for example "primary_readonly".
|
36
|
+
# * <tt>:url</tt> - The database URL.
|
37
|
+
# * <tt>:config</tt> - The config hash. This is the hash that contains the
|
38
|
+
# database adapter, name, and other important information for database
|
39
|
+
# connections.
|
34
40
|
def initialize(env_name, name, url, configuration_hash = {})
|
35
41
|
super(env_name, name, configuration_hash)
|
36
42
|
|
@@ -7,15 +7,69 @@ require "active_record/database_configurations/url_config"
|
|
7
7
|
require "active_record/database_configurations/connection_url_resolver"
|
8
8
|
|
9
9
|
module ActiveRecord
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
10
|
+
# = Active Record Database Configurations
|
11
|
+
#
|
12
|
+
# +ActiveRecord::DatabaseConfigurations+ returns an array of +DatabaseConfig+
|
13
|
+
# objects that are constructed from the application's database
|
14
|
+
# configuration hash or URL string.
|
15
|
+
#
|
16
|
+
# The array of +DatabaseConfig+ objects in an application default to either a
|
17
|
+
# HashConfig or UrlConfig. You can retrieve your application's config by using
|
18
|
+
# ActiveRecord::Base.configurations.
|
19
|
+
#
|
20
|
+
# If you register a custom handler, objects will be created according to the
|
21
|
+
# conditions of the handler. See ::register_db_config_handler for more on
|
22
|
+
# registering custom handlers.
|
13
23
|
class DatabaseConfigurations
|
14
24
|
class InvalidConfigurationError < StandardError; end
|
15
25
|
|
16
26
|
attr_reader :configurations
|
17
27
|
delegate :any?, to: :configurations
|
18
28
|
|
29
|
+
singleton_class.attr_accessor :db_config_handlers # :nodoc:
|
30
|
+
self.db_config_handlers = [] # :nodoc:
|
31
|
+
|
32
|
+
# Allows an application to register a custom handler for database configuration
|
33
|
+
# objects. This is useful for creating a custom handler that responds to
|
34
|
+
# methods your application needs but Active Record doesn't implement. For
|
35
|
+
# example if you are using Vitess, you may want your Vitess configurations
|
36
|
+
# to respond to `sharded?`. To implement this define the following in an
|
37
|
+
# initializer:
|
38
|
+
#
|
39
|
+
# ActiveRecord::DatabaseConfigurations.register_db_config_handler do |env_name, name, url, config|
|
40
|
+
# next unless config.key?(:vitess)
|
41
|
+
# VitessConfig.new(env_name, name, config)
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# Note: applications must handle the condition in which custom config should be
|
45
|
+
# created in your handler registration otherwise all objects will use the custom
|
46
|
+
# handler.
|
47
|
+
#
|
48
|
+
# Then define your +VitessConfig+ to respond to the methods your application
|
49
|
+
# needs. It is recommended that you inherit from one of the existing
|
50
|
+
# database config classes to avoid having to reimplement all methods. Custom
|
51
|
+
# config handlers should only implement methods Active Record does not.
|
52
|
+
#
|
53
|
+
# class VitessConfig < ActiveRecord::DatabaseConfigurations::UrlConfig
|
54
|
+
# def sharded?
|
55
|
+
# configuration_hash.fetch("sharded", false)
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# For configs that have a +:vitess+ key, a +VitessConfig+ object will be
|
60
|
+
# created instead of a +UrlConfig+.
|
61
|
+
def self.register_db_config_handler(&block)
|
62
|
+
db_config_handlers << block
|
63
|
+
end
|
64
|
+
|
65
|
+
register_db_config_handler do |env_name, name, url, config|
|
66
|
+
if url
|
67
|
+
UrlConfig.new(env_name, name, url, config)
|
68
|
+
else
|
69
|
+
HashConfig.new(env_name, name, config)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
19
73
|
def initialize(configurations = {})
|
20
74
|
@configurations = build_configs(configurations)
|
21
75
|
end
|
@@ -23,8 +77,8 @@ module ActiveRecord
|
|
23
77
|
# Collects the configs for the environment and optionally the specification
|
24
78
|
# name passed in. To include replica configurations pass <tt>include_hidden: true</tt>.
|
25
79
|
#
|
26
|
-
# If a name is provided a single DatabaseConfig object will be
|
27
|
-
# returned, otherwise an array of DatabaseConfig objects will be
|
80
|
+
# If a name is provided a single +DatabaseConfig+ object will be
|
81
|
+
# returned, otherwise an array of +DatabaseConfig+ objects will be
|
28
82
|
# returned that corresponds with the environment and type requested.
|
29
83
|
#
|
30
84
|
# ==== Options
|
@@ -34,20 +88,14 @@ module ActiveRecord
|
|
34
88
|
# * <tt>name:</tt> The db config name (i.e. primary, animals, etc.). Defaults
|
35
89
|
# to +nil+. If no +env_name+ is specified the config for the default env and the
|
36
90
|
# passed +name+ will be returned.
|
37
|
-
# * <tt>
|
38
|
-
#
|
39
|
-
#
|
40
|
-
# Defaults to +false+.
|
91
|
+
# * <tt>config_key:</tt> Selects configs that contain a particular key in the configuration
|
92
|
+
# hash. Useful for selecting configs that use a custom db config handler or finding
|
93
|
+
# configs with hashes that contain a particular key.
|
41
94
|
# * <tt>include_hidden:</tt> Determines whether to include replicas and configurations
|
42
|
-
# hidden by
|
95
|
+
# hidden by <tt>database_tasks: false</tt> in the returned list. Most of the time we're only
|
43
96
|
# iterating over the primary connections (i.e. migrations don't need to run for the
|
44
97
|
# write and read connection). Defaults to +false+.
|
45
|
-
def configs_for(env_name: nil, name: nil,
|
46
|
-
if include_replicas
|
47
|
-
include_hidden = include_replicas
|
48
|
-
ActiveSupport::Deprecation.warn("The kwarg `include_replicas` is deprecated in favor of `include_hidden`. When `include_hidden` is passed, configurations with `replica: true` or `database_tasks: false` will be returned. `include_replicas` will be removed in Rails 7.1.")
|
49
|
-
end
|
50
|
-
|
98
|
+
def configs_for(env_name: nil, name: nil, config_key: nil, include_hidden: false)
|
51
99
|
env_name ||= default_env if name
|
52
100
|
configs = env_with_configs(env_name)
|
53
101
|
|
@@ -57,6 +105,12 @@ module ActiveRecord
|
|
57
105
|
end
|
58
106
|
end
|
59
107
|
|
108
|
+
if config_key
|
109
|
+
configs = configs.select do |db_config|
|
110
|
+
db_config.configuration_hash.key?(config_key)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
60
114
|
if name
|
61
115
|
configs.find do |db_config|
|
62
116
|
db_config.name == name
|
@@ -66,17 +120,17 @@ module ActiveRecord
|
|
66
120
|
end
|
67
121
|
end
|
68
122
|
|
69
|
-
# Returns a single DatabaseConfig object based on the requested environment.
|
123
|
+
# Returns a single +DatabaseConfig+ object based on the requested environment.
|
70
124
|
#
|
71
125
|
# If the application has multiple databases +find_db_config+ will return
|
72
|
-
# the first DatabaseConfig for the environment.
|
126
|
+
# the first +DatabaseConfig+ for the environment.
|
73
127
|
def find_db_config(env)
|
74
|
-
|
75
|
-
|
76
|
-
.
|
77
|
-
|
78
|
-
|
79
|
-
|
128
|
+
env = env.to_s
|
129
|
+
configurations.find do |db_config|
|
130
|
+
db_config.for_current_env? && (db_config.env_name == env || db_config.name == env)
|
131
|
+
end || configurations.find do |db_config|
|
132
|
+
db_config.env_name == env
|
133
|
+
end
|
80
134
|
end
|
81
135
|
|
82
136
|
# A primary configuration is one that is named primary or if there is
|
@@ -93,8 +147,6 @@ module ActiveRecord
|
|
93
147
|
end
|
94
148
|
|
95
149
|
# Checks if the application's configurations are empty.
|
96
|
-
#
|
97
|
-
# Aliased to blank?
|
98
150
|
def empty?
|
99
151
|
configurations.empty?
|
100
152
|
end
|
@@ -219,15 +271,16 @@ module ActiveRecord
|
|
219
271
|
end
|
220
272
|
|
221
273
|
def build_db_config_from_hash(env_name, name, config)
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
config_without_url.delete :url
|
274
|
+
url = config[:url]
|
275
|
+
config_without_url = config.dup
|
276
|
+
config_without_url.delete :url
|
226
277
|
|
227
|
-
|
228
|
-
|
229
|
-
|
278
|
+
DatabaseConfigurations.db_config_handlers.reverse_each do |handler|
|
279
|
+
config = handler.call(env_name, name, url, config_without_url)
|
280
|
+
return config if config
|
230
281
|
end
|
282
|
+
|
283
|
+
nil
|
231
284
|
end
|
232
285
|
|
233
286
|
def merge_db_environment_variables(current_env, configs)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "active_support/core_ext/string/inquiry"
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
|
-
#
|
6
|
+
# = Delegated types
|
7
7
|
#
|
8
8
|
# Class hierarchies can map to relational database tables in many ways. Active Record, for example, offers
|
9
9
|
# purely abstract classes, where the superclass doesn't persist any attributes, and single-table inheritance,
|
@@ -36,7 +36,7 @@ module ActiveRecord
|
|
36
36
|
#
|
37
37
|
# Let's look at that entry/message/comment example using delegated types:
|
38
38
|
#
|
39
|
-
# # Schema: entries[ id, account_id, creator_id,
|
39
|
+
# # Schema: entries[ id, account_id, creator_id, entryable_type, entryable_id, created_at, updated_at ]
|
40
40
|
# class Entry < ApplicationRecord
|
41
41
|
# belongs_to :account
|
42
42
|
# belongs_to :creator
|
@@ -51,12 +51,12 @@ module ActiveRecord
|
|
51
51
|
# end
|
52
52
|
# end
|
53
53
|
#
|
54
|
-
# # Schema: messages[ id, subject, body ]
|
54
|
+
# # Schema: messages[ id, subject, body, created_at, updated_at ]
|
55
55
|
# class Message < ApplicationRecord
|
56
56
|
# include Entryable
|
57
57
|
# end
|
58
58
|
#
|
59
|
-
# # Schema: comments[ id, content ]
|
59
|
+
# # Schema: comments[ id, content, created_at, updated_at ]
|
60
60
|
# class Comment < ApplicationRecord
|
61
61
|
# include Entryable
|
62
62
|
# end
|
@@ -102,14 +102,14 @@ module ActiveRecord
|
|
102
102
|
# You create a new record that uses delegated typing by creating the delegator and delegatee at the same time,
|
103
103
|
# like so:
|
104
104
|
#
|
105
|
-
# Entry.create! entryable: Comment.new(content: "Hello!"), creator: Current.user
|
105
|
+
# Entry.create! entryable: Comment.new(content: "Hello!"), creator: Current.user, account: Current.account
|
106
106
|
#
|
107
107
|
# If you need more complicated composition, or you need to perform dependent validation, you should build a factory
|
108
108
|
# method or class to take care of the complicated needs. This could be as simple as:
|
109
109
|
#
|
110
110
|
# class Entry < ApplicationRecord
|
111
|
-
# def self.create_with_comment(content, creator: Current.user)
|
112
|
-
# create! entryable: Comment.new(content: content), creator: creator
|
111
|
+
# def self.create_with_comment(content, creator: Current.user, account: Current.account)
|
112
|
+
# create! entryable: Comment.new(content: content), creator: creator, account: account
|
113
113
|
# end
|
114
114
|
# end
|
115
115
|
#
|
@@ -138,7 +138,7 @@ module ActiveRecord
|
|
138
138
|
#
|
139
139
|
# Now you can list a bunch of entries, call <tt>Entry#title</tt>, and polymorphism will provide you with the answer.
|
140
140
|
#
|
141
|
-
# == Nested Attributes
|
141
|
+
# == Nested \Attributes
|
142
142
|
#
|
143
143
|
# Enabling nested attributes on a delegated_type association allows you to
|
144
144
|
# create the entry and message in one go:
|
@@ -192,6 +192,11 @@ module ActiveRecord
|
|
192
192
|
# +role+ with an "_id" suffix. So a class that defines a
|
193
193
|
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_id" as
|
194
194
|
# the default <tt>:foreign_key</tt>.
|
195
|
+
# [:foreign_type]
|
196
|
+
# Specify the column used to store the associated object's type. By default this is inferred to be the passed
|
197
|
+
# +role+ with a "_type" suffix. A class that defines a
|
198
|
+
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_type" as
|
199
|
+
# the default <tt>:foreign_type</tt>.
|
195
200
|
# [:primary_key]
|
196
201
|
# Specify the method that returns the primary key of associated object used for the convenience methods.
|
197
202
|
# By default this is +id+.
|
@@ -211,11 +216,11 @@ module ActiveRecord
|
|
211
216
|
private
|
212
217
|
def define_delegated_type_methods(role, types:, options:)
|
213
218
|
primary_key = options[:primary_key] || "id"
|
214
|
-
role_type = "#{role}_type"
|
219
|
+
role_type = options[:foreign_type] || "#{role}_type"
|
215
220
|
role_id = options[:foreign_key] || "#{role}_id"
|
216
221
|
|
217
222
|
define_method "#{role}_class" do
|
218
|
-
public_send(
|
223
|
+
public_send(role_type).constantize
|
219
224
|
end
|
220
225
|
|
221
226
|
define_method "#{role}_name" do
|
@@ -4,6 +4,8 @@ module ActiveRecord
|
|
4
4
|
class DestroyAssociationAsyncError < StandardError
|
5
5
|
end
|
6
6
|
|
7
|
+
# = Active Record Destroy Association Async Job
|
8
|
+
#
|
7
9
|
# Job to destroy the records associated with a destroyed record in background.
|
8
10
|
class DestroyAssociationAsyncJob < ActiveJob::Base
|
9
11
|
queue_as { ActiveRecord.queues[:destroy] }
|
@@ -17,7 +19,7 @@ module ActiveRecord
|
|
17
19
|
)
|
18
20
|
association_model = association_class.constantize
|
19
21
|
owner_class = owner_model_name.constantize
|
20
|
-
owner = owner_class.find_by(owner_class.primary_key
|
22
|
+
owner = owner_class.find_by(owner_class.primary_key => [owner_id])
|
21
23
|
|
22
24
|
if !owner_destroyed?(owner, ensuring_owner_was_method)
|
23
25
|
raise DestroyAssociationAsyncError, "owner record not destroyed"
|