omg-activerecord 8.0.0.alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (412) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +355 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +219 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record/aggregations.rb +287 -0
  8. data/lib/active_record/association_relation.rb +50 -0
  9. data/lib/active_record/associations/alias_tracker.rb +90 -0
  10. data/lib/active_record/associations/association.rb +417 -0
  11. data/lib/active_record/associations/association_scope.rb +175 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +163 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  14. data/lib/active_record/associations/builder/association.rb +170 -0
  15. data/lib/active_record/associations/builder/belongs_to.rb +160 -0
  16. data/lib/active_record/associations/builder/collection_association.rb +80 -0
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -0
  18. data/lib/active_record/associations/builder/has_many.rb +23 -0
  19. data/lib/active_record/associations/builder/has_one.rb +61 -0
  20. data/lib/active_record/associations/builder/singular_association.rb +48 -0
  21. data/lib/active_record/associations/collection_association.rb +535 -0
  22. data/lib/active_record/associations/collection_proxy.rb +1163 -0
  23. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  24. data/lib/active_record/associations/errors.rb +265 -0
  25. data/lib/active_record/associations/foreign_association.rb +40 -0
  26. data/lib/active_record/associations/has_many_association.rb +167 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +232 -0
  28. data/lib/active_record/associations/has_one_association.rb +142 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +106 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/join_dependency.rb +301 -0
  34. data/lib/active_record/associations/nested_error.rb +47 -0
  35. data/lib/active_record/associations/preloader/association.rb +316 -0
  36. data/lib/active_record/associations/preloader/batch.rb +48 -0
  37. data/lib/active_record/associations/preloader/branch.rb +153 -0
  38. data/lib/active_record/associations/preloader/through_association.rb +150 -0
  39. data/lib/active_record/associations/preloader.rb +135 -0
  40. data/lib/active_record/associations/singular_association.rb +76 -0
  41. data/lib/active_record/associations/through_association.rb +132 -0
  42. data/lib/active_record/associations.rb +1897 -0
  43. data/lib/active_record/asynchronous_queries_tracker.rb +64 -0
  44. data/lib/active_record/attribute_assignment.rb +82 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +106 -0
  46. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  47. data/lib/active_record/attribute_methods/dirty.rb +262 -0
  48. data/lib/active_record/attribute_methods/primary_key.rb +158 -0
  49. data/lib/active_record/attribute_methods/query.rb +50 -0
  50. data/lib/active_record/attribute_methods/read.rb +46 -0
  51. data/lib/active_record/attribute_methods/serialization.rb +232 -0
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +94 -0
  53. data/lib/active_record/attribute_methods/write.rb +49 -0
  54. data/lib/active_record/attribute_methods.rb +542 -0
  55. data/lib/active_record/attributes.rb +307 -0
  56. data/lib/active_record/autosave_association.rb +586 -0
  57. data/lib/active_record/base.rb +338 -0
  58. data/lib/active_record/callbacks.rb +452 -0
  59. data/lib/active_record/coders/column_serializer.rb +61 -0
  60. data/lib/active_record/coders/json.rb +15 -0
  61. data/lib/active_record/coders/yaml_column.rb +95 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +290 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +210 -0
  64. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +923 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +31 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +747 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +319 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +239 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +24 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +190 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +961 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +106 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1883 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +676 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +1218 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1016 -0
  78. data/lib/active_record/connection_adapters/column.rb +122 -0
  79. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  80. data/lib/active_record/connection_adapters/mysql/column.rb +28 -0
  81. data/lib/active_record/connection_adapters/mysql/database_statements.rb +95 -0
  82. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  83. data/lib/active_record/connection_adapters/mysql/quoting.rb +114 -0
  84. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +106 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +106 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +97 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +300 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  89. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +96 -0
  90. data/lib/active_record/connection_adapters/mysql2_adapter.rb +196 -0
  91. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  92. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  93. data/lib/active_record/connection_adapters/postgresql/column.rb +82 -0
  94. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +231 -0
  95. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +91 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +54 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +31 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +20 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +109 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +42 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +74 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +124 -0
  115. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  116. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  117. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  118. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +125 -0
  119. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +45 -0
  120. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  121. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  122. data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -0
  123. data/lib/active_record/connection_adapters/postgresql/quoting.rb +238 -0
  124. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +71 -0
  125. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +169 -0
  126. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +392 -0
  127. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +127 -0
  128. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1162 -0
  129. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
  130. data/lib/active_record/connection_adapters/postgresql/utils.rb +79 -0
  131. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1182 -0
  132. data/lib/active_record/connection_adapters/schema_cache.rb +478 -0
  133. data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
  134. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  135. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +145 -0
  136. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  137. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +116 -0
  138. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +37 -0
  139. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +39 -0
  140. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +47 -0
  141. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +221 -0
  142. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +843 -0
  143. data/lib/active_record/connection_adapters/statement_pool.rb +67 -0
  144. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +69 -0
  145. data/lib/active_record/connection_adapters/trilogy_adapter.rb +212 -0
  146. data/lib/active_record/connection_adapters.rb +176 -0
  147. data/lib/active_record/connection_handling.rb +413 -0
  148. data/lib/active_record/core.rb +836 -0
  149. data/lib/active_record/counter_cache.rb +230 -0
  150. data/lib/active_record/database_configurations/connection_url_resolver.rb +105 -0
  151. data/lib/active_record/database_configurations/database_config.rb +104 -0
  152. data/lib/active_record/database_configurations/hash_config.rb +172 -0
  153. data/lib/active_record/database_configurations/url_config.rb +78 -0
  154. data/lib/active_record/database_configurations.rb +309 -0
  155. data/lib/active_record/delegated_type.rb +289 -0
  156. data/lib/active_record/deprecator.rb +7 -0
  157. data/lib/active_record/destroy_association_async_job.rb +38 -0
  158. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  159. data/lib/active_record/dynamic_matchers.rb +121 -0
  160. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  161. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  162. data/lib/active_record/encryption/cipher.rb +53 -0
  163. data/lib/active_record/encryption/config.rb +70 -0
  164. data/lib/active_record/encryption/configurable.rb +60 -0
  165. data/lib/active_record/encryption/context.rb +42 -0
  166. data/lib/active_record/encryption/contexts.rb +76 -0
  167. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  168. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  169. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  170. data/lib/active_record/encryption/encrypted_attribute_type.rb +184 -0
  171. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  172. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  173. data/lib/active_record/encryption/encryptor.rb +177 -0
  174. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  175. data/lib/active_record/encryption/errors.rb +15 -0
  176. data/lib/active_record/encryption/extended_deterministic_queries.rb +159 -0
  177. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  178. data/lib/active_record/encryption/key.rb +28 -0
  179. data/lib/active_record/encryption/key_generator.rb +53 -0
  180. data/lib/active_record/encryption/key_provider.rb +46 -0
  181. data/lib/active_record/encryption/message.rb +33 -0
  182. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  183. data/lib/active_record/encryption/message_serializer.rb +96 -0
  184. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  185. data/lib/active_record/encryption/properties.rb +76 -0
  186. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  187. data/lib/active_record/encryption/scheme.rb +107 -0
  188. data/lib/active_record/encryption.rb +58 -0
  189. data/lib/active_record/enum.rb +424 -0
  190. data/lib/active_record/errors.rb +614 -0
  191. data/lib/active_record/explain.rb +63 -0
  192. data/lib/active_record/explain_registry.rb +37 -0
  193. data/lib/active_record/explain_subscriber.rb +34 -0
  194. data/lib/active_record/fixture_set/file.rb +89 -0
  195. data/lib/active_record/fixture_set/model_metadata.rb +42 -0
  196. data/lib/active_record/fixture_set/render_context.rb +19 -0
  197. data/lib/active_record/fixture_set/table_row.rb +208 -0
  198. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  199. data/lib/active_record/fixtures.rb +850 -0
  200. data/lib/active_record/future_result.rb +182 -0
  201. data/lib/active_record/gem_version.rb +17 -0
  202. data/lib/active_record/inheritance.rb +366 -0
  203. data/lib/active_record/insert_all.rb +328 -0
  204. data/lib/active_record/integration.rb +209 -0
  205. data/lib/active_record/internal_metadata.rb +164 -0
  206. data/lib/active_record/legacy_yaml_adapter.rb +15 -0
  207. data/lib/active_record/locale/en.yml +48 -0
  208. data/lib/active_record/locking/optimistic.rb +228 -0
  209. data/lib/active_record/locking/pessimistic.rb +102 -0
  210. data/lib/active_record/log_subscriber.rb +149 -0
  211. data/lib/active_record/marshalling.rb +56 -0
  212. data/lib/active_record/message_pack.rb +124 -0
  213. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  214. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  215. data/lib/active_record/middleware/database_selector.rb +87 -0
  216. data/lib/active_record/middleware/shard_selector.rb +62 -0
  217. data/lib/active_record/migration/command_recorder.rb +406 -0
  218. data/lib/active_record/migration/compatibility.rb +490 -0
  219. data/lib/active_record/migration/default_strategy.rb +22 -0
  220. data/lib/active_record/migration/execution_strategy.rb +19 -0
  221. data/lib/active_record/migration/join_table.rb +16 -0
  222. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  223. data/lib/active_record/migration.rb +1626 -0
  224. data/lib/active_record/model_schema.rb +635 -0
  225. data/lib/active_record/nested_attributes.rb +633 -0
  226. data/lib/active_record/no_touching.rb +65 -0
  227. data/lib/active_record/normalization.rb +163 -0
  228. data/lib/active_record/persistence.rb +968 -0
  229. data/lib/active_record/promise.rb +84 -0
  230. data/lib/active_record/query_cache.rb +56 -0
  231. data/lib/active_record/query_logs.rb +247 -0
  232. data/lib/active_record/query_logs_formatter.rb +30 -0
  233. data/lib/active_record/querying.rb +122 -0
  234. data/lib/active_record/railtie.rb +440 -0
  235. data/lib/active_record/railties/console_sandbox.rb +5 -0
  236. data/lib/active_record/railties/controller_runtime.rb +65 -0
  237. data/lib/active_record/railties/databases.rake +641 -0
  238. data/lib/active_record/railties/job_runtime.rb +23 -0
  239. data/lib/active_record/readonly_attributes.rb +66 -0
  240. data/lib/active_record/reflection.rb +1287 -0
  241. data/lib/active_record/relation/batches/batch_enumerator.rb +115 -0
  242. data/lib/active_record/relation/batches.rb +491 -0
  243. data/lib/active_record/relation/calculations.rb +679 -0
  244. data/lib/active_record/relation/delegation.rb +154 -0
  245. data/lib/active_record/relation/finder_methods.rb +661 -0
  246. data/lib/active_record/relation/from_clause.rb +30 -0
  247. data/lib/active_record/relation/merger.rb +192 -0
  248. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  249. data/lib/active_record/relation/predicate_builder/association_query_value.rb +76 -0
  250. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  251. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +60 -0
  252. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  253. data/lib/active_record/relation/predicate_builder/relation_handler.rb +24 -0
  254. data/lib/active_record/relation/predicate_builder.rb +181 -0
  255. data/lib/active_record/relation/query_attribute.rb +68 -0
  256. data/lib/active_record/relation/query_methods.rb +2235 -0
  257. data/lib/active_record/relation/record_fetch_warning.rb +52 -0
  258. data/lib/active_record/relation/spawn_methods.rb +78 -0
  259. data/lib/active_record/relation/where_clause.rb +218 -0
  260. data/lib/active_record/relation.rb +1495 -0
  261. data/lib/active_record/result.rb +249 -0
  262. data/lib/active_record/runtime_registry.rb +82 -0
  263. data/lib/active_record/sanitization.rb +254 -0
  264. data/lib/active_record/schema.rb +77 -0
  265. data/lib/active_record/schema_dumper.rb +364 -0
  266. data/lib/active_record/schema_migration.rb +106 -0
  267. data/lib/active_record/scoping/default.rb +205 -0
  268. data/lib/active_record/scoping/named.rb +202 -0
  269. data/lib/active_record/scoping.rb +136 -0
  270. data/lib/active_record/secure_password.rb +60 -0
  271. data/lib/active_record/secure_token.rb +66 -0
  272. data/lib/active_record/serialization.rb +29 -0
  273. data/lib/active_record/signed_id.rb +137 -0
  274. data/lib/active_record/statement_cache.rb +164 -0
  275. data/lib/active_record/store.rb +299 -0
  276. data/lib/active_record/suppressor.rb +59 -0
  277. data/lib/active_record/table_metadata.rb +85 -0
  278. data/lib/active_record/tasks/database_tasks.rb +681 -0
  279. data/lib/active_record/tasks/mysql_database_tasks.rb +120 -0
  280. data/lib/active_record/tasks/postgresql_database_tasks.rb +147 -0
  281. data/lib/active_record/tasks/sqlite_database_tasks.rb +89 -0
  282. data/lib/active_record/test_databases.rb +24 -0
  283. data/lib/active_record/test_fixtures.rb +321 -0
  284. data/lib/active_record/testing/query_assertions.rb +121 -0
  285. data/lib/active_record/timestamp.rb +177 -0
  286. data/lib/active_record/token_for.rb +123 -0
  287. data/lib/active_record/touch_later.rb +70 -0
  288. data/lib/active_record/transaction.rb +132 -0
  289. data/lib/active_record/transactions.rb +523 -0
  290. data/lib/active_record/translation.rb +22 -0
  291. data/lib/active_record/type/adapter_specific_registry.rb +144 -0
  292. data/lib/active_record/type/date.rb +9 -0
  293. data/lib/active_record/type/date_time.rb +9 -0
  294. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  295. data/lib/active_record/type/hash_lookup_type_map.rb +57 -0
  296. data/lib/active_record/type/internal/timezone.rb +22 -0
  297. data/lib/active_record/type/json.rb +30 -0
  298. data/lib/active_record/type/serialized.rb +76 -0
  299. data/lib/active_record/type/text.rb +11 -0
  300. data/lib/active_record/type/time.rb +35 -0
  301. data/lib/active_record/type/type_map.rb +58 -0
  302. data/lib/active_record/type/unsigned_integer.rb +16 -0
  303. data/lib/active_record/type.rb +83 -0
  304. data/lib/active_record/type_caster/connection.rb +33 -0
  305. data/lib/active_record/type_caster/map.rb +23 -0
  306. data/lib/active_record/type_caster.rb +9 -0
  307. data/lib/active_record/validations/absence.rb +25 -0
  308. data/lib/active_record/validations/associated.rb +65 -0
  309. data/lib/active_record/validations/length.rb +26 -0
  310. data/lib/active_record/validations/numericality.rb +36 -0
  311. data/lib/active_record/validations/presence.rb +45 -0
  312. data/lib/active_record/validations/uniqueness.rb +295 -0
  313. data/lib/active_record/validations.rb +101 -0
  314. data/lib/active_record/version.rb +10 -0
  315. data/lib/active_record.rb +616 -0
  316. data/lib/arel/alias_predication.rb +9 -0
  317. data/lib/arel/attributes/attribute.rb +33 -0
  318. data/lib/arel/collectors/bind.rb +31 -0
  319. data/lib/arel/collectors/composite.rb +46 -0
  320. data/lib/arel/collectors/plain_string.rb +20 -0
  321. data/lib/arel/collectors/sql_string.rb +27 -0
  322. data/lib/arel/collectors/substitute_binds.rb +35 -0
  323. data/lib/arel/crud.rb +48 -0
  324. data/lib/arel/delete_manager.rb +32 -0
  325. data/lib/arel/errors.rb +19 -0
  326. data/lib/arel/expressions.rb +29 -0
  327. data/lib/arel/factory_methods.rb +53 -0
  328. data/lib/arel/filter_predications.rb +9 -0
  329. data/lib/arel/insert_manager.rb +48 -0
  330. data/lib/arel/math.rb +45 -0
  331. data/lib/arel/nodes/ascending.rb +23 -0
  332. data/lib/arel/nodes/binary.rb +125 -0
  333. data/lib/arel/nodes/bind_param.rb +44 -0
  334. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  335. data/lib/arel/nodes/case.rb +55 -0
  336. data/lib/arel/nodes/casted.rb +62 -0
  337. data/lib/arel/nodes/comment.rb +29 -0
  338. data/lib/arel/nodes/count.rb +12 -0
  339. data/lib/arel/nodes/cte.rb +36 -0
  340. data/lib/arel/nodes/delete_statement.rb +44 -0
  341. data/lib/arel/nodes/descending.rb +23 -0
  342. data/lib/arel/nodes/equality.rb +15 -0
  343. data/lib/arel/nodes/extract.rb +24 -0
  344. data/lib/arel/nodes/false.rb +16 -0
  345. data/lib/arel/nodes/filter.rb +10 -0
  346. data/lib/arel/nodes/fragments.rb +35 -0
  347. data/lib/arel/nodes/full_outer_join.rb +8 -0
  348. data/lib/arel/nodes/function.rb +45 -0
  349. data/lib/arel/nodes/grouping.rb +11 -0
  350. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  351. data/lib/arel/nodes/in.rb +15 -0
  352. data/lib/arel/nodes/infix_operation.rb +92 -0
  353. data/lib/arel/nodes/inner_join.rb +8 -0
  354. data/lib/arel/nodes/insert_statement.rb +37 -0
  355. data/lib/arel/nodes/join_source.rb +20 -0
  356. data/lib/arel/nodes/leading_join.rb +8 -0
  357. data/lib/arel/nodes/matches.rb +18 -0
  358. data/lib/arel/nodes/named_function.rb +23 -0
  359. data/lib/arel/nodes/nary.rb +39 -0
  360. data/lib/arel/nodes/node.rb +161 -0
  361. data/lib/arel/nodes/node_expression.rb +13 -0
  362. data/lib/arel/nodes/ordering.rb +27 -0
  363. data/lib/arel/nodes/outer_join.rb +8 -0
  364. data/lib/arel/nodes/over.rb +15 -0
  365. data/lib/arel/nodes/regexp.rb +16 -0
  366. data/lib/arel/nodes/right_outer_join.rb +8 -0
  367. data/lib/arel/nodes/select_core.rb +67 -0
  368. data/lib/arel/nodes/select_statement.rb +41 -0
  369. data/lib/arel/nodes/sql_literal.rb +32 -0
  370. data/lib/arel/nodes/string_join.rb +11 -0
  371. data/lib/arel/nodes/table_alias.rb +35 -0
  372. data/lib/arel/nodes/terminal.rb +16 -0
  373. data/lib/arel/nodes/true.rb +16 -0
  374. data/lib/arel/nodes/unary.rb +44 -0
  375. data/lib/arel/nodes/unary_operation.rb +20 -0
  376. data/lib/arel/nodes/unqualified_column.rb +22 -0
  377. data/lib/arel/nodes/update_statement.rb +46 -0
  378. data/lib/arel/nodes/values_list.rb +9 -0
  379. data/lib/arel/nodes/window.rb +126 -0
  380. data/lib/arel/nodes/with.rb +11 -0
  381. data/lib/arel/nodes.rb +75 -0
  382. data/lib/arel/order_predications.rb +13 -0
  383. data/lib/arel/predications.rb +260 -0
  384. data/lib/arel/select_manager.rb +276 -0
  385. data/lib/arel/table.rb +121 -0
  386. data/lib/arel/tree_manager.rb +65 -0
  387. data/lib/arel/update_manager.rb +49 -0
  388. data/lib/arel/visitors/dot.rb +299 -0
  389. data/lib/arel/visitors/mysql.rb +111 -0
  390. data/lib/arel/visitors/postgresql.rb +99 -0
  391. data/lib/arel/visitors/sqlite.rb +38 -0
  392. data/lib/arel/visitors/to_sql.rb +1033 -0
  393. data/lib/arel/visitors/visitor.rb +45 -0
  394. data/lib/arel/visitors.rb +13 -0
  395. data/lib/arel/window_predications.rb +9 -0
  396. data/lib/arel.rb +73 -0
  397. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  398. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  399. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  400. data/lib/rails/generators/active_record/migration/migration_generator.rb +76 -0
  401. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +29 -0
  402. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  403. data/lib/rails/generators/active_record/migration.rb +54 -0
  404. data/lib/rails/generators/active_record/model/USAGE +113 -0
  405. data/lib/rails/generators/active_record/model/model_generator.rb +94 -0
  406. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  407. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  408. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  409. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  410. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  411. data/lib/rails/generators/active_record.rb +19 -0
  412. metadata +505 -0
