activerecord 7.2.2.1 → 8.0.5
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 +523 -677
- data/README.rdoc +2 -2
- data/lib/active_record/association_relation.rb +1 -0
- data/lib/active_record/associations/alias_tracker.rb +6 -4
- data/lib/active_record/associations/association.rb +34 -10
- data/lib/active_record/associations/belongs_to_association.rb +18 -2
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/collection_association.rb +10 -8
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- 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 +2 -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/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +34 -4
- 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 +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
- data/lib/active_record/attribute_methods.rb +24 -19
- data/lib/active_record/attributes.rb +37 -26
- data/lib/active_record/autosave_association.rb +91 -39
- data/lib/active_record/base.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +34 -25
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +55 -47
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +91 -44
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +40 -10
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +92 -70
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +35 -46
- data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -9
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +50 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +84 -94
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -9
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -43
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -24
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +60 -17
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +61 -99
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +80 -100
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -0
- 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 +10 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +63 -17
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -18
- data/lib/active_record/connection_adapters.rb +0 -56
- data/lib/active_record/connection_handling.rb +35 -9
- data/lib/active_record/core.rb +57 -22
- data/lib/active_record/counter_cache.rb +1 -1
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
- data/lib/active_record/database_configurations/database_config.rb +4 -0
- data/lib/active_record/database_configurations/hash_config.rb +16 -2
- data/lib/active_record/delegated_type.rb +18 -18
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
- data/lib/active_record/encryption/encryptor.rb +37 -28
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/enum.rb +22 -34
- data/lib/active_record/errors.rb +16 -8
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +0 -2
- data/lib/active_record/future_result.rb +15 -9
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/insert_all.rb +3 -3
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +5 -11
- data/lib/active_record/migration/command_recorder.rb +32 -12
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +40 -43
- data/lib/active_record/model_schema.rb +3 -4
- data/lib/active_record/nested_attributes.rb +4 -6
- data/lib/active_record/persistence.rb +128 -130
- data/lib/active_record/query_logs.rb +106 -50
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +12 -12
- data/lib/active_record/railtie.rb +4 -28
- data/lib/active_record/railties/databases.rake +11 -34
- data/lib/active_record/reflection.rb +18 -21
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +132 -72
- data/lib/active_record/relation/calculations.rb +71 -65
- data/lib/active_record/relation/delegation.rb +25 -14
- data/lib/active_record/relation/finder_methods.rb +32 -31
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +13 -0
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +121 -70
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation/where_clause.rb +9 -3
- data/lib/active_record/relation.rb +95 -67
- data/lib/active_record/result.rb +66 -4
- data/lib/active_record/sanitization.rb +7 -6
- data/lib/active_record/schema_dumper.rb +34 -11
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +7 -6
- data/lib/active_record/statement_cache.rb +14 -14
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +1 -3
- data/lib/active_record/tasks/database_tasks.rb +69 -60
- data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +9 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +12 -0
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transactions.rb +8 -7
- data/lib/active_record/type/serialized.rb +5 -0
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +22 -49
- 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 +2 -0
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/node.rb +1 -1
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/predications.rb +1 -3
- data/lib/arel/select_manager.rb +6 -2
- data/lib/arel/table.rb +3 -7
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -0
- data/lib/arel/visitors/to_sql.rb +3 -1
- metadata +11 -15
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
|
@@ -9,10 +9,11 @@ module ActiveRecord
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
if value.select_values.empty?
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
model = value.model
|
|
13
|
+
if model.composite_primary_key?
|
|
14
|
+
raise ArgumentError, "Cannot map composite primary key #{model.primary_key} to #{attribute.name}"
|
|
14
15
|
else
|
|
15
|
-
value = value.select(value.table[
|
|
16
|
+
value = value.select(value.table[model.primary_key])
|
|
16
17
|
end
|
|
17
18
|
end
|
|
18
19
|
|
|
@@ -72,11 +72,24 @@ module ActiveRecord
|
|
|
72
72
|
table.associated_table(table_name, &block).arel_table[column_name]
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
def with(table)
|
|
76
|
+
other = dup
|
|
77
|
+
other.table = table
|
|
78
|
+
other
|
|
79
|
+
end
|
|
80
|
+
|
|
75
81
|
protected
|
|
82
|
+
attr_writer :table
|
|
83
|
+
|
|
76
84
|
def expand_from_hash(attributes, &block)
|
|
77
85
|
return ["1=0"] if attributes.empty?
|
|
78
86
|
|
|
79
87
|
attributes.flat_map do |key, value|
|
|
88
|
+
if key.is_a?(Array) && key.size == 1
|
|
89
|
+
key = key.first
|
|
90
|
+
value = value.flatten
|
|
91
|
+
end
|
|
92
|
+
|
|
80
93
|
if key.is_a?(Array)
|
|
81
94
|
queries = Array(value).map do |ids_set|
|
|
82
95
|
raise ArgumentError, "Expected corresponding value for #{key} to be an Array" unless ids_set.is_a?(Array)
|
|
@@ -35,7 +35,7 @@ module ActiveRecord
|
|
|
35
35
|
def nil?
|
|
36
36
|
unless value_before_type_cast.is_a?(StatementCache::Substitute)
|
|
37
37
|
value_before_type_cast.nil? ||
|
|
38
|
-
type.respond_to?(:subtype) && serializable? && value_for_database.nil?
|
|
38
|
+
(type.respond_to?(:subtype) || type.respond_to?(:normalizer)) && serializable? && value_for_database.nil?
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -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
|
|
@@ -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.
|
|
@@ -1277,13 +1299,13 @@ module ActiveRecord
|
|
|
1277
1299
|
#
|
|
1278
1300
|
# users = User.readonly
|
|
1279
1301
|
# users.first.save
|
|
1280
|
-
# => ActiveRecord::ReadOnlyRecord: User is marked as readonly
|
|
1302
|
+
# # => ActiveRecord::ReadOnlyRecord: User is marked as readonly
|
|
1281
1303
|
#
|
|
1282
1304
|
# To make a readonly relation writable, pass +false+.
|
|
1283
1305
|
#
|
|
1284
1306
|
# users.readonly(false)
|
|
1285
1307
|
# users.first.save
|
|
1286
|
-
# => true
|
|
1308
|
+
# # => true
|
|
1287
1309
|
def readonly(value = true)
|
|
1288
1310
|
spawn.readonly!(value)
|
|
1289
1311
|
end
|
|
@@ -1298,7 +1320,7 @@ module ActiveRecord
|
|
|
1298
1320
|
#
|
|
1299
1321
|
# user = User.strict_loading.first
|
|
1300
1322
|
# user.comments.to_a
|
|
1301
|
-
# => ActiveRecord::StrictLoadingViolationError
|
|
1323
|
+
# # => ActiveRecord::StrictLoadingViolationError
|
|
1302
1324
|
def strict_loading(value = true)
|
|
1303
1325
|
spawn.strict_loading!(value)
|
|
1304
1326
|
end
|
|
@@ -1554,8 +1576,8 @@ module ActiveRecord
|
|
|
1554
1576
|
records.flatten!(1)
|
|
1555
1577
|
records.compact!
|
|
1556
1578
|
|
|
1557
|
-
unless records.all?(
|
|
1558
|
-
raise ArgumentError, "You must only pass a single or collection of #{
|
|
1579
|
+
unless records.all?(model) && relations.all? { |relation| relation.model == model }
|
|
1580
|
+
raise ArgumentError, "You must only pass a single or collection of #{model.name} objects to ##{__callee__}."
|
|
1559
1581
|
end
|
|
1560
1582
|
|
|
1561
1583
|
spawn.excluding!(records + relations.flat_map(&:ids))
|
|
@@ -1575,7 +1597,7 @@ module ActiveRecord
|
|
|
1575
1597
|
|
|
1576
1598
|
def construct_join_dependency(associations, join_type) # :nodoc:
|
|
1577
1599
|
ActiveRecord::Associations::JoinDependency.new(
|
|
1578
|
-
|
|
1600
|
+
model, table, associations, join_type
|
|
1579
1601
|
)
|
|
1580
1602
|
end
|
|
1581
1603
|
|
|
@@ -1604,15 +1626,15 @@ module ActiveRecord
|
|
|
1604
1626
|
elsif opts.include?("?")
|
|
1605
1627
|
parts = [build_bound_sql_literal(opts, rest)]
|
|
1606
1628
|
else
|
|
1607
|
-
parts = [
|
|
1629
|
+
parts = [model.sanitize_sql(rest.empty? ? opts : [opts, *rest])]
|
|
1608
1630
|
end
|
|
1609
1631
|
when Hash
|
|
1610
1632
|
opts = opts.transform_keys do |key|
|
|
1611
1633
|
if key.is_a?(Array)
|
|
1612
|
-
key.map { |k|
|
|
1634
|
+
key.map { |k| model.attribute_aliases[k.to_s] || k.to_s }
|
|
1613
1635
|
else
|
|
1614
1636
|
key = key.to_s
|
|
1615
|
-
|
|
1637
|
+
model.attribute_aliases[key] || key
|
|
1616
1638
|
end
|
|
1617
1639
|
end
|
|
1618
1640
|
references = PredicateBuilder.references(opts)
|
|
@@ -1640,12 +1662,8 @@ module ActiveRecord
|
|
|
1640
1662
|
def arel_columns(columns)
|
|
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
|
|
@@ -1828,7 +1846,7 @@ module ActiveRecord
|
|
|
1828
1846
|
|
|
1829
1847
|
joins = joins_values.dup
|
|
1830
1848
|
if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
|
|
1831
|
-
stashed_eager_load = joins.pop if joins.last.base_klass ==
|
|
1849
|
+
stashed_eager_load = joins.pop if joins.last.base_klass == model
|
|
1832
1850
|
end
|
|
1833
1851
|
|
|
1834
1852
|
joins.each_with_index do |join, i|
|
|
@@ -1885,8 +1903,8 @@ module ActiveRecord
|
|
|
1885
1903
|
def build_select(arel)
|
|
1886
1904
|
if select_values.any?
|
|
1887
1905
|
arel.project(*arel_columns(select_values))
|
|
1888
|
-
elsif
|
|
1889
|
-
arel.project(*
|
|
1906
|
+
elsif model.ignored_columns.any? || model.enumerate_columns_in_select_statements
|
|
1907
|
+
arel.project(*model.column_names.map { |field| table[field] })
|
|
1890
1908
|
else
|
|
1891
1909
|
arel.project(table[Arel.star])
|
|
1892
1910
|
end
|
|
@@ -1896,8 +1914,6 @@ module ActiveRecord
|
|
|
1896
1914
|
return if with_values.empty?
|
|
1897
1915
|
|
|
1898
1916
|
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
1917
|
build_with_value_from_hash(with_value)
|
|
1902
1918
|
end
|
|
1903
1919
|
|
|
@@ -1912,7 +1928,8 @@ module ActiveRecord
|
|
|
1912
1928
|
|
|
1913
1929
|
def build_with_expression_from_value(value, nested = false)
|
|
1914
1930
|
case value
|
|
1915
|
-
when Arel::Nodes::SqlLiteral
|
|
1931
|
+
when Arel::Nodes::SqlLiteral, Arel::Nodes::BoundSqlLiteral
|
|
1932
|
+
Arel::Nodes::Grouping.new(value)
|
|
1916
1933
|
when ActiveRecord::Relation
|
|
1917
1934
|
if nested
|
|
1918
1935
|
value.arel.ast
|
|
@@ -1939,29 +1956,60 @@ module ActiveRecord
|
|
|
1939
1956
|
with_table = Arel::Table.new(name)
|
|
1940
1957
|
|
|
1941
1958
|
table.join(with_table, kind).on(
|
|
1942
|
-
with_table[
|
|
1959
|
+
with_table[model.model_name.to_s.foreign_key].eq(table[model.primary_key])
|
|
1943
1960
|
).join_sources.first
|
|
1944
1961
|
end
|
|
1945
1962
|
|
|
1963
|
+
def arel_columns_from_hash(fields)
|
|
1964
|
+
fields.flat_map do |table_name, columns|
|
|
1965
|
+
table_name = table_name.name if table_name.is_a?(Symbol)
|
|
1966
|
+
case columns
|
|
1967
|
+
when Symbol, String
|
|
1968
|
+
arel_column_with_table(table_name, columns)
|
|
1969
|
+
when Array
|
|
1970
|
+
columns.map do |column|
|
|
1971
|
+
arel_column_with_table(table_name, column)
|
|
1972
|
+
end
|
|
1973
|
+
else
|
|
1974
|
+
raise TypeError, "Expected Symbol, String or Array, got: #{columns.class}"
|
|
1975
|
+
end
|
|
1976
|
+
end
|
|
1977
|
+
end
|
|
1978
|
+
|
|
1979
|
+
def arel_column_with_table(table_name, column_name)
|
|
1980
|
+
self.references_values |= [Arel.sql(table_name, retryable: true)]
|
|
1981
|
+
|
|
1982
|
+
if column_name.is_a?(Symbol) || !column_name.match?(/\W/)
|
|
1983
|
+
predicate_builder.resolve_arel_attribute(table_name, column_name) do
|
|
1984
|
+
lookup_table_klass_from_join_dependencies(table_name)
|
|
1985
|
+
end
|
|
1986
|
+
else
|
|
1987
|
+
Arel.sql("#{model.adapter_class.quote_table_name(table_name)}.#{column_name}")
|
|
1988
|
+
end
|
|
1989
|
+
end
|
|
1990
|
+
|
|
1946
1991
|
def arel_column(field)
|
|
1947
|
-
field =
|
|
1992
|
+
field = field.name if is_symbol = field.is_a?(Symbol)
|
|
1993
|
+
|
|
1994
|
+
field = model.attribute_aliases[field] || field.to_s
|
|
1948
1995
|
from = from_clause.name || from_clause.value
|
|
1949
1996
|
|
|
1950
|
-
if
|
|
1997
|
+
if model.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
|
1951
1998
|
table[field]
|
|
1952
1999
|
elsif /\A(?<table>(?:\w+\.)?\w+)\.(?<column>\w+)\z/ =~ field
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
lookup_table_klass_from_join_dependencies(table)
|
|
1956
|
-
end
|
|
1957
|
-
else
|
|
2000
|
+
arel_column_with_table(table, column)
|
|
2001
|
+
elsif block_given?
|
|
1958
2002
|
yield field
|
|
2003
|
+
elsif Arel.arel_node?(field)
|
|
2004
|
+
field
|
|
2005
|
+
else
|
|
2006
|
+
Arel.sql(is_symbol ? model.adapter_class.quote_table_name(field) : field)
|
|
1959
2007
|
end
|
|
1960
2008
|
end
|
|
1961
2009
|
|
|
1962
2010
|
def table_name_matches?(from)
|
|
1963
2011
|
table_name = Regexp.escape(table.name)
|
|
1964
|
-
quoted_table_name = Regexp.escape(adapter_class.quote_table_name(table.name))
|
|
2012
|
+
quoted_table_name = Regexp.escape(model.adapter_class.quote_table_name(table.name))
|
|
1965
2013
|
/(?:\A|(?<!FROM)\s)(?:\b#{table_name}\b|#{quoted_table_name})(?!\.)/i.match?(from.to_s)
|
|
1966
2014
|
end
|
|
1967
2015
|
|
|
@@ -2032,7 +2080,7 @@ module ActiveRecord
|
|
|
2032
2080
|
end
|
|
2033
2081
|
|
|
2034
2082
|
def preprocess_order_args(order_args)
|
|
2035
|
-
|
|
2083
|
+
model.disallow_raw_sql!(
|
|
2036
2084
|
flattened_args(order_args),
|
|
2037
2085
|
permit: model.adapter_class.column_name_with_order_matcher
|
|
2038
2086
|
)
|
|
@@ -2070,7 +2118,7 @@ module ActiveRecord
|
|
|
2070
2118
|
|
|
2071
2119
|
def sanitize_order_arguments(order_args)
|
|
2072
2120
|
order_args.map! do |arg|
|
|
2073
|
-
|
|
2121
|
+
model.sanitize_sql_for_order(arg)
|
|
2074
2122
|
end
|
|
2075
2123
|
end
|
|
2076
2124
|
|
|
@@ -2096,7 +2144,7 @@ module ActiveRecord
|
|
|
2096
2144
|
arg.expr.relation.name
|
|
2097
2145
|
end
|
|
2098
2146
|
end
|
|
2099
|
-
end.
|
|
2147
|
+
end.filter_map { |ref| Arel.sql(ref, retryable: true) if ref }
|
|
2100
2148
|
end
|
|
2101
2149
|
|
|
2102
2150
|
def extract_table_name_from(string)
|
|
@@ -2108,17 +2156,18 @@ module ActiveRecord
|
|
|
2108
2156
|
if attr_name == "count" && !group_values.empty?
|
|
2109
2157
|
table[attr_name]
|
|
2110
2158
|
else
|
|
2111
|
-
Arel.sql(adapter_class.quote_table_name(attr_name), retryable: true)
|
|
2159
|
+
Arel.sql(model.adapter_class.quote_table_name(attr_name), retryable: true)
|
|
2112
2160
|
end
|
|
2113
2161
|
end
|
|
2114
2162
|
end
|
|
2115
2163
|
|
|
2116
|
-
def build_case_for_value_position(column, values)
|
|
2164
|
+
def build_case_for_value_position(column, values, filter: true)
|
|
2117
2165
|
node = Arel::Nodes::Case.new
|
|
2118
2166
|
values.each.with_index(1) do |value, order|
|
|
2119
2167
|
node.when(column.eq(value)).then(order)
|
|
2120
2168
|
end
|
|
2121
2169
|
|
|
2170
|
+
node = node.else(values.length + 1) unless filter
|
|
2122
2171
|
Arel::Nodes::Ascending.new(node)
|
|
2123
2172
|
end
|
|
2124
2173
|
|
|
@@ -2176,38 +2225,40 @@ module ActiveRecord
|
|
|
2176
2225
|
def process_select_args(fields)
|
|
2177
2226
|
fields.flat_map do |field|
|
|
2178
2227
|
if field.is_a?(Hash)
|
|
2179
|
-
|
|
2228
|
+
arel_column_aliases_from_hash(field)
|
|
2180
2229
|
else
|
|
2181
2230
|
field
|
|
2182
2231
|
end
|
|
2183
2232
|
end
|
|
2184
2233
|
end
|
|
2185
2234
|
|
|
2186
|
-
def
|
|
2235
|
+
def arel_column_aliases_from_hash(fields)
|
|
2187
2236
|
fields.flat_map do |key, columns_aliases|
|
|
2237
|
+
table_name = key.is_a?(Symbol) ? key.name : key
|
|
2188
2238
|
case columns_aliases
|
|
2189
2239
|
when Hash
|
|
2190
2240
|
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)
|
|
2241
|
+
arel_column_with_table(table_name, column)
|
|
2242
|
+
.as(model.adapter_class.quote_column_name(column_alias.to_s))
|
|
2198
2243
|
end
|
|
2199
2244
|
when Array
|
|
2200
2245
|
columns_aliases.map do |column|
|
|
2201
|
-
|
|
2246
|
+
arel_column_with_table(table_name, column)
|
|
2202
2247
|
end
|
|
2203
2248
|
when String, Symbol
|
|
2204
|
-
arel_column(key
|
|
2205
|
-
|
|
2206
|
-
end.as(columns_aliases.to_s)
|
|
2249
|
+
arel_column(key)
|
|
2250
|
+
.as(model.adapter_class.quote_column_name(columns_aliases.to_s))
|
|
2207
2251
|
end
|
|
2208
2252
|
end
|
|
2209
2253
|
end
|
|
2210
2254
|
|
|
2255
|
+
def process_with_args(args)
|
|
2256
|
+
args.flat_map do |arg|
|
|
2257
|
+
raise ArgumentError, "Unsupported argument type: #{arg} #{arg.class}" unless arg.is_a?(Hash)
|
|
2258
|
+
arg.map { |k, v| { k => v } }
|
|
2259
|
+
end
|
|
2260
|
+
end
|
|
2261
|
+
|
|
2211
2262
|
STRUCTURAL_VALUE_METHODS = (
|
|
2212
2263
|
Relation::VALUE_METHODS -
|
|
2213
2264
|
[:extending, :where, :having, :unscope, :references, :annotate, :optimizer_hints]
|
|
@@ -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)
|