activerecord 4.2.0 → 6.0.5.1

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 (373) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +852 -801
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +14 -13
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/advisory_lock_base.rb +18 -0
  8. data/lib/active_record/aggregations.rb +267 -249
  9. data/lib/active_record/association_relation.rb +26 -6
  10. data/lib/active_record/associations/alias_tracker.rb +29 -36
  11. data/lib/active_record/associations/association.rb +137 -55
  12. data/lib/active_record/associations/association_scope.rb +110 -132
  13. data/lib/active_record/associations/belongs_to_association.rb +67 -54
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  15. data/lib/active_record/associations/builder/association.rb +27 -40
  16. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  17. data/lib/active_record/associations/builder/collection_association.rb +10 -29
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +58 -70
  19. data/lib/active_record/associations/builder/has_many.rb +8 -4
  20. data/lib/active_record/associations/builder/has_one.rb +46 -5
  21. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  22. data/lib/active_record/associations/collection_association.rb +150 -275
  23. data/lib/active_record/associations/collection_proxy.rb +253 -152
  24. data/lib/active_record/associations/foreign_association.rb +20 -0
  25. data/lib/active_record/associations/has_many_association.rb +35 -84
  26. data/lib/active_record/associations/has_many_through_association.rb +62 -80
  27. data/lib/active_record/associations/has_one_association.rb +62 -49
  28. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  29. data/lib/active_record/associations/join_dependency/join_association.rb +43 -78
  30. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  31. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  32. data/lib/active_record/associations/join_dependency.rb +159 -162
  33. data/lib/active_record/associations/preloader/association.rb +102 -113
  34. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  35. data/lib/active_record/associations/preloader.rb +96 -95
  36. data/lib/active_record/associations/singular_association.rb +18 -45
  37. data/lib/active_record/associations/through_association.rb +49 -24
  38. data/lib/active_record/associations.rb +1737 -1596
  39. data/lib/active_record/attribute_assignment.rb +57 -185
  40. data/lib/active_record/attribute_decorators.rb +39 -17
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +14 -5
  42. data/lib/active_record/attribute_methods/dirty.rb +174 -134
  43. data/lib/active_record/attribute_methods/primary_key.rb +90 -84
  44. data/lib/active_record/attribute_methods/query.rb +6 -5
  45. data/lib/active_record/attribute_methods/read.rb +20 -77
  46. data/lib/active_record/attribute_methods/serialization.rb +40 -21
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -37
  48. data/lib/active_record/attribute_methods/write.rb +33 -56
  49. data/lib/active_record/attribute_methods.rb +124 -143
  50. data/lib/active_record/attributes.rb +213 -74
  51. data/lib/active_record/autosave_association.rb +125 -54
  52. data/lib/active_record/base.rb +60 -49
  53. data/lib/active_record/callbacks.rb +101 -76
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +36 -13
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +810 -291
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +253 -108
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -24
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +171 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -47
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +383 -239
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +736 -235
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +190 -87
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -192
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +536 -600
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -153
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +196 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +71 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -196
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +71 -115
  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 +49 -57
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +17 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +9 -5
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +465 -291
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +565 -363
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +299 -364
  127. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  128. data/lib/active_record/connection_handling.rb +167 -41
  129. data/lib/active_record/core.rb +277 -233
  130. data/lib/active_record/counter_cache.rb +71 -50
  131. data/lib/active_record/database_configurations/database_config.rb +37 -0
  132. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  133. data/lib/active_record/database_configurations/url_config.rb +78 -0
  134. data/lib/active_record/database_configurations.rb +233 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -106
  137. data/lib/active_record/enum.rb +172 -89
  138. data/lib/active_record/errors.rb +189 -53
  139. data/lib/active_record/explain.rb +22 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +11 -6
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +152 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  147. data/lib/active_record/fixtures.rb +225 -497
  148. data/lib/active_record/gem_version.rb +6 -4
  149. data/lib/active_record/inheritance.rb +158 -115
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +99 -98
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  160. data/lib/active_record/middleware/database_selector.rb +74 -0
  161. data/lib/active_record/migration/command_recorder.rb +166 -91
  162. data/lib/active_record/migration/compatibility.rb +244 -0
  163. data/lib/active_record/migration/join_table.rb +8 -7
  164. data/lib/active_record/migration.rb +636 -290
  165. data/lib/active_record/model_schema.rb +344 -112
  166. data/lib/active_record/nested_attributes.rb +265 -215
  167. data/lib/active_record/no_touching.rb +15 -2
  168. data/lib/active_record/null_relation.rb +24 -38
  169. data/lib/active_record/persistence.rb +559 -125
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +44 -30
  172. data/lib/active_record/railtie.rb +166 -47
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +341 -202
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +461 -302
  179. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  180. data/lib/active_record/relation/batches.rb +206 -55
  181. data/lib/active_record/relation/calculations.rb +270 -249
  182. data/lib/active_record/relation/delegation.rb +76 -84
  183. data/lib/active_record/relation/finder_methods.rb +287 -255
  184. data/lib/active_record/relation/from_clause.rb +30 -0
  185. data/lib/active_record/relation/merger.rb +86 -68
  186. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -25
  187. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  188. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  189. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  190. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  191. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  192. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  193. data/lib/active_record/relation/predicate_builder.rb +112 -92
  194. data/lib/active_record/relation/query_attribute.rb +50 -0
  195. data/lib/active_record/relation/query_methods.rb +612 -392
  196. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  197. data/lib/active_record/relation/spawn_methods.rb +18 -17
  198. data/lib/active_record/relation/where_clause.rb +189 -0
  199. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  200. data/lib/active_record/relation.rb +533 -340
  201. data/lib/active_record/result.rb +79 -43
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -20
  207. data/lib/active_record/scoping/default.rb +98 -82
  208. data/lib/active_record/scoping/named.rb +91 -33
  209. data/lib/active_record/scoping.rb +45 -27
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +90 -0
  216. data/lib/active_record/tasks/database_tasks.rb +309 -99
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +58 -89
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -31
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +37 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +243 -0
  222. data/lib/active_record/timestamp.rb +86 -41
  223. data/lib/active_record/touch_later.rb +65 -0
  224. data/lib/active_record/transactions.rb +222 -146
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  227. data/lib/active_record/type/date.rb +4 -41
  228. data/lib/active_record/type/date_time.rb +4 -38
  229. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  230. data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
  231. data/lib/active_record/type/internal/timezone.rb +17 -0
  232. data/lib/active_record/type/json.rb +30 -0
  233. data/lib/active_record/type/serialized.rb +29 -15
  234. data/lib/active_record/type/text.rb +2 -2
  235. data/lib/active_record/type/time.rb +21 -16
  236. data/lib/active_record/type/type_map.rb +16 -19
  237. data/lib/active_record/type/unsigned_integer.rb +9 -8
  238. data/lib/active_record/type.rb +77 -23
  239. data/lib/active_record/type_caster/connection.rb +34 -0
  240. data/lib/active_record/type_caster/map.rb +20 -0
  241. data/lib/active_record/type_caster.rb +9 -0
  242. data/lib/active_record/validations/absence.rb +25 -0
  243. data/lib/active_record/validations/associated.rb +12 -4
  244. data/lib/active_record/validations/length.rb +26 -0
  245. data/lib/active_record/validations/presence.rb +14 -13
  246. data/lib/active_record/validations/uniqueness.rb +43 -46
  247. data/lib/active_record/validations.rb +38 -35
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/active_record.rb +44 -21
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes/attribute.rb +37 -0
  252. data/lib/arel/attributes.rb +22 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes/and.rb +32 -0
  266. data/lib/arel/nodes/ascending.rb +23 -0
  267. data/lib/arel/nodes/binary.rb +52 -0
  268. data/lib/arel/nodes/bind_param.rb +36 -0
  269. data/lib/arel/nodes/case.rb +55 -0
  270. data/lib/arel/nodes/casted.rb +50 -0
  271. data/lib/arel/nodes/comment.rb +29 -0
  272. data/lib/arel/nodes/count.rb +12 -0
  273. data/lib/arel/nodes/delete_statement.rb +45 -0
  274. data/lib/arel/nodes/descending.rb +23 -0
  275. data/lib/arel/nodes/equality.rb +18 -0
  276. data/lib/arel/nodes/extract.rb +24 -0
  277. data/lib/arel/nodes/false.rb +16 -0
  278. data/lib/arel/nodes/full_outer_join.rb +8 -0
  279. data/lib/arel/nodes/function.rb +44 -0
  280. data/lib/arel/nodes/grouping.rb +8 -0
  281. data/lib/arel/nodes/in.rb +8 -0
  282. data/lib/arel/nodes/infix_operation.rb +80 -0
  283. data/lib/arel/nodes/inner_join.rb +8 -0
  284. data/lib/arel/nodes/insert_statement.rb +37 -0
  285. data/lib/arel/nodes/join_source.rb +20 -0
  286. data/lib/arel/nodes/matches.rb +18 -0
  287. data/lib/arel/nodes/named_function.rb +23 -0
  288. data/lib/arel/nodes/node.rb +50 -0
  289. data/lib/arel/nodes/node_expression.rb +13 -0
  290. data/lib/arel/nodes/outer_join.rb +8 -0
  291. data/lib/arel/nodes/over.rb +15 -0
  292. data/lib/arel/nodes/regexp.rb +16 -0
  293. data/lib/arel/nodes/right_outer_join.rb +8 -0
  294. data/lib/arel/nodes/select_core.rb +67 -0
  295. data/lib/arel/nodes/select_statement.rb +41 -0
  296. data/lib/arel/nodes/sql_literal.rb +16 -0
  297. data/lib/arel/nodes/string_join.rb +11 -0
  298. data/lib/arel/nodes/table_alias.rb +27 -0
  299. data/lib/arel/nodes/terminal.rb +16 -0
  300. data/lib/arel/nodes/true.rb +16 -0
  301. data/lib/arel/nodes/unary.rb +45 -0
  302. data/lib/arel/nodes/unary_operation.rb +20 -0
  303. data/lib/arel/nodes/unqualified_column.rb +22 -0
  304. data/lib/arel/nodes/update_statement.rb +41 -0
  305. data/lib/arel/nodes/values_list.rb +9 -0
  306. data/lib/arel/nodes/window.rb +126 -0
  307. data/lib/arel/nodes/with.rb +11 -0
  308. data/lib/arel/nodes.rb +68 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +256 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors/depth_first.rb +203 -0
  316. data/lib/arel/visitors/dot.rb +296 -0
  317. data/lib/arel/visitors/ibm_db.rb +34 -0
  318. data/lib/arel/visitors/informix.rb +62 -0
  319. data/lib/arel/visitors/mssql.rb +156 -0
  320. data/lib/arel/visitors/mysql.rb +83 -0
  321. data/lib/arel/visitors/oracle.rb +158 -0
  322. data/lib/arel/visitors/oracle12.rb +65 -0
  323. data/lib/arel/visitors/postgresql.rb +109 -0
  324. data/lib/arel/visitors/sqlite.rb +38 -0
  325. data/lib/arel/visitors/to_sql.rb +888 -0
  326. data/lib/arel/visitors/visitor.rb +45 -0
  327. data/lib/arel/visitors/where_sql.rb +22 -0
  328. data/lib/arel/visitors.rb +20 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/arel.rb +62 -0
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  334. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  335. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -8
  336. data/lib/rails/generators/active_record/migration.rb +30 -1
  337. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record.rb +7 -5
  340. metadata +174 -63
  341. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  342. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  343. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  344. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  345. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  346. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  347. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  348. data/lib/active_record/attribute.rb +0 -149
  349. data/lib/active_record/attribute_set/builder.rb +0 -86
  350. data/lib/active_record/attribute_set.rb +0 -77
  351. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  352. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  353. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  354. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  355. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  356. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  359. data/lib/active_record/type/big_integer.rb +0 -13
  360. data/lib/active_record/type/binary.rb +0 -50
  361. data/lib/active_record/type/boolean.rb +0 -30
  362. data/lib/active_record/type/decimal.rb +0 -40
  363. data/lib/active_record/type/decorator.rb +0 -14
  364. data/lib/active_record/type/float.rb +0 -19
  365. data/lib/active_record/type/integer.rb +0 -55
  366. data/lib/active_record/type/mutable.rb +0 -16
  367. data/lib/active_record/type/numeric.rb +0 -36
  368. data/lib/active_record/type/string.rb +0 -36
  369. data/lib/active_record/type/time_value.rb +0 -38
  370. data/lib/active_record/type/value.rb +0 -101
  371. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
  372. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
  373. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Calculations
