activerecord 4.2.11.1 → 6.0.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (373) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +711 -1547
  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 +266 -251
  9. data/lib/active_record/association_relation.rb +20 -13
  10. data/lib/active_record/associations/alias_tracker.rb +29 -36
  11. data/lib/active_record/associations/association.rb +128 -57
  12. data/lib/active_record/associations/association_scope.rb +103 -132
  13. data/lib/active_record/associations/belongs_to_association.rb +65 -60
  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 -33
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -66
  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 +136 -288
  23. data/lib/active_record/associations/collection_proxy.rb +241 -147
  24. data/lib/active_record/associations/foreign_association.rb +10 -1
  25. data/lib/active_record/associations/has_many_association.rb +34 -98
  26. data/lib/active_record/associations/has_many_through_association.rb +60 -87
  27. data/lib/active_record/associations/has_one_association.rb +61 -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 +38 -86
  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 +149 -166
  33. data/lib/active_record/associations/preloader/association.rb +90 -123
  34. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  35. data/lib/active_record/associations/preloader.rb +90 -93
  36. data/lib/active_record/associations/singular_association.rb +18 -39
  37. data/lib/active_record/associations/through_association.rb +38 -18
  38. data/lib/active_record/associations.rb +1737 -1597
  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 +13 -9
  42. data/lib/active_record/attribute_methods/dirty.rb +174 -144
  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 +57 -37
  48. data/lib/active_record/attribute_methods/write.rb +32 -55
  49. data/lib/active_record/attribute_methods.rb +120 -135
  50. data/lib/active_record/attributes.rb +213 -82
  51. data/lib/active_record/autosave_association.rb +97 -41
  52. data/lib/active_record/base.rb +57 -45
  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 +23 -12
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +804 -297
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +240 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -24
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -47
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +371 -242
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +694 -256
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +190 -83
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +473 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +507 -639
  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 +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -181
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -114
  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 -58
  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 +4 -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 +9 -22
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  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 +51 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  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 +462 -296
  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 +558 -356
  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 -349
  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 +252 -230
  130. data/lib/active_record/counter_cache.rb +70 -49
  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 +163 -86
  138. data/lib/active_record/errors.rb +188 -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 +10 -5
  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 +227 -501
  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 +21 -3
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +86 -96
  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 +623 -305
  165. data/lib/active_record/model_schema.rb +313 -112
  166. data/lib/active_record/nested_attributes.rb +263 -223
  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 +557 -126
  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 +143 -44
  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 +331 -185
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +430 -281
  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 +268 -254
  182. data/lib/active_record/relation/delegation.rb +75 -84
  183. data/lib/active_record/relation/finder_methods.rb +285 -241
  184. data/lib/active_record/relation/from_clause.rb +30 -0
  185. data/lib/active_record/relation/merger.rb +78 -88
  186. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
  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 +110 -119
  194. data/lib/active_record/relation/query_attribute.rb +50 -0
  195. data/lib/active_record/relation/query_methods.rb +603 -397
  196. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  197. data/lib/active_record/relation/spawn_methods.rb +11 -14
  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 +530 -341
  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 -17
  207. data/lib/active_record/scoping/default.rb +98 -83
  208. data/lib/active_record/scoping/named.rb +86 -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 +307 -100
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +55 -100
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +80 -41
  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 +225 -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 +223 -157
  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 -45
  228. data/lib/active_record/type/date_time.rb +4 -49
  229. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  230. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  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 +23 -15
  234. data/lib/active_record/type/text.rb +2 -2
  235. data/lib/active_record/type/time.rb +11 -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 +42 -55
  247. data/lib/active_record/validations.rb +38 -35
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/active_record.rb +42 -22
  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 -2
  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/model/templates/{module.rb → module.rb.tt} +0 -0
  340. data/lib/rails/generators/active_record.rb +7 -5
  341. metadata +168 -59
  342. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  343. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  344. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  345. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  346. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  347. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  348. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  349. data/lib/active_record/attribute.rb +0 -163
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/attribute_set.rb +0 -81
  352. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  353. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  354. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  355. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  356. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  357. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  358. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  359. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  360. data/lib/active_record/type/big_integer.rb +0 -13
  361. data/lib/active_record/type/binary.rb +0 -50
  362. data/lib/active_record/type/boolean.rb +0 -31
  363. data/lib/active_record/type/decimal.rb +0 -64
  364. data/lib/active_record/type/decorator.rb +0 -14
  365. data/lib/active_record/type/float.rb +0 -19
  366. data/lib/active_record/type/integer.rb +0 -59
  367. data/lib/active_record/type/mutable.rb +0 -16
  368. data/lib/active_record/type/numeric.rb +0 -36
  369. data/lib/active_record/type/string.rb +0 -40
  370. data/lib/active_record/type/time_value.rb +0 -38
  371. data/lib/active_record/type/value.rb +0 -110
  372. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  373. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Calculations
