activerecord 3.2.22.5 → 5.2.8

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.

Files changed (275) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -621
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +41 -46
  5. data/examples/performance.rb +55 -42
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +264 -236
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +127 -75
  11. data/lib/active_record/associations/association_scope.rb +126 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +78 -27
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
  14. data/lib/active_record/associations/builder/association.rb +117 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +135 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -54
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
  18. data/lib/active_record/associations/builder/has_many.rb +10 -64
  19. data/lib/active_record/associations/builder/has_one.rb +19 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +28 -18
  21. data/lib/active_record/associations/collection_association.rb +226 -293
  22. data/lib/active_record/associations/collection_proxy.rb +1067 -69
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +83 -47
  25. data/lib/active_record/associations/has_many_through_association.rb +98 -65
  26. data/lib/active_record/associations/has_one_association.rb +57 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +212 -164
  32. data/lib/active_record/associations/preloader/association.rb +95 -89
  33. data/lib/active_record/associations/preloader/through_association.rb +84 -44
  34. data/lib/active_record/associations/preloader.rb +123 -111
  35. data/lib/active_record/associations/singular_association.rb +33 -24
  36. data/lib/active_record/associations/through_association.rb +60 -26
  37. data/lib/active_record/associations.rb +1759 -1506
  38. data/lib/active_record/attribute_assignment.rb +60 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +113 -74
  42. data/lib/active_record/attribute_methods/primary_key.rb +106 -77
  43. data/lib/active_record/attribute_methods/query.rb +8 -5
  44. data/lib/active_record/attribute_methods/read.rb +63 -114
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -90
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
  47. data/lib/active_record/attribute_methods/write.rb +43 -45
  48. data/lib/active_record/attribute_methods.rb +366 -149
  49. data/lib/active_record/attributes.rb +266 -0
  50. data/lib/active_record/autosave_association.rb +312 -225
  51. data/lib/active_record/base.rb +114 -505
  52. data/lib/active_record/callbacks.rb +145 -67
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
  69. data/lib/active_record/connection_adapters/column.rb +50 -255
  70. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -210
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
  117. data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +545 -27
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +145 -0
  128. data/lib/active_record/core.rb +559 -0
  129. data/lib/active_record/counter_cache.rb +200 -105
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +107 -69
  132. data/lib/active_record/enum.rb +244 -0
  133. data/lib/active_record/errors.rb +245 -60
  134. data/lib/active_record/explain.rb +35 -71
  135. data/lib/active_record/explain_registry.rb +32 -0
  136. data/lib/active_record/explain_subscriber.rb +18 -9
  137. data/lib/active_record/fixture_set/file.rb +82 -0
  138. data/lib/active_record/fixtures.rb +418 -275
  139. data/lib/active_record/gem_version.rb +17 -0
  140. data/lib/active_record/inheritance.rb +209 -100
  141. data/lib/active_record/integration.rb +116 -21
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  144. data/lib/active_record/locale/en.yml +9 -1
  145. data/lib/active_record/locking/optimistic.rb +107 -94
  146. data/lib/active_record/locking/pessimistic.rb +20 -8
  147. data/lib/active_record/log_subscriber.rb +99 -34
  148. data/lib/active_record/migration/command_recorder.rb +199 -64
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +17 -0
  151. data/lib/active_record/migration.rb +893 -296
  152. data/lib/active_record/model_schema.rb +328 -175
  153. data/lib/active_record/nested_attributes.rb +338 -242
  154. data/lib/active_record/no_touching.rb +58 -0
  155. data/lib/active_record/null_relation.rb +68 -0
  156. data/lib/active_record/persistence.rb +557 -170
  157. data/lib/active_record/query_cache.rb +14 -43
  158. data/lib/active_record/querying.rb +36 -24
  159. data/lib/active_record/railtie.rb +147 -52
  160. data/lib/active_record/railties/console_sandbox.rb +5 -4
  161. data/lib/active_record/railties/controller_runtime.rb +13 -6
  162. data/lib/active_record/railties/databases.rake +206 -488
  163. data/lib/active_record/readonly_attributes.rb +4 -6
  164. data/lib/active_record/reflection.rb +734 -228
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +249 -52
  167. data/lib/active_record/relation/calculations.rb +330 -284
  168. data/lib/active_record/relation/delegation.rb +135 -37
  169. data/lib/active_record/relation/finder_methods.rb +450 -287
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +193 -0
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  179. data/lib/active_record/relation/predicate_builder.rb +132 -43
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +1037 -221
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +48 -151
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +451 -359
  187. data/lib/active_record/result.rb +129 -20
  188. data/lib/active_record/runtime_registry.rb +24 -0
  189. data/lib/active_record/sanitization.rb +164 -136
  190. data/lib/active_record/schema.rb +31 -19
  191. data/lib/active_record/schema_dumper.rb +154 -107
  192. data/lib/active_record/schema_migration.rb +56 -0
  193. data/lib/active_record/scoping/default.rb +108 -98
  194. data/lib/active_record/scoping/named.rb +125 -112
  195. data/lib/active_record/scoping.rb +77 -123
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +10 -6
  198. data/lib/active_record/statement_cache.rb +121 -0
  199. data/lib/active_record/store.rb +175 -16
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +337 -0
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  206. data/lib/active_record/timestamp.rb +80 -41
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +240 -119
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +9 -0
  212. data/lib/active_record/type/date_time.rb +9 -0
  213. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  214. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +71 -0
  218. data/lib/active_record/type/text.rb +11 -0
  219. data/lib/active_record/type/time.rb +21 -0
  220. data/lib/active_record/type/type_map.rb +62 -0
  221. data/lib/active_record/type/unsigned_integer.rb +17 -0
  222. data/lib/active_record/type.rb +79 -0
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +35 -18
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +68 -0
  230. data/lib/active_record/validations/uniqueness.rb +133 -75
  231. data/lib/active_record/validations.rb +53 -43
  232. data/lib/active_record/version.rb +7 -7
  233. data/lib/active_record.rb +89 -57
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +61 -8
  237. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  238. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  239. data/lib/rails/generators/active_record/migration.rb +28 -8
  240. data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
  241. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  243. data/lib/rails/generators/active_record.rb +10 -16
  244. metadata +141 -62
  245. data/examples/associations.png +0 -0
  246. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  247. data/lib/active_record/associations/join_helper.rb +0 -55
  248. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  249. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  250. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  251. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  252. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  253. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  254. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  255. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  256. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  257. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  258. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  259. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  260. data/lib/active_record/dynamic_finder_match.rb +0 -68
  261. data/lib/active_record/dynamic_scope_match.rb +0 -23
  262. data/lib/active_record/fixtures/file.rb +0 -65
  263. data/lib/active_record/identity_map.rb +0 -162
  264. data/lib/active_record/observer.rb +0 -121
  265. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  266. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  267. data/lib/active_record/session_store.rb +0 -360
  268. data/lib/active_record/test_case.rb +0 -73
  269. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  270. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  271. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  272. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  273. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  274. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  275. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,372 +1,418 @@