3
5
  # Count the records.
@@ -14,121 +16,134 @@ module ActiveRecord
14
16
  # Person.distinct.count(:age)
15
17
  # # => counts the number of different age values
16
18
  #
17
- # If +count+ is used with +group+, it returns a Hash whose keys represent the aggregated column,
19
+ # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group],
20
+ # it returns a Hash whose keys represent the aggregated column,
18
21
  # and the values are the respective amounts:
19
22
  #
20
23
  # Person.group(:city).count
21
24
  # # => { 'Rome' => 5, 'Paris' => 3 }
22
25
  #
23
- # If +count+ is used with +group+ for multiple columns, it returns a Hash whose
26
+ # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group] for multiple columns, it returns a Hash whose
24
27
  # keys are an array containing the individual values of each column and the value
25
- # of each key would be the +count+.
28
+ # of each key would be the #count.
26
29
  #
27
30
  # Article.group(:status, :category).count
28
31
  # # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
29
32
  # ["published", "business"]=>0, ["published", "technology"]=>2}
30
33
  #
31
- # If +count+ is used with +select+, it will count the selected columns:
34
+ # If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
32
35
  #
33
36
  # Person.select(:age).count
34
37
  # # => counts the number of different age values
35
38
  #
36
- # Note: not all valid +select+ expressions are valid +count+ expressions. The specifics differ
39
+ # Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
37
40
  # between databases. In invalid cases, an error from the database is thrown.
