activerecord 4.2.11.3 → 5.0.0.1
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 +1281 -1204
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -8
- data/examples/performance.rb +2 -3
- data/examples/simple.rb +0 -1
- data/lib/active_record/aggregations.rb +35 -24
- data/lib/active_record/association_relation.rb +3 -3
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +11 -9
- data/lib/active_record/associations/association_scope.rb +73 -102
- data/lib/active_record/associations/belongs_to_association.rb +21 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +43 -18
- data/lib/active_record/associations/builder/collection_association.rb +7 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +11 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -10
- data/lib/active_record/associations/collection_association.rb +49 -41
- data/lib/active_record/associations/collection_proxy.rb +67 -27
- 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 -47
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
- data/lib/active_record/associations/join_dependency.rb +29 -19
- data/lib/active_record/associations/preloader/association.rb +46 -52
- 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 +27 -14
- data/lib/active_record/associations/preloader.rb +14 -4
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +11 -3
- data/lib/active_record/associations.rb +317 -209
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +68 -18
- data/lib/active_record/attribute_assignment.rb +19 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- 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 +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
- data/lib/active_record/attribute_methods/write.rb +13 -37
- data/lib/active_record/attribute_methods.rb +76 -47
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +6 -4
- data/lib/active_record/attribute_set.rb +30 -3
- data/lib/active_record/attributes.rb +199 -81
- data/lib/active_record/autosave_association.rb +49 -16
- data/lib/active_record/base.rb +32 -23
- data/lib/active_record/callbacks.rb +39 -43
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -10
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +380 -141
- data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +141 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -370
- data/lib/active_record/connection_adapters/column.rb +28 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +29 -166
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -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 +3 -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 -4
- 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 +31 -17
- 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/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
- 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 +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
- 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/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +149 -192
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +37 -14
- data/lib/active_record/core.rb +89 -107
- data/lib/active_record/counter_cache.rb +13 -24
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +113 -76
- data/lib/active_record/errors.rb +87 -48
- 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 +26 -5
- data/lib/active_record/fixtures.rb +76 -40
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/internal_metadata.rb +56 -0
- 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 +15 -15
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +43 -21
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration.rb +363 -133
- data/lib/active_record/model_schema.rb +129 -41
- data/lib/active_record/nested_attributes.rb +58 -29
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +121 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- data/lib/active_record/railtie.rb +23 -16
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +69 -46
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +282 -115
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +139 -34
- data/lib/active_record/relation/calculations.rb +79 -108
- data/lib/active_record/relation/delegation.rb +7 -20
- data/lib/active_record/relation/finder_methods.rb +163 -81
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +16 -42
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -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/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +120 -107
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +308 -244
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -7
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/relation.rb +176 -116
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +95 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +62 -38
- data/lib/active_record/schema_migration.rb +11 -14
- data/lib/active_record/scoping/default.rb +23 -9
- data/lib/active_record/scoping/named.rb +49 -28
- data/lib/active_record/scoping.rb +32 -15
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +16 -14
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +57 -43
- data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
- 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 +20 -9
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +138 -56
- 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 +29 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +15 -14
- data/lib/active_record/type/time.rb +10 -16
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +11 -12
- data/lib/active_record/validations/uniqueness.rb +30 -29
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record.rb +8 -4
- 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 +8 -1
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +60 -34
- 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
@@ -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,27 @@ 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
|
|
57
|
+
FROZEN_EMPTY_ARRAY = [].freeze
|
63
58
|
Relation::MULTI_VALUE_METHODS.each do |name|
|
64
59
|
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
|
60
|
+
def #{name}_values
|
61
|
+
@values[:#{name}] || FROZEN_EMPTY_ARRAY
|
62
|
+
end
|
63
|
+
|
64
|
+
def #{name}_values=(values)
|
65
|
+
assert_mutability!
|
66
|
+
@values[:#{name}] = values
|
67
|
+
end
|
74
68
|
CODE
|
75
69
|
end
|
76
70
|
|
@@ -85,25 +79,53 @@ module ActiveRecord
|
|
85
79
|
Relation::SINGLE_VALUE_METHODS.each do |name|
|
86
80
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
87
81
|
def #{name}_value=(value) # def readonly_value=(value)
|
88
|
-
|
89
|
-
check_cached_relation
|
82
|
+
assert_mutability! # assert_mutability!
|
90
83
|
@values[:#{name}] = value # @values[:readonly] = value
|
91
84
|
end # end
|
92
85
|
CODE
|
93
86
|
end
|
94
87
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
88
|
+
Relation::CLAUSE_METHODS.each do |name|
|
89
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
90
|
+
def #{name}_clause # def where_clause
|
91
|
+
@values[:#{name}] || new_#{name}_clause # @values[:where] || new_where_clause
|
92
|
+
end # end
|
93
|
+
#
|
94
|
+
def #{name}_clause=(value) # def where_clause=(value)
|
95
|
+
assert_mutability! # assert_mutability!
|
96
|
+
@values[:#{name}] = value # @values[:where] = value
|
97
|
+
end # end
|
98
|
+
CODE
|
99
|
+
end
|
100
|
+
|
101
|
+
def bound_attributes
|
102
|
+
if limit_value && !string_containing_comma?(limit_value)
|
103
|
+
limit_bind = 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
|
+
offset_bind = Attribute.with_cast_value(
|
111
|
+
"OFFSET".freeze,
|
112
|
+
offset_value.to_i,
|
113
|
+
Type::Value.new,
|
114
|
+
)
|
102
115
|
end
|
116
|
+
connection.combine_bind_parameters(
|
117
|
+
from_clause: from_clause.binds,
|
118
|
+
join_clause: arel.bind_values,
|
119
|
+
where_clause: where_clause.binds,
|
120
|
+
having_clause: having_clause.binds,
|
121
|
+
limit: limit_bind,
|
122
|
+
offset: offset_bind,
|
123
|
+
)
|
103
124
|
end
|
104
125
|
|
126
|
+
FROZEN_EMPTY_HASH = {}.freeze
|
105
127
|
def create_with_value # :nodoc:
|
106
|
-
@values[:create_with] ||
|
128
|
+
@values[:create_with] || FROZEN_EMPTY_HASH
|
107
129
|
end
|
108
130
|
|
109
131
|
alias extensions extending_values
|
@@ -118,7 +140,7 @@ module ActiveRecord
|
|
118
140
|
#
|
119
141
|
# allows you to access the +address+ attribute of the +User+ model without
|
120
142
|
# firing an additional query. This will often result in a
|
121
|
-
# performance improvement over a simple
|
143
|
+
# performance improvement over a simple join.
|
122
144
|
#
|
123
145
|
# You can also specify multiple relationships, like this:
|
124
146
|
#
|
@@ -139,7 +161,7 @@ module ActiveRecord
|
|
139
161
|
#
|
140
162
|
# User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
|
141
163
|
#
|
142
|
-
# Note that
|
164
|
+
# Note that #includes works with association names while #references needs
|
143
165
|
# the actual table name.
|
144
166
|
def includes(*args)
|
145
167
|
check_if_method_has_arguments!(:includes, args)
|
@@ -157,9 +179,9 @@ module ActiveRecord
|
|
157
179
|
# Forces eager loading by performing a LEFT OUTER JOIN on +args+:
|
158
180
|
#
|
159
181
|
# User.eager_load(:posts)
|
160
|
-
#
|
161
|
-
# FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
|
162
|
-
# "users"."id"
|
182
|
+
# # SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ...
|
183
|
+
# # FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
|
184
|
+
# # "users"."id"
|
163
185
|
def eager_load(*args)
|
164
186
|
check_if_method_has_arguments!(:eager_load, args)
|
165
187
|
spawn.eager_load!(*args)
|
@@ -170,10 +192,10 @@ module ActiveRecord
|
|
170
192
|
self
|
171
193
|
end
|
172
194
|
|
173
|
-
# Allows preloading of +args+, in the same way that
|
195
|
+
# Allows preloading of +args+, in the same way that #includes does:
|
174
196
|
#
|
175
197
|
# User.preload(:posts)
|
176
|
-
#
|
198
|
+
# # SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
|
177
199
|
def preload(*args)
|
178
200
|
check_if_method_has_arguments!(:preload, args)
|
179
201
|
spawn.preload!(*args)
|
@@ -186,14 +208,14 @@ module ActiveRecord
|
|
186
208
|
|
187
209
|
# Use to indicate that the given +table_names+ are referenced by an SQL string,
|
188
210
|
# and should therefore be JOINed in any query rather than loaded separately.
|
189
|
-
# This method only works in conjunction with
|
211
|
+
# This method only works in conjunction with #includes.
|
190
212
|
# See #includes for more details.
|
191
213
|
#
|
192
214
|
# User.includes(:posts).where("posts.name = 'foo'")
|
193
|
-
# #
|
215
|
+
# # Doesn't JOIN the posts table, resulting in an error.
|
194
216
|
#
|
195
217
|
# User.includes(:posts).where("posts.name = 'foo'").references(:posts)
|
196
|
-
# #
|
218
|
+
# # Query now knows the string references posts, so adds a JOIN
|
197
219
|
def references(*table_names)
|
198
220
|
check_if_method_has_arguments!(:references, table_names)
|
199
221
|
spawn.references!(*table_names)
|
@@ -209,12 +231,12 @@ module ActiveRecord
|
|
209
231
|
|
210
232
|
# Works in two unique ways.
|
211
233
|
#
|
212
|
-
# First: takes a block so it can be used just like Array#select
|
234
|
+
# First: takes a block so it can be used just like +Array#select+.
|
213
235
|
#
|
214
236
|
# Model.all.select { |m| m.field == value }
|
215
237
|
#
|
216
238
|
# 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
|
239
|
+
# converting them into an array and iterating through them using +Array#select+.
|
218
240
|
#
|
219
241
|
# Second: Modifies the SELECT statement for the query so that only certain
|
220
242
|
# fields are retrieved:
|
@@ -242,17 +264,14 @@ module ActiveRecord
|
|
242
264
|
# # => "value"
|
243
265
|
#
|
244
266
|
# Accessing attributes of an object that do not have fields retrieved by a select
|
245
|
-
# except +id+ will throw
|
267
|
+
# except +id+ will throw ActiveModel::MissingAttributeError:
|
246
268
|
#
|
247
269
|
# Model.select(:field).first.other_field
|
248
270
|
# # => ActiveModel::MissingAttributeError: missing attribute: other_field
|
249
271
|
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
|
272
|
+
return super if block_given?
|
273
|
+
raise ArgumentError, 'Call this with at least one field' if fields.empty?
|
274
|
+
spawn._select!(*fields)
|
256
275
|
end
|
257
276
|
|
258
277
|
def _select!(*fields) # :nodoc:
|
@@ -267,22 +286,23 @@ module ActiveRecord
|
|
267
286
|
# Allows to specify a group attribute:
|
268
287
|
#
|
269
288
|
# User.group(:name)
|
270
|
-
#
|
289
|
+
# # SELECT "users".* FROM "users" GROUP BY name
|
271
290
|
#
|
272
291
|
# Returns an array with distinct records based on the +group+ attribute:
|
273
292
|
#
|
274
293
|
# User.select([:id, :name])
|
275
|
-
# => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">
|
294
|
+
# # => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">]
|
276
295
|
#
|
277
296
|
# User.group(:name)
|
278
|
-
# => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
|
297
|
+
# # => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
|
279
298
|
#
|
280
299
|
# 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, ...>]
|
300
|
+
# # => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
|
282
301
|
#
|
283
302
|
# Passing in an array of attributes to group by is also supported.
|
303
|
+
#
|
284
304
|
# 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">]
|
305
|
+
# # => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
|
286
306
|
def group(*args)
|
287
307
|
check_if_method_has_arguments!(:group, args)
|
288
308
|
spawn.group!(*args)
|
@@ -298,22 +318,22 @@ module ActiveRecord
|
|
298
318
|
# Allows to specify an order attribute:
|
299
319
|
#
|
300
320
|
# User.order(:name)
|
301
|
-
#
|
321
|
+
# # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
|
302
322
|
#
|
303
323
|
# User.order(email: :desc)
|
304
|
-
#
|
324
|
+
# # SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
|
305
325
|
#
|
306
326
|
# User.order(:name, email: :desc)
|
307
|
-
#
|
327
|
+
# # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
|
308
328
|
#
|
309
329
|
# User.order('name')
|
310
|
-
#
|
330
|
+
# # SELECT "users".* FROM "users" ORDER BY name
|
311
331
|
#
|
312
332
|
# User.order('name DESC')
|
313
|
-
#
|
333
|
+
# # SELECT "users".* FROM "users" ORDER BY name DESC
|
314
334
|
#
|
315
335
|
# User.order('name DESC, email')
|
316
|
-
#
|
336
|
+
# # SELECT "users".* FROM "users" ORDER BY name DESC, email
|
317
337
|
def order(*args)
|
318
338
|
check_if_method_has_arguments!(:order, args)
|
319
339
|
spawn.order!(*args)
|
@@ -365,15 +385,15 @@ module ActiveRecord
|
|
365
385
|
# User.order('email DESC').select('id').where(name: "John")
|
366
386
|
# .unscope(:order, :select, :where) == User.all
|
367
387
|
#
|
368
|
-
# One can additionally pass a hash as an argument to unscope specific
|
388
|
+
# One can additionally pass a hash as an argument to unscope specific +:where+ values.
|
369
389
|
# This is done by passing a hash with a single key-value pair. The key should be
|
370
|
-
#
|
390
|
+
# +:where+ and the value should be the where value to unscope. For example:
|
371
391
|
#
|
372
392
|
# User.where(name: "John", active: true).unscope(where: :name)
|
373
393
|
# == User.where(active: true)
|
374
394
|
#
|
375
|
-
# This method is similar to
|
376
|
-
#
|
395
|
+
# This method is similar to #except, but unlike
|
396
|
+
# #except, it persists across merges:
|
377
397
|
#
|
378
398
|
# User.order('email').merge(User.except(:order))
|
379
399
|
# == User.order('email')
|
@@ -383,7 +403,7 @@ module ActiveRecord
|
|
383
403
|
#
|
384
404
|
# This means it can be used in association definitions:
|
385
405
|
#
|
386
|
-
# has_many :comments, -> { unscope
|
406
|
+
# has_many :comments, -> { unscope(where: :trashed) }
|
387
407
|
#
|
388
408
|
def unscope(*args)
|
389
409
|
check_if_method_has_arguments!(:unscope, args)
|
@@ -404,9 +424,8 @@ module ActiveRecord
|
|
404
424
|
raise ArgumentError, "Hash arguments in .unscope(*args) must have :where as the key."
|
405
425
|
end
|
406
426
|
|
407
|
-
Array(target_value).
|
408
|
-
|
409
|
-
end
|
427
|
+
target_values = Array(target_value).map(&:to_s)
|
428
|
+
self.where_clause = where_clause.except(*target_values)
|
410
429
|
end
|
411
430
|
else
|
412
431
|
raise ArgumentError, "Unrecognized scoping: #{args.inspect}. Use .unscope(where: :attribute_name) or .unscope(:order), for example."
|
@@ -416,15 +435,35 @@ module ActiveRecord
|
|
416
435
|
self
|
417
436
|
end
|
418
437
|
|
419
|
-
# Performs a joins on +args
|
438
|
+
# Performs a joins on +args+. The given symbol(s) should match the name of
|
439
|
+
# the association(s).
|
420
440
|
#
|
421
441
|
# User.joins(:posts)
|
422
|
-
#
|
442
|
+
# # SELECT "users".*
|
443
|
+
# # FROM "users"
|
444
|
+
# # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
445
|
+
#
|
446
|
+
# Multiple joins:
|
447
|
+
#
|
448
|
+
# User.joins(:posts, :account)
|
449
|
+
# # SELECT "users".*
|
450
|
+
# # FROM "users"
|
451
|
+
# # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
452
|
+
# # INNER JOIN "accounts" ON "accounts"."id" = "users"."account_id"
|
453
|
+
#
|
454
|
+
# Nested joins:
|
455
|
+
#
|
456
|
+
# User.joins(posts: [:comments])
|
457
|
+
# # SELECT "users".*
|
458
|
+
# # FROM "users"
|
459
|
+
# # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
460
|
+
# # INNER JOIN "comments" "comments_posts"
|
461
|
+
# # ON "comments_posts"."post_id" = "posts"."id"
|
423
462
|
#
|
424
463
|
# You can use strings in order to customize your joins:
|
425
464
|
#
|
426
465
|
# User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id")
|
427
|
-
#
|
466
|
+
# # SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
|
428
467
|
def joins(*args)
|
429
468
|
check_if_method_has_arguments!(:joins, args)
|
430
469
|
spawn.joins!(*args)
|
@@ -437,14 +476,26 @@ module ActiveRecord
|
|
437
476
|
self
|
438
477
|
end
|
439
478
|
|
440
|
-
|
441
|
-
|
479
|
+
# Performs a left outer joins on +args+:
|
480
|
+
#
|
481
|
+
# User.left_outer_joins(:posts)
|
482
|
+
# => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
483
|
+
#
|
484
|
+
def left_outer_joins(*args)
|
485
|
+
check_if_method_has_arguments!(:left_outer_joins, args)
|
486
|
+
|
487
|
+
args.compact!
|
488
|
+
args.flatten!
|
489
|
+
|
490
|
+
spawn.left_outer_joins!(*args)
|
442
491
|
end
|
492
|
+
alias :left_joins :left_outer_joins
|
443
493
|
|
444
|
-
def
|
445
|
-
self.
|
494
|
+
def left_outer_joins!(*args) # :nodoc:
|
495
|
+
self.left_outer_joins_values += args
|
446
496
|
self
|
447
497
|
end
|
498
|
+
alias :left_joins! :left_outer_joins!
|
448
499
|
|
449
500
|
# Returns a new relation, which is the result of filtering the current relation
|
450
501
|
# according to the conditions in the arguments.
|
@@ -489,7 +540,7 @@ module ActiveRecord
|
|
489
540
|
# than the previous methods; you are responsible for ensuring that the values in the template
|
490
541
|
# are properly quoted. The values are passed to the connector for quoting, but the caller
|
491
542
|
# 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
|
543
|
+
# the values are inserted using the same escapes as the Ruby core method +Kernel::sprintf+.
|
493
544
|
#
|
494
545
|
# User.where(["name = '%s' and email = '%s'", "Joe", "joe@example.com"])
|
495
546
|
# # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
|
@@ -566,7 +617,7 @@ module ActiveRecord
|
|
566
617
|
# If the condition is any blank-ish object, then #where is a no-op and returns
|
567
618
|
# the current relation.
|
568
619
|
def where(opts = :chain, *rest)
|
569
|
-
if
|
620
|
+
if :chain == opts
|
570
621
|
WhereChain.new(spawn)
|
571
622
|
elsif opts.blank?
|
572
623
|
self
|
@@ -576,27 +627,60 @@ module ActiveRecord
|
|
576
627
|
end
|
577
628
|
|
578
629
|
def where!(opts, *rest) # :nodoc:
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
end
|
583
|
-
|
584
|
-
self.where_values += build_where(opts, rest)
|
630
|
+
opts = sanitize_forbidden_attributes(opts)
|
631
|
+
references!(PredicateBuilder.references(opts)) if Hash === opts
|
632
|
+
self.where_clause += where_clause_factory.build(opts, rest)
|
585
633
|
self
|
586
634
|
end
|
587
635
|
|
588
636
|
# Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
|
589
637
|
#
|
590
|
-
# Post.where(trashed: true).where(trashed: false)
|
591
|
-
#
|
592
|
-
#
|
638
|
+
# Post.where(trashed: true).where(trashed: false)
|
639
|
+
# # WHERE `trashed` = 1 AND `trashed` = 0
|
640
|
+
#
|
641
|
+
# Post.where(trashed: true).rewhere(trashed: false)
|
642
|
+
# # WHERE `trashed` = 0
|
643
|
+
#
|
644
|
+
# Post.where(active: true).where(trashed: true).rewhere(trashed: false)
|
645
|
+
# # WHERE `active` = 1 AND `trashed` = 0
|
593
646
|
#
|
594
|
-
# This is short-hand for unscope(where: conditions.keys).where(conditions)
|
595
|
-
# the named conditions -- not the entire where statement.
|
647
|
+
# This is short-hand for <tt>unscope(where: conditions.keys).where(conditions)</tt>.
|
648
|
+
# Note that unlike reorder, we're only unscoping the named conditions -- not the entire where statement.
|
596
649
|
def rewhere(conditions)
|
597
650
|
unscope(where: conditions.keys).where(conditions)
|
598
651
|
end
|
599
652
|
|
653
|
+
# Returns a new relation, which is the logical union of this relation and the one passed as an
|
654
|
+
# argument.
|
655
|
+
#
|
656
|
+
# The two relations must be structurally compatible: they must be scoping the same model, and
|
657
|
+
# they must differ only by #where (if no #group has been defined) or #having (if a #group is
|
658
|
+
# present). Neither relation may have a #limit, #offset, or #distinct set.
|
659
|
+
#
|
660
|
+
# Post.where("id = 1").or(Post.where("author_id = 3"))
|
661
|
+
# # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'author_id = 3'))
|
662
|
+
#
|
663
|
+
def or(other)
|
664
|
+
unless other.is_a? Relation
|
665
|
+
raise ArgumentError, "You have passed #{other.class.name} object to #or. Pass an ActiveRecord::Relation object instead."
|
666
|
+
end
|
667
|
+
|
668
|
+
spawn.or!(other)
|
669
|
+
end
|
670
|
+
|
671
|
+
def or!(other) # :nodoc:
|
672
|
+
incompatible_values = structurally_incompatible_values_for_or(other)
|
673
|
+
|
674
|
+
unless incompatible_values.empty?
|
675
|
+
raise ArgumentError, "Relation passed to #or must be structurally compatible. Incompatible values: #{incompatible_values}"
|
676
|
+
end
|
677
|
+
|
678
|
+
self.where_clause = self.where_clause.or(other.where_clause)
|
679
|
+
self.having_clause = self.having_clause.or(other.having_clause)
|
680
|
+
|
681
|
+
self
|
682
|
+
end
|
683
|
+
|
600
684
|
# Allows to specify a HAVING clause. Note that you can't use HAVING
|
601
685
|
# without also specifying a GROUP clause.
|
602
686
|
#
|
@@ -606,9 +690,10 @@ module ActiveRecord
|
|
606
690
|
end
|
607
691
|
|
608
692
|
def having!(opts, *rest) # :nodoc:
|
693
|
+
opts = sanitize_forbidden_attributes(opts)
|
609
694
|
references!(PredicateBuilder.references(opts)) if Hash === opts
|
610
695
|
|
611
|
-
self.
|
696
|
+
self.having_clause += having_clause_factory.build(opts, rest)
|
612
697
|
self
|
613
698
|
end
|
614
699
|
|
@@ -622,6 +707,13 @@ module ActiveRecord
|
|
622
707
|
end
|
623
708
|
|
624
709
|
def limit!(value) # :nodoc:
|
710
|
+
if string_containing_comma?(value)
|
711
|
+
# Remove `string_containing_comma?` when removing this deprecation
|
712
|
+
ActiveSupport::Deprecation.warn(<<-WARNING.squish)
|
713
|
+
Passing a string to limit in the form "1,2" is deprecated and will be
|
714
|
+
removed in Rails 5.1. Please call `offset` explicitly instead.
|
715
|
+
WARNING
|
716
|
+
end
|
625
717
|
self.limit_value = value
|
626
718
|
self
|
627
719
|
end
|
@@ -643,7 +735,7 @@ module ActiveRecord
|
|
643
735
|
end
|
644
736
|
|
645
737
|
# Specifies locking settings (default to +true+). For more information
|
646
|
-
# on locking, please see
|
738
|
+
# on locking, please see ActiveRecord::Locking.
|
647
739
|
def lock(locks = true)
|
648
740
|
spawn.lock!(locks)
|
649
741
|
end
|
@@ -674,7 +766,7 @@ module ActiveRecord
|
|
674
766
|
# For example:
|
675
767
|
#
|
676
768
|
# @posts = current_user.visible_posts.where(name: params[:name])
|
677
|
-
# #
|
769
|
+
# # the visible_posts method is expected to return a chainable Relation
|
678
770
|
#
|
679
771
|
# def visible_posts
|
680
772
|
# case role
|
@@ -700,7 +792,7 @@ module ActiveRecord
|
|
700
792
|
#
|
701
793
|
# users = User.readonly
|
702
794
|
# users.first.save
|
703
|
-
# => ActiveRecord::ReadOnlyRecord:
|
795
|
+
# => ActiveRecord::ReadOnlyRecord: User is marked as readonly
|
704
796
|
def readonly(value = true)
|
705
797
|
spawn.readonly!(value)
|
706
798
|
end
|
@@ -719,7 +811,7 @@ module ActiveRecord
|
|
719
811
|
# users = users.create_with(name: 'DHH')
|
720
812
|
# users.new.name # => 'DHH'
|
721
813
|
#
|
722
|
-
# You can pass +nil+ to
|
814
|
+
# You can pass +nil+ to #create_with to reset attributes:
|
723
815
|
#
|
724
816
|
# users = users.create_with(nil)
|
725
817
|
# users.new.name # => 'Oscar'
|
@@ -741,42 +833,40 @@ module ActiveRecord
|
|
741
833
|
# Specifies table from which the records will be fetched. For example:
|
742
834
|
#
|
743
835
|
# Topic.select('title').from('posts')
|
744
|
-
# #
|
836
|
+
# # SELECT title FROM posts
|
745
837
|
#
|
746
838
|
# Can accept other relation objects. For example:
|
747
839
|
#
|
748
840
|
# Topic.select('title').from(Topic.approved)
|
749
|
-
# #
|
841
|
+
# # SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
|
750
842
|
#
|
751
843
|
# Topic.select('a.title').from(Topic.approved, :a)
|
752
|
-
# #
|
844
|
+
# # SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
|
753
845
|
#
|
754
846
|
def from(value, subquery_name = nil)
|
755
847
|
spawn.from!(value, subquery_name)
|
756
848
|
end
|
757
849
|
|
758
850
|
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
|
851
|
+
self.from_clause = Relation::FromClause.new(value, subquery_name)
|
763
852
|
self
|
764
853
|
end
|
765
854
|
|
766
855
|
# Specifies whether the records should be unique or not. For example:
|
767
856
|
#
|
768
857
|
# User.select(:name)
|
769
|
-
# #
|
858
|
+
# # Might return two records with the same name
|
770
859
|
#
|
771
860
|
# User.select(:name).distinct
|
772
|
-
# #
|
861
|
+
# # Returns 1 record per distinct name
|
773
862
|
#
|
774
863
|
# User.select(:name).distinct.distinct(false)
|
775
|
-
# #
|
864
|
+
# # You can also remove the uniqueness
|
776
865
|
def distinct(value = true)
|
777
866
|
spawn.distinct!(value)
|
778
867
|
end
|
779
868
|
alias uniq distinct
|
869
|
+
deprecate uniq: :distinct
|
780
870
|
|
781
871
|
# Like #distinct, but modifies relation in place.
|
782
872
|
def distinct!(value = true) # :nodoc:
|
@@ -784,6 +874,7 @@ module ActiveRecord
|
|
784
874
|
self
|
785
875
|
end
|
786
876
|
alias uniq! distinct!
|
877
|
+
deprecate uniq!: :distinct!
|
787
878
|
|
788
879
|
# Used to extend a scope with additional methods, either through
|
789
880
|
# a module or through a block provided.
|
@@ -860,17 +951,27 @@ module ActiveRecord
|
|
860
951
|
|
861
952
|
private
|
862
953
|
|
954
|
+
def assert_mutability!
|
955
|
+
raise ImmutableRelation if @loaded
|
956
|
+
raise ImmutableRelation if defined?(@arel) && @arel
|
957
|
+
end
|
958
|
+
|
863
959
|
def build_arel
|
864
|
-
arel = Arel::SelectManager.new(table
|
960
|
+
arel = Arel::SelectManager.new(table)
|
865
961
|
|
866
962
|
build_joins(arel, joins_values.flatten) unless joins_values.empty?
|
963
|
+
build_left_outer_joins(arel, left_outer_joins_values.flatten) unless left_outer_joins_values.empty?
|
867
964
|
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
965
|
+
arel.where(where_clause.ast) unless where_clause.empty?
|
966
|
+
arel.having(having_clause.ast) unless having_clause.empty?
|
967
|
+
if limit_value
|
968
|
+
if string_containing_comma?(limit_value)
|
969
|
+
arel.take(connection.sanitize_limit(limit_value))
|
970
|
+
else
|
971
|
+
arel.take(Arel::Nodes::BindParam.new)
|
972
|
+
end
|
973
|
+
end
|
974
|
+
arel.skip(Arel::Nodes::BindParam.new) if offset_value
|
874
975
|
arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
|
875
976
|
|
876
977
|
build_order(arel)
|
@@ -878,7 +979,7 @@ module ActiveRecord
|
|
878
979
|
build_select(arel)
|
879
980
|
|
880
981
|
arel.distinct(distinct_value)
|
881
|
-
arel.from(build_from)
|
982
|
+
arel.from(build_from) unless from_clause.empty?
|
882
983
|
arel.lock(lock_value) if lock_value
|
883
984
|
|
884
985
|
arel
|
@@ -889,113 +990,24 @@ module ActiveRecord
|
|
889
990
|
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
|
890
991
|
end
|
891
992
|
|
892
|
-
|
893
|
-
|
993
|
+
clause_method = Relation::CLAUSE_METHODS.include?(scope)
|
994
|
+
multi_val_method = Relation::MULTI_VALUE_METHODS.include?(scope)
|
995
|
+
if clause_method
|
996
|
+
unscope_code = "#{scope}_clause="
|
997
|
+
else
|
998
|
+
unscope_code = "#{scope}_value#{'s' if multi_val_method}="
|
999
|
+
end
|
894
1000
|
|
895
1001
|
case scope
|
896
1002
|
when :order
|
897
1003
|
result = []
|
898
|
-
when :where
|
899
|
-
self.bind_values = []
|
900
1004
|
else
|
901
|
-
result = []
|
1005
|
+
result = [] if multi_val_method
|
902
1006
|
end
|
903
1007
|
|
904
1008
|
self.send(unscope_code, result)
|
905
1009
|
end
|
906
1010
|
|
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
1011
|
def association_for_table(table_name)
|
1000
1012
|
table_name = table_name.to_s
|
1001
1013
|
@klass._reflect_on_association(table_name) ||
|
@@ -1003,7 +1015,8 @@ module ActiveRecord
|
|
1003
1015
|
end
|
1004
1016
|
|
1005
1017
|
def build_from
|
1006
|
-
opts
|
1018
|
+
opts = from_clause.value
|
1019
|
+
name = from_clause.name
|
1007
1020
|
case opts
|
1008
1021
|
when Relation
|
1009
1022
|
name ||= 'subquery'
|
@@ -1013,6 +1026,19 @@ module ActiveRecord
|
|
1013
1026
|
end
|
1014
1027
|
end
|
1015
1028
|
|
1029
|
+
def build_left_outer_joins(manager, outer_joins)
|
1030
|
+
buckets = outer_joins.group_by do |join|
|
1031
|
+
case join
|
1032
|
+
when Hash, Symbol, Array
|
1033
|
+
:association_join
|
1034
|
+
else
|
1035
|
+
raise ArgumentError, 'only Hash, Symbol and Array are allowed'
|
1036
|
+
end
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
|
1040
|
+
end
|
1041
|
+
|
1016
1042
|
def build_joins(manager, joins)
|
1017
1043
|
buckets = joins.group_by do |join|
|
1018
1044
|
case join
|
@@ -1029,12 +1055,18 @@ module ActiveRecord
|
|
1029
1055
|
end
|
1030
1056
|
end
|
1031
1057
|
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1058
|
+
build_join_query(manager, buckets, Arel::Nodes::InnerJoin)
|
1059
|
+
end
|
1060
|
+
|
1061
|
+
def build_join_query(manager, buckets, join_type)
|
1062
|
+
buckets.default = []
|
1036
1063
|
|
1037
|
-
|
1064
|
+
association_joins = buckets[:association_join]
|
1065
|
+
stashed_association_joins = buckets[:stashed_join]
|
1066
|
+
join_nodes = buckets[:join_node].uniq
|
1067
|
+
string_joins = buckets[:string_join].map(&:strip).uniq
|
1068
|
+
|
1069
|
+
join_list = join_nodes + convert_join_strings_to_ast(manager, string_joins)
|
1038
1070
|
|
1039
1071
|
join_dependency = ActiveRecord::Associations::JoinDependency.new(
|
1040
1072
|
@klass,
|
@@ -1042,7 +1074,7 @@ module ActiveRecord
|
|
1042
1074
|
join_list
|
1043
1075
|
)
|
1044
1076
|
|
1045
|
-
join_infos = join_dependency.join_constraints stashed_association_joins
|
1077
|
+
join_infos = join_dependency.join_constraints stashed_association_joins, join_type
|
1046
1078
|
|
1047
1079
|
join_infos.each do |info|
|
1048
1080
|
info.joins.each { |join| manager.from(join) }
|
@@ -1054,6 +1086,13 @@ module ActiveRecord
|
|
1054
1086
|
manager
|
1055
1087
|
end
|
1056
1088
|
|
1089
|
+
def convert_join_strings_to_ast(table, joins)
|
1090
|
+
joins
|
1091
|
+
.flatten
|
1092
|
+
.reject(&:blank?)
|
1093
|
+
.map { |join| table.create_string_join(Arel.sql(join)) }
|
1094
|
+
end
|
1095
|
+
|
1057
1096
|
def build_select(arel)
|
1058
1097
|
if select_values.any?
|
1059
1098
|
arel.project(*arel_columns(select_values.uniq))
|
@@ -1064,8 +1103,8 @@ module ActiveRecord
|
|
1064
1103
|
|
1065
1104
|
def arel_columns(columns)
|
1066
1105
|
columns.map do |field|
|
1067
|
-
if (Symbol === field || String === field) &&
|
1068
|
-
|
1106
|
+
if (Symbol === field || String === field) && (klass.has_attribute?(field) || klass.attribute_alias?(field)) && !from_clause.value
|
1107
|
+
arel_attribute(field)
|
1069
1108
|
elsif Symbol === field
|
1070
1109
|
connection.quote_table_name(field.to_s)
|
1071
1110
|
else
|
@@ -1075,14 +1114,23 @@ module ActiveRecord
|
|
1075
1114
|
end
|
1076
1115
|
|
1077
1116
|
def reverse_sql_order(order_query)
|
1078
|
-
|
1117
|
+
if order_query.empty?
|
1118
|
+
return [arel_attribute(primary_key).desc] if primary_key
|
1119
|
+
raise IrreversibleOrderError,
|
1120
|
+
"Relation has no current order and table has no primary key to be used as default order"
|
1121
|
+
end
|
1079
1122
|
|
1080
1123
|
order_query.flat_map do |o|
|
1081
1124
|
case o
|
1125
|
+
when Arel::Attribute
|
1126
|
+
o.desc
|
1082
1127
|
when Arel::Nodes::Ordering
|
1083
1128
|
o.reverse
|
1084
1129
|
when String
|
1085
|
-
o
|
1130
|
+
if does_not_support_reverse?(o)
|
1131
|
+
raise IrreversibleOrderError, "Order #{o.inspect} can not be reversed automatically"
|
1132
|
+
end
|
1133
|
+
o.split(',').map! do |s|
|
1086
1134
|
s.strip!
|
1087
1135
|
s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
|
1088
1136
|
end
|
@@ -1092,8 +1140,11 @@ module ActiveRecord
|
|
1092
1140
|
end
|
1093
1141
|
end
|
1094
1142
|
|
1095
|
-
def
|
1096
|
-
|
1143
|
+
def does_not_support_reverse?(order)
|
1144
|
+
#uses sql function with multiple arguments
|
1145
|
+
order =~ /\([^()]*,[^()]*\)/ ||
|
1146
|
+
# uses "nulls first" like construction
|
1147
|
+
order =~ /nulls (first|last)\Z/i
|
1097
1148
|
end
|
1098
1149
|
|
1099
1150
|
def build_order(arel)
|
@@ -1117,6 +1168,9 @@ module ActiveRecord
|
|
1117
1168
|
end
|
1118
1169
|
|
1119
1170
|
def preprocess_order_args(order_args)
|
1171
|
+
order_args.map! do |arg|
|
1172
|
+
klass.send(:sanitize_sql_for_order, arg)
|
1173
|
+
end
|
1120
1174
|
order_args.flatten!
|
1121
1175
|
validate_order_args(order_args)
|
1122
1176
|
|
@@ -1128,12 +1182,10 @@ module ActiveRecord
|
|
1128
1182
|
order_args.map! do |arg|
|
1129
1183
|
case arg
|
1130
1184
|
when Symbol
|
1131
|
-
|
1132
|
-
table[arg].asc
|
1185
|
+
arel_attribute(arg).asc
|
1133
1186
|
when Hash
|
1134
1187
|
arg.map { |field, dir|
|
1135
|
-
|
1136
|
-
table[field].send(dir.downcase)
|
1188
|
+
arel_attribute(field).send(dir.downcase)
|
1137
1189
|
}
|
1138
1190
|
else
|
1139
1191
|
arg
|
@@ -1147,8 +1199,8 @@ module ActiveRecord
|
|
1147
1199
|
#
|
1148
1200
|
# Example:
|
1149
1201
|
#
|
1150
|
-
# Post.references() #
|
1151
|
-
# Post.references([]) #
|
1202
|
+
# Post.references() # raises an error
|
1203
|
+
# Post.references([]) # does not raise an error
|
1152
1204
|
#
|
1153
1205
|
# This particular method should be called with a method_name and the args
|
1154
1206
|
# passed into that method as an input. For example:
|
@@ -1163,16 +1215,28 @@ module ActiveRecord
|
|
1163
1215
|
end
|
1164
1216
|
end
|
1165
1217
|
|
1166
|
-
def
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1218
|
+
def structurally_incompatible_values_for_or(other)
|
1219
|
+
Relation::SINGLE_VALUE_METHODS.reject { |m| send("#{m}_value") == other.send("#{m}_value") } +
|
1220
|
+
(Relation::MULTI_VALUE_METHODS - [:extending]).reject { |m| send("#{m}_values") == other.send("#{m}_values") } +
|
1221
|
+
(Relation::CLAUSE_METHODS - [:having, :where]).reject { |m| send("#{m}_clause") == other.send("#{m}_clause") }
|
1222
|
+
end
|
1223
|
+
|
1224
|
+
def new_where_clause
|
1225
|
+
Relation::WhereClause.empty
|
1226
|
+
end
|
1227
|
+
alias new_having_clause new_where_clause
|
1228
|
+
|
1229
|
+
def where_clause_factory
|
1230
|
+
@where_clause_factory ||= Relation::WhereClauseFactory.new(klass, predicate_builder)
|
1231
|
+
end
|
1232
|
+
alias having_clause_factory where_clause_factory
|
1233
|
+
|
1234
|
+
def new_from_clause
|
1235
|
+
Relation::FromClause.empty
|
1236
|
+
end
|
1237
|
+
|
1238
|
+
def string_containing_comma?(value)
|
1239
|
+
::String === value && value.include?(",")
|
1176
1240
|
end
|
1177
1241
|
end
|
1178
1242
|
end
|