1
- require 'active_support/core_ext/object/blank'
2
- require 'active_support/core_ext/object/try'
1
+ # frozen_string_literal: true
3
2
 
4
3
  module ActiveRecord
5
4
  module Calculations
6
- # Count operates using three different approaches.
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
37
- #
38
- # Examples for counting by column:
39
- # Person.count(:age) # returns the total count of all people whose age is present in database
40
- #
41
- # Examples for count with options:
42
- # Person.count(:conditions => "age > 26")
43
- #
44
- # # because of the named association, it finds the DISTINCT count using LEFT OUTER JOIN.
45
- # Person.count(:conditions => "age > 26 AND job.salary > 60000", :include => :job)
46
- #
47
- # # finds the number of rows matching the conditions and joins.
48
- # Person.count(:conditions => "age > 26 AND job.salary > 60000",
49
- # :joins => "LEFT JOIN jobs on jobs.person_id = person.id")
50
- #
51
- # Person.count('id', :conditions => "age > 26") # Performs a COUNT(id)
52
- # Person.count(:all, :conditions => "age > 26") # Performs a COUNT(*) (:all is an alias for '*')
53
- #
54
- # Note: <tt>Person.count(:all)</tt> will not work because it will use <tt>:all</tt> as the condition.
55
- # Use Person.count instead.
56
- def count(column_name = nil, options = {})
57
- column_name, options = nil, column_name if column_name.is_a?(Hash)
58
- calculate(:count, column_name, options)
5
+ # Count the records.
6
+ #
7
+ # Person.count
8
+ # # => the total count of all people
9
+ #
10
+ # Person.count(:age)
11
+ # # => returns the total count of all people whose age is present in database
12
+ #
13
+ # Person.count(:all)
14
+ # # => performs a COUNT(*) (:all is an alias for '*')
15
+ #
16
+ # Person.distinct.count(:age)
17
+ # # => counts the number of different age values
18
+ #
19
+ # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group],
20
+ # it returns a Hash whose keys represent the aggregated column,
21
+ # and the values are the respective amounts:
22
+ #
23
+ # Person.group(:city).count
24
+ # # => { 'Rome' => 5, 'Paris' => 3 }
25
+ #
26
+ # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group] for multiple columns, it returns a Hash whose
27
+ # keys are an array containing the individual values of each column and the value
28
+ # of each key would be the #count.
29
+ #
30
+ # Article.group(:status, :category).count
31
+ # # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
32
+ # ["published", "business"]=>0, ["published", "technology"]=>2}
33
+ #
34
+ # If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
35
+ #
36
+ # Person.select(:age).count
37
+ # # => counts the number of different age values
38
+ #
39
+ # Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
40
+ # between databases. In invalid cases, an error from the database is thrown.
41
+ def count(column_name = nil)
42
+ if block_given?
43
+ unless column_name.nil?
44
+ ActiveSupport::Deprecation.warn \
45
+ "When `count' is called with a block, it ignores other arguments. " \
46
+ "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
47
+ end
48
+
49
+ return super()
50
+ end
51
+
52
+ calculate(:count, column_name)
59
53
  end
