activerecord 4.1.15 → 4.2.11.3
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 +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
@@ -1,16 +1,16 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
+
require 'arel/collectors/bind'
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
5
|
# = Active Record Relation
|
5
6
|
class Relation
|
6
|
-
JoinOperation = Struct.new(:relation, :join_class, :on)
|
7
|
-
|
8
7
|
MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
|
9
8
|
:order, :joins, :where, :having, :bind, :references,
|
10
9
|
:extending, :unscope]
|
11
10
|
|
12
11
|
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering,
|
13
12
|
:reverse_order, :distinct, :create_with, :uniq]
|
13
|
+
INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
|
14
14
|
|
15
15
|
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
|
16
16
|
|
@@ -79,22 +79,26 @@ module ActiveRecord
|
|
79
79
|
scope.unscope!(where: @klass.inheritance_column)
|
80
80
|
end
|
81
81
|
|
82
|
-
|
82
|
+
relation = scope.where(@klass.primary_key => (id_was || id))
|
83
|
+
bvs = binds + relation.bind_values
|
84
|
+
um = relation
|
85
|
+
.arel
|
86
|
+
.compile_update(substitutes, @klass.primary_key)
|
83
87
|
|
84
88
|
@klass.connection.update(
|
85
89
|
um,
|
86
90
|
'SQL',
|
87
|
-
|
91
|
+
bvs,
|
92
|
+
)
|
88
93
|
end
|
89
94
|
|
90
95
|
def substitute_values(values) # :nodoc:
|
91
|
-
|
92
|
-
binds = substitutes.map do |arel_attr, value|
|
96
|
+
binds = values.map do |arel_attr, value|
|
93
97
|
[@klass.columns_hash[arel_attr.name], value]
|
94
98
|
end
|
95
99
|
|
96
|
-
substitutes.each_with_index do |
|
97
|
-
|
100
|
+
substitutes = values.each_with_index.map do |(arel_attr, _), i|
|
101
|
+
[arel_attr, @klass.connection.substitute_at(binds[i][0])]
|
98
102
|
end
|
99
103
|
|
100
104
|
[substitutes, binds]
|
@@ -230,6 +234,7 @@ module ActiveRecord
|
|
230
234
|
# Please see further details in the
|
231
235
|
# {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
|
232
236
|
def explain
|
237
|
+
#TODO: Fix for binds.
|
233
238
|
exec_explain(collecting_queries_for_explain { exec_queries })
|
234
239
|
end
|
235
240
|
|
@@ -239,6 +244,11 @@ module ActiveRecord
|
|
239
244
|
@records
|
240
245
|
end
|
241
246
|
|
247
|
+
# Serializes the relation objects Array.
|
248
|
+
def encode_with(coder)
|
249
|
+
coder.represent_seq(nil, to_a)
|
250
|
+
end
|
251
|
+
|
242
252
|
def as_json(options = nil) #:nodoc:
|
243
253
|
to_a.as_json(options)
|
244
254
|
end
|
@@ -294,10 +304,11 @@ module ActiveRecord
|
|
294
304
|
klass.current_scope = previous
|
295
305
|
end
|
296
306
|
|
297
|
-
# Updates all records with details given
|
298
|
-
#
|
299
|
-
#
|
300
|
-
#
|
307
|
+
# Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
|
308
|
+
# statement and sends it straight to the database. It does not instantiate the involved models and it does not
|
309
|
+
# trigger Active Record callbacks or validations. Values passed to `update_all` will not go through
|
310
|
+
# ActiveRecord's type-casting behavior. It should receive only values that can be passed as-is to the SQL
|
311
|
+
# database.
|
301
312
|
#
|
302
313
|
# ==== Parameters
|
303
314
|
#
|
@@ -330,7 +341,8 @@ module ActiveRecord
|
|
330
341
|
stmt.wheres = arel.constraints
|
331
342
|
end
|
332
343
|
|
333
|
-
|
344
|
+
bvs = arel.bind_values + bind_values
|
345
|
+
@klass.connection.update stmt, 'SQL', bvs
|
334
346
|
end
|
335
347
|
|
336
348
|
# Updates an object (or multiple objects) and saves it to the database, if validations pass.
|
@@ -434,12 +446,21 @@ module ActiveRecord
|
|
434
446
|
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
|
435
447
|
# +after_destroy+ callbacks, use the +destroy_all+ method instead.
|
436
448
|
#
|
437
|
-
# If
|
449
|
+
# If an invalid method is supplied, +delete_all+ raises an ActiveRecord error:
|
438
450
|
#
|
439
451
|
# Post.limit(100).delete_all
|
440
|
-
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
|
452
|
+
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
|
441
453
|
def delete_all(conditions = nil)
|
442
|
-
|
454
|
+
invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
|
455
|
+
if MULTI_VALUE_METHODS.include?(method)
|
456
|
+
send("#{method}_values").any?
|
457
|
+
else
|
458
|
+
send("#{method}_value")
|
459
|
+
end
|
460
|
+
}
|
461
|
+
if invalid_methods.any?
|
462
|
+
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
|
463
|
+
end
|
443
464
|
|
444
465
|
if conditions
|
445
466
|
where(conditions).delete_all
|
@@ -453,7 +474,8 @@ module ActiveRecord
|
|
453
474
|
stmt.wheres = arel.constraints
|
454
475
|
end
|
455
476
|
|
456
|
-
|
477
|
+
bvs = arel.bind_values + bind_values
|
478
|
+
affected = @klass.connection.delete(stmt, 'SQL', bvs)
|
457
479
|
|
458
480
|
reset
|
459
481
|
affected
|
@@ -523,11 +545,11 @@ module ActiveRecord
|
|
523
545
|
find_with_associations { |rel| relation = rel }
|
524
546
|
end
|
525
547
|
|
526
|
-
|
527
|
-
binds = relation.bind_values.dup
|
528
|
-
|
529
|
-
|
530
|
-
|
548
|
+
arel = relation.arel
|
549
|
+
binds = (arel.bind_values + relation.bind_values).dup
|
550
|
+
binds.map! { |bv| connection.quote(*bv.reverse) }
|
551
|
+
collect = visitor.accept(arel.ast, Arel::Collectors::Bind.new)
|
552
|
+
collect.substitute_binds(binds).join
|
531
553
|
end
|
532
554
|
end
|
533
555
|
|
@@ -544,7 +566,13 @@ module ActiveRecord
|
|
544
566
|
|
545
567
|
Hash[equalities.map { |where|
|
546
568
|
name = where.left.name
|
547
|
-
[name, binds.fetch(name.to_s) {
|
569
|
+
[name, binds.fetch(name.to_s) {
|
570
|
+
case where.right
|
571
|
+
when Array then where.right.map(&:val)
|
572
|
+
when Arel::Nodes::Casted
|
573
|
+
where.right.val
|
574
|
+
end
|
575
|
+
}]
|
548
576
|
}]
|
549
577
|
end
|
550
578
|
|
@@ -608,11 +636,11 @@ module ActiveRecord
|
|
608
636
|
private
|
609
637
|
|
610
638
|
def exec_queries
|
611
|
-
@records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bind_values)
|
639
|
+
@records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, arel.bind_values + bind_values)
|
612
640
|
|
613
641
|
preload = preload_values
|
614
642
|
preload += includes_values unless eager_loading?
|
615
|
-
preloader =
|
643
|
+
preloader = build_preloader
|
616
644
|
preload.each do |associations|
|
617
645
|
preloader.preload @records, associations
|
618
646
|
end
|
@@ -623,6 +651,10 @@ module ActiveRecord
|
|
623
651
|
@records
|
624
652
|
end
|
625
653
|
|
654
|
+
def build_preloader
|
655
|
+
ActiveRecord::Associations::Preloader.new
|
656
|
+
end
|
657
|
+
|
626
658
|
def references_eager_loaded_tables?
|
627
659
|
joined_tables = arel.join_sources.map do |join|
|
628
660
|
if join.is_a?(Arel::Nodes::StringJoin)
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
module ActiveRecord
|
3
2
|
module Batches
|
4
3
|
# Looping through a collection of records from the database
|
@@ -115,7 +114,6 @@ module ActiveRecord
|
|
115
114
|
end
|
116
115
|
|
117
116
|
relation = relation.reorder(batch_order).limit(batch_size)
|
118
|
-
relation.reverse_order_value = false
|
119
117
|
records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a
|
120
118
|
|
121
119
|
while records.any?
|
@@ -20,17 +20,31 @@ module ActiveRecord
|
|
20
20
|
# Person.group(:city).count
|
21
21
|
# # => { 'Rome' => 5, 'Paris' => 3 }
|
22
22
|
#
|
23
|
+
# If +count+ is used with +group+ for multiple columns, it returns a Hash whose
|
24
|
+
# keys are an array containing the individual values of each column and the value
|
25
|
+
# of each key would be the +count+.
|
26
|
+
#
|
27
|
+
# Article.group(:status, :category).count
|
28
|
+
# # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
|
29
|
+
# ["published", "business"]=>0, ["published", "technology"]=>2}
|
30
|
+
#
|
23
31
|
# If +count+ is used with +select+, it will count the selected columns:
|
24
32
|
#
|
25
33
|
# Person.select(:age).count
|
26
34
|
# # => counts the number of different age values
|
27
35
|
#
|
28
36
|
# Note: not all valid +select+ expressions are valid +count+ expressions. The specifics differ
|
29
|
-
# between databases. In invalid cases, an error from the
|
37
|
+
# between databases. In invalid cases, an error from the database is thrown.
|
30
38
|
def count(column_name = nil, options = {})
|
39
|
+
if options.present? && !ActiveRecord.const_defined?(:DeprecatedFinders)
|
40
|
+
raise ArgumentError, "Relation#count does not support finder options anymore. " \
|
41
|
+
"Please build a scope and then call count on it or use the " \
|
42
|
+
"activerecord-deprecated_finders gem to enable this functionality."
|
43
|
+
|
44
|
+
end
|
45
|
+
|
31
46
|
# TODO: Remove options argument as soon we remove support to
|
32
47
|
# activerecord-deprecated_finders.
|
33
|
-
column_name, options = nil, column_name if column_name.is_a?(Hash)
|
34
48
|
calculate(:count, column_name, options)
|
35
49
|
end
|
36
50
|
|
@@ -80,7 +94,7 @@ module ActiveRecord
|
|
80
94
|
#
|
81
95
|
# There are two basic forms of output:
|
82
96
|
#
|
83
|
-
# * Single aggregate value: The single value is type cast to
|
97
|
+
# * Single aggregate value: The single value is type cast to Integer for COUNT, Float
|
84
98
|
# for AVG, and the given column's type for everything else.
|
85
99
|
#
|
86
100
|
# * Grouped values: This returns an ordered hash of the values and groups them. It
|
@@ -169,22 +183,8 @@ module ActiveRecord
|
|
169
183
|
relation.select_values = column_names.map { |cn|
|
170
184
|
columns_hash.key?(cn) ? arel_table[cn] : cn
|
171
185
|
}
|
172
|
-
result = klass.connection.select_all(relation.arel, nil, bind_values)
|
173
|
-
|
174
|
-
klass.column_types.fetch(key) {
|
175
|
-
result.column_types.fetch(key) { result.identity_type }
|
176
|
-
}
|
177
|
-
end
|
178
|
-
|
179
|
-
result = result.rows.map do |values|
|
180
|
-
values = result.columns.zip(values).map do |column_name, value|
|
181
|
-
single_attr_hash = { column_name => value }
|
182
|
-
klass.initialize_attributes(single_attr_hash).values.first
|
183
|
-
end
|
184
|
-
|
185
|
-
columns.zip(values).map { |column, value| column.type_cast value }
|
186
|
-
end
|
187
|
-
columns.one? ? result.map!(&:first) : result
|
186
|
+
result = klass.connection.select_all(relation.arel, nil, relation.arel.bind_values + bind_values)
|
187
|
+
result.cast_values(klass.column_types)
|
188
188
|
end
|
189
189
|
end
|
190
190
|
|
@@ -246,27 +246,32 @@ module ActiveRecord
|
|
246
246
|
|
247
247
|
column_alias = column_name
|
248
248
|
|
249
|
+
bind_values = nil
|
250
|
+
|
249
251
|
if operation == "count" && (relation.limit_value || relation.offset_value)
|
250
252
|
# Shortcut when limit is zero.
|
251
253
|
return 0 if relation.limit_value == 0
|
252
254
|
|
253
255
|
query_builder = build_count_subquery(relation, column_name, distinct)
|
256
|
+
bind_values = query_builder.bind_values + relation.bind_values
|
254
257
|
else
|
255
258
|
column = aggregate_column(column_name)
|
256
259
|
|
257
260
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
258
261
|
|
259
262
|
column_alias = select_value.alias
|
263
|
+
column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
|
260
264
|
relation.select_values = [select_value]
|
261
265
|
|
262
266
|
query_builder = relation.arel
|
267
|
+
bind_values = query_builder.bind_values + relation.bind_values
|
263
268
|
end
|
264
269
|
|
265
|
-
result = @klass.connection.select_all(query_builder, nil,
|
270
|
+
result = @klass.connection.select_all(query_builder, nil, bind_values)
|
266
271
|
row = result.first
|
267
272
|
value = row && row.values.first
|
268
273
|
column = result.column_types.fetch(column_alias) do
|
269
|
-
|
274
|
+
type_for(column_name)
|
270
275
|
end
|
271
276
|
|
272
277
|
type_cast_calculated_value(value, column, operation)
|
@@ -276,12 +281,13 @@ module ActiveRecord
|
|
276
281
|
group_attrs = group_values
|
277
282
|
|
278
283
|
if group_attrs.first.respond_to?(:to_sym)
|
279
|
-
association = @klass._reflect_on_association(group_attrs.first
|
280
|
-
associated = group_attrs.size == 1 && association && association.
|
284
|
+
association = @klass._reflect_on_association(group_attrs.first)
|
285
|
+
associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
|
281
286
|
group_fields = Array(associated ? association.foreign_key : group_attrs)
|
282
287
|
else
|
283
288
|
group_fields = group_attrs
|
284
289
|
end
|
290
|
+
group_fields = arel_columns(group_fields)
|
285
291
|
|
286
292
|
group_aliases = group_fields.map { |field|
|
287
293
|
column_alias_for(field)
|
@@ -304,7 +310,7 @@ module ActiveRecord
|
|
304
310
|
operation,
|
305
311
|
distinct).as(aggregate_alias)
|
306
312
|
]
|
307
|
-
select_values += select_values unless having_values.empty?
|
313
|
+
select_values += self.select_values unless having_values.empty?
|
308
314
|
|
309
315
|
select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
|
310
316
|
if field.respond_to?(:as)
|
@@ -318,7 +324,7 @@ module ActiveRecord
|
|
318
324
|
relation.group_values = group
|
319
325
|
relation.select_values = select_values
|
320
326
|
|
321
|
-
calculated_data = @klass.connection.select_all(relation, nil, bind_values)
|
327
|
+
calculated_data = @klass.connection.select_all(relation, nil, relation.arel.bind_values + bind_values)
|
322
328
|
|
323
329
|
if association
|
324
330
|
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
@@ -329,14 +335,14 @@ module ActiveRecord
|
|
329
335
|
Hash[calculated_data.map do |row|
|
330
336
|
key = group_columns.map { |aliaz, col_name|
|
331
337
|
column = calculated_data.column_types.fetch(aliaz) do
|
332
|
-
|
338
|
+
type_for(col_name)
|
333
339
|
end
|
334
340
|
type_cast_calculated_value(row[aliaz], column)
|
335
341
|
}
|
336
342
|
key = key.first if key.size == 1
|
337
343
|
key = key_records[key] if associated
|
338
344
|
|
339
|
-
column_type = calculated_data.column_types.fetch(aggregate_alias) {
|
345
|
+
column_type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
|
340
346
|
[key, type_cast_calculated_value(row[aggregate_alias], column_type, operation)]
|
341
347
|
end]
|
342
348
|
end
|
@@ -363,24 +369,20 @@ module ActiveRecord
|
|
363
369
|
@klass.connection.table_alias_for(table_name)
|
364
370
|
end
|
365
371
|
|
366
|
-
def
|
372
|
+
def type_for(field)
|
367
373
|
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
|
368
|
-
@klass.
|
374
|
+
@klass.type_for_attribute(field_name)
|
369
375
|
end
|
370
376
|
|
371
|
-
def type_cast_calculated_value(value,
|
377
|
+
def type_cast_calculated_value(value, type, operation = nil)
|
372
378
|
case operation
|
373
379
|
when 'count' then value.to_i
|
374
|
-
when 'sum' then
|
380
|
+
when 'sum' then type.type_cast_from_database(value || 0)
|
375
381
|
when 'average' then value.respond_to?(:to_d) ? value.to_d : value
|
376
|
-
else
|
382
|
+
else type.type_cast_from_database(value)
|
377
383
|
end
|
378
384
|
end
|
379
385
|
|
380
|
-
def type_cast_using_column(value, column)
|
381
|
-
column ? column.type_cast(value) : value
|
382
|
-
end
|
383
|
-
|
384
386
|
# TODO: refactor to allow non-string `select_values` (eg. Arel nodes).
|
385
387
|
def select_for_count
|
386
388
|
if select_values.present?
|
@@ -396,9 +398,11 @@ module ActiveRecord
|
|
396
398
|
|
397
399
|
aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
|
398
400
|
relation.select_values = [aliased_column]
|
399
|
-
|
401
|
+
arel = relation.arel
|
402
|
+
subquery = arel.as(subquery_alias)
|
400
403
|
|
401
404
|
sm = Arel::SelectManager.new relation.engine
|
405
|
+
sm.bind_values = arel.bind_values
|
402
406
|
select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
|
403
407
|
sm.project(select_value).from(subquery)
|
404
408
|
end
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'active_support/deprecation'
|
2
|
+
require 'active_support/core_ext/string/filters'
|
3
|
+
|
1
4
|
module ActiveRecord
|
2
5
|
module FinderMethods
|
3
6
|
ONE_AS_ONE = '1 AS one'
|
@@ -79,12 +82,16 @@ module ActiveRecord
|
|
79
82
|
# Post.find_by "published_at < ?", 2.weeks.ago
|
80
83
|
def find_by(*args)
|
81
84
|
where(*args).take
|
85
|
+
rescue RangeError
|
86
|
+
nil
|
82
87
|
end
|
83
88
|
|
84
89
|
# Like <tt>find_by</tt>, except that if no record is found, raises
|
85
90
|
# an <tt>ActiveRecord::RecordNotFound</tt> error.
|
86
91
|
def find_by!(*args)
|
87
92
|
where(*args).take!
|
93
|
+
rescue RangeError
|
94
|
+
raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range value"
|
88
95
|
end
|
89
96
|
|
90
97
|
# Gives a record (or N records if a parameter is supplied) without any implied
|
@@ -101,42 +108,30 @@ module ActiveRecord
|
|
101
108
|
# Same as +take+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
102
109
|
# is found. Note that <tt>take!</tt> accepts no arguments.
|
103
110
|
def take!
|
104
|
-
take or raise RecordNotFound
|
111
|
+
take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
|
105
112
|
end
|
106
113
|
|
107
114
|
# Find the first record (or first N records if a parameter is supplied).
|
108
115
|
# If no order is defined it will order by primary key.
|
109
116
|
#
|
110
|
-
# Person.first # returns the first object fetched by SELECT * FROM people
|
117
|
+
# Person.first # returns the first object fetched by SELECT * FROM people ORDER BY people.id LIMIT 1
|
111
118
|
# Person.where(["user_name = ?", user_name]).first
|
112
119
|
# Person.where(["user_name = :u", { u: user_name }]).first
|
113
120
|
# Person.order("created_on DESC").offset(5).first
|
114
|
-
# Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3
|
115
|
-
#
|
116
|
-
# ==== Rails 3
|
117
|
-
#
|
118
|
-
# Person.first # SELECT "people".* FROM "people" LIMIT 1
|
119
|
-
#
|
120
|
-
# NOTE: Rails 3 may not order this query by the primary key and the order
|
121
|
-
# will depend on the database implementation. In order to ensure that behavior,
|
122
|
-
# use <tt>User.order(:id).first</tt> instead.
|
123
|
-
#
|
124
|
-
# ==== Rails 4
|
125
|
-
#
|
126
|
-
# Person.first # SELECT "people".* FROM "people" ORDER BY "people"."id" ASC LIMIT 1
|
121
|
+
# Person.first(3) # returns the first three objects fetched by SELECT * FROM people ORDER BY people.id LIMIT 3
|
127
122
|
#
|
128
123
|
def first(limit = nil)
|
129
124
|
if limit
|
130
|
-
find_nth_with_limit(
|
125
|
+
find_nth_with_limit(offset_index, limit)
|
131
126
|
else
|
132
|
-
find_nth(
|
127
|
+
find_nth(0, offset_index)
|
133
128
|
end
|
134
129
|
end
|
135
130
|
|
136
131
|
# Same as +first+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
137
132
|
# is found. Note that <tt>first!</tt> accepts no arguments.
|
138
133
|
def first!
|
139
|
-
|
134
|
+
find_nth! 0
|
140
135
|
end
|
141
136
|
|
142
137
|
# Find the last record (or last N records if a parameter is supplied).
|
@@ -169,7 +164,7 @@ module ActiveRecord
|
|
169
164
|
# Same as +last+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
170
165
|
# is found. Note that <tt>last!</tt> accepts no arguments.
|
171
166
|
def last!
|
172
|
-
last or raise RecordNotFound
|
167
|
+
last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
|
173
168
|
end
|
174
169
|
|
175
170
|
# Find the second record.
|
@@ -179,13 +174,13 @@ module ActiveRecord
|
|
179
174
|
# Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4)
|
180
175
|
# Person.where(["user_name = :u", { u: user_name }]).second
|
181
176
|
def second
|
182
|
-
find_nth(
|
177
|
+
find_nth(1, offset_index)
|
183
178
|
end
|
184
179
|
|
185
180
|
# Same as +second+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
186
181
|
# is found.
|
187
182
|
def second!
|
188
|
-
|
183
|
+
find_nth! 1
|
189
184
|
end
|
190
185
|
|
191
186
|
# Find the third record.
|
@@ -195,13 +190,13 @@ module ActiveRecord
|
|
195
190
|
# Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5)
|
196
191
|
# Person.where(["user_name = :u", { u: user_name }]).third
|
197
192
|
def third
|
198
|
-
find_nth(
|
193
|
+
find_nth(2, offset_index)
|
199
194
|
end
|
200
195
|
|
201
196
|
# Same as +third+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
202
197
|
# is found.
|
203
198
|
def third!
|
204
|
-
|
199
|
+
find_nth! 2
|
205
200
|
end
|
206
201
|
|
207
202
|
# Find the fourth record.
|
@@ -211,13 +206,13 @@ module ActiveRecord
|
|
211
206
|
# Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6)
|
212
207
|
# Person.where(["user_name = :u", { u: user_name }]).fourth
|
213
208
|
def fourth
|
214
|
-
find_nth(
|
209
|
+
find_nth(3, offset_index)
|
215
210
|
end
|
216
211
|
|
217
212
|
# Same as +fourth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
218
213
|
# is found.
|
219
214
|
def fourth!
|
220
|
-
|
215
|
+
find_nth! 3
|
221
216
|
end
|
222
217
|
|
223
218
|
# Find the fifth record.
|
@@ -227,13 +222,13 @@ module ActiveRecord
|
|
227
222
|
# Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7)
|
228
223
|
# Person.where(["user_name = :u", { u: user_name }]).fifth
|
229
224
|
def fifth
|
230
|
-
find_nth(
|
225
|
+
find_nth(4, offset_index)
|
231
226
|
end
|
232
227
|
|
233
228
|
# Same as +fifth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
234
229
|
# is found.
|
235
230
|
def fifth!
|
236
|
-
|
231
|
+
find_nth! 4
|
237
232
|
end
|
238
233
|
|
239
234
|
# Find the forty-second record. Also known as accessing "the reddit".
|
@@ -243,13 +238,13 @@ module ActiveRecord
|
|
243
238
|
# Person.offset(3).forty_two # returns the forty-second object from OFFSET 3 (which is OFFSET 44)
|
244
239
|
# Person.where(["user_name = :u", { u: user_name }]).forty_two
|
245
240
|
def forty_two
|
246
|
-
find_nth(
|
241
|
+
find_nth(41, offset_index)
|
247
242
|
end
|
248
243
|
|
249
244
|
# Same as +forty_two+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
250
245
|
# is found.
|
251
246
|
def forty_two!
|
252
|
-
|
247
|
+
find_nth! 41
|
253
248
|
end
|
254
249
|
|
255
250
|
# Returns +true+ if a record exists in the table that matches the +id+ or
|
@@ -280,7 +275,14 @@ module ActiveRecord
|
|
280
275
|
# Person.exists?(false)
|
281
276
|
# Person.exists?
|
282
277
|
def exists?(conditions = :none)
|
283
|
-
|
278
|
+
if Base === conditions
|
279
|
+
conditions = conditions.id
|
280
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
281
|
+
You are passing an instance of ActiveRecord::Base to `exists?`.
|
282
|
+
Please pass the id of the object by calling `.id`
|
283
|
+
MSG
|
284
|
+
end
|
285
|
+
|
284
286
|
return false if !conditions
|
285
287
|
|
286
288
|
relation = apply_join_dependency(self, construct_join_dependency)
|
@@ -292,10 +294,12 @@ module ActiveRecord
|
|
292
294
|
when Array, Hash
|
293
295
|
relation = relation.where(conditions)
|
294
296
|
else
|
295
|
-
|
297
|
+
unless conditions == :none
|
298
|
+
relation = relation.where(primary_key => conditions)
|
299
|
+
end
|
296
300
|
end
|
297
301
|
|
298
|
-
connection.select_value(relation, "#{name} Exists", relation.bind_values) ? true : false
|
302
|
+
connection.select_value(relation, "#{name} Exists", relation.arel.bind_values + relation.bind_values) ? true : false
|
299
303
|
end
|
300
304
|
|
301
305
|
# This method is called whenever no records are found with either a single
|
@@ -322,6 +326,10 @@ module ActiveRecord
|
|
322
326
|
|
323
327
|
private
|
324
328
|
|
329
|
+
def offset_index
|
330
|
+
offset_value || 0
|
331
|
+
end
|
332
|
+
|
325
333
|
def find_with_associations
|
326
334
|
# NOTE: the JoinDependency constructed here needs to know about
|
327
335
|
# any joins already present in `self`, so pass them in
|
@@ -344,7 +352,8 @@ module ActiveRecord
|
|
344
352
|
if ActiveRecord::NullRelation === relation
|
345
353
|
[]
|
346
354
|
else
|
347
|
-
|
355
|
+
arel = relation.arel
|
356
|
+
rows = connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
|
348
357
|
join_dependency.instantiate(rows, aliases)
|
349
358
|
end
|
350
359
|
end
|
@@ -387,8 +396,9 @@ module ActiveRecord
|
|
387
396
|
"#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
|
388
397
|
|
389
398
|
relation = relation.except(:select).select(values).distinct!
|
399
|
+
arel = relation.arel
|
390
400
|
|
391
|
-
id_rows = @klass.connection.select_all(
|
401
|
+
id_rows = @klass.connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
|
392
402
|
id_rows.map {|row| row[primary_key]}
|
393
403
|
end
|
394
404
|
|
@@ -415,15 +425,20 @@ module ActiveRecord
|
|
415
425
|
else
|
416
426
|
find_some(ids)
|
417
427
|
end
|
428
|
+
rescue RangeError
|
429
|
+
raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
|
418
430
|
end
|
419
431
|
|
420
432
|
def find_one(id)
|
421
|
-
|
433
|
+
if ActiveRecord::Base === id
|
434
|
+
id = id.id
|
435
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
436
|
+
You are passing an instance of ActiveRecord::Base to `find`.
|
437
|
+
Please pass the id of the object by calling `.id`
|
438
|
+
MSG
|
439
|
+
end
|
422
440
|
|
423
|
-
|
424
|
-
substitute = connection.substitute_at(column, bind_values.length)
|
425
|
-
relation = where(table[primary_key].eq(substitute))
|
426
|
-
relation.bind_values += [[column, id]]
|
441
|
+
relation = where(primary_key => id)
|
427
442
|
record = relation.take
|
428
443
|
|
429
444
|
raise_record_not_found_exception!(id, 0, 1) unless record
|
@@ -432,7 +447,7 @@ module ActiveRecord
|
|
432
447
|
end
|
433
448
|
|
434
449
|
def find_some(ids)
|
435
|
-
result = where(
|
450
|
+
result = where(primary_key => ids).to_a
|
436
451
|
|
437
452
|
expected_size =
|
438
453
|
if limit_value && ids.size > limit_value
|
@@ -461,20 +476,28 @@ module ActiveRecord
|
|
461
476
|
end
|
462
477
|
end
|
463
478
|
|
464
|
-
def find_nth(
|
479
|
+
def find_nth(index, offset)
|
465
480
|
if loaded?
|
466
|
-
@records
|
481
|
+
@records[index]
|
467
482
|
else
|
483
|
+
offset += index
|
468
484
|
@offsets[offset] ||= find_nth_with_limit(offset, 1).first
|
469
485
|
end
|
470
486
|
end
|
471
487
|
|
488
|
+
def find_nth!(index)
|
489
|
+
find_nth(index, offset_index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
|
490
|
+
end
|
491
|
+
|
472
492
|
def find_nth_with_limit(offset, limit)
|
473
|
-
if order_values.empty? && primary_key
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
493
|
+
relation = if order_values.empty? && primary_key
|
494
|
+
order(arel_table[primary_key].asc)
|
495
|
+
else
|
496
|
+
self
|
497
|
+
end
|
498
|
+
|
499
|
+
relation = relation.offset(offset) unless offset.zero?
|
500
|
+
relation.limit(limit).to_a
|
478
501
|
end
|
479
502
|
|
480
503
|
def find_last
|