3
5
  # Count the records.
@@ -14,127 +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
- if options.present? && !ActiveRecord.const_defined?(:DeprecatedFinders)
40
- raise ArgumentError, "Relation#count does not support finder options anymore. " \
41
- "Please build a scope and then call count on it or use the " \
42
- "activerecord-deprecated_finders gem to enable this functionality."
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
43
46
 
47
+ super()
48
+ else
49
+ calculate(:count, column_name)
44
50
  end
45
-
46
- # TODO: Remove options argument as soon we remove support to
47
- # activerecord-deprecated_finders.
48
- calculate(:count, column_name, options)
49
51
  end
50
52
 
51
53
  # Calculates the average value on a given column. Returns +nil+ if there's
52
- # no row. See +calculate+ for examples with options.
54
+ # no row. See #calculate for examples with options.
53
55
  #
54
56
  # Person.average(:age) # => 35.8
55
- def average(column_name, options = {})
56
- # TODO: Remove options argument as soon we remove support to
57
- # activerecord-deprecated_finders.
58
- calculate(:average, column_name, options)
57
+ def average(column_name)
58
+ calculate(:average, column_name)
59
59
  end
60
60
 
61
61
  # Calculates the minimum value on a given column. The value is returned
62
62
  # with the same data type of the column, or +nil+ if there's no row. See
63
- # +calculate+ for examples with options.
63
+ # #calculate for examples with options.
64
64
  #
65
65
  # Person.minimum(:age) # => 7
66
- def minimum(column_name, options = {})
67
- # TODO: Remove options argument as soon we remove support to
68
- # activerecord-deprecated_finders.
69
- calculate(:minimum, column_name, options)
66
+ def minimum(column_name)
67
+ calculate(:minimum, column_name)
70
68
  end
71
69
 
72
70
  # Calculates the maximum value on a given column. The value is returned
73
71
  # with the same data type of the column, or +nil+ if there's no row. See
74
- # +calculate+ for examples with options.
72
+ # #calculate for examples with options.
75
73
  #
76
74
  # Person.maximum(:age) # => 93
77
- def maximum(column_name, options = {})
78
- # TODO: Remove options argument as soon we remove support to
79
- # activerecord-deprecated_finders.
80
- calculate(:maximum, column_name, options)
75
+ def maximum(column_name)
76
+ calculate(:maximum, column_name)
81
77
  end
82
78
 
83
79
  # Calculates the sum of values on a given column. The value is returned
84
- # with the same data type of the column, 0 if there's no row. See
85
- # +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.
86
82
  #
87
83
  # Person.sum(:age) # => 4562
88
- def sum(*args)
89
- 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
90
94
  end
91
95
 
92
- # This calculates aggregate values in the given column. Methods for count, sum, average,
93
- # 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.
94
98
  #
95
- # 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...
96
101
  #
97
- # * Single aggregate value: The single value is type cast to Integer for COUNT, Float
98
- # 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)
99
104
  #
100
- # * Grouped values: This returns an ordered hash of the values and groups them. It
101
- # takes either a column name, or the name of a belongs_to association.
105
+ # Person.sum("2 * age")
102
106
  #
