activerecord 6.1.7.4 → 7.0.0
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 +1055 -1170
- data/MIT-LICENSE +1 -1
- 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 +18 -19
- data/lib/active_record/associations/collection_proxy.rb +8 -3
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- 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 +6 -2
- 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 +90 -82
- 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 +66 -12
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +13 -14
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +6 -21
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +2 -14
- 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 +43 -82
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +34 -13
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +69 -18
- 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 +97 -81
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +35 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
- 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 +28 -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 +50 -76
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -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 +27 -16
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -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 +15 -16
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +47 -53
- data/lib/active_record/core.rb +121 -146
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -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 +52 -11
- 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 +61 -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 +208 -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 +49 -42
- 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 +17 -20
- 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 +9 -3
- data/lib/active_record/log_subscriber.rb +14 -3
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +8 -3
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +89 -10
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +110 -80
- data/lib/active_record/model_schema.rb +45 -58
- 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 +219 -52
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +127 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +66 -129
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +67 -50
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +40 -36
- data/lib/active_record/relation/delegation.rb +6 -6
- 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 +235 -63
- 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 +169 -84
- data/lib/active_record/result.rb +17 -7
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +10 -3
- data/lib/active_record/schema_migration.rb +4 -4
- data/lib/active_record/scoping/default.rb +61 -12
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/store.rb +1 -6
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +116 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +9 -13
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +2 -2
- 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 +1 -1
- data/lib/active_record/validations/uniqueness.rb +1 -1
- data/lib/active_record.rb +204 -28
- 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 +58 -14
@@ -63,8 +63,8 @@ module ActiveRecord
|
|
63
63
|
# go through Active Record's type casting and serialization.
|
64
64
|
#
|
65
65
|
# See <tt>ActiveRecord::Persistence#insert_all</tt> for documentation.
|
66
|
-
def insert(attributes, returning: nil, unique_by: nil)
|
67
|
-
insert_all([ attributes ], returning: returning, unique_by: unique_by)
|
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
|
@@ -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
|
@@ -130,8 +152,8 @@ module ActiveRecord
|
|
130
152
|
# go through Active Record's type casting and serialization.
|
131
153
|
#
|
132
154
|
# See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
|
133
|
-
def insert!(attributes, returning: nil)
|
134
|
-
insert_all!([ attributes ], returning: returning)
|
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
|
@@ -160,6 +182,20 @@ module ActiveRecord
|
|
160
182
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
161
183
|
# clause entirely.
|
162
184
|
#
|
185
|
+
# You can also pass an SQL string if you need more control on the return values
|
186
|
+
# (for example, <tt>returning: "id, name as new_name"</tt>).
|
187
|
+
#
|
188
|
+
# [:record_timestamps]
|
189
|
+
# By default, automatic setting of timestamp columns is controlled by
|
190
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
191
|
+
# behavior.
|
192
|
+
#
|
193
|
+
# To override this and force automatic setting of timestamp columns one
|
194
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
195
|
+
#
|
196
|
+
# record_timestamps: true # Always set timestamps automatically
|
197
|
+
# record_timestamps: false # Never set timestamps automatically
|
198
|
+
#
|
163
199
|
# ==== Examples
|
164
200
|
#
|
165
201
|
# # Insert multiple records
|
@@ -174,8 +210,8 @@ module ActiveRecord
|
|
174
210
|
# { id: 1, title: "Rework", author: "David" },
|
175
211
|
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
176
212
|
# ])
|
177
|
-
def insert_all!(attributes, returning: nil)
|
178
|
-
InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
|
213
|
+
def insert_all!(attributes, returning: nil, record_timestamps: nil)
|
214
|
+
InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
|
179
215
|
end
|
180
216
|
|
181
217
|
# Updates or inserts (upserts) a single record into the database in a
|
@@ -184,8 +220,8 @@ module ActiveRecord
|
|
184
220
|
# go through Active Record's type casting and serialization.
|
185
221
|
#
|
186
222
|
# See <tt>ActiveRecord::Persistence#upsert_all</tt> for documentation.
|
187
|
-
def upsert(attributes, returning: nil, unique_by: nil)
|
188
|
-
upsert_all([ attributes ], returning: returning, unique_by: unique_by)
|
223
|
+
def upsert(attributes, on_duplicate: :update, returning: nil, unique_by: nil, record_timestamps: nil)
|
224
|
+
upsert_all([ attributes ], on_duplicate: on_duplicate, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
189
225
|
end
|
190
226
|
|
191
227
|
# Updates or inserts (upserts) multiple records into the database in a
|
@@ -199,6 +235,10 @@ module ActiveRecord
|
|
199
235
|
# Returns an <tt>ActiveRecord::Result</tt> with its contents based on
|
200
236
|
# <tt>:returning</tt> (see below).
|
201
237
|
#
|
238
|
+
# By default, +upsert_all+ will update all the columns that can be updated when
|
239
|
+
# there is a conflict. These are all the columns except primary keys, read-only
|
240
|
+
# columns, and columns covered by the optional +unique_by+.
|
241
|
+
#
|
202
242
|
# ==== Options
|
203
243
|
#
|
204
244
|
# [:returning]
|
@@ -208,6 +248,9 @@ module ActiveRecord
|
|
208
248
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
209
249
|
# clause entirely.
|
210
250
|
#
|
251
|
+
# You can also pass an SQL string if you need more control on the return values
|
252
|
+
# (for example, <tt>returning: "id, name as new_name"</tt>).
|
253
|
+
#
|
211
254
|
# [:unique_by]
|
212
255
|
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
213
256
|
# by every unique index on the table. Any duplicate rows are skipped.
|
@@ -228,6 +271,54 @@ module ActiveRecord
|
|
228
271
|
# <tt>:unique_by</tt> is recommended to be paired with
|
229
272
|
# Active Record's schema_cache.
|
230
273
|
#
|
274
|
+
# [:on_duplicate]
|
275
|
+
# Configure the SQL update sentence that will be used in case of conflict.
|
276
|
+
#
|
277
|
+
# NOTE: If you use this option you must provide all the columns you want to update
|
278
|
+
# by yourself.
|
279
|
+
#
|
280
|
+
# Example:
|
281
|
+
#
|
282
|
+
# Commodity.upsert_all(
|
283
|
+
# [
|
284
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
285
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
286
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
287
|
+
# ],
|
288
|
+
# on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
|
289
|
+
# )
|
290
|
+
#
|
291
|
+
# See the related +:update_only+ option. Both options can't be used at the same time.
|
292
|
+
#
|
293
|
+
# [:update_only]
|
294
|
+
# Provide a list of column names that will be updated in case of conflict. If not provided,
|
295
|
+
# +upsert_all+ will update all the columns that can be updated. These are all the columns
|
296
|
+
# except primary keys, read-only columns, and columns covered by the optional +unique_by+
|
297
|
+
#
|
298
|
+
# Example:
|
299
|
+
#
|
300
|
+
# Commodity.upsert_all(
|
301
|
+
# [
|
302
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
303
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
304
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
305
|
+
# ],
|
306
|
+
# update_only: [:price] # Only prices will be updated
|
307
|
+
# )
|
308
|
+
#
|
309
|
+
# See the related +:on_duplicate+ option. Both options can't be used at the same time.
|
310
|
+
#
|
311
|
+
# [:record_timestamps]
|
312
|
+
# By default, automatic setting of timestamp columns is controlled by
|
313
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
314
|
+
# behavior.
|
315
|
+
#
|
316
|
+
# To override this and force automatic setting of timestamp columns one
|
317
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
318
|
+
#
|
319
|
+
# record_timestamps: true # Always set timestamps automatically
|
320
|
+
# record_timestamps: false # Never set timestamps automatically
|
321
|
+
#
|
231
322
|
# ==== Examples
|
232
323
|
#
|
233
324
|
# # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
|
@@ -239,8 +330,8 @@ module ActiveRecord
|
|
239
330
|
# ], unique_by: :isbn)
|
240
331
|
#
|
241
332
|
# 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: :
|
333
|
+
def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
|
334
|
+
InsertAll.new(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
|
244
335
|
end
|
245
336
|
|
246
337
|
# Given an attributes hash, +instantiate+ returns a new instance of
|
@@ -264,6 +355,7 @@ module ActiveRecord
|
|
264
355
|
# ==== Parameters
|
265
356
|
#
|
266
357
|
# * +id+ - This should be the id or an array of ids to be updated.
|
358
|
+
# Optional argument, defaults to all records in the relation.
|
267
359
|
# * +attributes+ - This should be a hash of attributes or an array of hashes.
|
268
360
|
#
|
269
361
|
# ==== Examples
|
@@ -286,6 +378,11 @@ module ActiveRecord
|
|
286
378
|
# for updating all records in a single query.
|
287
379
|
def update(id = :all, attributes)
|
288
380
|
if id.is_a?(Array)
|
381
|
+
if id.any?(ActiveRecord::Base)
|
382
|
+
raise ArgumentError,
|
383
|
+
"You are passing an array of ActiveRecord::Base instances to `update`. " \
|
384
|
+
"Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
|
385
|
+
end
|
289
386
|
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
290
387
|
object.update(attributes[idx])
|
291
388
|
}
|
@@ -303,6 +400,32 @@ module ActiveRecord
|
|
303
400
|
end
|
304
401
|
end
|
305
402
|
|
403
|
+
# Updates the object (or multiple objects) just like #update but calls #update! instead
|
404
|
+
# of +update+, so an exception is raised if the record is invalid and saving will fail.
|
405
|
+
def update!(id = :all, attributes)
|
406
|
+
if id.is_a?(Array)
|
407
|
+
if id.any?(ActiveRecord::Base)
|
408
|
+
raise ArgumentError,
|
409
|
+
"You are passing an array of ActiveRecord::Base instances to `update!`. " \
|
410
|
+
"Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
|
411
|
+
end
|
412
|
+
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
413
|
+
object.update!(attributes[idx])
|
414
|
+
}
|
415
|
+
elsif id == :all
|
416
|
+
all.each { |record| record.update!(attributes) }
|
417
|
+
else
|
418
|
+
if ActiveRecord::Base === id
|
419
|
+
raise ArgumentError,
|
420
|
+
"You are passing an instance of ActiveRecord::Base to `update!`. " \
|
421
|
+
"Please pass the id of the object by calling `.id`."
|
422
|
+
end
|
423
|
+
object = find(id)
|
424
|
+
object.update!(attributes)
|
425
|
+
object
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
306
429
|
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
|
307
430
|
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
308
431
|
# less efficient than #delete but allows cleanup methods and other actions to be run.
|
@@ -356,40 +479,52 @@ module ActiveRecord
|
|
356
479
|
primary_key = self.primary_key
|
357
480
|
primary_key_value = nil
|
358
481
|
|
359
|
-
if
|
360
|
-
|
361
|
-
|
362
|
-
if !primary_key_value && prefetch_primary_key?
|
482
|
+
if prefetch_primary_key? && primary_key
|
483
|
+
values[primary_key] ||= begin
|
363
484
|
primary_key_value = next_sequence_value
|
364
|
-
|
485
|
+
_default_attributes[primary_key].with_cast_value(primary_key_value)
|
365
486
|
end
|
366
487
|
end
|
367
488
|
|
489
|
+
im = Arel::InsertManager.new(arel_table)
|
490
|
+
|
368
491
|
if values.empty?
|
369
|
-
im
|
370
|
-
im.into arel_table
|
492
|
+
im.insert(connection.empty_insert_statement_value(primary_key))
|
371
493
|
else
|
372
|
-
im
|
494
|
+
im.insert(values.transform_keys { |name| arel_table[name] })
|
373
495
|
end
|
374
496
|
|
375
497
|
connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
|
376
498
|
end
|
377
499
|
|
378
500
|
def _update_record(values, constraints) # :nodoc:
|
379
|
-
constraints =
|
501
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
502
|
+
|
503
|
+
default_constraint = build_default_constraint
|
504
|
+
constraints << default_constraint if default_constraint
|
505
|
+
|
506
|
+
if current_scope = self.global_current_scope
|
507
|
+
constraints << current_scope.where_clause.ast
|
508
|
+
end
|
380
509
|
|
381
|
-
um =
|
382
|
-
|
383
|
-
|
510
|
+
um = Arel::UpdateManager.new(arel_table)
|
511
|
+
um.set(values.transform_keys { |name| arel_table[name] })
|
512
|
+
um.wheres = constraints
|
384
513
|
|
385
514
|
connection.update(um, "#{self} Update")
|
386
515
|
end
|
387
516
|
|
388
517
|
def _delete_record(constraints) # :nodoc:
|
389
|
-
constraints =
|
518
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
390
519
|
|
391
|
-
|
392
|
-
|
520
|
+
default_constraint = build_default_constraint
|
521
|
+
constraints << default_constraint if default_constraint
|
522
|
+
|
523
|
+
if current_scope = self.global_current_scope
|
524
|
+
constraints << current_scope.where_clause.ast
|
525
|
+
end
|
526
|
+
|
527
|
+
dm = Arel::DeleteManager.new(arel_table)
|
393
528
|
dm.wheres = constraints
|
394
529
|
|
395
530
|
connection.delete(dm, "#{self} Destroy")
|
@@ -412,12 +547,14 @@ module ActiveRecord
|
|
412
547
|
self
|
413
548
|
end
|
414
549
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
550
|
+
# Called by +_update_record+ and +_delete_record+
|
551
|
+
# to build `where` clause from default scopes.
|
552
|
+
# Skips empty scopes.
|
553
|
+
def build_default_constraint
|
554
|
+
return unless default_scopes?(all_queries: true)
|
555
|
+
|
556
|
+
default_where_clause = default_scoped(all_queries: true).where_clause
|
557
|
+
default_where_clause.ast unless default_where_clause.empty?
|
421
558
|
end
|
422
559
|
end
|
423
560
|
|
@@ -434,6 +571,11 @@ module ActiveRecord
|
|
434
571
|
@previously_new_record
|
435
572
|
end
|
436
573
|
|
574
|
+
# Returns true if this object was previously persisted but now it has been deleted.
|
575
|
+
def previously_persisted?
|
576
|
+
!new_record? && destroyed?
|
577
|
+
end
|
578
|
+
|
437
579
|
# Returns true if this object has been destroyed, otherwise returns false.
|
438
580
|
def destroyed?
|
439
581
|
@destroyed
|
@@ -556,17 +698,17 @@ module ActiveRecord
|
|
556
698
|
end
|
557
699
|
|
558
700
|
# 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
|
701
|
+
# current record. This is mostly useful in relation to single table
|
702
|
+
# inheritance (STI) structures where you want a subclass to appear as the
|
561
703
|
# superclass. This can be used along with record identification in
|
562
704
|
# Action Pack to allow, say, <tt>Client < Company</tt> to do something
|
563
705
|
# like render <tt>partial: @client.becomes(Company)</tt> to render that
|
564
706
|
# instance using the companies/company partial instead of clients/client.
|
565
707
|
#
|
566
708
|
# Note: The new instance will share a link to the same attributes as the original class.
|
567
|
-
# Therefore the
|
709
|
+
# Therefore the STI column value will still be the same.
|
568
710
|
# Any change to the attributes on either instance will affect both instances.
|
569
|
-
# If you want to change the
|
711
|
+
# If you want to change the STI column as well, use #becomes! instead.
|
570
712
|
def becomes(klass)
|
571
713
|
became = klass.allocate
|
572
714
|
|
@@ -581,11 +723,11 @@ module ActiveRecord
|
|
581
723
|
became
|
582
724
|
end
|
583
725
|
|
584
|
-
# Wrapper around #becomes that also changes the instance's
|
726
|
+
# Wrapper around #becomes that also changes the instance's STI column value.
|
585
727
|
# This is especially useful if you want to persist the changed class in your
|
586
728
|
# database.
|
587
729
|
#
|
588
|
-
# Note: The old instance's
|
730
|
+
# Note: The old instance's STI column value will be changed too, as both objects
|
589
731
|
# share the same set of attributes.
|
590
732
|
def becomes!(klass)
|
591
733
|
became = becomes(klass)
|
@@ -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
|
@@ -865,6 +1008,29 @@ module ActiveRecord
|
|
865
1008
|
end
|
866
1009
|
|
867
1010
|
private
|
1011
|
+
def strict_loaded_associations
|
1012
|
+
@association_cache.find_all do |_, assoc|
|
1013
|
+
assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
|
1014
|
+
end.map(&:first)
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
def _find_record(options)
|
1018
|
+
if options && options[:lock]
|
1019
|
+
self.class.preload(strict_loaded_associations).lock(options[:lock]).find(id)
|
1020
|
+
else
|
1021
|
+
self.class.preload(strict_loaded_associations).find(id)
|
1022
|
+
end
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
def apply_scoping?(options)
|
1026
|
+
!(options && options[:unscoped]) &&
|
1027
|
+
(self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
def _primary_key_constraints_hash
|
1031
|
+
{ @primary_key => id_in_database }
|
1032
|
+
end
|
1033
|
+
|
868
1034
|
# A hook to be overridden by association modules.
|
869
1035
|
def destroy_associations
|
870
1036
|
end
|
@@ -874,7 +1040,7 @@ module ActiveRecord
|
|
874
1040
|
end
|
875
1041
|
|
876
1042
|
def _delete_row
|
877
|
-
self.class._delete_record(
|
1043
|
+
self.class._delete_record(_primary_key_constraints_hash)
|
878
1044
|
end
|
879
1045
|
|
880
1046
|
def _touch_row(attribute_names, time)
|
@@ -890,7 +1056,7 @@ module ActiveRecord
|
|
890
1056
|
def _update_row(attribute_names, attempted_action = "update")
|
891
1057
|
self.class._update_record(
|
892
1058
|
attributes_with_values(attribute_names),
|
893
|
-
|
1059
|
+
_primary_key_constraints_hash
|
894
1060
|
)
|
895
1061
|
end
|
896
1062
|
|
@@ -946,7 +1112,8 @@ module ActiveRecord
|
|
946
1112
|
|
947
1113
|
def _raise_record_not_destroyed
|
948
1114
|
@_association_destroy_exception ||= nil
|
949
|
-
|
1115
|
+
key = self.class.primary_key
|
1116
|
+
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{send(key)}", self)
|
950
1117
|
ensure
|
951
1118
|
@_association_destroy_exception = nil
|
952
1119
|
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
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Querying
|
5
5
|
QUERYING_METHODS = [
|
6
|
-
:find, :find_by, :find_by!, :take, :take!, :first, :first!, :last, :last!,
|
6
|
+
:find, :find_by, :find_by!, :take, :take!, :sole, :find_sole_by, :first, :first!, :last, :last!,
|
7
7
|
:second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!,
|
8
8
|
:forty_two, :forty_two!, :third_to_last, :third_to_last!, :second_to_last, :second_to_last!,
|
9
9
|
:exists?, :any?, :many?, :none?, :one?,
|
@@ -12,12 +12,12 @@ module ActiveRecord
|
|
12
12
|
:create_or_find_by, :create_or_find_by!,
|
13
13
|
:destroy_all, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
|
14
14
|
:find_each, :find_in_batches, :in_batches,
|
15
|
-
:select, :reselect, :order, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
|
16
|
-
:where, :rewhere, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
|
15
|
+
:select, :reselect, :order, :in_order_of, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
|
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, :strict_loading
|
20
|
+
:pluck, :pick, :ids, :strict_loading, :excluding, :without
|
21
21
|
].freeze # :nodoc:
|
22
22
|
delegate(*QUERYING_METHODS, to: :all)
|
23
23
|
|
@@ -43,8 +43,18 @@ module ActiveRecord
|
|
43
43
|
#
|
44
44
|
# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
|
45
45
|
# Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
|
46
|
+
#
|
47
|
+
# Note that building your own SQL query string from user input may expose your application to
|
48
|
+
# injection attacks (https://guides.rubyonrails.org/security.html#sql-injection).
|
46
49
|
def find_by_sql(sql, binds = [], preparable: nil, &block)
|
47
|
-
|
50
|
+
_load_from_sql(_query_by_sql(sql, binds, preparable: preparable), &block)
|
51
|
+
end
|
52
|
+
|
53
|
+
def _query_by_sql(sql, binds = [], preparable: nil, async: false) # :nodoc:
|
54
|
+
connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable, async: async)
|
55
|
+
end
|
56
|
+
|
57
|
+
def _load_from_sql(result_set, &block) # :nodoc:
|
48
58
|
column_types = result_set.column_types
|
49
59
|
|
50
60
|
unless column_types.empty?
|