activerecord 4.1.15 → 4.2.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1162 -1792
- data/README.rdoc +15 -10
- data/lib/active_record.rb +4 -0
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +83 -38
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +63 -27
- data/lib/active_record/associations/collection_proxy.rb +29 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +26 -13
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/preloader.rb +36 -26
- data/lib/active_record/associations/preloader/association.rb +14 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +5 -12
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods.rb +56 -94
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +19 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -39
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -11
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +55 -69
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- data/lib/active_record/migration.rb +71 -46
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +5 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +46 -26
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +18 -11
- data/lib/active_record/railties/databases.rake +50 -51
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +273 -114
- data/lib/active_record/relation.rb +57 -25
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/finder_methods.rb +70 -47
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder.rb +16 -8
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
- data/lib/active_record/relation/query_methods.rb +114 -65
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +53 -27
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +5 -3
- data/lib/active_record/validations/uniqueness.rb +25 -29
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +66 -11
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -13,7 +13,7 @@ module ActiveRecord
|
|
13
13
|
@hash = hash
|
14
14
|
end
|
15
15
|
|
16
|
-
def merge
|
16
|
+
def merge #:nodoc:
|
17
17
|
Merger.new(relation, other).merge
|
18
18
|
end
|
19
19
|
|
@@ -51,7 +51,8 @@ module ActiveRecord
|
|
51
51
|
|
52
52
|
NORMAL_VALUES = Relation::SINGLE_VALUE_METHODS +
|
53
53
|
Relation::MULTI_VALUE_METHODS -
|
54
|
-
[:joins, :where, :order, :bind, :reverse_order, :lock, :create_with, :reordering, :from] # :nodoc:
|
54
|
+
[:includes, :preload, :joins, :where, :order, :bind, :reverse_order, :lock, :create_with, :reordering, :from] # :nodoc:
|
55
|
+
|
55
56
|
|
56
57
|
def normal_values
|
57
58
|
NORMAL_VALUES
|
@@ -75,6 +76,7 @@ module ActiveRecord
|
|
75
76
|
|
76
77
|
merge_multi_values
|
77
78
|
merge_single_values
|
79
|
+
merge_preloads
|
78
80
|
merge_joins
|
79
81
|
|
80
82
|
relation
|
@@ -82,13 +84,34 @@ module ActiveRecord
|
|
82
84
|
|
83
85
|
private
|
84
86
|
|
87
|
+
def merge_preloads
|
88
|
+
return if other.preload_values.empty? && other.includes_values.empty?
|
89
|
+
|
90
|
+
if other.klass == relation.klass
|
91
|
+
relation.preload!(*other.preload_values) unless other.preload_values.empty?
|
92
|
+
relation.includes!(other.includes_values) unless other.includes_values.empty?
|
93
|
+
else
|
94
|
+
reflection = relation.klass.reflect_on_all_associations.find do |r|
|
95
|
+
r.class_name == other.klass.name
|
96
|
+
end || return
|
97
|
+
|
98
|
+
unless other.preload_values.empty?
|
99
|
+
relation.preload! reflection.name => other.preload_values
|
100
|
+
end
|
101
|
+
|
102
|
+
unless other.includes_values.empty?
|
103
|
+
relation.includes! reflection.name => other.includes_values
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
85
108
|
def merge_joins
|
86
|
-
return if
|
109
|
+
return if other.joins_values.blank?
|
87
110
|
|
88
111
|
if other.klass == relation.klass
|
89
|
-
relation.joins!(*
|
112
|
+
relation.joins!(*other.joins_values)
|
90
113
|
else
|
91
|
-
joins_dependency, rest =
|
114
|
+
joins_dependency, rest = other.joins_values.partition do |join|
|
92
115
|
case join
|
93
116
|
when Hash, Symbol, Array
|
94
117
|
true
|
@@ -108,49 +131,36 @@ module ActiveRecord
|
|
108
131
|
|
109
132
|
def merge_multi_values
|
110
133
|
lhs_wheres = relation.where_values
|
111
|
-
rhs_wheres =
|
134
|
+
rhs_wheres = other.where_values
|
112
135
|
|
113
136
|
lhs_binds = relation.bind_values
|
114
|
-
rhs_binds =
|
137
|
+
rhs_binds = other.bind_values
|
115
138
|
|
116
139
|
removed, kept = partition_overwrites(lhs_wheres, rhs_wheres)
|
117
140
|
|
118
141
|
where_values = kept + rhs_wheres
|
119
142
|
bind_values = filter_binds(lhs_binds, removed) + rhs_binds
|
120
143
|
|
121
|
-
conn = relation.klass.connection
|
122
|
-
bv_index = 0
|
123
|
-
where_values.map! do |node|
|
124
|
-
if Arel::Nodes::Equality === node && Arel::Nodes::BindParam === node.right
|
125
|
-
substitute = conn.substitute_at(bind_values[bv_index].first, bv_index)
|
126
|
-
bv_index += 1
|
127
|
-
Arel::Nodes::Equality.new(node.left, substitute)
|
128
|
-
else
|
129
|
-
node
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
144
|
relation.where_values = where_values
|
134
145
|
relation.bind_values = bind_values
|
135
146
|
|
136
|
-
if
|
147
|
+
if other.reordering_value
|
137
148
|
# override any order specified in the original relation
|
138
|
-
relation.reorder!
|
139
|
-
elsif
|
149
|
+
relation.reorder! other.order_values
|
150
|
+
elsif other.order_values
|
140
151
|
# merge in order_values from relation
|
141
|
-
relation.order!
|
152
|
+
relation.order! other.order_values
|
142
153
|
end
|
143
154
|
|
144
|
-
relation.extend(*
|
155
|
+
relation.extend(*other.extending_values) unless other.extending_values.blank?
|
145
156
|
end
|
146
157
|
|
147
158
|
def merge_single_values
|
148
|
-
relation.from_value =
|
149
|
-
relation.lock_value =
|
150
|
-
relation.reverse_order_value = values[:reverse_order]
|
159
|
+
relation.from_value = other.from_value unless relation.from_value
|
160
|
+
relation.lock_value = other.lock_value unless relation.lock_value
|
151
161
|
|
152
|
-
unless
|
153
|
-
relation.create_with_value = (relation.create_with_value || {}).merge(
|
162
|
+
unless other.create_with_value.blank?
|
163
|
+
relation.create_with_value = (relation.create_with_value || {}).merge(other.create_with_value)
|
154
164
|
end
|
155
165
|
end
|
156
166
|
|
@@ -6,7 +6,9 @@ module ActiveRecord
|
|
6
6
|
autoload :ArrayHandler, 'active_record/relation/predicate_builder/array_handler'
|
7
7
|
|
8
8
|
def self.resolve_column_aliases(klass, hash)
|
9
|
-
|
9
|
+
# This method is a hot spot, so for now, use Hash[] to dup the hash.
|
10
|
+
# https://bugs.ruby-lang.org/issues/7166
|
11
|
+
hash = Hash[hash]
|
10
12
|
hash.keys.grep(Symbol) do |key|
|
11
13
|
if klass.attribute_alias? key
|
12
14
|
hash[klass.attribute_alias(key)] = hash.delete key
|
@@ -26,7 +28,7 @@ module ActiveRecord
|
|
26
28
|
queries << '1=0'
|
27
29
|
else
|
28
30
|
table = Arel::Table.new(column, default_table.engine)
|
29
|
-
association = klass._reflect_on_association(column
|
31
|
+
association = klass._reflect_on_association(column)
|
30
32
|
|
31
33
|
value.each do |k, v|
|
32
34
|
queries.concat expand(association && association.klass, table, k, v)
|
@@ -55,8 +57,9 @@ module ActiveRecord
|
|
55
57
|
#
|
56
58
|
# For polymorphic relationships, find the foreign key and type:
|
57
59
|
# PriceEstimate.where(estimate_of: treasure)
|
58
|
-
if klass && reflection = klass._reflect_on_association(column
|
60
|
+
if klass && reflection = klass._reflect_on_association(column)
|
59
61
|
base_class = polymorphic_base_class_from_value(value)
|
62
|
+
|
60
63
|
if reflection.polymorphic? && base_class
|
61
64
|
queries << build(table[reflection.foreign_type], base_class)
|
62
65
|
end
|
@@ -111,16 +114,15 @@ module ActiveRecord
|
|
111
114
|
@handlers.unshift([klass, handler])
|
112
115
|
end
|
113
116
|
|
114
|
-
|
117
|
+
BASIC_OBJECT_HANDLER = ->(attribute, value) { attribute.eq(value) } # :nodoc:
|
118
|
+
register_handler(BasicObject, BASIC_OBJECT_HANDLER)
|
115
119
|
# FIXME: I think we need to deprecate this behavior
|
116
120
|
register_handler(Class, ->(attribute, value) { attribute.eq(value.name) })
|
117
121
|
register_handler(Base, ->(attribute, value) { attribute.eq(value.id) })
|
118
|
-
register_handler(Range, ->(attribute, value) { attribute.
|
122
|
+
register_handler(Range, ->(attribute, value) { attribute.between(value) })
|
119
123
|
register_handler(Relation, RelationHandler.new)
|
120
124
|
register_handler(Array, ArrayHandler.new)
|
121
125
|
|
122
|
-
private
|
123
|
-
|
124
126
|
def self.build(attribute, value)
|
125
127
|
handler_for(value).call(attribute, value)
|
126
128
|
end
|
@@ -138,10 +140,16 @@ module ActiveRecord
|
|
138
140
|
when Array
|
139
141
|
value.map { |v| convert_value_to_association_ids(v, primary_key) }
|
140
142
|
when Base
|
141
|
-
value.
|
143
|
+
value._read_attribute(primary_key)
|
142
144
|
else
|
143
145
|
value
|
144
146
|
end
|
145
147
|
end
|
148
|
+
|
149
|
+
def self.can_be_bound?(value) # :nodoc:
|
150
|
+
!value.nil? &&
|
151
|
+
!value.is_a?(Hash) &&
|
152
|
+
handler_for(value) == BASIC_OBJECT_HANDLER
|
153
|
+
end
|
146
154
|
end
|
147
155
|
end
|
@@ -1,29 +1,48 @@
|
|
1
|
+
require 'active_support/core_ext/string/filters'
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
class PredicateBuilder
|
3
5
|
class ArrayHandler # :nodoc:
|
4
6
|
def call(attribute, value)
|
5
7
|
values = value.map { |x| x.is_a?(Base) ? x.id : x }
|
6
|
-
|
8
|
+
nils, values = values.partition(&:nil?)
|
9
|
+
|
10
|
+
if values.any? { |val| val.is_a?(Array) }
|
11
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
12
|
+
Passing a nested array to Active Record finder methods is
|
13
|
+
deprecated and will be removed. Flatten your array before using
|
14
|
+
it for 'IN' conditions.
|
15
|
+
MSG
|
7
16
|
|
8
|
-
|
9
|
-
values =
|
17
|
+
flat_values = values.flatten
|
18
|
+
values = flat_values unless flat_values.include?(nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
return attribute.in([]) if values.empty? && nils.empty?
|
10
22
|
|
23
|
+
ranges, values = values.partition { |v| v.is_a?(Range) }
|
24
|
+
|
25
|
+
values_predicate =
|
11
26
|
case values.length
|
12
|
-
when 0
|
13
|
-
|
14
|
-
|
15
|
-
attribute.eq(values.first).or(attribute.eq(nil))
|
16
|
-
else
|
17
|
-
attribute.in(values).or(attribute.eq(nil))
|
27
|
+
when 0 then NullPredicate
|
28
|
+
when 1 then attribute.eq(values.first)
|
29
|
+
else attribute.in(values)
|
18
30
|
end
|
19
|
-
|
20
|
-
|
31
|
+
|
32
|
+
unless nils.empty?
|
33
|
+
values_predicate = values_predicate.or(attribute.eq(nil))
|
21
34
|
end
|
22
35
|
|
23
|
-
array_predicates = ranges.map { |range| attribute.
|
24
|
-
array_predicates
|
36
|
+
array_predicates = ranges.map { |range| attribute.between(range) }
|
37
|
+
array_predicates.unshift(values_predicate)
|
25
38
|
array_predicates.inject { |composite, predicate| composite.or(predicate) }
|
26
39
|
end
|
40
|
+
|
41
|
+
module NullPredicate # :nodoc:
|
42
|
+
def self.or(other)
|
43
|
+
other
|
44
|
+
end
|
45
|
+
end
|
27
46
|
end
|
28
47
|
end
|
29
48
|
end
|
@@ -6,11 +6,7 @@ module ActiveRecord
|
|
6
6
|
value = value.select(value.klass.arel_table[value.klass.primary_key])
|
7
7
|
end
|
8
8
|
|
9
|
-
value
|
10
|
-
value.where_values = value.where_values.map do |node|
|
11
|
-
node.dup rescue node
|
12
|
-
end
|
13
|
-
attribute.in(value.arel.ast)
|
9
|
+
attribute.in(value.arel)
|
14
10
|
end
|
15
11
|
end
|
16
12
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_support/core_ext/array/wrap'
|
2
|
+
require 'active_support/core_ext/string/filters'
|
2
3
|
require 'active_model/forbidden_attributes_protection'
|
3
4
|
|
4
5
|
module ActiveRecord
|
@@ -67,6 +68,7 @@ module ActiveRecord
|
|
67
68
|
#
|
68
69
|
def #{name}_values=(values) # def select_values=(values)
|
69
70
|
raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
|
71
|
+
check_cached_relation
|
70
72
|
@values[:#{name}] = values # @values[:select] = values
|
71
73
|
end # end
|
72
74
|
CODE
|
@@ -84,11 +86,22 @@ module ActiveRecord
|
|
84
86
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
85
87
|
def #{name}_value=(value) # def readonly_value=(value)
|
86
88
|
raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
|
89
|
+
check_cached_relation
|
87
90
|
@values[:#{name}] = value # @values[:readonly] = value
|
88
91
|
end # end
|
89
92
|
CODE
|
90
93
|
end
|
91
94
|
|
95
|
+
def check_cached_relation # :nodoc:
|
96
|
+
if defined?(@arel) && @arel
|
97
|
+
@arel = nil
|
98
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
99
|
+
Modifying already cached Relation. The cache will be reset. Use a
|
100
|
+
cloned Relation to prevent this warning.
|
101
|
+
MSG
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
92
105
|
def create_with_value # :nodoc:
|
93
106
|
@values[:create_with] || {}
|
94
107
|
end
|
@@ -173,7 +186,7 @@ module ActiveRecord
|
|
173
186
|
|
174
187
|
# Use to indicate that the given +table_names+ are referenced by an SQL string,
|
175
188
|
# and should therefore be JOINed in any query rather than loaded separately.
|
176
|
-
# This method only works in
|
189
|
+
# This method only works in conjunction with +includes+.
|
177
190
|
# See #includes for more details.
|
178
191
|
#
|
179
192
|
# User.includes(:posts).where("posts.name = 'foo'")
|
@@ -207,7 +220,7 @@ module ActiveRecord
|
|
207
220
|
# fields are retrieved:
|
208
221
|
#
|
209
222
|
# Model.select(:field)
|
210
|
-
# # => [#<Model field:value>]
|
223
|
+
# # => [#<Model id: nil, field: "value">]
|
211
224
|
#
|
212
225
|
# Although in the above example it looks as though this method returns an
|
213
226
|
# array, it actually returns a relation object and can have other query
|
@@ -216,12 +229,12 @@ module ActiveRecord
|
|
216
229
|
# The argument to the method can also be an array of fields.
|
217
230
|
#
|
218
231
|
# Model.select(:field, :other_field, :and_one_more)
|
219
|
-
# # => [#<Model field: "value", other_field: "value", and_one_more: "value">]
|
232
|
+
# # => [#<Model id: nil, field: "value", other_field: "value", and_one_more: "value">]
|
220
233
|
#
|
221
234
|
# You can also use one or more strings, which will be used unchanged as SELECT fields.
|
222
235
|
#
|
223
236
|
# Model.select('field AS field_one', 'other_field AS field_two')
|
224
|
-
# # => [#<Model field: "value", other_field: "value">]
|
237
|
+
# # => [#<Model id: nil, field: "value", other_field: "value">]
|
225
238
|
#
|
226
239
|
# If an alias was specified, it will be accessible from the resulting objects:
|
227
240
|
#
|
@@ -229,7 +242,7 @@ module ActiveRecord
|
|
229
242
|
# # => "value"
|
230
243
|
#
|
231
244
|
# Accessing attributes of an object that do not have fields retrieved by a select
|
232
|
-
# will throw <tt>ActiveModel::MissingAttributeError</tt>:
|
245
|
+
# except +id+ will throw <tt>ActiveModel::MissingAttributeError</tt>:
|
233
246
|
#
|
234
247
|
# Model.select(:field).first.other_field
|
235
248
|
# # => ActiveModel::MissingAttributeError: missing attribute: other_field
|
@@ -245,7 +258,7 @@ module ActiveRecord
|
|
245
258
|
def _select!(*fields) # :nodoc:
|
246
259
|
fields.flatten!
|
247
260
|
fields.map! do |field|
|
248
|
-
klass.attribute_alias?(field) ? klass.attribute_alias(field) : field
|
261
|
+
klass.attribute_alias?(field) ? klass.attribute_alias(field).to_sym : field
|
249
262
|
end
|
250
263
|
self.select_values += fields
|
251
264
|
self
|
@@ -266,6 +279,10 @@ module ActiveRecord
|
|
266
279
|
#
|
267
280
|
# User.group('name AS grouped_name, age')
|
268
281
|
# => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
|
282
|
+
#
|
283
|
+
# Passing in an array of attributes to group by is also supported.
|
284
|
+
# 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">]
|
269
286
|
def group(*args)
|
270
287
|
check_if_method_has_arguments!(:group, args)
|
271
288
|
spawn.group!(*args)
|
@@ -280,15 +297,6 @@ module ActiveRecord
|
|
280
297
|
|
281
298
|
# Allows to specify an order attribute:
|
282
299
|
#
|
283
|
-
# User.order('name')
|
284
|
-
# => SELECT "users".* FROM "users" ORDER BY name
|
285
|
-
#
|
286
|
-
# User.order('name DESC')
|
287
|
-
# => SELECT "users".* FROM "users" ORDER BY name DESC
|
288
|
-
#
|
289
|
-
# User.order('name DESC, email')
|
290
|
-
# => SELECT "users".* FROM "users" ORDER BY name DESC, email
|
291
|
-
#
|
292
300
|
# User.order(:name)
|
293
301
|
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
|
294
302
|
#
|
@@ -297,6 +305,15 @@ module ActiveRecord
|
|
297
305
|
#
|
298
306
|
# User.order(:name, email: :desc)
|
299
307
|
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
|
308
|
+
#
|
309
|
+
# User.order('name')
|
310
|
+
# => SELECT "users".* FROM "users" ORDER BY name
|
311
|
+
#
|
312
|
+
# User.order('name DESC')
|
313
|
+
# => SELECT "users".* FROM "users" ORDER BY name DESC
|
314
|
+
#
|
315
|
+
# User.order('name DESC, email')
|
316
|
+
# => SELECT "users".* FROM "users" ORDER BY name DESC, email
|
300
317
|
def order(*args)
|
301
318
|
check_if_method_has_arguments!(:order, args)
|
302
319
|
spawn.order!(*args)
|
@@ -410,19 +427,17 @@ module ActiveRecord
|
|
410
427
|
# => SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
|
411
428
|
def joins(*args)
|
412
429
|
check_if_method_has_arguments!(:joins, args)
|
413
|
-
|
414
|
-
args.compact!
|
415
|
-
args.flatten!
|
416
|
-
|
417
430
|
spawn.joins!(*args)
|
418
431
|
end
|
419
432
|
|
420
433
|
def joins!(*args) # :nodoc:
|
434
|
+
args.compact!
|
435
|
+
args.flatten!
|
421
436
|
self.joins_values += args
|
422
437
|
self
|
423
438
|
end
|
424
439
|
|
425
|
-
def bind(value)
|
440
|
+
def bind(value) # :nodoc:
|
426
441
|
spawn.bind!(value)
|
427
442
|
end
|
428
443
|
|
@@ -560,18 +575,14 @@ module ActiveRecord
|
|
560
575
|
end
|
561
576
|
end
|
562
577
|
|
563
|
-
def where!(opts
|
564
|
-
if
|
565
|
-
|
566
|
-
|
567
|
-
if Hash === opts
|
568
|
-
opts = sanitize_forbidden_attributes(opts)
|
569
|
-
references!(PredicateBuilder.references(opts))
|
570
|
-
end
|
571
|
-
|
572
|
-
self.where_values += build_where(opts, rest)
|
573
|
-
self
|
578
|
+
def where!(opts, *rest) # :nodoc:
|
579
|
+
if Hash === opts
|
580
|
+
opts = sanitize_forbidden_attributes(opts)
|
581
|
+
references!(PredicateBuilder.references(opts))
|
574
582
|
end
|
583
|
+
|
584
|
+
self.where_values += build_where(opts, rest)
|
585
|
+
self
|
575
586
|
end
|
576
587
|
|
577
588
|
# Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
|
@@ -746,6 +757,9 @@ module ActiveRecord
|
|
746
757
|
|
747
758
|
def from!(value, subquery_name = nil) # :nodoc:
|
748
759
|
self.from_value = [value, subquery_name]
|
760
|
+
if value.is_a? Relation
|
761
|
+
self.bind_values = value.arel.bind_values + value.bind_values + bind_values
|
762
|
+
end
|
749
763
|
self
|
750
764
|
end
|
751
765
|
|
@@ -833,7 +847,9 @@ module ActiveRecord
|
|
833
847
|
end
|
834
848
|
|
835
849
|
def reverse_order! # :nodoc:
|
836
|
-
|
850
|
+
orders = order_values.uniq
|
851
|
+
orders.reject!(&:blank?)
|
852
|
+
self.order_values = reverse_sql_order(orders)
|
837
853
|
self
|
838
854
|
end
|
839
855
|
|
@@ -849,7 +865,7 @@ module ActiveRecord
|
|
849
865
|
|
850
866
|
build_joins(arel, joins_values.flatten) unless joins_values.empty?
|
851
867
|
|
852
|
-
collapse_wheres(arel, (where_values - [''])
|
868
|
+
collapse_wheres(arel, (where_values - [''])) #TODO: Add uniq with real value comparison / ignore uniqs that have binds
|
853
869
|
|
854
870
|
arel.having(*having_values.uniq.reject(&:blank?)) unless having_values.empty?
|
855
871
|
|
@@ -865,15 +881,6 @@ module ActiveRecord
|
|
865
881
|
arel.from(build_from) if from_value
|
866
882
|
arel.lock(lock_value) if lock_value
|
867
883
|
|
868
|
-
# Reorder bind indexes if joins produced bind values
|
869
|
-
bvs = arel.bind_values + bind_values
|
870
|
-
if bvs.any?
|
871
|
-
arel.ast.grep(Arel::Nodes::BindParam).each_with_index do |bp, i|
|
872
|
-
column = bvs[i].first
|
873
|
-
bp.replace connection.substitute_at(column, i)
|
874
|
-
end
|
875
|
-
end
|
876
|
-
|
877
884
|
arel
|
878
885
|
end
|
879
886
|
|
@@ -887,8 +894,9 @@ module ActiveRecord
|
|
887
894
|
|
888
895
|
case scope
|
889
896
|
when :order
|
890
|
-
self.reverse_order_value = false
|
891
897
|
result = []
|
898
|
+
when :where
|
899
|
+
self.bind_values = []
|
892
900
|
else
|
893
901
|
result = [] unless single_val_method
|
894
902
|
end
|
@@ -899,9 +907,9 @@ module ActiveRecord
|
|
899
907
|
def where_unscoping(target_value)
|
900
908
|
target_value = target_value.to_s
|
901
909
|
|
902
|
-
where_values.reject
|
910
|
+
self.where_values = where_values.reject do |rel|
|
903
911
|
case rel
|
904
|
-
when Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual
|
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
|
905
913
|
subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right)
|
906
914
|
subrelation.name == target_value
|
907
915
|
end
|
@@ -939,18 +947,14 @@ module ActiveRecord
|
|
939
947
|
def build_where(opts, other = [])
|
940
948
|
case opts
|
941
949
|
when String, Array
|
942
|
-
#TODO: Remove duplication with: /activerecord/lib/active_record/sanitization.rb:113
|
943
|
-
values = Hash === other.first ? other.first.values : other
|
944
|
-
|
945
|
-
values.grep(ActiveRecord::Relation) do |rel|
|
946
|
-
self.bind_values += rel.bind_values
|
947
|
-
end
|
948
|
-
|
949
950
|
[@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
|
950
951
|
when Hash
|
951
952
|
opts = PredicateBuilder.resolve_column_aliases(klass, opts)
|
952
|
-
attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
|
953
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)
|
954
958
|
add_relations_to_bind_values(attributes)
|
955
959
|
|
956
960
|
PredicateBuilder.build_from_hash(klass, attributes, table)
|
@@ -959,12 +963,50 @@ module ActiveRecord
|
|
959
963
|
end
|
960
964
|
end
|
961
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
|
+
def association_for_table(table_name)
|
1000
|
+
table_name = table_name.to_s
|
1001
|
+
@klass._reflect_on_association(table_name) ||
|
1002
|
+
@klass._reflect_on_association(table_name.singularize)
|
1003
|
+
end
|
1004
|
+
|
962
1005
|
def build_from
|
963
1006
|
opts, name = from_value
|
964
1007
|
case opts
|
965
1008
|
when Relation
|
966
1009
|
name ||= 'subquery'
|
967
|
-
self.bind_values = opts.bind_values + self.bind_values
|
968
1010
|
opts.arel.as(name.to_s)
|
969
1011
|
else
|
970
1012
|
opts
|
@@ -1000,9 +1042,12 @@ module ActiveRecord
|
|
1000
1042
|
join_list
|
1001
1043
|
)
|
1002
1044
|
|
1003
|
-
|
1045
|
+
join_infos = join_dependency.join_constraints stashed_association_joins
|
1004
1046
|
|
1005
|
-
|
1047
|
+
join_infos.each do |info|
|
1048
|
+
info.joins.each { |join| manager.from(join) }
|
1049
|
+
manager.bind_values.concat info.binds
|
1050
|
+
end
|
1006
1051
|
|
1007
1052
|
manager.join_sources.concat(join_list)
|
1008
1053
|
|
@@ -1019,8 +1064,10 @@ module ActiveRecord
|
|
1019
1064
|
|
1020
1065
|
def arel_columns(columns)
|
1021
1066
|
columns.map do |field|
|
1022
|
-
if columns_hash.key?(field.to_s)
|
1067
|
+
if (Symbol === field || String === field) && columns_hash.key?(field.to_s) && !from_value
|
1023
1068
|
arel_table[field]
|
1069
|
+
elsif Symbol === field
|
1070
|
+
connection.quote_table_name(field.to_s)
|
1024
1071
|
else
|
1025
1072
|
field
|
1026
1073
|
end
|
@@ -1052,15 +1099,19 @@ module ActiveRecord
|
|
1052
1099
|
def build_order(arel)
|
1053
1100
|
orders = order_values.uniq
|
1054
1101
|
orders.reject!(&:blank?)
|
1055
|
-
orders = reverse_sql_order(orders) if reverse_order_value
|
1056
1102
|
|
1057
1103
|
arel.order(*orders) unless orders.empty?
|
1058
1104
|
end
|
1059
1105
|
|
1106
|
+
VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
|
1107
|
+
'asc', 'desc', 'ASC', 'DESC'] # :nodoc:
|
1108
|
+
|
1060
1109
|
def validate_order_args(args)
|
1061
|
-
args.
|
1062
|
-
unless (
|
1063
|
-
|
1110
|
+
args.each do |arg|
|
1111
|
+
next unless arg.is_a?(Hash)
|
1112
|
+
arg.each do |_key, value|
|
1113
|
+
raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
|
1114
|
+
"directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
|
1064
1115
|
end
|
1065
1116
|
end
|
1066
1117
|
end
|
@@ -1082,7 +1133,7 @@ module ActiveRecord
|
|
1082
1133
|
when Hash
|
1083
1134
|
arg.map { |field, dir|
|
1084
1135
|
field = klass.attribute_alias(field) if klass.attribute_alias?(field)
|
1085
|
-
table[field].send(dir)
|
1136
|
+
table[field].send(dir.downcase)
|
1086
1137
|
}
|
1087
1138
|
else
|
1088
1139
|
arg
|
@@ -1112,13 +1163,11 @@ module ActiveRecord
|
|
1112
1163
|
end
|
1113
1164
|
end
|
1114
1165
|
|
1115
|
-
# This function is recursive just for better readablity.
|
1116
|
-
# #where argument doesn't support more than one level nested hash in real world.
|
1117
1166
|
def add_relations_to_bind_values(attributes)
|
1118
1167
|
if attributes.is_a?(Hash)
|
1119
1168
|
attributes.each_value do |value|
|
1120
1169
|
if value.is_a?(ActiveRecord::Relation)
|
1121
|
-
self.bind_values += value.bind_values
|
1170
|
+
self.bind_values += value.arel.bind_values + value.bind_values
|
1122
1171
|
else
|
1123
1172
|
add_relations_to_bind_values(value)
|
1124
1173
|
end
|