38
- def count(column_name = nil, options = {})
39
- # TODO: Remove options argument as soon we remove support to
40
- # activerecord-deprecated_finders.
41
- column_name, options = nil, column_name if column_name.is_a?(Hash)
42
- calculate(:count, column_name, options)
41
+ def count(column_name = nil)
42
+ if block_given?
43
+ unless column_name.nil?
44
+ raise ArgumentError, "Column name argument is not supported when a block is passed."
45
+ end
46
+
47
+ super()
48
+ else
49
+ calculate(:count, column_name)
50
+ end
43
51
  end
44
52
 
45
53
  # Calculates the average value on a given column. Returns +nil+ if there's
46
- # no row. See +calculate+ for examples with options.
54
+ # no row. See #calculate for examples with options.
47
55
  #
48
56
  # Person.average(:age) # => 35.8
49
- def average(column_name, options = {})
50
- # TODO: Remove options argument as soon we remove support to
51
- # activerecord-deprecated_finders.
52
- calculate(:average, column_name, options)
57
+ def average(column_name)
58
+ calculate(:average, column_name)
53
59
  end
54
60
 
55
61
  # Calculates the minimum value on a given column. The value is returned
56
62
  # with the same data type of the column, or +nil+ if there's no row. See
57
- # +calculate+ for examples with options.
63
+ # #calculate for examples with options.
58
64
  #
