activerecord 6.0.3 → 6.1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +779 -705
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/active_record.rb +7 -14
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +22 -14
- data/lib/active_record/associations.rb +114 -11
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +40 -29
- data/lib/active_record/associations/association_scope.rb +17 -15
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +9 -3
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +19 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -5
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -2
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency.rb +72 -50
- data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/preloader.rb +11 -5
- data/lib/active_record/associations/preloader/association.rb +51 -25
- data/lib/active_record/associations/preloader/through_association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/attribute_assignment.rb +10 -8
- data/lib/active_record/attribute_methods.rb +52 -48
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +1 -11
- data/lib/active_record/attribute_methods/primary_key.rb +6 -2
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -11
- data/lib/active_record/attribute_methods/serialization.rb +4 -4
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
- data/lib/active_record/attribute_methods/write.rb +12 -20
- data/lib/active_record/attributes.rb +27 -7
- data/lib/active_record/autosave_association.rb +57 -40
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +32 -22
- data/lib/active_record/coders/yaml_column.rb +1 -1
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +186 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
- data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +112 -27
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
- data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -24
- data/lib/active_record/connection_adapters/abstract_adapter.rb +36 -69
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +129 -88
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -25
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +33 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +11 -7
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +13 -54
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
- data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +31 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +37 -4
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +49 -50
- data/lib/active_record/connection_handling.rb +210 -71
- data/lib/active_record/core.rb +220 -55
- data/lib/active_record/database_configurations.rb +124 -85
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -40
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/enum.rb +27 -10
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -4
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -2
- data/lib/active_record/fixtures.rb +54 -8
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +33 -6
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +15 -4
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +22 -16
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +26 -8
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/migration.rb +113 -83
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +67 -17
- data/lib/active_record/model_schema.rb +88 -13
- data/lib/active_record/nested_attributes.rb +2 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +50 -45
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +11 -6
- data/lib/active_record/railtie.rb +64 -44
- data/lib/active_record/railties/databases.rake +253 -98
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +70 -57
- data/lib/active_record/relation.rb +96 -67
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/calculations.rb +101 -44
- data/lib/active_record/relation/delegation.rb +2 -1
- data/lib/active_record/relation/finder_methods.rb +45 -15
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +27 -25
- data/lib/active_record/relation/predicate_builder.rb +57 -33
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +330 -195
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +6 -5
- data/lib/active_record/relation/where_clause.rb +104 -57
- data/lib/active_record/result.rb +41 -33
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +0 -4
- data/lib/active_record/scoping/named.rb +6 -17
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +2 -2
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +36 -52
- data/lib/active_record/tasks/database_tasks.rb +139 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +37 -16
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +15 -64
- data/lib/active_record/type.rb +8 -1
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/arel.rb +5 -13
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/predications.rb +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel/visitors/dot.rb +14 -2
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -4
- data/lib/arel/visitors/to_sql.rb +89 -78
- data/lib/rails/generators/active_record/migration.rb +6 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +27 -28
- data/lib/active_record/advisory_lock_base.rb +0 -18
- data/lib/active_record/attribute_decorators.rb +0 -88
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -203
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -156
- data/lib/arel/visitors/oracle.rb +0 -158
- data/lib/arel/visitors/oracle12.rb +0 -65
- data/lib/arel/visitors/where_sql.rb +0 -22
@@ -9,7 +9,7 @@ module ActiveRecord
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def queries
|
12
|
-
[associated_table.
|
12
|
+
[associated_table.join_foreign_key => ids]
|
13
13
|
end
|
14
14
|
|
15
15
|
private
|
@@ -27,7 +27,7 @@ module ActiveRecord
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def primary_key
|
30
|
-
associated_table.
|
30
|
+
associated_table.join_primary_key
|
31
31
|
end
|
32
32
|
|
33
33
|
def convert_to_id(value)
|
@@ -11,8 +11,8 @@ module ActiveRecord
|
|
11
11
|
def queries
|
12
12
|
type_to_ids_mapping.map do |type, ids|
|
13
13
|
{
|
14
|
-
associated_table.
|
15
|
-
associated_table.
|
14
|
+
associated_table.join_foreign_type => type,
|
15
|
+
associated_table.join_foreign_key => ids
|
16
16
|
}
|
17
17
|
end
|
18
18
|
end
|
@@ -28,7 +28,7 @@ module ActiveRecord
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def primary_key(value)
|
31
|
-
associated_table.
|
31
|
+
associated_table.join_primary_key(klass(value))
|
32
32
|
end
|
33
33
|
|
34
34
|
def klass(value)
|
@@ -3,8 +3,8 @@
|
|
3
3
|
require "active_record/relation/from_clause"
|
4
4
|
require "active_record/relation/query_attribute"
|
5
5
|
require "active_record/relation/where_clause"
|
6
|
-
require "active_record/relation/where_clause_factory"
|
7
6
|
require "active_model/forbidden_attributes_protection"
|
7
|
+
require "active_support/core_ext/array/wrap"
|
8
8
|
|
9
9
|
module ActiveRecord
|
10
10
|
module QueryMethods
|
@@ -15,8 +15,6 @@ module ActiveRecord
|
|
15
15
|
# WhereChain objects act as placeholder for queries in which #where does not have any parameter.
|
16
16
|
# In this case, #where must be chained with #not to return a new relation.
|
17
17
|
class WhereChain
|
18
|
-
include ActiveModel::ForbiddenAttributesProtection
|
19
|
-
|
20
18
|
def initialize(scope)
|
21
19
|
@scope = scope
|
22
20
|
end
|
@@ -41,64 +39,70 @@ module ActiveRecord
|
|
41
39
|
#
|
42
40
|
# User.where.not(name: %w(Ko1 Nobu))
|
43
41
|
# # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
|
42
|
+
#
|
43
|
+
# User.where.not(name: "Jon", role: "admin")
|
44
|
+
# # SELECT * FROM users WHERE NOT (name == 'Jon' AND role == 'admin')
|
44
45
|
def not(opts, *rest)
|
45
|
-
|
46
|
+
where_clause = @scope.send(:build_where_clause, opts, rest)
|
46
47
|
|
47
|
-
where_clause
|
48
|
-
|
49
|
-
@scope.references!(PredicateBuilder.references(opts)) if Hash === opts
|
50
|
-
|
51
|
-
if not_behaves_as_nor?(opts)
|
52
|
-
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
53
|
-
NOT conditions will no longer behave as NOR in Rails 6.1.
|
54
|
-
To continue using NOR conditions, NOT each condition individually
|
55
|
-
(`#{
|
56
|
-
opts.flat_map { |key, value|
|
57
|
-
if value.is_a?(Hash) && value.size > 1
|
58
|
-
value.map { |k, v| ".where.not(#{key.inspect} => { #{k.inspect} => ... })" }
|
59
|
-
else
|
60
|
-
".where.not(#{key.inspect} => ...)"
|
61
|
-
end
|
62
|
-
}.join
|
63
|
-
}`).
|
64
|
-
MSG
|
65
|
-
@scope.where_clause += where_clause.invert(:nor)
|
66
|
-
else
|
67
|
-
@scope.where_clause += where_clause.invert
|
68
|
-
end
|
48
|
+
@scope.where_clause += where_clause.invert
|
69
49
|
|
70
50
|
@scope
|
71
51
|
end
|
72
52
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
53
|
+
# Returns a new relation with left outer joins and where clause to identify
|
54
|
+
# missing relations.
|
55
|
+
#
|
56
|
+
# For example, posts that are missing a related author:
|
57
|
+
#
|
58
|
+
# Post.where.missing(:author)
|
59
|
+
# # SELECT "posts".* FROM "posts"
|
60
|
+
# # LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
|
61
|
+
# # WHERE "authors"."id" IS NULL
|
62
|
+
#
|
63
|
+
# Additionally, multiple relations can be combined. This will return posts
|
64
|
+
# that are missing both an author and any comments:
|
65
|
+
#
|
66
|
+
# Post.where.missing(:author, :comments)
|
67
|
+
# # SELECT "posts".* FROM "posts"
|
68
|
+
# # LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
|
69
|
+
# # LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id"
|
70
|
+
# # WHERE "authors"."id" IS NULL AND "comments"."id" IS NULL
|
71
|
+
def missing(*args)
|
72
|
+
args.each do |arg|
|
73
|
+
reflection = @scope.klass._reflect_on_association(arg)
|
74
|
+
opts = { reflection.table_name => { reflection.association_primary_key => nil } }
|
75
|
+
@scope.left_outer_joins!(arg)
|
76
|
+
@scope.where!(opts)
|
79
77
|
end
|
78
|
+
|
79
|
+
@scope
|
80
|
+
end
|
80
81
|
end
|
81
82
|
|
82
83
|
FROZEN_EMPTY_ARRAY = [].freeze
|
83
84
|
FROZEN_EMPTY_HASH = {}.freeze
|
84
85
|
|
85
86
|
Relation::VALUE_METHODS.each do |name|
|
86
|
-
method_name =
|
87
|
+
method_name, default =
|
87
88
|
case name
|
88
|
-
when *Relation::MULTI_VALUE_METHODS
|
89
|
-
|
90
|
-
when *Relation::
|
89
|
+
when *Relation::MULTI_VALUE_METHODS
|
90
|
+
["#{name}_values", "FROZEN_EMPTY_ARRAY"]
|
91
|
+
when *Relation::SINGLE_VALUE_METHODS
|
92
|
+
["#{name}_value", name == :create_with ? "FROZEN_EMPTY_HASH" : "nil"]
|
93
|
+
when *Relation::CLAUSE_METHODS
|
94
|
+
["#{name}_clause", name == :from ? "Relation::FromClause.empty" : "Relation::WhereClause.empty"]
|
91
95
|
end
|
96
|
+
|
92
97
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
93
|
-
def #{method_name}
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
end # end
|
98
|
+
def #{method_name} # def includes_values
|
99
|
+
@values.fetch(:#{name}, #{default}) # @values.fetch(:includes, FROZEN_EMPTY_ARRAY)
|
100
|
+
end # end
|
101
|
+
|
102
|
+
def #{method_name}=(value) # def includes_values=(value)
|
103
|
+
assert_mutability! # assert_mutability!
|
104
|
+
@values[:#{name}] = value # @values[:includes] = value
|
105
|
+
end # end
|
102
106
|
CODE
|
103
107
|
end
|
104
108
|
|
@@ -149,9 +153,6 @@ module ActiveRecord
|
|
149
153
|
end
|
150
154
|
|
151
155
|
def includes!(*args) # :nodoc:
|
152
|
-
args.reject!(&:blank?)
|
153
|
-
args.flatten!
|
154
|
-
|
155
156
|
self.includes_values |= args
|
156
157
|
self
|
157
158
|
end
|
@@ -215,9 +216,6 @@ module ActiveRecord
|
|
215
216
|
end
|
216
217
|
|
217
218
|
def references!(*table_names) # :nodoc:
|
218
|
-
table_names.flatten!
|
219
|
-
table_names.map!(&:to_s)
|
220
|
-
|
221
219
|
self.references_values |= table_names
|
222
220
|
self
|
223
221
|
end
|
@@ -271,14 +269,12 @@ module ActiveRecord
|
|
271
269
|
return super()
|
272
270
|
end
|
273
271
|
|
274
|
-
|
272
|
+
check_if_method_has_arguments!(:select, fields, "Call `select' with at least one field.")
|
275
273
|
spawn._select!(*fields)
|
276
274
|
end
|
277
275
|
|
278
276
|
def _select!(*fields) # :nodoc:
|
279
|
-
fields
|
280
|
-
fields.flatten!
|
281
|
-
self.select_values += fields
|
277
|
+
self.select_values |= fields
|
282
278
|
self
|
283
279
|
end
|
284
280
|
|
@@ -329,9 +325,7 @@ module ActiveRecord
|
|
329
325
|
end
|
330
326
|
|
331
327
|
def group!(*args) # :nodoc:
|
332
|
-
args
|
333
|
-
|
334
|
-
self.group_values |= args
|
328
|
+
self.group_values += args
|
335
329
|
self
|
336
330
|
end
|
337
331
|
|
@@ -355,15 +349,16 @@ module ActiveRecord
|
|
355
349
|
# User.order('name DESC, email')
|
356
350
|
# # SELECT "users".* FROM "users" ORDER BY name DESC, email
|
357
351
|
def order(*args)
|
358
|
-
check_if_method_has_arguments!(:order, args)
|
352
|
+
check_if_method_has_arguments!(:order, args) do
|
353
|
+
sanitize_order_arguments(args)
|
354
|
+
end
|
359
355
|
spawn.order!(*args)
|
360
356
|
end
|
361
357
|
|
362
358
|
# Same as #order but operates on relation in-place instead of copying.
|
363
359
|
def order!(*args) # :nodoc:
|
364
|
-
preprocess_order_args(args)
|
365
|
-
|
366
|
-
self.order_values += args
|
360
|
+
preprocess_order_args(args) unless args.empty?
|
361
|
+
self.order_values |= args
|
367
362
|
self
|
368
363
|
end
|
369
364
|
|
@@ -377,14 +372,16 @@ module ActiveRecord
|
|
377
372
|
#
|
378
373
|
# generates a query with 'ORDER BY id ASC, name ASC'.
|
379
374
|
def reorder(*args)
|
380
|
-
check_if_method_has_arguments!(:reorder, args)
|
375
|
+
check_if_method_has_arguments!(:reorder, args) do
|
376
|
+
sanitize_order_arguments(args) unless args.all?(&:blank?)
|
377
|
+
end
|
381
378
|
spawn.reorder!(*args)
|
382
379
|
end
|
383
380
|
|
384
381
|
# Same as #reorder but operates on relation in-place instead of copying.
|
385
382
|
def reorder!(*args) # :nodoc:
|
386
383
|
preprocess_order_args(args) unless args.all?(&:blank?)
|
387
|
-
|
384
|
+
args.uniq!
|
388
385
|
self.reordering_value = true
|
389
386
|
self.order_values = args
|
390
387
|
self
|
@@ -433,7 +430,6 @@ module ActiveRecord
|
|
433
430
|
end
|
434
431
|
|
435
432
|
def unscope!(*args) # :nodoc:
|
436
|
-
args.flatten!
|
437
433
|
self.unscope_values += args
|
438
434
|
|
439
435
|
args.each do |scope|
|
@@ -444,14 +440,14 @@ module ActiveRecord
|
|
444
440
|
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
|
445
441
|
end
|
446
442
|
assert_mutability!
|
447
|
-
@values
|
443
|
+
@values.delete(scope)
|
448
444
|
when Hash
|
449
445
|
scope.each do |key, target_value|
|
450
446
|
if key != :where
|
451
447
|
raise ArgumentError, "Hash arguments in .unscope(*args) must have :where as the key."
|
452
448
|
end
|
453
449
|
|
454
|
-
target_values = Array(target_value)
|
450
|
+
target_values = resolve_arel_attributes(Array.wrap(target_value))
|
455
451
|
self.where_clause = where_clause.except(*target_values)
|
456
452
|
end
|
457
453
|
else
|
@@ -484,8 +480,7 @@ module ActiveRecord
|
|
484
480
|
# # SELECT "users".*
|
485
481
|
# # FROM "users"
|
486
482
|
# # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
487
|
-
# # INNER JOIN "comments" "
|
488
|
-
# # ON "comments_posts"."post_id" = "posts"."id"
|
483
|
+
# # INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
|
489
484
|
#
|
490
485
|
# You can use strings in order to customize your joins:
|
491
486
|
#
|
@@ -497,8 +492,6 @@ module ActiveRecord
|
|
497
492
|
end
|
498
493
|
|
499
494
|
def joins!(*args) # :nodoc:
|
500
|
-
args.compact!
|
501
|
-
args.flatten!
|
502
495
|
self.joins_values |= args
|
503
496
|
self
|
504
497
|
end
|
@@ -515,8 +508,6 @@ module ActiveRecord
|
|
515
508
|
alias :left_joins :left_outer_joins
|
516
509
|
|
517
510
|
def left_outer_joins!(*args) # :nodoc:
|
518
|
-
args.compact!
|
519
|
-
args.flatten!
|
520
511
|
self.left_outer_joins_values |= args
|
521
512
|
self
|
522
513
|
end
|
@@ -640,20 +631,18 @@ module ActiveRecord
|
|
640
631
|
#
|
641
632
|
# If the condition is any blank-ish object, then #where is a no-op and returns
|
642
633
|
# the current relation.
|
643
|
-
def where(
|
644
|
-
if
|
634
|
+
def where(*args)
|
635
|
+
if args.empty?
|
645
636
|
WhereChain.new(spawn)
|
646
|
-
elsif
|
637
|
+
elsif args.length == 1 && args.first.blank?
|
647
638
|
self
|
648
639
|
else
|
649
|
-
spawn.where!(
|
640
|
+
spawn.where!(*args)
|
650
641
|
end
|
651
642
|
end
|
652
643
|
|
653
644
|
def where!(opts, *rest) # :nodoc:
|
654
|
-
|
655
|
-
references!(PredicateBuilder.references(opts)) if Hash === opts
|
656
|
-
self.where_clause += where_clause_factory.build(opts, rest)
|
645
|
+
self.where_clause += build_where_clause(opts, rest)
|
657
646
|
self
|
658
647
|
end
|
659
648
|
|
@@ -671,7 +660,44 @@ module ActiveRecord
|
|
671
660
|
# This is short-hand for <tt>unscope(where: conditions.keys).where(conditions)</tt>.
|
672
661
|
# Note that unlike reorder, we're only unscoping the named conditions -- not the entire where statement.
|
673
662
|
def rewhere(conditions)
|
674
|
-
|
663
|
+
scope = spawn
|
664
|
+
where_clause = scope.build_where_clause(conditions)
|
665
|
+
|
666
|
+
scope.unscope!(where: where_clause.extract_attributes)
|
667
|
+
scope.where_clause += where_clause
|
668
|
+
scope
|
669
|
+
end
|
670
|
+
|
671
|
+
# Returns a new relation, which is the logical intersection of this relation and the one passed
|
672
|
+
# as an argument.
|
673
|
+
#
|
674
|
+
# The two relations must be structurally compatible: they must be scoping the same model, and
|
675
|
+
# they must differ only by #where (if no #group has been defined) or #having (if a #group is
|
676
|
+
# present).
|
677
|
+
#
|
678
|
+
# Post.where(id: [1, 2]).and(Post.where(id: [2, 3]))
|
679
|
+
# # SELECT `posts`.* FROM `posts` WHERE `posts`.`id` IN (1, 2) AND `posts`.`id` IN (2, 3)
|
680
|
+
#
|
681
|
+
def and(other)
|
682
|
+
if other.is_a?(Relation)
|
683
|
+
spawn.and!(other)
|
684
|
+
else
|
685
|
+
raise ArgumentError, "You have passed #{other.class.name} object to #and. Pass an ActiveRecord::Relation object instead."
|
686
|
+
end
|
687
|
+
end
|
688
|
+
|
689
|
+
def and!(other) # :nodoc:
|
690
|
+
incompatible_values = structurally_incompatible_values_for(other)
|
691
|
+
|
692
|
+
unless incompatible_values.empty?
|
693
|
+
raise ArgumentError, "Relation passed to #and must be structurally compatible. Incompatible values: #{incompatible_values}"
|
694
|
+
end
|
695
|
+
|
696
|
+
self.where_clause |= other.where_clause
|
697
|
+
self.having_clause |= other.having_clause
|
698
|
+
self.references_values |= other.references_values
|
699
|
+
|
700
|
+
self
|
675
701
|
end
|
676
702
|
|
677
703
|
# Returns a new relation, which is the logical union of this relation and the one passed as an
|
@@ -679,21 +705,21 @@ module ActiveRecord
|
|
679
705
|
#
|
680
706
|
# The two relations must be structurally compatible: they must be scoping the same model, and
|
681
707
|
# they must differ only by #where (if no #group has been defined) or #having (if a #group is
|
682
|
-
# present).
|
708
|
+
# present).
|
683
709
|
#
|
684
710
|
# Post.where("id = 1").or(Post.where("author_id = 3"))
|
685
711
|
# # SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3))
|
686
712
|
#
|
687
713
|
def or(other)
|
688
|
-
|
714
|
+
if other.is_a?(Relation)
|
715
|
+
spawn.or!(other)
|
716
|
+
else
|
689
717
|
raise ArgumentError, "You have passed #{other.class.name} object to #or. Pass an ActiveRecord::Relation object instead."
|
690
718
|
end
|
691
|
-
|
692
|
-
spawn.or!(other)
|
693
719
|
end
|
694
720
|
|
695
721
|
def or!(other) # :nodoc:
|
696
|
-
incompatible_values =
|
722
|
+
incompatible_values = structurally_incompatible_values_for(other)
|
697
723
|
|
698
724
|
unless incompatible_values.empty?
|
699
725
|
raise ArgumentError, "Relation passed to #or must be structurally compatible. Incompatible values: #{incompatible_values}"
|
@@ -701,7 +727,7 @@ module ActiveRecord
|
|
701
727
|
|
702
728
|
self.where_clause = self.where_clause.or(other.where_clause)
|
703
729
|
self.having_clause = having_clause.or(other.having_clause)
|
704
|
-
self.references_values
|
730
|
+
self.references_values |= other.references_values
|
705
731
|
|
706
732
|
self
|
707
733
|
end
|
@@ -715,10 +741,7 @@ module ActiveRecord
|
|
715
741
|
end
|
716
742
|
|
717
743
|
def having!(opts, *rest) # :nodoc:
|
718
|
-
|
719
|
-
references!(PredicateBuilder.references(opts)) if Hash === opts
|
720
|
-
|
721
|
-
self.having_clause += having_clause_factory.build(opts, rest)
|
744
|
+
self.having_clause += build_having_clause(opts, rest)
|
722
745
|
self
|
723
746
|
end
|
724
747
|
|
@@ -820,6 +843,21 @@ module ActiveRecord
|
|
820
843
|
self
|
821
844
|
end
|
822
845
|
|
846
|
+
# Sets the returned relation to strict_loading mode. This will raise an error
|
847
|
+
# if the record tries to lazily load an association.
|
848
|
+
#
|
849
|
+
# user = User.strict_loading.first
|
850
|
+
# user.comments.to_a
|
851
|
+
# => ActiveRecord::StrictLoadingViolationError
|
852
|
+
def strict_loading(value = true)
|
853
|
+
spawn.strict_loading!(value)
|
854
|
+
end
|
855
|
+
|
856
|
+
def strict_loading!(value = true) # :nodoc:
|
857
|
+
self.strict_loading_value = value
|
858
|
+
self
|
859
|
+
end
|
860
|
+
|
823
861
|
# Sets attributes to be used when creating new records from a
|
824
862
|
# relation object.
|
825
863
|
#
|
@@ -961,8 +999,6 @@ module ActiveRecord
|
|
961
999
|
end
|
962
1000
|
|
963
1001
|
def optimizer_hints!(*args) # :nodoc:
|
964
|
-
args.flatten!
|
965
|
-
|
966
1002
|
self.optimizer_hints_values |= args
|
967
1003
|
self
|
968
1004
|
end
|
@@ -975,8 +1011,7 @@ module ActiveRecord
|
|
975
1011
|
end
|
976
1012
|
|
977
1013
|
def reverse_order! # :nodoc:
|
978
|
-
orders = order_values.
|
979
|
-
orders.reject!(&:blank?)
|
1014
|
+
orders = order_values.compact_blank
|
980
1015
|
self.order_values = reverse_sql_order(orders)
|
981
1016
|
self
|
982
1017
|
end
|
@@ -1011,6 +1046,14 @@ module ActiveRecord
|
|
1011
1046
|
self
|
1012
1047
|
end
|
1013
1048
|
|
1049
|
+
# Deduplicate multiple values.
|
1050
|
+
def uniq!(name)
|
1051
|
+
if values = @values[name]
|
1052
|
+
values.uniq! if values.is_a?(Array) && !values.empty?
|
1053
|
+
end
|
1054
|
+
self
|
1055
|
+
end
|
1056
|
+
|
1014
1057
|
# Returns the Arel object associated with the relation.
|
1015
1058
|
def arel(aliases = nil) # :nodoc:
|
1016
1059
|
@arel ||= build_arel(aliases)
|
@@ -1031,7 +1074,57 @@ module ActiveRecord
|
|
1031
1074
|
end
|
1032
1075
|
end
|
1033
1076
|
|
1077
|
+
def build_where_clause(opts, rest = []) # :nodoc:
|
1078
|
+
opts = sanitize_forbidden_attributes(opts)
|
1079
|
+
|
1080
|
+
case opts
|
1081
|
+
when String, Array
|
1082
|
+
parts = [klass.sanitize_sql(rest.empty? ? opts : [opts, *rest])]
|
1083
|
+
when Hash
|
1084
|
+
opts = opts.stringify_keys
|
1085
|
+
references = PredicateBuilder.references(opts)
|
1086
|
+
self.references_values |= references unless references.empty?
|
1087
|
+
|
1088
|
+
parts = predicate_builder.build_from_hash(opts) do |table_name|
|
1089
|
+
lookup_reflection_from_join_dependencies(table_name)
|
1090
|
+
end
|
1091
|
+
when Arel::Nodes::Node
|
1092
|
+
parts = [opts]
|
1093
|
+
else
|
1094
|
+
raise ArgumentError, "Unsupported argument type: #{opts} (#{opts.class})"
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
Relation::WhereClause.new(parts)
|
1098
|
+
end
|
1099
|
+
alias :build_having_clause :build_where_clause
|
1100
|
+
|
1034
1101
|
private
|
1102
|
+
def lookup_reflection_from_join_dependencies(table_name)
|
1103
|
+
each_join_dependencies do |join|
|
1104
|
+
return join.reflection if table_name == join.table_name
|
1105
|
+
end
|
1106
|
+
nil
|
1107
|
+
end
|
1108
|
+
|
1109
|
+
def each_join_dependencies(join_dependencies = build_join_dependencies)
|
1110
|
+
join_dependencies.each do |join_dependency|
|
1111
|
+
join_dependency.each do |join|
|
1112
|
+
yield join
|
1113
|
+
end
|
1114
|
+
end
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
def build_join_dependencies
|
1118
|
+
associations = joins_values | left_outer_joins_values
|
1119
|
+
associations |= eager_load_values unless eager_load_values.empty?
|
1120
|
+
associations |= includes_values unless includes_values.empty?
|
1121
|
+
|
1122
|
+
join_dependencies = []
|
1123
|
+
join_dependencies.unshift construct_join_dependency(
|
1124
|
+
select_association_list(associations, join_dependencies), nil
|
1125
|
+
)
|
1126
|
+
end
|
1127
|
+
|
1035
1128
|
def assert_mutability!
|
1036
1129
|
raise ImmutableRelation if @loaded
|
1037
1130
|
raise ImmutableRelation if defined?(@arel) && @arel
|
@@ -1040,45 +1133,44 @@ module ActiveRecord
|
|
1040
1133
|
def build_arel(aliases)
|
1041
1134
|
arel = Arel::SelectManager.new(table)
|
1042
1135
|
|
1043
|
-
|
1044
|
-
build_joins(arel, joins_values.flatten, aliases)
|
1045
|
-
elsif !left_outer_joins_values.empty?
|
1046
|
-
build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases)
|
1047
|
-
end
|
1136
|
+
build_joins(arel.join_sources, aliases)
|
1048
1137
|
|
1049
1138
|
arel.where(where_clause.ast) unless where_clause.empty?
|
1050
1139
|
arel.having(having_clause.ast) unless having_clause.empty?
|
1051
|
-
if limit_value
|
1052
|
-
|
1053
|
-
|
1054
|
-
connection.sanitize_limit(limit_value),
|
1055
|
-
Type.default_value,
|
1056
|
-
)
|
1057
|
-
arel.take(Arel::Nodes::BindParam.new(limit_attribute))
|
1058
|
-
end
|
1059
|
-
if offset_value
|
1060
|
-
offset_attribute = ActiveModel::Attribute.with_cast_value(
|
1061
|
-
"OFFSET",
|
1062
|
-
offset_value.to_i,
|
1063
|
-
Type.default_value,
|
1064
|
-
)
|
1065
|
-
arel.skip(Arel::Nodes::BindParam.new(offset_attribute))
|
1066
|
-
end
|
1067
|
-
arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
|
1140
|
+
arel.take(build_cast_value("LIMIT", connection.sanitize_limit(limit_value))) if limit_value
|
1141
|
+
arel.skip(build_cast_value("OFFSET", offset_value.to_i)) if offset_value
|
1142
|
+
arel.group(*arel_columns(group_values.uniq)) unless group_values.empty?
|
1068
1143
|
|
1069
1144
|
build_order(arel)
|
1070
|
-
|
1071
1145
|
build_select(arel)
|
1072
1146
|
|
1073
1147
|
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
1074
1148
|
arel.distinct(distinct_value)
|
1075
1149
|
arel.from(build_from) unless from_clause.empty?
|
1076
1150
|
arel.lock(lock_value) if lock_value
|
1077
|
-
|
1151
|
+
|
1152
|
+
unless annotate_values.empty?
|
1153
|
+
annotates = annotate_values
|
1154
|
+
annotates = annotates.uniq if annotates.size > 1
|
1155
|
+
unless annotates == annotate_values
|
1156
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
1157
|
+
Duplicated query annotations are no longer shown in queries in Rails 6.2.
|
1158
|
+
To migrate to Rails 6.2's behavior, use `uniq!(:annotate)` to deduplicate query annotations
|
1159
|
+
(`#{klass.name&.tableize || klass.table_name}.uniq!(:annotate)`).
|
1160
|
+
MSG
|
1161
|
+
annotates = annotate_values
|
1162
|
+
end
|
1163
|
+
arel.comment(*annotates)
|
1164
|
+
end
|
1078
1165
|
|
1079
1166
|
arel
|
1080
1167
|
end
|
1081
1168
|
|
1169
|
+
def build_cast_value(name, value)
|
1170
|
+
cast_value = ActiveModel::Attribute.with_cast_value(name, value, Type.default_value)
|
1171
|
+
Arel::Nodes::BindParam.new(cast_value)
|
1172
|
+
end
|
1173
|
+
|
1082
1174
|
def build_from
|
1083
1175
|
opts = from_clause.value
|
1084
1176
|
name = from_clause.name
|
@@ -1094,99 +1186,101 @@ module ActiveRecord
|
|
1094
1186
|
end
|
1095
1187
|
end
|
1096
1188
|
|
1097
|
-
def select_association_list(associations)
|
1189
|
+
def select_association_list(associations, stashed_joins = nil)
|
1098
1190
|
result = []
|
1099
1191
|
associations.each do |association|
|
1100
1192
|
case association
|
1101
1193
|
when Hash, Symbol, Array
|
1102
1194
|
result << association
|
1195
|
+
when ActiveRecord::Associations::JoinDependency
|
1196
|
+
stashed_joins&.<< association
|
1103
1197
|
else
|
1104
|
-
yield if block_given?
|
1198
|
+
yield association if block_given?
|
1105
1199
|
end
|
1106
1200
|
end
|
1107
1201
|
result
|
1108
1202
|
end
|
1109
1203
|
|
1110
|
-
|
1111
|
-
select_association_list(associations) do
|
1112
|
-
raise ArgumentError, "only Hash, Symbol and Array are allowed"
|
1113
|
-
end
|
1204
|
+
class ::Arel::Nodes::LeadingJoin < Arel::Nodes::InnerJoin # :nodoc:
|
1114
1205
|
end
|
1115
1206
|
|
1116
|
-
def
|
1117
|
-
buckets = Hash.new { |h, k| h[k] = [] }
|
1118
|
-
buckets[:association_join] = valid_association_list(outer_joins)
|
1119
|
-
build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
|
1120
|
-
end
|
1121
|
-
|
1122
|
-
def build_joins(manager, joins, aliases)
|
1207
|
+
def build_join_buckets
|
1123
1208
|
buckets = Hash.new { |h, k| h[k] = [] }
|
1124
1209
|
|
1125
1210
|
unless left_outer_joins_values.empty?
|
1126
|
-
|
1127
|
-
|
1211
|
+
stashed_left_joins = []
|
1212
|
+
left_joins = select_association_list(left_outer_joins_values, stashed_left_joins) do
|
1213
|
+
raise ArgumentError, "only Hash, Symbol and Array are allowed"
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
if joins_values.empty?
|
1217
|
+
buckets[:association_join] = left_joins
|
1218
|
+
buckets[:stashed_join] = stashed_left_joins
|
1219
|
+
return buckets, Arel::Nodes::OuterJoin
|
1220
|
+
else
|
1221
|
+
stashed_left_joins.unshift construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
|
1222
|
+
end
|
1128
1223
|
end
|
1129
1224
|
|
1225
|
+
joins = joins_values.dup
|
1130
1226
|
if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
|
1131
|
-
|
1227
|
+
stashed_eager_load = joins.pop if joins.last.base_klass == klass
|
1132
1228
|
end
|
1133
1229
|
|
1134
|
-
joins.
|
1135
|
-
if join.is_a?(String)
|
1136
|
-
|
1137
|
-
else
|
1138
|
-
join
|
1139
|
-
end
|
1140
|
-
end.delete_if(&:blank?).uniq!
|
1230
|
+
joins.each_with_index do |join, i|
|
1231
|
+
joins[i] = Arel::Nodes::StringJoin.new(Arel.sql(join.strip)) if join.is_a?(String)
|
1232
|
+
end
|
1141
1233
|
|
1142
1234
|
while joins.first.is_a?(Arel::Nodes::Join)
|
1143
1235
|
join_node = joins.shift
|
1144
|
-
if join_node.is_a?(Arel::Nodes::
|
1236
|
+
if !join_node.is_a?(Arel::Nodes::LeadingJoin) && (stashed_eager_load || stashed_left_joins)
|
1145
1237
|
buckets[:join_node] << join_node
|
1146
1238
|
else
|
1147
1239
|
buckets[:leading_join] << join_node
|
1148
1240
|
end
|
1149
1241
|
end
|
1150
1242
|
|
1151
|
-
joins
|
1152
|
-
|
1153
|
-
when Hash, Symbol, Array
|
1154
|
-
buckets[:association_join] << join
|
1155
|
-
when ActiveRecord::Associations::JoinDependency
|
1156
|
-
buckets[:stashed_join] << join
|
1157
|
-
when Arel::Nodes::Join
|
1243
|
+
buckets[:association_join] = select_association_list(joins, buckets[:stashed_join]) do |join|
|
1244
|
+
if join.is_a?(Arel::Nodes::Join)
|
1158
1245
|
buckets[:join_node] << join
|
1159
1246
|
else
|
1160
1247
|
raise "unknown class: %s" % join.class.name
|
1161
1248
|
end
|
1162
1249
|
end
|
1163
1250
|
|
1164
|
-
|
1251
|
+
buckets[:stashed_join].concat stashed_left_joins if stashed_left_joins
|
1252
|
+
buckets[:stashed_join] << stashed_eager_load if stashed_eager_load
|
1253
|
+
|
1254
|
+
return buckets, Arel::Nodes::InnerJoin
|
1165
1255
|
end
|
1166
1256
|
|
1167
|
-
def
|
1257
|
+
def build_joins(join_sources, aliases = nil)
|
1258
|
+
return join_sources if joins_values.empty? && left_outer_joins_values.empty?
|
1259
|
+
|
1260
|
+
buckets, join_type = build_join_buckets
|
1261
|
+
|
1168
1262
|
association_joins = buckets[:association_join]
|
1169
1263
|
stashed_joins = buckets[:stashed_join]
|
1170
1264
|
leading_joins = buckets[:leading_join]
|
1171
1265
|
join_nodes = buckets[:join_node]
|
1172
1266
|
|
1173
|
-
join_sources = manager.join_sources
|
1174
1267
|
join_sources.concat(leading_joins) unless leading_joins.empty?
|
1175
1268
|
|
1176
1269
|
unless association_joins.empty? && stashed_joins.empty?
|
1177
1270
|
alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
|
1178
1271
|
join_dependency = construct_join_dependency(association_joins, join_type)
|
1179
|
-
join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
|
1272
|
+
join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker, references_values))
|
1180
1273
|
end
|
1181
1274
|
|
1182
1275
|
join_sources.concat(join_nodes) unless join_nodes.empty?
|
1276
|
+
join_sources
|
1183
1277
|
end
|
1184
1278
|
|
1185
1279
|
def build_select(arel)
|
1186
1280
|
if select_values.any?
|
1187
|
-
arel.project(*arel_columns(select_values
|
1281
|
+
arel.project(*arel_columns(select_values))
|
1188
1282
|
elsif klass.ignored_columns.any?
|
1189
|
-
arel.project(*klass.column_names.map { |field|
|
1283
|
+
arel.project(*klass.column_names.map { |field| table[field] })
|
1190
1284
|
else
|
1191
1285
|
arel.project(table[Arel.star])
|
1192
1286
|
end
|
@@ -1214,7 +1308,12 @@ module ActiveRecord
|
|
1214
1308
|
from = from_clause.name || from_clause.value
|
1215
1309
|
|
1216
1310
|
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
1217
|
-
|
1311
|
+
table[field]
|
1312
|
+
elsif field.match?(/\A\w+\.\w+\z/)
|
1313
|
+
table, column = field.split(".")
|
1314
|
+
predicate_builder.resolve_arel_attribute(table, column) do
|
1315
|
+
lookup_reflection_from_join_dependencies(table)
|
1316
|
+
end
|
1218
1317
|
else
|
1219
1318
|
yield field
|
1220
1319
|
end
|
@@ -1228,7 +1327,7 @@ module ActiveRecord
|
|
1228
1327
|
|
1229
1328
|
def reverse_sql_order(order_query)
|
1230
1329
|
if order_query.empty?
|
1231
|
-
return [
|
1330
|
+
return [table[primary_key].desc] if primary_key
|
1232
1331
|
raise IrreversibleOrderError,
|
1233
1332
|
"Relation has no current order and table has no primary key to be used as default order"
|
1234
1333
|
end
|
@@ -1239,6 +1338,8 @@ module ActiveRecord
|
|
1239
1338
|
o.desc
|
1240
1339
|
when Arel::Nodes::Ordering
|
1241
1340
|
o.reverse
|
1341
|
+
when Arel::Nodes::NodeExpression
|
1342
|
+
o.desc
|
1242
1343
|
when String
|
1243
1344
|
if does_not_support_reverse?(o)
|
1244
1345
|
raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
|
@@ -1265,9 +1366,7 @@ module ActiveRecord
|
|
1265
1366
|
end
|
1266
1367
|
|
1267
1368
|
def build_order(arel)
|
1268
|
-
orders = order_values.
|
1269
|
-
orders.reject!(&:blank?)
|
1270
|
-
|
1369
|
+
orders = order_values.compact_blank
|
1271
1370
|
arel.order(*orders) unless orders.empty?
|
1272
1371
|
end
|
1273
1372
|
|
@@ -1287,12 +1386,6 @@ module ActiveRecord
|
|
1287
1386
|
end
|
1288
1387
|
|
1289
1388
|
def preprocess_order_args(order_args)
|
1290
|
-
order_args.reject!(&:blank?)
|
1291
|
-
order_args.map! do |arg|
|
1292
|
-
klass.sanitize_sql_for_order(arg)
|
1293
|
-
end
|
1294
|
-
order_args.flatten!
|
1295
|
-
|
1296
1389
|
@klass.disallow_raw_sql!(
|
1297
1390
|
order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
|
1298
1391
|
permit: connection.column_name_with_order_matcher
|
@@ -1300,9 +1393,8 @@ module ActiveRecord
|
|
1300
1393
|
|
1301
1394
|
validate_order_args(order_args)
|
1302
1395
|
|
1303
|
-
references = order_args
|
1304
|
-
|
1305
|
-
references!(references) if references.any?
|
1396
|
+
references = column_references(order_args)
|
1397
|
+
self.references_values |= references unless references.empty?
|
1306
1398
|
|
1307
1399
|
# if a symbol is given we prepend the quoted table name
|
1308
1400
|
order_args.map! do |arg|
|
@@ -1313,9 +1405,9 @@ module ActiveRecord
|
|
1313
1405
|
arg.map { |field, dir|
|
1314
1406
|
case field
|
1315
1407
|
when Arel::Nodes::SqlLiteral
|
1316
|
-
field.
|
1408
|
+
field.public_send(dir.downcase)
|
1317
1409
|
else
|
1318
|
-
order_column(field.to_s).
|
1410
|
+
order_column(field.to_s).public_send(dir.downcase)
|
1319
1411
|
end
|
1320
1412
|
}
|
1321
1413
|
else
|
@@ -1324,16 +1416,54 @@ module ActiveRecord
|
|
1324
1416
|
end.flatten!
|
1325
1417
|
end
|
1326
1418
|
|
1419
|
+
def sanitize_order_arguments(order_args)
|
1420
|
+
order_args.map! do |arg|
|
1421
|
+
klass.sanitize_sql_for_order(arg)
|
1422
|
+
end
|
1423
|
+
order_args.flatten!
|
1424
|
+
order_args.compact_blank!
|
1425
|
+
end
|
1426
|
+
|
1427
|
+
def column_references(order_args)
|
1428
|
+
references = order_args.grep(String)
|
1429
|
+
references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
|
1430
|
+
references
|
1431
|
+
end
|
1432
|
+
|
1327
1433
|
def order_column(field)
|
1328
1434
|
arel_column(field) do |attr_name|
|
1329
1435
|
if attr_name == "count" && !group_values.empty?
|
1330
|
-
|
1436
|
+
table[attr_name]
|
1331
1437
|
else
|
1332
1438
|
Arel.sql(connection.quote_table_name(attr_name))
|
1333
1439
|
end
|
1334
1440
|
end
|
1335
1441
|
end
|
1336
1442
|
|
1443
|
+
def resolve_arel_attributes(attrs)
|
1444
|
+
attrs.flat_map do |attr|
|
1445
|
+
case attr
|
1446
|
+
when Arel::Predications
|
1447
|
+
attr
|
1448
|
+
when Hash
|
1449
|
+
attr.flat_map do |table, columns|
|
1450
|
+
table = table.to_s
|
1451
|
+
Array(columns).map do |column|
|
1452
|
+
predicate_builder.resolve_arel_attribute(table, column)
|
1453
|
+
end
|
1454
|
+
end
|
1455
|
+
else
|
1456
|
+
attr = attr.to_s
|
1457
|
+
if attr.include?(".")
|
1458
|
+
table, column = attr.split(".", 2)
|
1459
|
+
predicate_builder.resolve_arel_attribute(table, column)
|
1460
|
+
else
|
1461
|
+
attr
|
1462
|
+
end
|
1463
|
+
end
|
1464
|
+
end
|
1465
|
+
end
|
1466
|
+
|
1337
1467
|
# Checks to make sure that the arguments are not blank. Note that if some
|
1338
1468
|
# blank-like object were initially passed into the query method, then this
|
1339
1469
|
# method will not raise an error.
|
@@ -1350,35 +1480,40 @@ module ActiveRecord
|
|
1350
1480
|
# check_if_method_has_arguments!("references", args)
|
1351
1481
|
# ...
|
1352
1482
|
# end
|
1353
|
-
def check_if_method_has_arguments!(method_name, args)
|
1483
|
+
def check_if_method_has_arguments!(method_name, args, message = nil)
|
1354
1484
|
if args.blank?
|
1355
|
-
raise ArgumentError, "The method .#{method_name}() must contain arguments."
|
1485
|
+
raise ArgumentError, message || "The method .#{method_name}() must contain arguments."
|
1486
|
+
elsif block_given?
|
1487
|
+
yield args
|
1488
|
+
else
|
1489
|
+
args.flatten!
|
1490
|
+
args.compact_blank!
|
1356
1491
|
end
|
1357
1492
|
end
|
1358
1493
|
|
1359
|
-
|
1360
|
-
|
1494
|
+
STRUCTURAL_VALUE_METHODS = (
|
1495
|
+
Relation::VALUE_METHODS -
|
1496
|
+
[:extending, :where, :having, :unscope, :references, :annotate, :optimizer_hints]
|
1497
|
+
).freeze # :nodoc:
|
1498
|
+
|
1499
|
+
def structurally_incompatible_values_for(other)
|
1361
1500
|
values = other.values
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1501
|
+
STRUCTURAL_VALUE_METHODS.reject do |method|
|
1502
|
+
v1, v2 = @values[method], values[method]
|
1503
|
+
if v1.is_a?(Array)
|
1504
|
+
next true unless v2.is_a?(Array)
|
1505
|
+
v1 = v1.uniq
|
1506
|
+
v2 = v2.uniq
|
1507
|
+
end
|
1508
|
+
v1 == v2 || (!v1 || v1.empty?) && (!v2 || v2.empty?)
|
1365
1509
|
end
|
1366
1510
|
end
|
1511
|
+
end
|
1367
1512
|
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
DEFAULT_VALUES = {
|
1374
|
-
create_with: FROZEN_EMPTY_HASH,
|
1375
|
-
where: Relation::WhereClause.empty,
|
1376
|
-
having: Relation::WhereClause.empty,
|
1377
|
-
from: Relation::FromClause.empty
|
1378
|
-
}
|
1379
|
-
|
1380
|
-
Relation::MULTI_VALUE_METHODS.each do |value|
|
1381
|
-
DEFAULT_VALUES[value] ||= FROZEN_EMPTY_ARRAY
|
1382
|
-
end
|
1513
|
+
class Relation # :nodoc:
|
1514
|
+
# No-op WhereClauseFactory to work Mashal.load(File.read("legacy_relation.dump")).
|
1515
|
+
# TODO: Remove the class once Rails 6.1 has released.
|
1516
|
+
class WhereClauseFactory # :nodoc:
|
1517
|
+
end
|
1383
1518
|
end
|
1384
1519
|
end
|