activerecord 4.1.15 → 4.2.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 +4 -4
- data/CHANGELOG.md +634 -2176
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/association_scope.rb +53 -21
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
- 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 +32 -44
- data/lib/active_record/associations/collection_proxy.rb +1 -10
- data/lib/active_record/associations/has_many_association.rb +60 -14
- data/lib/active_record/associations/has_many_through_association.rb +34 -23
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
- data/lib/active_record/associations/join_dependency.rb +7 -9
- data/lib/active_record/associations/preloader/association.rb +9 -5
- data/lib/active_record/associations/preloader/through_association.rb +3 -3
- data/lib/active_record/associations/preloader.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +16 -1
- data/lib/active_record/associations/through_association.rb +6 -22
- data/lib/active_record/associations.rb +58 -33
- data/lib/active_record/attribute.rb +131 -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/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +85 -42
- data/lib/active_record/attribute_methods/primary_key.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +14 -57
- data/lib/active_record/attribute_methods/serialization.rb +12 -146
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
- data/lib/active_record/attribute_methods/write.rb +8 -23
- data/lib/active_record/attribute_methods.rb +53 -90
- data/lib/active_record/attribute_set/builder.rb +32 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +122 -0
- data/lib/active_record/autosave_association.rb +11 -21
- data/lib/active_record/base.rb +9 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
- data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
- data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
- data/lib/active_record/connection_adapters/column.rb +13 -244
- data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
- data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
- 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 +39 -20
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -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 +14 -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 +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -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 +76 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -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 +85 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -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/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
- data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +119 -22
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -10
- data/lib/active_record/errors.rb +27 -26
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +52 -45
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +33 -8
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +34 -16
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +22 -32
- data/lib/active_record/model_schema.rb +39 -48
- data/lib/active_record/nested_attributes.rb +8 -18
- data/lib/active_record/persistence.rb +39 -22
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +1 -8
- data/lib/active_record/railtie.rb +17 -10
- data/lib/active_record/railties/databases.rake +47 -42
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +225 -92
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +28 -32
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +42 -20
- data/lib/active_record/relation/merger.rb +0 -1
- data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
- data/lib/active_record/relation/predicate_builder.rb +1 -22
- data/lib/active_record/relation/query_methods.rb +98 -62
- data/lib/active_record/relation/spawn_methods.rb +6 -7
- data/lib/active_record/relation.rb +35 -11
- data/lib/active_record/result.rb +16 -9
- data/lib/active_record/sanitization.rb +8 -1
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +51 -9
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +5 -4
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +79 -5
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +37 -5
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +35 -21
- data/lib/active_record/type/binary.rb +40 -0
- data/lib/active_record/type/boolean.rb +19 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
- data/lib/active_record/type/integer.rb +23 -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 +51 -0
- data/lib/active_record/type/string.rb +36 -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 +48 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/type.rb +20 -0
- data/lib/active_record/validations/uniqueness.rb +9 -23
- data/lib/active_record/validations.rb +21 -16
- data/lib/active_record.rb +2 -1
- 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 +71 -14
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -67,6 +67,7 @@ module ActiveRecord
|
|
67
67
|
#
|
68
68
|
def #{name}_values=(values) # def select_values=(values)
|
69
69
|
raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
|
70
|
+
check_cached_relation
|
70
71
|
@values[:#{name}] = values # @values[:select] = values
|
71
72
|
end # end
|
72
73
|
CODE
|
@@ -84,11 +85,22 @@ module ActiveRecord
|
|
84
85
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
85
86
|
def #{name}_value=(value) # def readonly_value=(value)
|
86
87
|
raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
|
88
|
+
check_cached_relation
|
87
89
|
@values[:#{name}] = value # @values[:readonly] = value
|
88
90
|
end # end
|
89
91
|
CODE
|
90
92
|
end
|
91
93
|
|
94
|
+
def check_cached_relation # :nodoc:
|
95
|
+
if defined?(@arel) && @arel
|
96
|
+
@arel = nil
|
97
|
+
ActiveSupport::Deprecation.warn <<-WARNING
|
98
|
+
Modifying already cached Relation. The cache will be reset.
|
99
|
+
Use a cloned Relation to prevent this warning.
|
100
|
+
WARNING
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
92
104
|
def create_with_value # :nodoc:
|
93
105
|
@values[:create_with] || {}
|
94
106
|
end
|
@@ -173,7 +185,7 @@ module ActiveRecord
|
|
173
185
|
|
174
186
|
# Use to indicate that the given +table_names+ are referenced by an SQL string,
|
175
187
|
# and should therefore be JOINed in any query rather than loaded separately.
|
176
|
-
# This method only works in
|
188
|
+
# This method only works in conjunction with +includes+.
|
177
189
|
# See #includes for more details.
|
178
190
|
#
|
179
191
|
# User.includes(:posts).where("posts.name = 'foo'")
|
@@ -207,7 +219,7 @@ module ActiveRecord
|
|
207
219
|
# fields are retrieved:
|
208
220
|
#
|
209
221
|
# Model.select(:field)
|
210
|
-
# # => [#<Model field:value>]
|
222
|
+
# # => [#<Model id: nil, field: "value">]
|
211
223
|
#
|
212
224
|
# Although in the above example it looks as though this method returns an
|
213
225
|
# array, it actually returns a relation object and can have other query
|
@@ -216,12 +228,12 @@ module ActiveRecord
|
|
216
228
|
# The argument to the method can also be an array of fields.
|
217
229
|
#
|
218
230
|
# Model.select(:field, :other_field, :and_one_more)
|
219
|
-
# # => [#<Model field: "value", other_field: "value", and_one_more: "value">]
|
231
|
+
# # => [#<Model id: nil, field: "value", other_field: "value", and_one_more: "value">]
|
220
232
|
#
|
221
233
|
# You can also use one or more strings, which will be used unchanged as SELECT fields.
|
222
234
|
#
|
223
235
|
# Model.select('field AS field_one', 'other_field AS field_two')
|
224
|
-
# # => [#<Model field: "value", other_field: "value">]
|
236
|
+
# # => [#<Model id: nil, field: "value", other_field: "value">]
|
225
237
|
#
|
226
238
|
# If an alias was specified, it will be accessible from the resulting objects:
|
227
239
|
#
|
@@ -229,7 +241,7 @@ module ActiveRecord
|
|
229
241
|
# # => "value"
|
230
242
|
#
|
231
243
|
# Accessing attributes of an object that do not have fields retrieved by a select
|
232
|
-
# will throw <tt>ActiveModel::MissingAttributeError</tt>:
|
244
|
+
# except +id+ will throw <tt>ActiveModel::MissingAttributeError</tt>:
|
233
245
|
#
|
234
246
|
# Model.select(:field).first.other_field
|
235
247
|
# # => ActiveModel::MissingAttributeError: missing attribute: other_field
|
@@ -266,6 +278,10 @@ module ActiveRecord
|
|
266
278
|
#
|
267
279
|
# User.group('name AS grouped_name, age')
|
268
280
|
# => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
|
281
|
+
#
|
282
|
+
# Passing in an array of attributes to group by is also supported.
|
283
|
+
# User.select([:id, :first_name]).group(:id, :first_name).first(3)
|
284
|
+
# => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
|
269
285
|
def group(*args)
|
270
286
|
check_if_method_has_arguments!(:group, args)
|
271
287
|
spawn.group!(*args)
|
@@ -280,15 +296,6 @@ module ActiveRecord
|
|
280
296
|
|
281
297
|
# Allows to specify an order attribute:
|
282
298
|
#
|
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
299
|
# User.order(:name)
|
293
300
|
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
|
294
301
|
#
|
@@ -297,6 +304,15 @@ module ActiveRecord
|
|
297
304
|
#
|
298
305
|
# User.order(:name, email: :desc)
|
299
306
|
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
|
307
|
+
#
|
308
|
+
# User.order('name')
|
309
|
+
# => SELECT "users".* FROM "users" ORDER BY name
|
310
|
+
#
|
311
|
+
# User.order('name DESC')
|
312
|
+
# => SELECT "users".* FROM "users" ORDER BY name DESC
|
313
|
+
#
|
314
|
+
# User.order('name DESC, email')
|
315
|
+
# => SELECT "users".* FROM "users" ORDER BY name DESC, email
|
300
316
|
def order(*args)
|
301
317
|
check_if_method_has_arguments!(:order, args)
|
302
318
|
spawn.order!(*args)
|
@@ -560,18 +576,14 @@ module ActiveRecord
|
|
560
576
|
end
|
561
577
|
end
|
562
578
|
|
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
|
579
|
+
def where!(opts, *rest) # :nodoc:
|
580
|
+
if Hash === opts
|
581
|
+
opts = sanitize_forbidden_attributes(opts)
|
582
|
+
references!(PredicateBuilder.references(opts))
|
574
583
|
end
|
584
|
+
|
585
|
+
self.where_values += build_where(opts, rest)
|
586
|
+
self
|
575
587
|
end
|
576
588
|
|
577
589
|
# Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
|
@@ -677,11 +689,11 @@ module ActiveRecord
|
|
677
689
|
# end
|
678
690
|
#
|
679
691
|
def none
|
680
|
-
|
692
|
+
extending(NullRelation)
|
681
693
|
end
|
682
694
|
|
683
695
|
def none! # :nodoc:
|
684
|
-
|
696
|
+
extending!(NullRelation)
|
685
697
|
end
|
686
698
|
|
687
699
|
# Sets readonly attributes for the returned relation. If value is
|
@@ -833,7 +845,9 @@ module ActiveRecord
|
|
833
845
|
end
|
834
846
|
|
835
847
|
def reverse_order! # :nodoc:
|
836
|
-
|
848
|
+
orders = order_values.uniq
|
849
|
+
orders.reject!(&:blank?)
|
850
|
+
self.order_values = reverse_sql_order(orders)
|
837
851
|
self
|
838
852
|
end
|
839
853
|
|
@@ -849,25 +863,26 @@ module ActiveRecord
|
|
849
863
|
|
850
864
|
build_joins(arel, joins_values.flatten) unless joins_values.empty?
|
851
865
|
|
852
|
-
collapse_wheres(arel, (where_values - [''])
|
866
|
+
collapse_wheres(arel, (where_values - [''])) #TODO: Add uniq with real value comparison / ignore uniqs that have binds
|
853
867
|
|
854
868
|
arel.having(*having_values.uniq.reject(&:blank?)) unless having_values.empty?
|
855
869
|
|
856
870
|
arel.take(connection.sanitize_limit(limit_value)) if limit_value
|
857
871
|
arel.skip(offset_value.to_i) if offset_value
|
858
|
-
|
872
|
+
|
873
|
+
arel.group(*group_values.uniq.reject(&:blank?)) unless group_values.empty?
|
859
874
|
|
860
875
|
build_order(arel)
|
861
876
|
|
862
|
-
build_select(arel)
|
877
|
+
build_select(arel, select_values.uniq)
|
863
878
|
|
864
879
|
arel.distinct(distinct_value)
|
865
880
|
arel.from(build_from) if from_value
|
866
881
|
arel.lock(lock_value) if lock_value
|
867
882
|
|
868
883
|
# Reorder bind indexes if joins produced bind values
|
869
|
-
|
870
|
-
|
884
|
+
if arel.bind_values.any?
|
885
|
+
bvs = arel.bind_values + bind_values
|
871
886
|
arel.ast.grep(Arel::Nodes::BindParam).each_with_index do |bp, i|
|
872
887
|
column = bvs[i].first
|
873
888
|
bp.replace connection.substitute_at(column, i)
|
@@ -887,8 +902,9 @@ module ActiveRecord
|
|
887
902
|
|
888
903
|
case scope
|
889
904
|
when :order
|
890
|
-
self.reverse_order_value = false
|
891
905
|
result = []
|
906
|
+
when :where
|
907
|
+
self.bind_values = []
|
892
908
|
else
|
893
909
|
result = [] unless single_val_method
|
894
910
|
end
|
@@ -939,18 +955,15 @@ module ActiveRecord
|
|
939
955
|
def build_where(opts, other = [])
|
940
956
|
case opts
|
941
957
|
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
958
|
[@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
|
950
959
|
when Hash
|
951
960
|
opts = PredicateBuilder.resolve_column_aliases(klass, opts)
|
952
|
-
attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
|
953
961
|
|
962
|
+
bv_len = bind_values.length
|
963
|
+
tmp_opts, bind_values = create_binds(opts, bv_len)
|
964
|
+
self.bind_values += bind_values
|
965
|
+
|
966
|
+
attributes = @klass.send(:expand_hash_conditions_for_aggregates, tmp_opts)
|
954
967
|
add_relations_to_bind_values(attributes)
|
955
968
|
|
956
969
|
PredicateBuilder.build_from_hash(klass, attributes, table)
|
@@ -959,6 +972,29 @@ module ActiveRecord
|
|
959
972
|
end
|
960
973
|
end
|
961
974
|
|
975
|
+
def create_binds(opts, idx)
|
976
|
+
bindable, non_binds = opts.partition do |column, value|
|
977
|
+
case value
|
978
|
+
when String, Integer, ActiveRecord::StatementCache::Substitute
|
979
|
+
@klass.columns_hash.include? column.to_s
|
980
|
+
else
|
981
|
+
false
|
982
|
+
end
|
983
|
+
end
|
984
|
+
|
985
|
+
new_opts = {}
|
986
|
+
binds = []
|
987
|
+
|
988
|
+
bindable.each_with_index do |(column,value), index|
|
989
|
+
binds.push [@klass.columns_hash[column.to_s], value]
|
990
|
+
new_opts[column] = connection.substitute_at(column, index + idx)
|
991
|
+
end
|
992
|
+
|
993
|
+
non_binds.each { |column,value| new_opts[column] = value }
|
994
|
+
|
995
|
+
[new_opts, binds]
|
996
|
+
end
|
997
|
+
|
962
998
|
def build_from
|
963
999
|
opts, name = from_value
|
964
1000
|
case opts
|
@@ -1000,33 +1036,29 @@ module ActiveRecord
|
|
1000
1036
|
join_list
|
1001
1037
|
)
|
1002
1038
|
|
1003
|
-
|
1039
|
+
join_infos = join_dependency.join_constraints stashed_association_joins
|
1004
1040
|
|
1005
|
-
|
1041
|
+
join_infos.each do |info|
|
1042
|
+
info.joins.each { |join| manager.from(join) }
|
1043
|
+
manager.bind_values.concat info.binds
|
1044
|
+
end
|
1006
1045
|
|
1007
1046
|
manager.join_sources.concat(join_list)
|
1008
1047
|
|
1009
1048
|
manager
|
1010
1049
|
end
|
1011
1050
|
|
1012
|
-
def build_select(arel)
|
1013
|
-
if
|
1014
|
-
|
1051
|
+
def build_select(arel, selects)
|
1052
|
+
if !selects.empty?
|
1053
|
+
expanded_select = selects.map do |field|
|
1054
|
+
columns_hash.key?(field.to_s) ? arel_table[field] : field
|
1055
|
+
end
|
1056
|
+
arel.project(*expanded_select)
|
1015
1057
|
else
|
1016
1058
|
arel.project(@klass.arel_table[Arel.star])
|
1017
1059
|
end
|
1018
1060
|
end
|
1019
1061
|
|
1020
|
-
def arel_columns(columns)
|
1021
|
-
columns.map do |field|
|
1022
|
-
if columns_hash.key?(field.to_s)
|
1023
|
-
arel_table[field]
|
1024
|
-
else
|
1025
|
-
field
|
1026
|
-
end
|
1027
|
-
end
|
1028
|
-
end
|
1029
|
-
|
1030
1062
|
def reverse_sql_order(order_query)
|
1031
1063
|
order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
|
1032
1064
|
|
@@ -1052,15 +1084,19 @@ module ActiveRecord
|
|
1052
1084
|
def build_order(arel)
|
1053
1085
|
orders = order_values.uniq
|
1054
1086
|
orders.reject!(&:blank?)
|
1055
|
-
orders = reverse_sql_order(orders) if reverse_order_value
|
1056
1087
|
|
1057
1088
|
arel.order(*orders) unless orders.empty?
|
1058
1089
|
end
|
1059
1090
|
|
1091
|
+
VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
|
1092
|
+
'asc', 'desc', 'ASC', 'DESC'] # :nodoc:
|
1093
|
+
|
1060
1094
|
def validate_order_args(args)
|
1061
|
-
args.
|
1062
|
-
unless (
|
1063
|
-
|
1095
|
+
args.each do |arg|
|
1096
|
+
next unless arg.is_a?(Hash)
|
1097
|
+
arg.each do |_key, value|
|
1098
|
+
raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
|
1099
|
+
"directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
|
1064
1100
|
end
|
1065
1101
|
end
|
1066
1102
|
end
|
@@ -1082,7 +1118,7 @@ module ActiveRecord
|
|
1082
1118
|
when Hash
|
1083
1119
|
arg.map { |field, dir|
|
1084
1120
|
field = klass.attribute_alias(field) if klass.attribute_alias?(field)
|
1085
|
-
table[field].send(dir)
|
1121
|
+
table[field].send(dir.downcase)
|
1086
1122
|
}
|
1087
1123
|
else
|
1088
1124
|
arg
|
@@ -12,7 +12,6 @@ module ActiveRecord
|
|
12
12
|
|
13
13
|
# Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an <tt>ActiveRecord::Relation</tt>.
|
14
14
|
# Returns an array representing the intersection of the resulting records with <tt>other</tt>, if <tt>other</tt> is an array.
|
15
|
-
#
|
16
15
|
# Post.where(published: true).joins(:comments).merge( Comment.where(spam: false) )
|
17
16
|
# # Performs a single join query with both where conditions.
|
18
17
|
#
|
@@ -38,14 +37,11 @@ module ActiveRecord
|
|
38
37
|
end
|
39
38
|
|
40
39
|
def merge!(other) # :nodoc:
|
41
|
-
if other.is_a?(
|
42
|
-
Relation::HashMerger.new(self, other).merge
|
43
|
-
elsif other.is_a?(Relation)
|
44
|
-
Relation::Merger.new(self, other).merge
|
45
|
-
elsif other.respond_to?(:to_proc)
|
40
|
+
if !other.is_a?(Relation) && other.respond_to?(:to_proc)
|
46
41
|
instance_exec(&other)
|
47
42
|
else
|
48
|
-
|
43
|
+
klass = other.is_a?(Hash) ? Relation::HashMerger : Relation::Merger
|
44
|
+
klass.new(self, other).merge
|
49
45
|
end
|
50
46
|
end
|
51
47
|
|
@@ -62,6 +58,9 @@ module ActiveRecord
|
|
62
58
|
# Post.order('id asc').only(:where) # discards the order condition
|
63
59
|
# Post.order('id asc').only(:where, :order) # uses the specified order
|
64
60
|
def only(*onlies)
|
61
|
+
if onlies.any? { |o| o == :where }
|
62
|
+
onlies << :bind
|
63
|
+
end
|
65
64
|
relation_with values.slice(*onlies)
|
66
65
|
end
|
67
66
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
+
require 'arel/collectors/bind'
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
5
|
# = Active Record Relation
|
@@ -11,6 +12,7 @@ module ActiveRecord
|
|
11
12
|
|
12
13
|
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering,
|
13
14
|
:reverse_order, :distinct, :create_with, :uniq]
|
15
|
+
INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
|
14
16
|
|
15
17
|
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
|
16
18
|
|
@@ -230,6 +232,7 @@ module ActiveRecord
|
|
230
232
|
# Please see further details in the
|
231
233
|
# {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
|
232
234
|
def explain
|
235
|
+
#TODO: Fix for binds.
|
233
236
|
exec_explain(collecting_queries_for_explain { exec_queries })
|
234
237
|
end
|
235
238
|
|
@@ -239,6 +242,11 @@ module ActiveRecord
|
|
239
242
|
@records
|
240
243
|
end
|
241
244
|
|
245
|
+
# Serializes the relation objects Array.
|
246
|
+
def encode_with(coder)
|
247
|
+
coder.represent_seq(nil, to_a)
|
248
|
+
end
|
249
|
+
|
242
250
|
def as_json(options = nil) #:nodoc:
|
243
251
|
to_a.as_json(options)
|
244
252
|
end
|
@@ -330,7 +338,8 @@ module ActiveRecord
|
|
330
338
|
stmt.wheres = arel.constraints
|
331
339
|
end
|
332
340
|
|
333
|
-
|
341
|
+
bvs = bind_values + arel.bind_values
|
342
|
+
@klass.connection.update stmt, 'SQL', bvs
|
334
343
|
end
|
335
344
|
|
336
345
|
# Updates an object (or multiple objects) and saves it to the database, if validations pass.
|
@@ -434,12 +443,21 @@ module ActiveRecord
|
|
434
443
|
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
|
435
444
|
# +after_destroy+ callbacks, use the +destroy_all+ method instead.
|
436
445
|
#
|
437
|
-
# If
|
446
|
+
# If an invalid method is supplied, +delete_all+ raises an ActiveRecord error:
|
438
447
|
#
|
439
448
|
# Post.limit(100).delete_all
|
440
|
-
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
|
449
|
+
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
|
441
450
|
def delete_all(conditions = nil)
|
442
|
-
|
451
|
+
invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
|
452
|
+
if MULTI_VALUE_METHODS.include?(method)
|
453
|
+
send("#{method}_values").any?
|
454
|
+
else
|
455
|
+
send("#{method}_value")
|
456
|
+
end
|
457
|
+
}
|
458
|
+
if invalid_methods.any?
|
459
|
+
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
|
460
|
+
end
|
443
461
|
|
444
462
|
if conditions
|
445
463
|
where(conditions).delete_all
|
@@ -523,11 +541,11 @@ module ActiveRecord
|
|
523
541
|
find_with_associations { |rel| relation = rel }
|
524
542
|
end
|
525
543
|
|
526
|
-
|
527
|
-
binds = relation.bind_values.dup
|
528
|
-
|
529
|
-
|
530
|
-
|
544
|
+
arel = relation.arel
|
545
|
+
binds = (arel.bind_values + relation.bind_values).dup
|
546
|
+
binds.map! { |bv| connection.quote(*bv.reverse) }
|
547
|
+
collect = visitor.accept(arel.ast, Arel::Collectors::Bind.new)
|
548
|
+
collect.substitute_binds(binds).join
|
531
549
|
end
|
532
550
|
end
|
533
551
|
|
@@ -544,7 +562,13 @@ module ActiveRecord
|
|
544
562
|
|
545
563
|
Hash[equalities.map { |where|
|
546
564
|
name = where.left.name
|
547
|
-
[name, binds.fetch(name.to_s) {
|
565
|
+
[name, binds.fetch(name.to_s) {
|
566
|
+
case where.right
|
567
|
+
when Array then where.right.map(&:val)
|
568
|
+
else
|
569
|
+
where.right.val
|
570
|
+
end
|
571
|
+
}]
|
548
572
|
}]
|
549
573
|
end
|
550
574
|
|
@@ -608,7 +632,7 @@ module ActiveRecord
|
|
608
632
|
private
|
609
633
|
|
610
634
|
def exec_queries
|
611
|
-
@records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bind_values)
|
635
|
+
@records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, arel.bind_values + bind_values)
|
612
636
|
|
613
637
|
preload = preload_values
|
614
638
|
preload += includes_values unless eager_loading?
|
data/lib/active_record/result.rb
CHANGED
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
class Result
|
32
32
|
include Enumerable
|
33
33
|
|
34
|
-
IDENTITY_TYPE =
|
34
|
+
IDENTITY_TYPE = Type::Value.new # :nodoc:
|
35
35
|
|
36
36
|
attr_reader :columns, :rows, :column_types
|
37
37
|
|
@@ -42,14 +42,6 @@ module ActiveRecord
|
|
42
42
|
@column_types = column_types
|
43
43
|
end
|
44
44
|
|
45
|
-
def identity_type # :nodoc:
|
46
|
-
IDENTITY_TYPE
|
47
|
-
end
|
48
|
-
|
49
|
-
def column_type(name)
|
50
|
-
@column_types[name] || identity_type
|
51
|
-
end
|
52
|
-
|
53
45
|
def each
|
54
46
|
if block_given?
|
55
47
|
hash_rows.each { |row| yield row }
|
@@ -82,6 +74,15 @@ module ActiveRecord
|
|
82
74
|
hash_rows.last
|
83
75
|
end
|
84
76
|
|
77
|
+
def cast_values(type_overrides = {}) # :nodoc:
|
78
|
+
types = columns.map { |name| column_type(name, type_overrides) }
|
79
|
+
result = rows.map do |values|
|
80
|
+
types.zip(values).map { |type, value| type.type_cast_from_database(value) }
|
81
|
+
end
|
82
|
+
|
83
|
+
columns.one? ? result.map!(&:first) : result
|
84
|
+
end
|
85
|
+
|
85
86
|
def initialize_copy(other)
|
86
87
|
@columns = columns.dup
|
87
88
|
@rows = rows.dup
|
@@ -91,6 +92,12 @@ module ActiveRecord
|
|
91
92
|
|
92
93
|
private
|
93
94
|
|
95
|
+
def column_type(name, type_overrides = {})
|
96
|
+
type_overrides.fetch(name) do
|
97
|
+
column_types.fetch(name, IDENTITY_TYPE)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
94
101
|
def hash_rows
|
95
102
|
@hash_rows ||=
|
96
103
|
begin
|
@@ -92,7 +92,7 @@ module ActiveRecord
|
|
92
92
|
|
93
93
|
table = Arel::Table.new(table_name, arel_engine).alias(default_table_name)
|
94
94
|
PredicateBuilder.build_from_hash(self, attrs, table).map { |b|
|
95
|
-
connection.visitor.
|
95
|
+
connection.visitor.compile b
|
96
96
|
}.join(' AND ')
|
97
97
|
end
|
98
98
|
alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
|
@@ -107,6 +107,13 @@ module ActiveRecord
|
|
107
107
|
end.join(', ')
|
108
108
|
end
|
109
109
|
|
110
|
+
# Sanitizes a +string+ so that it is safe to use within an SQL
|
111
|
+
# LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%"
|
112
|
+
def sanitize_sql_like(string, escape_character = "\\")
|
113
|
+
pattern = Regexp.union(escape_character, "%", "_")
|
114
|
+
string.gsub(pattern) { |x| [escape_character, x].join }
|
115
|
+
end
|
116
|
+
|
110
117
|
# Accepts an array of conditions. The array has each value
|
111
118
|
# sanitized and interpolated into the SQL statement.
|
112
119
|
# ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
|
data/lib/active_record/schema.rb
CHANGED
@@ -91,16 +91,17 @@ HEADER
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def tables(stream)
|
94
|
-
@connection.tables.sort
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
94
|
+
sorted_tables = @connection.tables.sort
|
95
|
+
|
96
|
+
sorted_tables.each do |table_name|
|
97
|
+
table(table_name, stream) unless ignored?(table_name)
|
98
|
+
end
|
99
|
+
|
100
|
+
# dump foreign keys at the end to make sure all dependent tables exist.
|
101
|
+
if @connection.supports_foreign_keys?
|
102
|
+
sorted_tables.each do |tbl|
|
103
|
+
foreign_keys(tbl, stream)
|
102
104
|
end
|
103
|
-
table(tbl, stream)
|
104
105
|
end
|
105
106
|
end
|
106
107
|
|
@@ -213,8 +214,49 @@ HEADER
|
|
213
214
|
end
|
214
215
|
end
|
215
216
|
|
217
|
+
def foreign_keys(table, stream)
|
218
|
+
if (foreign_keys = @connection.foreign_keys(table)).any?
|
219
|
+
add_foreign_key_statements = foreign_keys.map do |foreign_key|
|
220
|
+
parts = [
|
221
|
+
'add_foreign_key ' + remove_prefix_and_suffix(foreign_key.from_table).inspect,
|
222
|
+
remove_prefix_and_suffix(foreign_key.to_table).inspect,
|
223
|
+
]
|
224
|
+
|
225
|
+
if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table)
|
226
|
+
parts << ('column: ' + foreign_key.column.inspect)
|
227
|
+
end
|
228
|
+
|
229
|
+
if foreign_key.custom_primary_key?
|
230
|
+
parts << ('primary_key: ' + foreign_key.primary_key.inspect)
|
231
|
+
end
|
232
|
+
|
233
|
+
if foreign_key.name !~ /^fk_rails_[0-9a-f]{10}$/
|
234
|
+
parts << ('name: ' + foreign_key.name.inspect)
|
235
|
+
end
|
236
|
+
|
237
|
+
parts << ('on_update: ' + foreign_key.on_update.inspect) if foreign_key.on_update
|
238
|
+
parts << ('on_delete: ' + foreign_key.on_delete.inspect) if foreign_key.on_delete
|
239
|
+
|
240
|
+
' ' + parts.join(', ')
|
241
|
+
end
|
242
|
+
|
243
|
+
stream.puts add_foreign_key_statements.sort.join("\n")
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
216
247
|
def remove_prefix_and_suffix(table)
|
217
248
|
table.gsub(/^(#{@options[:table_name_prefix]})(.+)(#{@options[:table_name_suffix]})$/, "\\2")
|
218
249
|
end
|
250
|
+
|
251
|
+
def ignored?(table_name)
|
252
|
+
['schema_migrations', ignore_tables].flatten.any? do |ignored|
|
253
|
+
case ignored
|
254
|
+
when String; remove_prefix_and_suffix(table_name) == ignored
|
255
|
+
when Regexp; remove_prefix_and_suffix(table_name) =~ ignored
|
256
|
+
else
|
257
|
+
raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
219
261
|
end
|
220
262
|
end
|
@@ -11,7 +11,7 @@ module ActiveRecord
|
|
11
11
|
end
|
12
12
|
|
13
13
|
module ClassMethods
|
14
|
-
# Returns a scope for the model without the
|
14
|
+
# Returns a scope for the model without the previously set scopes.
|
15
15
|
#
|
16
16
|
# class Post < ActiveRecord::Base
|
17
17
|
# def self.default_scope
|
@@ -19,11 +19,12 @@ module ActiveRecord
|
|
19
19
|
# end
|
20
20
|
# end
|
21
21
|
#
|
22
|
-
# Post.all
|
23
|
-
# Post.unscoped.all
|
22
|
+
# Post.all # Fires "SELECT * FROM posts WHERE published = true"
|
23
|
+
# Post.unscoped.all # Fires "SELECT * FROM posts"
|
24
|
+
# Post.where(published: false).unscoped.all # Fires "SELECT * FROM posts"
|
24
25
|
#
|
25
26
|
# This method also accepts a block. All queries inside the block will
|
26
|
-
# not use the
|
27
|
+
# not use the previously set scopes.
|
27
28
|
#
|
28
29
|
# Post.unscoped {
|
29
30
|
# Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
|