activerecord 6.0.1 → 6.1.7
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 +1314 -633
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/active_record/aggregations.rb +5 -6
- data/lib/active_record/association_relation.rb +26 -15
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +55 -37
- data/lib/active_record/associations/association_scope.rb +19 -15
- data/lib/active_record/associations/belongs_to_association.rb +23 -10
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
- data/lib/active_record/associations/builder/association.rb +32 -5
- 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 -3
- 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 +38 -13
- data/lib/active_record/associations/collection_proxy.rb +14 -7
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -3
- 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/join_association.rb +39 -16
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/join_dependency.rb +73 -42
- data/lib/active_record/associations/preloader/association.rb +49 -25
- data/lib/active_record/associations/preloader/through_association.rb +2 -2
- data/lib/active_record/associations/preloader.rb +12 -7
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +119 -12
- data/lib/active_record/attribute_assignment.rb +10 -9
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
- data/lib/active_record/attribute_methods/dirty.rb +3 -13
- data/lib/active_record/attribute_methods/primary_key.rb +6 -4
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -12
- data/lib/active_record/attribute_methods/serialization.rb +11 -6
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +12 -21
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attributes.rb +33 -9
- data/lib/active_record/autosave_association.rb +56 -41
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +153 -24
- data/lib/active_record/coders/yaml_column.rb +24 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +190 -136
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -38
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -9
- 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 +152 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +145 -52
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
- data/lib/active_record/connection_adapters/abstract/transaction.rb +94 -36
- data/lib/active_record/connection_adapters/abstract_adapter.rb +63 -77
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +136 -111
- 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 +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
- 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 +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +80 -66
- data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -57
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +218 -87
- data/lib/active_record/core.rb +269 -68
- data/lib/active_record/counter_cache.rb +4 -1
- data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -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 -41
- data/lib/active_record/database_configurations.rb +125 -85
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +2 -3
- data/lib/active_record/enum.rb +80 -38
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -5
- 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 -3
- data/lib/active_record/fixture_set/table_rows.rb +0 -1
- data/lib/active_record/fixtures.rb +58 -12
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +40 -21
- data/lib/active_record/insert_all.rb +42 -9
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +18 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +33 -18
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +28 -9
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -2
- data/lib/active_record/middleware/database_selector.rb +4 -2
- data/lib/active_record/migration/command_recorder.rb +53 -45
- data/lib/active_record/migration/compatibility.rb +75 -21
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/migration.rb +115 -85
- data/lib/active_record/model_schema.rb +117 -15
- data/lib/active_record/nested_attributes.rb +2 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +50 -46
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +12 -7
- data/lib/active_record/railtie.rb +65 -45
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/databases.rake +280 -99
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +77 -63
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +38 -32
- data/lib/active_record/relation/calculations.rb +106 -45
- data/lib/active_record/relation/delegation.rb +9 -7
- data/lib/active_record/relation/finder_methods.rb +45 -16
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +27 -26
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +59 -40
- data/lib/active_record/relation/query_methods.rb +339 -188
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -8
- data/lib/active_record/relation/where_clause.rb +111 -62
- data/lib/active_record/relation.rb +116 -83
- data/lib/active_record/result.rb +41 -34
- 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 +2 -8
- data/lib/active_record/scoping/default.rb +1 -4
- data/lib/active_record/scoping/named.rb +7 -18
- data/lib/active_record/scoping.rb +0 -1
- 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 +9 -4
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -36
- data/lib/active_record/tasks/database_tasks.rb +140 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +87 -20
- data/lib/active_record/timestamp.rb +4 -7
- data/lib/active_record/touch_later.rb +20 -21
- data/lib/active_record/transactions.rb +25 -72
- data/lib/active_record/type/adapter_specific_registry.rb +2 -5
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +8 -2
- 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/associated.rb +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/active_record/validations.rb +3 -3
- data/lib/active_record.rb +7 -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/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 +76 -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/nodes.rb +3 -1
- data/lib/arel/predications.rb +17 -24
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors/dot.rb +14 -3
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -5
- data/lib/arel/visitors/sqlite.rb +0 -1
- data/lib/arel/visitors/to_sql.rb +89 -79
- data/lib/arel/visitors/visitor.rb +0 -1
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +5 -9
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -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 +4 -4
- data/lib/rails/generators/active_record/migration.rb +6 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +30 -29
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- 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 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
@@ -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,53 +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
|
+
@scope.where_clause += where_clause.invert
|
48
49
|
|
49
|
-
@scope
|
50
|
+
@scope
|
51
|
+
end
|
50
52
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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)
|
60
77
|
end
|
61
78
|
|
62
79
|
@scope
|
63
80
|
end
|
64
|
-
|
65
|
-
private
|
66
|
-
def not_behaves_as_nor?(opts)
|
67
|
-
opts.is_a?(Hash) && opts.size > 1
|
68
|
-
end
|
69
81
|
end
|
70
82
|
|
71
83
|
FROZEN_EMPTY_ARRAY = [].freeze
|
72
84
|
FROZEN_EMPTY_HASH = {}.freeze
|
73
85
|
|
74
86
|
Relation::VALUE_METHODS.each do |name|
|
75
|
-
method_name =
|
87
|
+
method_name, default =
|
76
88
|
case name
|
77
|
-
when *Relation::MULTI_VALUE_METHODS
|
78
|
-
|
79
|
-
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"]
|
80
95
|
end
|
96
|
+
|
81
97
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
82
|
-
def #{method_name}
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
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
|
91
106
|
CODE
|
92
107
|
end
|
93
108
|
|
@@ -138,9 +153,6 @@ module ActiveRecord
|
|
138
153
|
end
|
139
154
|
|
140
155
|
def includes!(*args) # :nodoc:
|
141
|
-
args.reject!(&:blank?)
|
142
|
-
args.flatten!
|
143
|
-
|
144
156
|
self.includes_values |= args
|
145
157
|
self
|
146
158
|
end
|
@@ -157,7 +169,7 @@ module ActiveRecord
|
|
157
169
|
end
|
158
170
|
|
159
171
|
def eager_load!(*args) # :nodoc:
|
160
|
-
self.eager_load_values
|
172
|
+
self.eager_load_values |= args
|
161
173
|
self
|
162
174
|
end
|
163
175
|
|
@@ -171,7 +183,7 @@ module ActiveRecord
|
|
171
183
|
end
|
172
184
|
|
173
185
|
def preload!(*args) # :nodoc:
|
174
|
-
self.preload_values
|
186
|
+
self.preload_values |= args
|
175
187
|
self
|
176
188
|
end
|
177
189
|
|
@@ -204,9 +216,6 @@ module ActiveRecord
|
|
204
216
|
end
|
205
217
|
|
206
218
|
def references!(*table_names) # :nodoc:
|
207
|
-
table_names.flatten!
|
208
|
-
table_names.map!(&:to_s)
|
209
|
-
|
210
219
|
self.references_values |= table_names
|
211
220
|
self
|
212
221
|
end
|
@@ -260,14 +269,12 @@ module ActiveRecord
|
|
260
269
|
return super()
|
261
270
|
end
|
262
271
|
|
263
|
-
|
272
|
+
check_if_method_has_arguments!(:select, fields, "Call `select' with at least one field.")
|
264
273
|
spawn._select!(*fields)
|
265
274
|
end
|
266
275
|
|
267
276
|
def _select!(*fields) # :nodoc:
|
268
|
-
fields
|
269
|
-
fields.flatten!
|
270
|
-
self.select_values += fields
|
277
|
+
self.select_values |= fields
|
271
278
|
self
|
272
279
|
end
|
273
280
|
|
@@ -318,8 +325,6 @@ module ActiveRecord
|
|
318
325
|
end
|
319
326
|
|
320
327
|
def group!(*args) # :nodoc:
|
321
|
-
args.flatten!
|
322
|
-
|
323
328
|
self.group_values += args
|
324
329
|
self
|
325
330
|
end
|
@@ -344,15 +349,16 @@ module ActiveRecord
|
|
344
349
|
# User.order('name DESC, email')
|
345
350
|
# # SELECT "users".* FROM "users" ORDER BY name DESC, email
|
346
351
|
def order(*args)
|
347
|
-
check_if_method_has_arguments!(:order, args)
|
352
|
+
check_if_method_has_arguments!(:order, args) do
|
353
|
+
sanitize_order_arguments(args)
|
354
|
+
end
|
348
355
|
spawn.order!(*args)
|
349
356
|
end
|
350
357
|
|
351
358
|
# Same as #order but operates on relation in-place instead of copying.
|
352
359
|
def order!(*args) # :nodoc:
|
353
|
-
preprocess_order_args(args)
|
354
|
-
|
355
|
-
self.order_values += args
|
360
|
+
preprocess_order_args(args) unless args.empty?
|
361
|
+
self.order_values |= args
|
356
362
|
self
|
357
363
|
end
|
358
364
|
|
@@ -366,14 +372,16 @@ module ActiveRecord
|
|
366
372
|
#
|
367
373
|
# generates a query with 'ORDER BY id ASC, name ASC'.
|
368
374
|
def reorder(*args)
|
369
|
-
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
|
370
378
|
spawn.reorder!(*args)
|
371
379
|
end
|
372
380
|
|
373
381
|
# Same as #reorder but operates on relation in-place instead of copying.
|
374
382
|
def reorder!(*args) # :nodoc:
|
375
383
|
preprocess_order_args(args) unless args.all?(&:blank?)
|
376
|
-
|
384
|
+
args.uniq!
|
377
385
|
self.reordering_value = true
|
378
386
|
self.order_values = args
|
379
387
|
self
|
@@ -422,7 +430,6 @@ module ActiveRecord
|
|
422
430
|
end
|
423
431
|
|
424
432
|
def unscope!(*args) # :nodoc:
|
425
|
-
args.flatten!
|
426
433
|
self.unscope_values += args
|
427
434
|
|
428
435
|
args.each do |scope|
|
@@ -433,14 +440,14 @@ module ActiveRecord
|
|
433
440
|
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
|
434
441
|
end
|
435
442
|
assert_mutability!
|
436
|
-
@values
|
443
|
+
@values.delete(scope)
|
437
444
|
when Hash
|
438
445
|
scope.each do |key, target_value|
|
439
446
|
if key != :where
|
440
447
|
raise ArgumentError, "Hash arguments in .unscope(*args) must have :where as the key."
|
441
448
|
end
|
442
449
|
|
443
|
-
target_values = Array(target_value)
|
450
|
+
target_values = resolve_arel_attributes(Array.wrap(target_value))
|
444
451
|
self.where_clause = where_clause.except(*target_values)
|
445
452
|
end
|
446
453
|
else
|
@@ -473,8 +480,7 @@ module ActiveRecord
|
|
473
480
|
# # SELECT "users".*
|
474
481
|
# # FROM "users"
|
475
482
|
# # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
476
|
-
# # INNER JOIN "comments" "
|
477
|
-
# # ON "comments_posts"."post_id" = "posts"."id"
|
483
|
+
# # INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
|
478
484
|
#
|
479
485
|
# You can use strings in order to customize your joins:
|
480
486
|
#
|
@@ -486,9 +492,7 @@ module ActiveRecord
|
|
486
492
|
end
|
487
493
|
|
488
494
|
def joins!(*args) # :nodoc:
|
489
|
-
args
|
490
|
-
args.flatten!
|
491
|
-
self.joins_values += args
|
495
|
+
self.joins_values |= args
|
492
496
|
self
|
493
497
|
end
|
494
498
|
|
@@ -504,9 +508,7 @@ module ActiveRecord
|
|
504
508
|
alias :left_joins :left_outer_joins
|
505
509
|
|
506
510
|
def left_outer_joins!(*args) # :nodoc:
|
507
|
-
args
|
508
|
-
args.flatten!
|
509
|
-
self.left_outer_joins_values += args
|
511
|
+
self.left_outer_joins_values |= args
|
510
512
|
self
|
511
513
|
end
|
512
514
|
|
@@ -629,20 +631,18 @@ module ActiveRecord
|
|
629
631
|
#
|
630
632
|
# If the condition is any blank-ish object, then #where is a no-op and returns
|
631
633
|
# the current relation.
|
632
|
-
def where(
|
633
|
-
if
|
634
|
+
def where(*args)
|
635
|
+
if args.empty?
|
634
636
|
WhereChain.new(spawn)
|
635
|
-
elsif
|
637
|
+
elsif args.length == 1 && args.first.blank?
|
636
638
|
self
|
637
639
|
else
|
638
|
-
spawn.where!(
|
640
|
+
spawn.where!(*args)
|
639
641
|
end
|
640
642
|
end
|
641
643
|
|
642
644
|
def where!(opts, *rest) # :nodoc:
|
643
|
-
|
644
|
-
references!(PredicateBuilder.references(opts)) if Hash === opts
|
645
|
-
self.where_clause += where_clause_factory.build(opts, rest)
|
645
|
+
self.where_clause += build_where_clause(opts, rest)
|
646
646
|
self
|
647
647
|
end
|
648
648
|
|
@@ -660,7 +660,44 @@ module ActiveRecord
|
|
660
660
|
# This is short-hand for <tt>unscope(where: conditions.keys).where(conditions)</tt>.
|
661
661
|
# Note that unlike reorder, we're only unscoping the named conditions -- not the entire where statement.
|
662
662
|
def rewhere(conditions)
|
663
|
-
|
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
|
664
701
|
end
|
665
702
|
|
666
703
|
# Returns a new relation, which is the logical union of this relation and the one passed as an
|
@@ -668,21 +705,21 @@ module ActiveRecord
|
|
668
705
|
#
|
669
706
|
# The two relations must be structurally compatible: they must be scoping the same model, and
|
670
707
|
# they must differ only by #where (if no #group has been defined) or #having (if a #group is
|
671
|
-
# present).
|
708
|
+
# present).
|
672
709
|
#
|
673
710
|
# Post.where("id = 1").or(Post.where("author_id = 3"))
|
674
711
|
# # SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3))
|
675
712
|
#
|
676
713
|
def or(other)
|
677
|
-
|
714
|
+
if other.is_a?(Relation)
|
715
|
+
spawn.or!(other)
|
716
|
+
else
|
678
717
|
raise ArgumentError, "You have passed #{other.class.name} object to #or. Pass an ActiveRecord::Relation object instead."
|
679
718
|
end
|
680
|
-
|
681
|
-
spawn.or!(other)
|
682
719
|
end
|
683
720
|
|
684
721
|
def or!(other) # :nodoc:
|
685
|
-
incompatible_values =
|
722
|
+
incompatible_values = structurally_incompatible_values_for(other)
|
686
723
|
|
687
724
|
unless incompatible_values.empty?
|
688
725
|
raise ArgumentError, "Relation passed to #or must be structurally compatible. Incompatible values: #{incompatible_values}"
|
@@ -690,7 +727,7 @@ module ActiveRecord
|
|
690
727
|
|
691
728
|
self.where_clause = self.where_clause.or(other.where_clause)
|
692
729
|
self.having_clause = having_clause.or(other.having_clause)
|
693
|
-
self.references_values
|
730
|
+
self.references_values |= other.references_values
|
694
731
|
|
695
732
|
self
|
696
733
|
end
|
@@ -704,10 +741,7 @@ module ActiveRecord
|
|
704
741
|
end
|
705
742
|
|
706
743
|
def having!(opts, *rest) # :nodoc:
|
707
|
-
|
708
|
-
references!(PredicateBuilder.references(opts)) if Hash === opts
|
709
|
-
|
710
|
-
self.having_clause += having_clause_factory.build(opts, rest)
|
744
|
+
self.having_clause += build_having_clause(opts, rest)
|
711
745
|
self
|
712
746
|
end
|
713
747
|
|
@@ -809,6 +843,21 @@ module ActiveRecord
|
|
809
843
|
self
|
810
844
|
end
|
811
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
|
+
|
812
861
|
# Sets attributes to be used when creating new records from a
|
813
862
|
# relation object.
|
814
863
|
#
|
@@ -950,8 +999,6 @@ module ActiveRecord
|
|
950
999
|
end
|
951
1000
|
|
952
1001
|
def optimizer_hints!(*args) # :nodoc:
|
953
|
-
args.flatten!
|
954
|
-
|
955
1002
|
self.optimizer_hints_values |= args
|
956
1003
|
self
|
957
1004
|
end
|
@@ -964,8 +1011,7 @@ module ActiveRecord
|
|
964
1011
|
end
|
965
1012
|
|
966
1013
|
def reverse_order! # :nodoc:
|
967
|
-
orders = order_values.
|
968
|
-
orders.reject!(&:blank?)
|
1014
|
+
orders = order_values.compact_blank
|
969
1015
|
self.order_values = reverse_sql_order(orders)
|
970
1016
|
self
|
971
1017
|
end
|
@@ -1000,6 +1046,14 @@ module ActiveRecord
|
|
1000
1046
|
self
|
1001
1047
|
end
|
1002
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
|
+
|
1003
1057
|
# Returns the Arel object associated with the relation.
|
1004
1058
|
def arel(aliases = nil) # :nodoc:
|
1005
1059
|
@arel ||= build_arel(aliases)
|
@@ -1020,54 +1074,106 @@ module ActiveRecord
|
|
1020
1074
|
end
|
1021
1075
|
end
|
1022
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.transform_keys do |key|
|
1085
|
+
key = key.to_s
|
1086
|
+
klass.attribute_aliases[key] || key
|
1087
|
+
end
|
1088
|
+
references = PredicateBuilder.references(opts)
|
1089
|
+
self.references_values |= references unless references.empty?
|
1090
|
+
|
1091
|
+
parts = predicate_builder.build_from_hash(opts) do |table_name|
|
1092
|
+
lookup_table_klass_from_join_dependencies(table_name)
|
1093
|
+
end
|
1094
|
+
when Arel::Nodes::Node
|
1095
|
+
parts = [opts]
|
1096
|
+
else
|
1097
|
+
raise ArgumentError, "Unsupported argument type: #{opts} (#{opts.class})"
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
Relation::WhereClause.new(parts)
|
1101
|
+
end
|
1102
|
+
alias :build_having_clause :build_where_clause
|
1103
|
+
|
1023
1104
|
private
|
1105
|
+
def lookup_table_klass_from_join_dependencies(table_name)
|
1106
|
+
each_join_dependencies do |join|
|
1107
|
+
return join.base_klass if table_name == join.table_name
|
1108
|
+
end
|
1109
|
+
nil
|
1110
|
+
end
|
1111
|
+
|
1112
|
+
def each_join_dependencies(join_dependencies = build_join_dependencies)
|
1113
|
+
join_dependencies.each do |join_dependency|
|
1114
|
+
join_dependency.each do |join|
|
1115
|
+
yield join
|
1116
|
+
end
|
1117
|
+
end
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
def build_join_dependencies
|
1121
|
+
associations = joins_values | left_outer_joins_values
|
1122
|
+
associations |= eager_load_values unless eager_load_values.empty?
|
1123
|
+
associations |= includes_values unless includes_values.empty?
|
1124
|
+
|
1125
|
+
join_dependencies = []
|
1126
|
+
join_dependencies.unshift construct_join_dependency(
|
1127
|
+
select_association_list(associations, join_dependencies), nil
|
1128
|
+
)
|
1129
|
+
end
|
1130
|
+
|
1024
1131
|
def assert_mutability!
|
1025
1132
|
raise ImmutableRelation if @loaded
|
1026
1133
|
raise ImmutableRelation if defined?(@arel) && @arel
|
1027
1134
|
end
|
1028
1135
|
|
1029
|
-
def build_arel(aliases)
|
1136
|
+
def build_arel(aliases = nil)
|
1030
1137
|
arel = Arel::SelectManager.new(table)
|
1031
1138
|
|
1032
|
-
|
1033
|
-
build_joins(arel, joins_values.flatten, aliases)
|
1034
|
-
elsif !left_outer_joins_values.empty?
|
1035
|
-
build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases)
|
1036
|
-
end
|
1139
|
+
build_joins(arel.join_sources, aliases)
|
1037
1140
|
|
1038
1141
|
arel.where(where_clause.ast) unless where_clause.empty?
|
1039
1142
|
arel.having(having_clause.ast) unless having_clause.empty?
|
1040
|
-
if limit_value
|
1041
|
-
|
1042
|
-
|
1043
|
-
connection.sanitize_limit(limit_value),
|
1044
|
-
Type.default_value,
|
1045
|
-
)
|
1046
|
-
arel.take(Arel::Nodes::BindParam.new(limit_attribute))
|
1047
|
-
end
|
1048
|
-
if offset_value
|
1049
|
-
offset_attribute = ActiveModel::Attribute.with_cast_value(
|
1050
|
-
"OFFSET",
|
1051
|
-
offset_value.to_i,
|
1052
|
-
Type.default_value,
|
1053
|
-
)
|
1054
|
-
arel.skip(Arel::Nodes::BindParam.new(offset_attribute))
|
1055
|
-
end
|
1056
|
-
arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
|
1143
|
+
arel.take(build_cast_value("LIMIT", connection.sanitize_limit(limit_value))) if limit_value
|
1144
|
+
arel.skip(build_cast_value("OFFSET", offset_value.to_i)) if offset_value
|
1145
|
+
arel.group(*arel_columns(group_values.uniq)) unless group_values.empty?
|
1057
1146
|
|
1058
1147
|
build_order(arel)
|
1059
|
-
|
1060
1148
|
build_select(arel)
|
1061
1149
|
|
1062
1150
|
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
1063
1151
|
arel.distinct(distinct_value)
|
1064
1152
|
arel.from(build_from) unless from_clause.empty?
|
1065
1153
|
arel.lock(lock_value) if lock_value
|
1066
|
-
|
1154
|
+
|
1155
|
+
unless annotate_values.empty?
|
1156
|
+
annotates = annotate_values
|
1157
|
+
annotates = annotates.uniq if annotates.size > 1
|
1158
|
+
unless annotates == annotate_values
|
1159
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
1160
|
+
Duplicated query annotations are no longer shown in queries in Rails 7.0.
|
1161
|
+
To migrate to Rails 7.0's behavior, use `uniq!(:annotate)` to deduplicate query annotations
|
1162
|
+
(`#{klass.name&.tableize || klass.table_name}.uniq!(:annotate)`).
|
1163
|
+
MSG
|
1164
|
+
annotates = annotate_values
|
1165
|
+
end
|
1166
|
+
arel.comment(*annotates)
|
1167
|
+
end
|
1067
1168
|
|
1068
1169
|
arel
|
1069
1170
|
end
|
1070
1171
|
|
1172
|
+
def build_cast_value(name, value)
|
1173
|
+
cast_value = ActiveModel::Attribute.with_cast_value(name, value, Type.default_value)
|
1174
|
+
Arel::Nodes::BindParam.new(cast_value)
|
1175
|
+
end
|
1176
|
+
|
1071
1177
|
def build_from
|
1072
1178
|
opts = from_clause.value
|
1073
1179
|
name = from_clause.name
|
@@ -1083,99 +1189,101 @@ module ActiveRecord
|
|
1083
1189
|
end
|
1084
1190
|
end
|
1085
1191
|
|
1086
|
-
def select_association_list(associations)
|
1192
|
+
def select_association_list(associations, stashed_joins = nil)
|
1087
1193
|
result = []
|
1088
1194
|
associations.each do |association|
|
1089
1195
|
case association
|
1090
1196
|
when Hash, Symbol, Array
|
1091
1197
|
result << association
|
1198
|
+
when ActiveRecord::Associations::JoinDependency
|
1199
|
+
stashed_joins&.<< association
|
1092
1200
|
else
|
1093
|
-
yield if block_given?
|
1201
|
+
yield association if block_given?
|
1094
1202
|
end
|
1095
1203
|
end
|
1096
1204
|
result
|
1097
1205
|
end
|
1098
1206
|
|
1099
|
-
|
1100
|
-
select_association_list(associations) do
|
1101
|
-
raise ArgumentError, "only Hash, Symbol and Array are allowed"
|
1102
|
-
end
|
1207
|
+
class ::Arel::Nodes::LeadingJoin < Arel::Nodes::InnerJoin # :nodoc:
|
1103
1208
|
end
|
1104
1209
|
|
1105
|
-
def
|
1106
|
-
buckets = Hash.new { |h, k| h[k] = [] }
|
1107
|
-
buckets[:association_join] = valid_association_list(outer_joins)
|
1108
|
-
build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
|
1109
|
-
end
|
1110
|
-
|
1111
|
-
def build_joins(manager, joins, aliases)
|
1210
|
+
def build_join_buckets
|
1112
1211
|
buckets = Hash.new { |h, k| h[k] = [] }
|
1113
1212
|
|
1114
1213
|
unless left_outer_joins_values.empty?
|
1115
|
-
|
1116
|
-
|
1214
|
+
stashed_left_joins = []
|
1215
|
+
left_joins = select_association_list(left_outer_joins_values, stashed_left_joins) do
|
1216
|
+
raise ArgumentError, "only Hash, Symbol and Array are allowed"
|
1217
|
+
end
|
1218
|
+
|
1219
|
+
if joins_values.empty?
|
1220
|
+
buckets[:association_join] = left_joins
|
1221
|
+
buckets[:stashed_join] = stashed_left_joins
|
1222
|
+
return buckets, Arel::Nodes::OuterJoin
|
1223
|
+
else
|
1224
|
+
stashed_left_joins.unshift construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
|
1225
|
+
end
|
1117
1226
|
end
|
1118
1227
|
|
1228
|
+
joins = joins_values.dup
|
1119
1229
|
if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
|
1120
|
-
|
1230
|
+
stashed_eager_load = joins.pop if joins.last.base_klass == klass
|
1121
1231
|
end
|
1122
1232
|
|
1123
|
-
joins.
|
1124
|
-
if join.is_a?(String)
|
1125
|
-
|
1126
|
-
else
|
1127
|
-
join
|
1128
|
-
end
|
1129
|
-
end.delete_if(&:blank?).uniq!
|
1233
|
+
joins.each_with_index do |join, i|
|
1234
|
+
joins[i] = Arel::Nodes::StringJoin.new(Arel.sql(join.strip)) if join.is_a?(String)
|
1235
|
+
end
|
1130
1236
|
|
1131
1237
|
while joins.first.is_a?(Arel::Nodes::Join)
|
1132
1238
|
join_node = joins.shift
|
1133
|
-
if join_node.is_a?(Arel::Nodes::
|
1239
|
+
if !join_node.is_a?(Arel::Nodes::LeadingJoin) && (stashed_eager_load || stashed_left_joins)
|
1134
1240
|
buckets[:join_node] << join_node
|
1135
1241
|
else
|
1136
1242
|
buckets[:leading_join] << join_node
|
1137
1243
|
end
|
1138
1244
|
end
|
1139
1245
|
|
1140
|
-
joins
|
1141
|
-
|
1142
|
-
when Hash, Symbol, Array
|
1143
|
-
buckets[:association_join] << join
|
1144
|
-
when ActiveRecord::Associations::JoinDependency
|
1145
|
-
buckets[:stashed_join] << join
|
1146
|
-
when Arel::Nodes::Join
|
1246
|
+
buckets[:association_join] = select_association_list(joins, buckets[:stashed_join]) do |join|
|
1247
|
+
if join.is_a?(Arel::Nodes::Join)
|
1147
1248
|
buckets[:join_node] << join
|
1148
1249
|
else
|
1149
1250
|
raise "unknown class: %s" % join.class.name
|
1150
1251
|
end
|
1151
1252
|
end
|
1152
1253
|
|
1153
|
-
|
1254
|
+
buckets[:stashed_join].concat stashed_left_joins if stashed_left_joins
|
1255
|
+
buckets[:stashed_join] << stashed_eager_load if stashed_eager_load
|
1256
|
+
|
1257
|
+
return buckets, Arel::Nodes::InnerJoin
|
1154
1258
|
end
|
1155
1259
|
|
1156
|
-
def
|
1260
|
+
def build_joins(join_sources, aliases = nil)
|
1261
|
+
return join_sources if joins_values.empty? && left_outer_joins_values.empty?
|
1262
|
+
|
1263
|
+
buckets, join_type = build_join_buckets
|
1264
|
+
|
1157
1265
|
association_joins = buckets[:association_join]
|
1158
1266
|
stashed_joins = buckets[:stashed_join]
|
1159
1267
|
leading_joins = buckets[:leading_join]
|
1160
1268
|
join_nodes = buckets[:join_node]
|
1161
1269
|
|
1162
|
-
join_sources = manager.join_sources
|
1163
1270
|
join_sources.concat(leading_joins) unless leading_joins.empty?
|
1164
1271
|
|
1165
1272
|
unless association_joins.empty? && stashed_joins.empty?
|
1166
1273
|
alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
|
1167
1274
|
join_dependency = construct_join_dependency(association_joins, join_type)
|
1168
|
-
join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
|
1275
|
+
join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker, references_values))
|
1169
1276
|
end
|
1170
1277
|
|
1171
1278
|
join_sources.concat(join_nodes) unless join_nodes.empty?
|
1279
|
+
join_sources
|
1172
1280
|
end
|
1173
1281
|
|
1174
1282
|
def build_select(arel)
|
1175
1283
|
if select_values.any?
|
1176
|
-
arel.project(*arel_columns(select_values
|
1284
|
+
arel.project(*arel_columns(select_values))
|
1177
1285
|
elsif klass.ignored_columns.any?
|
1178
|
-
arel.project(*klass.column_names.map { |field|
|
1286
|
+
arel.project(*klass.column_names.map { |field| table[field] })
|
1179
1287
|
else
|
1180
1288
|
arel.project(table[Arel.star])
|
1181
1289
|
end
|
@@ -1203,19 +1311,26 @@ module ActiveRecord
|
|
1203
1311
|
from = from_clause.name || from_clause.value
|
1204
1312
|
|
1205
1313
|
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
1206
|
-
|
1314
|
+
table[field]
|
1315
|
+
elsif field.match?(/\A\w+\.\w+\z/)
|
1316
|
+
table, column = field.split(".")
|
1317
|
+
predicate_builder.resolve_arel_attribute(table, column) do
|
1318
|
+
lookup_table_klass_from_join_dependencies(table)
|
1319
|
+
end
|
1207
1320
|
else
|
1208
1321
|
yield field
|
1209
1322
|
end
|
1210
1323
|
end
|
1211
1324
|
|
1212
1325
|
def table_name_matches?(from)
|
1213
|
-
|
1326
|
+
table_name = Regexp.escape(table.name)
|
1327
|
+
quoted_table_name = Regexp.escape(connection.quote_table_name(table.name))
|
1328
|
+
/(?:\A|(?<!FROM)\s)(?:\b#{table_name}\b|#{quoted_table_name})(?!\.)/i.match?(from.to_s)
|
1214
1329
|
end
|
1215
1330
|
|
1216
1331
|
def reverse_sql_order(order_query)
|
1217
1332
|
if order_query.empty?
|
1218
|
-
return [
|
1333
|
+
return [table[primary_key].desc] if primary_key
|
1219
1334
|
raise IrreversibleOrderError,
|
1220
1335
|
"Relation has no current order and table has no primary key to be used as default order"
|
1221
1336
|
end
|
@@ -1226,6 +1341,8 @@ module ActiveRecord
|
|
1226
1341
|
o.desc
|
1227
1342
|
when Arel::Nodes::Ordering
|
1228
1343
|
o.reverse
|
1344
|
+
when Arel::Nodes::NodeExpression
|
1345
|
+
o.desc
|
1229
1346
|
when String
|
1230
1347
|
if does_not_support_reverse?(o)
|
1231
1348
|
raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
|
@@ -1252,9 +1369,7 @@ module ActiveRecord
|
|
1252
1369
|
end
|
1253
1370
|
|
1254
1371
|
def build_order(arel)
|
1255
|
-
orders = order_values.
|
1256
|
-
orders.reject!(&:blank?)
|
1257
|
-
|
1372
|
+
orders = order_values.compact_blank
|
1258
1373
|
arel.order(*orders) unless orders.empty?
|
1259
1374
|
end
|
1260
1375
|
|
@@ -1274,12 +1389,6 @@ module ActiveRecord
|
|
1274
1389
|
end
|
1275
1390
|
|
1276
1391
|
def preprocess_order_args(order_args)
|
1277
|
-
order_args.reject!(&:blank?)
|
1278
|
-
order_args.map! do |arg|
|
1279
|
-
klass.sanitize_sql_for_order(arg)
|
1280
|
-
end
|
1281
|
-
order_args.flatten!
|
1282
|
-
|
1283
1392
|
@klass.disallow_raw_sql!(
|
1284
1393
|
order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
|
1285
1394
|
permit: connection.column_name_with_order_matcher
|
@@ -1287,9 +1396,8 @@ module ActiveRecord
|
|
1287
1396
|
|
1288
1397
|
validate_order_args(order_args)
|
1289
1398
|
|
1290
|
-
references = order_args
|
1291
|
-
|
1292
|
-
references!(references) if references.any?
|
1399
|
+
references = column_references(order_args)
|
1400
|
+
self.references_values |= references unless references.empty?
|
1293
1401
|
|
1294
1402
|
# if a symbol is given we prepend the quoted table name
|
1295
1403
|
order_args.map! do |arg|
|
@@ -1300,9 +1408,9 @@ module ActiveRecord
|
|
1300
1408
|
arg.map { |field, dir|
|
1301
1409
|
case field
|
1302
1410
|
when Arel::Nodes::SqlLiteral
|
1303
|
-
field.
|
1411
|
+
field.public_send(dir.downcase)
|
1304
1412
|
else
|
1305
|
-
order_column(field.to_s).
|
1413
|
+
order_column(field.to_s).public_send(dir.downcase)
|
1306
1414
|
end
|
1307
1415
|
}
|
1308
1416
|
else
|
@@ -1311,16 +1419,54 @@ module ActiveRecord
|
|
1311
1419
|
end.flatten!
|
1312
1420
|
end
|
1313
1421
|
|
1422
|
+
def sanitize_order_arguments(order_args)
|
1423
|
+
order_args.map! do |arg|
|
1424
|
+
klass.sanitize_sql_for_order(arg)
|
1425
|
+
end
|
1426
|
+
order_args.flatten!
|
1427
|
+
order_args.compact_blank!
|
1428
|
+
end
|
1429
|
+
|
1430
|
+
def column_references(order_args)
|
1431
|
+
references = order_args.grep(String)
|
1432
|
+
references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
|
1433
|
+
references
|
1434
|
+
end
|
1435
|
+
|
1314
1436
|
def order_column(field)
|
1315
1437
|
arel_column(field) do |attr_name|
|
1316
1438
|
if attr_name == "count" && !group_values.empty?
|
1317
|
-
|
1439
|
+
table[attr_name]
|
1318
1440
|
else
|
1319
1441
|
Arel.sql(connection.quote_table_name(attr_name))
|
1320
1442
|
end
|
1321
1443
|
end
|
1322
1444
|
end
|
1323
1445
|
|
1446
|
+
def resolve_arel_attributes(attrs)
|
1447
|
+
attrs.flat_map do |attr|
|
1448
|
+
case attr
|
1449
|
+
when Arel::Predications
|
1450
|
+
attr
|
1451
|
+
when Hash
|
1452
|
+
attr.flat_map do |table, columns|
|
1453
|
+
table = table.to_s
|
1454
|
+
Array(columns).map do |column|
|
1455
|
+
predicate_builder.resolve_arel_attribute(table, column)
|
1456
|
+
end
|
1457
|
+
end
|
1458
|
+
else
|
1459
|
+
attr = attr.to_s
|
1460
|
+
if attr.include?(".")
|
1461
|
+
table, column = attr.split(".", 2)
|
1462
|
+
predicate_builder.resolve_arel_attribute(table, column)
|
1463
|
+
else
|
1464
|
+
attr
|
1465
|
+
end
|
1466
|
+
end
|
1467
|
+
end
|
1468
|
+
end
|
1469
|
+
|
1324
1470
|
# Checks to make sure that the arguments are not blank. Note that if some
|
1325
1471
|
# blank-like object were initially passed into the query method, then this
|
1326
1472
|
# method will not raise an error.
|
@@ -1337,35 +1483,40 @@ module ActiveRecord
|
|
1337
1483
|
# check_if_method_has_arguments!("references", args)
|
1338
1484
|
# ...
|
1339
1485
|
# end
|
1340
|
-
def check_if_method_has_arguments!(method_name, args)
|
1486
|
+
def check_if_method_has_arguments!(method_name, args, message = nil)
|
1341
1487
|
if args.blank?
|
1342
|
-
raise ArgumentError, "The method .#{method_name}() must contain arguments."
|
1488
|
+
raise ArgumentError, message || "The method .#{method_name}() must contain arguments."
|
1489
|
+
elsif block_given?
|
1490
|
+
yield args
|
1491
|
+
else
|
1492
|
+
args.flatten!
|
1493
|
+
args.compact_blank!
|
1343
1494
|
end
|
1344
1495
|
end
|
1345
1496
|
|
1346
|
-
|
1347
|
-
|
1497
|
+
STRUCTURAL_VALUE_METHODS = (
|
1498
|
+
Relation::VALUE_METHODS -
|
1499
|
+
[:extending, :where, :having, :unscope, :references, :annotate, :optimizer_hints]
|
1500
|
+
).freeze # :nodoc:
|
1501
|
+
|
1502
|
+
def structurally_incompatible_values_for(other)
|
1348
1503
|
values = other.values
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1504
|
+
STRUCTURAL_VALUE_METHODS.reject do |method|
|
1505
|
+
v1, v2 = @values[method], values[method]
|
1506
|
+
if v1.is_a?(Array)
|
1507
|
+
next true unless v2.is_a?(Array)
|
1508
|
+
v1 = v1.uniq
|
1509
|
+
v2 = v2.uniq
|
1510
|
+
end
|
1511
|
+
v1 == v2
|
1352
1512
|
end
|
1353
1513
|
end
|
1514
|
+
end
|
1354
1515
|
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
DEFAULT_VALUES = {
|
1361
|
-
create_with: FROZEN_EMPTY_HASH,
|
1362
|
-
where: Relation::WhereClause.empty,
|
1363
|
-
having: Relation::WhereClause.empty,
|
1364
|
-
from: Relation::FromClause.empty
|
1365
|
-
}
|
1366
|
-
|
1367
|
-
Relation::MULTI_VALUE_METHODS.each do |value|
|
1368
|
-
DEFAULT_VALUES[value] ||= FROZEN_EMPTY_ARRAY
|
1369
|
-
end
|
1516
|
+
class Relation # :nodoc:
|
1517
|
+
# No-op WhereClauseFactory to work Mashal.load(File.read("legacy_relation.dump")).
|
1518
|
+
# TODO: Remove the class once Rails 6.1 has released.
|
1519
|
+
class WhereClauseFactory # :nodoc:
|
1520
|
+
end
|
1370
1521
|
end
|
1371
1522
|
end
|