activerecord 4.2.11.3 → 5.0.0.beta1
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 +5 -5
- data/CHANGELOG.md +1029 -1349
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -7
- data/examples/performance.rb +2 -2
- data/lib/active_record.rb +7 -3
- data/lib/active_record/aggregations.rb +35 -25
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations.rb +305 -204
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +10 -8
- data/lib/active_record/associations/association_scope.rb +73 -102
- data/lib/active_record/associations/belongs_to_association.rb +20 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +41 -18
- data/lib/active_record/associations/builder/collection_association.rb +8 -24
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +11 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +10 -5
- data/lib/active_record/associations/builder/singular_association.rb +2 -9
- data/lib/active_record/associations/collection_association.rb +40 -43
- data/lib/active_record/associations/collection_proxy.rb +55 -29
- data/lib/active_record/associations/foreign_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +20 -71
- data/lib/active_record/associations/has_many_through_association.rb +8 -52
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +28 -18
- data/lib/active_record/associations/join_dependency/join_association.rb +13 -12
- data/lib/active_record/associations/preloader.rb +13 -4
- data/lib/active_record/associations/preloader/association.rb +45 -51
- data/lib/active_record/associations/preloader/collection_association.rb +0 -6
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/through_association.rb +5 -4
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +11 -3
- data/lib/active_record/attribute.rb +61 -17
- data/lib/active_record/attribute/user_provided_default.rb +23 -0
- data/lib/active_record/attribute_assignment.rb +27 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods.rb +79 -26
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +46 -86
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +26 -42
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +42 -9
- data/lib/active_record/attribute_methods/write.rb +13 -24
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set.rb +30 -3
- data/lib/active_record/attribute_set/builder.rb +6 -4
- data/lib/active_record/attributes.rb +194 -81
- data/lib/active_record/autosave_association.rb +33 -15
- data/lib/active_record/base.rb +30 -18
- data/lib/active_record/callbacks.rb +36 -40
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +31 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +431 -122
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +40 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -8
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -38
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +229 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +275 -115
- data/lib/active_record/connection_adapters/abstract/transaction.rb +32 -33
- data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -32
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +384 -221
- data/lib/active_record/connection_adapters/column.rb +27 -41
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -21
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +57 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +69 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +22 -101
- data/lib/active_record/connection_adapters/postgresql/column.rb +6 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +23 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +23 -16
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -11
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +174 -128
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -112
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +15 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +134 -110
- data/lib/active_record/connection_adapters/statement_pool.rb +28 -11
- data/lib/active_record/connection_handling.rb +5 -5
- data/lib/active_record/core.rb +72 -104
- data/lib/active_record/counter_cache.rb +9 -20
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +110 -76
- data/lib/active_record/errors.rb +72 -47
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +19 -4
- data/lib/active_record/fixtures.rb +76 -40
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +27 -40
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +18 -2
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +10 -14
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +40 -22
- data/lib/active_record/migration.rb +304 -133
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +90 -0
- data/lib/active_record/model_schema.rb +92 -40
- data/lib/active_record/nested_attributes.rb +45 -34
- data/lib/active_record/null_relation.rb +15 -7
- data/lib/active_record/persistence.rb +112 -72
- data/lib/active_record/querying.rb +6 -5
- data/lib/active_record/railtie.rb +20 -13
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +47 -38
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +182 -57
- data/lib/active_record/relation.rb +152 -100
- data/lib/active_record/relation/batches.rb +133 -33
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +80 -101
- data/lib/active_record/relation/delegation.rb +6 -19
- data/lib/active_record/relation/finder_methods.rb +58 -46
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +13 -42
- data/lib/active_record/relation/predicate_builder.rb +99 -105
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +78 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +17 -0
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +274 -238
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +3 -6
- data/lib/active_record/relation/where_clause.rb +173 -0
- data/lib/active_record/relation/where_clause_factory.rb +37 -0
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +94 -65
- data/lib/active_record/schema.rb +23 -22
- data/lib/active_record/schema_dumper.rb +33 -22
- data/lib/active_record/schema_migration.rb +10 -4
- data/lib/active_record/scoping.rb +17 -6
- data/lib/active_record/scoping/default.rb +19 -6
- data/lib/active_record/scoping/named.rb +39 -28
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +15 -13
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +54 -0
- data/lib/active_record/table_metadata.rb +64 -0
- data/lib/active_record/tasks/database_tasks.rb +30 -40
- data/lib/active_record/tasks/mysql_database_tasks.rb +7 -15
- data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +16 -9
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +138 -56
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -45
- data/lib/active_record/type/date_time.rb +2 -49
- data/lib/active_record/type/internal/abstract_json.rb +33 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +9 -14
- data/lib/active_record/type/time.rb +3 -21
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record/validations/absence.rb +24 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +36 -0
- data/lib/active_record/validations/presence.rb +12 -12
- data/lib/active_record/validations/uniqueness.rb +24 -21
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +4 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +21 -15
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +50 -35
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decimal_without_scale.rb +0 -11
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/text.rb +0 -11
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/unsigned_integer.rb +0 -15
- data/lib/active_record/type/value.rb +0 -110
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'active_record/attribute'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class Relation
|
5
|
+
class QueryAttribute < Attribute # :nodoc:
|
6
|
+
def type_cast(value)
|
7
|
+
value
|
8
|
+
end
|
9
|
+
|
10
|
+
def value_for_database
|
11
|
+
@value_for_database ||= super
|
12
|
+
end
|
13
|
+
|
14
|
+
def with_cast_value(value)
|
15
|
+
QueryAttribute.new(name, value, type)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,6 +1,9 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "active_record/relation/from_clause"
|
2
|
+
require "active_record/relation/query_attribute"
|
3
|
+
require "active_record/relation/where_clause"
|
4
|
+
require "active_record/relation/where_clause_factory"
|
3
5
|
require 'active_model/forbidden_attributes_protection'
|
6
|
+
require 'active_support/core_ext/string/filters'
|
4
7
|
|
5
8
|
module ActiveRecord
|
6
9
|
module QueryMethods
|
@@ -11,6 +14,8 @@ module ActiveRecord
|
|
11
14
|
# WhereChain objects act as placeholder for queries in which #where does not have any parameter.
|
12
15
|
# In this case, #where must be chained with #not to return a new relation.
|
13
16
|
class WhereChain
|
17
|
+
include ActiveModel::ForbiddenAttributesProtection
|
18
|
+
|
14
19
|
def initialize(scope)
|
15
20
|
@scope = scope
|
16
21
|
end
|
@@ -18,7 +23,7 @@ module ActiveRecord
|
|
18
23
|
# Returns a new relation expressing WHERE + NOT condition according to
|
19
24
|
# the conditions in the arguments.
|
20
25
|
#
|
21
|
-
#
|
26
|
+
# #not accepts conditions as a string, array, or hash. See QueryMethods#where for
|
22
27
|
# more details on each format.
|
23
28
|
#
|
24
29
|
# User.where.not("name = 'Jon'")
|
@@ -39,38 +44,26 @@ module ActiveRecord
|
|
39
44
|
# User.where.not(name: "Jon", role: "admin")
|
40
45
|
# # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
|
41
46
|
def not(opts, *rest)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
raise ArgumentError, 'Invalid argument for .where.not(), got nil.'
|
46
|
-
when Arel::Nodes::In
|
47
|
-
Arel::Nodes::NotIn.new(rel.left, rel.right)
|
48
|
-
when Arel::Nodes::Equality
|
49
|
-
Arel::Nodes::NotEqual.new(rel.left, rel.right)
|
50
|
-
when String
|
51
|
-
Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(rel))
|
52
|
-
else
|
53
|
-
Arel::Nodes::Not.new(rel)
|
54
|
-
end
|
55
|
-
end
|
47
|
+
opts = sanitize_forbidden_attributes(opts)
|
48
|
+
|
49
|
+
where_clause = @scope.send(:where_clause_factory).build(opts, rest)
|
56
50
|
|
57
51
|
@scope.references!(PredicateBuilder.references(opts)) if Hash === opts
|
58
|
-
@scope.
|
52
|
+
@scope.where_clause += where_clause.invert
|
59
53
|
@scope
|
60
54
|
end
|
61
55
|
end
|
62
56
|
|
63
57
|
Relation::MULTI_VALUE_METHODS.each do |name|
|
64
58
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
65
|
-
def #{name}_values
|
66
|
-
@values[:#{name}] || []
|
67
|
-
end
|
68
|
-
|
69
|
-
def #{name}_values=(values)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end # end
|
59
|
+
def #{name}_values # def select_values
|
60
|
+
@values[:#{name}] || [] # @values[:select] || []
|
61
|
+
end # end
|
62
|
+
#
|
63
|
+
def #{name}_values=(values) # def select_values=(values)
|
64
|
+
assert_mutability! # assert_mutability!
|
65
|
+
@values[:#{name}] = values # @values[:select] = values
|
66
|
+
end # end
|
74
67
|
CODE
|
75
68
|
end
|
76
69
|
|
@@ -85,21 +78,42 @@ module ActiveRecord
|
|
85
78
|
Relation::SINGLE_VALUE_METHODS.each do |name|
|
86
79
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
87
80
|
def #{name}_value=(value) # def readonly_value=(value)
|
88
|
-
|
89
|
-
check_cached_relation
|
81
|
+
assert_mutability! # assert_mutability!
|
90
82
|
@values[:#{name}] = value # @values[:readonly] = value
|
91
83
|
end # end
|
92
84
|
CODE
|
93
85
|
end
|
94
86
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
87
|
+
Relation::CLAUSE_METHODS.each do |name|
|
88
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
89
|
+
def #{name}_clause # def where_clause
|
90
|
+
@values[:#{name}] || new_#{name}_clause # @values[:where] || new_where_clause
|
91
|
+
end # end
|
92
|
+
#
|
93
|
+
def #{name}_clause=(value) # def where_clause=(value)
|
94
|
+
assert_mutability! # assert_mutability!
|
95
|
+
@values[:#{name}] = value # @values[:where] = value
|
96
|
+
end # end
|
97
|
+
CODE
|
98
|
+
end
|
99
|
+
|
100
|
+
def bound_attributes
|
101
|
+
result = from_clause.binds + arel.bind_values + where_clause.binds + having_clause.binds
|
102
|
+
if limit_value && !string_containing_comma?(limit_value)
|
103
|
+
result << Attribute.with_cast_value(
|
104
|
+
"LIMIT".freeze,
|
105
|
+
connection.sanitize_limit(limit_value),
|
106
|
+
Type::Value.new,
|
107
|
+
)
|
108
|
+
end
|
109
|
+
if offset_value
|
110
|
+
result << Attribute.with_cast_value(
|
111
|
+
"OFFSET".freeze,
|
112
|
+
offset_value.to_i,
|
113
|
+
Type::Value.new,
|
114
|
+
)
|
102
115
|
end
|
116
|
+
result
|
103
117
|
end
|
104
118
|
|
105
119
|
def create_with_value # :nodoc:
|
@@ -118,7 +132,7 @@ module ActiveRecord
|
|
118
132
|
#
|
119
133
|
# allows you to access the +address+ attribute of the +User+ model without
|
120
134
|
# firing an additional query. This will often result in a
|
121
|
-
# performance improvement over a simple
|
135
|
+
# performance improvement over a simple join.
|
122
136
|
#
|
123
137
|
# You can also specify multiple relationships, like this:
|
124
138
|
#
|
@@ -139,7 +153,7 @@ module ActiveRecord
|
|
139
153
|
#
|
140
154
|
# User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
|
141
155
|
#
|
142
|
-
# Note that
|
156
|
+
# Note that #includes works with association names while #references needs
|
143
157
|
# the actual table name.
|
144
158
|
def includes(*args)
|
145
159
|
check_if_method_has_arguments!(:includes, args)
|
@@ -157,9 +171,9 @@ module ActiveRecord
|
|
157
171
|
# Forces eager loading by performing a LEFT OUTER JOIN on +args+:
|
158
172
|
#
|
159
173
|
# User.eager_load(:posts)
|
160
|
-
#
|
161
|
-
# FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
|
162
|
-
# "users"."id"
|
174
|
+
# # SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ...
|
175
|
+
# # FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
|
176
|
+
# # "users"."id"
|
163
177
|
def eager_load(*args)
|
164
178
|
check_if_method_has_arguments!(:eager_load, args)
|
165
179
|
spawn.eager_load!(*args)
|
@@ -170,10 +184,10 @@ module ActiveRecord
|
|
170
184
|
self
|
171
185
|
end
|
172
186
|
|
173
|
-
# Allows preloading of +args+, in the same way that
|
187
|
+
# Allows preloading of +args+, in the same way that #includes does:
|
174
188
|
#
|
175
189
|
# User.preload(:posts)
|
176
|
-
#
|
190
|
+
# # SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
|
177
191
|
def preload(*args)
|
178
192
|
check_if_method_has_arguments!(:preload, args)
|
179
193
|
spawn.preload!(*args)
|
@@ -186,14 +200,14 @@ module ActiveRecord
|
|
186
200
|
|
187
201
|
# Use to indicate that the given +table_names+ are referenced by an SQL string,
|
188
202
|
# and should therefore be JOINed in any query rather than loaded separately.
|
189
|
-
# This method only works in conjunction with
|
203
|
+
# This method only works in conjunction with #includes.
|
190
204
|
# See #includes for more details.
|
191
205
|
#
|
192
206
|
# User.includes(:posts).where("posts.name = 'foo'")
|
193
|
-
# #
|
207
|
+
# # Doesn't JOIN the posts table, resulting in an error.
|
194
208
|
#
|
195
209
|
# User.includes(:posts).where("posts.name = 'foo'").references(:posts)
|
196
|
-
# #
|
210
|
+
# # Query now knows the string references posts, so adds a JOIN
|
197
211
|
def references(*table_names)
|
198
212
|
check_if_method_has_arguments!(:references, table_names)
|
199
213
|
spawn.references!(*table_names)
|
@@ -209,12 +223,12 @@ module ActiveRecord
|
|
209
223
|
|
210
224
|
# Works in two unique ways.
|
211
225
|
#
|
212
|
-
# First: takes a block so it can be used just like Array#select
|
226
|
+
# First: takes a block so it can be used just like +Array#select+.
|
213
227
|
#
|
214
228
|
# Model.all.select { |m| m.field == value }
|
215
229
|
#
|
216
230
|
# This will build an array of objects from the database for the scope,
|
217
|
-
# converting them into an array and iterating through them using Array#select
|
231
|
+
# converting them into an array and iterating through them using +Array#select+.
|
218
232
|
#
|
219
233
|
# Second: Modifies the SELECT statement for the query so that only certain
|
220
234
|
# fields are retrieved:
|
@@ -242,17 +256,14 @@ module ActiveRecord
|
|
242
256
|
# # => "value"
|
243
257
|
#
|
244
258
|
# Accessing attributes of an object that do not have fields retrieved by a select
|
245
|
-
# except +id+ will throw
|
259
|
+
# except +id+ will throw ActiveModel::MissingAttributeError:
|
246
260
|
#
|
247
261
|
# Model.select(:field).first.other_field
|
248
262
|
# # => ActiveModel::MissingAttributeError: missing attribute: other_field
|
249
263
|
def select(*fields)
|
250
|
-
if block_given?
|
251
|
-
|
252
|
-
|
253
|
-
raise ArgumentError, 'Call this with at least one field' if fields.empty?
|
254
|
-
spawn._select!(*fields)
|
255
|
-
end
|
264
|
+
return super if block_given?
|
265
|
+
raise ArgumentError, 'Call this with at least one field' if fields.empty?
|
266
|
+
spawn._select!(*fields)
|
256
267
|
end
|
257
268
|
|
258
269
|
def _select!(*fields) # :nodoc:
|
@@ -267,22 +278,23 @@ module ActiveRecord
|
|
267
278
|
# Allows to specify a group attribute:
|
268
279
|
#
|
269
280
|
# User.group(:name)
|
270
|
-
#
|
281
|
+
# # SELECT "users".* FROM "users" GROUP BY name
|
271
282
|
#
|
272
283
|
# Returns an array with distinct records based on the +group+ attribute:
|
273
284
|
#
|
274
285
|
# User.select([:id, :name])
|
275
|
-
# => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">
|
286
|
+
# # => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">]
|
276
287
|
#
|
277
288
|
# User.group(:name)
|
278
|
-
# => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
|
289
|
+
# # => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
|
279
290
|
#
|
280
291
|
# User.group('name AS grouped_name, age')
|
281
|
-
# => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
|
292
|
+
# # => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
|
282
293
|
#
|
283
294
|
# Passing in an array of attributes to group by is also supported.
|
295
|
+
#
|
284
296
|
# User.select([:id, :first_name]).group(:id, :first_name).first(3)
|
285
|
-
# => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
|
297
|
+
# # => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
|
286
298
|
def group(*args)
|
287
299
|
check_if_method_has_arguments!(:group, args)
|
288
300
|
spawn.group!(*args)
|
@@ -298,22 +310,22 @@ module ActiveRecord
|
|
298
310
|
# Allows to specify an order attribute:
|
299
311
|
#
|
300
312
|
# User.order(:name)
|
301
|
-
#
|
313
|
+
# # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
|
302
314
|
#
|
303
315
|
# User.order(email: :desc)
|
304
|
-
#
|
316
|
+
# # SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
|
305
317
|
#
|
306
318
|
# User.order(:name, email: :desc)
|
307
|
-
#
|
319
|
+
# # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
|
308
320
|
#
|
309
321
|
# User.order('name')
|
310
|
-
#
|
322
|
+
# # SELECT "users".* FROM "users" ORDER BY name
|
311
323
|
#
|
312
324
|
# User.order('name DESC')
|
313
|
-
#
|
325
|
+
# # SELECT "users".* FROM "users" ORDER BY name DESC
|
314
326
|
#
|
315
327
|
# User.order('name DESC, email')
|
316
|
-
#
|
328
|
+
# # SELECT "users".* FROM "users" ORDER BY name DESC, email
|
317
329
|
def order(*args)
|
318
330
|
check_if_method_has_arguments!(:order, args)
|
319
331
|
spawn.order!(*args)
|
@@ -365,15 +377,15 @@ module ActiveRecord
|
|
365
377
|
# User.order('email DESC').select('id').where(name: "John")
|
366
378
|
# .unscope(:order, :select, :where) == User.all
|
367
379
|
#
|
368
|
-
# One can additionally pass a hash as an argument to unscope specific
|
380
|
+
# One can additionally pass a hash as an argument to unscope specific +:where+ values.
|
369
381
|
# This is done by passing a hash with a single key-value pair. The key should be
|
370
|
-
#
|
382
|
+
# +:where+ and the value should be the where value to unscope. For example:
|
371
383
|
#
|
372
384
|
# User.where(name: "John", active: true).unscope(where: :name)
|
373
385
|
# == User.where(active: true)
|
374
386
|
#
|
375
|
-
# This method is similar to
|
376
|
-
#
|
387
|
+
# This method is similar to #except, but unlike
|
388
|
+
# #except, it persists across merges:
|
377
389
|
#
|
378
390
|
# User.order('email').merge(User.except(:order))
|
379
391
|
# == User.order('email')
|
@@ -383,7 +395,7 @@ module ActiveRecord
|
|
383
395
|
#
|
384
396
|
# This means it can be used in association definitions:
|
385
397
|
#
|
386
|
-
# has_many :comments, -> { unscope
|
398
|
+
# has_many :comments, -> { unscope(where: :trashed) }
|
387
399
|
#
|
388
400
|
def unscope(*args)
|
389
401
|
check_if_method_has_arguments!(:unscope, args)
|
@@ -404,9 +416,8 @@ module ActiveRecord
|
|
404
416
|
raise ArgumentError, "Hash arguments in .unscope(*args) must have :where as the key."
|
405
417
|
end
|
406
418
|
|
407
|
-
Array(target_value).
|
408
|
-
|
409
|
-
end
|
419
|
+
target_values = Array(target_value).map(&:to_s)
|
420
|
+
self.where_clause = where_clause.except(*target_values)
|
410
421
|
end
|
411
422
|
else
|
412
423
|
raise ArgumentError, "Unrecognized scoping: #{args.inspect}. Use .unscope(where: :attribute_name) or .unscope(:order), for example."
|
@@ -416,15 +427,35 @@ module ActiveRecord
|
|
416
427
|
self
|
417
428
|
end
|
418
429
|
|
419
|
-
# Performs a joins on +args
|
430
|
+
# Performs a joins on +args+. The given symbol(s) should match the name of
|
431
|
+
# the association(s).
|
420
432
|
#
|
421
433
|
# User.joins(:posts)
|
422
|
-
#
|
434
|
+
# # SELECT "users".*
|
435
|
+
# # FROM "users"
|
436
|
+
# # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
437
|
+
#
|
438
|
+
# Multiple joins:
|
439
|
+
#
|
440
|
+
# User.joins(:posts, :account)
|
441
|
+
# # SELECT "users".*
|
442
|
+
# # FROM "users"
|
443
|
+
# # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
444
|
+
# # INNER JOIN "accounts" ON "accounts"."id" = "users"."account_id"
|
445
|
+
#
|
446
|
+
# Nested joins:
|
447
|
+
#
|
448
|
+
# User.joins(posts: [:comments])
|
449
|
+
# # SELECT "users".*
|
450
|
+
# # FROM "users"
|
451
|
+
# # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
452
|
+
# # INNER JOIN "comments" "comments_posts"
|
453
|
+
# # ON "comments_posts"."post_id" = "posts"."id"
|
423
454
|
#
|
424
455
|
# You can use strings in order to customize your joins:
|
425
456
|
#
|
426
457
|
# User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id")
|
427
|
-
#
|
458
|
+
# # SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
|
428
459
|
def joins(*args)
|
429
460
|
check_if_method_has_arguments!(:joins, args)
|
430
461
|
spawn.joins!(*args)
|
@@ -437,14 +468,26 @@ module ActiveRecord
|
|
437
468
|
self
|
438
469
|
end
|
439
470
|
|
440
|
-
|
441
|
-
|
471
|
+
# Performs a left outer joins on +args+:
|
472
|
+
#
|
473
|
+
# User.left_outer_joins(:posts)
|
474
|
+
# => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
475
|
+
#
|
476
|
+
def left_outer_joins(*args)
|
477
|
+
check_if_method_has_arguments!(:left_outer_joins, args)
|
478
|
+
|
479
|
+
args.compact!
|
480
|
+
args.flatten!
|
481
|
+
|
482
|
+
spawn.left_outer_joins!(*args)
|
442
483
|
end
|
484
|
+
alias :left_joins :left_outer_joins
|
443
485
|
|
444
|
-
def
|
445
|
-
self.
|
486
|
+
def left_outer_joins!(*args) # :nodoc:
|
487
|
+
self.left_outer_joins_values += args
|
446
488
|
self
|
447
489
|
end
|
490
|
+
alias :left_joins! :left_outer_joins!
|
448
491
|
|
449
492
|
# Returns a new relation, which is the result of filtering the current relation
|
450
493
|
# according to the conditions in the arguments.
|
@@ -489,7 +532,7 @@ module ActiveRecord
|
|
489
532
|
# than the previous methods; you are responsible for ensuring that the values in the template
|
490
533
|
# are properly quoted. The values are passed to the connector for quoting, but the caller
|
491
534
|
# is responsible for ensuring they are enclosed in quotes in the resulting SQL. After quoting,
|
492
|
-
# the values are inserted using the same escapes as the Ruby core method
|
535
|
+
# the values are inserted using the same escapes as the Ruby core method +Kernel::sprintf+.
|
493
536
|
#
|
494
537
|
# User.where(["name = '%s' and email = '%s'", "Joe", "joe@example.com"])
|
495
538
|
# # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
|
@@ -566,7 +609,7 @@ module ActiveRecord
|
|
566
609
|
# If the condition is any blank-ish object, then #where is a no-op and returns
|
567
610
|
# the current relation.
|
568
611
|
def where(opts = :chain, *rest)
|
569
|
-
if
|
612
|
+
if :chain == opts
|
570
613
|
WhereChain.new(spawn)
|
571
614
|
elsif opts.blank?
|
572
615
|
self
|
@@ -576,27 +619,54 @@ module ActiveRecord
|
|
576
619
|
end
|
577
620
|
|
578
621
|
def where!(opts, *rest) # :nodoc:
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
end
|
583
|
-
|
584
|
-
self.where_values += build_where(opts, rest)
|
622
|
+
opts = sanitize_forbidden_attributes(opts)
|
623
|
+
references!(PredicateBuilder.references(opts)) if Hash === opts
|
624
|
+
self.where_clause += where_clause_factory.build(opts, rest)
|
585
625
|
self
|
586
626
|
end
|
587
627
|
|
588
628
|
# Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
|
589
629
|
#
|
590
|
-
# Post.where(trashed: true).where(trashed: false)
|
591
|
-
#
|
592
|
-
#
|
630
|
+
# Post.where(trashed: true).where(trashed: false)
|
631
|
+
# # WHERE `trashed` = 1 AND `trashed` = 0
|
632
|
+
#
|
633
|
+
# Post.where(trashed: true).rewhere(trashed: false)
|
634
|
+
# # WHERE `trashed` = 0
|
593
635
|
#
|
594
|
-
#
|
595
|
-
#
|
636
|
+
# Post.where(active: true).where(trashed: true).rewhere(trashed: false)
|
637
|
+
# # WHERE `active` = 1 AND `trashed` = 0
|
638
|
+
#
|
639
|
+
# This is short-hand for <tt>unscope(where: conditions.keys).where(conditions)</tt>.
|
640
|
+
# Note that unlike reorder, we're only unscoping the named conditions -- not the entire where statement.
|
596
641
|
def rewhere(conditions)
|
597
642
|
unscope(where: conditions.keys).where(conditions)
|
598
643
|
end
|
599
644
|
|
645
|
+
# Returns a new relation, which is the logical union of this relation and the one passed as an
|
646
|
+
# argument.
|
647
|
+
#
|
648
|
+
# The two relations must be structurally compatible: they must be scoping the same model, and
|
649
|
+
# they must differ only by #where (if no #group has been defined) or #having (if a #group is
|
650
|
+
# present). Neither relation may have a #limit, #offset, or #distinct set.
|
651
|
+
#
|
652
|
+
# Post.where("id = 1").or(Post.where("id = 2"))
|
653
|
+
# # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'id = 2'))
|
654
|
+
#
|
655
|
+
def or(other)
|
656
|
+
spawn.or!(other)
|
657
|
+
end
|
658
|
+
|
659
|
+
def or!(other) # :nodoc:
|
660
|
+
unless structurally_compatible_for_or?(other)
|
661
|
+
raise ArgumentError, 'Relation passed to #or must be structurally compatible'
|
662
|
+
end
|
663
|
+
|
664
|
+
self.where_clause = self.where_clause.or(other.where_clause)
|
665
|
+
self.having_clause = self.having_clause.or(other.having_clause)
|
666
|
+
|
667
|
+
self
|
668
|
+
end
|
669
|
+
|
600
670
|
# Allows to specify a HAVING clause. Note that you can't use HAVING
|
601
671
|
# without also specifying a GROUP clause.
|
602
672
|
#
|
@@ -606,9 +676,10 @@ module ActiveRecord
|
|
606
676
|
end
|
607
677
|
|
608
678
|
def having!(opts, *rest) # :nodoc:
|
679
|
+
opts = sanitize_forbidden_attributes(opts)
|
609
680
|
references!(PredicateBuilder.references(opts)) if Hash === opts
|
610
681
|
|
611
|
-
self.
|
682
|
+
self.having_clause += having_clause_factory.build(opts, rest)
|
612
683
|
self
|
613
684
|
end
|
614
685
|
|
@@ -622,6 +693,13 @@ module ActiveRecord
|
|
622
693
|
end
|
623
694
|
|
624
695
|
def limit!(value) # :nodoc:
|
696
|
+
if string_containing_comma?(value)
|
697
|
+
# Remove `string_containing_comma?` when removing this deprecation
|
698
|
+
ActiveSupport::Deprecation.warn(<<-WARNING.squish)
|
699
|
+
Passing a string to limit in the form "1,2" is deprecated and will be
|
700
|
+
removed in Rails 5.1. Please call `offset` explicitly instead.
|
701
|
+
WARNING
|
702
|
+
end
|
625
703
|
self.limit_value = value
|
626
704
|
self
|
627
705
|
end
|
@@ -643,7 +721,7 @@ module ActiveRecord
|
|
643
721
|
end
|
644
722
|
|
645
723
|
# Specifies locking settings (default to +true+). For more information
|
646
|
-
# on locking, please see
|
724
|
+
# on locking, please see ActiveRecord::Locking.
|
647
725
|
def lock(locks = true)
|
648
726
|
spawn.lock!(locks)
|
649
727
|
end
|
@@ -674,7 +752,7 @@ module ActiveRecord
|
|
674
752
|
# For example:
|
675
753
|
#
|
676
754
|
# @posts = current_user.visible_posts.where(name: params[:name])
|
677
|
-
# #
|
755
|
+
# # the visible_posts method is expected to return a chainable Relation
|
678
756
|
#
|
679
757
|
# def visible_posts
|
680
758
|
# case role
|
@@ -700,7 +778,7 @@ module ActiveRecord
|
|
700
778
|
#
|
701
779
|
# users = User.readonly
|
702
780
|
# users.first.save
|
703
|
-
# => ActiveRecord::ReadOnlyRecord:
|
781
|
+
# => ActiveRecord::ReadOnlyRecord: User is marked as readonly
|
704
782
|
def readonly(value = true)
|
705
783
|
spawn.readonly!(value)
|
706
784
|
end
|
@@ -719,7 +797,7 @@ module ActiveRecord
|
|
719
797
|
# users = users.create_with(name: 'DHH')
|
720
798
|
# users.new.name # => 'DHH'
|
721
799
|
#
|
722
|
-
# You can pass +nil+ to
|
800
|
+
# You can pass +nil+ to #create_with to reset attributes:
|
723
801
|
#
|
724
802
|
# users = users.create_with(nil)
|
725
803
|
# users.new.name # => 'Oscar'
|
@@ -741,42 +819,40 @@ module ActiveRecord
|
|
741
819
|
# Specifies table from which the records will be fetched. For example:
|
742
820
|
#
|
743
821
|
# Topic.select('title').from('posts')
|
744
|
-
# #
|
822
|
+
# # SELECT title FROM posts
|
745
823
|
#
|
746
824
|
# Can accept other relation objects. For example:
|
747
825
|
#
|
748
826
|
# Topic.select('title').from(Topic.approved)
|
749
|
-
# #
|
827
|
+
# # SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
|
750
828
|
#
|
751
829
|
# Topic.select('a.title').from(Topic.approved, :a)
|
752
|
-
# #
|
830
|
+
# # SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
|
753
831
|
#
|
754
832
|
def from(value, subquery_name = nil)
|
755
833
|
spawn.from!(value, subquery_name)
|
756
834
|
end
|
757
835
|
|
758
836
|
def from!(value, subquery_name = nil) # :nodoc:
|
759
|
-
self.
|
760
|
-
if value.is_a? Relation
|
761
|
-
self.bind_values = value.arel.bind_values + value.bind_values + bind_values
|
762
|
-
end
|
837
|
+
self.from_clause = Relation::FromClause.new(value, subquery_name)
|
763
838
|
self
|
764
839
|
end
|
765
840
|
|
766
841
|
# Specifies whether the records should be unique or not. For example:
|
767
842
|
#
|
768
843
|
# User.select(:name)
|
769
|
-
# #
|
844
|
+
# # Might return two records with the same name
|
770
845
|
#
|
771
846
|
# User.select(:name).distinct
|
772
|
-
# #
|
847
|
+
# # Returns 1 record per distinct name
|
773
848
|
#
|
774
849
|
# User.select(:name).distinct.distinct(false)
|
775
|
-
# #
|
850
|
+
# # You can also remove the uniqueness
|
776
851
|
def distinct(value = true)
|
777
852
|
spawn.distinct!(value)
|
778
853
|
end
|
779
854
|
alias uniq distinct
|
855
|
+
deprecate uniq: :distinct
|
780
856
|
|
781
857
|
# Like #distinct, but modifies relation in place.
|
782
858
|
def distinct!(value = true) # :nodoc:
|
@@ -784,6 +860,7 @@ module ActiveRecord
|
|
784
860
|
self
|
785
861
|
end
|
786
862
|
alias uniq! distinct!
|
863
|
+
deprecate uniq!: :distinct!
|
787
864
|
|
788
865
|
# Used to extend a scope with additional methods, either through
|
789
866
|
# a module or through a block provided.
|
@@ -860,17 +937,27 @@ module ActiveRecord
|
|
860
937
|
|
861
938
|
private
|
862
939
|
|
940
|
+
def assert_mutability!
|
941
|
+
raise ImmutableRelation if @loaded
|
942
|
+
raise ImmutableRelation if defined?(@arel) && @arel
|
943
|
+
end
|
944
|
+
|
863
945
|
def build_arel
|
864
|
-
arel = Arel::SelectManager.new(table
|
946
|
+
arel = Arel::SelectManager.new(table)
|
865
947
|
|
866
948
|
build_joins(arel, joins_values.flatten) unless joins_values.empty?
|
949
|
+
build_left_outer_joins(arel, left_outer_joins_values.flatten) unless left_outer_joins_values.empty?
|
867
950
|
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
951
|
+
arel.where(where_clause.ast) unless where_clause.empty?
|
952
|
+
arel.having(having_clause.ast) unless having_clause.empty?
|
953
|
+
if limit_value
|
954
|
+
if string_containing_comma?(limit_value)
|
955
|
+
arel.take(connection.sanitize_limit(limit_value))
|
956
|
+
else
|
957
|
+
arel.take(Arel::Nodes::BindParam.new)
|
958
|
+
end
|
959
|
+
end
|
960
|
+
arel.skip(Arel::Nodes::BindParam.new) if offset_value
|
874
961
|
arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
|
875
962
|
|
876
963
|
build_order(arel)
|
@@ -878,7 +965,7 @@ module ActiveRecord
|
|
878
965
|
build_select(arel)
|
879
966
|
|
880
967
|
arel.distinct(distinct_value)
|
881
|
-
arel.from(build_from)
|
968
|
+
arel.from(build_from) unless from_clause.empty?
|
882
969
|
arel.lock(lock_value) if lock_value
|
883
970
|
|
884
971
|
arel
|
@@ -889,113 +976,24 @@ module ActiveRecord
|
|
889
976
|
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
|
890
977
|
end
|
891
978
|
|
892
|
-
|
893
|
-
|
979
|
+
clause_method = Relation::CLAUSE_METHODS.include?(scope)
|
980
|
+
multi_val_method = Relation::MULTI_VALUE_METHODS.include?(scope)
|
981
|
+
if clause_method
|
982
|
+
unscope_code = "#{scope}_clause="
|
983
|
+
else
|
984
|
+
unscope_code = "#{scope}_value#{'s' if multi_val_method}="
|
985
|
+
end
|
894
986
|
|
895
987
|
case scope
|
896
988
|
when :order
|
897
989
|
result = []
|
898
|
-
when :where
|
899
|
-
self.bind_values = []
|
900
990
|
else
|
901
|
-
result = []
|
991
|
+
result = [] if multi_val_method
|
902
992
|
end
|
903
993
|
|
904
994
|
self.send(unscope_code, result)
|
905
995
|
end
|
906
996
|
|
907
|
-
def where_unscoping(target_value)
|
908
|
-
target_value = target_value.to_s
|
909
|
-
|
910
|
-
self.where_values = where_values.reject do |rel|
|
911
|
-
case rel
|
912
|
-
when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
|
913
|
-
subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right)
|
914
|
-
subrelation.name == target_value
|
915
|
-
end
|
916
|
-
end
|
917
|
-
|
918
|
-
bind_values.reject! { |col,_| col.name == target_value }
|
919
|
-
end
|
920
|
-
|
921
|
-
def custom_join_ast(table, joins)
|
922
|
-
joins = joins.reject(&:blank?)
|
923
|
-
|
924
|
-
return [] if joins.empty?
|
925
|
-
|
926
|
-
joins.map! do |join|
|
927
|
-
case join
|
928
|
-
when Array
|
929
|
-
join = Arel.sql(join.join(' ')) if array_of_strings?(join)
|
930
|
-
when String
|
931
|
-
join = Arel.sql(join)
|
932
|
-
end
|
933
|
-
table.create_string_join(join)
|
934
|
-
end
|
935
|
-
end
|
936
|
-
|
937
|
-
def collapse_wheres(arel, wheres)
|
938
|
-
predicates = wheres.map do |where|
|
939
|
-
next where if ::Arel::Nodes::Equality === where
|
940
|
-
where = Arel.sql(where) if String === where
|
941
|
-
Arel::Nodes::Grouping.new(where)
|
942
|
-
end
|
943
|
-
|
944
|
-
arel.where(Arel::Nodes::And.new(predicates)) if predicates.present?
|
945
|
-
end
|
946
|
-
|
947
|
-
def build_where(opts, other = [])
|
948
|
-
case opts
|
949
|
-
when String, Array
|
950
|
-
[@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
|
951
|
-
when Hash
|
952
|
-
opts = PredicateBuilder.resolve_column_aliases(klass, opts)
|
953
|
-
|
954
|
-
tmp_opts, bind_values = create_binds(opts)
|
955
|
-
self.bind_values += bind_values
|
956
|
-
|
957
|
-
attributes = @klass.send(:expand_hash_conditions_for_aggregates, tmp_opts)
|
958
|
-
add_relations_to_bind_values(attributes)
|
959
|
-
|
960
|
-
PredicateBuilder.build_from_hash(klass, attributes, table)
|
961
|
-
else
|
962
|
-
[opts]
|
963
|
-
end
|
964
|
-
end
|
965
|
-
|
966
|
-
def create_binds(opts)
|
967
|
-
bindable, non_binds = opts.partition do |column, value|
|
968
|
-
PredicateBuilder.can_be_bound?(value) &&
|
969
|
-
@klass.columns_hash.include?(column.to_s) &&
|
970
|
-
!@klass.reflect_on_aggregation(column)
|
971
|
-
end
|
972
|
-
|
973
|
-
association_binds, non_binds = non_binds.partition do |column, value|
|
974
|
-
value.is_a?(Hash) && association_for_table(column)
|
975
|
-
end
|
976
|
-
|
977
|
-
new_opts = {}
|
978
|
-
binds = []
|
979
|
-
|
980
|
-
connection = self.connection
|
981
|
-
|
982
|
-
bindable.each do |(column,value)|
|
983
|
-
binds.push [@klass.columns_hash[column.to_s], value]
|
984
|
-
new_opts[column] = connection.substitute_at(column)
|
985
|
-
end
|
986
|
-
|
987
|
-
association_binds.each do |(column, value)|
|
988
|
-
association_relation = association_for_table(column).klass.send(:relation)
|
989
|
-
association_new_opts, association_bind = association_relation.send(:create_binds, value)
|
990
|
-
new_opts[column] = association_new_opts
|
991
|
-
binds += association_bind
|
992
|
-
end
|
993
|
-
|
994
|
-
non_binds.each { |column,value| new_opts[column] = value }
|
995
|
-
|
996
|
-
[new_opts, binds]
|
997
|
-
end
|
998
|
-
|
999
997
|
def association_for_table(table_name)
|
1000
998
|
table_name = table_name.to_s
|
1001
999
|
@klass._reflect_on_association(table_name) ||
|
@@ -1003,7 +1001,8 @@ module ActiveRecord
|
|
1003
1001
|
end
|
1004
1002
|
|
1005
1003
|
def build_from
|
1006
|
-
opts
|
1004
|
+
opts = from_clause.value
|
1005
|
+
name = from_clause.name
|
1007
1006
|
case opts
|
1008
1007
|
when Relation
|
1009
1008
|
name ||= 'subquery'
|
@@ -1013,6 +1012,19 @@ module ActiveRecord
|
|
1013
1012
|
end
|
1014
1013
|
end
|
1015
1014
|
|
1015
|
+
def build_left_outer_joins(manager, outer_joins)
|
1016
|
+
buckets = outer_joins.group_by do |join|
|
1017
|
+
case join
|
1018
|
+
when Hash, Symbol, Array
|
1019
|
+
:association_join
|
1020
|
+
else
|
1021
|
+
raise ArgumentError, 'only Hash, Symbol and Array are allowed'
|
1022
|
+
end
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
|
1026
|
+
end
|
1027
|
+
|
1016
1028
|
def build_joins(manager, joins)
|
1017
1029
|
buckets = joins.group_by do |join|
|
1018
1030
|
case join
|
@@ -1029,12 +1041,18 @@ module ActiveRecord
|
|
1029
1041
|
end
|
1030
1042
|
end
|
1031
1043
|
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1044
|
+
build_join_query(manager, buckets, Arel::Nodes::InnerJoin)
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
def build_join_query(manager, buckets, join_type)
|
1048
|
+
buckets.default = []
|
1049
|
+
|
1050
|
+
association_joins = buckets[:association_join]
|
1051
|
+
stashed_association_joins = buckets[:stashed_join]
|
1052
|
+
join_nodes = buckets[:join_node].uniq
|
1053
|
+
string_joins = buckets[:string_join].map(&:strip).uniq
|
1036
1054
|
|
1037
|
-
join_list = join_nodes +
|
1055
|
+
join_list = join_nodes + convert_join_strings_to_ast(manager, string_joins)
|
1038
1056
|
|
1039
1057
|
join_dependency = ActiveRecord::Associations::JoinDependency.new(
|
1040
1058
|
@klass,
|
@@ -1042,7 +1060,7 @@ module ActiveRecord
|
|
1042
1060
|
join_list
|
1043
1061
|
)
|
1044
1062
|
|
1045
|
-
join_infos = join_dependency.join_constraints stashed_association_joins
|
1063
|
+
join_infos = join_dependency.join_constraints stashed_association_joins, join_type
|
1046
1064
|
|
1047
1065
|
join_infos.each do |info|
|
1048
1066
|
info.joins.each { |join| manager.from(join) }
|
@@ -1054,6 +1072,13 @@ module ActiveRecord
|
|
1054
1072
|
manager
|
1055
1073
|
end
|
1056
1074
|
|
1075
|
+
def convert_join_strings_to_ast(table, joins)
|
1076
|
+
joins
|
1077
|
+
.flatten
|
1078
|
+
.reject(&:blank?)
|
1079
|
+
.map { |join| table.create_string_join(Arel.sql(join)) }
|
1080
|
+
end
|
1081
|
+
|
1057
1082
|
def build_select(arel)
|
1058
1083
|
if select_values.any?
|
1059
1084
|
arel.project(*arel_columns(select_values.uniq))
|
@@ -1064,7 +1089,7 @@ module ActiveRecord
|
|
1064
1089
|
|
1065
1090
|
def arel_columns(columns)
|
1066
1091
|
columns.map do |field|
|
1067
|
-
if (Symbol === field || String === field) && columns_hash.key?(field.to_s) && !
|
1092
|
+
if (Symbol === field || String === field) && columns_hash.key?(field.to_s) && !from_clause.value
|
1068
1093
|
arel_table[field]
|
1069
1094
|
elsif Symbol === field
|
1070
1095
|
connection.quote_table_name(field.to_s)
|
@@ -1092,10 +1117,6 @@ module ActiveRecord
|
|
1092
1117
|
end
|
1093
1118
|
end
|
1094
1119
|
|
1095
|
-
def array_of_strings?(o)
|
1096
|
-
o.is_a?(Array) && o.all? { |obj| obj.is_a?(String) }
|
1097
|
-
end
|
1098
|
-
|
1099
1120
|
def build_order(arel)
|
1100
1121
|
orders = order_values.uniq
|
1101
1122
|
orders.reject!(&:blank?)
|
@@ -1117,6 +1138,9 @@ module ActiveRecord
|
|
1117
1138
|
end
|
1118
1139
|
|
1119
1140
|
def preprocess_order_args(order_args)
|
1141
|
+
order_args.map! do |arg|
|
1142
|
+
klass.send(:sanitize_sql_for_order, arg)
|
1143
|
+
end
|
1120
1144
|
order_args.flatten!
|
1121
1145
|
validate_order_args(order_args)
|
1122
1146
|
|
@@ -1147,8 +1171,8 @@ module ActiveRecord
|
|
1147
1171
|
#
|
1148
1172
|
# Example:
|
1149
1173
|
#
|
1150
|
-
# Post.references() #
|
1151
|
-
# Post.references([]) #
|
1174
|
+
# Post.references() # raises an error
|
1175
|
+
# Post.references([]) # does not raise an error
|
1152
1176
|
#
|
1153
1177
|
# This particular method should be called with a method_name and the args
|
1154
1178
|
# passed into that method as an input. For example:
|
@@ -1163,16 +1187,28 @@ module ActiveRecord
|
|
1163
1187
|
end
|
1164
1188
|
end
|
1165
1189
|
|
1166
|
-
def
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1190
|
+
def structurally_compatible_for_or?(other)
|
1191
|
+
Relation::SINGLE_VALUE_METHODS.all? { |m| send("#{m}_value") == other.send("#{m}_value") } &&
|
1192
|
+
(Relation::MULTI_VALUE_METHODS - [:extending]).all? { |m| send("#{m}_values") == other.send("#{m}_values") } &&
|
1193
|
+
(Relation::CLAUSE_METHODS - [:having, :where]).all? { |m| send("#{m}_clause") != other.send("#{m}_clause") }
|
1194
|
+
end
|
1195
|
+
|
1196
|
+
def new_where_clause
|
1197
|
+
Relation::WhereClause.empty
|
1198
|
+
end
|
1199
|
+
alias new_having_clause new_where_clause
|
1200
|
+
|
1201
|
+
def where_clause_factory
|
1202
|
+
@where_clause_factory ||= Relation::WhereClauseFactory.new(klass, predicate_builder)
|
1203
|
+
end
|
1204
|
+
alias having_clause_factory where_clause_factory
|
1205
|
+
|
1206
|
+
def new_from_clause
|
1207
|
+
Relation::FromClause.empty
|
1208
|
+
end
|
1209
|
+
|
1210
|
+
def string_containing_comma?(value)
|
1211
|
+
::String === value && value.include?(",")
|
1176
1212
|
end
|
1177
1213
|
end
|
1178
1214
|
end
|