@@ -0,0 +1,679 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/enumerable"
4
+
5
+ module ActiveRecord
6
+ # = Active Record \Calculations
7
+ module Calculations
8
+ class ColumnAliasTracker # :nodoc:
9
+ def initialize(connection)
10
+ @connection = connection
11
+ @aliases = Hash.new(0)
12
+ end
13
+
14
+ def alias_for(field)
15
+ aliased_name = column_alias_for(field)
16
+
17
+ if @aliases[aliased_name] == 0
18
+ @aliases[aliased_name] = 1
19
+ aliased_name
20
+ else
21
+ # Update the count
22
+ count = @aliases[aliased_name] += 1
23
+ "#{truncate(aliased_name)}_#{count}"
24
+ end
25
+ end
26
+
27
+ private
28
+ # Converts the given field to the value that the database adapter returns as
29
+ # a usable column name:
30
+ #
31
+ # column_alias_for("users.id") # => "users_id"
32
+ # column_alias_for("sum(id)") # => "sum_id"
33
+ # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
34
+ # column_alias_for("count(*)") # => "count_all"
35
+ def column_alias_for(field)
36
+ column_alias = +field
37
+ column_alias.gsub!(/\*/, "all")
38
+ column_alias.gsub!(/\W+/, " ")
39
+ column_alias.strip!
40
+ column_alias.gsub!(/ +/, "_")
41
+ @connection.table_alias_for(column_alias)
42
+ end
43
+
44
+ def truncate(name)
45
+ name.slice(0, @connection.table_alias_length - 2)
46
+ end
47
+ end
48
+
49
+ # Count the records.
50
+ #
51
+ # Person.count
52
+ # # => the total count of all people
53
+ #
54
+ # Person.count(:age)
55
+ # # => returns the total count of all people whose age is present in database
56
+ #
57
+ # Person.count(:all)
58
+ # # => performs a COUNT(*) (:all is an alias for '*')
59
+ #
60
+ # Person.distinct.count(:age)
61
+ # # => counts the number of different age values
62
+ #
63
+ # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group],
64
+ # it returns a Hash whose keys represent the aggregated column,
65
+ # and the values are the respective amounts:
66
+ #
67
+ # Person.group(:city).count
68
+ # # => { 'Rome' => 5, 'Paris' => 3 }
69
+ #
70
+ # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group] for multiple columns, it returns a Hash whose
71
+ # keys are an array containing the individual values of each column and the value
72
+ # of each key would be the #count.
73
+ #
74
+ # Article.group(:status, :category).count
75
+ # # => {["draft", "business"]=>10, ["draft", "technology"]=>4, ["published", "technology"]=>2}
76
+ #
77
+ # If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
78
+ #
79
+ # Person.select(:age).count
80
+ # # => counts the number of different age values
81
+ #
82
+ # Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
83
+ # between databases. In invalid cases, an error from the database is thrown.
84
+ #
85
+ # When given a block, loads all records in the relation, if the relation
86
+ # hasn't been loaded yet. Calls the block with each record in the relation.
87
+ # Returns the number of records for which the block returns a truthy value.
88
+ #
89
+ # Person.count { |person| person.age > 21 }
90
+ # # => counts the number of people older that 21
91
+ #
92
+ # Note: If there are a lot of records in the relation, loading all records
93
+ # could result in performance issues.
94
+ def count(column_name = nil)
95
+ if block_given?
96
+ unless column_name.nil?
97
+ raise ArgumentError, "Column name argument is not supported when a block is passed."
98
+ end
99
+
100
+ super()
101
+ else
102
+ calculate(:count, column_name)
103
+ end
104
+ end
105
+
106
+ # Same as #count, but performs the query asynchronously and returns an
107
+ # ActiveRecord::Promise.
108
+ def async_count(column_name = nil)
109
+ async.count(column_name)
110
+ end
111
+
112
+ # Calculates the average value on a given column. Returns +nil+ if there's
113
+ # no row. See #calculate for examples with options.
114
+ #
115
+ # Person.average(:age) # => 35.8
116
+ def average(column_name)
117
+ calculate(:average, column_name)
118
+ end
119
+
120
+ # Same as #average, but performs the query asynchronously and returns an
121
+ # ActiveRecord::Promise.
122
+ def async_average(column_name)
123
+ async.average(column_name)
124
+ end
125
+
126
+ # Calculates the minimum value on a given column. The value is returned
127
+ # with the same data type of the column, or +nil+ if there's no row. See
128
+ # #calculate for examples with options.
129
+ #
130
+ # Person.minimum(:age) # => 7
131
+ def minimum(column_name)
132
+ calculate(:minimum, column_name)
133
+ end
134
+
135
+ # Same as #minimum, but performs the query asynchronously and returns an
136
+ # ActiveRecord::Promise.
137
+ def async_minimum(column_name)
138
+ async.minimum(column_name)
139
+ end
140
+
141
+ # Calculates the maximum value on a given column. The value is returned
142
+ # with the same data type of the column, or +nil+ if there's no row. See
143
+ # #calculate for examples with options.
144
+ #
145
+ # Person.maximum(:age) # => 93
146
+ def maximum(column_name)
147
+ calculate(:maximum, column_name)
148
+ end
149
+
150
+ # Same as #maximum, but performs the query asynchronously and returns an
151
+ # ActiveRecord::Promise.
152
+ def async_maximum(column_name)
153
+ async.maximum(column_name)
154
+ end
155
+
156
+ # Calculates the sum of values on a given column. The value is returned
157
+ # with the same data type of the column, +0+ if there's no row. See
158
+ # #calculate for examples with options.
159
+ #
160
+ # Person.sum(:age) # => 4562
161
+ #
162
+ # When given a block, loads all records in the relation, if the relation
163
+ # hasn't been loaded yet. Calls the block with each record in the relation.
164
+ # Returns the sum of +initial_value_or_column+ and the block return
165
+ # values:
166
+ #
167
+ # Person.sum { |person| person.age } # => 4562
168
+ # Person.sum(1000) { |person| person.age } # => 5562
169
+ #
170
+ # Note: If there are a lot of records in the relation, loading all records
171
+ # could result in performance issues.
172
+ def sum(initial_value_or_column = 0, &block)
173
+ if block_given?
174
+ map(&block).sum(initial_value_or_column)
175
+ else
176
+ calculate(:sum, initial_value_or_column)
177
+ end
178
+ end
179
+
180
+ # Same as #sum, but performs the query asynchronously and returns an
181
+ # ActiveRecord::Promise.
182
+ def async_sum(identity_or_column = nil)
183
+ async.sum(identity_or_column)
184
+ end
185
+
186
+ # This calculates aggregate values in the given column. Methods for #count, #sum, #average,
187
+ # #minimum, and #maximum have been added as shortcuts.
188
+ #
189
+ # Person.calculate(:count, :all) # The same as Person.count
190
+ # Person.average(:age) # SELECT AVG(age) FROM people...
191
+ #
192
+ # # Selects the minimum age for any family without any minors
193
+ # Person.group(:last_name).having("min(age) > 17").minimum(:age)
194
+ #
195
+ # Person.sum("2 * age")
196
+ #
197
+ # There are two basic forms of output:
198
+ #
199
+ # * Single aggregate value: The single value is type cast to Integer for COUNT, Float
200
+ # for AVG, and the given column's type for everything else.
201
+ #
202
+ # * Grouped values: This returns an ordered hash of the values and groups them. It
203
+ # takes either a column name, or the name of a belongs_to association.
204
+ #
205
+ # values = Person.group('last_name').maximum(:age)
206
+ # puts values["Drake"]
207
+ # # => 43
208
+ #
209
+ # drake = Family.find_by(last_name: 'Drake')
210
+ # values = Person.group(:family).maximum(:age) # Person belongs_to :family
211
+ # puts values[drake]
212
+ # # => 43
213
+ #
214
+ # values.each do |family, max_age|
215
+ # ...
216
+ # end
217
+ def calculate(operation, column_name)
218
+ operation = operation.to_s.downcase
219
+
220
+ if @none
221
+ case operation
222
+ when "count", "sum"
223
+ result = group_values.any? ? Hash.new : 0
224
+ return @async ? Promise::Complete.new(result) : result
225
+ when "average", "minimum", "maximum"
226
+ result = group_values.any? ? Hash.new : nil
227
+ return @async ? Promise::Complete.new(result) : result
228
+ end
229
+ end
230
+
231
+ if has_include?(column_name)
232
+ relation = apply_join_dependency
233
+
234
+ if operation == "count"
235
+ unless distinct_value || distinct_select?(column_name || select_for_count)
236
+ relation.distinct!
237
+ relation.select_values = Array(model.primary_key || table[Arel.star])
238
+ end
239
+ # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
240
+ relation.order_values = [] if group_values.empty?
241
+ end
242
+
243
+ relation.calculate(operation, column_name)
244
+ else
245
+ perform_calculation(operation, column_name)
246
+ end
247
+ end
248
+
249
+ # Use #pluck as a shortcut to select one or more attributes without
250
+ # loading an entire record object per row.
251
+ #
252
+ # Person.pluck(:name)
253
+ #
254
+ # instead of
255
+ #
256
+ # Person.all.map(&:name)
257
+ #
258
+ # Pluck returns an Array of attribute values type-casted to match
259
+ # the plucked column names, if they can be deduced. Plucking an SQL fragment
260
+ # returns String values by default.
261
+ #
262
+ # Person.pluck(:name)
263
+ # # SELECT people.name FROM people
264
+ # # => ['David', 'Jeremy', 'Jose']
265
+ #
266
+ # Person.pluck(:id, :name)
267
+ # # SELECT people.id, people.name FROM people
268
+ # # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
269
+ #
270
+ # Person.distinct.pluck(:role)
271
+ # # SELECT DISTINCT role FROM people
272
+ # # => ['admin', 'member', 'guest']
273
+ #
274
+ # Person.where(age: 21).limit(5).pluck(:id)
275
+ # # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
276
+ # # => [2, 3]
277
+ #
278
+ # Comment.joins(:person).pluck(:id, person: [:id])
279
+ # # SELECT comments.id, people.id FROM comments INNER JOIN people on comments.person_id = people.id
280
+ # # => [[1, 2], [2, 2]]
281
+ #
282
+ # Person.pluck(Arel.sql('DATEDIFF(updated_at, created_at)'))
283
+ # # SELECT DATEDIFF(updated_at, created_at) FROM people
284
+ # # => ['0', '27761', '173']
285
+ #
286
+ # See also #ids.
287
+ def pluck(*column_names)
288
+ if @none
289
+ if @async
290
+ return Promise::Complete.new([])
291
+ else
292
+ return []
293
+ end
294
+ end
295
+
296
+ if loaded? && all_attributes?(column_names)
297
+ result = records.pluck(*column_names)
298
+ if @async
299
+ return Promise::Complete.new(result)
300
+ else
301
+ return result
302
+ end
303
+ end
304
+
305
+ if has_include?(column_names.first)
306
+ relation = apply_join_dependency
307
+ relation.pluck(*column_names)
308
+ else
309
+ model.disallow_raw_sql!(flattened_args(column_names))
310
+ columns = arel_columns(column_names)
311
+ relation = spawn
312
+ relation.select_values = columns
313
+ result = skip_query_cache_if_necessary do
314
+ if where_clause.contradiction?
315
+ ActiveRecord::Result.empty(async: @async)
316
+ else
317
+ model.with_connection do |c|
318
+ c.select_all(relation.arel, "#{model.name} Pluck", async: @async)
319
+ end
320
+ end
321
+ end
322
+ result.then do |result|
323
+ type_cast_pluck_values(result, columns)
324
+ end
325
+ end
326
+ end
327
+
328
+ # Same as #pluck, but performs the query asynchronously and returns an
329
+ # ActiveRecord::Promise.
330
+ def async_pluck(*column_names)
331
+ async.pluck(*column_names)
332
+ end
333
+
334
+ # Pick the value(s) from the named column(s) in the current relation.
335
+ # This is short-hand for <tt>relation.limit(1).pluck(*column_names).first</tt>, and is primarily useful
336
+ # when you have a relation that's already narrowed down to a single row.
337
+ #
338
+ # Just like #pluck, #pick will only load the actual value, not the entire record object, so it's also
339
+ # more efficient. The value is, again like with pluck, typecast by the column type.
340
+ #
341
+ # Person.where(id: 1).pick(:name)
342
+ # # SELECT people.name FROM people WHERE id = 1 LIMIT 1
343
+ # # => 'David'
344
+ #
345
+ # Person.where(id: 1).pick(:name, :email_address)
346
+ # # SELECT people.name, people.email_address FROM people WHERE id = 1 LIMIT 1
347
+ # # => [ 'David', 'david@loudthinking.com' ]
348
+ def pick(*column_names)
349
+ if loaded? && all_attributes?(column_names)
350
+ result = records.pick(*column_names)
351
+ return @async ? Promise::Complete.new(result) : result
352
+ end
353
+
354
+ limit(1).pluck(*column_names).then(&:first)
355
+ end
356
+
357
+ # Same as #pick, but performs the query asynchronously and returns an
358
+ # ActiveRecord::Promise.
359
+ def async_pick(*column_names)
360
+ async.pick(*column_names)
361
+ end
362
+
363
+ # Returns the base model's ID's for the relation using the table's primary key
364
+ #
365
+ # Person.ids # SELECT people.id FROM people
366
+ # Person.joins(:company).ids # SELECT people.id FROM people INNER JOIN companies ON companies.id = people.company_id
367
+ def ids
368
+ primary_key_array = Array(primary_key)
369
+
370
+ if loaded?
371
+ result = records.map do |record|
372
+ if primary_key_array.one?
373
+ record._read_attribute(primary_key_array.first)
374
+ else
375
+ primary_key_array.map { |column| record._read_attribute(column) }
376
+ end
377
+ end
378
+ return @async ? Promise::Complete.new(result) : result
379
+ end
380
+
381
+ if has_include?(primary_key)
382
+ relation = apply_join_dependency.group(*primary_key_array)
383
+ return relation.ids
384
+ end
385
+
386
+ columns = arel_columns(primary_key_array)
387
+ relation = spawn
388
+ relation.select_values = columns
389
+
390
+ result = if relation.where_clause.contradiction?
391
+ ActiveRecord::Result.empty
392
+ else
393
+ skip_query_cache_if_necessary do
394
+ model.with_connection do |c|
395
+ c.select_all(relation, "#{model.name} Ids", async: @async)
396
+ end
397
+ end
398
+ end
399
+
400
+ result.then { |result| type_cast_pluck_values(result, columns) }
401
+ end
402
+
403
+ # Same as #ids, but performs the query asynchronously and returns an
404
+ # ActiveRecord::Promise.
405
+ def async_ids
406
+ async.ids
407
+ end
408
+
409
+ private
410
+ def all_attributes?(column_names)
411
+ (column_names.map(&:to_s) - model.attribute_names - model.attribute_aliases.keys).empty?
412
+ end
413
+
414
+ def has_include?(column_name)
415
+ eager_loading? || (includes_values.present? && column_name && column_name != :all)
416
+ end
417
+
418
+ def perform_calculation(operation, column_name)
419
+ operation = operation.to_s.downcase
420
+
421
+ # If #count is used with #distinct (i.e. `relation.distinct.count`) it is
422
+ # considered distinct.
423
+ distinct = distinct_value
424
+
425
+ if operation == "count"
426
+ column_name ||= select_for_count
427
+ if column_name == :all
428
+ if !distinct
429
+ distinct = distinct_select?(select_for_count) if group_values.empty?
430
+ elsif group_values.any? || select_values.empty? && order_values.empty?
431
+ column_name = primary_key
432
+ end
433
+ elsif distinct_select?(column_name)
434
+ distinct = nil
435
+ end
436
+ end
437
+
438
+ if group_values.any?
439
+ execute_grouped_calculation(operation, column_name, distinct)
440
+ else
441
+ execute_simple_calculation(operation, column_name, distinct)
442
+ end
443
+ end
444
+
445
+ def distinct_select?(column_name)
446
+ column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
447
+ end
448
+
449
+ def aggregate_column(column_name)
450
+ return column_name if Arel::Expressions === column_name
451
+
452
+ arel_column(column_name.to_s) do |name|
453
+ column_name == :all ? Arel.sql("*", retryable: true) : Arel.sql(name)
454
+ end
455
+ end
456
+
457
+ def operation_over_aggregate_column(column, operation, distinct)
458
+ operation == "count" ? column.count(distinct) : column.public_send(operation)
459
+ end
460
+
461
+ def execute_simple_calculation(operation, column_name, distinct) # :nodoc:
462
+ if build_count_subquery?(operation, column_name, distinct)
463
+ # Shortcut when limit is zero.
464
+ return 0 if limit_value == 0
465
+
466
+ relation = self
467
+ query_builder = build_count_subquery(spawn, column_name, distinct)
468
+ else
469
+ # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
470
+ relation = unscope(:order).distinct!(false)
471
+
472
+ column = aggregate_column(column_name)
473
+ select_value = operation_over_aggregate_column(column, operation, distinct)
474
+ select_value.distinct = true if operation == "sum" && distinct
475
+
476
+ relation.select_values = [select_value]
477
+
478
+ query_builder = relation.arel
479
+ end
480
+
481
+ query_result = if relation.where_clause.contradiction?
482
+ ActiveRecord::Result.empty
483
+ else
484
+ skip_query_cache_if_necessary do
485
+ model.with_connection do |c|
486
+ c.select_all(query_builder, "#{model.name} #{operation.capitalize}", async: @async)
487
+ end
488
+ end
489
+ end
490
+
491
+ query_result.then do |result|
492
+ if operation != "count"
493
+ type = column.try(:type_caster) ||
494
+ lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
495
+ type = type.subtype if Enum::EnumType === type
496
+ end
497
+
498
+ type_cast_calculated_value(result.cast_values.first, operation, type)
499
+ end
500
+ end
501
+
502
+ def execute_grouped_calculation(operation, column_name, distinct) # :nodoc:
503
+ group_fields = group_values
504
+ group_fields = group_fields.uniq if group_fields.size > 1
505
+
506
+ if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
507
+ association = model._reflect_on_association(group_fields.first)
508
+ associated = association && association.belongs_to? # only count belongs_to associations
509
+ group_fields = Array(association.foreign_key) if associated
510
+ end
511
+ group_fields = arel_columns(group_fields)
512
+
513
+ model.with_connection do |connection|
514
+ column_alias_tracker = ColumnAliasTracker.new(connection)
515
+
516
+ group_aliases = group_fields.map { |field|
517
+ field = connection.visitor.compile(field) if Arel.arel_node?(field)
518
+ column_alias_tracker.alias_for(field.to_s.downcase)
519
+ }
520
+ group_columns = group_aliases.zip(group_fields)
521
+
522
+ column = aggregate_column(column_name)
523
+ column_alias = column_alias_tracker.alias_for("#{operation} #{column_name.to_s.downcase}")
524
+ select_value = operation_over_aggregate_column(column, operation, distinct)
525
+ select_value.as(model.adapter_class.quote_column_name(column_alias))
526
+
527
+ select_values = [select_value]
528
+ select_values += self.select_values unless having_clause.empty?
529
+
530
+ select_values.concat group_columns.map { |aliaz, field|
531
+ aliaz = model.adapter_class.quote_column_name(aliaz)
532
+ if field.respond_to?(:as)
533
+ field.as(aliaz)
534
+ else
535
+ "#{field} AS #{aliaz}"
536
+ end
537
+ }
538
+
539
+ relation = except(:group).distinct!(false)
540
+ relation.group_values = group_fields
541
+ relation.select_values = select_values
542
+
543
+ result = skip_query_cache_if_necessary do
544
+ connection.select_all(relation.arel, "#{model.name} #{operation.capitalize}", async: @async)
545
+ end
546
+
547
+ result.then do |calculated_data|
548
+ if association
549
+ key_ids = calculated_data.collect { |row| row[group_aliases.first] }
550
+ key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
551
+ key_records = key_records.index_by(&:id)
552
+ end
553
+
554
+ key_types = group_columns.each_with_object({}) do |(aliaz, col_name), types|
555
+ types[aliaz] = col_name.try(:type_caster) ||
556
+ type_for(col_name) do
557
+ calculated_data.column_types.fetch(aliaz, Type.default_value)
558
+ end
559
+ end
560
+
561
+ hash_rows = calculated_data.cast_values(key_types).map! do |row|
562
+ calculated_data.columns.each_with_object({}).with_index do |(col_name, hash), i|
563
+ hash[col_name] = row[i]
564
+ end
565
+ end
566
+
567
+ if operation != "count"
568
+ type = column.try(:type_caster) ||
569
+ lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
570
+ type = type.subtype if Enum::EnumType === type
571
+ end
572
+
573
+ hash_rows.each_with_object({}) do |row, result|
574
+ key = group_aliases.map { |aliaz| row[aliaz] }
575
+ key = key.first if key.size == 1
576
+ key = key_records[key] if associated
577
+
578
+ result[key] = type_cast_calculated_value(row[column_alias], operation, type)
579
+ end
580
+ end
581
+ end
582
+ end
583
+
584
+ def type_for(field, &block)
585
+ field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split(".").last
586
+ model.type_for_attribute(field_name, &block)
587
+ end
588
+
589
+ def lookup_cast_type_from_join_dependencies(name, join_dependencies = build_join_dependencies)
590
+ each_join_dependencies(join_dependencies) do |join|
591
+ type = join.base_klass.attribute_types.fetch(name, nil)
592
+ return type if type
593
+ end
594
+ nil
595
+ end
596
+
597
+ def type_cast_pluck_values(result, columns)
598
+ cast_types = if result.columns.size != columns.size
599
+ model.attribute_types
600
+ else
601
+ join_dependencies = nil
602
+ columns.map.with_index do |column, i|
603
+ column.try(:type_caster) ||
604
+ model.attribute_types.fetch(name = result.columns[i]) do
605
+ join_dependencies ||= build_join_dependencies
606
+ lookup_cast_type_from_join_dependencies(name, join_dependencies) ||
607
+ result.column_types[i] || Type.default_value
608
+ end
609
+ end
610
+ end
611
+ result.cast_values(cast_types)
612
+ end
613
+
614
+ def type_cast_calculated_value(value, operation, type)
615
+ case operation
616
+ when "count"
617
+ value.to_i
618
+ when "sum"
619
+ type.deserialize(value || 0)
620
+ when "average"
621
+ case type.type
622
+ when :integer, :decimal
623
+ value&.to_d
624
+ else
625
+ type.deserialize(value)
626
+ end
627
+ else # "minimum", "maximum"
628
+ type.deserialize(value)
629
+ end
630
+ end
631
+
632
+ def select_for_count
633
+ if select_values.present?
634
+ return select_values.first if select_values.one?
635
+
636
+ adapter_class = model.adapter_class
637
+ select_values.map do |field|
638
+ column = if Arel.arel_node?(field)
639
+ field
640
+ else
641
+ arel_column(field.to_s) do |attr_name|
642
+ Arel.sql(attr_name)
643
+ end
644
+ end
645
+
646
+ if column.is_a?(Arel::Nodes::SqlLiteral)
647
+ column
648
+ else
649
+ "#{adapter_class.quote_table_name(column.relation.name)}.#{adapter_class.quote_column_name(column.name)}"
650
+ end
651
+ end.join(", ")
652
+ else
653
+ :all
654
+ end
655
+ end
656
+
657
+ def build_count_subquery?(operation, column_name, distinct)
658
+ # SQLite and older MySQL does not support `COUNT DISTINCT` with `*` or
659
+ # multiple columns, so we need to use subquery for this.
660
+ operation == "count" &&
661
+ (((column_name == :all || select_values.many?) && distinct) || has_limit_or_offset?)
662
+ end
663
+
664
+ def build_count_subquery(relation, column_name, distinct)
665
+ if column_name == :all
666
+ column_alias = Arel.star
667
+ relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
668
+ else
669
+ column_alias = Arel.sql("count_column")
670
+ relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
671
+ end
672
+
673
+ subquery_alias = Arel.sql("subquery_for_count", retryable: true)
674
+ select_value = operation_over_aggregate_column(column_alias, "count", false)
675
+
676
+ relation.build_subquery(subquery_alias, select_value)
677
+ end
678
+ end
679
+ end