activerecord 3.2.22.5 → 4.0.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 +1024 -543
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -29
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +55 -44
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/associations.rb +204 -276
- data/lib/active_record/associations/alias_tracker.rb +1 -1
- data/lib/active_record/associations/association.rb +30 -35
- data/lib/active_record/associations/association_scope.rb +40 -40
- data/lib/active_record/associations/belongs_to_association.rb +15 -2
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +35 -57
- data/lib/active_record/associations/builder/collection_association.rb +54 -40
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +13 -50
- data/lib/active_record/associations/builder/singular_association.rb +13 -13
- data/lib/active_record/associations/collection_association.rb +92 -88
- data/lib/active_record/associations/collection_proxy.rb +913 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
- data/lib/active_record/associations/has_many_association.rb +35 -9
- data/lib/active_record/associations/has_many_through_association.rb +24 -14
- data/lib/active_record/associations/has_one_association.rb +33 -13
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader.rb +14 -17
- data/lib/active_record/associations/preloader/association.rb +29 -33
- data/lib/active_record/associations/preloader/collection_association.rb +1 -1
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/through_association.rb +13 -17
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +133 -153
- data/lib/active_record/attribute_methods.rb +196 -93
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +31 -28
- data/lib/active_record/attribute_methods/primary_key.rb +38 -30
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +62 -91
- data/lib/active_record/attribute_methods/serialization.rb +97 -66
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
- data/lib/active_record/attribute_methods/write.rb +32 -39
- data/lib/active_record/autosave_association.rb +56 -70
- data/lib/active_record/base.rb +53 -450
- data/lib/active_record/callbacks.rb +53 -18
- data/lib/active_record/coders/yaml_column.rb +11 -9
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
- data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
- data/lib/active_record/connection_adapters/column.rb +46 -24
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +428 -0
- data/lib/active_record/counter_cache.rb +106 -108
- data/lib/active_record/dynamic_matchers.rb +110 -63
- data/lib/active_record/errors.rb +25 -8
- data/lib/active_record/explain.rb +8 -58
- data/lib/active_record/explain_subscriber.rb +6 -3
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +146 -148
- data/lib/active_record/inheritance.rb +77 -59
- data/lib/active_record/integration.rb +5 -5
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +38 -42
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration.rb +318 -153
- data/lib/active_record/migration/command_recorder.rb +90 -31
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/model_schema.rb +69 -92
- data/lib/active_record/nested_attributes.rb +113 -148
- data/lib/active_record/null_relation.rb +65 -0
- data/lib/active_record/persistence.rb +188 -97
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +91 -36
- data/lib/active_record/railties/console_sandbox.rb +0 -2
- data/lib/active_record/railties/controller_runtime.rb +2 -2
- data/lib/active_record/railties/databases.rake +90 -309
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +7 -3
- data/lib/active_record/reflection.rb +72 -56
- data/lib/active_record/relation.rb +241 -157
- data/lib/active_record/relation/batches.rb +25 -22
- data/lib/active_record/relation/calculations.rb +143 -121
- data/lib/active_record/relation/delegation.rb +96 -18
- data/lib/active_record/relation/finder_methods.rb +117 -183
- data/lib/active_record/relation/merger.rb +133 -0
- data/lib/active_record/relation/predicate_builder.rb +90 -42
- data/lib/active_record/relation/query_methods.rb +666 -136
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/result.rb +33 -6
- data/lib/active_record/sanitization.rb +24 -50
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +31 -39
- data/lib/active_record/schema_migration.rb +36 -0
- data/lib/active_record/scoping.rb +0 -124
- data/lib/active_record/scoping/default.rb +48 -45
- data/lib/active_record/scoping/named.rb +74 -103
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/store.rb +119 -15
- data/lib/active_record/tasks/database_tasks.rb +158 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/test_case.rb +61 -38
- data/lib/active_record/timestamp.rb +8 -9
- data/lib/active_record/transactions.rb +65 -51
- data/lib/active_record/validations.rb +17 -15
- data/lib/active_record/validations/associated.rb +20 -14
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +93 -52
- data/lib/active_record/version.rb +4 -4
- data/lib/rails/generators/active_record.rb +3 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- metadata +53 -46
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/rails/generators/active_record/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,21 +1,26 @@
|
|
1
|
-
require 'active_support/core_ext/object/blank'
|
2
1
|
|
3
2
|
module ActiveRecord
|
4
3
|
module Batches
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
4
|
+
# Looping through a collection of records from the database
|
5
|
+
# (using the +all+ method, for example) is very inefficient
|
6
|
+
# since it will try to instantiate all the objects at once.
|
8
7
|
#
|
9
|
-
#
|
8
|
+
# In that case, batch processing methods allow you to work
|
9
|
+
# with the records in batches, thereby greatly reducing memory consumption.
|
10
|
+
#
|
11
|
+
# The #find_each method uses #find_in_batches with a batch size of 1000 (or as
|
12
|
+
# specified by the +:batch_size+ option).
|
13
|
+
#
|
14
|
+
# Person.all.find_each do |person|
|
15
|
+
# person.do_awesome_stuff
|
16
|
+
# end
|
10
17
|
#
|
11
18
|
# Person.where("age > 21").find_each do |person|
|
12
19
|
# person.party_all_night!
|
13
20
|
# end
|
14
21
|
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# you just need to loop over less than 1000 records, it's probably
|
18
|
-
# better just to use the regular find methods.
|
22
|
+
# You can also pass the +:start+ option to specify
|
23
|
+
# an offset to control the starting point.
|
19
24
|
def find_each(options = {})
|
20
25
|
find_in_batches(options) do |records|
|
21
26
|
records.each { |record| yield record }
|
@@ -23,42 +28,40 @@ module ActiveRecord
|
|
23
28
|
end
|
24
29
|
|
25
30
|
# Yields each batch of records that was found by the find +options+ as
|
26
|
-
# an array. The size of each batch is set by the
|
31
|
+
# an array. The size of each batch is set by the +:batch_size+
|
27
32
|
# option; the default is 1000.
|
28
33
|
#
|
29
34
|
# You can control the starting point for the batch processing by
|
30
|
-
# supplying the
|
35
|
+
# supplying the +:start+ option. This is especially useful if you
|
31
36
|
# want multiple workers dealing with the same processing queue. You can
|
32
37
|
# make worker 1 handle all the records between id 0 and 10,000 and
|
33
|
-
# worker 2 handle from 10,000 and beyond (by setting the
|
38
|
+
# worker 2 handle from 10,000 and beyond (by setting the +:start+
|
34
39
|
# option on that worker).
|
35
40
|
#
|
36
41
|
# It's not possible to set the order. That is automatically set to
|
37
42
|
# ascending on the primary key ("id ASC") to make the batch ordering
|
38
|
-
# work. This also
|
43
|
+
# work. This also means that this method only works with integer-based
|
39
44
|
# primary keys. You can't set the limit either, that's used to control
|
40
45
|
# the batch sizes.
|
41
46
|
#
|
42
|
-
# Example:
|
43
|
-
#
|
44
47
|
# Person.where("age > 21").find_in_batches do |group|
|
45
48
|
# sleep(50) # Make sure it doesn't get too crowded in there!
|
46
49
|
# group.each { |person| person.party_all_night! }
|
47
50
|
# end
|
51
|
+
#
|
52
|
+
# # Let's process the next 2000 records
|
53
|
+
# Person.all.find_in_batches(start: 2000, batch_size: 2000) do |group|
|
54
|
+
# group.each { |person| person.party_all_night! }
|
55
|
+
# end
|
48
56
|
def find_in_batches(options = {})
|
57
|
+
options.assert_valid_keys(:start, :batch_size)
|
58
|
+
|
49
59
|
relation = self
|
50
60
|
|
51
61
|
unless arel.orders.blank? && arel.taken.blank?
|
52
62
|
ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
|
53
63
|
end
|
54
64
|
|
55
|
-
if (finder_options = options.except(:start, :batch_size)).present?
|
56
|
-
raise "You can't specify an order, it's forced to be #{batch_order}" if options[:order].present?
|
57
|
-
raise "You can't specify a limit, it's forced to be the batch_size" if options[:limit].present?
|
58
|
-
|
59
|
-
relation = apply_finder_options(finder_options)
|
60
|
-
end
|
61
|
-
|
62
65
|
start = options.delete(:start)
|
63
66
|
batch_size = options.delete(:batch_size) || 1000
|
64
67
|
|
@@ -1,58 +1,24 @@
|
|
1
|
-
require 'active_support/core_ext/object/blank'
|
2
|
-
require 'active_support/core_ext/object/try'
|
3
|
-
|
4
1
|
module ActiveRecord
|
5
2
|
module Calculations
|
6
|
-
# Count
|
7
|
-
#
|
8
|
-
# * Count all: By not passing any parameters to count, it will return a count of all the rows for the model.
|
9
|
-
# * Count using column: By passing a column name to count, it will return a count of all the
|
10
|
-
# rows for the model with supplied column present.
|
11
|
-
# * Count using options will find the row count matched by the options used.
|
12
|
-
#
|
13
|
-
# The third approach, count using options, accepts an option hash as the only parameter. The options are:
|
14
|
-
#
|
15
|
-
# * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
|
16
|
-
# See conditions in the intro to ActiveRecord::Base.
|
17
|
-
# * <tt>:joins</tt>: Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id"
|
18
|
-
# (rarely needed) or named associations in the same form used for the <tt>:include</tt> option, which will
|
19
|
-
# perform an INNER JOIN on the associated table(s). If the value is a string, then the records
|
20
|
-
# will be returned read-only since they will have attributes that do not correspond to the table's columns.
|
21
|
-
# Pass <tt>:readonly => false</tt> to override.
|
22
|
-
# * <tt>:include</tt>: Named associations that should be loaded alongside using LEFT OUTER JOINs.
|
23
|
-
# The symbols named refer to already defined associations. When using named associations, count
|
24
|
-
# returns the number of DISTINCT items for the model you're counting.
|
25
|
-
# See eager loading under Associations.
|
26
|
-
# * <tt>:order</tt>: An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
|
27
|
-
# * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
|
28
|
-
# * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you, for example,
|
29
|
-
# want to do a join but not include the joined columns.
|
30
|
-
# * <tt>:distinct</tt>: Set this to true to make this a distinct calculation, such as
|
31
|
-
# SELECT COUNT(DISTINCT posts.id) ...
|
32
|
-
# * <tt>:from</tt> - By default, this is the table name of the class, but can be changed to an
|
33
|
-
# alternate table name (or even the name of a database view).
|
34
|
-
#
|
35
|
-
# Examples for counting all:
|
36
|
-
# Person.count # returns the total count of all people
|
3
|
+
# Count the records.
|
37
4
|
#
|
38
|
-
#
|
39
|
-
#
|
5
|
+
# Person.count
|
6
|
+
# # => the total count of all people
|
40
7
|
#
|
41
|
-
#
|
42
|
-
#
|
8
|
+
# Person.count(:age)
|
9
|
+
# # => returns the total count of all people whose age is present in database
|
43
10
|
#
|
44
|
-
#
|
45
|
-
#
|
11
|
+
# Person.count(:all)
|
12
|
+
# # => performs a COUNT(*) (:all is an alias for '*')
|
46
13
|
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
# :joins => "LEFT JOIN jobs on jobs.person_id = person.id")
|
14
|
+
# Person.count(:age, distinct: true)
|
15
|
+
# # => counts the number of different age values
|
50
16
|
#
|
51
|
-
#
|
52
|
-
#
|
17
|
+
# If +count+ is used with +group+, it returns a Hash whose keys represent the aggregated column,
|
18
|
+
# and the values are the respective amounts:
|
53
19
|
#
|
54
|
-
#
|
55
|
-
#
|
20
|
+
# Person.group(:city).count
|
21
|
+
# # => { 'Rome' => 5, 'Paris' => 3 }
|
56
22
|
def count(column_name = nil, options = {})
|
57
23
|
column_name, options = nil, column_name if column_name.is_a?(Hash)
|
58
24
|
calculate(:count, column_name, options)
|
@@ -91,6 +57,10 @@ module ActiveRecord
|
|
91
57
|
# Person.sum('age') # => 4562
|
92
58
|
def sum(*args)
|
93
59
|
if block_given?
|
60
|
+
ActiveSupport::Deprecation.warn(
|
61
|
+
"Calling #sum with a block is deprecated and will be removed in Rails 4.1. " \
|
62
|
+
"If you want to perform sum calculation over the array of elements, use `to_a.sum(&block)`."
|
63
|
+
)
|
94
64
|
self.to_a.sum(*args) {|*block_args| yield(*block_args)}
|
95
65
|
else
|
96
66
|
calculate(:sum, *args)
|
@@ -98,99 +68,133 @@ module ActiveRecord
|
|
98
68
|
end
|
99
69
|
|
100
70
|
# This calculates aggregate values in the given column. Methods for count, sum, average,
|
101
|
-
# minimum, and maximum have been added as shortcuts.
|
102
|
-
# <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
|
71
|
+
# minimum, and maximum have been added as shortcuts.
|
103
72
|
#
|
104
73
|
# There are two basic forms of output:
|
74
|
+
#
|
105
75
|
# * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
|
106
76
|
# for AVG, and the given column's type for everything else.
|
107
|
-
# * Grouped values: This returns an ordered hash of the values and groups them by the
|
108
|
-
# <tt>:group</tt> option. It takes either a column name, or the name of a belongs_to association.
|
109
77
|
#
|
110
|
-
#
|
78
|
+
# * Grouped values: This returns an ordered hash of the values and groups them. It
|
79
|
+
# takes either a column name, or the name of a belongs_to association.
|
80
|
+
#
|
81
|
+
# values = Person.group('last_name').maximum(:age)
|
111
82
|
# puts values["Drake"]
|
112
|
-
# => 43
|
83
|
+
# # => 43
|
113
84
|
#
|
114
85
|
# drake = Family.find_by_last_name('Drake')
|
115
|
-
# values = Person.maximum(:age
|
86
|
+
# values = Person.group(:family).maximum(:age) # Person belongs_to :family
|
116
87
|
# puts values[drake]
|
117
|
-
# => 43
|
88
|
+
# # => 43
|
118
89
|
#
|
119
90
|
# values.each do |family, max_age|
|
120
91
|
# ...
|
121
92
|
# end
|
122
93
|
#
|
123
|
-
# Options:
|
124
|
-
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
|
125
|
-
# See conditions in the intro to ActiveRecord::Base.
|
126
|
-
# * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything,
|
127
|
-
# the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
|
128
|
-
# * <tt>:joins</tt> - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id".
|
129
|
-
# (Rarely needed).
|
130
|
-
# The records will be returned read-only since they will have attributes that do not correspond to the
|
131
|
-
# table's columns.
|
132
|
-
# * <tt>:order</tt> - An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
|
133
|
-
# * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
|
134
|
-
# * <tt>:select</tt> - By default, this is * as in SELECT * FROM, but can be changed if you for example
|
135
|
-
# want to do a join, but not include the joined columns.
|
136
|
-
# * <tt>:distinct</tt> - Set this to true to make this a distinct calculation, such as
|
137
|
-
# SELECT COUNT(DISTINCT posts.id) ...
|
138
|
-
#
|
139
|
-
# Examples:
|
140
94
|
# Person.calculate(:count, :all) # The same as Person.count
|
141
95
|
# Person.average(:age) # SELECT AVG(age) FROM people...
|
142
|
-
# Person.minimum(:age, :conditions => ['last_name != ?', 'Drake']) # Selects the minimum age for
|
143
|
-
# # everyone with a last name other than 'Drake'
|
144
96
|
#
|
145
97
|
# # Selects the minimum age for any family without any minors
|
146
|
-
# Person.
|
98
|
+
# Person.group(:last_name).having("min(age) > 17").minimum(:age)
|
147
99
|
#
|
148
100
|
# Person.sum("2 * age")
|
149
101
|
def calculate(operation, column_name, options = {})
|
150
|
-
|
151
|
-
apply_finder_options(options.except(:distinct)).calculate(operation, column_name, :distinct => options[:distinct])
|
152
|
-
else
|
153
|
-
relation = with_default_scope
|
102
|
+
relation = with_default_scope
|
154
103
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
else
|
159
|
-
perform_calculation(operation, column_name, options)
|
160
|
-
end
|
104
|
+
if relation.equal?(self)
|
105
|
+
if has_include?(column_name)
|
106
|
+
construct_relation_for_association_calculations.calculate(operation, column_name, options)
|
161
107
|
else
|
162
|
-
|
108
|
+
perform_calculation(operation, column_name, options)
|
163
109
|
end
|
110
|
+
else
|
111
|
+
relation.calculate(operation, column_name, options)
|
164
112
|
end
|
165
113
|
rescue ThrowResult
|
166
114
|
0
|
167
115
|
end
|
168
116
|
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
117
|
+
# Use <tt>pluck</tt> as a shortcut to select one or more attributes without
|
118
|
+
# loading a bunch of records just to grab the attributes you want.
|
119
|
+
#
|
120
|
+
# Person.pluck(:name)
|
121
|
+
#
|
122
|
+
# instead of
|
123
|
+
#
|
124
|
+
# Person.all.map(&:name)
|
125
|
+
#
|
126
|
+
# Pluck returns an <tt>Array</tt> of attribute values type-casted to match
|
127
|
+
# the plucked column names, if they can be deduced. Plucking an SQL fragment
|
128
|
+
# returns String values by default.
|
172
129
|
#
|
173
|
-
#
|
130
|
+
# Person.pluck(:id)
|
131
|
+
# # SELECT people.id FROM people
|
132
|
+
# # => [1, 2, 3]
|
174
133
|
#
|
175
|
-
# Person.pluck(:id)
|
176
|
-
#
|
177
|
-
#
|
134
|
+
# Person.pluck(:id, :name)
|
135
|
+
# # SELECT people.id, people.name FROM people
|
136
|
+
# # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
|
178
137
|
#
|
179
|
-
|
180
|
-
|
181
|
-
|
138
|
+
# Person.uniq.pluck(:role)
|
139
|
+
# # SELECT DISTINCT role FROM people
|
140
|
+
# # => ['admin', 'member', 'guest']
|
141
|
+
#
|
142
|
+
# Person.where(age: 21).limit(5).pluck(:id)
|
143
|
+
# # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
|
144
|
+
# # => [2, 3]
|
145
|
+
#
|
146
|
+
# Person.pluck('DATEDIFF(updated_at, created_at)')
|
147
|
+
# # SELECT DATEDIFF(updated_at, created_at) FROM people
|
148
|
+
# # => ['0', '27761', '173']
|
149
|
+
#
|
150
|
+
def pluck(*column_names)
|
151
|
+
column_names.map! do |column_name|
|
152
|
+
if column_name.is_a?(Symbol) && self.column_names.include?(column_name.to_s)
|
153
|
+
"#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
|
154
|
+
else
|
155
|
+
column_name
|
156
|
+
end
|
182
157
|
end
|
183
158
|
|
184
|
-
|
185
|
-
|
159
|
+
if has_include?(column_names.first)
|
160
|
+
construct_relation_for_association_calculations.pluck(*column_names)
|
161
|
+
else
|
162
|
+
relation = spawn
|
163
|
+
relation.select_values = column_names
|
164
|
+
result = klass.connection.select_all(relation.arel, nil, bind_values)
|
165
|
+
columns = result.columns.map do |key|
|
166
|
+
klass.column_types.fetch(key) {
|
167
|
+
result.column_types.fetch(key) {
|
168
|
+
Class.new { def type_cast(v); v; end }.new
|
169
|
+
}
|
170
|
+
}
|
171
|
+
end
|
186
172
|
|
187
|
-
|
188
|
-
|
173
|
+
result = result.map do |attributes|
|
174
|
+
values = klass.initialize_attributes(attributes).values
|
175
|
+
|
176
|
+
columns.zip(values).map do |column, value|
|
177
|
+
column.type_cast(value)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
columns.one? ? result.map!(&:first) : result
|
189
181
|
end
|
190
182
|
end
|
191
183
|
|
184
|
+
# Pluck all the ID's for the relation using the table's primary key
|
185
|
+
#
|
186
|
+
# Person.ids # SELECT people.id FROM people
|
187
|
+
# Person.joins(:companies).ids # SELECT people.id FROM people INNER JOIN companies ON companies.person_id = people.id
|
188
|
+
def ids
|
189
|
+
pluck primary_key
|
190
|
+
end
|
191
|
+
|
192
192
|
private
|
193
193
|
|
194
|
+
def has_include?(column_name)
|
195
|
+
eager_loading? || (includes_values.present? && (column_name || references_eager_loaded_tables?))
|
196
|
+
end
|
197
|
+
|
194
198
|
def perform_calculation(operation, column_name, options = {})
|
195
199
|
operation = operation.to_s.downcase
|
196
200
|
|
@@ -209,7 +213,7 @@ module ActiveRecord
|
|
209
213
|
distinct = nil if column_name =~ /\s*DISTINCT\s+/i
|
210
214
|
end
|
211
215
|
|
212
|
-
if
|
216
|
+
if group_values.any?
|
213
217
|
execute_grouped_calculation(operation, column_name, distinct)
|
214
218
|
else
|
215
219
|
execute_simple_calculation(operation, column_name, distinct)
|
@@ -232,6 +236,8 @@ module ActiveRecord
|
|
232
236
|
# Postgresql doesn't like ORDER BY when there are no GROUP BY
|
233
237
|
relation = reorder(nil)
|
234
238
|
|
239
|
+
column_alias = column_name
|
240
|
+
|
235
241
|
if operation == "count" && (relation.limit_value || relation.offset_value)
|
236
242
|
# Shortcut when limit is zero.
|
237
243
|
return 0 if relation.limit_value == 0
|
@@ -242,16 +248,24 @@ module ActiveRecord
|
|
242
248
|
|
243
249
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
244
250
|
|
251
|
+
column_alias = select_value.alias
|
245
252
|
relation.select_values = [select_value]
|
246
253
|
|
247
254
|
query_builder = relation.arel
|
248
255
|
end
|
249
256
|
|
250
|
-
|
257
|
+
result = @klass.connection.select_all(query_builder, nil, relation.bind_values)
|
258
|
+
row = result.first
|
259
|
+
value = row && row.values.first
|
260
|
+
column = result.column_types.fetch(column_alias) do
|
261
|
+
column_for(column_name)
|
262
|
+
end
|
263
|
+
|
264
|
+
type_cast_calculated_value(value, column, operation)
|
251
265
|
end
|
252
266
|
|
253
267
|
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
254
|
-
group_attrs =
|
268
|
+
group_attrs = group_values
|
255
269
|
|
256
270
|
if group_attrs.first.respond_to?(:to_sym)
|
257
271
|
association = @klass.reflect_on_association(group_attrs.first.to_sym)
|
@@ -261,17 +275,19 @@ module ActiveRecord
|
|
261
275
|
group_fields = group_attrs
|
262
276
|
end
|
263
277
|
|
264
|
-
group_aliases = group_fields.map { |field|
|
278
|
+
group_aliases = group_fields.map { |field|
|
279
|
+
column_alias_for(field)
|
280
|
+
}
|
265
281
|
group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
|
266
|
-
[aliaz,
|
282
|
+
[aliaz, field]
|
267
283
|
}
|
268
284
|
|
269
|
-
group =
|
285
|
+
group = group_fields
|
270
286
|
|
271
287
|
if operation == 'count' && column_name == :all
|
272
288
|
aggregate_alias = 'count_all'
|
273
289
|
else
|
274
|
-
aggregate_alias = column_alias_for(operation, column_name)
|
290
|
+
aggregate_alias = column_alias_for([operation, column_name].join(' '))
|
275
291
|
end
|
276
292
|
|
277
293
|
select_values = [
|
@@ -280,7 +296,7 @@ module ActiveRecord
|
|
280
296
|
operation,
|
281
297
|
distinct).as(aggregate_alias)
|
282
298
|
]
|
283
|
-
select_values +=
|
299
|
+
select_values += select_values unless having_values.empty?
|
284
300
|
|
285
301
|
select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
|
286
302
|
if field.respond_to?(:as)
|
@@ -290,10 +306,11 @@ module ActiveRecord
|
|
290
306
|
end
|
291
307
|
}
|
292
308
|
|
293
|
-
relation = except(:group)
|
309
|
+
relation = except(:group)
|
310
|
+
relation.group_values = group
|
294
311
|
relation.select_values = select_values
|
295
312
|
|
296
|
-
calculated_data = @klass.connection.select_all(relation)
|
313
|
+
calculated_data = @klass.connection.select_all(relation, nil, bind_values)
|
297
314
|
|
298
315
|
if association
|
299
316
|
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
@@ -301,8 +318,11 @@ module ActiveRecord
|
|
301
318
|
key_records = Hash[key_records.map { |r| [r.id, r] }]
|
302
319
|
end
|
303
320
|
|
304
|
-
|
305
|
-
key = group_columns.map { |aliaz,
|
321
|
+
Hash[calculated_data.map do |row|
|
322
|
+
key = group_columns.map { |aliaz, col_name|
|
323
|
+
column = calculated_data.column_types.fetch(aliaz) do
|
324
|
+
column_for(col_name)
|
325
|
+
end
|
306
326
|
type_cast_calculated_value(row[aliaz], column)
|
307
327
|
}
|
308
328
|
key = key.first if key.size == 1
|
@@ -319,10 +339,12 @@ module ActiveRecord
|
|
319
339
|
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
320
340
|
# column_alias_for("count(*)") # => "count_all"
|
321
341
|
# column_alias_for("count", "id") # => "count_id"
|
322
|
-
def column_alias_for(
|
323
|
-
keys.
|
324
|
-
|
325
|
-
|
342
|
+
def column_alias_for(keys)
|
343
|
+
if keys.respond_to? :name
|
344
|
+
keys = "#{keys.relation.name}.#{keys.name}"
|
345
|
+
end
|
346
|
+
|
347
|
+
table_name = keys.to_s.downcase
|
326
348
|
table_name.gsub!(/\*/, 'all')
|
327
349
|
table_name.gsub!(/\W+/, ' ')
|
328
350
|
table_name.strip!
|
@@ -333,13 +355,13 @@ module ActiveRecord
|
|
333
355
|
|
334
356
|
def column_for(field)
|
335
357
|
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
|
336
|
-
@klass.
|
358
|
+
@klass.columns_hash[field_name]
|
337
359
|
end
|
338
360
|
|
339
361
|
def type_cast_calculated_value(value, column, operation = nil)
|
340
362
|
case operation
|
341
363
|
when 'count' then value.to_i
|
342
|
-
when 'sum' then type_cast_using_column(value ||
|
364
|
+
when 'sum' then type_cast_using_column(value || 0, column)
|
343
365
|
when 'average' then value.respond_to?(:to_d) ? value.to_d : value
|
344
366
|
else type_cast_using_column(value, column)
|
345
367
|
end
|
@@ -350,9 +372,9 @@ module ActiveRecord
|
|
350
372
|
end
|
351
373
|
|
352
374
|
def select_for_count
|
353
|
-
if
|
354
|
-
select =
|
355
|
-
select if select !~ /
|
375
|
+
if select_values.present?
|
376
|
+
select = select_values.join(", ")
|
377
|
+
select if select !~ /[,*]/
|
356
378
|
end
|
357
379
|
end
|
358
380
|
|