activerecord 7.2.3 → 8.1.3
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 +612 -1055
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/association.rb +35 -11
- 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 +1 -1
- 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.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 +16 -3
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
- data/lib/active_record/attributes.rb +3 -0
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/base.rb +1 -2
- 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 +412 -88
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +137 -75
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +27 -5
- 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 +32 -35
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +122 -32
- data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
- data/lib/active_record/connection_adapters/abstract_adapter.rb +150 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +63 -52
- 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 +0 -8
- 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 +2 -10
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
- 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 +14 -33
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +71 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +139 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +78 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +3 -5
- data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -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 -14
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +102 -37
- 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 +1 -18
- data/lib/active_record/connection_adapters.rb +1 -56
- data/lib/active_record/connection_handling.rb +25 -2
- data/lib/active_record/core.rb +33 -17
- data/lib/active_record/counter_cache.rb +33 -8
- 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 +1 -1
- 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 +8 -8
- data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
- data/lib/active_record/encryption/encryptor.rb +28 -8
- 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 +33 -30
- data/lib/active_record/errors.rb +33 -9
- 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/fixtures.rb +2 -4
- data/lib/active_record/future_result.rb +15 -9
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +14 -9
- 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 +45 -12
- 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 +48 -42
- 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 +100 -52
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +8 -8
- data/lib/active_record/railtie.rb +35 -30
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +26 -38
- 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 +52 -40
- data/lib/active_record/relation/delegation.rb +25 -15
- data/lib/active_record/relation/finder_methods.rb +40 -24
- 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 +9 -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 +3 -1
- data/lib/active_record/relation/query_methods.rb +140 -86
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation/where_clause.rb +2 -9
- data/lib/active_record/relation.rb +107 -75
- 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 +18 -11
- 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/signed_id.rb +43 -15
- 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 +7 -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 +37 -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 +13 -2
- data/lib/active_record/type/serialized.rb +16 -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 +84 -49
- 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 +6 -11
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/count.rb +2 -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.rb +0 -2
- data/lib/arel/predications.rb +1 -3
- data/lib/arel/select_manager.rb +7 -2
- data/lib/arel/table.rb +3 -7
- data/lib/arel/visitors/dot.rb +0 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +3 -21
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +16 -13
- 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
|
#
|
|
@@ -500,7 +507,7 @@ module ActiveRecord
|
|
|
500
507
|
#
|
|
501
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')])
|
|
502
509
|
# # => ActiveRecord::Relation
|
|
503
|
-
# # WITH post_and_replies AS (
|
|
510
|
+
# # WITH RECURSIVE post_and_replies AS (
|
|
504
511
|
# # (SELECT * FROM posts WHERE id = 42)
|
|
505
512
|
# # UNION ALL
|
|
506
513
|
# # (SELECT * FROM posts JOIN post_and_replies ON posts.in_reply_to_id = post_and_replies.id)
|
|
@@ -569,7 +576,7 @@ module ActiveRecord
|
|
|
569
576
|
end
|
|
570
577
|
|
|
571
578
|
def group!(*args) # :nodoc:
|
|
572
|
-
self.group_values
|
|
579
|
+
self.group_values |= args
|
|
573
580
|
self
|
|
574
581
|
end
|
|
575
582
|
|
|
@@ -698,26 +705,39 @@ module ActiveRecord
|
|
|
698
705
|
# # WHEN "conversations"."status" = 0 THEN 3
|
|
699
706
|
# # END ASC
|
|
700
707
|
#
|
|
701
|
-
|
|
702
|
-
|
|
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)
|
|
703
719
|
return spawn.none! if values.empty?
|
|
704
720
|
|
|
705
721
|
references = column_references([column])
|
|
706
722
|
self.references_values |= references unless references.empty?
|
|
707
723
|
|
|
708
|
-
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) }
|
|
709
725
|
arel_column = column.is_a?(Arel::Nodes::SqlLiteral) ? column : order_column(column.to_s)
|
|
710
726
|
|
|
711
|
-
|
|
712
|
-
if values.include?(nil)
|
|
713
|
-
arel_column.in(values.compact).or(arel_column.eq(nil))
|
|
714
|
-
else
|
|
715
|
-
arel_column.in(values)
|
|
716
|
-
end
|
|
727
|
+
scope = spawn.order!(build_case_for_value_position(arel_column, values, filter: filter))
|
|
717
728
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
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
|
|
721
741
|
end
|
|
722
742
|
|
|
723
743
|
# Replaces any existing order defined on the relation with the specified order.
|
|
@@ -789,7 +809,7 @@ module ActiveRecord
|
|
|
789
809
|
end
|
|
790
810
|
|
|
791
811
|
def unscope!(*args) # :nodoc:
|
|
792
|
-
self.unscope_values
|
|
812
|
+
self.unscope_values |= args
|
|
793
813
|
|
|
794
814
|
args.each do |scope|
|
|
795
815
|
case scope
|
|
@@ -1193,6 +1213,7 @@ module ActiveRecord
|
|
|
1193
1213
|
end
|
|
1194
1214
|
|
|
1195
1215
|
def limit!(value) # :nodoc:
|
|
1216
|
+
value = Integer(value) unless value.nil?
|
|
1196
1217
|
self.limit_value = value
|
|
1197
1218
|
self
|
|
1198
1219
|
end
|
|
@@ -1445,7 +1466,7 @@ module ActiveRecord
|
|
|
1445
1466
|
modules << Module.new(&block) if block
|
|
1446
1467
|
modules.flatten!
|
|
1447
1468
|
|
|
1448
|
-
self.extending_values
|
|
1469
|
+
self.extending_values |= modules
|
|
1449
1470
|
extend(*extending_values) if extending_values.any?
|
|
1450
1471
|
|
|
1451
1472
|
self
|
|
@@ -1513,7 +1534,7 @@ module ActiveRecord
|
|
|
1513
1534
|
|
|
1514
1535
|
# Like #annotate, but modifies relation in place.
|
|
1515
1536
|
def annotate!(*args) # :nodoc:
|
|
1516
|
-
self.annotate_values
|
|
1537
|
+
self.annotate_values |= args
|
|
1517
1538
|
self
|
|
1518
1539
|
end
|
|
1519
1540
|
|
|
@@ -1556,8 +1577,8 @@ module ActiveRecord
|
|
|
1556
1577
|
records.flatten!(1)
|
|
1557
1578
|
records.compact!
|
|
1558
1579
|
|
|
1559
|
-
unless records.all?(
|
|
1560
|
-
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__}."
|
|
1561
1582
|
end
|
|
1562
1583
|
|
|
1563
1584
|
spawn.excluding!(records + relations.flat_map(&:ids))
|
|
@@ -1572,12 +1593,12 @@ module ActiveRecord
|
|
|
1572
1593
|
|
|
1573
1594
|
# Returns the Arel object associated with the relation.
|
|
1574
1595
|
def arel(aliases = nil) # :nodoc:
|
|
1575
|
-
@arel ||=
|
|
1596
|
+
@arel ||= build_arel(aliases)
|
|
1576
1597
|
end
|
|
1577
1598
|
|
|
1578
1599
|
def construct_join_dependency(associations, join_type) # :nodoc:
|
|
1579
1600
|
ActiveRecord::Associations::JoinDependency.new(
|
|
1580
|
-
|
|
1601
|
+
model, table, associations, join_type
|
|
1581
1602
|
)
|
|
1582
1603
|
end
|
|
1583
1604
|
|
|
@@ -1606,15 +1627,15 @@ module ActiveRecord
|
|
|
1606
1627
|
elsif opts.include?("?")
|
|
1607
1628
|
parts = [build_bound_sql_literal(opts, rest)]
|
|
1608
1629
|
else
|
|
1609
|
-
parts = [
|
|
1630
|
+
parts = [Arel.sql(model.sanitize_sql([opts, *rest]))]
|
|
1610
1631
|
end
|
|
1611
1632
|
when Hash
|
|
1612
1633
|
opts = opts.transform_keys do |key|
|
|
1613
1634
|
if key.is_a?(Array)
|
|
1614
|
-
key.map { |k|
|
|
1635
|
+
key.map { |k| model.attribute_aliases[k.to_s] || k.to_s }
|
|
1615
1636
|
else
|
|
1616
1637
|
key = key.to_s
|
|
1617
|
-
|
|
1638
|
+
model.attribute_aliases[key] || key
|
|
1618
1639
|
end
|
|
1619
1640
|
end
|
|
1620
1641
|
references = PredicateBuilder.references(opts)
|
|
@@ -1633,21 +1654,16 @@ module ActiveRecord
|
|
|
1633
1654
|
end
|
|
1634
1655
|
alias :build_having_clause :build_where_clause
|
|
1635
1656
|
|
|
1636
|
-
def async!
|
|
1657
|
+
def async! # :nodoc:
|
|
1637
1658
|
@async = true
|
|
1638
1659
|
self
|
|
1639
1660
|
end
|
|
1640
1661
|
|
|
1641
|
-
|
|
1642
|
-
def arel_columns(columns)
|
|
1662
|
+
def arel_columns(columns) # :nodoc:
|
|
1643
1663
|
columns.flat_map do |field|
|
|
1644
1664
|
case field
|
|
1645
|
-
when Symbol
|
|
1646
|
-
arel_column(field
|
|
1647
|
-
adapter_class.quote_table_name(attr_name)
|
|
1648
|
-
end
|
|
1649
|
-
when String
|
|
1650
|
-
arel_column(field, &:itself)
|
|
1665
|
+
when Symbol, String
|
|
1666
|
+
arel_column(field)
|
|
1651
1667
|
when Proc
|
|
1652
1668
|
field.call
|
|
1653
1669
|
when Hash
|
|
@@ -1731,32 +1747,27 @@ module ActiveRecord
|
|
|
1731
1747
|
raise UnmodifiableRelation if @loaded || @arel
|
|
1732
1748
|
end
|
|
1733
1749
|
|
|
1734
|
-
def build_arel(
|
|
1750
|
+
def build_arel(aliases)
|
|
1735
1751
|
arel = Arel::SelectManager.new(table)
|
|
1736
1752
|
|
|
1737
1753
|
build_joins(arel.join_sources, aliases)
|
|
1738
1754
|
|
|
1739
1755
|
arel.where(where_clause.ast) unless where_clause.empty?
|
|
1740
1756
|
arel.having(having_clause.ast) unless having_clause.empty?
|
|
1741
|
-
arel.take(build_cast_value("LIMIT",
|
|
1757
|
+
arel.take(build_cast_value("LIMIT", limit_value)) if limit_value
|
|
1742
1758
|
arel.skip(build_cast_value("OFFSET", offset_value.to_i)) if offset_value
|
|
1743
|
-
arel.group(*arel_columns(group_values
|
|
1759
|
+
arel.group(*arel_columns(group_values)) unless group_values.empty?
|
|
1744
1760
|
|
|
1745
1761
|
build_order(arel)
|
|
1746
1762
|
build_with(arel)
|
|
1747
1763
|
build_select(arel)
|
|
1748
1764
|
|
|
1749
1765
|
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
|
1766
|
+
arel.comment(*annotate_values) unless annotate_values.empty?
|
|
1750
1767
|
arel.distinct(distinct_value)
|
|
1751
1768
|
arel.from(build_from) unless from_clause.empty?
|
|
1752
1769
|
arel.lock(lock_value) if lock_value
|
|
1753
1770
|
|
|
1754
|
-
unless annotate_values.empty?
|
|
1755
|
-
annotates = annotate_values
|
|
1756
|
-
annotates = annotates.uniq if annotates.size > 1
|
|
1757
|
-
arel.comment(*annotates)
|
|
1758
|
-
end
|
|
1759
|
-
|
|
1760
1771
|
arel
|
|
1761
1772
|
end
|
|
1762
1773
|
|
|
@@ -1830,7 +1841,7 @@ module ActiveRecord
|
|
|
1830
1841
|
|
|
1831
1842
|
joins = joins_values.dup
|
|
1832
1843
|
if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
|
|
1833
|
-
stashed_eager_load = joins.pop if joins.last.base_klass ==
|
|
1844
|
+
stashed_eager_load = joins.pop if joins.last.base_klass == model
|
|
1834
1845
|
end
|
|
1835
1846
|
|
|
1836
1847
|
joins.each_with_index do |join, i|
|
|
@@ -1887,8 +1898,8 @@ module ActiveRecord
|
|
|
1887
1898
|
def build_select(arel)
|
|
1888
1899
|
if select_values.any?
|
|
1889
1900
|
arel.project(*arel_columns(select_values))
|
|
1890
|
-
elsif
|
|
1891
|
-
arel.project(*
|
|
1901
|
+
elsif model.ignored_columns.any? || model.enumerate_columns_in_select_statements
|
|
1902
|
+
arel.project(*model.column_names.map { |field| table[field] })
|
|
1892
1903
|
else
|
|
1893
1904
|
arel.project(table[Arel.star])
|
|
1894
1905
|
end
|
|
@@ -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
|
|
|
@@ -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,34 +2235,29 @@ 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
|
|
@@ -2224,11 +2278,11 @@ module ActiveRecord
|
|
|
2224
2278
|
values = other.values
|
|
2225
2279
|
STRUCTURAL_VALUE_METHODS.reject do |method|
|
|
2226
2280
|
v1, v2 = @values[method], values[method]
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
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
|
+
|
|
2232
2286
|
v1 == v2
|
|
2233
2287
|
end
|
|
2234
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
|
|
@@ -182,7 +182,7 @@ module ActiveRecord
|
|
|
182
182
|
non_attrs = columns.extract! { |node| node.is_a?(Arel::Predications) }
|
|
183
183
|
|
|
184
184
|
predicates.reject do |node|
|
|
185
|
-
if !non_attrs.empty? && node
|
|
185
|
+
if !non_attrs.empty? && equality_node?(node) && node.left.is_a?(Arel::Predications)
|
|
186
186
|
non_attrs.include?(node.left)
|
|
187
187
|
end || Arel.fetch_attribute(node) do |attr|
|
|
188
188
|
attrs.include?(attr) || columns.include?(attr.name.to_s)
|
|
@@ -194,7 +194,7 @@ module ActiveRecord
|
|
|
194
194
|
non_empty_predicates.map do |node|
|
|
195
195
|
case node
|
|
196
196
|
when Arel::Nodes::SqlLiteral, ::String
|
|
197
|
-
|
|
197
|
+
Arel::Nodes::Grouping.new(node)
|
|
198
198
|
else node
|
|
199
199
|
end
|
|
200
200
|
end
|
|
@@ -205,13 +205,6 @@ module ActiveRecord
|
|
|
205
205
|
predicates - ARRAY_WITH_EMPTY_STRING
|
|
206
206
|
end
|
|
207
207
|
|
|
208
|
-
def wrap_sql_literal(node)
|
|
209
|
-
if ::String === node
|
|
210
|
-
node = Arel.sql(node)
|
|
211
|
-
end
|
|
212
|
-
Arel::Nodes::Grouping.new(node)
|
|
213
|
-
end
|
|
214
|
-
|
|
215
208
|
def extract_node_value(node)
|
|
216
209
|
if node.respond_to?(:value_before_type_cast)
|
|
217
210
|
node.value_before_type_cast
|