activerecord 6.0.6.1 → 6.1.7.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1143 -780
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/active_record/aggregations.rb +5 -5
- data/lib/active_record/association_relation.rb +30 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +49 -26
- data/lib/active_record/associations/association_scope.rb +18 -20
- 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 -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 +32 -18
- data/lib/active_record/associations/collection_proxy.rb +12 -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/join_association.rb +37 -21
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +63 -49
- data/lib/active_record/associations/preloader/association.rb +14 -8
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +5 -3
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations.rb +118 -11
- data/lib/active_record/attribute_assignment.rb +10 -8
- 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 +11 -5
- 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/attribute_methods.rb +64 -54
- data/lib/active_record/attributes.rb +33 -8
- data/lib/active_record/autosave_association.rb +47 -30
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +152 -22
- data/lib/active_record/coders/yaml_column.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -23
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -8
- 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 +114 -26
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +228 -83
- data/lib/active_record/connection_adapters/abstract/transaction.rb +92 -33
- data/lib/active_record/connection_adapters/abstract_adapter.rb +52 -76
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
- 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/database_statements.rb +24 -24
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
- 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 +7 -4
- 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 +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 +14 -53
- 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/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +30 -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 +75 -64
- 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 +32 -5
- 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 +36 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +218 -71
- data/lib/active_record/core.rb +264 -63
- 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 -40
- 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/enum.rb +69 -34
- 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 +58 -9
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +38 -5
- 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 +24 -17
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +27 -8
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +72 -18
- data/lib/active_record/migration.rb +114 -84
- data/lib/active_record/model_schema.rb +89 -14
- 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/console_sandbox.rb +2 -4
- data/lib/active_record/railties/databases.rake +279 -101
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +60 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/calculations.rb +104 -43
- data/lib/active_record/relation/finder_methods.rb +44 -14
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +20 -23
- 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 +61 -38
- data/lib/active_record/relation/query_methods.rb +322 -196
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -7
- data/lib/active_record/relation/where_clause.rb +111 -61
- data/lib/active_record/relation.rb +100 -81
- 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 +2 -8
- data/lib/active_record/scoping/default.rb +1 -3
- data/lib/active_record/scoping/named.rb +1 -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 +8 -3
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -51
- data/lib/active_record/tasks/database_tasks.rb +140 -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 +79 -31
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +19 -66
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type.rb +8 -1
- 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 -1
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +7 -14
- 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 +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- 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/arel/visitors.rb +0 -7
- data/lib/arel.rb +5 -13
- 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/migration.rb +6 -1
- 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 +25 -26
- 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
@@ -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,8 +325,6 @@ module ActiveRecord
|
|
329
325
|
end
|
330
326
|
|
331
327
|
def group!(*args) # :nodoc:
|
332
|
-
args.flatten!
|
333
|
-
|
334
328
|
self.group_values += args
|
335
329
|
self
|
336
330
|
end
|
@@ -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
|
@@ -1013,6 +1048,14 @@ module ActiveRecord
|
|
1013
1048
|
self
|
1014
1049
|
end
|
1015
1050
|
|
1051
|
+
# Deduplicate multiple values.
|
1052
|
+
def uniq!(name)
|
1053
|
+
if values = @values[name]
|
1054
|
+
values.uniq! if values.is_a?(Array) && !values.empty?
|
1055
|
+
end
|
1056
|
+
self
|
1057
|
+
end
|
1058
|
+
|
1016
1059
|
# Returns the Arel object associated with the relation.
|
1017
1060
|
def arel(aliases = nil) # :nodoc:
|
1018
1061
|
@arel ||= build_arel(aliases)
|
@@ -1033,54 +1076,106 @@ module ActiveRecord
|
|
1033
1076
|
end
|
1034
1077
|
end
|
1035
1078
|
|
1079
|
+
def build_where_clause(opts, rest = []) # :nodoc:
|
1080
|
+
opts = sanitize_forbidden_attributes(opts)
|
1081
|
+
|
1082
|
+
case opts
|
1083
|
+
when String, Array
|
1084
|
+
parts = [klass.sanitize_sql(rest.empty? ? opts : [opts, *rest])]
|
1085
|
+
when Hash
|
1086
|
+
opts = opts.transform_keys do |key|
|
1087
|
+
key = key.to_s
|
1088
|
+
klass.attribute_aliases[key] || key
|
1089
|
+
end
|
1090
|
+
references = PredicateBuilder.references(opts)
|
1091
|
+
self.references_values |= references unless references.empty?
|
1092
|
+
|
1093
|
+
parts = predicate_builder.build_from_hash(opts) do |table_name|
|
1094
|
+
lookup_table_klass_from_join_dependencies(table_name)
|
1095
|
+
end
|
1096
|
+
when Arel::Nodes::Node
|
1097
|
+
parts = [opts]
|
1098
|
+
else
|
1099
|
+
raise ArgumentError, "Unsupported argument type: #{opts} (#{opts.class})"
|
1100
|
+
end
|
1101
|
+
|
1102
|
+
Relation::WhereClause.new(parts)
|
1103
|
+
end
|
1104
|
+
alias :build_having_clause :build_where_clause
|
1105
|
+
|
1036
1106
|
private
|
1107
|
+
def lookup_table_klass_from_join_dependencies(table_name)
|
1108
|
+
each_join_dependencies do |join|
|
1109
|
+
return join.base_klass if table_name == join.table_name
|
1110
|
+
end
|
1111
|
+
nil
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
def each_join_dependencies(join_dependencies = build_join_dependencies)
|
1115
|
+
join_dependencies.each do |join_dependency|
|
1116
|
+
join_dependency.each do |join|
|
1117
|
+
yield join
|
1118
|
+
end
|
1119
|
+
end
|
1120
|
+
end
|
1121
|
+
|
1122
|
+
def build_join_dependencies
|
1123
|
+
associations = joins_values | left_outer_joins_values
|
1124
|
+
associations |= eager_load_values unless eager_load_values.empty?
|
1125
|
+
associations |= includes_values unless includes_values.empty?
|
1126
|
+
|
1127
|
+
join_dependencies = []
|
1128
|
+
join_dependencies.unshift construct_join_dependency(
|
1129
|
+
select_association_list(associations, join_dependencies), nil
|
1130
|
+
)
|
1131
|
+
end
|
1132
|
+
|
1037
1133
|
def assert_mutability!
|
1038
1134
|
raise ImmutableRelation if @loaded
|
1039
1135
|
raise ImmutableRelation if defined?(@arel) && @arel
|
1040
1136
|
end
|
1041
1137
|
|
1042
|
-
def build_arel(aliases)
|
1138
|
+
def build_arel(aliases = nil)
|
1043
1139
|
arel = Arel::SelectManager.new(table)
|
1044
1140
|
|
1045
|
-
|
1046
|
-
build_joins(arel, joins_values.flatten, aliases)
|
1047
|
-
elsif !left_outer_joins_values.empty?
|
1048
|
-
build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases)
|
1049
|
-
end
|
1141
|
+
build_joins(arel.join_sources, aliases)
|
1050
1142
|
|
1051
1143
|
arel.where(where_clause.ast) unless where_clause.empty?
|
1052
1144
|
arel.having(having_clause.ast) unless having_clause.empty?
|
1053
|
-
if limit_value
|
1054
|
-
|
1055
|
-
|
1056
|
-
connection.sanitize_limit(limit_value),
|
1057
|
-
Type.default_value,
|
1058
|
-
)
|
1059
|
-
arel.take(Arel::Nodes::BindParam.new(limit_attribute))
|
1060
|
-
end
|
1061
|
-
if offset_value
|
1062
|
-
offset_attribute = ActiveModel::Attribute.with_cast_value(
|
1063
|
-
"OFFSET",
|
1064
|
-
offset_value.to_i,
|
1065
|
-
Type.default_value,
|
1066
|
-
)
|
1067
|
-
arel.skip(Arel::Nodes::BindParam.new(offset_attribute))
|
1068
|
-
end
|
1069
|
-
arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
|
1145
|
+
arel.take(build_cast_value("LIMIT", connection.sanitize_limit(limit_value))) if limit_value
|
1146
|
+
arel.skip(build_cast_value("OFFSET", offset_value.to_i)) if offset_value
|
1147
|
+
arel.group(*arel_columns(group_values.uniq)) unless group_values.empty?
|
1070
1148
|
|
1071
1149
|
build_order(arel)
|
1072
|
-
|
1073
1150
|
build_select(arel)
|
1074
1151
|
|
1075
1152
|
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
1076
1153
|
arel.distinct(distinct_value)
|
1077
1154
|
arel.from(build_from) unless from_clause.empty?
|
1078
1155
|
arel.lock(lock_value) if lock_value
|
1079
|
-
|
1156
|
+
|
1157
|
+
unless annotate_values.empty?
|
1158
|
+
annotates = annotate_values
|
1159
|
+
annotates = annotates.uniq if annotates.size > 1
|
1160
|
+
unless annotates == annotate_values
|
1161
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
1162
|
+
Duplicated query annotations are no longer shown in queries in Rails 7.0.
|
1163
|
+
To migrate to Rails 7.0's behavior, use `uniq!(:annotate)` to deduplicate query annotations
|
1164
|
+
(`#{klass.name&.tableize || klass.table_name}.uniq!(:annotate)`).
|
1165
|
+
MSG
|
1166
|
+
annotates = annotate_values
|
1167
|
+
end
|
1168
|
+
arel.comment(*annotates)
|
1169
|
+
end
|
1080
1170
|
|
1081
1171
|
arel
|
1082
1172
|
end
|
1083
1173
|
|
1174
|
+
def build_cast_value(name, value)
|
1175
|
+
cast_value = ActiveModel::Attribute.with_cast_value(name, value, Type.default_value)
|
1176
|
+
Arel::Nodes::BindParam.new(cast_value)
|
1177
|
+
end
|
1178
|
+
|
1084
1179
|
def build_from
|
1085
1180
|
opts = from_clause.value
|
1086
1181
|
name = from_clause.name
|
@@ -1105,47 +1200,41 @@ module ActiveRecord
|
|
1105
1200
|
when ActiveRecord::Associations::JoinDependency
|
1106
1201
|
stashed_joins&.<< association
|
1107
1202
|
else
|
1108
|
-
yield if block_given?
|
1203
|
+
yield association if block_given?
|
1109
1204
|
end
|
1110
1205
|
end
|
1111
1206
|
result
|
1112
1207
|
end
|
1113
1208
|
|
1114
|
-
def valid_association_list(associations, stashed_joins)
|
1115
|
-
select_association_list(associations, stashed_joins) do
|
1116
|
-
raise ArgumentError, "only Hash, Symbol and Array are allowed"
|
1117
|
-
end
|
1118
|
-
end
|
1119
|
-
|
1120
|
-
def build_left_outer_joins(manager, outer_joins, aliases)
|
1121
|
-
buckets = Hash.new { |h, k| h[k] = [] }
|
1122
|
-
buckets[:association_join] = valid_association_list(outer_joins, buckets[:stashed_join])
|
1123
|
-
build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
|
1124
|
-
end
|
1125
|
-
|
1126
1209
|
class ::Arel::Nodes::LeadingJoin < Arel::Nodes::InnerJoin # :nodoc:
|
1127
1210
|
end
|
1128
1211
|
|
1129
|
-
def
|
1212
|
+
def build_join_buckets
|
1130
1213
|
buckets = Hash.new { |h, k| h[k] = [] }
|
1131
1214
|
|
1132
1215
|
unless left_outer_joins_values.empty?
|
1133
1216
|
stashed_left_joins = []
|
1134
|
-
left_joins =
|
1135
|
-
|
1217
|
+
left_joins = select_association_list(left_outer_joins_values, stashed_left_joins) do
|
1218
|
+
raise ArgumentError, "only Hash, Symbol and Array are allowed"
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
if joins_values.empty?
|
1222
|
+
buckets[:association_join] = left_joins
|
1223
|
+
buckets[:stashed_join] = stashed_left_joins
|
1224
|
+
return buckets, Arel::Nodes::OuterJoin
|
1225
|
+
else
|
1226
|
+
stashed_left_joins.unshift construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
|
1227
|
+
end
|
1136
1228
|
end
|
1137
1229
|
|
1230
|
+
joins = joins_values.dup
|
1138
1231
|
if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
|
1139
1232
|
stashed_eager_load = joins.pop if joins.last.base_klass == klass
|
1140
1233
|
end
|
1141
1234
|
|
1142
|
-
joins.
|
1143
|
-
if join.is_a?(String)
|
1144
|
-
|
1145
|
-
else
|
1146
|
-
join
|
1147
|
-
end
|
1148
|
-
end.delete_if(&:blank?).uniq!
|
1235
|
+
joins.each_with_index do |join, i|
|
1236
|
+
joins[i] = Arel::Nodes::StringJoin.new(Arel.sql(join.strip)) if join.is_a?(String)
|
1237
|
+
end
|
1149
1238
|
|
1150
1239
|
while joins.first.is_a?(Arel::Nodes::Join)
|
1151
1240
|
join_node = joins.shift
|
@@ -1156,13 +1245,8 @@ module ActiveRecord
|
|
1156
1245
|
end
|
1157
1246
|
end
|
1158
1247
|
|
1159
|
-
joins
|
1160
|
-
|
1161
|
-
when Hash, Symbol, Array
|
1162
|
-
buckets[:association_join] << join
|
1163
|
-
when ActiveRecord::Associations::JoinDependency
|
1164
|
-
buckets[:stashed_join] << join
|
1165
|
-
when Arel::Nodes::Join
|
1248
|
+
buckets[:association_join] = select_association_list(joins, buckets[:stashed_join]) do |join|
|
1249
|
+
if join.is_a?(Arel::Nodes::Join)
|
1166
1250
|
buckets[:join_node] << join
|
1167
1251
|
else
|
1168
1252
|
raise "unknown class: %s" % join.class.name
|
@@ -1172,32 +1256,36 @@ module ActiveRecord
|
|
1172
1256
|
buckets[:stashed_join].concat stashed_left_joins if stashed_left_joins
|
1173
1257
|
buckets[:stashed_join] << stashed_eager_load if stashed_eager_load
|
1174
1258
|
|
1175
|
-
|
1259
|
+
return buckets, Arel::Nodes::InnerJoin
|
1176
1260
|
end
|
1177
1261
|
|
1178
|
-
def
|
1262
|
+
def build_joins(join_sources, aliases = nil)
|
1263
|
+
return join_sources if joins_values.empty? && left_outer_joins_values.empty?
|
1264
|
+
|
1265
|
+
buckets, join_type = build_join_buckets
|
1266
|
+
|
1179
1267
|
association_joins = buckets[:association_join]
|
1180
1268
|
stashed_joins = buckets[:stashed_join]
|
1181
1269
|
leading_joins = buckets[:leading_join]
|
1182
1270
|
join_nodes = buckets[:join_node]
|
1183
1271
|
|
1184
|
-
join_sources = manager.join_sources
|
1185
1272
|
join_sources.concat(leading_joins) unless leading_joins.empty?
|
1186
1273
|
|
1187
1274
|
unless association_joins.empty? && stashed_joins.empty?
|
1188
1275
|
alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
|
1189
1276
|
join_dependency = construct_join_dependency(association_joins, join_type)
|
1190
|
-
join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
|
1277
|
+
join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker, references_values))
|
1191
1278
|
end
|
1192
1279
|
|
1193
1280
|
join_sources.concat(join_nodes) unless join_nodes.empty?
|
1281
|
+
join_sources
|
1194
1282
|
end
|
1195
1283
|
|
1196
1284
|
def build_select(arel)
|
1197
1285
|
if select_values.any?
|
1198
|
-
arel.project(*arel_columns(select_values
|
1286
|
+
arel.project(*arel_columns(select_values))
|
1199
1287
|
elsif klass.ignored_columns.any?
|
1200
|
-
arel.project(*klass.column_names.map { |field|
|
1288
|
+
arel.project(*klass.column_names.map { |field| table[field] })
|
1201
1289
|
else
|
1202
1290
|
arel.project(table[Arel.star])
|
1203
1291
|
end
|
@@ -1225,7 +1313,12 @@ module ActiveRecord
|
|
1225
1313
|
from = from_clause.name || from_clause.value
|
1226
1314
|
|
1227
1315
|
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
1228
|
-
|
1316
|
+
table[field]
|
1317
|
+
elsif field.match?(/\A\w+\.\w+\z/)
|
1318
|
+
table, column = field.split(".")
|
1319
|
+
predicate_builder.resolve_arel_attribute(table, column) do
|
1320
|
+
lookup_table_klass_from_join_dependencies(table)
|
1321
|
+
end
|
1229
1322
|
else
|
1230
1323
|
yield field
|
1231
1324
|
end
|
@@ -1239,7 +1332,7 @@ module ActiveRecord
|
|
1239
1332
|
|
1240
1333
|
def reverse_sql_order(order_query)
|
1241
1334
|
if order_query.empty?
|
1242
|
-
return [
|
1335
|
+
return [table[primary_key].desc] if primary_key
|
1243
1336
|
raise IrreversibleOrderError,
|
1244
1337
|
"Relation has no current order and table has no primary key to be used as default order"
|
1245
1338
|
end
|
@@ -1250,6 +1343,8 @@ module ActiveRecord
|
|
1250
1343
|
o.desc
|
1251
1344
|
when Arel::Nodes::Ordering
|
1252
1345
|
o.reverse
|
1346
|
+
when Arel::Nodes::NodeExpression
|
1347
|
+
o.desc
|
1253
1348
|
when String
|
1254
1349
|
if does_not_support_reverse?(o)
|
1255
1350
|
raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
|
@@ -1276,9 +1371,7 @@ module ActiveRecord
|
|
1276
1371
|
end
|
1277
1372
|
|
1278
1373
|
def build_order(arel)
|
1279
|
-
orders = order_values.
|
1280
|
-
orders.reject!(&:blank?)
|
1281
|
-
|
1374
|
+
orders = order_values.compact_blank
|
1282
1375
|
arel.order(*orders) unless orders.empty?
|
1283
1376
|
end
|
1284
1377
|
|
@@ -1298,12 +1391,6 @@ module ActiveRecord
|
|
1298
1391
|
end
|
1299
1392
|
|
1300
1393
|
def preprocess_order_args(order_args)
|
1301
|
-
order_args.reject!(&:blank?)
|
1302
|
-
order_args.map! do |arg|
|
1303
|
-
klass.sanitize_sql_for_order(arg)
|
1304
|
-
end
|
1305
|
-
order_args.flatten!
|
1306
|
-
|
1307
1394
|
@klass.disallow_raw_sql!(
|
1308
1395
|
order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
|
1309
1396
|
permit: connection.column_name_with_order_matcher
|
@@ -1311,9 +1398,8 @@ module ActiveRecord
|
|
1311
1398
|
|
1312
1399
|
validate_order_args(order_args)
|
1313
1400
|
|
1314
|
-
references = order_args
|
1315
|
-
|
1316
|
-
references!(references) if references.any?
|
1401
|
+
references = column_references(order_args)
|
1402
|
+
self.references_values |= references unless references.empty?
|
1317
1403
|
|
1318
1404
|
# if a symbol is given we prepend the quoted table name
|
1319
1405
|
order_args.map! do |arg|
|
@@ -1324,9 +1410,9 @@ module ActiveRecord
|
|
1324
1410
|
arg.map { |field, dir|
|
1325
1411
|
case field
|
1326
1412
|
when Arel::Nodes::SqlLiteral
|
1327
|
-
field.
|
1413
|
+
field.public_send(dir.downcase)
|
1328
1414
|
else
|
1329
|
-
order_column(field.to_s).
|
1415
|
+
order_column(field.to_s).public_send(dir.downcase)
|
1330
1416
|
end
|
1331
1417
|
}
|
1332
1418
|
else
|
@@ -1335,16 +1421,54 @@ module ActiveRecord
|
|
1335
1421
|
end.flatten!
|
1336
1422
|
end
|
1337
1423
|
|
1424
|
+
def sanitize_order_arguments(order_args)
|
1425
|
+
order_args.map! do |arg|
|
1426
|
+
klass.sanitize_sql_for_order(arg)
|
1427
|
+
end
|
1428
|
+
order_args.flatten!
|
1429
|
+
order_args.compact_blank!
|
1430
|
+
end
|
1431
|
+
|
1432
|
+
def column_references(order_args)
|
1433
|
+
references = order_args.grep(String)
|
1434
|
+
references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
|
1435
|
+
references
|
1436
|
+
end
|
1437
|
+
|
1338
1438
|
def order_column(field)
|
1339
1439
|
arel_column(field) do |attr_name|
|
1340
1440
|
if attr_name == "count" && !group_values.empty?
|
1341
|
-
|
1441
|
+
table[attr_name]
|
1342
1442
|
else
|
1343
1443
|
Arel.sql(connection.quote_table_name(attr_name))
|
1344
1444
|
end
|
1345
1445
|
end
|
1346
1446
|
end
|
1347
1447
|
|
1448
|
+
def resolve_arel_attributes(attrs)
|
1449
|
+
attrs.flat_map do |attr|
|
1450
|
+
case attr
|
1451
|
+
when Arel::Predications
|
1452
|
+
attr
|
1453
|
+
when Hash
|
1454
|
+
attr.flat_map do |table, columns|
|
1455
|
+
table = table.to_s
|
1456
|
+
Array(columns).map do |column|
|
1457
|
+
predicate_builder.resolve_arel_attribute(table, column)
|
1458
|
+
end
|
1459
|
+
end
|
1460
|
+
else
|
1461
|
+
attr = attr.to_s
|
1462
|
+
if attr.include?(".")
|
1463
|
+
table, column = attr.split(".", 2)
|
1464
|
+
predicate_builder.resolve_arel_attribute(table, column)
|
1465
|
+
else
|
1466
|
+
attr
|
1467
|
+
end
|
1468
|
+
end
|
1469
|
+
end
|
1470
|
+
end
|
1471
|
+
|
1348
1472
|
# Checks to make sure that the arguments are not blank. Note that if some
|
1349
1473
|
# blank-like object were initially passed into the query method, then this
|
1350
1474
|
# method will not raise an error.
|
@@ -1361,38 +1485,40 @@ module ActiveRecord
|
|
1361
1485
|
# check_if_method_has_arguments!("references", args)
|
1362
1486
|
# ...
|
1363
1487
|
# end
|
1364
|
-
def check_if_method_has_arguments!(method_name, args)
|
1488
|
+
def check_if_method_has_arguments!(method_name, args, message = nil)
|
1365
1489
|
if args.blank?
|
1366
|
-
raise ArgumentError, "The method .#{method_name}() must contain arguments."
|
1490
|
+
raise ArgumentError, message || "The method .#{method_name}() must contain arguments."
|
1491
|
+
elsif block_given?
|
1492
|
+
yield args
|
1493
|
+
else
|
1494
|
+
args.flatten!
|
1495
|
+
args.compact_blank!
|
1367
1496
|
end
|
1368
1497
|
end
|
1369
1498
|
|
1370
|
-
|
1371
|
-
|
1499
|
+
STRUCTURAL_VALUE_METHODS = (
|
1500
|
+
Relation::VALUE_METHODS -
|
1501
|
+
[:extending, :where, :having, :unscope, :references, :annotate, :optimizer_hints]
|
1502
|
+
).freeze # :nodoc:
|
1503
|
+
|
1504
|
+
def structurally_incompatible_values_for(other)
|
1372
1505
|
values = other.values
|
1373
|
-
|
1374
|
-
|
1375
|
-
v1
|
1376
|
-
|
1377
|
-
|
1506
|
+
STRUCTURAL_VALUE_METHODS.reject do |method|
|
1507
|
+
v1, v2 = @values[method], values[method]
|
1508
|
+
if v1.is_a?(Array)
|
1509
|
+
next true unless v2.is_a?(Array)
|
1510
|
+
v1 = v1.uniq
|
1511
|
+
v2 = v2.uniq
|
1512
|
+
end
|
1378
1513
|
v1 == v2
|
1379
1514
|
end
|
1380
1515
|
end
|
1516
|
+
end
|
1381
1517
|
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
DEFAULT_VALUES = {
|
1388
|
-
create_with: FROZEN_EMPTY_HASH,
|
1389
|
-
where: Relation::WhereClause.empty,
|
1390
|
-
having: Relation::WhereClause.empty,
|
1391
|
-
from: Relation::FromClause.empty
|
1392
|
-
}
|
1393
|
-
|
1394
|
-
Relation::MULTI_VALUE_METHODS.each do |value|
|
1395
|
-
DEFAULT_VALUES[value] ||= FROZEN_EMPTY_ARRAY
|
1396
|
-
end
|
1518
|
+
class Relation # :nodoc:
|
1519
|
+
# No-op WhereClauseFactory to work Mashal.load(File.read("legacy_relation.dump")).
|
1520
|
+
# TODO: Remove the class once Rails 6.1 has released.
|
1521
|
+
class WhereClauseFactory # :nodoc:
|
1522
|
+
end
|
1397
1523
|
end
|
1398
1524
|
end
|