59
65
  # Person.minimum(:age) # => 7
60
- def minimum(column_name, options = {})
61
- # TODO: Remove options argument as soon we remove support to
62
- # activerecord-deprecated_finders.
63
- calculate(:minimum, column_name, options)
66
+ def minimum(column_name)
67
+ calculate(:minimum, column_name)
64
68
  end
65
69
 
66
70
  # Calculates the maximum value on a given column. The value is returned
67
71
  # with the same data type of the column, or +nil+ if there's no row. See
68
- # +calculate+ for examples with options.
72
+ # #calculate for examples with options.
69
73
  #
70
74
  # Person.maximum(:age) # => 93
71
- def maximum(column_name, options = {})
72
- # TODO: Remove options argument as soon we remove support to
73
- # activerecord-deprecated_finders.
74
- calculate(:maximum, column_name, options)
75
+ def maximum(column_name)
76
+ calculate(:maximum, column_name)
75
77
  end
76
78
 
77
79
  # Calculates the sum of values on a given column. The value is returned
78
- # with the same data type of the column, 0 if there's no row. See
79
- # +calculate+ for examples with options.
80
+ # with the same data type of the column, +0+ if there's no row. See
81
+ # #calculate for examples with options.
80
82
  #
81
83
  # Person.sum(:age) # => 4562
82
- def sum(*args)
83
- calculate(:sum, *args)
84
+ def sum(column_name = nil)
85
+ if block_given?
86
+ unless column_name.nil?
87
+ raise ArgumentError, "Column name argument is not supported when a block is passed."
88
+ end
89
+
90
+ super()
91
+ else
92
+ calculate(:sum, column_name)
93
+ end
84
94
  end
85
95
 
86
- # This calculates aggregate values in the given column. Methods for count, sum, average,
87
- # minimum, and maximum have been added as shortcuts.
96
+ # This calculates aggregate values in the given column. Methods for #count, #sum, #average,
97
+ # #minimum, and #maximum have been added as shortcuts.
88
98
  #
89
- # There are two basic forms of output:
99
+ # Person.calculate(:count, :all) # The same as Person.count
100
+ # Person.average(:age) # SELECT AVG(age) FROM people...
90
101
  #
91
- # * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
92
- # for AVG, and the given column's type for everything else.
102
+ # # Selects the minimum age for any family without any minors
103
+ # Person.group(:last_name).having("min(age) > 17").minimum(:age)
93
104
  #
94
- # * Grouped values: This returns an ordered hash of the values and groups them. It
95
- # takes either a column name, or the name of a belongs_to association.
105
+ # Person.sum("2 * age")
96
106
  #
97
- # values = Person.group('last_name').maximum(:age)
98
- # puts values["Drake"]
99
- # # => 43
107
+ # There are two basic forms of output:
100
108
  #
101
- # drake = Family.find_by(last_name: 'Drake')
102
- # values = Person.group(:family).maximum(:age) # Person belongs_to :family
103
- # puts values[drake]
104
- # # => 43
109
+ # * Single aggregate value: The single value is type cast to Integer for COUNT, Float
110
+ # for AVG, and the given column's type for everything else.
105
111
  #
106
- # values.each do |family, max_age|
107
- # ...
108
- # end
112
+ # * Grouped values: This returns an ordered hash of the values and groups them. It
113
+ # takes either a column name, or the name of a belongs_to association.
109
114
  #
110
- # Person.calculate(:count, :all) # The same as Person.count
111
- # Person.average(:age) # SELECT AVG(age) FROM people...
115
+ # values = Person.group('last_name').maximum(:age)
116
+ # puts values["Drake"]
117
+ # # => 43
112
118
  #