60
54
 
61
55
  # Calculates the average value on a given column. Returns +nil+ if there's
62
- # no row. See +calculate+ for examples with options.
56
+ # no row. See #calculate for examples with options.
63
57
  #
64
- # Person.average('age') # => 35.8
65
- def average(column_name, options = {})
66
- calculate(:average, column_name, options)
58
+ # Person.average(:age) # => 35.8
59
+ def average(column_name)
60
+ calculate(:average, column_name)
67
61
  end
68
62
 
69
63
  # Calculates the minimum value on a given column. The value is returned
70
64
  # with the same data type of the column, or +nil+ if there's no row. See
71
- # +calculate+ for examples with options.
65
+ # #calculate for examples with options.
72
66
  #
73
- # Person.minimum('age') # => 7
74
- def minimum(column_name, options = {})
75
- calculate(:minimum, column_name, options)
67
+ # Person.minimum(:age) # => 7
68
+ def minimum(column_name)
69
+ calculate(:minimum, column_name)
76
70
  end
77
71
 
78
72
  # Calculates the maximum value on a given column. The value is returned
79
73
  # with the same data type of the column, or +nil+ if there's no row. See
80
- # +calculate+ for examples with options.
74
+ # #calculate for examples with options.
81
75
  #
82
- # Person.maximum('age') # => 93
83
- def maximum(column_name, options = {})
84
- calculate(:maximum, column_name, options)
76
+ # Person.maximum(:age) # => 93
77
+ def maximum(column_name)
78
+ calculate(:maximum, column_name)
85
79
  end
86
80
 
87
81
  # Calculates the sum of values on a given column. The value is returned
88
- # with the same data type of the column, 0 if there's no row. See
89
- # +calculate+ for examples with options.
82
+ # with the same data type of the column, +0+ if there's no row. See
83
+ # #calculate for examples with options.
90
84
  #
