activerecord 3.2.22.4 → 4.0.13
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 +2799 -617
- data/MIT-LICENSE +1 -1
- data/README.rdoc +23 -32
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/association_relation.rb +22 -0
- data/lib/active_record/associations/alias_tracker.rb +4 -2
- data/lib/active_record/associations/association.rb +60 -46
- data/lib/active_record/associations/association_scope.rb +46 -40
- data/lib/active_record/associations/belongs_to_association.rb +17 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +73 -56
- 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 +130 -96
- data/lib/active_record/associations/collection_proxy.rb +916 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
- data/lib/active_record/associations/has_many_association.rb +35 -8
- data/lib/active_record/associations/has_many_through_association.rb +37 -17
- data/lib/active_record/associations/has_one_association.rb +42 -19
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
- data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
- data/lib/active_record/associations/join_dependency.rb +30 -9
- data/lib/active_record/associations/join_helper.rb +1 -11
- 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 +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
- 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/preloader.rb +20 -43
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +223 -282
- data/lib/active_record/attribute_assignment.rb +134 -154
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +36 -29
- data/lib/active_record/attribute_methods/primary_key.rb +45 -31
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +67 -90
- data/lib/active_record/attribute_methods/serialization.rb +133 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
- data/lib/active_record/attribute_methods/write.rb +34 -39
- data/lib/active_record/attribute_methods.rb +268 -108
- data/lib/active_record/autosave_association.rb +80 -73
- data/lib/active_record/base.rb +54 -451
- data/lib/active_record/callbacks.rb +60 -22
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
- data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
- data/lib/active_record/connection_adapters/column.rb +67 -36
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
- data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
- data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +472 -0
- data/lib/active_record/counter_cache.rb +107 -108
- data/lib/active_record/dynamic_matchers.rb +115 -63
- data/lib/active_record/errors.rb +36 -18
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +8 -4
- data/lib/active_record/fixture_set/file.rb +55 -0
- data/lib/active_record/fixtures.rb +159 -155
- data/lib/active_record/inheritance.rb +93 -59
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +39 -43
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration/command_recorder.rb +102 -33
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +411 -173
- data/lib/active_record/model_schema.rb +81 -94
- data/lib/active_record/nested_attributes.rb +173 -131
- data/lib/active_record/null_relation.rb +67 -0
- data/lib/active_record/persistence.rb +254 -106
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +113 -38
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +4 -3
- data/lib/active_record/railties/databases.rake +115 -368
- 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 +110 -61
- data/lib/active_record/relation/batches.rb +29 -29
- data/lib/active_record/relation/calculations.rb +155 -125
- data/lib/active_record/relation/delegation.rb +94 -18
- data/lib/active_record/relation/finder_methods.rb +151 -203
- data/lib/active_record/relation/merger.rb +188 -0
- data/lib/active_record/relation/predicate_builder.rb +85 -42
- data/lib/active_record/relation/query_methods.rb +793 -146
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/relation.rb +293 -173
- data/lib/active_record/result.rb +48 -7
- data/lib/active_record/runtime_registry.rb +17 -0
- data/lib/active_record/sanitization.rb +41 -54
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +41 -41
- data/lib/active_record/schema_migration.rb +46 -0
- data/lib/active_record/scoping/default.rb +56 -52
- data/lib/active_record/scoping/named.rb +78 -103
- data/lib/active_record/scoping.rb +54 -124
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/statement_cache.rb +26 -0
- data/lib/active_record/store.rb +131 -15
- data/lib/active_record/tasks/database_tasks.rb +204 -0
- data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
- data/lib/active_record/tasks/oracle_database_tasks.rb +45 -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/tasks/sqlserver_database_tasks.rb +48 -0
- data/lib/active_record/test_case.rb +67 -38
- data/lib/active_record/timestamp.rb +16 -11
- data/lib/active_record/transactions.rb +73 -51
- data/lib/active_record/validations/associated.rb +19 -13
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +110 -57
- data/lib/active_record/validations.rb +18 -17
- data/lib/active_record/version.rb +7 -6
- data/lib/active_record.rb +63 -45
- data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
- data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -5
- metadata +43 -29
- data/examples/associations.png +0 -0
- 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,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.distinct.count(:age)
|
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,104 +68,143 @@ 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
|
-
# drake = Family.
|
115
|
-
# values = Person.maximum(:age
|
85
|
+
# drake = Family.find_by(last_name: 'Drake')
|
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.pluck('DISTINCT 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
|
172
|
+
|
173
|
+
result = result.map do |attributes|
|
174
|
+
values = klass.initialize_attributes(attributes).values
|
186
175
|
|
187
|
-
|
188
|
-
|
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
|
|
197
|
-
# If #count is used
|
198
|
-
distinct =
|
201
|
+
# If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count)
|
202
|
+
distinct = self.distinct_value
|
203
|
+
if options.has_key?(:distinct)
|
204
|
+
ActiveSupport::Deprecation.warn "The :distinct option for `Relation#count` is deprecated. " \
|
205
|
+
"Please use `Relation#distinct` instead. (eg. `relation.distinct.count`)"
|
206
|
+
distinct = options[:distinct]
|
207
|
+
end
|
199
208
|
|
200
209
|
if operation == "count"
|
201
210
|
column_name ||= (select_for_count || :all)
|
@@ -209,7 +218,7 @@ module ActiveRecord
|
|
209
218
|
distinct = nil if column_name =~ /\s*DISTINCT\s+/i
|
210
219
|
end
|
211
220
|
|
212
|
-
if
|
221
|
+
if group_values.any?
|
213
222
|
execute_grouped_calculation(operation, column_name, distinct)
|
214
223
|
else
|
215
224
|
execute_simple_calculation(operation, column_name, distinct)
|
@@ -232,6 +241,8 @@ module ActiveRecord
|
|
232
241
|
# Postgresql doesn't like ORDER BY when there are no GROUP BY
|
233
242
|
relation = reorder(nil)
|
234
243
|
|
244
|
+
column_alias = column_name
|
245
|
+
|
235
246
|
if operation == "count" && (relation.limit_value || relation.offset_value)
|
236
247
|
# Shortcut when limit is zero.
|
237
248
|
return 0 if relation.limit_value == 0
|
@@ -242,16 +253,24 @@ module ActiveRecord
|
|
242
253
|
|
243
254
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
244
255
|
|
256
|
+
column_alias = select_value.alias
|
245
257
|
relation.select_values = [select_value]
|
246
258
|
|
247
259
|
query_builder = relation.arel
|
248
260
|
end
|
249
261
|
|
250
|
-
|
262
|
+
result = @klass.connection.select_all(query_builder, nil, relation.bind_values)
|
263
|
+
row = result.first
|
264
|
+
value = row && row.values.first
|
265
|
+
column = result.column_types.fetch(column_alias) do
|
266
|
+
column_for(column_name)
|
267
|
+
end
|
268
|
+
|
269
|
+
type_cast_calculated_value(value, column, operation)
|
251
270
|
end
|
252
271
|
|
253
272
|
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
254
|
-
group_attrs =
|
273
|
+
group_attrs = group_values
|
255
274
|
|
256
275
|
if group_attrs.first.respond_to?(:to_sym)
|
257
276
|
association = @klass.reflect_on_association(group_attrs.first.to_sym)
|
@@ -261,17 +280,19 @@ module ActiveRecord
|
|
261
280
|
group_fields = group_attrs
|
262
281
|
end
|
263
282
|
|
264
|
-
group_aliases = group_fields.map { |field|
|
283
|
+
group_aliases = group_fields.map { |field|
|
284
|
+
column_alias_for(field)
|
285
|
+
}
|
265
286
|
group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
|
266
|
-
[aliaz,
|
287
|
+
[aliaz, field]
|
267
288
|
}
|
268
289
|
|
269
|
-
group =
|
290
|
+
group = group_fields
|
270
291
|
|
271
292
|
if operation == 'count' && column_name == :all
|
272
293
|
aggregate_alias = 'count_all'
|
273
294
|
else
|
274
|
-
aggregate_alias = column_alias_for(operation, column_name)
|
295
|
+
aggregate_alias = column_alias_for([operation, column_name].join(' '))
|
275
296
|
end
|
276
297
|
|
277
298
|
select_values = [
|
@@ -280,7 +301,7 @@ module ActiveRecord
|
|
280
301
|
operation,
|
281
302
|
distinct).as(aggregate_alias)
|
282
303
|
]
|
283
|
-
select_values +=
|
304
|
+
select_values += select_values unless having_values.empty?
|
284
305
|
|
285
306
|
select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
|
286
307
|
if field.respond_to?(:as)
|
@@ -290,10 +311,11 @@ module ActiveRecord
|
|
290
311
|
end
|
291
312
|
}
|
292
313
|
|
293
|
-
relation = except(:group)
|
314
|
+
relation = except(:group)
|
315
|
+
relation.group_values = group
|
294
316
|
relation.select_values = select_values
|
295
317
|
|
296
|
-
calculated_data = @klass.connection.select_all(relation)
|
318
|
+
calculated_data = @klass.connection.select_all(relation, nil, bind_values)
|
297
319
|
|
298
320
|
if association
|
299
321
|
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
@@ -301,13 +323,18 @@ module ActiveRecord
|
|
301
323
|
key_records = Hash[key_records.map { |r| [r.id, r] }]
|
302
324
|
end
|
303
325
|
|
304
|
-
|
305
|
-
key = group_columns.map { |aliaz,
|
326
|
+
Hash[calculated_data.map do |row|
|
327
|
+
key = group_columns.map { |aliaz, col_name|
|
328
|
+
column = calculated_data.column_types.fetch(aliaz) do
|
329
|
+
column_for(col_name)
|
330
|
+
end
|
306
331
|
type_cast_calculated_value(row[aliaz], column)
|
307
332
|
}
|
308
333
|
key = key.first if key.size == 1
|
309
334
|
key = key_records[key] if associated
|
310
|
-
|
335
|
+
|
336
|
+
column_type = calculated_data.column_types.fetch(aggregate_alias) { column_for(column_name) }
|
337
|
+
[key, type_cast_calculated_value(row[aggregate_alias], column_type, operation)]
|
311
338
|
end]
|
312
339
|
end
|
313
340
|
|
@@ -319,10 +346,12 @@ module ActiveRecord
|
|
319
346
|
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
320
347
|
# column_alias_for("count(*)") # => "count_all"
|
321
348
|
# column_alias_for("count", "id") # => "count_id"
|
322
|
-
def column_alias_for(
|
323
|
-
keys.
|
324
|
-
|
325
|
-
|
349
|
+
def column_alias_for(keys)
|
350
|
+
if keys.respond_to? :name
|
351
|
+
keys = "#{keys.relation.name}.#{keys.name}"
|
352
|
+
end
|
353
|
+
|
354
|
+
table_name = keys.to_s.downcase
|
326
355
|
table_name.gsub!(/\*/, 'all')
|
327
356
|
table_name.gsub!(/\W+/, ' ')
|
328
357
|
table_name.strip!
|
@@ -333,13 +362,13 @@ module ActiveRecord
|
|
333
362
|
|
334
363
|
def column_for(field)
|
335
364
|
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
|
336
|
-
@klass.
|
365
|
+
@klass.columns_hash[field_name]
|
337
366
|
end
|
338
367
|
|
339
368
|
def type_cast_calculated_value(value, column, operation = nil)
|
340
369
|
case operation
|
341
370
|
when 'count' then value.to_i
|
342
|
-
when 'sum' then type_cast_using_column(value ||
|
371
|
+
when 'sum' then type_cast_using_column(value || 0, column)
|
343
372
|
when 'average' then value.respond_to?(:to_d) ? value.to_d : value
|
344
373
|
else type_cast_using_column(value, column)
|
345
374
|
end
|
@@ -349,10 +378,11 @@ module ActiveRecord
|
|
349
378
|
column ? column.type_cast(value) : value
|
350
379
|
end
|
351
380
|
|
381
|
+
# TODO: refactor to allow non-string `select_values` (eg. Arel nodes).
|
352
382
|
def select_for_count
|
353
|
-
if
|
354
|
-
select =
|
355
|
-
select if select !~ /
|
383
|
+
if select_values.present?
|
384
|
+
select = select_values.join(", ")
|
385
|
+
select if select !~ /[,*]/
|
356
386
|
end
|
357
387
|
end
|
358
388
|
|
@@ -1,25 +1,104 @@
|
|
1
|
-
require '
|
1
|
+
require 'thread'
|
2
|
+
require 'thread_safe'
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
|
-
module Delegation
|
5
|
-
|
5
|
+
module Delegation # :nodoc:
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
# This module creates compiled delegation methods dynamically at runtime, which makes
|
9
|
+
# subsequent calls to that method faster by avoiding method_missing. The delegations
|
10
|
+
# may vary depending on the klass of a relation, so we create a subclass of Relation
|
11
|
+
# for each different klass, and the delegations are compiled into that subclass only.
|
12
|
+
|
6
13
|
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :to => :to_a
|
7
14
|
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
|
8
|
-
:connection, :columns_hash, :
|
15
|
+
:connection, :columns_hash, :to => :klass
|
16
|
+
|
17
|
+
module ClassSpecificRelation # :nodoc:
|
18
|
+
extend ActiveSupport::Concern
|
19
|
+
|
20
|
+
included do
|
21
|
+
@delegation_mutex = Mutex.new
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods # :nodoc:
|
25
|
+
def name
|
26
|
+
superclass.name
|
27
|
+
end
|
9
28
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
29
|
+
def delegate_to_scoped_klass(method)
|
30
|
+
@delegation_mutex.synchronize do
|
31
|
+
return if method_defined?(method)
|
32
|
+
|
33
|
+
if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
|
34
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
35
|
+
def #{method}(*args, &block)
|
36
|
+
scoping { @klass.#{method}(*args, &block) }
|
37
|
+
end
|
38
|
+
RUBY
|
39
|
+
else
|
40
|
+
define_method method do |*args, &block|
|
41
|
+
scoping { @klass.send(method, *args, &block) }
|
42
|
+
end
|
43
|
+
end
|
15
44
|
end
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
45
|
+
end
|
46
|
+
|
47
|
+
def delegate(method, opts = {})
|
48
|
+
@delegation_mutex.synchronize do
|
49
|
+
return if method_defined?(method)
|
50
|
+
super
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def method_missing(method, *args, &block)
|
58
|
+
if @klass.respond_to?(method)
|
59
|
+
self.class.delegate_to_scoped_klass(method)
|
60
|
+
scoping { @klass.send(method, *args, &block) }
|
61
|
+
elsif Array.method_defined?(method)
|
62
|
+
self.class.delegate method, :to => :to_a
|
63
|
+
to_a.send(method, *args, &block)
|
64
|
+
elsif arel.respond_to?(method)
|
65
|
+
self.class.delegate method, :to => :arel
|
66
|
+
arel.send(method, *args, &block)
|
67
|
+
else
|
68
|
+
super
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
module ClassMethods # :nodoc:
|
74
|
+
@@subclasses = ThreadSafe::Cache.new(:initial_capacity => 2)
|
75
|
+
|
76
|
+
def new(klass, *args)
|
77
|
+
relation = relation_class_for(klass).allocate
|
78
|
+
relation.__send__(:initialize, klass, *args)
|
79
|
+
relation
|
80
|
+
end
|
81
|
+
|
82
|
+
# This doesn't have to be thread-safe. relation_class_for guarantees that this will only be
|
83
|
+
# called exactly once for a given const name.
|
84
|
+
def const_missing(name)
|
85
|
+
const_set(name, Class.new(self) { include ClassSpecificRelation })
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
# Cache the constants in @@subclasses because looking them up via const_get
|
90
|
+
# make instantiation significantly slower.
|
91
|
+
def relation_class_for(klass)
|
92
|
+
if klass && (klass_name = klass.name)
|
93
|
+
my_cache = @@subclasses.compute_if_absent(self) { ThreadSafe::Cache.new }
|
94
|
+
# This hash is keyed by klass.name to avoid memory leaks in development mode
|
95
|
+
my_cache.compute_if_absent(klass_name) do
|
96
|
+
# Cache#compute_if_absent guarantees that the block will only executed once for the given klass_name
|
97
|
+
const_get("#{name.gsub('::', '_')}_#{klass_name.gsub('::', '_')}", false)
|
21
98
|
end
|
22
|
-
|
99
|
+
else
|
100
|
+
ActiveRecord::Relation
|
101
|
+
end
|
23
102
|
end
|
24
103
|
end
|
25
104
|
|
@@ -33,13 +112,10 @@ module ActiveRecord
|
|
33
112
|
|
34
113
|
def method_missing(method, *args, &block)
|
35
114
|
if @klass.respond_to?(method)
|
36
|
-
::ActiveRecord::Delegation.delegate_to_scoped_klass(method)
|
37
115
|
scoping { @klass.send(method, *args, &block) }
|
38
116
|
elsif Array.method_defined?(method)
|
39
|
-
::ActiveRecord::Delegation.delegate method, :to => :to_a
|
40
117
|
to_a.send(method, *args, &block)
|
41
118
|
elsif arel.respond_to?(method)
|
42
|
-
::ActiveRecord::Delegation.delegate method, :to => :arel
|
43
119
|
arel.send(method, *args, &block)
|
44
120
|
else
|
45
121
|
super
|