113
- # # Selects the minimum age for any family without any minors
114
- # Person.group(:last_name).having("min(age) > 17").minimum(:age)
119
+ # drake = Family.find_by(last_name: 'Drake')
120
+ # values = Person.group(:family).maximum(:age) # Person belongs_to :family
121
+ # puts values[drake]
122
+ # # => 43
115
123
  #
116
- # Person.sum("2 * age")
117
- def calculate(operation, column_name, options = {})
118
- # TODO: Remove options argument as soon we remove support to
119
- # activerecord-deprecated_finders.
120
- if column_name.is_a?(Symbol) && attribute_alias?(column_name)
121
- column_name = attribute_alias(column_name)
122
- end
123
-
124
+ # values.each do |family, max_age|
125
+ # ...
126
+ # end
127
+ def calculate(operation, column_name)
124
128
  if has_include?(column_name)
125
- construct_relation_for_association_calculations.calculate(operation, column_name, options)
129
+ relation = apply_join_dependency
130
+
131
+ if operation.to_s.downcase == "count"
132
+ unless distinct_value || distinct_select?(column_name || select_for_count)
133
+ relation.distinct!
134
+ relation.select_values = [ klass.primary_key || table[Arel.star] ]
135
+ end
136
+ # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
137
+ relation.order_values = [] if group_values.empty?
138
+ end
139
+
140
+ relation.calculate(operation, column_name)
126
141
  else
127
- perform_calculation(operation, column_name, options)
142
+ perform_calculation(operation, column_name)
128
143
  end
129
144
  end
130
145
 
131
- # Use <tt>pluck</tt> as a shortcut to select one or more attributes without
146
+ # Use #pluck as a shortcut to select one or more attributes without
132
147
  # loading a bunch of records just to grab the attributes you want.
133
148
  #
134
149
  # Person.pluck(:name)
@@ -137,19 +152,19 @@ module ActiveRecord
137
152
  #
138
153
  # Person.all.map(&:name)
139
154
  #
140
- # Pluck returns an <tt>Array</tt> of attribute values type-casted to match
155
+ # Pluck returns an Array of attribute values type-casted to match
141
156
  # the plucked column names, if they can be deduced. Plucking an SQL fragment
142
157
  # returns String values by default.
143
158
  #
144
- # Person.pluck(:id)
145
- # # SELECT people.id FROM people
146
- # # => [1, 2, 3]
159
+ # Person.pluck(:name)
160
+ # # SELECT people.name FROM people
161
+ # # => ['David', 'Jeremy', 'Jose']
147
162
  #
148
163
  # Person.pluck(:id, :name)
149
164
  # # SELECT people.id, people.name FROM people
150
165
  # # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
151
166
  #
152
- # Person.pluck('DISTINCT role')
167
+ # Person.distinct.pluck(:role)
153
168
  # # SELECT DISTINCT role FROM people
154
169
  # # => ['admin', 'member', 'guest']
155
170
  #
@@ -157,31 +172,47 @@ module ActiveRecord
157
172
  # # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
158
173
  # # => [2, 3]
159
174
  #
160
- # Person.pluck('DATEDIFF(updated_at, created_at)')
175
+ # Person.pluck(Arel.sql('DATEDIFF(updated_at, created_at)'))
161
176
  # # SELECT DATEDIFF(updated_at, created_at) FROM people
162
177
  # # => ['0', '27761', '173']
163
178
  #
179
+ # See also #ids.
180
+ #
164
181
  def pluck(*column_names)
165
- column_names.map! do |column_name|
166
- if column_name.is_a?(Symbol) && attribute_alias?(column_name)
167
- attribute_alias(column_name)
168
- else
169
- column_name.to_s
170
- end
182
+ if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
183
+ return records.pluck(*column_names)
171
184
  end
172
185
 
173
186
  if has_include?(column_names.first)
174
- construct_relation_for_association_calculations.pluck(*column_names)
187
+ relation = apply_join_dependency
188
+ relation.pluck(*column_names)
175
189
  else
190
+ klass.disallow_raw_sql!(column_names)
176
191
  relation = spawn
177
- relation.select_values = column_names.map { |cn|
178
- columns_hash.key?(cn) ? arel_table[cn] : cn
179
- }
180
- result = klass.connection.select_all(relation.arel, nil, relation.arel.bind_values + bind_values)
181
- result.cast_values(klass.column_types)
192
+ relation.select_values = column_names
193
+ result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
194
+ result.cast_values(klass.attribute_types)
182
195
  end
183
196
  end
184
197
 
