activerecord 6.1.6 → 7.0.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1314 -975
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +0 -10
- data/lib/active_record/associations/association.rb +33 -17
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +15 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +8 -2
- data/lib/active_record/associations/builder/belongs_to.rb +19 -6
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -2
- data/lib/active_record/associations/collection_association.rb +19 -21
- data/lib/active_record/associations/collection_proxy.rb +10 -5
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +8 -5
- data/lib/active_record/associations/has_many_through_association.rb +2 -1
- data/lib/active_record/associations/has_one_association.rb +10 -7
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +23 -15
- data/lib/active_record/associations/preloader/association.rb +186 -52
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +49 -13
- data/lib/active_record/associations/preloader.rb +39 -113
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +124 -95
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +49 -16
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +7 -5
- data/lib/active_record/attribute_methods/serialization.rb +57 -19
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +14 -15
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +8 -23
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +10 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +38 -13
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -24
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +105 -81
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -24
- data/lib/active_record/connection_adapters/mysql/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +51 -51
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +37 -19
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +208 -107
- data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +96 -32
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +49 -55
- data/lib/active_record/core.rb +124 -134
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -9
- data/lib/active_record/database_configurations/hash_config.rb +63 -5
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +15 -32
- data/lib/active_record/delegated_type.rb +53 -12
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +67 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +206 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +50 -43
- data/lib/active_record/errors.rb +67 -4
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +41 -6
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +20 -23
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +55 -17
- data/lib/active_record/insert_all.rb +80 -14
- data/lib/active_record/integration.rb +4 -3
- data/lib/active_record/internal_metadata.rb +1 -5
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/locking/pessimistic.rb +10 -4
- data/lib/active_record/log_subscriber.rb +23 -7
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +18 -6
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +7 -7
- data/lib/active_record/migration/compatibility.rb +84 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +114 -83
- data/lib/active_record/model_schema.rb +58 -59
- data/lib/active_record/nested_attributes.rb +13 -12
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +228 -60
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +16 -6
- data/lib/active_record/railtie.rb +136 -22
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +78 -136
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +73 -50
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +6 -6
- data/lib/active_record/relation/calculations.rb +43 -38
- data/lib/active_record/relation/delegation.rb +7 -7
- data/lib/active_record/relation/finder_methods.rb +31 -35
- data/lib/active_record/relation/merger.rb +20 -13
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_attribute.rb +5 -11
- data/lib/active_record/relation/query_methods.rb +276 -67
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +189 -88
- data/lib/active_record/result.rb +17 -7
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +17 -12
- data/lib/active_record/schema.rb +38 -23
- data/lib/active_record/schema_dumper.rb +25 -19
- data/lib/active_record/schema_migration.rb +4 -4
- data/lib/active_record/scoping/default.rb +60 -13
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +3 -3
- data/lib/active_record/store.rb +7 -2
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +127 -60
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +16 -9
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +32 -7
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +1 -1
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/associated.rb +4 -4
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +217 -27
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +1 -0
- data/lib/arel/predications.rb +11 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +0 -1
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +8 -2
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +58 -2
- data/lib/arel.rb +2 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +55 -11
@@ -62,9 +62,9 @@ module ActiveRecord
|
|
62
62
|
# Active Record callbacks or validations. Though passed values
|
63
63
|
# go through Active Record's type casting and serialization.
|
64
64
|
#
|
65
|
-
# See
|
66
|
-
def insert(attributes, returning: nil, unique_by: nil)
|
67
|
-
insert_all([ attributes ], returning: returning, unique_by: unique_by)
|
65
|
+
# See #insert_all for documentation.
|
66
|
+
def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
67
|
+
insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
68
68
|
end
|
69
69
|
|
70
70
|
# Inserts multiple records into the database in a single SQL INSERT
|
@@ -79,7 +79,7 @@ module ActiveRecord
|
|
79
79
|
# duplicate rows are skipped.
|
80
80
|
# Override with <tt>:unique_by</tt> (see below).
|
81
81
|
#
|
82
|
-
# Returns an
|
82
|
+
# Returns an ActiveRecord::Result with its contents based on
|
83
83
|
# <tt>:returning</tt> (see below).
|
84
84
|
#
|
85
85
|
# ==== Options
|
@@ -91,6 +91,9 @@ module ActiveRecord
|
|
91
91
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
92
92
|
# clause entirely.
|
93
93
|
#
|
94
|
+
# You can also pass an SQL string if you need more control on the return values
|
95
|
+
# (for example, <tt>returning: "id, name as new_name"</tt>).
|
96
|
+
#
|
94
97
|
# [:unique_by]
|
95
98
|
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
96
99
|
# by every unique index on the table. Any duplicate rows are skipped.
|
@@ -107,6 +110,17 @@ module ActiveRecord
|
|
107
110
|
# unique_by: %i[ author_id name ]
|
108
111
|
# unique_by: :index_books_on_isbn
|
109
112
|
#
|
113
|
+
# [:record_timestamps]
|
114
|
+
# By default, automatic setting of timestamp columns is controlled by
|
115
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
116
|
+
# behavior.
|
117
|
+
#
|
118
|
+
# To override this and force automatic setting of timestamp columns one
|
119
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
120
|
+
#
|
121
|
+
# record_timestamps: true # Always set timestamps automatically
|
122
|
+
# record_timestamps: false # Never set timestamps automatically
|
123
|
+
#
|
110
124
|
# Because it relies on the index information from the database
|
111
125
|
# <tt>:unique_by</tt> is recommended to be paired with
|
112
126
|
# Active Record's schema_cache.
|
@@ -120,8 +134,16 @@ module ActiveRecord
|
|
120
134
|
# { id: 1, title: "Rework", author: "David" },
|
121
135
|
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
122
136
|
# ])
|
123
|
-
|
124
|
-
|
137
|
+
#
|
138
|
+
# # insert_all works on chained scopes, and you can use create_with
|
139
|
+
# # to set default attributes for all inserted records.
|
140
|
+
#
|
141
|
+
# author.books.create_with(created_at: Time.now).insert_all([
|
142
|
+
# { id: 1, title: "Rework" },
|
143
|
+
# { id: 2, title: "Eloquent Ruby" }
|
144
|
+
# ])
|
145
|
+
def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
146
|
+
InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
|
125
147
|
end
|
126
148
|
|
127
149
|
# Inserts a single record into the database in a single SQL INSERT
|
@@ -129,9 +151,9 @@ module ActiveRecord
|
|
129
151
|
# Active Record callbacks or validations. Though passed values
|
130
152
|
# go through Active Record's type casting and serialization.
|
131
153
|
#
|
132
|
-
# See
|
133
|
-
def insert!(attributes, returning: nil)
|
134
|
-
insert_all!([ attributes ], returning: returning)
|
154
|
+
# See #insert_all! for more.
|
155
|
+
def insert!(attributes, returning: nil, record_timestamps: nil)
|
156
|
+
insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
|
135
157
|
end
|
136
158
|
|
137
159
|
# Inserts multiple records into the database in a single SQL INSERT
|
@@ -145,10 +167,9 @@ module ActiveRecord
|
|
145
167
|
# Raises <tt>ActiveRecord::RecordNotUnique</tt> if any rows violate a
|
146
168
|
# unique index on the table. In that case, no rows are inserted.
|
147
169
|
#
|
148
|
-
# To skip duplicate rows, see
|
149
|
-
# To replace them, see <tt>ActiveRecord::Persistence#upsert_all</tt>.
|
170
|
+
# To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
|
150
171
|
#
|
151
|
-
# Returns an
|
172
|
+
# Returns an ActiveRecord::Result with its contents based on
|
152
173
|
# <tt>:returning</tt> (see below).
|
153
174
|
#
|
154
175
|
# ==== Options
|
@@ -160,6 +181,20 @@ module ActiveRecord
|
|
160
181
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
161
182
|
# clause entirely.
|
162
183
|
#
|
184
|
+
# You can also pass an SQL string if you need more control on the return values
|
185
|
+
# (for example, <tt>returning: "id, name as new_name"</tt>).
|
186
|
+
#
|
187
|
+
# [:record_timestamps]
|
188
|
+
# By default, automatic setting of timestamp columns is controlled by
|
189
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
190
|
+
# behavior.
|
191
|
+
#
|
192
|
+
# To override this and force automatic setting of timestamp columns one
|
193
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
194
|
+
#
|
195
|
+
# record_timestamps: true # Always set timestamps automatically
|
196
|
+
# record_timestamps: false # Never set timestamps automatically
|
197
|
+
#
|
163
198
|
# ==== Examples
|
164
199
|
#
|
165
200
|
# # Insert multiple records
|
@@ -174,8 +209,8 @@ module ActiveRecord
|
|
174
209
|
# { id: 1, title: "Rework", author: "David" },
|
175
210
|
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
176
211
|
# ])
|
177
|
-
def insert_all!(attributes, returning: nil)
|
178
|
-
InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
|
212
|
+
def insert_all!(attributes, returning: nil, record_timestamps: nil)
|
213
|
+
InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
|
179
214
|
end
|
180
215
|
|
181
216
|
# Updates or inserts (upserts) a single record into the database in a
|
@@ -183,9 +218,9 @@ module ActiveRecord
|
|
183
218
|
# it trigger Active Record callbacks or validations. Though passed values
|
184
219
|
# go through Active Record's type casting and serialization.
|
185
220
|
#
|
186
|
-
# See
|
187
|
-
def upsert(attributes, returning: nil, unique_by: nil)
|
188
|
-
upsert_all([ attributes ], returning: returning, unique_by: unique_by)
|
221
|
+
# See #upsert_all for documentation.
|
222
|
+
def upsert(attributes, on_duplicate: :update, returning: nil, unique_by: nil, record_timestamps: nil)
|
223
|
+
upsert_all([ attributes ], on_duplicate: on_duplicate, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
189
224
|
end
|
190
225
|
|
191
226
|
# Updates or inserts (upserts) multiple records into the database in a
|
@@ -196,9 +231,13 @@ module ActiveRecord
|
|
196
231
|
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
197
232
|
# the attributes for a single row and must have the same keys.
|
198
233
|
#
|
199
|
-
# Returns an
|
234
|
+
# Returns an ActiveRecord::Result with its contents based on
|
200
235
|
# <tt>:returning</tt> (see below).
|
201
236
|
#
|
237
|
+
# By default, +upsert_all+ will update all the columns that can be updated when
|
238
|
+
# there is a conflict. These are all the columns except primary keys, read-only
|
239
|
+
# columns, and columns covered by the optional +unique_by+.
|
240
|
+
#
|
202
241
|
# ==== Options
|
203
242
|
#
|
204
243
|
# [:returning]
|
@@ -208,6 +247,9 @@ module ActiveRecord
|
|
208
247
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
209
248
|
# clause entirely.
|
210
249
|
#
|
250
|
+
# You can also pass an SQL string if you need more control on the return values
|
251
|
+
# (for example, <tt>returning: "id, name as new_name"</tt>).
|
252
|
+
#
|
211
253
|
# [:unique_by]
|
212
254
|
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
213
255
|
# by every unique index on the table. Any duplicate rows are skipped.
|
@@ -228,6 +270,54 @@ module ActiveRecord
|
|
228
270
|
# <tt>:unique_by</tt> is recommended to be paired with
|
229
271
|
# Active Record's schema_cache.
|
230
272
|
#
|
273
|
+
# [:on_duplicate]
|
274
|
+
# Configure the SQL update sentence that will be used in case of conflict.
|
275
|
+
#
|
276
|
+
# NOTE: If you use this option you must provide all the columns you want to update
|
277
|
+
# by yourself.
|
278
|
+
#
|
279
|
+
# Example:
|
280
|
+
#
|
281
|
+
# Commodity.upsert_all(
|
282
|
+
# [
|
283
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
284
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
285
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
286
|
+
# ],
|
287
|
+
# on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
|
288
|
+
# )
|
289
|
+
#
|
290
|
+
# See the related +:update_only+ option. Both options can't be used at the same time.
|
291
|
+
#
|
292
|
+
# [:update_only]
|
293
|
+
# Provide a list of column names that will be updated in case of conflict. If not provided,
|
294
|
+
# +upsert_all+ will update all the columns that can be updated. These are all the columns
|
295
|
+
# except primary keys, read-only columns, and columns covered by the optional +unique_by+
|
296
|
+
#
|
297
|
+
# Example:
|
298
|
+
#
|
299
|
+
# Commodity.upsert_all(
|
300
|
+
# [
|
301
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
302
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
303
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
304
|
+
# ],
|
305
|
+
# update_only: [:price] # Only prices will be updated
|
306
|
+
# )
|
307
|
+
#
|
308
|
+
# See the related +:on_duplicate+ option. Both options can't be used at the same time.
|
309
|
+
#
|
310
|
+
# [:record_timestamps]
|
311
|
+
# By default, automatic setting of timestamp columns is controlled by
|
312
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
313
|
+
# behavior.
|
314
|
+
#
|
315
|
+
# To override this and force automatic setting of timestamp columns one
|
316
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
317
|
+
#
|
318
|
+
# record_timestamps: true # Always set timestamps automatically
|
319
|
+
# record_timestamps: false # Never set timestamps automatically
|
320
|
+
#
|
231
321
|
# ==== Examples
|
232
322
|
#
|
233
323
|
# # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
|
@@ -239,8 +329,8 @@ module ActiveRecord
|
|
239
329
|
# ], unique_by: :isbn)
|
240
330
|
#
|
241
331
|
# Book.find_by(isbn: "1").title # => "Eloquent Ruby"
|
242
|
-
def upsert_all(attributes, returning: nil, unique_by: nil)
|
243
|
-
InsertAll.new(self, attributes, on_duplicate: :
|
332
|
+
def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
|
333
|
+
InsertAll.new(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
|
244
334
|
end
|
245
335
|
|
246
336
|
# Given an attributes hash, +instantiate+ returns a new instance of
|
@@ -264,6 +354,7 @@ module ActiveRecord
|
|
264
354
|
# ==== Parameters
|
265
355
|
#
|
266
356
|
# * +id+ - This should be the id or an array of ids to be updated.
|
357
|
+
# Optional argument, defaults to all records in the relation.
|
267
358
|
# * +attributes+ - This should be a hash of attributes or an array of hashes.
|
268
359
|
#
|
269
360
|
# ==== Examples
|
@@ -286,6 +377,11 @@ module ActiveRecord
|
|
286
377
|
# for updating all records in a single query.
|
287
378
|
def update(id = :all, attributes)
|
288
379
|
if id.is_a?(Array)
|
380
|
+
if id.any?(ActiveRecord::Base)
|
381
|
+
raise ArgumentError,
|
382
|
+
"You are passing an array of ActiveRecord::Base instances to `update`. " \
|
383
|
+
"Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
|
384
|
+
end
|
289
385
|
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
290
386
|
object.update(attributes[idx])
|
291
387
|
}
|
@@ -303,6 +399,32 @@ module ActiveRecord
|
|
303
399
|
end
|
304
400
|
end
|
305
401
|
|
402
|
+
# Updates the object (or multiple objects) just like #update but calls #update! instead
|
403
|
+
# of +update+, so an exception is raised if the record is invalid and saving will fail.
|
404
|
+
def update!(id = :all, attributes)
|
405
|
+
if id.is_a?(Array)
|
406
|
+
if id.any?(ActiveRecord::Base)
|
407
|
+
raise ArgumentError,
|
408
|
+
"You are passing an array of ActiveRecord::Base instances to `update!`. " \
|
409
|
+
"Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
|
410
|
+
end
|
411
|
+
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
412
|
+
object.update!(attributes[idx])
|
413
|
+
}
|
414
|
+
elsif id == :all
|
415
|
+
all.each { |record| record.update!(attributes) }
|
416
|
+
else
|
417
|
+
if ActiveRecord::Base === id
|
418
|
+
raise ArgumentError,
|
419
|
+
"You are passing an instance of ActiveRecord::Base to `update!`. " \
|
420
|
+
"Please pass the id of the object by calling `.id`."
|
421
|
+
end
|
422
|
+
object = find(id)
|
423
|
+
object.update!(attributes)
|
424
|
+
object
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
306
428
|
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
|
307
429
|
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
308
430
|
# less efficient than #delete but allows cleanup methods and other actions to be run.
|
@@ -356,40 +478,52 @@ module ActiveRecord
|
|
356
478
|
primary_key = self.primary_key
|
357
479
|
primary_key_value = nil
|
358
480
|
|
359
|
-
if
|
360
|
-
|
361
|
-
|
362
|
-
if !primary_key_value && prefetch_primary_key?
|
481
|
+
if prefetch_primary_key? && primary_key
|
482
|
+
values[primary_key] ||= begin
|
363
483
|
primary_key_value = next_sequence_value
|
364
|
-
|
484
|
+
_default_attributes[primary_key].with_cast_value(primary_key_value)
|
365
485
|
end
|
366
486
|
end
|
367
487
|
|
488
|
+
im = Arel::InsertManager.new(arel_table)
|
489
|
+
|
368
490
|
if values.empty?
|
369
|
-
im
|
370
|
-
im.into arel_table
|
491
|
+
im.insert(connection.empty_insert_statement_value(primary_key))
|
371
492
|
else
|
372
|
-
im
|
493
|
+
im.insert(values.transform_keys { |name| arel_table[name] })
|
373
494
|
end
|
374
495
|
|
375
496
|
connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
|
376
497
|
end
|
377
498
|
|
378
499
|
def _update_record(values, constraints) # :nodoc:
|
379
|
-
constraints =
|
500
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
380
501
|
|
381
|
-
|
382
|
-
|
383
|
-
|
502
|
+
default_constraint = build_default_constraint
|
503
|
+
constraints << default_constraint if default_constraint
|
504
|
+
|
505
|
+
if current_scope = self.global_current_scope
|
506
|
+
constraints << current_scope.where_clause.ast
|
507
|
+
end
|
508
|
+
|
509
|
+
um = Arel::UpdateManager.new(arel_table)
|
510
|
+
um.set(values.transform_keys { |name| arel_table[name] })
|
511
|
+
um.wheres = constraints
|
384
512
|
|
385
513
|
connection.update(um, "#{self} Update")
|
386
514
|
end
|
387
515
|
|
388
516
|
def _delete_record(constraints) # :nodoc:
|
389
|
-
constraints =
|
517
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
390
518
|
|
391
|
-
|
392
|
-
|
519
|
+
default_constraint = build_default_constraint
|
520
|
+
constraints << default_constraint if default_constraint
|
521
|
+
|
522
|
+
if current_scope = self.global_current_scope
|
523
|
+
constraints << current_scope.where_clause.ast
|
524
|
+
end
|
525
|
+
|
526
|
+
dm = Arel::DeleteManager.new(arel_table)
|
393
527
|
dm.wheres = constraints
|
394
528
|
|
395
529
|
connection.delete(dm, "#{self} Destroy")
|
@@ -412,12 +546,14 @@ module ActiveRecord
|
|
412
546
|
self
|
413
547
|
end
|
414
548
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
549
|
+
# Called by +_update_record+ and +_delete_record+
|
550
|
+
# to build `where` clause from default scopes.
|
551
|
+
# Skips empty scopes.
|
552
|
+
def build_default_constraint
|
553
|
+
return unless default_scopes?(all_queries: true)
|
554
|
+
|
555
|
+
default_where_clause = default_scoped(all_queries: true).where_clause
|
556
|
+
default_where_clause.ast unless default_where_clause.empty?
|
421
557
|
end
|
422
558
|
end
|
423
559
|
|
@@ -434,6 +570,11 @@ module ActiveRecord
|
|
434
570
|
@previously_new_record
|
435
571
|
end
|
436
572
|
|
573
|
+
# Returns true if this object was previously persisted but now it has been deleted.
|
574
|
+
def previously_persisted?
|
575
|
+
!new_record? && destroyed?
|
576
|
+
end
|
577
|
+
|
437
578
|
# Returns true if this object has been destroyed, otherwise returns false.
|
438
579
|
def destroyed?
|
439
580
|
@destroyed
|
@@ -556,17 +697,17 @@ module ActiveRecord
|
|
556
697
|
end
|
557
698
|
|
558
699
|
# Returns an instance of the specified +klass+ with the attributes of the
|
559
|
-
# current record. This is mostly useful in relation to single
|
560
|
-
# inheritance structures where you want a subclass to appear as the
|
700
|
+
# current record. This is mostly useful in relation to single table
|
701
|
+
# inheritance (STI) structures where you want a subclass to appear as the
|
561
702
|
# superclass. This can be used along with record identification in
|
562
703
|
# Action Pack to allow, say, <tt>Client < Company</tt> to do something
|
563
704
|
# like render <tt>partial: @client.becomes(Company)</tt> to render that
|
564
705
|
# instance using the companies/company partial instead of clients/client.
|
565
706
|
#
|
566
707
|
# Note: The new instance will share a link to the same attributes as the original class.
|
567
|
-
# Therefore the
|
708
|
+
# Therefore the STI column value will still be the same.
|
568
709
|
# Any change to the attributes on either instance will affect both instances.
|
569
|
-
# If you want to change the
|
710
|
+
# If you want to change the STI column as well, use #becomes! instead.
|
570
711
|
def becomes(klass)
|
571
712
|
became = klass.allocate
|
572
713
|
|
@@ -581,11 +722,11 @@ module ActiveRecord
|
|
581
722
|
became
|
582
723
|
end
|
583
724
|
|
584
|
-
# Wrapper around #becomes that also changes the instance's
|
725
|
+
# Wrapper around #becomes that also changes the instance's STI column value.
|
585
726
|
# This is especially useful if you want to persist the changed class in your
|
586
727
|
# database.
|
587
728
|
#
|
588
|
-
# Note: The old instance's
|
729
|
+
# Note: The old instance's STI column value will be changed too, as both objects
|
589
730
|
# share the same set of attributes.
|
590
731
|
def becomes!(klass)
|
591
732
|
became = becomes(klass)
|
@@ -664,6 +805,7 @@ module ActiveRecord
|
|
664
805
|
def update_columns(attributes)
|
665
806
|
raise ActiveRecordError, "cannot update a new record" if new_record?
|
666
807
|
raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
|
808
|
+
_raise_readonly_record_error if readonly?
|
667
809
|
|
668
810
|
attributes = attributes.transform_keys do |key|
|
669
811
|
name = key.to_s
|
@@ -671,14 +813,15 @@ module ActiveRecord
|
|
671
813
|
verify_readonly_attribute(name) || name
|
672
814
|
end
|
673
815
|
|
674
|
-
|
675
|
-
attributes.
|
676
|
-
|
816
|
+
update_constraints = _primary_key_constraints_hash
|
817
|
+
attributes = attributes.each_with_object({}) do |(k, v), h|
|
818
|
+
h[k] = @attributes.write_cast_value(k, v)
|
819
|
+
clear_attribute_change(k)
|
677
820
|
end
|
678
821
|
|
679
822
|
affected_rows = self.class._update_record(
|
680
823
|
attributes,
|
681
|
-
|
824
|
+
update_constraints
|
682
825
|
)
|
683
826
|
|
684
827
|
affected_rows == 1
|
@@ -800,13 +943,13 @@ module ActiveRecord
|
|
800
943
|
def reload(options = nil)
|
801
944
|
self.class.connection.clear_query_cache
|
802
945
|
|
803
|
-
fresh_object =
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
end
|
946
|
+
fresh_object = if apply_scoping?(options)
|
947
|
+
_find_record(options)
|
948
|
+
else
|
949
|
+
self.class.unscoped { _find_record(options) }
|
950
|
+
end
|
809
951
|
|
952
|
+
@association_cache = fresh_object.instance_variable_get(:@association_cache)
|
810
953
|
@attributes = fresh_object.instance_variable_get(:@attributes)
|
811
954
|
@new_record = false
|
812
955
|
@previously_new_record = false
|
@@ -849,6 +992,7 @@ module ActiveRecord
|
|
849
992
|
#
|
850
993
|
def touch(*names, time: nil)
|
851
994
|
_raise_record_not_touched_error unless persisted?
|
995
|
+
_raise_readonly_record_error if readonly?
|
852
996
|
|
853
997
|
attribute_names = timestamp_attributes_for_update_in_model
|
854
998
|
attribute_names |= names.map! do |name|
|
@@ -865,6 +1009,29 @@ module ActiveRecord
|
|
865
1009
|
end
|
866
1010
|
|
867
1011
|
private
|
1012
|
+
def strict_loaded_associations
|
1013
|
+
@association_cache.find_all do |_, assoc|
|
1014
|
+
assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
|
1015
|
+
end.map(&:first)
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
def _find_record(options)
|
1019
|
+
if options && options[:lock]
|
1020
|
+
self.class.preload(strict_loaded_associations).lock(options[:lock]).find(id)
|
1021
|
+
else
|
1022
|
+
self.class.preload(strict_loaded_associations).find(id)
|
1023
|
+
end
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
def apply_scoping?(options)
|
1027
|
+
!(options && options[:unscoped]) &&
|
1028
|
+
(self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
def _primary_key_constraints_hash
|
1032
|
+
{ @primary_key => id_in_database }
|
1033
|
+
end
|
1034
|
+
|
868
1035
|
# A hook to be overridden by association modules.
|
869
1036
|
def destroy_associations
|
870
1037
|
end
|
@@ -874,7 +1041,7 @@ module ActiveRecord
|
|
874
1041
|
end
|
875
1042
|
|
876
1043
|
def _delete_row
|
877
|
-
self.class._delete_record(
|
1044
|
+
self.class._delete_record(_primary_key_constraints_hash)
|
878
1045
|
end
|
879
1046
|
|
880
1047
|
def _touch_row(attribute_names, time)
|
@@ -890,7 +1057,7 @@ module ActiveRecord
|
|
890
1057
|
def _update_row(attribute_names, attempted_action = "update")
|
891
1058
|
self.class._update_record(
|
892
1059
|
attributes_with_values(attribute_names),
|
893
|
-
|
1060
|
+
_primary_key_constraints_hash
|
894
1061
|
)
|
895
1062
|
end
|
896
1063
|
|
@@ -946,7 +1113,8 @@ module ActiveRecord
|
|
946
1113
|
|
947
1114
|
def _raise_record_not_destroyed
|
948
1115
|
@_association_destroy_exception ||= nil
|
949
|
-
|
1116
|
+
key = self.class.primary_key
|
1117
|
+
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{send(key)}", self)
|
950
1118
|
ensure
|
951
1119
|
@_association_destroy_exception = nil
|
952
1120
|
end
|
@@ -28,7 +28,7 @@ module ActiveRecord
|
|
28
28
|
def self.run
|
29
29
|
pools = []
|
30
30
|
|
31
|
-
if ActiveRecord
|
31
|
+
if ActiveRecord.legacy_connection_handling
|
32
32
|
ActiveRecord::Base.connection_handlers.each do |key, handler|
|
33
33
|
pools.concat(handler.connection_pool_list.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! })
|
34
34
|
end
|
@@ -42,7 +42,7 @@ module ActiveRecord
|
|
42
42
|
def self.complete(pools)
|
43
43
|
pools.each { |pool| pool.disable_query_cache! }
|
44
44
|
|
45
|
-
if ActiveRecord
|
45
|
+
if ActiveRecord.legacy_connection_handling
|
46
46
|
ActiveRecord::Base.connection_handlers.each do |_, handler|
|
47
47
|
handler.connection_pool_list.each do |pool|
|
48
48
|
pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module/attribute_accessors_per_thread"
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
# = Active Record Query Logs
|
7
|
+
#
|
8
|
+
# Automatically tag SQL queries with runtime information.
|
9
|
+
#
|
10
|
+
# Default tags available for use:
|
11
|
+
#
|
12
|
+
# * +application+
|
13
|
+
# * +pid+
|
14
|
+
# * +socket+
|
15
|
+
# * +db_host+
|
16
|
+
# * +database+
|
17
|
+
#
|
18
|
+
# _Action Controller and Active Job tags are also defined when used in Rails:_
|
19
|
+
#
|
20
|
+
# * +controller+
|
21
|
+
# * +action+
|
22
|
+
# * +job+
|
23
|
+
#
|
24
|
+
# The tags used in a query can be configured directly:
|
25
|
+
#
|
26
|
+
# ActiveRecord::QueryLogs.tags = [ :application, :controller, :action, :job ]
|
27
|
+
#
|
28
|
+
# or via Rails configuration:
|
29
|
+
#
|
30
|
+
# config.active_record.query_log_tags = [ :application, :controller, :action, :job ]
|
31
|
+
#
|
32
|
+
# To add new comment tags, add a hash to the tags array containing the keys and values you
|
33
|
+
# want to add to the comment. Dynamic content can be created by setting a proc or lambda value in a hash,
|
34
|
+
# and can reference any value stored in the +context+ object.
|
35
|
+
#
|
36
|
+
# Example:
|
37
|
+
#
|
38
|
+
# tags = [
|
39
|
+
# :application,
|
40
|
+
# {
|
41
|
+
# custom_tag: ->(context) { context[:controller]&.controller_name },
|
42
|
+
# custom_value: -> { Custom.value },
|
43
|
+
# }
|
44
|
+
# ]
|
45
|
+
# ActiveRecord::QueryLogs.tags = tags
|
46
|
+
#
|
47
|
+
# The QueryLogs +context+ can be manipulated via the +ActiveSupport::ExecutionContext.set+ method.
|
48
|
+
#
|
49
|
+
# Temporary updates limited to the execution of a block:
|
50
|
+
#
|
51
|
+
# ActiveSupport::ExecutionContext.set(foo: Bar.new) do
|
52
|
+
# posts = Post.all
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# Direct updates to a context value:
|
56
|
+
#
|
57
|
+
# ActiveSupport::ExecutionContext[:foo] = Bar.new
|
58
|
+
#
|
59
|
+
# Tag comments can be prepended to the query:
|
60
|
+
#
|
61
|
+
# ActiveRecord::QueryLogs.prepend_comment = true
|
62
|
+
#
|
63
|
+
# For applications where the content will not change during the lifetime of
|
64
|
+
# the request or job execution, the tags can be cached for reuse in every query:
|
65
|
+
#
|
66
|
+
# ActiveRecord::QueryLogs.cache_query_log_tags = true
|
67
|
+
#
|
68
|
+
# This option can be set during application configuration or in a Rails initializer:
|
69
|
+
#
|
70
|
+
# config.active_record.cache_query_log_tags = true
|
71
|
+
module QueryLogs
|
72
|
+
mattr_accessor :taggings, instance_accessor: false, default: {}
|
73
|
+
mattr_accessor :tags, instance_accessor: false, default: [ :application ]
|
74
|
+
mattr_accessor :prepend_comment, instance_accessor: false, default: false
|
75
|
+
mattr_accessor :cache_query_log_tags, instance_accessor: false, default: false
|
76
|
+
thread_mattr_accessor :cached_comment, instance_accessor: false
|
77
|
+
|
78
|
+
class << self
|
79
|
+
def call(sql) # :nodoc:
|
80
|
+
if prepend_comment
|
81
|
+
"#{self.comment} #{sql}"
|
82
|
+
else
|
83
|
+
"#{sql} #{self.comment}"
|
84
|
+
end.strip
|
85
|
+
end
|
86
|
+
|
87
|
+
def clear_cache # :nodoc:
|
88
|
+
self.cached_comment = nil
|
89
|
+
end
|
90
|
+
|
91
|
+
ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
|
92
|
+
|
93
|
+
private
|
94
|
+
# Returns an SQL comment +String+ containing the query log tags.
|
95
|
+
# Sets and returns a cached comment if <tt>cache_query_log_tags</tt> is +true+.
|
96
|
+
def comment
|
97
|
+
if cache_query_log_tags
|
98
|
+
self.cached_comment ||= uncached_comment
|
99
|
+
else
|
100
|
+
uncached_comment
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def uncached_comment
|
105
|
+
content = tag_content
|
106
|
+
if content.present?
|
107
|
+
"/*#{escape_sql_comment(content)}*/"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def escape_sql_comment(content)
|
112
|
+
content.to_s.gsub(%r{ (/ (?: | \g<1>) \*) \+? \s* | \s* (\* (?: | \g<2>) /) }x, "")
|
113
|
+
end
|
114
|
+
|
115
|
+
def tag_content
|
116
|
+
context = ActiveSupport::ExecutionContext.to_h
|
117
|
+
|
118
|
+
tags.flat_map { |i| [*i] }.filter_map do |tag|
|
119
|
+
key, handler = tag
|
120
|
+
handler ||= taggings[key]
|
121
|
+
|
122
|
+
val = if handler.nil?
|
123
|
+
context[key]
|
124
|
+
elsif handler.respond_to?(:call)
|
125
|
+
if handler.arity == 0
|
126
|
+
handler.call
|
127
|
+
else
|
128
|
+
handler.call(context)
|
129
|
+
end
|
130
|
+
else
|
131
|
+
handler
|
132
|
+
end
|
133
|
+
"#{key}:#{val}" unless val.nil?
|
134
|
+
end.join(",")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|