activerecord 7.1.6 → 7.2.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 +839 -2248
- data/README.rdoc +16 -16
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +15 -8
- data/lib/active_record/associations/belongs_to_association.rb +31 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/collection_association.rb +16 -8
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +7 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +2 -1
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +59 -292
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +23 -55
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +5 -25
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods.rb +51 -60
- data/lib/active_record/attributes.rb +93 -68
- data/lib/active_record/autosave_association.rb +25 -32
- data/lib/active_record/base.rb +4 -5
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +294 -72
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -75
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +6 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +18 -6
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -62
- data/lib/active_record/connection_adapters/abstract_adapter.rb +46 -44
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +53 -15
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +6 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +19 -18
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -23
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +30 -8
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +16 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +36 -26
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +133 -78
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +15 -15
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +68 -49
- data/lib/active_record/core.rb +112 -44
- data/lib/active_record/counter_cache.rb +19 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +19 -4
- data/lib/active_record/database_configurations/hash_config.rb +38 -34
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +42 -18
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +4 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +25 -5
- data/lib/active_record/encryption/encryptor.rb +35 -19
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/enum.rb +31 -13
- data/lib/active_record/errors.rb +49 -23
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +8 -4
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +18 -15
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +7 -6
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +5 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +87 -77
- data/lib/active_record/model_schema.rb +31 -68
- data/lib/active_record/nested_attributes.rb +11 -3
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +30 -352
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +19 -0
- data/lib/active_record/querying.rb +25 -13
- data/lib/active_record/railtie.rb +39 -57
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +42 -44
- data/lib/active_record/reflection.rb +98 -36
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +14 -8
- data/lib/active_record/relation/calculations.rb +127 -89
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +26 -12
- data/lib/active_record/relation/merger.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +238 -65
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +15 -21
- data/lib/active_record/relation.rb +508 -74
- data/lib/active_record/result.rb +31 -44
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +48 -20
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/scoping/named.rb +1 -0
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +27 -7
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/table_metadata.rb +1 -10
- data/lib/active_record/tasks/database_tasks.rb +69 -41
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +8 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +86 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +73 -15
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +15 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +148 -39
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +3 -1
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +2 -0
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +0 -6
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +4 -3
- data/lib/arel/nodes/sql_literal.rb +7 -0
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +7 -3
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +31 -16
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +16 -10
|
@@ -72,10 +72,26 @@ module ActiveRecord
|
|
|
72
72
|
# # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
|
|
73
73
|
# # INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
|
|
74
74
|
# # WHERE "authors"."id" IS NOT NULL AND "comments"."id" IS NOT NULL
|
|
75
|
+
#
|
|
76
|
+
# You can define join type in the scope and +associated+ will not use `JOIN` by default.
|
|
77
|
+
#
|
|
78
|
+
# Post.left_joins(:author).where.associated(:author)
|
|
79
|
+
# # SELECT "posts".* FROM "posts"
|
|
80
|
+
# # LEFT OUTER JOIN "authors" "authors"."id" = "posts"."author_id"
|
|
81
|
+
# # WHERE "authors"."id" IS NOT NULL
|
|
82
|
+
#
|
|
83
|
+
# Post.left_joins(:comments).where.associated(:author)
|
|
84
|
+
# # SELECT "posts".* FROM "posts"
|
|
85
|
+
# # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
|
|
86
|
+
# # LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id"
|
|
87
|
+
# # WHERE "author"."id" IS NOT NULL
|
|
75
88
|
def associated(*associations)
|
|
76
89
|
associations.each do |association|
|
|
77
90
|
reflection = scope_association_reflection(association)
|
|
78
|
-
@scope.
|
|
91
|
+
unless @scope.joins_values.include?(reflection.name) || @scope.left_outer_joins_values.include?(reflection.name)
|
|
92
|
+
@scope.joins!(association)
|
|
93
|
+
end
|
|
94
|
+
|
|
79
95
|
if reflection.options[:class_name]
|
|
80
96
|
self.not(association => { reflection.association_primary_key => nil })
|
|
81
97
|
else
|
|
@@ -157,7 +173,7 @@ module ActiveRecord
|
|
|
157
173
|
end # end
|
|
158
174
|
|
|
159
175
|
def #{method_name}=(value) # def includes_values=(value)
|
|
160
|
-
|
|
176
|
+
assert_modifiable! # assert_modifiable!
|
|
161
177
|
@values[:#{name}] = value # @values[:includes] = value
|
|
162
178
|
end # end
|
|
163
179
|
CODE
|
|
@@ -419,6 +435,17 @@ module ActiveRecord
|
|
|
419
435
|
# # )
|
|
420
436
|
# # SELECT * FROM posts
|
|
421
437
|
#
|
|
438
|
+
# You can also pass an array of sub-queries to be joined in a +UNION ALL+.
|
|
439
|
+
#
|
|
440
|
+
# Post.with(posts_with_tags_or_comments: [Post.where("tags_count > ?", 0), Post.where("comments_count > ?", 0)])
|
|
441
|
+
# # => ActiveRecord::Relation
|
|
442
|
+
# # WITH posts_with_tags_or_comments AS (
|
|
443
|
+
# # (SELECT * FROM posts WHERE (tags_count > 0))
|
|
444
|
+
# # UNION ALL
|
|
445
|
+
# # (SELECT * FROM posts WHERE (comments_count > 0))
|
|
446
|
+
# # )
|
|
447
|
+
# # SELECT * FROM posts
|
|
448
|
+
#
|
|
422
449
|
# Once you define Common Table Expression you can use custom +FROM+ value or +JOIN+ to reference it.
|
|
423
450
|
#
|
|
424
451
|
# Post.with(posts_with_tags: Post.where("tags_count > ?", 0)).from("posts_with_tags AS posts")
|
|
@@ -457,13 +484,40 @@ module ActiveRecord
|
|
|
457
484
|
# .with(posts_with_comments: Post.where("comments_count > ?", 0))
|
|
458
485
|
# .with(posts_with_tags: Post.where("tags_count > ?", 0))
|
|
459
486
|
def with(*args)
|
|
487
|
+
raise ArgumentError, "ActiveRecord::Relation#with does not accept a block" if block_given?
|
|
460
488
|
check_if_method_has_arguments!(__callee__, args)
|
|
461
489
|
spawn.with!(*args)
|
|
462
490
|
end
|
|
463
491
|
|
|
464
492
|
# Like #with, but modifies relation in place.
|
|
465
493
|
def with!(*args) # :nodoc:
|
|
466
|
-
|
|
494
|
+
args = process_with_args(args)
|
|
495
|
+
self.with_values |= args
|
|
496
|
+
self
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
# Add a recursive Common Table Expression (CTE) that you can then reference within another SELECT statement.
|
|
500
|
+
#
|
|
501
|
+
# 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
|
+
# # => ActiveRecord::Relation
|
|
503
|
+
# # WITH post_and_replies AS (
|
|
504
|
+
# # (SELECT * FROM posts WHERE id = 42)
|
|
505
|
+
# # UNION ALL
|
|
506
|
+
# # (SELECT * FROM posts JOIN post_and_replies ON posts.in_reply_to_id = post_and_replies.id)
|
|
507
|
+
# # )
|
|
508
|
+
# # SELECT * FROM posts
|
|
509
|
+
#
|
|
510
|
+
# See `#with` for more information.
|
|
511
|
+
def with_recursive(*args)
|
|
512
|
+
check_if_method_has_arguments!(__callee__, args)
|
|
513
|
+
spawn.with_recursive!(*args)
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
# Like #with_recursive but modifies the relation in place.
|
|
517
|
+
def with_recursive!(*args) # :nodoc:
|
|
518
|
+
args = process_with_args(args)
|
|
519
|
+
self.with_values |= args
|
|
520
|
+
@with_is_recursive = true
|
|
467
521
|
self
|
|
468
522
|
end
|
|
469
523
|
|
|
@@ -606,7 +660,8 @@ module ActiveRecord
|
|
|
606
660
|
self
|
|
607
661
|
end
|
|
608
662
|
|
|
609
|
-
#
|
|
663
|
+
# Applies an <tt>ORDER BY</tt> clause based on a given +column+,
|
|
664
|
+
# ordered and filtered by a specific set of +values+.
|
|
610
665
|
#
|
|
611
666
|
# User.in_order_of(:id, [1, 5, 3])
|
|
612
667
|
# # SELECT "users".* FROM "users"
|
|
@@ -617,8 +672,34 @@ module ActiveRecord
|
|
|
617
672
|
# # WHEN "users"."id" = 3 THEN 3
|
|
618
673
|
# # END ASC
|
|
619
674
|
#
|
|
675
|
+
# +column+ can point to an enum column; the actual query generated may be different depending
|
|
676
|
+
# on the database adapter and the column definition.
|
|
677
|
+
#
|
|
678
|
+
# class Conversation < ActiveRecord::Base
|
|
679
|
+
# enum :status, [ :active, :archived ]
|
|
680
|
+
# end
|
|
681
|
+
#
|
|
682
|
+
# Conversation.in_order_of(:status, [:archived, :active])
|
|
683
|
+
# # SELECT "conversations".* FROM "conversations"
|
|
684
|
+
# # WHERE "conversations"."status" IN (1, 0)
|
|
685
|
+
# # ORDER BY CASE
|
|
686
|
+
# # WHEN "conversations"."status" = 1 THEN 1
|
|
687
|
+
# # WHEN "conversations"."status" = 0 THEN 2
|
|
688
|
+
# # END ASC
|
|
689
|
+
#
|
|
690
|
+
# +values+ can also include +nil+.
|
|
691
|
+
#
|
|
692
|
+
# Conversation.in_order_of(:status, [nil, :archived, :active])
|
|
693
|
+
# # SELECT "conversations".* FROM "conversations"
|
|
694
|
+
# # WHERE ("conversations"."status" IN (1, 0) OR "conversations"."status" IS NULL)
|
|
695
|
+
# # ORDER BY CASE
|
|
696
|
+
# # WHEN "conversations"."status" IS NULL THEN 1
|
|
697
|
+
# # WHEN "conversations"."status" = 1 THEN 2
|
|
698
|
+
# # WHEN "conversations"."status" = 0 THEN 3
|
|
699
|
+
# # END ASC
|
|
700
|
+
#
|
|
620
701
|
def in_order_of(column, values)
|
|
621
|
-
klass.disallow_raw_sql!([column], permit:
|
|
702
|
+
klass.disallow_raw_sql!([column], permit: model.adapter_class.column_name_with_order_matcher)
|
|
622
703
|
return spawn.none! if values.empty?
|
|
623
704
|
|
|
624
705
|
references = column_references([column])
|
|
@@ -667,7 +748,7 @@ module ActiveRecord
|
|
|
667
748
|
VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
|
|
668
749
|
:limit, :offset, :joins, :left_outer_joins, :annotate,
|
|
669
750
|
:includes, :eager_load, :preload, :from, :readonly,
|
|
670
|
-
:having, :optimizer_hints])
|
|
751
|
+
:having, :optimizer_hints, :with])
|
|
671
752
|
|
|
672
753
|
# Removes an unwanted relation that is already defined on a chain of relations.
|
|
673
754
|
# This is useful when passing around chains of relations and would like to
|
|
@@ -717,7 +798,7 @@ module ActiveRecord
|
|
|
717
798
|
if !VALID_UNSCOPING_VALUES.include?(scope)
|
|
718
799
|
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
|
|
719
800
|
end
|
|
720
|
-
|
|
801
|
+
assert_modifiable!
|
|
721
802
|
@values.delete(scope)
|
|
722
803
|
when Hash
|
|
723
804
|
scope.each do |key, target_value|
|
|
@@ -1082,7 +1163,7 @@ module ActiveRecord
|
|
|
1082
1163
|
raise ArgumentError, "Relation passed to #or must be structurally compatible. Incompatible values: #{incompatible_values}"
|
|
1083
1164
|
end
|
|
1084
1165
|
|
|
1085
|
-
self.where_clause =
|
|
1166
|
+
self.where_clause = where_clause.or(other.where_clause)
|
|
1086
1167
|
self.having_clause = having_clause.or(other.having_clause)
|
|
1087
1168
|
self.references_values |= other.references_values
|
|
1088
1169
|
|
|
@@ -1198,13 +1279,13 @@ module ActiveRecord
|
|
|
1198
1279
|
#
|
|
1199
1280
|
# users = User.readonly
|
|
1200
1281
|
# users.first.save
|
|
1201
|
-
# => ActiveRecord::ReadOnlyRecord: User is marked as readonly
|
|
1282
|
+
# # => ActiveRecord::ReadOnlyRecord: User is marked as readonly
|
|
1202
1283
|
#
|
|
1203
1284
|
# To make a readonly relation writable, pass +false+.
|
|
1204
1285
|
#
|
|
1205
1286
|
# users.readonly(false)
|
|
1206
1287
|
# users.first.save
|
|
1207
|
-
# => true
|
|
1288
|
+
# # => true
|
|
1208
1289
|
def readonly(value = true)
|
|
1209
1290
|
spawn.readonly!(value)
|
|
1210
1291
|
end
|
|
@@ -1219,7 +1300,7 @@ module ActiveRecord
|
|
|
1219
1300
|
#
|
|
1220
1301
|
# user = User.strict_loading.first
|
|
1221
1302
|
# user.comments.to_a
|
|
1222
|
-
# => ActiveRecord::StrictLoadingViolationError
|
|
1303
|
+
# # => ActiveRecord::StrictLoadingViolationError
|
|
1223
1304
|
def strict_loading(value = true)
|
|
1224
1305
|
spawn.strict_loading!(value)
|
|
1225
1306
|
end
|
|
@@ -1453,6 +1534,9 @@ module ActiveRecord
|
|
|
1453
1534
|
# Post.excluding(post_one, post_two)
|
|
1454
1535
|
# # SELECT "posts".* FROM "posts" WHERE "posts"."id" NOT IN (1, 2)
|
|
1455
1536
|
#
|
|
1537
|
+
# Post.excluding(Post.drafts)
|
|
1538
|
+
# # SELECT "posts".* FROM "posts" WHERE "posts"."id" NOT IN (3, 4, 5)
|
|
1539
|
+
#
|
|
1456
1540
|
# This can also be called on associations. As with the above example, either
|
|
1457
1541
|
# a single record of collection thereof may be specified:
|
|
1458
1542
|
#
|
|
@@ -1468,14 +1552,15 @@ module ActiveRecord
|
|
|
1468
1552
|
# is passed in) are not instances of the same model that the relation is
|
|
1469
1553
|
# scoping.
|
|
1470
1554
|
def excluding(*records)
|
|
1555
|
+
relations = records.extract! { |element| element.is_a?(Relation) }
|
|
1471
1556
|
records.flatten!(1)
|
|
1472
1557
|
records.compact!
|
|
1473
1558
|
|
|
1474
|
-
unless records.all?(klass)
|
|
1559
|
+
unless records.all?(klass) && relations.all? { |relation| relation.klass == klass }
|
|
1475
1560
|
raise ArgumentError, "You must only pass a single or collection of #{klass.name} objects to ##{__callee__}."
|
|
1476
1561
|
end
|
|
1477
1562
|
|
|
1478
|
-
spawn.excluding!(records)
|
|
1563
|
+
spawn.excluding!(records + relations.flat_map(&:ids))
|
|
1479
1564
|
end
|
|
1480
1565
|
alias :without :excluding
|
|
1481
1566
|
|
|
@@ -1487,7 +1572,7 @@ module ActiveRecord
|
|
|
1487
1572
|
|
|
1488
1573
|
# Returns the Arel object associated with the relation.
|
|
1489
1574
|
def arel(aliases = nil) # :nodoc:
|
|
1490
|
-
@arel ||= build_arel(aliases)
|
|
1575
|
+
@arel ||= with_connection { |c| build_arel(c, aliases) }
|
|
1491
1576
|
end
|
|
1492
1577
|
|
|
1493
1578
|
def construct_join_dependency(associations, join_type) # :nodoc:
|
|
@@ -1508,9 +1593,21 @@ module ActiveRecord
|
|
|
1508
1593
|
def build_where_clause(opts, rest = []) # :nodoc:
|
|
1509
1594
|
opts = sanitize_forbidden_attributes(opts)
|
|
1510
1595
|
|
|
1596
|
+
if opts.is_a?(Array)
|
|
1597
|
+
opts, *rest = opts
|
|
1598
|
+
end
|
|
1599
|
+
|
|
1511
1600
|
case opts
|
|
1512
|
-
when String
|
|
1513
|
-
|
|
1601
|
+
when String
|
|
1602
|
+
if rest.empty?
|
|
1603
|
+
parts = [Arel.sql(opts)]
|
|
1604
|
+
elsif rest.first.is_a?(Hash) && /:\w+/.match?(opts)
|
|
1605
|
+
parts = [build_named_bound_sql_literal(opts, rest.first)]
|
|
1606
|
+
elsif opts.include?("?")
|
|
1607
|
+
parts = [build_bound_sql_literal(opts, rest)]
|
|
1608
|
+
else
|
|
1609
|
+
parts = [klass.sanitize_sql(rest.empty? ? opts : [opts, *rest])]
|
|
1610
|
+
end
|
|
1514
1611
|
when Hash
|
|
1515
1612
|
opts = opts.transform_keys do |key|
|
|
1516
1613
|
if key.is_a?(Array)
|
|
@@ -1541,11 +1638,71 @@ module ActiveRecord
|
|
|
1541
1638
|
self
|
|
1542
1639
|
end
|
|
1543
1640
|
|
|
1641
|
+
protected
|
|
1642
|
+
def arel_columns(columns)
|
|
1643
|
+
columns.flat_map do |field|
|
|
1644
|
+
case field
|
|
1645
|
+
when Symbol
|
|
1646
|
+
arel_column(field.to_s) do |attr_name|
|
|
1647
|
+
adapter_class.quote_table_name(attr_name)
|
|
1648
|
+
end
|
|
1649
|
+
when String
|
|
1650
|
+
arel_column(field, &:itself)
|
|
1651
|
+
when Proc
|
|
1652
|
+
field.call
|
|
1653
|
+
when Hash
|
|
1654
|
+
arel_columns_from_hash(field)
|
|
1655
|
+
else
|
|
1656
|
+
field
|
|
1657
|
+
end
|
|
1658
|
+
end
|
|
1659
|
+
end
|
|
1660
|
+
|
|
1544
1661
|
private
|
|
1545
1662
|
def async
|
|
1546
1663
|
spawn.async!
|
|
1547
1664
|
end
|
|
1548
1665
|
|
|
1666
|
+
def build_named_bound_sql_literal(statement, values)
|
|
1667
|
+
bound_values = values.transform_values do |value|
|
|
1668
|
+
if ActiveRecord::Relation === value
|
|
1669
|
+
Arel.sql(value.to_sql)
|
|
1670
|
+
elsif value.respond_to?(:map) && !value.acts_like?(:string)
|
|
1671
|
+
values = value.map { |v| v.respond_to?(:id_for_database) ? v.id_for_database : v }
|
|
1672
|
+
values.empty? ? nil : values
|
|
1673
|
+
else
|
|
1674
|
+
value = value.id_for_database if value.respond_to?(:id_for_database)
|
|
1675
|
+
value
|
|
1676
|
+
end
|
|
1677
|
+
end
|
|
1678
|
+
|
|
1679
|
+
begin
|
|
1680
|
+
Arel::Nodes::BoundSqlLiteral.new("(#{statement})", nil, bound_values)
|
|
1681
|
+
rescue Arel::BindError => error
|
|
1682
|
+
raise ActiveRecord::PreparedStatementInvalid, error.message
|
|
1683
|
+
end
|
|
1684
|
+
end
|
|
1685
|
+
|
|
1686
|
+
def build_bound_sql_literal(statement, values)
|
|
1687
|
+
bound_values = values.map do |value|
|
|
1688
|
+
if ActiveRecord::Relation === value
|
|
1689
|
+
Arel.sql(value.to_sql)
|
|
1690
|
+
elsif value.respond_to?(:map) && !value.acts_like?(:string)
|
|
1691
|
+
values = value.map { |v| v.respond_to?(:id_for_database) ? v.id_for_database : v }
|
|
1692
|
+
values.empty? ? nil : values
|
|
1693
|
+
else
|
|
1694
|
+
value = value.id_for_database if value.respond_to?(:id_for_database)
|
|
1695
|
+
value
|
|
1696
|
+
end
|
|
1697
|
+
end
|
|
1698
|
+
|
|
1699
|
+
begin
|
|
1700
|
+
Arel::Nodes::BoundSqlLiteral.new("(#{statement})", bound_values, nil)
|
|
1701
|
+
rescue Arel::BindError => error
|
|
1702
|
+
raise ActiveRecord::PreparedStatementInvalid, error.message
|
|
1703
|
+
end
|
|
1704
|
+
end
|
|
1705
|
+
|
|
1549
1706
|
def lookup_table_klass_from_join_dependencies(table_name)
|
|
1550
1707
|
each_join_dependencies do |join|
|
|
1551
1708
|
return join.base_klass if table_name == join.table_name
|
|
@@ -1570,12 +1727,11 @@ module ActiveRecord
|
|
|
1570
1727
|
)
|
|
1571
1728
|
end
|
|
1572
1729
|
|
|
1573
|
-
def
|
|
1574
|
-
raise
|
|
1575
|
-
raise ImmutableRelation if defined?(@arel) && @arel
|
|
1730
|
+
def assert_modifiable!
|
|
1731
|
+
raise UnmodifiableRelation if @loaded || @arel
|
|
1576
1732
|
end
|
|
1577
1733
|
|
|
1578
|
-
def build_arel(aliases = nil)
|
|
1734
|
+
def build_arel(connection, aliases = nil)
|
|
1579
1735
|
arel = Arel::SelectManager.new(table)
|
|
1580
1736
|
|
|
1581
1737
|
build_joins(arel.join_sources, aliases)
|
|
@@ -1742,25 +1898,40 @@ module ActiveRecord
|
|
|
1742
1898
|
return if with_values.empty?
|
|
1743
1899
|
|
|
1744
1900
|
with_statements = with_values.map do |with_value|
|
|
1745
|
-
raise ArgumentError, "Unsupported argument type: #{with_value} #{with_value.class}" unless with_value.is_a?(Hash)
|
|
1746
|
-
|
|
1747
1901
|
build_with_value_from_hash(with_value)
|
|
1748
1902
|
end
|
|
1749
1903
|
|
|
1750
|
-
arel.with(with_statements)
|
|
1904
|
+
@with_is_recursive ? arel.with(:recursive, with_statements) : arel.with(with_statements)
|
|
1751
1905
|
end
|
|
1752
1906
|
|
|
1753
1907
|
def build_with_value_from_hash(hash)
|
|
1754
1908
|
hash.map do |name, value|
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1909
|
+
Arel::Nodes::TableAlias.new(build_with_expression_from_value(value), name)
|
|
1910
|
+
end
|
|
1911
|
+
end
|
|
1912
|
+
|
|
1913
|
+
def build_with_expression_from_value(value, nested = false)
|
|
1914
|
+
case value
|
|
1915
|
+
when Arel::Nodes::SqlLiteral then Arel::Nodes::Grouping.new(value)
|
|
1916
|
+
when ActiveRecord::Relation
|
|
1917
|
+
if nested
|
|
1918
|
+
value.arel.ast
|
|
1919
|
+
else
|
|
1920
|
+
value.arel
|
|
1921
|
+
end
|
|
1922
|
+
when Arel::SelectManager then value
|
|
1923
|
+
when Array
|
|
1924
|
+
return build_with_expression_from_value(value.first, false) if value.size == 1
|
|
1925
|
+
|
|
1926
|
+
parts = value.map do |query|
|
|
1927
|
+
build_with_expression_from_value(query, true)
|
|
1928
|
+
end
|
|
1929
|
+
|
|
1930
|
+
parts.reduce do |result, value|
|
|
1931
|
+
Arel::Nodes::UnionAll.new(result, value)
|
|
1932
|
+
end
|
|
1933
|
+
else
|
|
1934
|
+
raise ArgumentError, "Unsupported argument type: `#{value}` #{value.class}"
|
|
1764
1935
|
end
|
|
1765
1936
|
end
|
|
1766
1937
|
|
|
@@ -1772,31 +1943,14 @@ module ActiveRecord
|
|
|
1772
1943
|
).join_sources.first
|
|
1773
1944
|
end
|
|
1774
1945
|
|
|
1775
|
-
def arel_columns(columns)
|
|
1776
|
-
columns.flat_map do |field|
|
|
1777
|
-
case field
|
|
1778
|
-
when Symbol
|
|
1779
|
-
arel_column(field.to_s) do |attr_name|
|
|
1780
|
-
connection.quote_table_name(attr_name)
|
|
1781
|
-
end
|
|
1782
|
-
when String
|
|
1783
|
-
arel_column(field, &:itself)
|
|
1784
|
-
when Proc
|
|
1785
|
-
field.call
|
|
1786
|
-
else
|
|
1787
|
-
field
|
|
1788
|
-
end
|
|
1789
|
-
end
|
|
1790
|
-
end
|
|
1791
|
-
|
|
1792
1946
|
def arel_column(field)
|
|
1793
1947
|
field = klass.attribute_aliases[field] || field
|
|
1794
1948
|
from = from_clause.name || from_clause.value
|
|
1795
1949
|
|
|
1796
1950
|
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
|
1797
1951
|
table[field]
|
|
1798
|
-
elsif
|
|
1799
|
-
table,
|
|
1952
|
+
elsif /\A(?<table>(?:\w+\.)?\w+)\.(?<column>\w+)\z/ =~ field
|
|
1953
|
+
self.references_values |= [Arel.sql(table, retryable: true)]
|
|
1800
1954
|
predicate_builder.resolve_arel_attribute(table, column) do
|
|
1801
1955
|
lookup_table_klass_from_join_dependencies(table)
|
|
1802
1956
|
end
|
|
@@ -1807,7 +1961,7 @@ module ActiveRecord
|
|
|
1807
1961
|
|
|
1808
1962
|
def table_name_matches?(from)
|
|
1809
1963
|
table_name = Regexp.escape(table.name)
|
|
1810
|
-
quoted_table_name = Regexp.escape(
|
|
1964
|
+
quoted_table_name = Regexp.escape(adapter_class.quote_table_name(table.name))
|
|
1811
1965
|
/(?:\A|(?<!FROM)\s)(?:\b#{table_name}\b|#{quoted_table_name})(?!\.)/i.match?(from.to_s)
|
|
1812
1966
|
end
|
|
1813
1967
|
|
|
@@ -1863,7 +2017,9 @@ module ActiveRecord
|
|
|
1863
2017
|
args.each do |arg|
|
|
1864
2018
|
next unless arg.is_a?(Hash)
|
|
1865
2019
|
arg.each do |_key, value|
|
|
1866
|
-
|
|
2020
|
+
if value.is_a?(Hash)
|
|
2021
|
+
validate_order_args([value])
|
|
2022
|
+
elsif VALID_DIRECTIONS.exclude?(value)
|
|
1867
2023
|
raise ArgumentError,
|
|
1868
2024
|
"Direction \"#{value}\" is invalid. Valid directions are: #{VALID_DIRECTIONS.to_a.inspect}"
|
|
1869
2025
|
end
|
|
@@ -1871,10 +2027,14 @@ module ActiveRecord
|
|
|
1871
2027
|
end
|
|
1872
2028
|
end
|
|
1873
2029
|
|
|
2030
|
+
def flattened_args(args)
|
|
2031
|
+
args.flat_map { |e| (e.is_a?(Hash) || e.is_a?(Array)) ? flattened_args(e.to_a) : e }
|
|
2032
|
+
end
|
|
2033
|
+
|
|
1874
2034
|
def preprocess_order_args(order_args)
|
|
1875
2035
|
@klass.disallow_raw_sql!(
|
|
1876
|
-
order_args
|
|
1877
|
-
permit:
|
|
2036
|
+
flattened_args(order_args),
|
|
2037
|
+
permit: model.adapter_class.column_name_with_order_matcher
|
|
1878
2038
|
)
|
|
1879
2039
|
|
|
1880
2040
|
validate_order_args(order_args)
|
|
@@ -1888,14 +2048,20 @@ module ActiveRecord
|
|
|
1888
2048
|
when Symbol
|
|
1889
2049
|
order_column(arg.to_s).asc
|
|
1890
2050
|
when Hash
|
|
1891
|
-
arg.map
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
2051
|
+
arg.map do |key, value|
|
|
2052
|
+
if value.is_a?(Hash)
|
|
2053
|
+
value.map do |field, dir|
|
|
2054
|
+
order_column([key.to_s, field.to_s].join(".")).public_send(dir.downcase)
|
|
2055
|
+
end
|
|
1895
2056
|
else
|
|
1896
|
-
|
|
2057
|
+
case key
|
|
2058
|
+
when Arel::Nodes::SqlLiteral, Arel::Nodes::Node, Arel::Attribute
|
|
2059
|
+
key.public_send(value.downcase)
|
|
2060
|
+
else
|
|
2061
|
+
order_column(key.to_s).public_send(value.downcase)
|
|
2062
|
+
end
|
|
1897
2063
|
end
|
|
1898
|
-
|
|
2064
|
+
end
|
|
1899
2065
|
else
|
|
1900
2066
|
arg
|
|
1901
2067
|
end
|
|
@@ -1930,7 +2096,7 @@ module ActiveRecord
|
|
|
1930
2096
|
arg.expr.relation.name
|
|
1931
2097
|
end
|
|
1932
2098
|
end
|
|
1933
|
-
end.
|
|
2099
|
+
end.filter_map { |ref| Arel.sql(ref, retryable: true) if ref }
|
|
1934
2100
|
end
|
|
1935
2101
|
|
|
1936
2102
|
def extract_table_name_from(string)
|
|
@@ -1942,7 +2108,7 @@ module ActiveRecord
|
|
|
1942
2108
|
if attr_name == "count" && !group_values.empty?
|
|
1943
2109
|
table[attr_name]
|
|
1944
2110
|
else
|
|
1945
|
-
Arel.sql(
|
|
2111
|
+
Arel.sql(adapter_class.quote_table_name(attr_name), retryable: true)
|
|
1946
2112
|
end
|
|
1947
2113
|
end
|
|
1948
2114
|
end
|
|
@@ -2010,14 +2176,14 @@ module ActiveRecord
|
|
|
2010
2176
|
def process_select_args(fields)
|
|
2011
2177
|
fields.flat_map do |field|
|
|
2012
2178
|
if field.is_a?(Hash)
|
|
2013
|
-
|
|
2179
|
+
arel_columns_from_hash(field)
|
|
2014
2180
|
else
|
|
2015
2181
|
field
|
|
2016
2182
|
end
|
|
2017
2183
|
end
|
|
2018
2184
|
end
|
|
2019
2185
|
|
|
2020
|
-
def
|
|
2186
|
+
def arel_columns_from_hash(fields)
|
|
2021
2187
|
fields.flat_map do |key, columns_aliases|
|
|
2022
2188
|
case columns_aliases
|
|
2023
2189
|
when Hash
|
|
@@ -2042,6 +2208,13 @@ module ActiveRecord
|
|
|
2042
2208
|
end
|
|
2043
2209
|
end
|
|
2044
2210
|
|
|
2211
|
+
def process_with_args(args)
|
|
2212
|
+
args.flat_map do |arg|
|
|
2213
|
+
raise ArgumentError, "Unsupported argument type: #{arg} #{arg.class}" unless arg.is_a?(Hash)
|
|
2214
|
+
arg.map { |k, v| { k => v } }
|
|
2215
|
+
end
|
|
2216
|
+
end
|
|
2217
|
+
|
|
2045
2218
|
STRUCTURAL_VALUE_METHODS = (
|
|
2046
2219
|
Relation::VALUE_METHODS -
|
|
2047
2220
|
[:extending, :where, :having, :unscope, :references, :annotate, :optimizer_hints]
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
module ActiveRecord
|
|
4
4
|
class Relation
|
|
5
5
|
module RecordFetchWarning
|
|
6
|
+
# Deprecated: subscribe to sql.active_record notifications and
|
|
7
|
+
# access the row count field to detect large result set sizes.
|
|
8
|
+
#
|
|
6
9
|
# When this module is prepended to ActiveRecord::Relation and
|
|
7
10
|
# +config.active_record.warn_on_records_fetched_greater_than+ is
|
|
8
11
|
# set to an integer, if the number of records a query returns is
|
|
@@ -41,26 +41,10 @@ module ActiveRecord
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def merge!(other, *rest) # :nodoc:
|
|
44
|
-
options = rest.extract_options!
|
|
45
|
-
|
|
46
|
-
if options.key?(:rewhere)
|
|
47
|
-
if options[:rewhere]
|
|
48
|
-
ActiveRecord.deprecator.warn(<<-MSG.squish)
|
|
49
|
-
Specifying `Relation#merge(rewhere: true)` is deprecated, as that has now been
|
|
50
|
-
the default since Rails 7.0. Setting the rewhere option will error in Rails 7.2
|
|
51
|
-
MSG
|
|
52
|
-
else
|
|
53
|
-
ActiveRecord.deprecator.warn(<<-MSG.squish)
|
|
54
|
-
`Relation#merge(rewhere: false)` is deprecated without replacement,
|
|
55
|
-
and will be removed in Rails 7.2
|
|
56
|
-
MSG
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
44
|
if other.is_a?(Hash)
|
|
61
|
-
Relation::HashMerger.new(self, other
|
|
45
|
+
Relation::HashMerger.new(self, other).merge
|
|
62
46
|
elsif other.is_a?(Relation)
|
|
63
|
-
Relation::Merger.new(self, other
|
|
47
|
+
Relation::Merger.new(self, other).merge
|
|
64
48
|
elsif other.respond_to?(:to_proc)
|
|
65
49
|
instance_exec(&other)
|
|
66
50
|
else
|
|
@@ -23,12 +23,8 @@ module ActiveRecord
|
|
|
23
23
|
WhereClause.new(predicates | other.predicates)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
def merge(other
|
|
27
|
-
predicates =
|
|
28
|
-
except_predicates(other.extract_attributes)
|
|
29
|
-
else
|
|
30
|
-
predicates_unreferenced_by(other)
|
|
31
|
-
end
|
|
26
|
+
def merge(other)
|
|
27
|
+
predicates = except_predicates(other.extract_attributes)
|
|
32
28
|
|
|
33
29
|
WhereClause.new(predicates | other.predicates)
|
|
34
30
|
end
|
|
@@ -51,7 +47,11 @@ module ActiveRecord
|
|
|
51
47
|
right = right.ast
|
|
52
48
|
right = right.expr if right.is_a?(Arel::Nodes::Grouping)
|
|
53
49
|
|
|
54
|
-
or_clause = Arel::Nodes::Or
|
|
50
|
+
or_clause = if left.is_a?(Arel::Nodes::Or)
|
|
51
|
+
Arel::Nodes::Or.new(left.children + [right])
|
|
52
|
+
else
|
|
53
|
+
Arel::Nodes::Or.new([left, right])
|
|
54
|
+
end
|
|
55
55
|
|
|
56
56
|
common.predicates << Arel::Nodes::Grouping.new(or_clause)
|
|
57
57
|
common
|
|
@@ -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
|
|
|
@@ -156,18 +160,6 @@ module ActiveRecord
|
|
|
156
160
|
equalities
|
|
157
161
|
end
|
|
158
162
|
|
|
159
|
-
def predicates_unreferenced_by(other)
|
|
160
|
-
referenced_columns = other.referenced_columns
|
|
161
|
-
|
|
162
|
-
predicates.reject do |node|
|
|
163
|
-
attr = extract_attribute(node) || begin
|
|
164
|
-
node.left if equality_node?(node) && node.left.is_a?(Arel::Predications)
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
attr && referenced_columns[attr]
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
|
|
171
163
|
def equality_node?(node)
|
|
172
164
|
!node.is_a?(String) && node.equality?
|
|
173
165
|
end
|
|
@@ -184,6 +176,8 @@ module ActiveRecord
|
|
|
184
176
|
end
|
|
185
177
|
|
|
186
178
|
def except_predicates(columns)
|
|
179
|
+
return predicates if columns.empty?
|
|
180
|
+
|
|
187
181
|
attrs = columns.extract! { |node| node.is_a?(Arel::Attribute) }
|
|
188
182
|
non_attrs = columns.extract! { |node| node.is_a?(Arel::Predications) }
|
|
189
183
|
|