91
- # Person.sum('age') # => 4562
92
- def sum(*args)
85
+ # Person.sum(:age) # => 4562
86
+ def sum(column_name = nil)
93
87
  if block_given?
94
- self.to_a.sum(*args) {|*block_args| yield(*block_args)}
95
- else
96
- calculate(:sum, *args)
88
+ unless column_name.nil?
89
+ ActiveSupport::Deprecation.warn \
90
+ "When `sum' is called with a block, it ignores other arguments. " \
91
+ "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
92
+ end
93
+
94
+ return super()
97
95
  end
96
+
97
+ calculate(:sum, column_name)
98
98
  end
99
99
 
100
- # This calculates aggregate values in the given column. Methods for count, sum, average,
101
- # minimum, and maximum have been added as shortcuts. Options such as <tt>:conditions</tt>,
102
- # <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
100
+ # This calculates aggregate values in the given column. Methods for #count, #sum, #average,
101
+ # #minimum, and #maximum have been added as shortcuts.
103
102
  #
104
- # There are two basic forms of output:
105
- # * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
106
- # 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
- #
110
- # values = Person.maximum(:age, :group => 'last_name')
111
- # puts values["Drake"]
112
- # => 43
113
- #
114
- # drake = Family.find_by_last_name('Drake')
115
- # values = Person.maximum(:age, :group => :family) # Person belongs_to :family
116
- # puts values[drake]
117
- # => 43
118
- #
119
- # values.each do |family, max_age|
120
- # ...
121
- # end
122
- #
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
103
  # Person.calculate(:count, :all) # The same as Person.count
141
104
  # 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
105
  #
145
106
  # # Selects the minimum age for any family without any minors
146
- # Person.minimum(:age, :having => 'min(age) > 17', :group => :last_name)
107
+ # Person.group(:last_name).having("min(age) > 17").minimum(:age)
147
108
  #
148
109
  # Person.sum("2 * age")
149
- def calculate(operation, column_name, options = {})
150
- if options.except(:distinct).present?
151
- apply_finder_options(options.except(:distinct)).calculate(operation, column_name, :distinct => options[:distinct])
152
- else
153
- relation = with_default_scope
154
-
155
- if relation.equal?(self)
156
- if eager_loading? || (includes_values.present? && references_eager_loaded_tables?)
157
- construct_relation_for_association_calculations.calculate(operation, column_name, options)
158
- else
159
- perform_calculation(operation, column_name, options)
110
+ #
111
+ # There are two basic forms of output:
112
+ #
113
+ # * Single aggregate value: The single value is type cast to Integer for COUNT, Float
114
+ # for AVG, and the given column's type for everything else.
115
+ #
116
+ # * Grouped values: This returns an ordered hash of the values and groups them. It
117
+ # takes either a column name, or the name of a belongs_to association.
118
+ #
119
+ # values = Person.group('last_name').maximum(:age)
120
+ # puts values["Drake"]
121
+ # # => 43
122
+ #
123
+ # drake = Family.find_by(last_name: 'Drake')
124
+ # values = Person.group(:family).maximum(:age) # Person belongs_to :family
125
+ # puts values[drake]
126
+ # # => 43
127
+ #
128
+ # values.each do |family, max_age|
129
+ # ...
130
+ # end
131
+ def calculate(operation, column_name)
132
+ if has_include?(column_name)
133
+ relation = apply_join_dependency
134
+
135
+ if operation.to_s.downcase == "count"
136
+ unless distinct_value || distinct_select?(column_name || select_for_count)
137
+ relation.distinct!
138
+ relation.select_values = [ klass.primary_key || table[Arel.star] ]
160
139
  end
161
- else
162
- relation.calculate(operation, column_name, options)
140
+ # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
141
+ relation.order_values = []
163
142
  end
143
+
144
+ relation.calculate(operation, column_name)
145
+ else
146
+ perform_calculation(operation, column_name)
164
147
  end
165
- rescue ThrowResult
166
- 0
167
148
  end