103
- # values = Person.group('last_name').maximum(:age)
104
- # puts values["Drake"]
105
- # # => 43
107
+ # There are two basic forms of output:
106
108
  #
107
- # drake = Family.find_by(last_name: 'Drake')
108
- # values = Person.group(:family).maximum(:age) # Person belongs_to :family
109
- # puts values[drake]
110
- # # => 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.
111
111
  #
112
- # values.each do |family, max_age|
113
- # ...
114
- # 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.
115
114
  #
116
- # Person.calculate(:count, :all) # The same as Person.count
117
- # Person.average(:age) # SELECT AVG(age) FROM people...
115
+ # values = Person.group('last_name').maximum(:age)
116
+ # puts values["Drake"]
117
+ # # => 43
118
118
  #
119
- # # Selects the minimum age for any family without any minors
120
- # 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
121
123
  #
122
- # Person.sum("2 * age")
123
- def calculate(operation, column_name, options = {})
124
- # TODO: Remove options argument as soon we remove support to
125
- # activerecord-deprecated_finders.
126
- if column_name.is_a?(Symbol) && attribute_alias?(column_name)
127
- column_name = attribute_alias(column_name)
128
- end
129
-
124
+ # values.each do |family, max_age|
125
+ # ...
126
+ # end
127
+ def calculate(operation, column_name)
130
128
  if has_include?(column_name)
131
- 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 = []
138
+ end
139
+
140
+ relation.calculate(operation, column_name)
132
141
  else
133
- perform_calculation(operation, column_name, options)
142
+ perform_calculation(operation, column_name)
134
143
  end
135
144
  end
136
145
 
137
- # 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
138
147
  # loading a bunch of records just to grab the attributes you want.
139
148
  #
140
149
  # Person.pluck(:name)
@@ -143,19 +152,19 @@ module ActiveRecord
143
152
  #
144
153
  # Person.all.map(&:name)
145
154
  #
146
- # 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
147
156
  # the plucked column names, if they can be deduced. Plucking an SQL fragment
148
157
  # returns String values by default.
149
158
  #
150
- # Person.pluck(:id)
151
- # # SELECT people.id FROM people
152
- # # => [1, 2, 3]
159
+ # Person.pluck(:name)
160
+ # # SELECT people.name FROM people
161
+ # # => ['David', 'Jeremy', 'Jose']
153
162
  #
154
163
  # Person.pluck(:id, :name)
155
164
  # # SELECT people.id, people.name FROM people
156
165
  # # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
157
166
  #
158
- # Person.pluck('DISTINCT role')
167
+ # Person.distinct.pluck(:role)
159
168
  # # SELECT DISTINCT role FROM people
160
169
  # # => ['admin', 'member', 'guest']
161
170
  #
@@ -163,31 +172,47 @@ module ActiveRecord
163
172
  # # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
164
173
  # # => [2, 3]
165
174
  #
166
- # Person.pluck('DATEDIFF(updated_at, created_at)')
175
+ # Person.pluck(Arel.sql('DATEDIFF(updated_at, created_at)'))
167
176
  # # SELECT DATEDIFF(updated_at, created_at) FROM people
168
177
  # # => ['0', '27761', '173']
169
178
  #
179
+ # See also #ids.
180
+ #
170
181
  def pluck(*column_names)
171
- column_names.map! do |column_name|
172
- if column_name.is_a?(Symbol) && attribute_alias?(column_name)
173
- attribute_alias(column_name)
174
- else
175
- column_name.to_s
176
- end
182
+ if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
183
+ return records.pluck(*column_names)
177
184
  end
178
185
 
179
186
  if has_include?(column_names.first)
180
- construct_relation_for_association_calculations.pluck(*column_names)
187
+ relation = apply_join_dependency
188
+ relation.pluck(*column_names)
181
189
  else
190
+ klass.disallow_raw_sql!(column_names)
182
191
  relation = spawn
183
- relation.select_values = column_names.map { |cn|
184
- columns_hash.key?(cn) ? arel_table[cn] : cn
185
- }
186
- result = klass.connection.select_all(relation.arel, nil, relation.arel.bind_values + bind_values)
187
- 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)
188
195
  end
