activerecord 6.1.4.1 → 7.0.1
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 +1132 -936
- 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 +34 -27
- 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 +187 -55
- 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 +118 -90
- 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/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 +34 -9
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +78 -22
- 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 +37 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +5 -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 +21 -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 +50 -50
- 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 +29 -18
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +205 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +29 -4
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +27 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +16 -14
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +89 -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 +122 -132
- 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 +16 -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 +3 -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 +107 -3
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +109 -79
- 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 +43 -38
- 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 +243 -61
- 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 +184 -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/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +120 -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 +4 -4
- 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 +59 -14
@@ -8,8 +8,6 @@ require "active_support/core_ext/array/wrap"
|
|
8
8
|
|
9
9
|
module ActiveRecord
|
10
10
|
module QueryMethods
|
11
|
-
extend ActiveSupport::Concern
|
12
|
-
|
13
11
|
include ActiveModel::ForbiddenAttributesProtection
|
14
12
|
|
15
13
|
# WhereChain objects act as placeholder for queries in which #where does not have any parameter.
|
@@ -50,6 +48,34 @@ module ActiveRecord
|
|
50
48
|
@scope
|
51
49
|
end
|
52
50
|
|
51
|
+
# Returns a new relation with joins and where clause to identify
|
52
|
+
# associated relations.
|
53
|
+
#
|
54
|
+
# For example, posts that are associated to a related author:
|
55
|
+
#
|
56
|
+
# Post.where.associated(:author)
|
57
|
+
# # SELECT "posts".* FROM "posts"
|
58
|
+
# # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
|
59
|
+
# # WHERE "authors"."id" IS NOT NULL
|
60
|
+
#
|
61
|
+
# Additionally, multiple relations can be combined. This will return posts
|
62
|
+
# associated to both an author and any comments:
|
63
|
+
#
|
64
|
+
# Post.where.associated(:author, :comments)
|
65
|
+
# # SELECT "posts".* FROM "posts"
|
66
|
+
# # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
|
67
|
+
# # INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
|
68
|
+
# # WHERE "authors"."id" IS NOT NULL AND "comments"."id" IS NOT NULL
|
69
|
+
def associated(*associations)
|
70
|
+
associations.each do |association|
|
71
|
+
reflection = @scope.klass._reflect_on_association(association)
|
72
|
+
@scope.joins!(association)
|
73
|
+
self.not(reflection.table_name => { reflection.association_primary_key => nil })
|
74
|
+
end
|
75
|
+
|
76
|
+
@scope
|
77
|
+
end
|
78
|
+
|
53
79
|
# Returns a new relation with left outer joins and where clause to identify
|
54
80
|
# missing relations.
|
55
81
|
#
|
@@ -68,12 +94,14 @@ module ActiveRecord
|
|
68
94
|
# # LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
|
69
95
|
# # LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id"
|
70
96
|
# # WHERE "authors"."id" IS NULL AND "comments"."id" IS NULL
|
71
|
-
def missing(*
|
72
|
-
|
73
|
-
reflection = @scope.klass._reflect_on_association(
|
74
|
-
|
75
|
-
|
76
|
-
|
97
|
+
def missing(*associations)
|
98
|
+
associations.each do |association|
|
99
|
+
reflection = @scope.klass._reflect_on_association(association)
|
100
|
+
unless reflection
|
101
|
+
raise ArgumentError.new("An association named `:#{association}` does not exist on the model `#{@scope.name}`.")
|
102
|
+
end
|
103
|
+
@scope.left_outer_joins!(association)
|
104
|
+
@scope.where!(reflection.table_name => { reflection.association_primary_key => nil })
|
77
105
|
end
|
78
106
|
|
79
107
|
@scope
|
@@ -148,7 +176,7 @@ module ActiveRecord
|
|
148
176
|
#
|
149
177
|
# User.includes(:posts).where(posts: { name: 'example' })
|
150
178
|
def includes(*args)
|
151
|
-
check_if_method_has_arguments!(
|
179
|
+
check_if_method_has_arguments!(__callee__, args)
|
152
180
|
spawn.includes!(*args)
|
153
181
|
end
|
154
182
|
|
@@ -164,7 +192,7 @@ module ActiveRecord
|
|
164
192
|
# # FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
|
165
193
|
# # "users"."id"
|
166
194
|
def eager_load(*args)
|
167
|
-
check_if_method_has_arguments!(
|
195
|
+
check_if_method_has_arguments!(__callee__, args)
|
168
196
|
spawn.eager_load!(*args)
|
169
197
|
end
|
170
198
|
|
@@ -178,7 +206,7 @@ module ActiveRecord
|
|
178
206
|
# User.preload(:posts)
|
179
207
|
# # SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
|
180
208
|
def preload(*args)
|
181
|
-
check_if_method_has_arguments!(
|
209
|
+
check_if_method_has_arguments!(__callee__, args)
|
182
210
|
spawn.preload!(*args)
|
183
211
|
end
|
184
212
|
|
@@ -211,7 +239,7 @@ module ActiveRecord
|
|
211
239
|
# User.includes(:posts).where("posts.name = 'foo'").references(:posts)
|
212
240
|
# # Query now knows the string references posts, so adds a JOIN
|
213
241
|
def references(*table_names)
|
214
|
-
check_if_method_has_arguments!(
|
242
|
+
check_if_method_has_arguments!(__callee__, table_names)
|
215
243
|
spawn.references!(*table_names)
|
216
244
|
end
|
217
245
|
|
@@ -269,7 +297,7 @@ module ActiveRecord
|
|
269
297
|
return super()
|
270
298
|
end
|
271
299
|
|
272
|
-
check_if_method_has_arguments!(
|
300
|
+
check_if_method_has_arguments!(__callee__, fields, "Call `select' with at least one field.")
|
273
301
|
spawn._select!(*fields)
|
274
302
|
end
|
275
303
|
|
@@ -289,7 +317,7 @@ module ActiveRecord
|
|
289
317
|
# This is short-hand for <tt>unscope(:select).select(fields)</tt>.
|
290
318
|
# Note that we're unscoping the entire select statement.
|
291
319
|
def reselect(*args)
|
292
|
-
check_if_method_has_arguments!(
|
320
|
+
check_if_method_has_arguments!(__callee__, args)
|
293
321
|
spawn.reselect!(*args)
|
294
322
|
end
|
295
323
|
|
@@ -320,7 +348,7 @@ module ActiveRecord
|
|
320
348
|
# User.select([:id, :first_name]).group(:id, :first_name).first(3)
|
321
349
|
# # => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
|
322
350
|
def group(*args)
|
323
|
-
check_if_method_has_arguments!(
|
351
|
+
check_if_method_has_arguments!(__callee__, args)
|
324
352
|
spawn.group!(*args)
|
325
353
|
end
|
326
354
|
|
@@ -329,17 +357,37 @@ module ActiveRecord
|
|
329
357
|
self
|
330
358
|
end
|
331
359
|
|
332
|
-
#
|
360
|
+
# Applies an <code>ORDER BY</code> clause to a query.
|
361
|
+
#
|
362
|
+
# #order accepts arguments in one of several formats.
|
363
|
+
#
|
364
|
+
# === symbols
|
365
|
+
#
|
366
|
+
# The symbol represents the name of the column you want to order the results by.
|
333
367
|
#
|
334
368
|
# User.order(:name)
|
335
369
|
# # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
|
336
370
|
#
|
371
|
+
# By default, the order is ascending. If you want descending order, you can
|
372
|
+
# map the column name symbol to +:desc+.
|
373
|
+
#
|
337
374
|
# User.order(email: :desc)
|
338
375
|
# # SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
|
339
376
|
#
|
377
|
+
# Multiple columns can be passed this way, and they will be applied in the order specified.
|
378
|
+
#
|
340
379
|
# User.order(:name, email: :desc)
|
341
380
|
# # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
|
342
381
|
#
|
382
|
+
# === strings
|
383
|
+
#
|
384
|
+
# Strings are passed directly to the database, allowing you to specify
|
385
|
+
# simple SQL expressions.
|
386
|
+
#
|
387
|
+
# This could be a source of SQL injection, so only strings composed of plain
|
388
|
+
# column names and simple <code>function(column_name)</code> expressions
|
389
|
+
# with optional +ASC+/+DESC+ modifiers are allowed.
|
390
|
+
#
|
343
391
|
# User.order('name')
|
344
392
|
# # SELECT "users".* FROM "users" ORDER BY name
|
345
393
|
#
|
@@ -348,8 +396,21 @@ module ActiveRecord
|
|
348
396
|
#
|
349
397
|
# User.order('name DESC, email')
|
350
398
|
# # SELECT "users".* FROM "users" ORDER BY name DESC, email
|
399
|
+
#
|
400
|
+
# === Arel
|
401
|
+
#
|
402
|
+
# If you need to pass in complicated expressions that you have verified
|
403
|
+
# are safe for the database, you can use Arel.
|
404
|
+
#
|
405
|
+
# User.order(Arel.sql('end_date - start_date'))
|
406
|
+
# # SELECT "users".* FROM "users" ORDER BY end_date - start_date
|
407
|
+
#
|
408
|
+
# Custom query syntax, like JSON columns for Postgres, is supported in this way.
|
409
|
+
#
|
410
|
+
# User.order(Arel.sql("payload->>'kind'"))
|
411
|
+
# # SELECT "users".* FROM "users" ORDER BY payload->>'kind'
|
351
412
|
def order(*args)
|
352
|
-
check_if_method_has_arguments!(
|
413
|
+
check_if_method_has_arguments!(__callee__, args) do
|
353
414
|
sanitize_order_arguments(args)
|
354
415
|
end
|
355
416
|
spawn.order!(*args)
|
@@ -362,6 +423,29 @@ module ActiveRecord
|
|
362
423
|
self
|
363
424
|
end
|
364
425
|
|
426
|
+
# Allows to specify an order by a specific set of values. Depending on your
|
427
|
+
# adapter this will either use a CASE statement or a built-in function.
|
428
|
+
#
|
429
|
+
# User.in_order_of(:id, [1, 5, 3])
|
430
|
+
# # SELECT "users".* FROM "users"
|
431
|
+
# # ORDER BY FIELD("users"."id", 1, 5, 3)
|
432
|
+
# # WHERE "users"."id" IN (1, 5, 3)
|
433
|
+
#
|
434
|
+
def in_order_of(column, values)
|
435
|
+
klass.disallow_raw_sql!([column], permit: connection.column_name_with_order_matcher)
|
436
|
+
return spawn.none! if values.empty?
|
437
|
+
|
438
|
+
references = column_references([column])
|
439
|
+
self.references_values |= references unless references.empty?
|
440
|
+
|
441
|
+
values = values.map { |value| type_caster.type_cast_for_database(column, value) }
|
442
|
+
arel_column = column.is_a?(Symbol) ? order_column(column.to_s) : column
|
443
|
+
|
444
|
+
spawn
|
445
|
+
.order!(connection.field_ordered_value(arel_column, values))
|
446
|
+
.where!(arel_column.in(values))
|
447
|
+
end
|
448
|
+
|
365
449
|
# Replaces any existing order defined on the relation with the specified order.
|
366
450
|
#
|
367
451
|
# User.order('email DESC').reorder('id ASC') # generated SQL has 'ORDER BY id ASC'
|
@@ -372,15 +456,15 @@ module ActiveRecord
|
|
372
456
|
#
|
373
457
|
# generates a query with 'ORDER BY id ASC, name ASC'.
|
374
458
|
def reorder(*args)
|
375
|
-
check_if_method_has_arguments!(
|
376
|
-
sanitize_order_arguments(args)
|
459
|
+
check_if_method_has_arguments!(__callee__, args) do
|
460
|
+
sanitize_order_arguments(args)
|
377
461
|
end
|
378
462
|
spawn.reorder!(*args)
|
379
463
|
end
|
380
464
|
|
381
465
|
# Same as #reorder but operates on relation in-place instead of copying.
|
382
466
|
def reorder!(*args) # :nodoc:
|
383
|
-
preprocess_order_args(args)
|
467
|
+
preprocess_order_args(args)
|
384
468
|
args.uniq!
|
385
469
|
self.reordering_value = true
|
386
470
|
self.order_values = args
|
@@ -425,7 +509,7 @@ module ActiveRecord
|
|
425
509
|
# has_many :comments, -> { unscope(where: :trashed) }
|
426
510
|
#
|
427
511
|
def unscope(*args)
|
428
|
-
check_if_method_has_arguments!(
|
512
|
+
check_if_method_has_arguments!(__callee__, args)
|
429
513
|
spawn.unscope!(*args)
|
430
514
|
end
|
431
515
|
|
@@ -458,7 +542,7 @@ module ActiveRecord
|
|
458
542
|
self
|
459
543
|
end
|
460
544
|
|
461
|
-
# Performs
|
545
|
+
# Performs JOINs on +args+. The given symbol(s) should match the name of
|
462
546
|
# the association(s).
|
463
547
|
#
|
464
548
|
# User.joins(:posts)
|
@@ -487,7 +571,7 @@ module ActiveRecord
|
|
487
571
|
# User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id")
|
488
572
|
# # SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
|
489
573
|
def joins(*args)
|
490
|
-
check_if_method_has_arguments!(
|
574
|
+
check_if_method_has_arguments!(__callee__, args)
|
491
575
|
spawn.joins!(*args)
|
492
576
|
end
|
493
577
|
|
@@ -496,7 +580,7 @@ module ActiveRecord
|
|
496
580
|
self
|
497
581
|
end
|
498
582
|
|
499
|
-
# Performs
|
583
|
+
# Performs LEFT OUTER JOINs on +args+:
|
500
584
|
#
|
501
585
|
# User.left_outer_joins(:posts)
|
502
586
|
# => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
@@ -578,13 +662,13 @@ module ActiveRecord
|
|
578
662
|
#
|
579
663
|
# Fields can be symbols or strings. Values can be single values, arrays, or ranges.
|
580
664
|
#
|
581
|
-
# User.where(
|
665
|
+
# User.where(name: "Joe", email: "joe@example.com")
|
582
666
|
# # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com'
|
583
667
|
#
|
584
|
-
# User.where(
|
668
|
+
# User.where(name: ["Alice", "Bob"])
|
585
669
|
# # SELECT * FROM users WHERE name IN ('Alice', 'Bob')
|
586
670
|
#
|
587
|
-
# User.where(
|
671
|
+
# User.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight)
|
588
672
|
# # SELECT * FROM users WHERE (created_at BETWEEN '2012-06-09 07:00:00.000000' AND '2012-06-10 07:00:00.000000')
|
589
673
|
#
|
590
674
|
# In the case of a belongs_to relationship, an association key can be used
|
@@ -614,8 +698,8 @@ module ActiveRecord
|
|
614
698
|
#
|
615
699
|
# For hash conditions, you can either use the table name in the key, or use a sub-hash.
|
616
700
|
#
|
617
|
-
# User.joins(:posts).where(
|
618
|
-
# User.joins(:posts).where(
|
701
|
+
# User.joins(:posts).where("posts.published" => true)
|
702
|
+
# User.joins(:posts).where(posts: { published: true })
|
619
703
|
#
|
620
704
|
# === no argument
|
621
705
|
#
|
@@ -668,6 +752,59 @@ module ActiveRecord
|
|
668
752
|
scope
|
669
753
|
end
|
670
754
|
|
755
|
+
# Allows you to invert an entire where clause instead of manually applying conditions.
|
756
|
+
#
|
757
|
+
# class User
|
758
|
+
# scope :active, -> { where(accepted: true, locked: false) }
|
759
|
+
# end
|
760
|
+
#
|
761
|
+
# User.where(accepted: true)
|
762
|
+
# # WHERE `accepted` = 1
|
763
|
+
#
|
764
|
+
# User.where(accepted: true).invert_where
|
765
|
+
# # WHERE `accepted` != 1
|
766
|
+
#
|
767
|
+
# User.active
|
768
|
+
# # WHERE `accepted` = 1 AND `locked` = 0
|
769
|
+
#
|
770
|
+
# User.active.invert_where
|
771
|
+
# # WHERE NOT (`accepted` = 1 AND `locked` = 0)
|
772
|
+
#
|
773
|
+
# Be careful because this inverts all conditions before +invert_where+ call.
|
774
|
+
#
|
775
|
+
# class User
|
776
|
+
# scope :active, -> { where(accepted: true, locked: false) }
|
777
|
+
# scope :inactive, -> { active.invert_where } # Do not attempt it
|
778
|
+
# end
|
779
|
+
#
|
780
|
+
# # It also inverts `where(role: 'admin')` unexpectedly.
|
781
|
+
# User.where(role: 'admin').inactive
|
782
|
+
# # WHERE NOT (`role` = 'admin' AND `accepted` = 1 AND `locked` = 0)
|
783
|
+
#
|
784
|
+
def invert_where
|
785
|
+
spawn.invert_where!
|
786
|
+
end
|
787
|
+
|
788
|
+
def invert_where! # :nodoc:
|
789
|
+
self.where_clause = where_clause.invert
|
790
|
+
self
|
791
|
+
end
|
792
|
+
|
793
|
+
# Checks whether the given relation is structurally compatible with this relation, to determine
|
794
|
+
# if it's possible to use the #and and #or methods without raising an error. Structurally
|
795
|
+
# compatible is defined as: they must be scoping the same model, and they must differ only by
|
796
|
+
# #where (if no #group has been defined) or #having (if a #group is present).
|
797
|
+
#
|
798
|
+
# Post.where("id = 1").structurally_compatible?(Post.where("author_id = 3"))
|
799
|
+
# # => true
|
800
|
+
#
|
801
|
+
# Post.joins(:comments).structurally_compatible?(Post.where("id = 1"))
|
802
|
+
# # => false
|
803
|
+
#
|
804
|
+
def structurally_compatible?(other)
|
805
|
+
structurally_incompatible_values_for(other).empty?
|
806
|
+
end
|
807
|
+
|
671
808
|
# Returns a new relation, which is the logical intersection of this relation and the one passed
|
672
809
|
# as an argument.
|
673
810
|
#
|
@@ -886,7 +1023,7 @@ module ActiveRecord
|
|
886
1023
|
self
|
887
1024
|
end
|
888
1025
|
|
889
|
-
# Specifies table from which the records will be fetched. For example:
|
1026
|
+
# Specifies the table from which the records will be fetched. For example:
|
890
1027
|
#
|
891
1028
|
# Topic.select('title').from('posts')
|
892
1029
|
# # SELECT title FROM posts
|
@@ -896,9 +1033,26 @@ module ActiveRecord
|
|
896
1033
|
# Topic.select('title').from(Topic.approved)
|
897
1034
|
# # SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
|
898
1035
|
#
|
1036
|
+
# Passing a second argument (string or symbol), creates the alias for the SQL from clause. Otherwise the alias "subquery" is used:
|
1037
|
+
#
|
899
1038
|
# Topic.select('a.title').from(Topic.approved, :a)
|
900
1039
|
# # SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
|
901
1040
|
#
|
1041
|
+
# It does not add multiple arguments to the SQL from clause. The last +from+ chained is the one used:
|
1042
|
+
#
|
1043
|
+
# Topic.select('title').from(Topic.approved).from(Topic.inactive)
|
1044
|
+
# # SELECT title FROM (SELECT topics.* FROM topics WHERE topics.active = 'f') subquery
|
1045
|
+
#
|
1046
|
+
# For multiple arguments for the SQL from clause, you can pass a string with the exact elements in the SQL from list:
|
1047
|
+
#
|
1048
|
+
# color = "red"
|
1049
|
+
# Color
|
1050
|
+
# .from("colors c, JSONB_ARRAY_ELEMENTS(colored_things) AS colorvalues(colorvalue)")
|
1051
|
+
# .where("colorvalue->>'color' = ?", color)
|
1052
|
+
# .select("c.*").to_a
|
1053
|
+
# # SELECT c.*
|
1054
|
+
# # FROM colors c, JSONB_ARRAY_ELEMENTS(colored_things) AS colorvalues(colorvalue)
|
1055
|
+
# # WHERE (colorvalue->>'color' = 'red')
|
902
1056
|
def from(value, subquery_name = nil)
|
903
1057
|
spawn.from!(value, subquery_name)
|
904
1058
|
end
|
@@ -994,7 +1148,7 @@ module ActiveRecord
|
|
994
1148
|
# Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
|
995
1149
|
# # SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
|
996
1150
|
def optimizer_hints(*args)
|
997
|
-
check_if_method_has_arguments!(
|
1151
|
+
check_if_method_has_arguments!(__callee__, args)
|
998
1152
|
spawn.optimizer_hints!(*args)
|
999
1153
|
end
|
1000
1154
|
|
@@ -1036,7 +1190,7 @@ module ActiveRecord
|
|
1036
1190
|
#
|
1037
1191
|
# The SQL block comment delimiters, "/*" and "*/", will be added automatically.
|
1038
1192
|
def annotate(*args)
|
1039
|
-
check_if_method_has_arguments!(
|
1193
|
+
check_if_method_has_arguments!(__callee__, args)
|
1040
1194
|
spawn.annotate!(*args)
|
1041
1195
|
end
|
1042
1196
|
|
@@ -1054,6 +1208,47 @@ module ActiveRecord
|
|
1054
1208
|
self
|
1055
1209
|
end
|
1056
1210
|
|
1211
|
+
# Excludes the specified record (or collection of records) from the resulting
|
1212
|
+
# relation. For example:
|
1213
|
+
#
|
1214
|
+
# Post.excluding(post)
|
1215
|
+
# # SELECT "posts".* FROM "posts" WHERE "posts"."id" != 1
|
1216
|
+
#
|
1217
|
+
# Post.excluding(post_one, post_two)
|
1218
|
+
# # SELECT "posts".* FROM "posts" WHERE "posts"."id" NOT IN (1, 2)
|
1219
|
+
#
|
1220
|
+
# This can also be called on associations. As with the above example, either
|
1221
|
+
# a single record of collection thereof may be specified:
|
1222
|
+
#
|
1223
|
+
# post = Post.find(1)
|
1224
|
+
# comment = Comment.find(2)
|
1225
|
+
# post.comments.excluding(comment)
|
1226
|
+
# # SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 AND "comments"."id" != 2
|
1227
|
+
#
|
1228
|
+
# This is short-hand for <tt>.where.not(id: post.id)</tt> and <tt>.where.not(id: [post_one.id, post_two.id])</tt>.
|
1229
|
+
#
|
1230
|
+
# An <tt>ArgumentError</tt> will be raised if either no records are
|
1231
|
+
# specified, or if any of the records in the collection (if a collection
|
1232
|
+
# is passed in) are not instances of the same model that the relation is
|
1233
|
+
# scoping.
|
1234
|
+
def excluding(*records)
|
1235
|
+
records.flatten!(1)
|
1236
|
+
records.compact!
|
1237
|
+
|
1238
|
+
unless records.all?(klass)
|
1239
|
+
raise ArgumentError, "You must only pass a single or collection of #{klass.name} objects to ##{__callee__}."
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
spawn.excluding!(records)
|
1243
|
+
end
|
1244
|
+
alias :without :excluding
|
1245
|
+
|
1246
|
+
def excluding!(records) # :nodoc:
|
1247
|
+
predicates = [ predicate_builder[primary_key, records].invert ]
|
1248
|
+
self.where_clause += Relation::WhereClause.new(predicates)
|
1249
|
+
self
|
1250
|
+
end
|
1251
|
+
|
1057
1252
|
# Returns the Arel object associated with the relation.
|
1058
1253
|
def arel(aliases = nil) # :nodoc:
|
1059
1254
|
@arel ||= build_arel(aliases)
|
@@ -1109,11 +1304,9 @@ module ActiveRecord
|
|
1109
1304
|
nil
|
1110
1305
|
end
|
1111
1306
|
|
1112
|
-
def each_join_dependencies(join_dependencies = build_join_dependencies)
|
1307
|
+
def each_join_dependencies(join_dependencies = build_join_dependencies, &block)
|
1113
1308
|
join_dependencies.each do |join_dependency|
|
1114
|
-
join_dependency.each
|
1115
|
-
yield join
|
1116
|
-
end
|
1309
|
+
join_dependency.each(&block)
|
1117
1310
|
end
|
1118
1311
|
end
|
1119
1312
|
|
@@ -1155,14 +1348,6 @@ module ActiveRecord
|
|
1155
1348
|
unless annotate_values.empty?
|
1156
1349
|
annotates = annotate_values
|
1157
1350
|
annotates = annotates.uniq if annotates.size > 1
|
1158
|
-
unless annotates == annotate_values
|
1159
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
1160
|
-
Duplicated query annotations are no longer shown in queries in Rails 6.2.
|
1161
|
-
To migrate to Rails 6.2's behavior, use `uniq!(:annotate)` to deduplicate query annotations
|
1162
|
-
(`#{klass.name&.tableize || klass.table_name}.uniq!(:annotate)`).
|
1163
|
-
MSG
|
1164
|
-
annotates = annotate_values
|
1165
|
-
end
|
1166
1351
|
arel.comment(*annotates)
|
1167
1352
|
end
|
1168
1353
|
|
@@ -1170,8 +1355,7 @@ module ActiveRecord
|
|
1170
1355
|
end
|
1171
1356
|
|
1172
1357
|
def build_cast_value(name, value)
|
1173
|
-
|
1174
|
-
Arel::Nodes::BindParam.new(cast_value)
|
1358
|
+
ActiveModel::Attribute.with_cast_value(name, value, Type.default_value)
|
1175
1359
|
end
|
1176
1360
|
|
1177
1361
|
def build_from
|
@@ -1282,7 +1466,7 @@ module ActiveRecord
|
|
1282
1466
|
def build_select(arel)
|
1283
1467
|
if select_values.any?
|
1284
1468
|
arel.project(*arel_columns(select_values))
|
1285
|
-
elsif klass.ignored_columns.any?
|
1469
|
+
elsif klass.ignored_columns.any? || klass.enumerate_columns_in_select_statements
|
1286
1470
|
arel.project(*klass.column_names.map { |field| table[field] })
|
1287
1471
|
else
|
1288
1472
|
arel.project(table[Arel.star])
|
@@ -1423,12 +1607,17 @@ module ActiveRecord
|
|
1423
1607
|
order_args.map! do |arg|
|
1424
1608
|
klass.sanitize_sql_for_order(arg)
|
1425
1609
|
end
|
1426
|
-
order_args.flatten!
|
1427
|
-
order_args.compact_blank!
|
1428
1610
|
end
|
1429
1611
|
|
1430
1612
|
def column_references(order_args)
|
1431
|
-
references = order_args.
|
1613
|
+
references = order_args.flat_map do |arg|
|
1614
|
+
case arg
|
1615
|
+
when String, Symbol
|
1616
|
+
arg
|
1617
|
+
when Hash
|
1618
|
+
arg.keys
|
1619
|
+
end
|
1620
|
+
end
|
1432
1621
|
references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
|
1433
1622
|
references
|
1434
1623
|
end
|
@@ -1476,19 +1665,19 @@ module ActiveRecord
|
|
1476
1665
|
# Post.references() # raises an error
|
1477
1666
|
# Post.references([]) # does not raise an error
|
1478
1667
|
#
|
1479
|
-
# This particular method should be called with a method_name and the args
|
1668
|
+
# This particular method should be called with a method_name (__callee__) and the args
|
1480
1669
|
# passed into that method as an input. For example:
|
1481
1670
|
#
|
1482
1671
|
# def references(*args)
|
1483
|
-
# check_if_method_has_arguments!(
|
1672
|
+
# check_if_method_has_arguments!(__callee__, args)
|
1484
1673
|
# ...
|
1485
1674
|
# end
|
1486
1675
|
def check_if_method_has_arguments!(method_name, args, message = nil)
|
1487
1676
|
if args.blank?
|
1488
1677
|
raise ArgumentError, message || "The method .#{method_name}() must contain arguments."
|
1489
|
-
elsif block_given?
|
1490
|
-
yield args
|
1491
1678
|
else
|
1679
|
+
yield args if block_given?
|
1680
|
+
|
1492
1681
|
args.flatten!
|
1493
1682
|
args.compact_blank!
|
1494
1683
|
end
|
@@ -1512,11 +1701,4 @@ module ActiveRecord
|
|
1512
1701
|
end
|
1513
1702
|
end
|
1514
1703
|
end
|
1515
|
-
|
1516
|
-
class Relation # :nodoc:
|
1517
|
-
# No-op WhereClauseFactory to work Mashal.load(File.read("legacy_relation.dump")).
|
1518
|
-
# TODO: Remove the class once Rails 6.1 has released.
|
1519
|
-
class WhereClauseFactory # :nodoc:
|
1520
|
-
end
|
1521
|
-
end
|
1522
1704
|
end
|
@@ -17,8 +17,8 @@ module ActiveRecord
|
|
17
17
|
QueryRegistry.reset
|
18
18
|
|
19
19
|
super.tap do |records|
|
20
|
-
if logger && warn_on_records_fetched_greater_than
|
21
|
-
if records.length > warn_on_records_fetched_greater_than
|
20
|
+
if logger && ActiveRecord.warn_on_records_fetched_greater_than
|
21
|
+
if records.length > ActiveRecord.warn_on_records_fetched_greater_than
|
22
22
|
logger.warn "Query fetched #{records.size} #{@klass} records: #{QueryRegistry.queries.join(";")}"
|
23
23
|
end
|
24
24
|
end
|
@@ -31,17 +31,15 @@ module ActiveRecord
|
|
31
31
|
end
|
32
32
|
# :startdoc:
|
33
33
|
|
34
|
-
|
35
|
-
extend
|
34
|
+
module QueryRegistry # :nodoc:
|
35
|
+
extend self
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
def initialize
|
40
|
-
@queries = []
|
37
|
+
def queries
|
38
|
+
ActiveSupport::IsolatedExecutionState[:active_record_query_registry] ||= []
|
41
39
|
end
|
42
40
|
|
43
41
|
def reset
|
44
|
-
|
42
|
+
queries.clear
|
45
43
|
end
|
46
44
|
end
|
47
45
|
end
|
@@ -7,8 +7,8 @@ require "active_record/relation/merger"
|
|
7
7
|
module ActiveRecord
|
8
8
|
module SpawnMethods
|
9
9
|
# This is overridden by Associations::CollectionProxy
|
10
|
-
def spawn
|
11
|
-
already_in_scope? ? klass.all : clone
|
10
|
+
def spawn # :nodoc:
|
11
|
+
already_in_scope?(klass.scope_registry) ? klass.all : clone
|
12
12
|
end
|
13
13
|
|
14
14
|
# Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
|
@@ -76,6 +76,11 @@ module ActiveRecord
|
|
76
76
|
other.is_a?(WhereClause) &&
|
77
77
|
predicates == other.predicates
|
78
78
|
end
|
79
|
+
alias :eql? :==
|
80
|
+
|
81
|
+
def hash
|
82
|
+
[self.class, predicates].hash
|
83
|
+
end
|
79
84
|
|
80
85
|
def invert
|
81
86
|
if predicates.size == 1
|
@@ -158,21 +163,8 @@ module ActiveRecord
|
|
158
163
|
attr = extract_attribute(node) || begin
|
159
164
|
node.left if equality_node?(node) && node.left.is_a?(Arel::Predications)
|
160
165
|
end
|
161
|
-
|
162
|
-
|
163
|
-
ref = referenced_columns[attr]
|
164
|
-
next false unless ref
|
165
|
-
|
166
|
-
if equality_node?(node) && equality_node?(ref) || node == ref
|
167
|
-
true
|
168
|
-
else
|
169
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
170
|
-
Merging (#{node.to_sql}) and (#{ref.to_sql}) no longer maintain
|
171
|
-
both conditions, and will be replaced by the latter in Rails 6.2.
|
172
|
-
To migrate to Rails 6.2's behavior, use `relation.merge(other, rewhere: true)`.
|
173
|
-
MSG
|
174
|
-
false
|
175
|
-
end
|
166
|
+
|
167
|
+
attr && referenced_columns[attr]
|
176
168
|
end
|
177
169
|
end
|
178
170
|
|
@@ -227,11 +219,10 @@ module ActiveRecord
|
|
227
219
|
end
|
228
220
|
|
229
221
|
def extract_node_value(node)
|
230
|
-
|
231
|
-
when Array
|
232
|
-
node.map { |v| extract_node_value(v) }
|
233
|
-
when Arel::Nodes::BindParam, Arel::Nodes::Casted, Arel::Nodes::Quoted
|
222
|
+
if node.respond_to?(:value_before_type_cast)
|
234
223
|
node.value_before_type_cast
|
224
|
+
elsif Array === node
|
225
|
+
node.map { |v| extract_node_value(v) }
|
235
226
|
end
|
236
227
|
end
|
237
228
|
end
|