activerecord 7.1.5.1 → 8.0.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 +369 -2484
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +21 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +7 -6
- 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 +17 -9
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- 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 +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +92 -295
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -18
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +63 -49
- data/lib/active_record/autosave_association.rb +92 -57
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +48 -122
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +286 -77
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +119 -55
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +197 -76
- data/lib/active_record/connection_adapters/abstract/quoting.rb +66 -92
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -12
- data/lib/active_record/connection_adapters/abstract/transaction.rb +140 -67
- data/lib/active_record/connection_adapters/abstract_adapter.rb +85 -90
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +71 -52
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +56 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +92 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +13 -31
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -41
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- 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/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +36 -20
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +75 -28
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +73 -113
- data/lib/active_record/connection_adapters/schema_cache.rb +124 -131
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +81 -97
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +29 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +35 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +183 -87
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +39 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +65 -0
- data/lib/active_record/connection_handling.rb +74 -37
- data/lib/active_record/core.rb +132 -51
- data/lib/active_record/counter_cache.rb +19 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +23 -4
- data/lib/active_record/database_configurations/hash_config.rb +46 -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 +41 -17
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +7 -7
- data/lib/active_record/encryption/encrypted_attribute_type.rb +33 -4
- data/lib/active_record/encryption/encryptor.rb +28 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- 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/scheme.rb +8 -1
- data/lib/active_record/enum.rb +20 -16
- data/lib/active_record/errors.rb +54 -20
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -33
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +19 -16
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +5 -32
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +33 -14
- data/lib/active_record/migration/compatibility.rb +8 -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 +104 -98
- data/lib/active_record/model_schema.rb +32 -70
- data/lib/active_record/nested_attributes.rb +15 -9
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +127 -451
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +104 -37
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +24 -12
- data/lib/active_record/railtie.rb +26 -68
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +43 -61
- data/lib/active_record/reflection.rb +112 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +138 -72
- data/lib/active_record/relation/calculations.rb +122 -82
- data/lib/active_record/relation/delegation.rb +30 -22
- data/lib/active_record/relation/finder_methods.rb +32 -18
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +16 -3
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +317 -101
- data/lib/active_record/relation/spawn_methods.rb +3 -19
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +561 -119
- data/lib/active_record/result.rb +95 -46
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +31 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +53 -20
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/signed_id.rb +24 -4
- data/lib/active_record/statement_cache.rb +19 -19
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +2 -13
- data/lib/active_record/tasks/database_tasks.rb +87 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -3
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +4 -3
- data/lib/active_record/test_fixtures.rb +98 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +72 -17
- 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 +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +138 -57
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/nodes/binary.rb +1 -7
- 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 +5 -4
- data/lib/arel/nodes/sql_literal.rb +8 -1
- 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/table.rb +3 -7
- 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/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +18 -16
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -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,16 @@ 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 do |pool|
|
39
|
+
next if pool.db_config&.query_cache == false
|
40
|
+
pool.enable_query_cache!
|
41
|
+
end
|
30
42
|
end
|
31
43
|
|
32
44
|
def self.complete(pools)
|
33
|
-
pools.each
|
34
|
-
|
35
|
-
|
36
|
-
pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
|
45
|
+
pools.each do |pool|
|
46
|
+
pool.disable_query_cache!
|
47
|
+
pool.clear_query_cache
|
37
48
|
end
|
38
49
|
end
|
39
50
|
|
@@ -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
|
#
|
@@ -71,14 +72,70 @@ module ActiveRecord
|
|
71
72
|
#
|
72
73
|
# config.active_record.cache_query_log_tags = true
|
73
74
|
module QueryLogs
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
75
|
+
class GetKeyHandler # :nodoc:
|
76
|
+
def initialize(name)
|
77
|
+
@name = name
|
78
|
+
end
|
79
|
+
|
80
|
+
def call(context)
|
81
|
+
context[@name]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class IdentityHandler # :nodoc:
|
86
|
+
def initialize(value)
|
87
|
+
@value = value
|
88
|
+
end
|
89
|
+
|
90
|
+
def call(_context)
|
91
|
+
@value
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class ZeroArityHandler # :nodoc:
|
96
|
+
def initialize(proc)
|
97
|
+
@proc = proc
|
98
|
+
end
|
99
|
+
|
100
|
+
def call(_context)
|
101
|
+
@proc.call
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
@taggings = {}.freeze
|
106
|
+
@tags = [ :application ].freeze
|
107
|
+
@prepend_comment = false
|
108
|
+
@cache_query_log_tags = false
|
109
|
+
@tags_formatter = false
|
110
|
+
|
79
111
|
thread_mattr_accessor :cached_comment, instance_accessor: false
|
80
112
|
|
81
113
|
class << self
|
114
|
+
attr_reader :tags, :taggings, :tags_formatter # :nodoc:
|
115
|
+
attr_accessor :prepend_comment, :cache_query_log_tags # :nodoc:
|
116
|
+
|
117
|
+
def taggings=(taggings) # :nodoc:
|
118
|
+
@taggings = taggings.freeze
|
119
|
+
@handlers = rebuild_handlers
|
120
|
+
end
|
121
|
+
|
122
|
+
def tags=(tags) # :nodoc:
|
123
|
+
@tags = tags.freeze
|
124
|
+
@handlers = rebuild_handlers
|
125
|
+
end
|
126
|
+
|
127
|
+
def tags_formatter=(format) # :nodoc:
|
128
|
+
@formatter = case format
|
129
|
+
when :legacy
|
130
|
+
LegacyFormatter
|
131
|
+
when :sqlcommenter
|
132
|
+
SQLCommenter
|
133
|
+
else
|
134
|
+
raise ArgumentError, "Formatter is unsupported: #{format}"
|
135
|
+
end
|
136
|
+
@tags_formatter = format
|
137
|
+
end
|
138
|
+
|
82
139
|
def call(sql, connection) # :nodoc:
|
83
140
|
comment = self.comment(connection)
|
84
141
|
|
@@ -95,22 +152,46 @@ module ActiveRecord
|
|
95
152
|
self.cached_comment = nil
|
96
153
|
end
|
97
154
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
when :sqlcommenter
|
105
|
-
SQLCommenter.new
|
106
|
-
else
|
107
|
-
raise ArgumentError, "Formatter is unsupported: #{formatter}"
|
108
|
-
end
|
155
|
+
def query_source_location # :nodoc:
|
156
|
+
Thread.each_caller_location do |location|
|
157
|
+
frame = LogSubscriber.backtrace_cleaner.clean_frame(location)
|
158
|
+
return frame if frame
|
159
|
+
end
|
160
|
+
nil
|
109
161
|
end
|
110
162
|
|
111
163
|
ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
|
112
164
|
|
113
165
|
private
|
166
|
+
def rebuild_handlers
|
167
|
+
handlers = []
|
168
|
+
@tags.each do |i|
|
169
|
+
if i.is_a?(Hash)
|
170
|
+
i.each do |k, v|
|
171
|
+
handlers << [k, build_handler(k, v)]
|
172
|
+
end
|
173
|
+
else
|
174
|
+
handlers << [i, build_handler(i)]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
handlers.sort_by! { |(key, _)| key.to_s }
|
178
|
+
end
|
179
|
+
|
180
|
+
def build_handler(name, handler = nil)
|
181
|
+
handler ||= @taggings[name]
|
182
|
+
if handler.nil?
|
183
|
+
GetKeyHandler.new(name)
|
184
|
+
elsif handler.respond_to?(:call)
|
185
|
+
if handler.arity == 0
|
186
|
+
ZeroArityHandler.new(handler)
|
187
|
+
else
|
188
|
+
handler
|
189
|
+
end
|
190
|
+
else
|
191
|
+
IdentityHandler.new(handler)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
114
195
|
# Returns an SQL comment +String+ containing the query log tags.
|
115
196
|
# Sets and returns a cached comment if <tt>cache_query_log_tags</tt> is +true+.
|
116
197
|
def comment(connection)
|
@@ -121,10 +202,6 @@ module ActiveRecord
|
|
121
202
|
end
|
122
203
|
end
|
123
204
|
|
124
|
-
def formatter
|
125
|
-
self.tags_formatter || self.update_formatter(:legacy)
|
126
|
-
end
|
127
|
-
|
128
205
|
def uncached_comment(connection)
|
129
206
|
content = tag_content(connection)
|
130
207
|
|
@@ -150,25 +227,15 @@ module ActiveRecord
|
|
150
227
|
context = ActiveSupport::ExecutionContext.to_h
|
151
228
|
context[:connection] ||= connection
|
152
229
|
|
153
|
-
pairs =
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
val = if handler.nil?
|
158
|
-
context[key]
|
159
|
-
elsif handler.respond_to?(:call)
|
160
|
-
if handler.arity == 0
|
161
|
-
handler.call
|
162
|
-
else
|
163
|
-
handler.call(context)
|
164
|
-
end
|
165
|
-
else
|
166
|
-
handler
|
167
|
-
end
|
168
|
-
[key, val] unless val.nil?
|
230
|
+
pairs = @handlers.filter_map do |(key, handler)|
|
231
|
+
val = handler.call(context)
|
232
|
+
@formatter.format(key, val) unless val.nil?
|
169
233
|
end
|
170
|
-
|
234
|
+
@formatter.join(pairs)
|
171
235
|
end
|
172
236
|
end
|
237
|
+
|
238
|
+
@handlers = rebuild_handlers
|
239
|
+
self.tags_formatter = :legacy
|
173
240
|
end
|
174
241
|
end
|
@@ -2,40 +2,29 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module QueryLogs
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# Formats the key value pairs into a string.
|
11
|
-
def format(pairs)
|
12
|
-
pairs.map! do |key, value|
|
13
|
-
"#{key}#{key_value_separator}#{format_value(value)}"
|
14
|
-
end.join(",")
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
attr_reader :key_value_separator
|
19
|
-
|
20
|
-
def format_value(value)
|
21
|
-
value
|
5
|
+
module LegacyFormatter # :nodoc:
|
6
|
+
class << self
|
7
|
+
# Formats the key value pairs into a string.
|
8
|
+
def format(key, value)
|
9
|
+
"#{key}:#{value}"
|
22
10
|
end
|
23
|
-
end
|
24
11
|
|
25
|
-
|
26
|
-
|
27
|
-
|
12
|
+
def join(pairs)
|
13
|
+
pairs.join(",")
|
14
|
+
end
|
28
15
|
end
|
16
|
+
end
|
29
17
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
18
|
+
class SQLCommenter # :nodoc:
|
19
|
+
class << self
|
20
|
+
def format(key, value)
|
21
|
+
"#{key}='#{ERB::Util.url_encode(value)}'"
|
22
|
+
end
|
34
23
|
|
35
|
-
|
36
|
-
|
37
|
-
"'#{ERB::Util.url_encode(value)}'"
|
24
|
+
def join(pairs)
|
25
|
+
pairs.join(",")
|
38
26
|
end
|
27
|
+
end
|
39
28
|
end
|
40
29
|
end
|
41
30
|
end
|
@@ -10,15 +10,16 @@ module ActiveRecord
|
|
10
10
|
:first_or_create, :first_or_create!, :first_or_initialize,
|
11
11
|
:find_or_create_by, :find_or_create_by!, :find_or_initialize_by,
|
12
12
|
:create_or_find_by, :create_or_find_by!,
|
13
|
-
:destroy_all, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
|
13
|
+
:destroy, :destroy_all, :delete, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
|
14
14
|
:find_each, :find_in_batches, :in_batches,
|
15
15
|
:select, :reselect, :order, :regroup, :in_order_of, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
|
16
16
|
:where, :rewhere, :invert_where, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
|
17
17
|
:and, :or, :annotate, :optimizer_hints, :extending,
|
18
18
|
:having, :create_with, :distinct, :references, :none, :unscope, :merge, :except, :only,
|
19
19
|
:count, :average, :minimum, :maximum, :sum, :calculate,
|
20
|
-
:pluck, :pick, :ids, :async_ids, :strict_loading, :excluding, :without, :with,
|
20
|
+
:pluck, :pick, :ids, :async_ids, :strict_loading, :excluding, :without, :with, :with_recursive,
|
21
21
|
:async_count, :async_average, :async_minimum, :async_maximum, :async_sum, :async_pluck, :async_pick,
|
22
|
+
:insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
|
22
23
|
].freeze # :nodoc:
|
23
24
|
delegate(*QUERYING_METHODS, to: :all)
|
24
25
|
|
@@ -47,22 +48,29 @@ module ActiveRecord
|
|
47
48
|
#
|
48
49
|
# Note that building your own SQL query string from user input may expose your application to
|
49
50
|
# injection attacks (https://guides.rubyonrails.org/security.html#sql-injection).
|
50
|
-
def find_by_sql(sql, binds = [], preparable: nil, &block)
|
51
|
-
|
51
|
+
def find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
|
52
|
+
result = with_connection do |c|
|
53
|
+
_query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry)
|
54
|
+
end
|
55
|
+
_load_from_sql(result, &block)
|
52
56
|
end
|
53
57
|
|
54
58
|
# Same as <tt>#find_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
|
55
|
-
def async_find_by_sql(sql, binds = [], preparable: nil, &block)
|
56
|
-
|
59
|
+
def async_find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
|
60
|
+
with_connection do |c|
|
61
|
+
_query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry, async: true)
|
62
|
+
end.then do |result|
|
57
63
|
_load_from_sql(result, &block)
|
58
64
|
end
|
59
65
|
end
|
60
66
|
|
61
|
-
def _query_by_sql(sql, binds = [], preparable: nil, async: false) # :nodoc:
|
62
|
-
connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable, async: async)
|
67
|
+
def _query_by_sql(connection, sql, binds = [], preparable: nil, async: false, allow_retry: false) # :nodoc:
|
68
|
+
connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable, async: async, allow_retry: allow_retry)
|
63
69
|
end
|
64
70
|
|
65
71
|
def _load_from_sql(result_set, &block) # :nodoc:
|
72
|
+
return [] if result_set.empty?
|
73
|
+
|
66
74
|
column_types = result_set.column_types
|
67
75
|
|
68
76
|
unless column_types.empty?
|
@@ -78,10 +86,10 @@ module ActiveRecord
|
|
78
86
|
|
79
87
|
message_bus.instrument("instantiation.active_record", payload) do
|
80
88
|
if result_set.includes_column?(inheritance_column)
|
81
|
-
result_set.map { |record| instantiate(record, column_types, &block) }
|
89
|
+
result_set.indexed_rows.map { |record| instantiate(record, column_types, &block) }
|
82
90
|
else
|
83
91
|
# Instantiate a homogeneous set
|
84
|
-
result_set.map { |record| instantiate_instance_of(self, record, column_types, &block) }
|
92
|
+
result_set.indexed_rows.map { |record| instantiate_instance_of(self, record, column_types, &block) }
|
85
93
|
end
|
86
94
|
end
|
87
95
|
end
|
@@ -99,12 +107,16 @@ module ActiveRecord
|
|
99
107
|
#
|
100
108
|
# * +sql+ - An SQL statement which should return a count query from the database, see the example above.
|
101
109
|
def count_by_sql(sql)
|
102
|
-
|
110
|
+
with_connection do |c|
|
111
|
+
c.select_value(sanitize_sql(sql), "#{name} Count").to_i
|
112
|
+
end
|
103
113
|
end
|
104
114
|
|
105
115
|
# Same as <tt>#count_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
|
106
116
|
def async_count_by_sql(sql)
|
107
|
-
|
117
|
+
with_connection do |c|
|
118
|
+
c.select_value(sanitize_sql(sql), "#{name} Count", async: true).then(&:to_i)
|
119
|
+
end
|
108
120
|
end
|
109
121
|
end
|
110
122
|
end
|
@@ -31,7 +31,6 @@ module ActiveRecord
|
|
31
31
|
config.active_record.check_schema_cache_dump_version = true
|
32
32
|
config.active_record.maintain_test_schema = true
|
33
33
|
config.active_record.has_many_inversing = false
|
34
|
-
config.active_record.sqlite3_production_warning = true
|
35
34
|
config.active_record.query_log_tags_enabled = false
|
36
35
|
config.active_record.query_log_tags = [ :application ]
|
37
36
|
config.active_record.query_log_tags_format = :legacy
|
@@ -70,6 +69,7 @@ module ActiveRecord
|
|
70
69
|
Rails.logger.broadcast_to(console)
|
71
70
|
end
|
72
71
|
ActiveRecord.verbose_query_logs = false
|
72
|
+
ActiveRecord::Base.attributes_for_inspect = :all
|
73
73
|
end
|
74
74
|
|
75
75
|
runner do
|
@@ -88,7 +88,9 @@ module ActiveRecord
|
|
88
88
|
|
89
89
|
initializer "active_record.postgresql_time_zone_aware_types" do
|
90
90
|
ActiveSupport.on_load(:active_record_postgresqladapter) do
|
91
|
-
|
91
|
+
ActiveSupport.on_load(:active_record) do
|
92
|
+
ActiveRecord::Base.time_zone_aware_types << :timestamptz
|
93
|
+
end
|
92
94
|
end
|
93
95
|
end
|
94
96
|
|
@@ -133,49 +135,11 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
133
135
|
end
|
134
136
|
end
|
135
137
|
|
136
|
-
initializer "active_record.
|
137
|
-
|
138
|
-
end
|
139
|
-
|
140
|
-
initializer "active_record.check_schema_cache_dump" do
|
141
|
-
check_schema_cache_dump_version = config.active_record.check_schema_cache_dump_version
|
142
|
-
|
143
|
-
ActiveRecord::ConnectionAdapters::SchemaReflection.check_schema_cache_dump_version = check_schema_cache_dump_version
|
144
|
-
|
145
|
-
if config.active_record.use_schema_cache_dump && !config.active_record.lazily_load_schema_cache
|
146
|
-
config.after_initialize do |app|
|
147
|
-
ActiveSupport.on_load(:active_record) do
|
148
|
-
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).first
|
149
|
-
next if db_config.nil?
|
150
|
-
|
151
|
-
filename = ActiveRecord::Tasks::DatabaseTasks.cache_dump_filename(
|
152
|
-
db_config.name,
|
153
|
-
schema_cache_path: db_config.schema_cache_path
|
154
|
-
)
|
138
|
+
initializer "active_record.copy_schema_cache_config" do
|
139
|
+
active_record_config = config.active_record
|
155
140
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
if check_schema_cache_dump_version
|
160
|
-
current_version = begin
|
161
|
-
ActiveRecord::Migrator.current_version
|
162
|
-
rescue ActiveRecordError => error
|
163
|
-
warn "Failed to validate the schema cache because of #{error.class}: #{error.message}"
|
164
|
-
nil
|
165
|
-
end
|
166
|
-
next if current_version.nil?
|
167
|
-
|
168
|
-
if cache.schema_version != current_version
|
169
|
-
warn "Ignoring #{filename} because it has expired. The current schema version is #{current_version}, but the one in the schema cache file is #{cache.schema_version}."
|
170
|
-
next
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
Rails.logger.info("Using schema cache file #{filename}")
|
175
|
-
connection_pool.schema_reflection.set_schema_cache(cache)
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
141
|
+
ActiveRecord::ConnectionAdapters::SchemaReflection.use_schema_cache_dump = active_record_config.use_schema_cache_dump
|
142
|
+
ActiveRecord::ConnectionAdapters::SchemaReflection.check_schema_cache_dump_version = active_record_config.check_schema_cache_dump_version
|
179
143
|
end
|
180
144
|
|
181
145
|
initializer "active_record.define_attribute_methods" do |app|
|
@@ -198,7 +162,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
198
162
|
# Additionally if `check_schema_cache_dump_version` is enabled (which is the default),
|
199
163
|
# loading the schema cache dump trigger a database connection to compare the schema
|
200
164
|
# versions.
|
201
|
-
# This means the attribute methods will be lazily defined
|
165
|
+
# This means the attribute methods will be lazily defined when the model is accessed,
|
202
166
|
# likely as part of the first few requests or jobs. This isn't good for performance
|
203
167
|
# but we unfortunately have to arbitrate between resiliency and performance, and chose
|
204
168
|
# resiliency.
|
@@ -220,24 +184,6 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
220
184
|
end
|
221
185
|
end
|
222
186
|
|
223
|
-
initializer "active_record.warn_on_records_fetched_greater_than" do
|
224
|
-
if config.active_record.warn_on_records_fetched_greater_than
|
225
|
-
ActiveSupport.on_load(:active_record) do
|
226
|
-
require "active_record/relation/record_fetch_warning"
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
SQLITE3_PRODUCTION_WARN = "You are running SQLite in production, this is generally not recommended."\
|
232
|
-
" You can disable this warning by setting \"config.active_record.sqlite3_production_warning=false\"."
|
233
|
-
initializer "active_record.sqlite3_production_warning" do
|
234
|
-
if config.active_record.sqlite3_production_warning && Rails.env.production?
|
235
|
-
ActiveSupport.on_load(:active_record_sqlite3adapter) do
|
236
|
-
Rails.logger.warn(SQLITE3_PRODUCTION_WARN)
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
187
|
initializer "active_record.sqlite3_adapter_strict_strings_by_default" do
|
242
188
|
config.after_initialize do
|
243
189
|
if config.active_record.sqlite3_adapter_strict_strings_by_default
|
@@ -248,6 +194,16 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
248
194
|
end
|
249
195
|
end
|
250
196
|
|
197
|
+
initializer "active_record.postgresql_adapter_decode_dates" do
|
198
|
+
config.after_initialize do
|
199
|
+
if config.active_record.postgresql_adapter_decode_dates
|
200
|
+
ActiveSupport.on_load(:active_record_postgresqladapter) do
|
201
|
+
self.decode_dates = true
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
251
207
|
initializer "active_record.set_configs" do |app|
|
252
208
|
configs = app.config.active_record
|
253
209
|
|
@@ -273,10 +229,10 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
273
229
|
:query_log_tags,
|
274
230
|
:query_log_tags_format,
|
275
231
|
:cache_query_log_tags,
|
276
|
-
:sqlite3_production_warning,
|
277
232
|
:sqlite3_adapter_strict_strings_by_default,
|
278
233
|
:check_schema_cache_dump_version,
|
279
|
-
:use_schema_cache_dump
|
234
|
+
:use_schema_cache_dump,
|
235
|
+
:postgresql_adapter_decode_dates,
|
280
236
|
)
|
281
237
|
|
282
238
|
configs_used_in_other_initializers.each do |k, v|
|
@@ -332,6 +288,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
332
288
|
initializer "active_record.set_executor_hooks" do
|
333
289
|
ActiveRecord::QueryCache.install_executor_hooks
|
334
290
|
ActiveRecord::AsynchronousQueriesTracker.install_executor_hooks
|
291
|
+
ActiveRecord::ConnectionAdapters::ConnectionPool.install_executor_hooks
|
335
292
|
end
|
336
293
|
|
337
294
|
initializer "active_record.add_watchable_files" do |app|
|
@@ -409,12 +366,13 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
409
366
|
config.after_initialize do
|
410
367
|
if app.config.active_record.query_log_tags_enabled
|
411
368
|
ActiveRecord.query_transformers << ActiveRecord::QueryLogs
|
412
|
-
ActiveRecord::QueryLogs.taggings.merge
|
369
|
+
ActiveRecord::QueryLogs.taggings = ActiveRecord::QueryLogs.taggings.merge(
|
413
370
|
application: Rails.application.class.name.split("::").first,
|
414
371
|
pid: -> { Process.pid.to_s },
|
415
372
|
socket: ->(context) { context[:connection].pool.db_config.socket },
|
416
373
|
db_host: ->(context) { context[:connection].pool.db_config.host },
|
417
|
-
database: ->(context) { context[:connection].pool.db_config.database }
|
374
|
+
database: ->(context) { context[:connection].pool.db_config.database },
|
375
|
+
source_location: -> { QueryLogs.query_source_location }
|
418
376
|
)
|
419
377
|
ActiveRecord.disable_prepared_statements = true
|
420
378
|
|
@@ -423,7 +381,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
423
381
|
end
|
424
382
|
|
425
383
|
if app.config.active_record.query_log_tags_format
|
426
|
-
ActiveRecord::QueryLogs.
|
384
|
+
ActiveRecord::QueryLogs.tags_formatter = app.config.active_record.query_log_tags_format
|
427
385
|
end
|
428
386
|
|
429
387
|
if app.config.active_record.cache_query_log_tags
|
@@ -11,7 +11,14 @@ module ActiveRecord
|
|
11
11
|
module ClassMethods # :nodoc:
|
12
12
|
def log_process_action(payload)
|
13
13
|
messages, db_runtime = super, payload[:db_runtime]
|
14
|
-
|
14
|
+
|
15
|
+
if db_runtime
|
16
|
+
queries_count = payload[:queries_count] || 0
|
17
|
+
cached_queries_count = payload[:cached_queries_count] || 0
|
18
|
+
messages << ("ActiveRecord: %.1fms (%d %s, %d cached)" % [db_runtime.to_f, queries_count,
|
19
|
+
"query".pluralize(queries_count), cached_queries_count])
|
20
|
+
end
|
21
|
+
|
15
22
|
messages
|
16
23
|
end
|
17
24
|
end
|
@@ -34,11 +41,11 @@ module ActiveRecord
|
|
34
41
|
|
35
42
|
def cleanup_view_runtime
|
36
43
|
if logger && logger.info?
|
37
|
-
db_rt_before_render = ActiveRecord::RuntimeRegistry.
|
44
|
+
db_rt_before_render = ActiveRecord::RuntimeRegistry.reset_runtimes
|
38
45
|
self.db_runtime = (db_runtime || 0) + db_rt_before_render
|
39
46
|
runtime = super
|
40
47
|
queries_rt = ActiveRecord::RuntimeRegistry.sql_runtime - ActiveRecord::RuntimeRegistry.async_sql_runtime
|
41
|
-
db_rt_after_render = ActiveRecord::RuntimeRegistry.
|
48
|
+
db_rt_after_render = ActiveRecord::RuntimeRegistry.reset_runtimes
|
42
49
|
self.db_runtime += db_rt_after_render
|
43
50
|
runtime - queries_rt
|
44
51
|
else
|
@@ -49,7 +56,9 @@ module ActiveRecord
|
|
49
56
|
def append_info_to_payload(payload)
|
50
57
|
super
|
51
58
|
|
52
|
-
payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::RuntimeRegistry.
|
59
|
+
payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::RuntimeRegistry.reset_runtimes
|
60
|
+
payload[:queries_count] = ActiveRecord::RuntimeRegistry.reset_queries_count
|
61
|
+
payload[:cached_queries_count] = ActiveRecord::RuntimeRegistry.reset_cached_queries_count
|
53
62
|
end
|
54
63
|
end
|
55
64
|
end
|