189
196
  end
190
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
+
191
216
  # Pluck all the ID's for the relation using the table's primary key
192
217
  #
193
218
  # Person.ids # SELECT people.id FROM people
@@ -197,214 +222,203 @@ module ActiveRecord
197
222
  end
198
223
 
199
224
  private
225
+ def has_include?(column_name)
226
+ eager_loading? || (includes_values.present? && column_name && column_name != :all)
227
+ end
200
228
 
201
- def has_include?(column_name)
202
- eager_loading? || (includes_values.present? && ((column_name && column_name != :all) || references_eager_loaded_tables?))
203
- end
204
-
205
- def perform_calculation(operation, column_name, options = {})
206
- # TODO: Remove options argument as soon we remove support to
207
- # activerecord-deprecated_finders.
208
- operation = operation.to_s.downcase
209
-
210
- # If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count)
211
- distinct = self.distinct_value
212
-
213
- if operation == "count"
214
- column_name ||= select_for_count
215
-
216
- unless arel.ast.grep(Arel::Nodes::OuterJoin).empty?
217
- 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
218
247
  end
219
248
 
220
- column_name = primary_key if column_name == :all && distinct
221
- 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
222
254
  end
223
255
 
224
- if group_values.any?
225
- execute_grouped_calculation(operation, column_name, distinct)
226
- else
227
- 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)
228
258
  end
229
- end
230
259
 
231
- def aggregate_column(column_name)
232
- if @klass.column_names.include?(column_name.to_s)
233
- Arel::Attribute.new(@klass.unscoped.table, column_name)
234
- else
235
- 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
236
266
  end
237
- end
238
267
 
239
- def operation_over_aggregate_column(column, operation, distinct)
240
- operation == 'count' ? column.count(distinct) : column.send(operation)
241
- end
268
+ def operation_over_aggregate_column(column, operation, distinct)
269
+ operation == "count" ? column.count(distinct) : column.send(operation)
270
+ end
242
271
 
243
- def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
244
- # Postgresql doesn't like ORDER BY when there are no GROUP BY
245
- relation = unscope(:order)
272
+ def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
273
+ column_alias = column_name
246
274
 
247
- 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
248
278
 
249
- 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)
250
283
 
251
- if operation == "count" && (relation.limit_value || relation.offset_value)
252
- # Shortcut when limit is zero.
253
- return 0 if relation.limit_value == 0
284
+ column = aggregate_column(column_name)
254
285
 
255
- query_builder = build_count_subquery(relation, column_name, distinct)
256
- bind_values = query_builder.bind_values + relation.bind_values
257
- else
258
- 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
259
290
 
260
- 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]
261
294
 
262
- column_alias = select_value.alias
263
- column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
264
- relation.select_values = [select_value]
295
+ query_builder = relation.arel
296
+ end
265
297
 
266
- query_builder = relation.arel
267
- bind_values = query_builder.bind_values + relation.bind_values
268
- 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
269
304
 
270
- result = @klass.connection.select_all(query_builder, nil, bind_values)
271
- row = result.first
272
- value = row && row.values.first
273
- column = result.column_types.fetch(column_alias) do
274
- type_for(column_name)
305
+ type_cast_calculated_value(value, type, operation)
275
306
  end
276
307
 
277
- type_cast_calculated_value(value, column, operation)
278
- end
279
-
280
- def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
281
- group_attrs = group_values
308
+ def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
309
+ group_fields = group_values
282
310
 
283
- if group_attrs.first.respond_to?(:to_sym)
284
- association = @klass._reflect_on_association(group_attrs.first)
285
- associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
286
- group_fields = Array(associated ? association.foreign_key : group_attrs)
287
- else
288
- group_fields = group_attrs
289
- end
290
- group_fields = arel_columns(group_fields)
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)
291
317
 
292
- group_aliases = group_fields.map { |field|
293
- column_alias_for(field)
294
- }
295
- group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
296
- [aliaz, field]
297
- }
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
+ }
298
341
 