198
+ # Pick the value(s) from the named column(s) in the current relation.
199
+ # This is short-hand for <tt>relation.limit(1).pluck(*column_names).first</tt>, and is primarily useful
200
+ # when you have a relation that's already narrowed down to a single row.
201
+ #
202
+ # Just like #pluck, #pick will only load the actual value, not the entire record object, so it's also
203
+ # more efficient. The value is, again like with pluck, typecast by the column type.
204
+ #
205
+ # Person.where(id: 1).pick(:name)
206
+ # # SELECT people.name FROM people WHERE id = 1 LIMIT 1
207
+ # # => 'David'
208
+ #
209
+ # Person.where(id: 1).pick(:name, :email_address)
210
+ # # SELECT people.name, people.email_address FROM people WHERE id = 1 LIMIT 1
211
+ # # => [ 'David', 'david@loudthinking.com' ]
212
+ def pick(*column_names)
213
+ limit(1).pluck(*column_names).first
214
+ end
215
+
185
216
  # Pluck all the ID's for the relation using the table's primary key
186
217
  #
187
218
  # Person.ids # SELECT people.id FROM people
@@ -191,213 +222,203 @@ module ActiveRecord
191
222
  end
192
223
 
193
224
  private
225
+ def has_include?(column_name)
226
+ eager_loading? || (includes_values.present? && column_name && column_name != :all)
227
+ end
194
228
 
195
- def has_include?(column_name)
196
- eager_loading? || (includes_values.present? && ((column_name && column_name != :all) || references_eager_loaded_tables?))
197
- end
198
-
199
- def perform_calculation(operation, column_name, options = {})
200
- # TODO: Remove options argument as soon we remove support to
201
- # activerecord-deprecated_finders.
202
- operation = operation.to_s.downcase
203
-
204
- # If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count)
205
- distinct = self.distinct_value
206
-
207
- if operation == "count"
208
- column_name ||= select_for_count
209
-
210
- unless arel.ast.grep(Arel::Nodes::OuterJoin).empty?
211
- distinct = true
229
+ def perform_calculation(operation, column_name)
230
+ operation = operation.to_s.downcase
231
+
232
+ # If #count is used with #distinct (i.e. `relation.distinct.count`) it is
233
+ # considered distinct.
234
+ distinct = distinct_value
235
+
236
+ if operation == "count"
237
+ column_name ||= select_for_count
238
+ if column_name == :all
239
+ if !distinct
240
+ distinct = distinct_select?(select_for_count) if group_values.empty?
241
+ elsif group_values.any? || select_values.empty? && order_values.empty?
242
+ column_name = primary_key
243
+ end
244
+ elsif distinct_select?(column_name)
245
+ distinct = nil
246
+ end
212
247
  end
213
248
 
214
- column_name = primary_key if column_name == :all && distinct
215
- distinct = nil if column_name =~ /\s*DISTINCT[\s(]+/i
249
+ if group_values.any?
250
+ execute_grouped_calculation(operation, column_name, distinct)
251
+ else
252
+ execute_simple_calculation(operation, column_name, distinct)
253
+ end
216
254
  end
217
255
 
218
- if group_values.any?
219
- execute_grouped_calculation(operation, column_name, distinct)
220
- else
221
- execute_simple_calculation(operation, column_name, distinct)
256
+ def distinct_select?(column_name)
257
+ column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
222
258
  end
223
- end
224
259
 
225
- def aggregate_column(column_name)
226
- if @klass.column_names.include?(column_name.to_s)
227
- Arel::Attribute.new(@klass.unscoped.table, column_name)
228
- else
229
- Arel.sql(column_name == :all ? "*" : column_name.to_s)
260
+ def aggregate_column(column_name)
261
+ return column_name if Arel::Expressions === column_name
262
+
263
+ arel_column(column_name.to_s) do |name|
264
+ Arel.sql(column_name == :all ? "*" : name)
265
+ end
230
266
  end
231
- end
232
267
 
233
- def operation_over_aggregate_column(column, operation, distinct)
234
- operation == 'count' ? column.count(distinct) : column.send(operation)
235
- end
268
+ def operation_over_aggregate_column(column, operation, distinct)
269
+ operation == "count" ? column.count(distinct) : column.send(operation)
270
+ end
236
271
 
237
- def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
238
- # Postgresql doesn't like ORDER BY when there are no GROUP BY
239
- relation = unscope(:order)
272
+ def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
273
+ column_alias = column_name
240
274
 
241
- column_alias = column_name
275
+ if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
276
+ # Shortcut when limit is zero.
277
+ return 0 if limit_value == 0
242
278
 
243
- bind_values = nil
279
+ query_builder = build_count_subquery(spawn, column_name, distinct)
280
+ else
281
+ # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
282
+ relation = unscope(:order).distinct!(false)
244
283
 
245
- if operation == "count" && (relation.limit_value || relation.offset_value)
246
- # Shortcut when limit is zero.
247
- return 0 if relation.limit_value == 0
284
+ column = aggregate_column(column_name)
248
285
 
249
- query_builder = build_count_subquery(relation, column_name, distinct)
250
- bind_values = query_builder.bind_values + relation.bind_values
251
- else
252
- column = aggregate_column(column_name)
286
+ select_value = operation_over_aggregate_column(column, operation, distinct)
287
+ if operation == "sum" && distinct
288
+ select_value.distinct = true
289
+ end
253
290
 
254
- select_value = operation_over_aggregate_column(column, operation, distinct)
291
+ column_alias = select_value.alias
292
+ column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
293
+ relation.select_values = [select_value]
255
294
 
256
- column_alias = select_value.alias
257
- column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
258
- relation.select_values = [select_value]
295
+ query_builder = relation.arel
296
+ end
259
297
 
260
- query_builder = relation.arel
261
- bind_values = query_builder.bind_values + relation.bind_values
262
- end
298
+ result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil) }
299
+ row = result.first
300
+ value = row && row.values.first
301
+ type = result.column_types.fetch(column_alias) do
302
+ type_for(column_name)
303
+ end
263
304
 