168
149
 
169
- # This method is designed to perform select by a single column as direct SQL query
170
- # Returns <tt>Array</tt> with values of the specified column name
171
- # The values has same data type as column.
150
+ # Use #pluck as a shortcut to select one or more attributes without
151
+ # loading a bunch of records just to grab the attributes you want.
152
+ #
153
+ # Person.pluck(:name)
154
+ #
155
+ # instead of
156
+ #
157
+ # Person.all.map(&:name)
158
+ #
159
+ # Pluck returns an Array of attribute values type-casted to match
160
+ # the plucked column names, if they can be deduced. Plucking an SQL fragment
161
+ # returns String values by default.
172
162
  #
173
- # Examples:
163
+ # Person.pluck(:name)
164
+ # # SELECT people.name FROM people
165
+ # # => ['David', 'Jeremy', 'Jose']
174
166
  #
175
- # Person.pluck(:id) # SELECT people.id FROM people
176
- # Person.uniq.pluck(:role) # SELECT DISTINCT role FROM people
177
- # Person.where(:confirmed => true).limit(5).pluck(:id)
167
+ # Person.pluck(:id, :name)
168
+ # # SELECT people.id, people.name FROM people
169
+ # # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
178
170
  #
179
- def pluck(column_name)
180
- if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s)
181
- column_name = "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
171
+ # Person.distinct.pluck(:role)
172
+ # # SELECT DISTINCT role FROM people
173
+ # # => ['admin', 'member', 'guest']
174
+ #
175
+ # Person.where(age: 21).limit(5).pluck(:id)
176
+ # # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
177
+ # # => [2, 3]
178
+ #
179
+ # Person.pluck('DATEDIFF(updated_at, created_at)')
180
+ # # SELECT DATEDIFF(updated_at, created_at) FROM people
181
+ # # => ['0', '27761', '173']
182
+ #
183
+ # See also #ids.
184
+ #
185
+ def pluck(*column_names)
186
+ if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
187
+ return records.pluck(*column_names)
182
188
  end
183
189
 
184
- result = klass.connection.exec_query(select(column_name).to_sql)
185
- last_column = result.columns.last
186
-
187
- result.map do |attributes|
188
- klass.type_cast_attribute(last_column, klass.initialize_attributes(attributes))
190
+ if has_include?(column_names.first)
191
+ relation = apply_join_dependency
192
+ relation.pluck(*column_names)
193
+ else
194
+ klass.enforce_raw_sql_whitelist(column_names)
195
+ relation = spawn
196
+ relation.select_values = column_names
197
+ result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
198
+ result.cast_values(klass.attribute_types)
189
199
  end
190
200
  end
191
201
 
192
- private
193
-
194
- def perform_calculation(operation, column_name, options = {})
195
- operation = operation.to_s.downcase
196
-
197
- # If #count is used in conjuction with #uniq it is considered distinct. (eg. relation.uniq.count)
198
- distinct = options[:distinct] || self.uniq_value
202
+ # Pluck all the ID's for the relation using the table's primary key
203
+ #
204
+ # Person.ids # SELECT people.id FROM people
205
+ # Person.joins(:companies).ids # SELECT people.id FROM people INNER JOIN companies ON companies.person_id = people.id
206
+ def ids
207
+ pluck primary_key
208
+ end
199
209
 
200
- if operation == "count"
201
- column_name ||= (select_for_count || :all)
210
+ private
211
+ def has_include?(column_name)
212
+ eager_loading? || (includes_values.present? && column_name && column_name != :all)
213
+ end
202
214
 
203
- unless arel.ast.grep(Arel::Nodes::OuterJoin).empty?
204
- distinct = true
215
+ def perform_calculation(operation, column_name)
216
+ operation = operation.to_s.downcase
217
+
218
+ # If #count is used with #distinct (i.e. `relation.distinct.count`) it is
219
+ # considered distinct.
220
+ distinct = distinct_value
221
+
222
+ if operation == "count"
223
+ column_name ||= select_for_count
224
+ if column_name == :all
225
+ if !distinct
226
+ distinct = distinct_select?(select_for_count) if group_values.empty?
227
+ elsif group_values.any? || select_values.empty? && order_values.empty?
228
+ column_name = primary_key
229
+ end
230
+ elsif distinct_select?(column_name)
231
+ distinct = nil
232
+ end
205
233
  end