299
- group = group_fields
342
+ relation = except(:group).distinct!(false)
343
+ relation.group_values = group_fields
344
+ relation.select_values = select_values
300
345
 
301
- if operation == 'count' && column_name == :all
302
- aggregate_alias = 'count_all'
303
- else
304
- aggregate_alias = column_alias_for([operation, column_name].join(' '))
305
- end
346
+ calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
306
347
 
307
- select_values = [
308
- operation_over_aggregate_column(
309
- aggregate_column(column_name),
310
- operation,
311
- distinct).as(aggregate_alias)
312
- ]
313
- select_values += self.select_values unless having_values.empty?
314
-
315
- select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
316
- if field.respond_to?(:as)
317
- field.as(aliaz)
318
- else
319
- "#{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] }]
320
352
  end
321
- }
322
-
323
- relation = except(:group)
324
- relation.group_values = group
325
- relation.select_values = select_values
326
-
327
- calculated_data = @klass.connection.select_all(relation, nil, relation.arel.bind_values + bind_values)
328
353
 
329
- if association
330
- key_ids = calculated_data.collect { |row| row[group_aliases.first] }
331
- key_records = association.klass.base_class.find(key_ids)
332
- 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]
333
367
  end
334
368
 
335
- Hash[calculated_data.map do |row|
336
- key = group_columns.map { |aliaz, col_name|
337
- column = calculated_data.column_types.fetch(aliaz) do
338
- type_for(col_name)
339
- end
340
- type_cast_calculated_value(row[aliaz], column)
341
- }
342
- key = key.first if key.size == 1
343
- key = key_records[key] if associated
344
-
345
- column_type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
346
- [key, type_cast_calculated_value(row[aggregate_alias], column_type, operation)]
347
- end]
348
- end
349
-
350
- # Converts the given keys to the value that the database adapter returns as
351
- # a usable column name:
352
- #
353
- # column_alias_for("users.id") # => "users_id"
354
- # column_alias_for("sum(id)") # => "sum_id"
355
- # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
356
- # column_alias_for("count(*)") # => "count_all"
357
- # column_alias_for("count", "id") # => "count_id"
358
- def column_alias_for(keys)
359
- if keys.respond_to? :name
360
- 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)
361
384
  end
362
385
 
363
- table_name = keys.to_s.downcase
364
- table_name.gsub!(/\*/, 'all')
365
- table_name.gsub!(/\W+/, ' ')
366
- table_name.strip!
367
- table_name.gsub!(/ +/, '_')
368
-
369
- @klass.connection.table_alias_for(table_name)
370
- end
371
-
372
- def type_for(field)
373
- field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
374
- @klass.type_for_attribute(field_name)
375
- 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
376
390
 
377
- def type_cast_calculated_value(value, type, operation = nil)
378
- case operation
379
- when 'count' then value.to_i
380
- when 'sum' then type.type_cast_from_database(value || 0)
381
- when 'average' then value.respond_to?(:to_d) ? value.to_d : value
382
- 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
383
398
  end
384
- end
385
399
 
386
- # TODO: refactor to allow non-string `select_values` (eg. Arel nodes).
387
- def select_for_count
388
- if select_values.present?
389
- select_values.join(", ")
390
- else
391
- :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
392
407
  end
393
- end
394
408
 
395
- def build_count_subquery(relation, column_name, distinct)
396
- column_alias = Arel.sql('count_column')
397
- 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
398
417
 
399
- aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
400
- relation.select_values = [aliased_column]
401
- arel = relation.arel
402
- subquery = arel.as(subquery_alias)
418
+ subquery_alias = Arel.sql("subquery_for_count")
419
+ select_value = operation_over_aggregate_column(column_alias, "count", false)
403
420
 
404
- sm = Arel::SelectManager.new relation.engine
405
- sm.bind_values = arel.bind_values
406
- select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
407
- sm.project(select_value).from(subquery)
408
- end
421
+ relation.build_subquery(subquery_alias, select_value)
422
+ end
409
423
  end
410
424
  end