264
- result = @klass.connection.select_all(query_builder, nil, bind_values)
265
- row = result.first
266
- value = row && row.values.first
267
- column = result.column_types.fetch(column_alias) do
268
- type_for(column_name)
305
+ type_cast_calculated_value(value, type, operation)
269
306
  end
270
307
 
271
- type_cast_calculated_value(value, column, operation)
272
- end
273
-
274
- def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
275
- group_attrs = group_values
308
+ def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
309
+ group_fields = group_values
276
310
 
277
- if group_attrs.first.respond_to?(:to_sym)
278
- association = @klass._reflect_on_association(group_attrs.first)
279
- associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
280
- group_fields = Array(associated ? association.foreign_key : group_attrs)
281
- else
282
- group_fields = group_attrs
283
- end
311
+ if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
312
+ association = klass._reflect_on_association(group_fields.first)
313
+ associated = association && association.belongs_to? # only count belongs_to associations
314
+ group_fields = Array(association.foreign_key) if associated
315
+ end
316
+ group_fields = arel_columns(group_fields)
284
317
 
285
- group_aliases = group_fields.map { |field|
286
- column_alias_for(field)
287
- }
288
- group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
289
- [aliaz, field]
290
- }
318
+ group_aliases = group_fields.map { |field|
319
+ field = connection.visitor.compile(field) if Arel.arel_node?(field)
320
+ column_alias_for(field.to_s.downcase)
321
+ }
322
+ group_columns = group_aliases.zip(group_fields)
323
+
324
+ aggregate_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
325
+
326
+ select_values = [
327
+ operation_over_aggregate_column(
328
+ aggregate_column(column_name),
329
+ operation,
330
+ distinct).as(aggregate_alias)
331
+ ]
332
+ select_values += self.select_values unless having_clause.empty?
333
+
334
+ select_values.concat group_columns.map { |aliaz, field|
335
+ if field.respond_to?(:as)
336
+ field.as(aliaz)
337
+ else
338
+ "#{field} AS #{aliaz}"
339
+ end
340
+ }
291
341
 
292
- group = group_fields
342
+ relation = except(:group).distinct!(false)
343
+ relation.group_values = group_fields
344
+ relation.select_values = select_values
293
345
 
294
- if operation == 'count' && column_name == :all
295
- aggregate_alias = 'count_all'
296
- else
297
- aggregate_alias = column_alias_for([operation, column_name].join(' '))
298
- end
346
+ calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
299
347
 
300
- select_values = [
301
- operation_over_aggregate_column(
302
- aggregate_column(column_name),
303
- operation,
304
- distinct).as(aggregate_alias)
305
- ]
306
- select_values += select_values unless having_values.empty?
307
-
308
- select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
309
- if field.respond_to?(:as)
310
- field.as(aliaz)
311
- else
312
- "#{field} AS #{aliaz}"
348
+ if association
349
+ key_ids = calculated_data.collect { |row| row[group_aliases.first] }
350
+ key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
351
+ key_records = Hash[key_records.map { |r| [r.id, r] }]
313
352
  end
314
- }
315
-
316
- relation = except(:group)
317
- relation.group_values = group
318
- relation.select_values = select_values
319
-
320
- calculated_data = @klass.connection.select_all(relation, nil, relation.arel.bind_values + bind_values)
321
353
 
322
- if association
323
- key_ids = calculated_data.collect { |row| row[group_aliases.first] }
324
- key_records = association.klass.base_class.find(key_ids)
325
- key_records = Hash[key_records.map { |r| [r.id, r] }]
354
+ Hash[calculated_data.map do |row|
355
+ key = group_columns.map { |aliaz, col_name|
356
+ type = type_for(col_name) do
357
+ calculated_data.column_types.fetch(aliaz, Type.default_value)
358
+ end
359
+ type_cast_calculated_value(row[aliaz], type)
360
+ }
361
+ key = key.first if key.size == 1
362
+ key = key_records[key] if associated
363
+
364
+ type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
365
+ [key, type_cast_calculated_value(row[aggregate_alias], type, operation)]
366
+ end]
326
367
  end