206
234
 
207
- column_name = primary_key if column_name == :all && distinct
208
-
209
- distinct = nil if column_name =~ /\s*DISTINCT\s+/i
235
+ if group_values.any?
236
+ execute_grouped_calculation(operation, column_name, distinct)
237
+ else
238
+ execute_simple_calculation(operation, column_name, distinct)
239
+ end
210
240
  end
211
241
 
212
- if @group_values.any?
213
- execute_grouped_calculation(operation, column_name, distinct)
214
- else
215
- execute_simple_calculation(operation, column_name, distinct)
242
+ def distinct_select?(column_name)
243
+ column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
216
244
  end
217
- end
218
245
 
219
- def aggregate_column(column_name)
220
- if @klass.column_names.include?(column_name.to_s)
221
- Arel::Attribute.new(@klass.unscoped.table, column_name)
222
- else
223
- Arel.sql(column_name == :all ? "*" : column_name.to_s)
246
+ def aggregate_column(column_name)
247
+ return column_name if Arel::Expressions === column_name
248
+
249
+ if @klass.has_attribute?(column_name) || @klass.attribute_alias?(column_name)
250
+ @klass.arel_attribute(column_name)
251
+ else
252
+ Arel.sql(column_name == :all ? "*" : column_name.to_s)
253
+ end
224
254
  end
225
- end
226
255
 
227
- def operation_over_aggregate_column(column, operation, distinct)
228
- operation == 'count' ? column.count(distinct) : column.send(operation)
229
- end
256
+ def operation_over_aggregate_column(column, operation, distinct)
257
+ operation == "count" ? column.count(distinct) : column.send(operation)
258
+ end
230
259
 
231
- def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
232
- # Postgresql doesn't like ORDER BY when there are no GROUP BY
233
- relation = reorder(nil)
260
+ def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
261
+ column_alias = column_name
234
262
 
235
- if operation == "count" && (relation.limit_value || relation.offset_value)
236
- # Shortcut when limit is zero.
237
- return 0 if relation.limit_value == 0
263
+ if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
264
+ # Shortcut when limit is zero.
265
+ return 0 if limit_value == 0
238
266
 
239
- query_builder = build_count_subquery(relation, column_name, distinct)
240
- else
241
- column = aggregate_column(column_name)
267
+ query_builder = build_count_subquery(spawn, column_name, distinct)
268
+ else
269
+ # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
270
+ relation = unscope(:order).distinct!(false)
242
271
 
243
- select_value = operation_over_aggregate_column(column, operation, distinct)
272
+ column = aggregate_column(column_name)
244
273
 
245
- relation.select_values = [select_value]
274
+ select_value = operation_over_aggregate_column(column, operation, distinct)
275
+ if operation == "sum" && distinct
276
+ select_value.distinct = true
277
+ end
246
278
 
247
- query_builder = relation.arel
248
- end
279
+ column_alias = select_value.alias
280
+ column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
281
+ relation.select_values = [select_value]
249
282
 
250
- type_cast_calculated_value(@klass.connection.select_value(query_builder), column_for(column_name), operation)
251
- end
283
+ query_builder = relation.arel
284
+ end
252
285
 
253
- def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
254
- group_attrs = @group_values
286
+ result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil) }
287
+ row = result.first
288
+ value = row && row.values.first
289
+ type = result.column_types.fetch(column_alias) do
290
+ type_for(column_name)
291
+ end
255
292
 
256
- if group_attrs.first.respond_to?(:to_sym)
257
- association = @klass.reflect_on_association(group_attrs.first.to_sym)
258
- associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
259
- group_fields = Array(associated ? association.foreign_key : group_attrs)
260
- else
261
- group_fields = group_attrs
293
+ type_cast_calculated_value(value, type, operation)
262
294
  end
