activerecord 5.0.7.2 → 5.1.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 +389 -2252
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +28 -28
- data/examples/simple.rb +3 -3
- data/lib/active_record.rb +20 -20
- data/lib/active_record/aggregations.rb +244 -244
- data/lib/active_record/association_relation.rb +5 -5
- data/lib/active_record/associations.rb +1579 -1569
- data/lib/active_record/associations/alias_tracker.rb +1 -1
- data/lib/active_record/associations/association.rb +23 -15
- data/lib/active_record/associations/association_scope.rb +83 -81
- data/lib/active_record/associations/belongs_to_association.rb +0 -1
- data/lib/active_record/associations/builder/belongs_to.rb +16 -14
- data/lib/active_record/associations/builder/collection_association.rb +1 -2
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
- data/lib/active_record/associations/collection_association.rb +74 -241
- data/lib/active_record/associations/collection_proxy.rb +144 -70
- data/lib/active_record/associations/has_many_association.rb +15 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -5
- data/lib/active_record/associations/has_one_association.rb +22 -28
- data/lib/active_record/associations/has_one_through_association.rb +5 -1
- data/lib/active_record/associations/join_dependency.rb +117 -115
- data/lib/active_record/associations/join_dependency/join_association.rb +16 -13
- data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/preloader.rb +94 -94
- data/lib/active_record/associations/preloader/association.rb +87 -64
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
- data/lib/active_record/associations/preloader/collection_association.rb +6 -6
- data/lib/active_record/associations/preloader/has_many.rb +0 -2
- data/lib/active_record/associations/preloader/singular_association.rb +6 -8
- data/lib/active_record/associations/preloader/through_association.rb +34 -41
- data/lib/active_record/associations/singular_association.rb +8 -25
- data/lib/active_record/associations/through_association.rb +3 -6
- data/lib/active_record/attribute.rb +98 -71
- data/lib/active_record/attribute/user_provided_default.rb +4 -2
- data/lib/active_record/attribute_assignment.rb +61 -61
- data/lib/active_record/attribute_decorators.rb +35 -13
- data/lib/active_record/attribute_methods.rb +56 -65
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
- data/lib/active_record/attribute_methods/dirty.rb +216 -34
- data/lib/active_record/attribute_methods/primary_key.rb +78 -73
- data/lib/active_record/attribute_methods/read.rb +39 -35
- data/lib/active_record/attribute_methods/serialization.rb +7 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
- data/lib/active_record/attribute_methods/write.rb +36 -30
- data/lib/active_record/attribute_mutation_tracker.rb +53 -10
- data/lib/active_record/attribute_set.rb +9 -6
- data/lib/active_record/attribute_set/builder.rb +41 -49
- data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
- data/lib/active_record/attributes.rb +21 -21
- data/lib/active_record/autosave_association.rb +13 -13
- data/lib/active_record/base.rb +24 -22
- data/lib/active_record/callbacks.rb +52 -14
- data/lib/active_record/coders/yaml_column.rb +9 -11
- data/lib/active_record/collection_cache_key.rb +6 -17
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +320 -278
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -34
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -57
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +9 -19
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +78 -79
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +99 -93
- data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +156 -128
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +424 -382
- data/lib/active_record/connection_adapters/column.rb +27 -5
- data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
- data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -43
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
- data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +49 -31
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +5 -6
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +24 -26
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -35
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +9 -9
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
- 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 +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
- data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +28 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +38 -36
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +161 -170
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +179 -152
- data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -20
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +187 -130
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
- data/lib/active_record/connection_handling.rb +14 -26
- data/lib/active_record/core.rb +110 -93
- data/lib/active_record/counter_cache.rb +62 -13
- data/lib/active_record/define_callbacks.rb +20 -0
- data/lib/active_record/dynamic_matchers.rb +80 -79
- data/lib/active_record/enum.rb +8 -6
- data/lib/active_record/errors.rb +58 -15
- data/lib/active_record/explain.rb +1 -2
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +7 -4
- data/lib/active_record/fixture_set/file.rb +11 -8
- data/lib/active_record/fixtures.rb +66 -53
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +93 -79
- data/lib/active_record/integration.rb +7 -7
- data/lib/active_record/internal_metadata.rb +3 -16
- data/lib/active_record/legacy_yaml_adapter.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +64 -56
- data/lib/active_record/locking/pessimistic.rb +10 -1
- data/lib/active_record/log_subscriber.rb +29 -29
- data/lib/active_record/migration.rb +155 -172
- data/lib/active_record/migration/command_recorder.rb +94 -94
- data/lib/active_record/migration/compatibility.rb +76 -37
- data/lib/active_record/migration/join_table.rb +6 -6
- data/lib/active_record/model_schema.rb +85 -119
- data/lib/active_record/nested_attributes.rb +200 -199
- data/lib/active_record/null_relation.rb +10 -33
- data/lib/active_record/persistence.rb +45 -38
- data/lib/active_record/query_cache.rb +4 -8
- data/lib/active_record/querying.rb +2 -3
- data/lib/active_record/railtie.rb +16 -17
- data/lib/active_record/railties/controller_runtime.rb +6 -2
- data/lib/active_record/railties/databases.rake +125 -140
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +2 -2
- data/lib/active_record/reflection.rb +79 -96
- data/lib/active_record/relation.rb +72 -115
- data/lib/active_record/relation/batches.rb +87 -58
- data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
- data/lib/active_record/relation/calculations.rb +154 -160
- data/lib/active_record/relation/delegation.rb +30 -29
- data/lib/active_record/relation/finder_methods.rb +195 -226
- data/lib/active_record/relation/merger.rb +58 -62
- data/lib/active_record/relation/predicate_builder.rb +92 -89
- data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
- data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +247 -295
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +4 -5
- data/lib/active_record/relation/where_clause.rb +79 -65
- data/lib/active_record/relation/where_clause_factory.rb +47 -8
- data/lib/active_record/result.rb +29 -31
- data/lib/active_record/runtime_registry.rb +3 -3
- data/lib/active_record/sanitization.rb +182 -197
- data/lib/active_record/schema.rb +3 -3
- data/lib/active_record/schema_dumper.rb +14 -37
- data/lib/active_record/schema_migration.rb +3 -3
- data/lib/active_record/scoping.rb +9 -10
- data/lib/active_record/scoping/default.rb +87 -91
- data/lib/active_record/scoping/named.rb +16 -28
- data/lib/active_record/secure_token.rb +2 -2
- data/lib/active_record/statement_cache.rb +13 -15
- data/lib/active_record/store.rb +31 -32
- data/lib/active_record/suppressor.rb +2 -1
- data/lib/active_record/table_metadata.rb +9 -5
- data/lib/active_record/tasks/database_tasks.rb +72 -65
- data/lib/active_record/tasks/mysql_database_tasks.rb +75 -72
- data/lib/active_record/tasks/postgresql_database_tasks.rb +53 -48
- data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +1 -2
- data/lib/active_record/transactions.rb +98 -110
- data/lib/active_record/type.rb +17 -13
- data/lib/active_record/type/adapter_specific_registry.rb +46 -42
- data/lib/active_record/type/decimal_without_scale.rb +9 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
- data/lib/active_record/type/serialized.rb +8 -8
- data/lib/active_record/type/text.rb +9 -0
- data/lib/active_record/type/time.rb +0 -1
- data/lib/active_record/type/type_map.rb +11 -15
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type_caster.rb +2 -2
- data/lib/active_record/type_caster/connection.rb +8 -6
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/validations.rb +4 -4
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +8 -39
- data/lib/active_record/version.rb +1 -1
- data/lib/rails/generators/active_record.rb +4 -4
- data/lib/rails/generators/active_record/migration.rb +2 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
- metadata +22 -13
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -9,9 +9,11 @@ module ActiveRecord
|
|
9
9
|
predicate_builder.build(attribute, value.id)
|
10
10
|
end
|
11
11
|
|
12
|
+
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
13
|
+
# Workaround for Ruby 2.2 "private attribute?" warning.
|
12
14
|
protected
|
13
15
|
|
14
|
-
|
16
|
+
attr_reader :predicate_builder
|
15
17
|
end
|
16
18
|
end
|
17
19
|
end
|
@@ -1,17 +1,9 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
class PredicateBuilder
|
3
3
|
class BasicObjectHandler # :nodoc:
|
4
|
-
def initialize(predicate_builder)
|
5
|
-
@predicate_builder = predicate_builder
|
6
|
-
end
|
7
|
-
|
8
4
|
def call(attribute, value)
|
9
5
|
attribute.eq(value)
|
10
6
|
end
|
11
|
-
|
12
|
-
protected
|
13
|
-
|
14
|
-
attr_reader :predicate_builder
|
15
7
|
end
|
16
8
|
end
|
17
9
|
end
|
@@ -21,9 +21,11 @@ module ActiveRecord
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
25
|
+
# Workaround for Ruby 2.2 "private attribute?" warning.
|
24
26
|
protected
|
25
27
|
|
26
|
-
|
28
|
+
attr_reader :predicate_builder
|
27
29
|
end
|
28
30
|
|
29
31
|
class PolymorphicArrayValue # :nodoc:
|
@@ -41,17 +43,17 @@ module ActiveRecord
|
|
41
43
|
|
42
44
|
private
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
|
46
|
+
def primary_key(value)
|
47
|
+
associated_table.association_primary_key(base_class(value))
|
48
|
+
end
|
47
49
|
|
48
|
-
|
49
|
-
|
50
|
-
|
50
|
+
def base_class(value)
|
51
|
+
value.class.base_class
|
52
|
+
end
|
51
53
|
|
52
|
-
|
53
|
-
|
54
|
-
|
54
|
+
def convert_to_id(value)
|
55
|
+
value._read_attribute(primary_key(value))
|
56
|
+
end
|
55
57
|
end
|
56
58
|
end
|
57
59
|
end
|
@@ -3,10 +3,6 @@ module ActiveRecord
|
|
3
3
|
class RangeHandler # :nodoc:
|
4
4
|
RangeWithBinds = Struct.new(:begin, :end, :exclude_end?)
|
5
5
|
|
6
|
-
def initialize(predicate_builder)
|
7
|
-
@predicate_builder = predicate_builder
|
8
|
-
end
|
9
|
-
|
10
6
|
def call(attribute, value)
|
11
7
|
if value.begin.respond_to?(:infinite?) && value.begin.infinite?
|
12
8
|
if value.end.respond_to?(:infinite?) && value.end.infinite?
|
@@ -24,10 +20,6 @@ module ActiveRecord
|
|
24
20
|
attribute.between(value)
|
25
21
|
end
|
26
22
|
end
|
27
|
-
|
28
|
-
protected
|
29
|
-
|
30
|
-
attr_reader :predicate_builder
|
31
23
|
end
|
32
24
|
end
|
33
25
|
end
|
@@ -2,8 +2,7 @@ require "active_record/relation/from_clause"
|
|
2
2
|
require "active_record/relation/query_attribute"
|
3
3
|
require "active_record/relation/where_clause"
|
4
4
|
require "active_record/relation/where_clause_factory"
|
5
|
-
require
|
6
|
-
require 'active_support/core_ext/string/filters'
|
5
|
+
require "active_model/forbidden_attributes_protection"
|
7
6
|
|
8
7
|
module ActiveRecord
|
9
8
|
module QueryMethods
|
@@ -55,62 +54,39 @@ module ActiveRecord
|
|
55
54
|
end
|
56
55
|
|
57
56
|
FROZEN_EMPTY_ARRAY = [].freeze
|
58
|
-
|
59
|
-
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
60
|
-
def #{name}_values
|
61
|
-
@values[:#{name}] || FROZEN_EMPTY_ARRAY
|
62
|
-
end
|
57
|
+
FROZEN_EMPTY_HASH = {}.freeze
|
63
58
|
|
64
|
-
|
65
|
-
|
66
|
-
|
59
|
+
Relation::VALUE_METHODS.each do |name|
|
60
|
+
method_name = \
|
61
|
+
case name
|
62
|
+
when *Relation::MULTI_VALUE_METHODS then "#{name}_values"
|
63
|
+
when *Relation::SINGLE_VALUE_METHODS then "#{name}_value"
|
64
|
+
when *Relation::CLAUSE_METHODS then "#{name}_clause"
|
67
65
|
end
|
68
|
-
CODE
|
69
|
-
end
|
70
|
-
|
71
|
-
(Relation::SINGLE_VALUE_METHODS - [:create_with]).each do |name|
|
72
66
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
73
|
-
def #{
|
74
|
-
|
67
|
+
def #{method_name} # def includes_values
|
68
|
+
get_value(#{name.inspect}) # get_value(:includes)
|
75
69
|
end # end
|
76
|
-
CODE
|
77
|
-
end
|
78
70
|
|
79
|
-
|
80
|
-
|
81
|
-
def #{name}_value=(value) # def readonly_value=(value)
|
82
|
-
assert_mutability! # assert_mutability!
|
83
|
-
@values[:#{name}] = value # @values[:readonly] = value
|
71
|
+
def #{method_name}=(value) # def includes_values=(value)
|
72
|
+
set_value(#{name.inspect}, value) # set_value(:includes, value)
|
84
73
|
end # end
|
85
74
|
CODE
|
86
75
|
end
|
87
76
|
|
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
77
|
def bound_attributes
|
102
|
-
if limit_value
|
78
|
+
if limit_value
|
103
79
|
limit_bind = Attribute.with_cast_value(
|
104
80
|
"LIMIT".freeze,
|
105
81
|
connection.sanitize_limit(limit_value),
|
106
|
-
Type
|
82
|
+
Type.default_value,
|
107
83
|
)
|
108
84
|
end
|
109
85
|
if offset_value
|
110
86
|
offset_bind = Attribute.with_cast_value(
|
111
87
|
"OFFSET".freeze,
|
112
88
|
offset_value.to_i,
|
113
|
-
Type
|
89
|
+
Type.default_value,
|
114
90
|
)
|
115
91
|
end
|
116
92
|
connection.combine_bind_parameters(
|
@@ -123,11 +99,6 @@ module ActiveRecord
|
|
123
99
|
)
|
124
100
|
end
|
125
101
|
|
126
|
-
FROZEN_EMPTY_HASH = {}.freeze
|
127
|
-
def create_with_value # :nodoc:
|
128
|
-
@values[:create_with] || FROZEN_EMPTY_HASH
|
129
|
-
end
|
130
|
-
|
131
102
|
alias extensions extending_values
|
132
103
|
|
133
104
|
# Specify relationships to be included in the result set. For
|
@@ -269,8 +240,15 @@ module ActiveRecord
|
|
269
240
|
# Model.select(:field).first.other_field
|
270
241
|
# # => ActiveModel::MissingAttributeError: missing attribute: other_field
|
271
242
|
def select(*fields)
|
272
|
-
|
273
|
-
|
243
|
+
if block_given?
|
244
|
+
if fields.any?
|
245
|
+
raise ArgumentError, "`select' with block doesn't take arguments."
|
246
|
+
end
|
247
|
+
|
248
|
+
return super()
|
249
|
+
end
|
250
|
+
|
251
|
+
raise ArgumentError, "Call this with at least one field" if fields.empty?
|
274
252
|
spawn._select!(*fields)
|
275
253
|
end
|
276
254
|
|
@@ -417,7 +395,10 @@ module ActiveRecord
|
|
417
395
|
args.each do |scope|
|
418
396
|
case scope
|
419
397
|
when Symbol
|
420
|
-
|
398
|
+
if !VALID_UNSCOPING_VALUES.include?(scope)
|
399
|
+
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
|
400
|
+
end
|
401
|
+
set_value(scope, nil)
|
421
402
|
when Hash
|
422
403
|
scope.each do |key, target_value|
|
423
404
|
if key != :where
|
@@ -495,7 +476,6 @@ module ActiveRecord
|
|
495
476
|
self.left_outer_joins_values += args
|
496
477
|
self
|
497
478
|
end
|
498
|
-
alias :left_joins! :left_outer_joins!
|
499
479
|
|
500
480
|
# Returns a new relation, which is the result of filtering the current relation
|
501
481
|
# according to the conditions in the arguments.
|
@@ -658,7 +638,7 @@ module ActiveRecord
|
|
658
638
|
# present). Neither relation may have a #limit, #offset, or #distinct set.
|
659
639
|
#
|
660
640
|
# Post.where("id = 1").or(Post.where("author_id = 3"))
|
661
|
-
# # SELECT `posts`.* FROM `posts`
|
641
|
+
# # SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3))
|
662
642
|
#
|
663
643
|
def or(other)
|
664
644
|
unless other.is_a? Relation
|
@@ -676,7 +656,7 @@ module ActiveRecord
|
|
676
656
|
end
|
677
657
|
|
678
658
|
self.where_clause = self.where_clause.or(other.where_clause)
|
679
|
-
self.having_clause =
|
659
|
+
self.having_clause = having_clause.or(other.having_clause)
|
680
660
|
|
681
661
|
self
|
682
662
|
end
|
@@ -707,13 +687,6 @@ module ActiveRecord
|
|
707
687
|
end
|
708
688
|
|
709
689
|
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
|
717
690
|
self.limit_value = value
|
718
691
|
self
|
719
692
|
end
|
@@ -780,7 +753,7 @@ module ActiveRecord
|
|
780
753
|
# end
|
781
754
|
#
|
782
755
|
def none
|
783
|
-
|
756
|
+
spawn.none!
|
784
757
|
end
|
785
758
|
|
786
759
|
def none! # :nodoc:
|
@@ -865,16 +838,12 @@ module ActiveRecord
|
|
865
838
|
def distinct(value = true)
|
866
839
|
spawn.distinct!(value)
|
867
840
|
end
|
868
|
-
alias uniq distinct
|
869
|
-
deprecate uniq: :distinct
|
870
841
|
|
871
842
|
# Like #distinct, but modifies relation in place.
|
872
843
|
def distinct!(value = true) # :nodoc:
|
873
844
|
self.distinct_value = value
|
874
845
|
self
|
875
846
|
end
|
876
|
-
alias uniq! distinct!
|
877
|
-
deprecate uniq!: :distinct!
|
878
847
|
|
879
848
|
# Used to extend a scope with additional methods, either through
|
880
849
|
# a module or through a block provided.
|
@@ -949,294 +918,277 @@ module ActiveRecord
|
|
949
918
|
@arel ||= build_arel
|
950
919
|
end
|
951
920
|
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
raise ImmutableRelation if @loaded
|
956
|
-
raise ImmutableRelation if defined?(@arel) && @arel
|
921
|
+
# Returns a relation value with a given name
|
922
|
+
def get_value(name) # :nodoc:
|
923
|
+
@values[name] || default_value_for(name)
|
957
924
|
end
|
958
925
|
|
959
|
-
|
960
|
-
|
926
|
+
# Sets the relation value with the given name
|
927
|
+
def set_value(name, value) # :nodoc:
|
928
|
+
assert_mutability!
|
929
|
+
@values[name] = value
|
930
|
+
end
|
961
931
|
|
962
|
-
|
963
|
-
build_left_outer_joins(arel, left_outer_joins_values.flatten) unless left_outer_joins_values.empty?
|
932
|
+
private
|
964
933
|
|
965
|
-
|
966
|
-
|
967
|
-
|
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
|
934
|
+
def assert_mutability!
|
935
|
+
raise ImmutableRelation if @loaded
|
936
|
+
raise ImmutableRelation if defined?(@arel) && @arel
|
973
937
|
end
|
974
|
-
arel.skip(Arel::Nodes::BindParam.new) if offset_value
|
975
|
-
arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
|
976
938
|
|
977
|
-
|
939
|
+
def build_arel
|
940
|
+
arel = Arel::SelectManager.new(table)
|
978
941
|
|
979
|
-
|
942
|
+
build_joins(arel, joins_values.flatten) unless joins_values.empty?
|
943
|
+
build_left_outer_joins(arel, left_outer_joins_values.flatten) unless left_outer_joins_values.empty?
|
980
944
|
|
981
|
-
|
982
|
-
|
983
|
-
|
945
|
+
arel.where(where_clause.ast) unless where_clause.empty?
|
946
|
+
arel.having(having_clause.ast) unless having_clause.empty?
|
947
|
+
arel.take(Arel::Nodes::BindParam.new) if limit_value
|
948
|
+
arel.skip(Arel::Nodes::BindParam.new) if offset_value
|
949
|
+
arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
|
984
950
|
|
985
|
-
|
986
|
-
end
|
951
|
+
build_order(arel)
|
987
952
|
|
988
|
-
|
989
|
-
if !VALID_UNSCOPING_VALUES.include?(scope)
|
990
|
-
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
|
991
|
-
end
|
953
|
+
build_select(arel)
|
992
954
|
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
unscope_code = "#{scope}_clause="
|
997
|
-
else
|
998
|
-
unscope_code = "#{scope}_value#{'s' if multi_val_method}="
|
999
|
-
end
|
955
|
+
arel.distinct(distinct_value)
|
956
|
+
arel.from(build_from) unless from_clause.empty?
|
957
|
+
arel.lock(lock_value) if lock_value
|
1000
958
|
|
1001
|
-
|
1002
|
-
when :order
|
1003
|
-
result = []
|
1004
|
-
else
|
1005
|
-
result = [] if multi_val_method
|
959
|
+
arel
|
1006
960
|
end
|
1007
961
|
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
end
|
1016
|
-
|
1017
|
-
def build_from
|
1018
|
-
opts = from_clause.value
|
1019
|
-
name = from_clause.name
|
1020
|
-
case opts
|
1021
|
-
when Relation
|
1022
|
-
name ||= 'subquery'
|
1023
|
-
opts.arel.as(name.to_s)
|
1024
|
-
else
|
1025
|
-
opts
|
1026
|
-
end
|
1027
|
-
end
|
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
|
962
|
+
def build_from
|
963
|
+
opts = from_clause.value
|
964
|
+
name = from_clause.name
|
965
|
+
case opts
|
966
|
+
when Relation
|
967
|
+
name ||= "subquery"
|
968
|
+
opts.arel.as(name.to_s)
|
1034
969
|
else
|
1035
|
-
|
970
|
+
opts
|
1036
971
|
end
|
1037
972
|
end
|
1038
973
|
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
when Hash, Symbol, Array
|
1048
|
-
:association_join
|
1049
|
-
when ActiveRecord::Associations::JoinDependency
|
1050
|
-
:stashed_join
|
1051
|
-
when Arel::Nodes::Join
|
1052
|
-
:join_node
|
1053
|
-
else
|
1054
|
-
raise 'unknown class: %s' % join.class.name
|
974
|
+
def build_left_outer_joins(manager, outer_joins)
|
975
|
+
buckets = outer_joins.group_by do |join|
|
976
|
+
case join
|
977
|
+
when Hash, Symbol, Array
|
978
|
+
:association_join
|
979
|
+
else
|
980
|
+
raise ArgumentError, "only Hash, Symbol and Array are allowed"
|
981
|
+
end
|
1055
982
|
end
|
983
|
+
|
984
|
+
build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
|
1056
985
|
end
|
1057
986
|
|
1058
|
-
|
1059
|
-
|
987
|
+
def build_joins(manager, joins)
|
988
|
+
buckets = joins.group_by do |join|
|
989
|
+
case join
|
990
|
+
when String
|
991
|
+
:string_join
|
992
|
+
when Hash, Symbol, Array
|
993
|
+
:association_join
|
994
|
+
when ActiveRecord::Associations::JoinDependency
|
995
|
+
:stashed_join
|
996
|
+
when Arel::Nodes::Join
|
997
|
+
:join_node
|
998
|
+
else
|
999
|
+
raise "unknown class: %s" % join.class.name
|
1000
|
+
end
|
1001
|
+
end
|
1060
1002
|
|
1061
|
-
|
1062
|
-
|
1003
|
+
build_join_query(manager, buckets, Arel::Nodes::InnerJoin)
|
1004
|
+
end
|
1063
1005
|
|
1064
|
-
|
1065
|
-
|
1066
|
-
join_nodes = buckets[:join_node].uniq
|
1067
|
-
string_joins = buckets[:string_join].map(&:strip).uniq
|
1006
|
+
def build_join_query(manager, buckets, join_type)
|
1007
|
+
buckets.default = []
|
1068
1008
|
|
1069
|
-
|
1009
|
+
association_joins = buckets[:association_join]
|
1010
|
+
stashed_association_joins = buckets[:stashed_join]
|
1011
|
+
join_nodes = buckets[:join_node].uniq
|
1012
|
+
string_joins = buckets[:string_join].map(&:strip).uniq
|
1070
1013
|
|
1071
|
-
|
1072
|
-
@klass,
|
1073
|
-
association_joins,
|
1074
|
-
join_list
|
1075
|
-
)
|
1014
|
+
join_list = join_nodes + convert_join_strings_to_ast(manager, string_joins)
|
1076
1015
|
|
1077
|
-
|
1016
|
+
join_dependency = ActiveRecord::Associations::JoinDependency.new(
|
1017
|
+
@klass,
|
1018
|
+
association_joins,
|
1019
|
+
join_list
|
1020
|
+
)
|
1078
1021
|
|
1079
|
-
|
1080
|
-
info.joins.each { |join| manager.from(join) }
|
1081
|
-
manager.bind_values.concat info.binds
|
1082
|
-
end
|
1022
|
+
join_infos = join_dependency.join_constraints stashed_association_joins, join_type
|
1083
1023
|
|
1084
|
-
|
1024
|
+
join_infos.each do |info|
|
1025
|
+
info.joins.each { |join| manager.from(join) }
|
1026
|
+
manager.bind_values.concat info.binds
|
1027
|
+
end
|
1085
1028
|
|
1086
|
-
|
1087
|
-
end
|
1029
|
+
manager.join_sources.concat(join_list)
|
1088
1030
|
|
1089
|
-
|
1090
|
-
|
1091
|
-
.flatten
|
1092
|
-
.reject(&:blank?)
|
1093
|
-
.map { |join| table.create_string_join(Arel.sql(join)) }
|
1094
|
-
end
|
1031
|
+
manager
|
1032
|
+
end
|
1095
1033
|
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1034
|
+
def convert_join_strings_to_ast(table, joins)
|
1035
|
+
joins
|
1036
|
+
.flatten
|
1037
|
+
.reject(&:blank?)
|
1038
|
+
.map { |join| table.create_string_join(Arel.sql(join)) }
|
1101
1039
|
end
|
1102
|
-
end
|
1103
1040
|
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
arel_attribute(field)
|
1108
|
-
elsif Symbol === field
|
1109
|
-
connection.quote_table_name(field.to_s)
|
1041
|
+
def build_select(arel)
|
1042
|
+
if select_values.any?
|
1043
|
+
arel.project(*arel_columns(select_values.uniq))
|
1110
1044
|
else
|
1111
|
-
|
1045
|
+
arel.project(@klass.arel_table[Arel.star])
|
1112
1046
|
end
|
1113
1047
|
end
|
1114
|
-
end
|
1115
1048
|
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1049
|
+
def arel_columns(columns)
|
1050
|
+
columns.map do |field|
|
1051
|
+
if (Symbol === field || String === field) && (klass.has_attribute?(field) || klass.attribute_alias?(field)) && !from_clause.value
|
1052
|
+
arel_attribute(field)
|
1053
|
+
elsif Symbol === field
|
1054
|
+
connection.quote_table_name(field.to_s)
|
1055
|
+
else
|
1056
|
+
field
|
1057
|
+
end
|
1058
|
+
end
|
1121
1059
|
end
|
1122
1060
|
|
1123
|
-
order_query
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1061
|
+
def reverse_sql_order(order_query)
|
1062
|
+
if order_query.empty?
|
1063
|
+
return [arel_attribute(primary_key).desc] if primary_key
|
1064
|
+
raise IrreversibleOrderError,
|
1065
|
+
"Relation has no current order and table has no primary key to be used as default order"
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
order_query.flat_map do |o|
|
1069
|
+
case o
|
1070
|
+
when Arel::Attribute
|
1071
|
+
o.desc
|
1072
|
+
when Arel::Nodes::Ordering
|
1073
|
+
o.reverse
|
1074
|
+
when String
|
1075
|
+
if does_not_support_reverse?(o)
|
1076
|
+
raise IrreversibleOrderError, "Order #{o.inspect} can not be reversed automatically"
|
1077
|
+
end
|
1078
|
+
o.split(",").map! do |s|
|
1079
|
+
s.strip!
|
1080
|
+
s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || s.concat(" DESC")
|
1081
|
+
end
|
1082
|
+
else
|
1083
|
+
o
|
1136
1084
|
end
|
1137
|
-
else
|
1138
|
-
o
|
1139
1085
|
end
|
1140
1086
|
end
|
1141
|
-
end
|
1142
1087
|
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1088
|
+
def does_not_support_reverse?(order)
|
1089
|
+
# Uses SQL function with multiple arguments.
|
1090
|
+
(order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
|
1091
|
+
# Uses "nulls first" like construction.
|
1092
|
+
/nulls (first|last)\Z/i.match?(order)
|
1093
|
+
end
|
1149
1094
|
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1095
|
+
def build_order(arel)
|
1096
|
+
orders = order_values.uniq
|
1097
|
+
orders.reject!(&:blank?)
|
1153
1098
|
|
1154
|
-
|
1155
|
-
|
1099
|
+
arel.order(*orders) unless orders.empty?
|
1100
|
+
end
|
1156
1101
|
|
1157
|
-
|
1158
|
-
|
1102
|
+
VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
|
1103
|
+
"asc", "desc", "ASC", "DESC"] # :nodoc:
|
1159
1104
|
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1105
|
+
def validate_order_args(args)
|
1106
|
+
args.each do |arg|
|
1107
|
+
next unless arg.is_a?(Hash)
|
1108
|
+
arg.each do |_key, value|
|
1109
|
+
raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
|
1110
|
+
"directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
|
1111
|
+
end
|
1166
1112
|
end
|
1167
1113
|
end
|
1168
|
-
end
|
1169
1114
|
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1115
|
+
def preprocess_order_args(order_args)
|
1116
|
+
order_args.map! do |arg|
|
1117
|
+
klass.send(:sanitize_sql_for_order, arg)
|
1118
|
+
end
|
1119
|
+
order_args.flatten!
|
1120
|
+
validate_order_args(order_args)
|
1121
|
+
|
1122
|
+
references = order_args.grep(String)
|
1123
|
+
references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
|
1124
|
+
references!(references) if references.any?
|
1125
|
+
|
1126
|
+
# if a symbol is given we prepend the quoted table name
|
1127
|
+
order_args.map! do |arg|
|
1128
|
+
case arg
|
1129
|
+
when Symbol
|
1130
|
+
arel_attribute(arg).asc
|
1131
|
+
when Hash
|
1132
|
+
arg.map { |field, dir|
|
1133
|
+
arel_attribute(field).send(dir.downcase)
|
1134
|
+
}
|
1135
|
+
else
|
1136
|
+
arg
|
1137
|
+
end
|
1138
|
+
end.flatten!
|
1173
1139
|
end
|
1174
|
-
order_args.flatten!
|
1175
|
-
validate_order_args(order_args)
|
1176
|
-
|
1177
|
-
references = order_args.grep(String)
|
1178
|
-
references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
|
1179
|
-
references!(references) if references.any?
|
1180
1140
|
|
1181
|
-
#
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1141
|
+
# Checks to make sure that the arguments are not blank. Note that if some
|
1142
|
+
# blank-like object were initially passed into the query method, then this
|
1143
|
+
# method will not raise an error.
|
1144
|
+
#
|
1145
|
+
# Example:
|
1146
|
+
#
|
1147
|
+
# Post.references() # raises an error
|
1148
|
+
# Post.references([]) # does not raise an error
|
1149
|
+
#
|
1150
|
+
# This particular method should be called with a method_name and the args
|
1151
|
+
# passed into that method as an input. For example:
|
1152
|
+
#
|
1153
|
+
# def references(*args)
|
1154
|
+
# check_if_method_has_arguments!("references", args)
|
1155
|
+
# ...
|
1156
|
+
# end
|
1157
|
+
def check_if_method_has_arguments!(method_name, args)
|
1158
|
+
if args.blank?
|
1159
|
+
raise ArgumentError, "The method .#{method_name}() must contain arguments."
|
1192
1160
|
end
|
1193
|
-
end.flatten!
|
1194
|
-
end
|
1195
|
-
|
1196
|
-
# Checks to make sure that the arguments are not blank. Note that if some
|
1197
|
-
# blank-like object were initially passed into the query method, then this
|
1198
|
-
# method will not raise an error.
|
1199
|
-
#
|
1200
|
-
# Example:
|
1201
|
-
#
|
1202
|
-
# Post.references() # raises an error
|
1203
|
-
# Post.references([]) # does not raise an error
|
1204
|
-
#
|
1205
|
-
# This particular method should be called with a method_name and the args
|
1206
|
-
# passed into that method as an input. For example:
|
1207
|
-
#
|
1208
|
-
# def references(*args)
|
1209
|
-
# check_if_method_has_arguments!("references", args)
|
1210
|
-
# ...
|
1211
|
-
# end
|
1212
|
-
def check_if_method_has_arguments!(method_name, args)
|
1213
|
-
if args.blank?
|
1214
|
-
raise ArgumentError, "The method .#{method_name}() must contain arguments."
|
1215
1161
|
end
|
1216
|
-
end
|
1217
1162
|
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
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
|
1163
|
+
STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having]
|
1164
|
+
def structurally_incompatible_values_for_or(other)
|
1165
|
+
STRUCTURAL_OR_METHODS.reject do |method|
|
1166
|
+
get_value(method) == other.get_value(method)
|
1167
|
+
end
|
1168
|
+
end
|
1237
1169
|
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1170
|
+
def where_clause_factory
|
1171
|
+
@where_clause_factory ||= Relation::WhereClauseFactory.new(klass, predicate_builder)
|
1172
|
+
end
|
1173
|
+
alias having_clause_factory where_clause_factory
|
1174
|
+
|
1175
|
+
def default_value_for(name)
|
1176
|
+
case name
|
1177
|
+
when :create_with
|
1178
|
+
FROZEN_EMPTY_HASH
|
1179
|
+
when :readonly
|
1180
|
+
false
|
1181
|
+
when :where, :having
|
1182
|
+
Relation::WhereClause.empty
|
1183
|
+
when :from
|
1184
|
+
Relation::FromClause.empty
|
1185
|
+
when *Relation::MULTI_VALUE_METHODS
|
1186
|
+
FROZEN_EMPTY_ARRAY
|
1187
|
+
when *Relation::SINGLE_VALUE_METHODS
|
1188
|
+
nil
|
1189
|
+
else
|
1190
|
+
raise ArgumentError, "unknown relation value #{name.inspect}"
|
1191
|
+
end
|
1192
|
+
end
|
1241
1193
|
end
|
1242
1194
|
end
|