327
368
 
328
- Hash[calculated_data.map do |row|
329
- key = group_columns.map { |aliaz, col_name|
330
- column = calculated_data.column_types.fetch(aliaz) do
331
- type_for(col_name)
332
- end
333
- type_cast_calculated_value(row[aliaz], column)
334
- }
335
- key = key.first if key.size == 1
336
- key = key_records[key] if associated
337
-
338
- column_type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
339
- [key, type_cast_calculated_value(row[aggregate_alias], column_type, operation)]
340
- end]
341
- end
342
-
343
- # Converts the given keys to the value that the database adapter returns as
344
- # a usable column name:
345
- #
346
- # column_alias_for("users.id") # => "users_id"
347
- # column_alias_for("sum(id)") # => "sum_id"
348
- # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
349
- # column_alias_for("count(*)") # => "count_all"
350
- # column_alias_for("count", "id") # => "count_id"
351
- def column_alias_for(keys)
352
- if keys.respond_to? :name
353
- keys = "#{keys.relation.name}.#{keys.name}"
369
+ # Converts the given field to the value that the database adapter returns as
370
+ # a usable column name:
371
+ #
372
+ # column_alias_for("users.id") # => "users_id"
373
+ # column_alias_for("sum(id)") # => "sum_id"
374
+ # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
375
+ # column_alias_for("count(*)") # => "count_all"
376
+ def column_alias_for(field)
377
+ column_alias = +field
378
+ column_alias.gsub!(/\*/, "all")
379
+ column_alias.gsub!(/\W+/, " ")
380
+ column_alias.strip!
381
+ column_alias.gsub!(/ +/, "_")
382
+
383
+ connection.table_alias_for(column_alias)
354
384
  end
355
385
 
356
- table_name = keys.to_s.downcase
357
- table_name.gsub!(/\*/, 'all')
358
- table_name.gsub!(/\W+/, ' ')
359
- table_name.strip!
360
- table_name.gsub!(/ +/, '_')
361
-
362
- @klass.connection.table_alias_for(table_name)
363
- end
364
-
365
- def type_for(field)
366
- field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
367
- @klass.type_for_attribute(field_name)
368
- end
386
+ def type_for(field, &block)
387
+ field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split(".").last
388
+ @klass.type_for_attribute(field_name, &block)
389
+ end
369
390
 
370
- def type_cast_calculated_value(value, type, operation = nil)
371
- case operation
372
- when 'count' then value.to_i
373
- when 'sum' then type.type_cast_from_database(value || 0)
374
- when 'average' then value.respond_to?(:to_d) ? value.to_d : value
375
- else type.type_cast_from_database(value)
391
+ def type_cast_calculated_value(value, type, operation = nil)
392
+ case operation
393
+ when "count" then value.to_i
394
+ when "sum" then type.deserialize(value || 0)
395
+ when "average" then value&.respond_to?(:to_d) ? value.to_d : value
396
+ else type.deserialize(value)
397
+ end
376
398
  end
377
- end
378
399
 
379
- # TODO: refactor to allow non-string `select_values` (eg. Arel nodes).
380
- def select_for_count
381
- if select_values.present?
382
- select_values.join(", ")
383
- else
384
- :all
400
+ def select_for_count
401
+ if select_values.present?
402
+ return select_values.first if select_values.one?
403
+ select_values.join(", ")
404
+ else
405
+ :all
406
+ end
385
407
  end
386
- end
387
408
 
388
- def build_count_subquery(relation, column_name, distinct)
389
- column_alias = Arel.sql('count_column')
390
- subquery_alias = Arel.sql('subquery_for_count')
409
+ def build_count_subquery(relation, column_name, distinct)
410
+ if column_name == :all
411
+ column_alias = Arel.star
412
+ relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
413
+ else
414
+ column_alias = Arel.sql("count_column")
415
+ relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
416
+ end
391
417
 
392
- aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
393
- relation.select_values = [aliased_column]
394
- arel = relation.arel
395
- subquery = arel.as(subquery_alias)
418
+ subquery_alias = Arel.sql("subquery_for_count")
419
+ select_value = operation_over_aggregate_column(column_alias, "count", false)
396
420
 
397
- sm = Arel::SelectManager.new relation.engine
398
- sm.bind_values = arel.bind_values
399
- select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
400
- sm.project(select_value).from(subquery)
401
- end
421
+ relation.build_subquery(subquery_alias, select_value)
422
+ end
402
423
  end
403
424
  end