263
295
 
264
- group_aliases = group_fields.map { |field| column_alias_for(field) }
265
- group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
266
- [aliaz, column_for(field)]
267
- }
296
+ def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
297
+ group_attrs = group_values
268
298
 
269
- group = @klass.connection.adapter_name == 'FrontBase' ? group_aliases : group_fields
299
+ if group_attrs.first.respond_to?(:to_sym)
300
+ association = @klass._reflect_on_association(group_attrs.first)
301
+ associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
302
+ group_fields = Array(associated ? association.foreign_key : group_attrs)
303
+ else
304
+ group_fields = group_attrs
305
+ end
306
+ group_fields = arel_columns(group_fields)
270
307
 
271
- if operation == 'count' && column_name == :all
272
- aggregate_alias = 'count_all'
273
- else
274
- aggregate_alias = column_alias_for(operation, column_name)
275
- end
308
+ group_aliases = group_fields.map { |field| column_alias_for(field) }
309
+ group_columns = group_aliases.zip(group_fields)
276
310
 
277
- select_values = [
278
- operation_over_aggregate_column(
279
- aggregate_column(column_name),
280
- operation,
281
- distinct).as(aggregate_alias)
282
- ]
283
- select_values += @select_values unless @having_values.empty?
284
-
285
- select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
286
- if field.respond_to?(:as)
287
- field.as(aliaz)
311
+ if operation == "count" && column_name == :all
312
+ aggregate_alias = "count_all"
288
313
  else
289
- "#{field} AS #{aliaz}"
314
+ aggregate_alias = column_alias_for([operation, column_name].join(" "))
290
315
  end
291
- }
292
316
 
293
- relation = except(:group).group(group)
294
- relation.select_values = select_values
317
+ select_values = [
318
+ operation_over_aggregate_column(
319
+ aggregate_column(column_name),
320
+ operation,
321
+ distinct).as(aggregate_alias)
322
+ ]
323
+ select_values += self.select_values unless having_clause.empty?
324
+
325
+ select_values.concat group_columns.map { |aliaz, field|
326
+ if field.respond_to?(:as)
327
+ field.as(aliaz)
328
+ else
329
+ "#{field} AS #{aliaz}"
330
+ end
331
+ }
332
+
333
+ relation = except(:group).distinct!(false)
334
+ relation.group_values = group_fields
335
+ relation.select_values = select_values
336
+
337
+ calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
295
338
 
296
- calculated_data = @klass.connection.select_all(relation)
339
+ if association
340
+ key_ids = calculated_data.collect { |row| row[group_aliases.first] }
341
+ key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
342
+ key_records = Hash[key_records.map { |r| [r.id, r] }]
343
+ end
297
344
 
298
- if association
299
- key_ids = calculated_data.collect { |row| row[group_aliases.first] }
300
- key_records = association.klass.base_class.find(key_ids)
301
- key_records = Hash[key_records.map { |r| [r.id, r] }]
345
+ Hash[calculated_data.map do |row|
346
+ key = group_columns.map { |aliaz, col_name|
347
+ type = type_for(col_name) do
348
+ calculated_data.column_types.fetch(aliaz, Type.default_value)
349
+ end
350
+ type_cast_calculated_value(row[aliaz], type)
351
+ }
352
+ key = key.first if key.size == 1
353
+ key = key_records[key] if associated
354
+
355
+ type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
356
+ [key, type_cast_calculated_value(row[aggregate_alias], type, operation)]
357
+ end]
302
358
  end
303
359
 
