activerecord 7.2.2.1 → 8.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +564 -753
- data/README.rdoc +2 -2
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/alias_tracker.rb +6 -4
- data/lib/active_record/associations/association.rb +35 -11
- data/lib/active_record/associations/belongs_to_association.rb +18 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +17 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +10 -8
- data/lib/active_record/associations/collection_proxy.rb +22 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +192 -24
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_methods/primary_key.rb +4 -8
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +17 -4
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
- data/lib/active_record/attribute_methods.rb +24 -19
- data/lib/active_record/attributes.rb +40 -26
- data/lib/active_record/autosave_association.rb +91 -39
- data/lib/active_record/base.rb +3 -4
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +35 -28
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +458 -117
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +136 -74
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +44 -11
- data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -25
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +122 -29
- data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
- data/lib/active_record/connection_adapters/abstract_adapter.rb +175 -87
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +77 -58
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -9
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +73 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +10 -11
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +28 -45
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +140 -64
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +83 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +3 -5
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +13 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +112 -42
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +38 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +2 -19
- data/lib/active_record/connection_adapters.rb +1 -56
- data/lib/active_record/connection_handling.rb +37 -10
- data/lib/active_record/core.rb +61 -25
- data/lib/active_record/counter_cache.rb +34 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
- data/lib/active_record/database_configurations/database_config.rb +9 -1
- data/lib/active_record/database_configurations/hash_config.rb +67 -9
- data/lib/active_record/database_configurations/url_config.rb +13 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +19 -19
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +9 -9
- data/lib/active_record/encryption/encrypted_attribute_type.rb +12 -3
- data/lib/active_record/encryption/encryptor.rb +49 -28
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +46 -42
- data/lib/active_record/errors.rb +36 -12
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +2 -4
- data/lib/active_record/future_result.rb +13 -9
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +12 -7
- data/lib/active_record/locking/optimistic.rb +8 -1
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -13
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +44 -11
- data/lib/active_record/migration/compatibility.rb +37 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +50 -43
- data/lib/active_record/model_schema.rb +38 -13
- data/lib/active_record/nested_attributes.rb +6 -6
- data/lib/active_record/persistence.rb +162 -133
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +104 -52
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +12 -12
- data/lib/active_record/railtie.rb +37 -32
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +26 -37
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +53 -21
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +147 -73
- data/lib/active_record/relation/calculations.rb +80 -63
- data/lib/active_record/relation/delegation.rb +25 -15
- data/lib/active_record/relation/finder_methods.rb +54 -37
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -9
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +22 -7
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +156 -95
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation/where_clause.rb +10 -11
- data/lib/active_record/relation.rb +122 -80
- data/lib/active_record/result.rb +109 -24
- data/lib/active_record/runtime_registry.rb +42 -58
- data/lib/active_record/sanitization.rb +9 -6
- data/lib/active_record/schema_dumper.rb +47 -22
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +47 -18
- data/lib/active_record/statement_cache.rb +24 -20
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +6 -23
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +85 -85
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +39 -2
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +39 -16
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +11 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +85 -50
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/select_manager.rb +13 -4
- data/lib/arel/table.rb +3 -7
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +6 -22
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +17 -17
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
|
@@ -92,10 +92,11 @@ module ActiveRecord
|
|
|
92
92
|
@scope.joins!(association)
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
+
association_conditions = Array(reflection.association_primary_key).index_with(nil)
|
|
95
96
|
if reflection.options[:class_name]
|
|
96
|
-
self.not(association =>
|
|
97
|
+
self.not(association => association_conditions)
|
|
97
98
|
else
|
|
98
|
-
self.not(reflection.table_name =>
|
|
99
|
+
self.not(reflection.table_name => association_conditions)
|
|
99
100
|
end
|
|
100
101
|
end
|
|
101
102
|
|
|
@@ -124,10 +125,11 @@ module ActiveRecord
|
|
|
124
125
|
associations.each do |association|
|
|
125
126
|
reflection = scope_association_reflection(association)
|
|
126
127
|
@scope.left_outer_joins!(association)
|
|
128
|
+
association_conditions = Array(reflection.association_primary_key).index_with(nil)
|
|
127
129
|
if reflection.options[:class_name]
|
|
128
|
-
@scope.where!(association =>
|
|
130
|
+
@scope.where!(association => association_conditions)
|
|
129
131
|
else
|
|
130
|
-
@scope.where!(reflection.table_name =>
|
|
132
|
+
@scope.where!(reflection.table_name => association_conditions)
|
|
131
133
|
end
|
|
132
134
|
end
|
|
133
135
|
|
|
@@ -136,9 +138,10 @@ module ActiveRecord
|
|
|
136
138
|
|
|
137
139
|
private
|
|
138
140
|
def scope_association_reflection(association)
|
|
139
|
-
|
|
141
|
+
model = @scope.model
|
|
142
|
+
reflection = model._reflect_on_association(association)
|
|
140
143
|
unless reflection
|
|
141
|
-
raise ArgumentError.new("An association named `:#{association}` does not exist on the model `#{
|
|
144
|
+
raise ArgumentError.new("An association named `:#{association}` does not exist on the model `#{model.name}`.")
|
|
142
145
|
end
|
|
143
146
|
reflection
|
|
144
147
|
end
|
|
@@ -254,6 +257,10 @@ module ActiveRecord
|
|
|
254
257
|
self
|
|
255
258
|
end
|
|
256
259
|
|
|
260
|
+
def all # :nodoc:
|
|
261
|
+
spawn
|
|
262
|
+
end
|
|
263
|
+
|
|
257
264
|
# Specify associations +args+ to be eager loaded using a <tt>LEFT OUTER JOIN</tt>.
|
|
258
265
|
# Performs a single query joining all specified associations. For example:
|
|
259
266
|
#
|
|
@@ -491,7 +498,8 @@ module ActiveRecord
|
|
|
491
498
|
|
|
492
499
|
# Like #with, but modifies relation in place.
|
|
493
500
|
def with!(*args) # :nodoc:
|
|
494
|
-
|
|
501
|
+
args = process_with_args(args)
|
|
502
|
+
self.with_values |= args
|
|
495
503
|
self
|
|
496
504
|
end
|
|
497
505
|
|
|
@@ -499,10 +507,10 @@ module ActiveRecord
|
|
|
499
507
|
#
|
|
500
508
|
# Post.with_recursive(post_and_replies: [Post.where(id: 42), Post.joins('JOIN post_and_replies ON posts.in_reply_to_id = post_and_replies.id')])
|
|
501
509
|
# # => ActiveRecord::Relation
|
|
502
|
-
# # WITH post_and_replies AS (
|
|
510
|
+
# # WITH RECURSIVE post_and_replies AS (
|
|
503
511
|
# # (SELECT * FROM posts WHERE id = 42)
|
|
504
512
|
# # UNION ALL
|
|
505
|
-
# # (SELECT * FROM posts JOIN
|
|
513
|
+
# # (SELECT * FROM posts JOIN post_and_replies ON posts.in_reply_to_id = post_and_replies.id)
|
|
506
514
|
# # )
|
|
507
515
|
# # SELECT * FROM posts
|
|
508
516
|
#
|
|
@@ -514,7 +522,8 @@ module ActiveRecord
|
|
|
514
522
|
|
|
515
523
|
# Like #with_recursive but modifies the relation in place.
|
|
516
524
|
def with_recursive!(*args) # :nodoc:
|
|
517
|
-
|
|
525
|
+
args = process_with_args(args)
|
|
526
|
+
self.with_values |= args
|
|
518
527
|
@with_is_recursive = true
|
|
519
528
|
self
|
|
520
529
|
end
|
|
@@ -567,7 +576,7 @@ module ActiveRecord
|
|
|
567
576
|
end
|
|
568
577
|
|
|
569
578
|
def group!(*args) # :nodoc:
|
|
570
|
-
self.group_values
|
|
579
|
+
self.group_values |= args
|
|
571
580
|
self
|
|
572
581
|
end
|
|
573
582
|
|
|
@@ -696,26 +705,39 @@ module ActiveRecord
|
|
|
696
705
|
# # WHEN "conversations"."status" = 0 THEN 3
|
|
697
706
|
# # END ASC
|
|
698
707
|
#
|
|
699
|
-
|
|
700
|
-
|
|
708
|
+
# +filter+ can be set to +false+ to include all results instead of only the ones specified in +values+.
|
|
709
|
+
#
|
|
710
|
+
# Conversation.in_order_of(:status, [:archived, :active], filter: false)
|
|
711
|
+
# # SELECT "conversations".* FROM "conversations"
|
|
712
|
+
# # ORDER BY CASE
|
|
713
|
+
# # WHEN "conversations"."status" = 1 THEN 1
|
|
714
|
+
# # WHEN "conversations"."status" = 0 THEN 2
|
|
715
|
+
# # ELSE 3
|
|
716
|
+
# # END ASC
|
|
717
|
+
def in_order_of(column, values, filter: true)
|
|
718
|
+
model.disallow_raw_sql!([column], permit: model.adapter_class.column_name_with_order_matcher)
|
|
701
719
|
return spawn.none! if values.empty?
|
|
702
720
|
|
|
703
721
|
references = column_references([column])
|
|
704
722
|
self.references_values |= references unless references.empty?
|
|
705
723
|
|
|
706
|
-
values = values.map { |value| type_caster.type_cast_for_database(column, value) }
|
|
724
|
+
values = values.map { |value| model.type_caster.type_cast_for_database(column, value) }
|
|
707
725
|
arel_column = column.is_a?(Arel::Nodes::SqlLiteral) ? column : order_column(column.to_s)
|
|
708
726
|
|
|
709
|
-
|
|
710
|
-
if values.include?(nil)
|
|
711
|
-
arel_column.in(values.compact).or(arel_column.eq(nil))
|
|
712
|
-
else
|
|
713
|
-
arel_column.in(values)
|
|
714
|
-
end
|
|
727
|
+
scope = spawn.order!(build_case_for_value_position(arel_column, values, filter: filter))
|
|
715
728
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
729
|
+
if filter
|
|
730
|
+
where_clause =
|
|
731
|
+
if values.include?(nil)
|
|
732
|
+
arel_column.in(values.compact).or(arel_column.eq(nil))
|
|
733
|
+
else
|
|
734
|
+
arel_column.in(values)
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
scope = scope.where!(where_clause)
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
scope
|
|
719
741
|
end
|
|
720
742
|
|
|
721
743
|
# Replaces any existing order defined on the relation with the specified order.
|
|
@@ -787,7 +809,7 @@ module ActiveRecord
|
|
|
787
809
|
end
|
|
788
810
|
|
|
789
811
|
def unscope!(*args) # :nodoc:
|
|
790
|
-
self.unscope_values
|
|
812
|
+
self.unscope_values |= args
|
|
791
813
|
|
|
792
814
|
args.each do |scope|
|
|
793
815
|
case scope
|
|
@@ -1191,6 +1213,7 @@ module ActiveRecord
|
|
|
1191
1213
|
end
|
|
1192
1214
|
|
|
1193
1215
|
def limit!(value) # :nodoc:
|
|
1216
|
+
value = Integer(value) unless value.nil?
|
|
1194
1217
|
self.limit_value = value
|
|
1195
1218
|
self
|
|
1196
1219
|
end
|
|
@@ -1277,13 +1300,13 @@ module ActiveRecord
|
|
|
1277
1300
|
#
|
|
1278
1301
|
# users = User.readonly
|
|
1279
1302
|
# users.first.save
|
|
1280
|
-
# => ActiveRecord::ReadOnlyRecord: User is marked as readonly
|
|
1303
|
+
# # => ActiveRecord::ReadOnlyRecord: User is marked as readonly
|
|
1281
1304
|
#
|
|
1282
1305
|
# To make a readonly relation writable, pass +false+.
|
|
1283
1306
|
#
|
|
1284
1307
|
# users.readonly(false)
|
|
1285
1308
|
# users.first.save
|
|
1286
|
-
# => true
|
|
1309
|
+
# # => true
|
|
1287
1310
|
def readonly(value = true)
|
|
1288
1311
|
spawn.readonly!(value)
|
|
1289
1312
|
end
|
|
@@ -1298,7 +1321,7 @@ module ActiveRecord
|
|
|
1298
1321
|
#
|
|
1299
1322
|
# user = User.strict_loading.first
|
|
1300
1323
|
# user.comments.to_a
|
|
1301
|
-
# => ActiveRecord::StrictLoadingViolationError
|
|
1324
|
+
# # => ActiveRecord::StrictLoadingViolationError
|
|
1302
1325
|
def strict_loading(value = true)
|
|
1303
1326
|
spawn.strict_loading!(value)
|
|
1304
1327
|
end
|
|
@@ -1443,7 +1466,7 @@ module ActiveRecord
|
|
|
1443
1466
|
modules << Module.new(&block) if block
|
|
1444
1467
|
modules.flatten!
|
|
1445
1468
|
|
|
1446
|
-
self.extending_values
|
|
1469
|
+
self.extending_values |= modules
|
|
1447
1470
|
extend(*extending_values) if extending_values.any?
|
|
1448
1471
|
|
|
1449
1472
|
self
|
|
@@ -1511,7 +1534,7 @@ module ActiveRecord
|
|
|
1511
1534
|
|
|
1512
1535
|
# Like #annotate, but modifies relation in place.
|
|
1513
1536
|
def annotate!(*args) # :nodoc:
|
|
1514
|
-
self.annotate_values
|
|
1537
|
+
self.annotate_values |= args
|
|
1515
1538
|
self
|
|
1516
1539
|
end
|
|
1517
1540
|
|
|
@@ -1554,8 +1577,8 @@ module ActiveRecord
|
|
|
1554
1577
|
records.flatten!(1)
|
|
1555
1578
|
records.compact!
|
|
1556
1579
|
|
|
1557
|
-
unless records.all?(
|
|
1558
|
-
raise ArgumentError, "You must only pass a single or collection of #{
|
|
1580
|
+
unless records.all?(model) && relations.all? { |relation| relation.model == model }
|
|
1581
|
+
raise ArgumentError, "You must only pass a single or collection of #{model.name} objects to ##{__callee__}."
|
|
1559
1582
|
end
|
|
1560
1583
|
|
|
1561
1584
|
spawn.excluding!(records + relations.flat_map(&:ids))
|
|
@@ -1570,12 +1593,12 @@ module ActiveRecord
|
|
|
1570
1593
|
|
|
1571
1594
|
# Returns the Arel object associated with the relation.
|
|
1572
1595
|
def arel(aliases = nil) # :nodoc:
|
|
1573
|
-
@arel ||=
|
|
1596
|
+
@arel ||= build_arel(aliases)
|
|
1574
1597
|
end
|
|
1575
1598
|
|
|
1576
1599
|
def construct_join_dependency(associations, join_type) # :nodoc:
|
|
1577
1600
|
ActiveRecord::Associations::JoinDependency.new(
|
|
1578
|
-
|
|
1601
|
+
model, table, associations, join_type
|
|
1579
1602
|
)
|
|
1580
1603
|
end
|
|
1581
1604
|
|
|
@@ -1604,15 +1627,15 @@ module ActiveRecord
|
|
|
1604
1627
|
elsif opts.include?("?")
|
|
1605
1628
|
parts = [build_bound_sql_literal(opts, rest)]
|
|
1606
1629
|
else
|
|
1607
|
-
parts = [
|
|
1630
|
+
parts = [Arel.sql(model.sanitize_sql([opts, *rest]))]
|
|
1608
1631
|
end
|
|
1609
1632
|
when Hash
|
|
1610
1633
|
opts = opts.transform_keys do |key|
|
|
1611
1634
|
if key.is_a?(Array)
|
|
1612
|
-
key.map { |k|
|
|
1635
|
+
key.map { |k| model.attribute_aliases[k.to_s] || k.to_s }
|
|
1613
1636
|
else
|
|
1614
1637
|
key = key.to_s
|
|
1615
|
-
|
|
1638
|
+
model.attribute_aliases[key] || key
|
|
1616
1639
|
end
|
|
1617
1640
|
end
|
|
1618
1641
|
references = PredicateBuilder.references(opts)
|
|
@@ -1631,21 +1654,16 @@ module ActiveRecord
|
|
|
1631
1654
|
end
|
|
1632
1655
|
alias :build_having_clause :build_where_clause
|
|
1633
1656
|
|
|
1634
|
-
def async!
|
|
1657
|
+
def async! # :nodoc:
|
|
1635
1658
|
@async = true
|
|
1636
1659
|
self
|
|
1637
1660
|
end
|
|
1638
1661
|
|
|
1639
|
-
|
|
1640
|
-
def arel_columns(columns)
|
|
1662
|
+
def arel_columns(columns) # :nodoc:
|
|
1641
1663
|
columns.flat_map do |field|
|
|
1642
1664
|
case field
|
|
1643
|
-
when Symbol
|
|
1644
|
-
arel_column(field
|
|
1645
|
-
adapter_class.quote_table_name(attr_name)
|
|
1646
|
-
end
|
|
1647
|
-
when String
|
|
1648
|
-
arel_column(field, &:itself)
|
|
1665
|
+
when Symbol, String
|
|
1666
|
+
arel_column(field)
|
|
1649
1667
|
when Proc
|
|
1650
1668
|
field.call
|
|
1651
1669
|
when Hash
|
|
@@ -1729,32 +1747,27 @@ module ActiveRecord
|
|
|
1729
1747
|
raise UnmodifiableRelation if @loaded || @arel
|
|
1730
1748
|
end
|
|
1731
1749
|
|
|
1732
|
-
def build_arel(
|
|
1750
|
+
def build_arel(aliases)
|
|
1733
1751
|
arel = Arel::SelectManager.new(table)
|
|
1734
1752
|
|
|
1735
1753
|
build_joins(arel.join_sources, aliases)
|
|
1736
1754
|
|
|
1737
1755
|
arel.where(where_clause.ast) unless where_clause.empty?
|
|
1738
1756
|
arel.having(having_clause.ast) unless having_clause.empty?
|
|
1739
|
-
arel.take(build_cast_value("LIMIT",
|
|
1757
|
+
arel.take(build_cast_value("LIMIT", limit_value)) if limit_value
|
|
1740
1758
|
arel.skip(build_cast_value("OFFSET", offset_value.to_i)) if offset_value
|
|
1741
|
-
arel.group(*arel_columns(group_values
|
|
1759
|
+
arel.group(*arel_columns(group_values)) unless group_values.empty?
|
|
1742
1760
|
|
|
1743
1761
|
build_order(arel)
|
|
1744
1762
|
build_with(arel)
|
|
1745
1763
|
build_select(arel)
|
|
1746
1764
|
|
|
1747
1765
|
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
|
1766
|
+
arel.comment(*annotate_values) unless annotate_values.empty?
|
|
1748
1767
|
arel.distinct(distinct_value)
|
|
1749
1768
|
arel.from(build_from) unless from_clause.empty?
|
|
1750
1769
|
arel.lock(lock_value) if lock_value
|
|
1751
1770
|
|
|
1752
|
-
unless annotate_values.empty?
|
|
1753
|
-
annotates = annotate_values
|
|
1754
|
-
annotates = annotates.uniq if annotates.size > 1
|
|
1755
|
-
arel.comment(*annotates)
|
|
1756
|
-
end
|
|
1757
|
-
|
|
1758
1771
|
arel
|
|
1759
1772
|
end
|
|
1760
1773
|
|
|
@@ -1828,7 +1841,7 @@ module ActiveRecord
|
|
|
1828
1841
|
|
|
1829
1842
|
joins = joins_values.dup
|
|
1830
1843
|
if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
|
|
1831
|
-
stashed_eager_load = joins.pop if joins.last.base_klass ==
|
|
1844
|
+
stashed_eager_load = joins.pop if joins.last.base_klass == model
|
|
1832
1845
|
end
|
|
1833
1846
|
|
|
1834
1847
|
joins.each_with_index do |join, i|
|
|
@@ -1885,8 +1898,8 @@ module ActiveRecord
|
|
|
1885
1898
|
def build_select(arel)
|
|
1886
1899
|
if select_values.any?
|
|
1887
1900
|
arel.project(*arel_columns(select_values))
|
|
1888
|
-
elsif
|
|
1889
|
-
arel.project(*
|
|
1901
|
+
elsif model.ignored_columns.any? || model.enumerate_columns_in_select_statements
|
|
1902
|
+
arel.project(*model.column_names.map { |field| table[field] })
|
|
1890
1903
|
else
|
|
1891
1904
|
arel.project(table[Arel.star])
|
|
1892
1905
|
end
|
|
@@ -1896,8 +1909,6 @@ module ActiveRecord
|
|
|
1896
1909
|
return if with_values.empty?
|
|
1897
1910
|
|
|
1898
1911
|
with_statements = with_values.map do |with_value|
|
|
1899
|
-
raise ArgumentError, "Unsupported argument type: #{with_value} #{with_value.class}" unless with_value.is_a?(Hash)
|
|
1900
|
-
|
|
1901
1912
|
build_with_value_from_hash(with_value)
|
|
1902
1913
|
end
|
|
1903
1914
|
|
|
@@ -1912,7 +1923,8 @@ module ActiveRecord
|
|
|
1912
1923
|
|
|
1913
1924
|
def build_with_expression_from_value(value, nested = false)
|
|
1914
1925
|
case value
|
|
1915
|
-
when Arel::Nodes::SqlLiteral
|
|
1926
|
+
when Arel::Nodes::SqlLiteral, Arel::Nodes::BoundSqlLiteral
|
|
1927
|
+
Arel::Nodes::Grouping.new(value)
|
|
1916
1928
|
when ActiveRecord::Relation
|
|
1917
1929
|
if nested
|
|
1918
1930
|
value.arel.ast
|
|
@@ -1939,37 +1951,76 @@ module ActiveRecord
|
|
|
1939
1951
|
with_table = Arel::Table.new(name)
|
|
1940
1952
|
|
|
1941
1953
|
table.join(with_table, kind).on(
|
|
1942
|
-
with_table[
|
|
1954
|
+
with_table[model.model_name.to_s.foreign_key].eq(table[model.primary_key])
|
|
1943
1955
|
).join_sources.first
|
|
1944
1956
|
end
|
|
1945
1957
|
|
|
1958
|
+
def arel_columns_from_hash(fields)
|
|
1959
|
+
fields.flat_map do |table_name, columns|
|
|
1960
|
+
table_name = table_name.name if table_name.is_a?(Symbol)
|
|
1961
|
+
case columns
|
|
1962
|
+
when Symbol, String
|
|
1963
|
+
arel_column_with_table(table_name, columns)
|
|
1964
|
+
when Array
|
|
1965
|
+
columns.map do |column|
|
|
1966
|
+
arel_column_with_table(table_name, column)
|
|
1967
|
+
end
|
|
1968
|
+
else
|
|
1969
|
+
raise TypeError, "Expected Symbol, String or Array, got: #{columns.class}"
|
|
1970
|
+
end
|
|
1971
|
+
end
|
|
1972
|
+
end
|
|
1973
|
+
|
|
1974
|
+
def arel_column_with_table(table_name, column_name)
|
|
1975
|
+
self.references_values |= [Arel.sql(table_name, retryable: true)]
|
|
1976
|
+
|
|
1977
|
+
if column_name.is_a?(Symbol) || !column_name.match?(/\W/)
|
|
1978
|
+
predicate_builder.resolve_arel_attribute(table_name, column_name) do
|
|
1979
|
+
lookup_table_klass_from_join_dependencies(table_name)
|
|
1980
|
+
end
|
|
1981
|
+
else
|
|
1982
|
+
Arel.sql("#{model.adapter_class.quote_table_name(table_name)}.#{column_name}")
|
|
1983
|
+
end
|
|
1984
|
+
end
|
|
1985
|
+
|
|
1946
1986
|
def arel_column(field)
|
|
1947
|
-
field =
|
|
1987
|
+
field = field.name if is_symbol = field.is_a?(Symbol)
|
|
1988
|
+
|
|
1989
|
+
field = model.attribute_aliases[field] || field
|
|
1948
1990
|
from = from_clause.name || from_clause.value
|
|
1949
1991
|
|
|
1950
|
-
if
|
|
1992
|
+
if model.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
|
1951
1993
|
table[field]
|
|
1952
1994
|
elsif /\A(?<table>(?:\w+\.)?\w+)\.(?<column>\w+)\z/ =~ field
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
lookup_table_klass_from_join_dependencies(table)
|
|
1956
|
-
end
|
|
1957
|
-
else
|
|
1995
|
+
arel_column_with_table(table, column)
|
|
1996
|
+
elsif block_given?
|
|
1958
1997
|
yield field
|
|
1998
|
+
elsif Arel.arel_node?(field)
|
|
1999
|
+
field
|
|
2000
|
+
elsif is_symbol
|
|
2001
|
+
Arel.sql(model.adapter_class.quote_table_name(field), retryable: true)
|
|
2002
|
+
else
|
|
2003
|
+
Arel.sql(field)
|
|
1959
2004
|
end
|
|
1960
2005
|
end
|
|
1961
2006
|
|
|
1962
2007
|
def table_name_matches?(from)
|
|
1963
2008
|
table_name = Regexp.escape(table.name)
|
|
1964
|
-
quoted_table_name = Regexp.escape(adapter_class.quote_table_name(table.name))
|
|
2009
|
+
quoted_table_name = Regexp.escape(model.adapter_class.quote_table_name(table.name))
|
|
1965
2010
|
/(?:\A|(?<!FROM)\s)(?:\b#{table_name}\b|#{quoted_table_name})(?!\.)/i.match?(from.to_s)
|
|
1966
2011
|
end
|
|
1967
2012
|
|
|
1968
2013
|
def reverse_sql_order(order_query)
|
|
1969
2014
|
if order_query.empty?
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
2015
|
+
if !_reverse_order_columns.empty?
|
|
2016
|
+
return _reverse_order_columns.map { |column| table[column].desc }
|
|
2017
|
+
end
|
|
2018
|
+
|
|
2019
|
+
raise IrreversibleOrderError, <<~MSG.squish
|
|
2020
|
+
Relation has no order values, and #{model} has no order columns to use as a default.
|
|
2021
|
+
Set at least one of `implicit_order_column`, or `primary_key` on the model when no
|
|
2022
|
+
`order `is specified on the relation.
|
|
2023
|
+
MSG
|
|
1973
2024
|
end
|
|
1974
2025
|
|
|
1975
2026
|
order_query.flat_map do |o|
|
|
@@ -1994,6 +2045,13 @@ module ActiveRecord
|
|
|
1994
2045
|
end
|
|
1995
2046
|
end
|
|
1996
2047
|
|
|
2048
|
+
def _reverse_order_columns
|
|
2049
|
+
roc = []
|
|
2050
|
+
roc << model.implicit_order_column if model.implicit_order_column
|
|
2051
|
+
roc << model.primary_key if model.primary_key
|
|
2052
|
+
roc.flatten.uniq.compact
|
|
2053
|
+
end
|
|
2054
|
+
|
|
1997
2055
|
def does_not_support_reverse?(order)
|
|
1998
2056
|
# Account for String subclasses like Arel::Nodes::SqlLiteral that
|
|
1999
2057
|
# override methods like #count.
|
|
@@ -2032,7 +2090,7 @@ module ActiveRecord
|
|
|
2032
2090
|
end
|
|
2033
2091
|
|
|
2034
2092
|
def preprocess_order_args(order_args)
|
|
2035
|
-
|
|
2093
|
+
model.disallow_raw_sql!(
|
|
2036
2094
|
flattened_args(order_args),
|
|
2037
2095
|
permit: model.adapter_class.column_name_with_order_matcher
|
|
2038
2096
|
)
|
|
@@ -2070,7 +2128,7 @@ module ActiveRecord
|
|
|
2070
2128
|
|
|
2071
2129
|
def sanitize_order_arguments(order_args)
|
|
2072
2130
|
order_args.map! do |arg|
|
|
2073
|
-
|
|
2131
|
+
model.sanitize_sql_for_order(arg)
|
|
2074
2132
|
end
|
|
2075
2133
|
end
|
|
2076
2134
|
|
|
@@ -2096,7 +2154,7 @@ module ActiveRecord
|
|
|
2096
2154
|
arg.expr.relation.name
|
|
2097
2155
|
end
|
|
2098
2156
|
end
|
|
2099
|
-
end.
|
|
2157
|
+
end.filter_map { |ref| Arel.sql(ref, retryable: true) if ref }
|
|
2100
2158
|
end
|
|
2101
2159
|
|
|
2102
2160
|
def extract_table_name_from(string)
|
|
@@ -2108,17 +2166,18 @@ module ActiveRecord
|
|
|
2108
2166
|
if attr_name == "count" && !group_values.empty?
|
|
2109
2167
|
table[attr_name]
|
|
2110
2168
|
else
|
|
2111
|
-
Arel.sql(adapter_class.quote_table_name(attr_name), retryable: true)
|
|
2169
|
+
Arel.sql(model.adapter_class.quote_table_name(attr_name), retryable: true)
|
|
2112
2170
|
end
|
|
2113
2171
|
end
|
|
2114
2172
|
end
|
|
2115
2173
|
|
|
2116
|
-
def build_case_for_value_position(column, values)
|
|
2174
|
+
def build_case_for_value_position(column, values, filter: true)
|
|
2117
2175
|
node = Arel::Nodes::Case.new
|
|
2118
2176
|
values.each.with_index(1) do |value, order|
|
|
2119
2177
|
node.when(column.eq(value)).then(order)
|
|
2120
2178
|
end
|
|
2121
2179
|
|
|
2180
|
+
node = node.else(values.length + 1) unless filter
|
|
2122
2181
|
Arel::Nodes::Ascending.new(node)
|
|
2123
2182
|
end
|
|
2124
2183
|
|
|
@@ -2176,38 +2235,40 @@ module ActiveRecord
|
|
|
2176
2235
|
def process_select_args(fields)
|
|
2177
2236
|
fields.flat_map do |field|
|
|
2178
2237
|
if field.is_a?(Hash)
|
|
2179
|
-
|
|
2238
|
+
arel_column_aliases_from_hash(field)
|
|
2180
2239
|
else
|
|
2181
2240
|
field
|
|
2182
2241
|
end
|
|
2183
2242
|
end
|
|
2184
2243
|
end
|
|
2185
2244
|
|
|
2186
|
-
def
|
|
2245
|
+
def arel_column_aliases_from_hash(fields)
|
|
2187
2246
|
fields.flat_map do |key, columns_aliases|
|
|
2247
|
+
table_name = key.is_a?(Symbol) ? key.name : key
|
|
2188
2248
|
case columns_aliases
|
|
2189
2249
|
when Hash
|
|
2190
2250
|
columns_aliases.map do |column, column_alias|
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
self.references_values |= references unless references.empty?
|
|
2194
|
-
end
|
|
2195
|
-
arel_column("#{key}.#{column}") do
|
|
2196
|
-
predicate_builder.resolve_arel_attribute(key.to_s, column)
|
|
2197
|
-
end.as(column_alias.to_s)
|
|
2251
|
+
arel_column_with_table(table_name, column)
|
|
2252
|
+
.as(model.adapter_class.quote_column_name(column_alias.to_s))
|
|
2198
2253
|
end
|
|
2199
2254
|
when Array
|
|
2200
2255
|
columns_aliases.map do |column|
|
|
2201
|
-
|
|
2256
|
+
arel_column_with_table(table_name, column)
|
|
2202
2257
|
end
|
|
2203
2258
|
when String, Symbol
|
|
2204
|
-
arel_column(key
|
|
2205
|
-
|
|
2206
|
-
end.as(columns_aliases.to_s)
|
|
2259
|
+
arel_column(key)
|
|
2260
|
+
.as(model.adapter_class.quote_column_name(columns_aliases.to_s))
|
|
2207
2261
|
end
|
|
2208
2262
|
end
|
|
2209
2263
|
end
|
|
2210
2264
|
|
|
2265
|
+
def process_with_args(args)
|
|
2266
|
+
args.flat_map do |arg|
|
|
2267
|
+
raise ArgumentError, "Unsupported argument type: #{arg} #{arg.class}" unless arg.is_a?(Hash)
|
|
2268
|
+
arg.map { |k, v| { k => v } }
|
|
2269
|
+
end
|
|
2270
|
+
end
|
|
2271
|
+
|
|
2211
2272
|
STRUCTURAL_VALUE_METHODS = (
|
|
2212
2273
|
Relation::VALUE_METHODS -
|
|
2213
2274
|
[:extending, :where, :having, :unscope, :references, :annotate, :optimizer_hints]
|
|
@@ -2217,11 +2278,11 @@ module ActiveRecord
|
|
|
2217
2278
|
values = other.values
|
|
2218
2279
|
STRUCTURAL_VALUE_METHODS.reject do |method|
|
|
2219
2280
|
v1, v2 = @values[method], values[method]
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2281
|
+
|
|
2282
|
+
# `and`/`or` are focused to combine where-like clauses, so it relaxes
|
|
2283
|
+
# the difference when other's multi values are uninitialized.
|
|
2284
|
+
next true if v1.is_a?(Array) && v2.nil?
|
|
2285
|
+
|
|
2225
2286
|
v1 == v2
|
|
2226
2287
|
end
|
|
2227
2288
|
end
|
|
@@ -7,7 +7,7 @@ require "active_record/relation/merger"
|
|
|
7
7
|
module ActiveRecord
|
|
8
8
|
module SpawnMethods
|
|
9
9
|
def spawn # :nodoc:
|
|
10
|
-
already_in_scope?(
|
|
10
|
+
already_in_scope?(model.scope_registry) ? model.all : clone
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
# Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
|
|
@@ -52,18 +52,18 @@ module ActiveRecord
|
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
# Removes
|
|
55
|
+
# Removes the condition(s) specified in +skips+ from the query.
|
|
56
56
|
#
|
|
57
|
-
# Post.order('id asc').except(:order) #
|
|
58
|
-
# Post.where('id > 10').order('id asc').except(:where) #
|
|
57
|
+
# Post.order('id asc').except(:order) # removes the order condition
|
|
58
|
+
# Post.where('id > 10').order('id asc').except(:where) # removes the where condition but keeps the order
|
|
59
59
|
def except(*skips)
|
|
60
60
|
relation_with values.except(*skips)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
#
|
|
63
|
+
# Keeps only the condition(s) specified in +onlies+ in the query, removing all others.
|
|
64
64
|
#
|
|
65
|
-
# Post.order('id asc').only(:where) #
|
|
66
|
-
# Post.order('id asc').only(:where, :order) #
|
|
65
|
+
# Post.order('id asc').only(:where) # keeps only the where condition, removes the order
|
|
66
|
+
# Post.order('id asc').only(:where, :order) # keeps only the where and order conditions
|
|
67
67
|
def only(*onlies)
|
|
68
68
|
relation_with values.slice(*onlies)
|
|
69
69
|
end
|
|
@@ -135,10 +135,14 @@ module ActiveRecord
|
|
|
135
135
|
|
|
136
136
|
def extract_attribute(node)
|
|
137
137
|
attr_node = nil
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
|
|
139
|
+
valid_attrs = Arel.fetch_attribute(node) do |attr|
|
|
140
|
+
!attr_node || attr_node == attr # all attr nodes should be the same
|
|
141
|
+
ensure
|
|
140
142
|
attr_node = attr
|
|
141
143
|
end
|
|
144
|
+
return unless valid_attrs # all nested nodes should yield an attribute
|
|
145
|
+
|
|
142
146
|
attr_node
|
|
143
147
|
end
|
|
144
148
|
|
|
@@ -172,11 +176,13 @@ module ActiveRecord
|
|
|
172
176
|
end
|
|
173
177
|
|
|
174
178
|
def except_predicates(columns)
|
|
179
|
+
return predicates if columns.empty?
|
|
180
|
+
|
|
175
181
|
attrs = columns.extract! { |node| node.is_a?(Arel::Attribute) }
|
|
176
182
|
non_attrs = columns.extract! { |node| node.is_a?(Arel::Predications) }
|
|
177
183
|
|
|
178
184
|
predicates.reject do |node|
|
|
179
|
-
if !non_attrs.empty? && node
|
|
185
|
+
if !non_attrs.empty? && equality_node?(node) && node.left.is_a?(Arel::Predications)
|
|
180
186
|
non_attrs.include?(node.left)
|
|
181
187
|
end || Arel.fetch_attribute(node) do |attr|
|
|
182
188
|
attrs.include?(attr) || columns.include?(attr.name.to_s)
|
|
@@ -188,7 +194,7 @@ module ActiveRecord
|
|
|
188
194
|
non_empty_predicates.map do |node|
|
|
189
195
|
case node
|
|
190
196
|
when Arel::Nodes::SqlLiteral, ::String
|
|
191
|
-
|
|
197
|
+
Arel::Nodes::Grouping.new(node)
|
|
192
198
|
else node
|
|
193
199
|
end
|
|
194
200
|
end
|
|
@@ -199,13 +205,6 @@ module ActiveRecord
|
|
|
199
205
|
predicates - ARRAY_WITH_EMPTY_STRING
|
|
200
206
|
end
|
|
201
207
|
|
|
202
|
-
def wrap_sql_literal(node)
|
|
203
|
-
if ::String === node
|
|
204
|
-
node = Arel.sql(node)
|
|
205
|
-
end
|
|
206
|
-
Arel::Nodes::Grouping.new(node)
|
|
207
|
-
end
|
|
208
|
-
|
|
209
208
|
def extract_node_value(node)
|
|
210
209
|
if node.respond_to?(:value_before_type_cast)
|
|
211
210
|
node.value_before_type_cast
|