304
- ActiveSupport::OrderedHash[calculated_data.map do |row|
305
- key = group_columns.map { |aliaz, column|
306
- type_cast_calculated_value(row[aliaz], column)
307
- }
308
- key = key.first if key.size == 1
309
- key = key_records[key] if associated
310
- [key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)]
311
- end]
312
- end
360
+ # Converts the given keys to the value that the database adapter returns as
361
+ # a usable column name:
362
+ #
363
+ # column_alias_for("users.id") # => "users_id"
364
+ # column_alias_for("sum(id)") # => "sum_id"
365
+ # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
366
+ # column_alias_for("count(*)") # => "count_all"
367
+ def column_alias_for(keys)
368
+ if keys.respond_to? :name
369
+ keys = "#{keys.relation.name}.#{keys.name}"
370
+ end
313
371
 
314
- # Converts the given keys to the value that the database adapter returns as
315
- # a usable column name:
316
- #
317
- # column_alias_for("users.id") # => "users_id"
318
- # column_alias_for("sum(id)") # => "sum_id"
319
- # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
320
- # column_alias_for("count(*)") # => "count_all"
321
- # column_alias_for("count", "id") # => "count_id"
322
- def column_alias_for(*keys)
323
- keys.map! {|k| k.respond_to?(:to_sql) ? k.to_sql : k}
324
- table_name = keys.join(' ')
325
- table_name.downcase!
326
- table_name.gsub!(/\*/, 'all')
327
- table_name.gsub!(/\W+/, ' ')
328
- table_name.strip!
329
- table_name.gsub!(/ +/, '_')
330
-
331
- @klass.connection.table_alias_for(table_name)
332
- end
372
+ table_name = keys.to_s.downcase
373
+ table_name.gsub!(/\*/, "all")
374
+ table_name.gsub!(/\W+/, " ")
375
+ table_name.strip!
376
+ table_name.gsub!(/ +/, "_")
333
377
 
334
- def column_for(field)
335
- field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
336
- @klass.columns.detect { |c| c.name.to_s == field_name }
337
- end
378
+ @klass.connection.table_alias_for(table_name)
379
+ end
338
380
 
339
- def type_cast_calculated_value(value, column, operation = nil)
340
- case operation
341
- when 'count' then value.to_i
342
- when 'sum' then type_cast_using_column(value || '0', column)
343
- when 'average' then value.respond_to?(:to_d) ? value.to_d : value
344
- else type_cast_using_column(value, column)
381
+ def type_for(field, &block)
382
+ field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split(".").last
383
+ @klass.type_for_attribute(field_name, &block)
345
384
  end
346
- end
347
385
 
348
- def type_cast_using_column(value, column)
349
- column ? column.type_cast(value) : value
350
- end
386
+ def type_cast_calculated_value(value, type, operation = nil)
387
+ case operation
388
+ when "count" then value.to_i
389
+ when "sum" then type.deserialize(value || 0)
390
+ when "average" then value && value.respond_to?(:to_d) ? value.to_d : value
391
+ else type.deserialize(value)
392
+ end
393
+ end
351
394
 
352
- def select_for_count
353
- if @select_values.present?
354
- select = @select_values.join(", ")
355
- select if select !~ /(,|\*)/
395
+ def select_for_count
396
+ if select_values.present?
397
+ return select_values.first if select_values.one?
398
+ select_values.join(", ")
399
+ else
400
+ :all
401
+ end
356
402
  end
357
- end
358
403
 
359
- def build_count_subquery(relation, column_name, distinct)
360
- column_alias = Arel.sql('count_column')
361
- subquery_alias = Arel.sql('subquery_for_count')
404
+ def build_count_subquery(relation, column_name, distinct)
405
+ if column_name == :all
406
+ relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
407
+ else
408
+ column_alias = Arel.sql("count_column")
409
+ relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
410
+ end
362
411
 
363
- aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
364
- relation.select_values = [aliased_column]
365
- subquery = relation.arel.as(subquery_alias)
412
+ subquery = relation.arel.as(Arel.sql("subquery_for_count"))
413
+ select_value = operation_over_aggregate_column(column_alias || Arel.star, "count", false)
366
414
 
367
- sm = Arel::SelectManager.new relation.engine
368
- select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
369
- sm.project(select_value).from(subquery)
370
- end
415
+ Arel::SelectManager.new(subquery).project(select_value)
416
+ end
371
417
  end
372
418
  end