activerecord 6.0.0 → 7.2.3

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 (376) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +996 -594
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +34 -34
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +22 -20
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +41 -30
  9. data/lib/active_record/associations/association.rb +106 -41
  10. data/lib/active_record/associations/association_scope.rb +30 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +69 -14
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
  13. data/lib/active_record/associations/builder/association.rb +39 -6
  14. data/lib/active_record/associations/builder/belongs_to.rb +47 -17
  15. data/lib/active_record/associations/builder/collection_association.rb +14 -6
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
  17. data/lib/active_record/associations/builder/has_many.rb +7 -3
  18. data/lib/active_record/associations/builder/has_one.rb +13 -16
  19. data/lib/active_record/associations/builder/singular_association.rb +7 -3
  20. data/lib/active_record/associations/collection_association.rb +90 -53
  21. data/lib/active_record/associations/collection_proxy.rb +54 -19
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +21 -1
  25. data/lib/active_record/associations/has_many_association.rb +41 -10
  26. data/lib/active_record/associations/has_many_through_association.rb +29 -12
  27. data/lib/active_record/associations/has_one_association.rb +33 -9
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
  30. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  31. data/lib/active_record/associations/join_dependency.rb +97 -54
  32. data/lib/active_record/associations/nested_error.rb +47 -0
  33. data/lib/active_record/associations/preloader/association.rb +237 -54
  34. data/lib/active_record/associations/preloader/batch.rb +48 -0
  35. data/lib/active_record/associations/preloader/branch.rb +153 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +51 -17
  37. data/lib/active_record/associations/preloader.rb +55 -121
  38. data/lib/active_record/associations/singular_association.rb +16 -4
  39. data/lib/active_record/associations/through_association.rb +26 -15
  40. data/lib/active_record/associations.rb +454 -440
  41. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  42. data/lib/active_record/attribute_assignment.rb +11 -14
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
  44. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  45. data/lib/active_record/attribute_methods/dirty.rb +75 -34
  46. data/lib/active_record/attribute_methods/primary_key.rb +53 -31
  47. data/lib/active_record/attribute_methods/query.rb +31 -22
  48. data/lib/active_record/attribute_methods/read.rb +16 -17
  49. data/lib/active_record/attribute_methods/serialization.rb +177 -35
  50. data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
  51. data/lib/active_record/attribute_methods/write.rb +16 -28
  52. data/lib/active_record/attribute_methods.rb +227 -100
  53. data/lib/active_record/attributes.rb +94 -56
  54. data/lib/active_record/autosave_association.rb +119 -73
  55. data/lib/active_record/base.rb +31 -21
  56. data/lib/active_record/callbacks.rb +168 -55
  57. data/lib/active_record/coders/column_serializer.rb +61 -0
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +70 -25
  60. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
  68. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  69. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  70. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
  71. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
  72. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
  73. data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
  74. data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
  75. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
  76. data/lib/active_record/connection_adapters/column.rb +28 -1
  77. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  78. data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
  79. data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
  80. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  81. data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
  82. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
  83. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
  84. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
  85. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
  86. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  87. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  88. data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
  89. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  90. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  91. data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
  92. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
  95. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  101. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
  106. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  110. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
  111. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
  121. data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  123. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  124. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
  131. data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
  132. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  133. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  134. data/lib/active_record/connection_adapters.rb +176 -0
  135. data/lib/active_record/connection_handling.rb +243 -115
  136. data/lib/active_record/core.rb +481 -199
  137. data/lib/active_record/counter_cache.rb +69 -32
  138. data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
  139. data/lib/active_record/database_configurations/database_config.rb +77 -10
  140. data/lib/active_record/database_configurations/hash_config.rb +148 -26
  141. data/lib/active_record/database_configurations/url_config.rb +44 -45
  142. data/lib/active_record/database_configurations.rb +190 -114
  143. data/lib/active_record/delegated_type.rb +279 -0
  144. data/lib/active_record/deprecator.rb +7 -0
  145. data/lib/active_record/destroy_association_async_job.rb +38 -0
  146. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  147. data/lib/active_record/dynamic_matchers.rb +5 -6
  148. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  149. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  150. data/lib/active_record/encryption/cipher.rb +53 -0
  151. data/lib/active_record/encryption/config.rb +68 -0
  152. data/lib/active_record/encryption/configurable.rb +60 -0
  153. data/lib/active_record/encryption/context.rb +42 -0
  154. data/lib/active_record/encryption/contexts.rb +76 -0
  155. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  156. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  157. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  158. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  159. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  160. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  161. data/lib/active_record/encryption/encryptor.rb +171 -0
  162. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  163. data/lib/active_record/encryption/errors.rb +15 -0
  164. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  165. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  166. data/lib/active_record/encryption/key.rb +28 -0
  167. data/lib/active_record/encryption/key_generator.rb +53 -0
  168. data/lib/active_record/encryption/key_provider.rb +46 -0
  169. data/lib/active_record/encryption/message.rb +33 -0
  170. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  171. data/lib/active_record/encryption/message_serializer.rb +96 -0
  172. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  173. data/lib/active_record/encryption/properties.rb +76 -0
  174. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  175. data/lib/active_record/encryption/scheme.rb +100 -0
  176. data/lib/active_record/encryption.rb +58 -0
  177. data/lib/active_record/enum.rb +224 -73
  178. data/lib/active_record/errors.rb +254 -36
  179. data/lib/active_record/explain.rb +30 -17
  180. data/lib/active_record/explain_registry.rb +11 -6
  181. data/lib/active_record/explain_subscriber.rb +2 -2
  182. data/lib/active_record/fixture_set/file.rb +22 -15
  183. data/lib/active_record/fixture_set/model_metadata.rb +15 -6
  184. data/lib/active_record/fixture_set/render_context.rb +3 -1
  185. data/lib/active_record/fixture_set/table_row.rb +88 -16
  186. data/lib/active_record/fixture_set/table_rows.rb +4 -5
  187. data/lib/active_record/fixtures.rb +229 -116
  188. data/lib/active_record/future_result.rb +178 -0
  189. data/lib/active_record/gem_version.rb +4 -4
  190. data/lib/active_record/inheritance.rb +121 -48
  191. data/lib/active_record/insert_all.rb +178 -29
  192. data/lib/active_record/integration.rb +16 -14
  193. data/lib/active_record/internal_metadata.rb +132 -21
  194. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  195. data/lib/active_record/locking/optimistic.rb +64 -33
  196. data/lib/active_record/locking/pessimistic.rb +21 -8
  197. data/lib/active_record/log_subscriber.rb +61 -30
  198. data/lib/active_record/marshalling.rb +59 -0
  199. data/lib/active_record/message_pack.rb +124 -0
  200. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  201. data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
  202. data/lib/active_record/middleware/database_selector.rb +25 -13
  203. data/lib/active_record/middleware/shard_selector.rb +62 -0
  204. data/lib/active_record/migration/command_recorder.rb +160 -55
  205. data/lib/active_record/migration/compatibility.rb +286 -43
  206. data/lib/active_record/migration/default_strategy.rb +22 -0
  207. data/lib/active_record/migration/execution_strategy.rb +19 -0
  208. data/lib/active_record/migration/join_table.rb +1 -2
  209. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  210. data/lib/active_record/migration.rb +421 -193
  211. data/lib/active_record/model_schema.rb +217 -125
  212. data/lib/active_record/nested_attributes.rb +62 -27
  213. data/lib/active_record/no_touching.rb +4 -4
  214. data/lib/active_record/normalization.rb +163 -0
  215. data/lib/active_record/persistence.rb +322 -319
  216. data/lib/active_record/promise.rb +84 -0
  217. data/lib/active_record/query_cache.rb +18 -15
  218. data/lib/active_record/query_logs.rb +193 -0
  219. data/lib/active_record/query_logs_formatter.rb +41 -0
  220. data/lib/active_record/querying.rb +54 -14
  221. data/lib/active_record/railtie.rb +250 -72
  222. data/lib/active_record/railties/console_sandbox.rb +2 -4
  223. data/lib/active_record/railties/controller_runtime.rb +25 -11
  224. data/lib/active_record/railties/databases.rake +312 -197
  225. data/lib/active_record/railties/job_runtime.rb +23 -0
  226. data/lib/active_record/readonly_attributes.rb +45 -3
  227. data/lib/active_record/reflection.rb +389 -146
  228. data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
  229. data/lib/active_record/relation/batches.rb +214 -73
  230. data/lib/active_record/relation/calculations.rb +379 -124
  231. data/lib/active_record/relation/delegation.rb +36 -23
  232. data/lib/active_record/relation/finder_methods.rb +159 -49
  233. data/lib/active_record/relation/from_clause.rb +5 -1
  234. data/lib/active_record/relation/merger.rb +41 -33
  235. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
  236. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
  237. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
  238. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  239. data/lib/active_record/relation/predicate_builder.rb +79 -53
  240. data/lib/active_record/relation/query_attribute.rb +30 -12
  241. data/lib/active_record/relation/query_methods.rb +1156 -279
  242. data/lib/active_record/relation/record_fetch_warning.rb +12 -11
  243. data/lib/active_record/relation/spawn_methods.rb +10 -9
  244. data/lib/active_record/relation/where_clause.rb +100 -66
  245. data/lib/active_record/relation.rb +829 -194
  246. data/lib/active_record/result.rb +76 -56
  247. data/lib/active_record/runtime_registry.rb +71 -13
  248. data/lib/active_record/sanitization.rb +86 -47
  249. data/lib/active_record/schema.rb +39 -23
  250. data/lib/active_record/schema_dumper.rb +140 -33
  251. data/lib/active_record/schema_migration.rb +74 -29
  252. data/lib/active_record/scoping/default.rb +73 -19
  253. data/lib/active_record/scoping/named.rb +10 -28
  254. data/lib/active_record/scoping.rb +65 -35
  255. data/lib/active_record/secure_password.rb +60 -0
  256. data/lib/active_record/secure_token.rb +34 -8
  257. data/lib/active_record/serialization.rb +11 -4
  258. data/lib/active_record/signed_id.rb +138 -0
  259. data/lib/active_record/statement_cache.rb +26 -10
  260. data/lib/active_record/store.rb +19 -14
  261. data/lib/active_record/suppressor.rb +15 -17
  262. data/lib/active_record/table_metadata.rb +46 -36
  263. data/lib/active_record/tasks/database_tasks.rb +371 -205
  264. data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
  265. data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
  266. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
  267. data/lib/active_record/test_databases.rb +5 -4
  268. data/lib/active_record/test_fixtures.rb +189 -104
  269. data/lib/active_record/testing/query_assertions.rb +121 -0
  270. data/lib/active_record/timestamp.rb +35 -25
  271. data/lib/active_record/token_for.rb +123 -0
  272. data/lib/active_record/touch_later.rb +31 -27
  273. data/lib/active_record/transaction.rb +132 -0
  274. data/lib/active_record/transactions.rb +131 -99
  275. data/lib/active_record/translation.rb +3 -5
  276. data/lib/active_record/type/adapter_specific_registry.rb +33 -18
  277. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  278. data/lib/active_record/type/internal/timezone.rb +7 -2
  279. data/lib/active_record/type/serialized.rb +11 -6
  280. data/lib/active_record/type/time.rb +14 -0
  281. data/lib/active_record/type/type_map.rb +17 -21
  282. data/lib/active_record/type/unsigned_integer.rb +0 -1
  283. data/lib/active_record/type.rb +7 -2
  284. data/lib/active_record/type_caster/connection.rb +4 -5
  285. data/lib/active_record/type_caster/map.rb +8 -5
  286. data/lib/active_record/validations/absence.rb +1 -1
  287. data/lib/active_record/validations/associated.rb +13 -8
  288. data/lib/active_record/validations/numericality.rb +36 -0
  289. data/lib/active_record/validations/presence.rb +5 -28
  290. data/lib/active_record/validations/uniqueness.rb +88 -18
  291. data/lib/active_record/validations.rb +15 -8
  292. data/lib/active_record/version.rb +1 -1
  293. data/lib/active_record.rb +446 -40
  294. data/lib/arel/alias_predication.rb +1 -1
  295. data/lib/arel/attributes/attribute.rb +4 -8
  296. data/lib/arel/collectors/bind.rb +8 -1
  297. data/lib/arel/collectors/composite.rb +15 -0
  298. data/lib/arel/collectors/sql_string.rb +7 -0
  299. data/lib/arel/collectors/substitute_binds.rb +7 -0
  300. data/lib/arel/crud.rb +30 -22
  301. data/lib/arel/delete_manager.rb +23 -4
  302. data/lib/arel/errors.rb +10 -0
  303. data/lib/arel/factory_methods.rb +4 -0
  304. data/lib/arel/filter_predications.rb +9 -0
  305. data/lib/arel/insert_manager.rb +2 -3
  306. data/lib/arel/nodes/binary.rb +82 -9
  307. data/lib/arel/nodes/bind_param.rb +8 -0
  308. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  309. data/lib/arel/nodes/casted.rb +22 -10
  310. data/lib/arel/nodes/cte.rb +36 -0
  311. data/lib/arel/nodes/delete_statement.rb +14 -13
  312. data/lib/arel/nodes/equality.rb +6 -9
  313. data/lib/arel/nodes/filter.rb +10 -0
  314. data/lib/arel/nodes/fragments.rb +35 -0
  315. data/lib/arel/nodes/function.rb +1 -0
  316. data/lib/arel/nodes/grouping.rb +3 -0
  317. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  318. data/lib/arel/nodes/in.rb +8 -1
  319. data/lib/arel/nodes/infix_operation.rb +13 -1
  320. data/lib/arel/nodes/insert_statement.rb +2 -2
  321. data/lib/arel/nodes/join_source.rb +1 -1
  322. data/lib/arel/nodes/leading_join.rb +8 -0
  323. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  324. data/lib/arel/nodes/node.rb +122 -11
  325. data/lib/arel/nodes/ordering.rb +27 -0
  326. data/lib/arel/nodes/select_core.rb +2 -2
  327. data/lib/arel/nodes/select_statement.rb +2 -2
  328. data/lib/arel/nodes/sql_literal.rb +16 -0
  329. data/lib/arel/nodes/table_alias.rb +11 -3
  330. data/lib/arel/nodes/unary.rb +0 -1
  331. data/lib/arel/nodes/update_statement.rb +11 -4
  332. data/lib/arel/nodes.rb +10 -3
  333. data/lib/arel/predications.rb +31 -28
  334. data/lib/arel/select_manager.rb +18 -9
  335. data/lib/arel/table.rb +21 -10
  336. data/lib/arel/tree_manager.rb +8 -15
  337. data/lib/arel/update_manager.rb +25 -5
  338. data/lib/arel/visitors/dot.rb +94 -90
  339. data/lib/arel/visitors/mysql.rb +34 -6
  340. data/lib/arel/visitors/postgresql.rb +5 -16
  341. data/lib/arel/visitors/sqlite.rb +25 -1
  342. data/lib/arel/visitors/to_sql.rb +227 -81
  343. data/lib/arel/visitors/visitor.rb +2 -3
  344. data/lib/arel/visitors.rb +0 -7
  345. data/lib/arel.rb +37 -15
  346. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  347. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  348. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  349. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  350. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
  351. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  352. data/lib/rails/generators/active_record/migration.rb +9 -3
  353. data/lib/rails/generators/active_record/model/USAGE +113 -0
  354. data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
  355. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  356. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  357. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  358. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  359. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  360. metadata +117 -30
  361. data/lib/active_record/attribute_decorators.rb +0 -90
  362. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  363. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  364. data/lib/active_record/define_callbacks.rb +0 -22
  365. data/lib/active_record/null_relation.rb +0 -68
  366. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  367. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  368. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  369. data/lib/arel/attributes.rb +0 -22
  370. data/lib/arel/visitors/depth_first.rb +0 -204
  371. data/lib/arel/visitors/ibm_db.rb +0 -34
  372. data/lib/arel/visitors/informix.rb +0 -62
  373. data/lib/arel/visitors/mssql.rb +0 -157
  374. data/lib/arel/visitors/oracle.rb +0 -159
  375. data/lib/arel/visitors/oracle12.rb +0 -66
  376. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -3,20 +3,70 @@
3
3
  module ActiveRecord
4
4
  # = Active Record \Relation
5
5
  class Relation
6
+ class ExplainProxy # :nodoc:
7
+ def initialize(relation, options)
8
+ @relation = relation
9
+ @options = options
10
+ end
11
+
12
+ def inspect
13
+ exec_explain { @relation.send(:exec_queries) }
14
+ end
15
+
16
+ def average(column_name)
17
+ exec_explain { @relation.average(column_name) }
18
+ end
19
+
20
+ def count(column_name = nil)
21
+ exec_explain { @relation.count(column_name) }
22
+ end
23
+
24
+ def first(limit = nil)
25
+ exec_explain { @relation.first(limit) }
26
+ end
27
+
28
+ def last(limit = nil)
29
+ exec_explain { @relation.last(limit) }
30
+ end
31
+
32
+ def maximum(column_name)
33
+ exec_explain { @relation.maximum(column_name) }
34
+ end
35
+
36
+ def minimum(column_name)
37
+ exec_explain { @relation.minimum(column_name) }
38
+ end
39
+
40
+ def pluck(*column_names)
41
+ exec_explain { @relation.pluck(*column_names) }
42
+ end
43
+
44
+ def sum(identity_or_column = nil)
45
+ exec_explain { @relation.sum(identity_or_column) }
46
+ end
47
+
48
+ private
49
+ def exec_explain(&block)
50
+ @relation.exec_explain(@relation.collecting_queries_for_explain { block.call }, @options)
51
+ end
52
+ end
53
+
6
54
  MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
7
55
  :order, :joins, :left_outer_joins, :references,
8
- :extending, :unscope, :optimizer_hints, :annotate]
56
+ :extending, :unscope, :optimizer_hints, :annotate,
57
+ :with]
9
58
 
10
- SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
59
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering, :strict_loading,
11
60
  :reverse_order, :distinct, :create_with, :skip_query_cache]
12
61
 
13
62
  CLAUSE_METHODS = [:where, :having, :from]
14
- INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
63
+ INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :with, :with_recursive]
15
64
 
16
65
  VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
17
66
 
18
67
  include Enumerable
19
68
  include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
69
+ include SignedId::RelationMethods, TokenFor::RelationMethods
20
70
 
21
71
  attr_reader :table, :klass, :loaded, :predicate_builder
22
72
  attr_accessor :skip_preloading_value
@@ -28,10 +78,13 @@ module ActiveRecord
28
78
  @klass = klass
29
79
  @table = table
30
80
  @values = values
31
- @offsets = {}
32
81
  @loaded = false
33
82
  @predicate_builder = predicate_builder
34
83
  @delegate_to_klass = false
84
+ @future_result = nil
85
+ @records = nil
86
+ @async = false
87
+ @none = false
35
88
  end
36
89
 
37
90
  def initialize_copy(other)
@@ -39,17 +92,13 @@ module ActiveRecord
39
92
  reset
40
93
  end
41
94
 
42
- def arel_attribute(name) # :nodoc:
43
- klass.arel_attribute(name, table)
44
- end
45
-
46
95
  def bind_attribute(name, value) # :nodoc:
47
96
  if reflection = klass._reflect_on_association(name)
48
97
  name = reflection.foreign_key
49
- value = value.read_attribute(reflection.klass.primary_key) unless value.nil?
98
+ value = value.read_attribute(reflection.association_primary_key) unless value.nil?
50
99
  end
51
100
 
52
- attr = arel_attribute(name)
101
+ attr = table[name]
53
102
  bind = predicate_builder.build_bind_attribute(attr.name, value)
54
103
  yield attr, bind
55
104
  end
@@ -67,10 +116,13 @@ module ActiveRecord
67
116
  # user = users.new { |user| user.name = 'Oscar' }
68
117
  # user.name # => Oscar
69
118
  def new(attributes = nil, &block)
70
- block = _deprecated_scope_block("new", &block)
71
- scoping { klass.new(attributes, &block) }
119
+ if attributes.is_a?(Array)
120
+ attributes.collect { |attr| new(attr, &block) }
121
+ else
122
+ block = current_scope_restoring_block(&block)
123
+ scoping { _new(attributes, &block) }
124
+ end
72
125
  end
73
-
74
126
  alias build new
75
127
 
76
128
  # Tries to create a new record with the same scoped attributes
@@ -96,8 +148,8 @@ module ActiveRecord
96
148
  if attributes.is_a?(Array)
97
149
  attributes.collect { |attr| create(attr, &block) }
98
150
  else
99
- block = _deprecated_scope_block("create", &block)
100
- scoping { klass.create(attributes, &block) }
151
+ block = current_scope_restoring_block(&block)
152
+ scoping { _create(attributes, &block) }
101
153
  end
102
154
  end
103
155
 
@@ -111,8 +163,8 @@ module ActiveRecord
111
163
  if attributes.is_a?(Array)
112
164
  attributes.collect { |attr| create!(attr, &block) }
113
165
  else
114
- block = _deprecated_scope_block("create!", &block)
115
- scoping { klass.create!(attributes, &block) }
166
+ block = current_scope_restoring_block(&block)
167
+ scoping { _create!(attributes, &block) }
116
168
  end
117
169
  end
118
170
 
@@ -149,7 +201,7 @@ module ActiveRecord
149
201
  # above can be alternatively written this way:
150
202
  #
151
203
  # # Find the first user named "Scarlett" or create a new one with a
152
- # # different last name.
204
+ # # particular last name.
153
205
  # User.find_or_create_by(first_name: 'Scarlett') do |user|
154
206
  # user.last_name = 'Johansson'
155
207
  # end
@@ -159,38 +211,41 @@ module ActiveRecord
159
211
  # failed due to validation errors it won't be persisted, you get what
160
212
  # #create returns in such situation.
161
213
  #
162
- # Please note <b>this method is not atomic</b>, it runs first a SELECT, and if
163
- # there are no results an INSERT is attempted. If there are other threads
164
- # or processes there is a race condition between both calls and it could
165
- # be the case that you end up with two similar records.
214
+ # If creation failed because of a unique constraint, this method will
215
+ # assume it encountered a race condition and will try finding the record
216
+ # once more. If somehow the second find still does not find a record
217
+ # because a concurrent DELETE happened, it will then raise an
218
+ # ActiveRecord::RecordNotFound exception.
166
219
  #
167
- # If this might be a problem for your application, please see #create_or_find_by.
220
+ # Please note <b>this method is not atomic</b>, it runs first a SELECT,
221
+ # and if there are no results an INSERT is attempted. So if the table
222
+ # doesn't have a relevant unique constraint it could be the case that
223
+ # you end up with two or more similar records.
168
224
  def find_or_create_by(attributes, &block)
169
- find_by(attributes) || create(attributes, &block)
225
+ find_by(attributes) || create_or_find_by(attributes, &block)
170
226
  end
171
227
 
172
228
  # Like #find_or_create_by, but calls
173
229
  # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
174
230
  # is raised if the created record is invalid.
175
231
  def find_or_create_by!(attributes, &block)
176
- find_by(attributes) || create!(attributes, &block)
232
+ find_by(attributes) || create_or_find_by!(attributes, &block)
177
233
  end
178
234
 
179
- # Attempts to create a record with the given attributes in a table that has a unique constraint
235
+ # Attempts to create a record with the given attributes in a table that has a unique database constraint
180
236
  # on one or several of its columns. If a row already exists with one or several of these
181
237
  # unique constraints, the exception such an insertion would normally raise is caught,
182
238
  # and the existing record with those attributes is found using #find_by!.
183
239
  #
184
- # This is similar to #find_or_create_by, but avoids the problem of stale reads between the SELECT
185
- # and the INSERT, as that method needs to first query the table, then attempt to insert a row
186
- # if none is found.
240
+ # This is similar to #find_or_create_by, but tries to create the record first. As such it is
241
+ # better suited for cases where the record is most likely not to exist yet.
187
242
  #
188
243
  # There are several drawbacks to #create_or_find_by, though:
189
244
  #
190
- # * The underlying table must have the relevant columns defined with unique constraints.
245
+ # * The underlying table must have the relevant columns defined with unique database constraints.
191
246
  # * A unique constraint violation may be triggered by only one, or at least less than all,
192
247
  # of the given attributes. This means that the subsequent #find_by! may fail to find a
193
- # matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
248
+ # matching record, which will then raise an ActiveRecord::RecordNotFound exception,
194
249
  # rather than a record with the given attributes.
195
250
  # * While we avoid the race condition between SELECT -> INSERT from #find_or_create_by,
196
251
  # we actually have another race condition between INSERT -> SELECT, which can be triggered
@@ -199,29 +254,53 @@ module ActiveRecord
199
254
  # * It relies on exception handling to handle control flow, which may be marginally slower.
200
255
  # * The primary key may auto-increment on each create, even if it fails. This can accelerate
201
256
  # the problem of running out of integers, if the underlying table is still stuck on a primary
202
- # key of type int (note: All Rails apps since 5.1+ have defaulted to bigint, which is not liable
257
+ # key of type int (note: All \Rails apps since 5.1+ have defaulted to bigint, which is not liable
203
258
  # to this problem).
259
+ # * Columns with unique database constraints should not have uniqueness validations defined,
260
+ # otherwise #create will fail due to validation errors and #find_by will never be called.
204
261
  #
205
262
  # This method will return a record if all given attributes are covered by unique constraints
206
263
  # (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
207
264
  # and failed due to validation errors it won't be persisted, you get what #create returns in
208
265
  # such situation.
209
266
  def create_or_find_by(attributes, &block)
210
- transaction(requires_new: true) { create(attributes, &block) }
211
- rescue ActiveRecord::RecordNotUnique
212
- find_by!(attributes)
267
+ with_connection do |connection|
268
+ record = nil
269
+ transaction(requires_new: true) do
270
+ record = create(attributes, &block)
271
+ record._last_transaction_return_status || raise(ActiveRecord::Rollback)
272
+ end
273
+ record
274
+ rescue ActiveRecord::RecordNotUnique
275
+ if connection.transaction_open?
276
+ where(attributes).lock.find_by!(attributes)
277
+ else
278
+ find_by!(attributes)
279
+ end
280
+ end
213
281
  end
214
282
 
215
283
  # Like #create_or_find_by, but calls
216
284
  # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
217
285
  # is raised if the created record is invalid.
218
286
  def create_or_find_by!(attributes, &block)
219
- transaction(requires_new: true) { create!(attributes, &block) }
220
- rescue ActiveRecord::RecordNotUnique
221
- find_by!(attributes)
287
+ with_connection do |connection|
288
+ record = nil
289
+ transaction(requires_new: true) do
290
+ record = create!(attributes, &block)
291
+ record._last_transaction_return_status || raise(ActiveRecord::Rollback)
292
+ end
293
+ record
294
+ rescue ActiveRecord::RecordNotUnique
295
+ if connection.transaction_open?
296
+ where(attributes).lock.find_by!(attributes)
297
+ else
298
+ find_by!(attributes)
299
+ end
300
+ end
222
301
  end
223
302
 
224
- # Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
303
+ # Like #find_or_create_by, but calls {new}[rdoc-ref:Core.new]
225
304
  # instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
226
305
  def find_or_initialize_by(attributes, &block)
227
306
  find_by(attributes) || new(attributes, &block)
@@ -231,13 +310,30 @@ module ActiveRecord
231
310
  # returns the result as a string. The string is formatted imitating the
232
311
  # ones printed by the database shell.
233
312
  #
313
+ # User.all.explain
314
+ # # EXPLAIN SELECT `users`.* FROM `users`
315
+ # # ...
316
+ #
234
317
  # Note that this method actually runs the queries, since the results of some
235
318
  # are needed by the next ones when eager loading is going on.
236
319
  #
320
+ # To run EXPLAIN on queries created by +first+, +pluck+ and +count+, call
321
+ # these methods on +explain+:
322
+ #
323
+ # User.all.explain.count
324
+ # # EXPLAIN SELECT COUNT(*) FROM `users`
325
+ # # ...
326
+ #
327
+ # The column name can be passed if required:
328
+ #
329
+ # User.all.explain.maximum(:id)
330
+ # # EXPLAIN SELECT MAX(`users`.`id`) FROM `users`
331
+ # # ...
332
+ #
237
333
  # Please see further details in the
238
334
  # {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
239
- def explain
240
- exec_explain(collecting_queries_for_explain { exec_queries })
335
+ def explain(*options)
336
+ ExplainProxy.new(self, options)
241
337
  end
242
338
 
243
339
  # Converts relation objects to Array.
@@ -258,37 +354,71 @@ module ActiveRecord
258
354
 
259
355
  # Returns size of the records.
260
356
  def size
261
- loaded? ? @records.length : count(:all)
357
+ if loaded?
358
+ records.length
359
+ else
360
+ count(:all)
361
+ end
262
362
  end
263
363
 
264
364
  # Returns true if there are no records.
265
365
  def empty?
266
- return @records.empty? if loaded?
267
- !exists?
366
+ return true if @none
367
+
368
+ if loaded?
369
+ records.empty?
370
+ else
371
+ !exists?
372
+ end
268
373
  end
269
374
 
270
375
  # Returns true if there are no records.
271
- def none?
272
- return super if block_given?
376
+ #
377
+ # When a pattern argument is given, this method checks whether elements in
378
+ # the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
379
+ #
380
+ # posts.none?(Comment) # => true or false
381
+ def none?(*args)
382
+ return true if @none
383
+
384
+ return super if args.present? || block_given?
273
385
  empty?
274
386
  end
275
387
 
276
388
  # Returns true if there are any records.
277
- def any?
278
- return super if block_given?
389
+ #
390
+ # When a pattern argument is given, this method checks whether elements in
391
+ # the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
392
+ #
393
+ # posts.any?(Post) # => true or false
394
+ def any?(*args)
395
+ return false if @none
396
+
397
+ return super if args.present? || block_given?
279
398
  !empty?
280
399
  end
281
400
 
282
401
  # Returns true if there is exactly one record.
283
- def one?
284
- return super if block_given?
285
- limit_value ? records.one? : size == 1
402
+ #
403
+ # When a pattern argument is given, this method checks whether elements in
404
+ # the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
405
+ #
406
+ # posts.one?(Post) # => true or false
407
+ def one?(*args)
408
+ return false if @none
409
+
410
+ return super if args.present? || block_given?
411
+ return records.one? if loaded?
412
+ limited_count == 1
286
413
  end
287
414
 
288
415
  # Returns true if there is more than one record.
289
416
  def many?
417
+ return false if @none
418
+
290
419
  return super if block_given?
291
- limit_value ? records.many? : size > 1
420
+ return records.many? if loaded?
421
+ limited_count > 1
292
422
  end
293
423
 
294
424
  # Returns a stable cache key that can be used to identify this query.
@@ -298,7 +428,7 @@ module ActiveRecord
298
428
  # # => "products/query-1850ab3d302391b85b8693e941286659"
299
429
  #
300
430
  # If ActiveRecord::Base.collection_cache_versioning is turned off, as it was
301
- # in Rails 6.0 and earlier, the cache key will also include a version.
431
+ # in \Rails 6.0 and earlier, the cache key will also include a version.
302
432
  #
303
433
  # ActiveRecord::Base.collection_cache_versioning = false
304
434
  # Product.where("name like ?", "%Cosmic Encounter%").cache_key
@@ -308,7 +438,7 @@ module ActiveRecord
308
438
  # last updated record.
309
439
  #
310
440
  # Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
311
- def cache_key(timestamp_column = :updated_at)
441
+ def cache_key(timestamp_column = "updated_at")
312
442
  @cache_keys ||= {}
313
443
  @cache_keys[timestamp_column] ||= klass.collection_cache_key(self, timestamp_column)
314
444
  end
@@ -317,7 +447,7 @@ module ActiveRecord
317
447
  query_signature = ActiveSupport::Digest.hexdigest(to_sql)
318
448
  key = "#{klass.model_name.cache_key}/query-#{query_signature}"
319
449
 
320
- if cache_version(timestamp_column)
450
+ if collection_cache_versioning
321
451
  key
322
452
  else
323
453
  "#{key}-#{compute_cache_version(timestamp_column)}"
@@ -343,64 +473,89 @@ module ActiveRecord
343
473
  end
344
474
 
345
475
  def compute_cache_version(timestamp_column) # :nodoc:
346
- if loaded? || distinct_value
476
+ timestamp_column = timestamp_column.to_s
477
+
478
+ if loaded?
347
479
  size = records.size
348
480
  if size > 0
349
- timestamp = max_by(&timestamp_column)._read_attribute(timestamp_column)
481
+ timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
350
482
  end
351
483
  else
352
484
  collection = eager_loading? ? apply_join_dependency : self
353
485
 
354
- column = connection.visitor.compile(arel_attribute(timestamp_column))
355
- select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
486
+ with_connection do |c|
487
+ column = c.visitor.compile(table[timestamp_column])
488
+ select_values = "COUNT(*) AS #{adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
356
489
 
357
- if collection.has_limit_or_offset?
358
- query = collection.select("#{column} AS collection_cache_key_timestamp")
359
- subquery_alias = "subquery_for_cache_key"
360
- subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
361
- arel = query.build_subquery(subquery_alias, select_values % subquery_column)
362
- else
363
- query = collection.unscope(:order)
364
- query.select_values = [select_values % column]
365
- arel = query.arel
366
- end
490
+ if collection.has_limit_or_offset?
491
+ query = collection.select("#{column} AS collection_cache_key_timestamp")
492
+ query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
493
+ subquery_alias = "subquery_for_cache_key"
494
+ subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
495
+ arel = query.build_subquery(subquery_alias, select_values % subquery_column)
496
+ else
497
+ query = collection.unscope(:order)
498
+ query.select_values = [select_values % column]
499
+ arel = query.arel
500
+ end
367
501
 
368
- result = connection.select_one(arel, nil)
502
+ size, timestamp = c.select_rows(arel, nil).first
369
503
 
370
- if result
371
- column_type = klass.type_for_attribute(timestamp_column)
372
- timestamp = column_type.deserialize(result["timestamp"])
373
- size = result["size"]
374
- else
375
- timestamp = nil
376
- size = 0
504
+ if size
505
+ column_type = klass.type_for_attribute(timestamp_column)
506
+ timestamp = column_type.deserialize(timestamp)
507
+ else
508
+ size = 0
509
+ end
377
510
  end
378
511
  end
379
512
 
380
513
  if timestamp
381
- "#{size}-#{timestamp.utc.to_s(cache_timestamp_format)}"
514
+ "#{size}-#{timestamp.utc.to_fs(cache_timestamp_format)}"
382
515
  else
383
516
  "#{size}"
384
517
  end
385
518
  end
386
519
  private :compute_cache_version
387
520
 
521
+ # Returns a cache key along with the version.
522
+ def cache_key_with_version
523
+ if version = cache_version
524
+ "#{cache_key}-#{version}"
525
+ else
526
+ cache_key
527
+ end
528
+ end
529
+
388
530
  # Scope all queries to the current scope.
389
531
  #
390
532
  # Comment.where(post_id: 1).scoping do
391
533
  # Comment.first
392
534
  # end
393
- # # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
535
+ # # SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
536
+ #
537
+ # If <tt>all_queries: true</tt> is passed, scoping will apply to all queries
538
+ # for the relation including +update+ and +delete+ on instances.
539
+ # Once +all_queries+ is set to true it cannot be set to false in a
540
+ # nested block.
394
541
  #
395
542
  # Please check unscoped if you want to remove all previous scopes (including
396
543
  # the default_scope) during the execution of a block.
397
- def scoping
398
- already_in_scope? ? yield : _scoping(self) { yield }
544
+ def scoping(all_queries: nil, &block)
545
+ registry = klass.scope_registry
546
+ if global_scope?(registry) && all_queries == false
547
+ raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
548
+ elsif already_in_scope?(registry)
549
+ yield
550
+ else
551
+ _scoping(self, registry, all_queries, &block)
552
+ end
399
553
  end
400
554
 
401
- def _exec_scope(name, *args, &block) # :nodoc:
555
+ def _exec_scope(...) # :nodoc:
402
556
  @delegate_to_klass = true
403
- _scoping(_deprecated_spawn(name)) { instance_exec(*args, &block) || self }
557
+ registry = klass.scope_registry
558
+ _scoping(nil, registry) { instance_exec(...) || self }
404
559
  ensure
405
560
  @delegate_to_klass = false
406
561
  end
@@ -408,13 +563,14 @@ module ActiveRecord
408
563
  # Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
409
564
  # statement and sends it straight to the database. It does not instantiate the involved models and it does not
410
565
  # trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
411
- # Active Record's normal type casting and serialization.
566
+ # Active Record's normal type casting and serialization. Returns the number of rows affected.
412
567
  #
413
568
  # Note: As Active Record callbacks are not triggered, this method will not automatically update +updated_at+/+updated_on+ columns.
414
569
  #
415
570
  # ==== Parameters
416
571
  #
417
- # * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
572
+ # * +updates+ - A string, array, or hash representing the SET part of an SQL statement. Any strings provided will
573
+ # be type cast, unless you use +Arel.sql+. (Don't pass user-provided values to +Arel.sql+.)
418
574
  #
419
575
  # ==== Examples
420
576
  #
@@ -429,35 +585,40 @@ module ActiveRecord
429
585
  #
430
586
  # # Update all invoices and set the number column to its id value.
431
587
  # Invoice.update_all('number = id')
588
+ #
589
+ # # Update all books with 'Rails' in their title
590
+ # Book.where('title LIKE ?', '%Rails%').update_all(title: Arel.sql("title + ' - volume 1'"))
432
591
  def update_all(updates)
433
592
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?
434
593
 
435
- if eager_loading?
436
- relation = apply_join_dependency
437
- return relation.update_all(updates)
438
- end
439
-
440
- stmt = Arel::UpdateManager.new
441
- stmt.table(arel.join_sources.empty? ? table : arel.source)
442
- stmt.key = arel_attribute(primary_key)
443
- stmt.take(arel.limit)
444
- stmt.offset(arel.offset)
445
- stmt.order(*arel.orders)
446
- stmt.wheres = arel.constraints
594
+ return 0 if @none
447
595
 
448
596
  if updates.is_a?(Hash)
449
597
  if klass.locking_enabled? &&
450
598
  !updates.key?(klass.locking_column) &&
451
599
  !updates.key?(klass.locking_column.to_sym)
452
- attr = arel_attribute(klass.locking_column)
600
+ attr = table[klass.locking_column]
453
601
  updates[attr.name] = _increment_attribute(attr)
454
602
  end
455
- stmt.set _substitute_values(updates)
603
+ values = _substitute_values(updates)
456
604
  else
457
- stmt.set Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
605
+ values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
458
606
  end
459
607
 
460
- @klass.connection.update stmt, "#{@klass} Update All"
608
+ klass.with_connection do |c|
609
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
610
+ arel.source.left = table
611
+
612
+ group_values_arel_columns = arel_columns(group_values.uniq)
613
+ having_clause_ast = having_clause.ast unless having_clause.empty?
614
+ key = if klass.composite_primary_key?
615
+ primary_key.map { |pk| table[pk] }
616
+ else
617
+ table[primary_key]
618
+ end
619
+ stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
620
+ c.update(stmt, "#{klass} Update All").tap { reset }
621
+ end
461
622
  end
462
623
 
463
624
  def update(id = :all, attributes) # :nodoc:
@@ -468,31 +629,330 @@ module ActiveRecord
468
629
  end
469
630
  end
470
631
 
471
- def update_counters(counters) # :nodoc:
632
+ def update!(id = :all, attributes) # :nodoc:
633
+ if id == :all
634
+ each { |record| record.update!(attributes) }
635
+ else
636
+ klass.update!(id, attributes)
637
+ end
638
+ end
639
+
640
+
641
+ # Inserts a single record into the database in a single SQL INSERT
642
+ # statement. It does not instantiate any models nor does it trigger
643
+ # Active Record callbacks or validations. Though passed values
644
+ # go through Active Record's type casting and serialization.
645
+ #
646
+ # See #insert_all for documentation.
647
+ def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
648
+ insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
649
+ end
650
+
651
+ # Inserts multiple records into the database in a single SQL INSERT
652
+ # statement. It does not instantiate any models nor does it trigger
653
+ # Active Record callbacks or validations. Though passed values
654
+ # go through Active Record's type casting and serialization.
655
+ #
656
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
657
+ # the attributes for a single row and must have the same keys.
658
+ #
659
+ # Rows are considered to be unique by every unique index on the table. Any
660
+ # duplicate rows are skipped.
661
+ # Override with <tt>:unique_by</tt> (see below).
662
+ #
663
+ # Returns an ActiveRecord::Result with its contents based on
664
+ # <tt>:returning</tt> (see below).
665
+ #
666
+ # ==== Options
667
+ #
668
+ # [:returning]
669
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
670
+ # inserted records, which by default is the primary key.
671
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
672
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
673
+ # clause entirely.
674
+ #
675
+ # You can also pass an SQL string if you need more control on the return values
676
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
677
+ #
678
+ # [:unique_by]
679
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
680
+ # by every unique index on the table. Any duplicate rows are skipped.
681
+ #
682
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
683
+ #
684
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
685
+ # row has an existing id, or is not unique by another unique index,
686
+ # ActiveRecord::RecordNotUnique is raised.
687
+ #
688
+ # Unique indexes can be identified by columns or name:
689
+ #
690
+ # unique_by: :isbn
691
+ # unique_by: %i[ author_id name ]
692
+ # unique_by: :index_books_on_isbn
693
+ #
694
+ # [:record_timestamps]
695
+ # By default, automatic setting of timestamp columns is controlled by
696
+ # the model's <tt>record_timestamps</tt> config, matching typical
697
+ # behavior.
698
+ #
699
+ # To override this and force automatic setting of timestamp columns one
700
+ # way or the other, pass <tt>:record_timestamps</tt>:
701
+ #
702
+ # record_timestamps: true # Always set timestamps automatically
703
+ # record_timestamps: false # Never set timestamps automatically
704
+ #
705
+ # Because it relies on the index information from the database
706
+ # <tt>:unique_by</tt> is recommended to be paired with
707
+ # Active Record's schema_cache.
708
+ #
709
+ # ==== Example
710
+ #
711
+ # # Insert records and skip inserting any duplicates.
712
+ # # Here "Eloquent Ruby" is skipped because its id is not unique.
713
+ #
714
+ # Book.insert_all([
715
+ # { id: 1, title: "Rework", author: "David" },
716
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
717
+ # ])
718
+ #
719
+ # # insert_all works on chained scopes, and you can use create_with
720
+ # # to set default attributes for all inserted records.
721
+ #
722
+ # author.books.create_with(created_at: Time.now).insert_all([
723
+ # { id: 1, title: "Rework" },
724
+ # { id: 2, title: "Eloquent Ruby" }
725
+ # ])
726
+ def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
727
+ InsertAll.execute(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
728
+ end
729
+
730
+ # Inserts a single record into the database in a single SQL INSERT
731
+ # statement. It does not instantiate any models nor does it trigger
732
+ # Active Record callbacks or validations. Though passed values
733
+ # go through Active Record's type casting and serialization.
734
+ #
735
+ # See #insert_all! for more.
736
+ def insert!(attributes, returning: nil, record_timestamps: nil)
737
+ insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
738
+ end
739
+
740
+ # Inserts multiple records into the database in a single SQL INSERT
741
+ # statement. It does not instantiate any models nor does it trigger
742
+ # Active Record callbacks or validations. Though passed values
743
+ # go through Active Record's type casting and serialization.
744
+ #
745
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
746
+ # the attributes for a single row and must have the same keys.
747
+ #
748
+ # Raises ActiveRecord::RecordNotUnique if any rows violate a
749
+ # unique index on the table. In that case, no rows are inserted.
750
+ #
751
+ # To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
752
+ #
753
+ # Returns an ActiveRecord::Result with its contents based on
754
+ # <tt>:returning</tt> (see below).
755
+ #
756
+ # ==== Options
757
+ #
758
+ # [:returning]
759
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
760
+ # inserted records, which by default is the primary key.
761
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
762
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
763
+ # clause entirely.
764
+ #
765
+ # You can also pass an SQL string if you need more control on the return values
766
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
767
+ #
768
+ # [:record_timestamps]
769
+ # By default, automatic setting of timestamp columns is controlled by
770
+ # the model's <tt>record_timestamps</tt> config, matching typical
771
+ # behavior.
772
+ #
773
+ # To override this and force automatic setting of timestamp columns one
774
+ # way or the other, pass <tt>:record_timestamps</tt>:
775
+ #
776
+ # record_timestamps: true # Always set timestamps automatically
777
+ # record_timestamps: false # Never set timestamps automatically
778
+ #
779
+ # ==== Examples
780
+ #
781
+ # # Insert multiple records
782
+ # Book.insert_all!([
783
+ # { title: "Rework", author: "David" },
784
+ # { title: "Eloquent Ruby", author: "Russ" }
785
+ # ])
786
+ #
787
+ # # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
788
+ # # does not have a unique id.
789
+ # Book.insert_all!([
790
+ # { id: 1, title: "Rework", author: "David" },
791
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
792
+ # ])
793
+ def insert_all!(attributes, returning: nil, record_timestamps: nil)
794
+ InsertAll.execute(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps)
795
+ end
796
+
797
+ # Updates or inserts (upserts) a single record into the database in a
798
+ # single SQL INSERT statement. It does not instantiate any models nor does
799
+ # it trigger Active Record callbacks or validations. Though passed values
800
+ # go through Active Record's type casting and serialization.
801
+ #
802
+ # See #upsert_all for documentation.
803
+ def upsert(attributes, **kwargs)
804
+ upsert_all([ attributes ], **kwargs)
805
+ end
806
+
807
+ # Updates or inserts (upserts) multiple records into the database in a
808
+ # single SQL INSERT statement. It does not instantiate any models nor does
809
+ # it trigger Active Record callbacks or validations. Though passed values
810
+ # go through Active Record's type casting and serialization.
811
+ #
812
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
813
+ # the attributes for a single row and must have the same keys.
814
+ #
815
+ # Returns an ActiveRecord::Result with its contents based on
816
+ # <tt>:returning</tt> (see below).
817
+ #
818
+ # By default, +upsert_all+ will update all the columns that can be updated when
819
+ # there is a conflict. These are all the columns except primary keys, read-only
820
+ # columns, and columns covered by the optional +unique_by+.
821
+ #
822
+ # ==== Options
823
+ #
824
+ # [:returning]
825
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
826
+ # upserted records, which by default is the primary key.
827
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
828
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
829
+ # clause entirely.
830
+ #
831
+ # You can also pass an SQL string if you need more control on the return values
832
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
833
+ #
834
+ # [:unique_by]
835
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
836
+ # by every unique index on the table. Any duplicate rows are skipped.
837
+ #
838
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
839
+ #
840
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
841
+ # row has an existing id, or is not unique by another unique index,
842
+ # ActiveRecord::RecordNotUnique is raised.
843
+ #
844
+ # Unique indexes can be identified by columns or name:
845
+ #
846
+ # unique_by: :isbn
847
+ # unique_by: %i[ author_id name ]
848
+ # unique_by: :index_books_on_isbn
849
+ #
850
+ # Because it relies on the index information from the database
851
+ # <tt>:unique_by</tt> is recommended to be paired with
852
+ # Active Record's schema_cache.
853
+ #
854
+ # [:on_duplicate]
855
+ # Configure the SQL update sentence that will be used in case of conflict.
856
+ #
857
+ # NOTE: If you use this option you must provide all the columns you want to update
858
+ # by yourself.
859
+ #
860
+ # Example:
861
+ #
862
+ # Commodity.upsert_all(
863
+ # [
864
+ # { id: 2, name: "Copper", price: 4.84 },
865
+ # { id: 4, name: "Gold", price: 1380.87 },
866
+ # { id: 6, name: "Aluminium", price: 0.35 }
867
+ # ],
868
+ # on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
869
+ # )
870
+ #
871
+ # See the related +:update_only+ option. Both options can't be used at the same time.
872
+ #
873
+ # [:update_only]
874
+ # Provide a list of column names that will be updated in case of conflict. If not provided,
875
+ # +upsert_all+ will update all the columns that can be updated. These are all the columns
876
+ # except primary keys, read-only columns, and columns covered by the optional +unique_by+
877
+ #
878
+ # Example:
879
+ #
880
+ # Commodity.upsert_all(
881
+ # [
882
+ # { id: 2, name: "Copper", price: 4.84 },
883
+ # { id: 4, name: "Gold", price: 1380.87 },
884
+ # { id: 6, name: "Aluminium", price: 0.35 }
885
+ # ],
886
+ # update_only: [:price] # Only prices will be updated
887
+ # )
888
+ #
889
+ # See the related +:on_duplicate+ option. Both options can't be used at the same time.
890
+ #
891
+ # [:record_timestamps]
892
+ # By default, automatic setting of timestamp columns is controlled by
893
+ # the model's <tt>record_timestamps</tt> config, matching typical
894
+ # behavior.
895
+ #
896
+ # To override this and force automatic setting of timestamp columns one
897
+ # way or the other, pass <tt>:record_timestamps</tt>:
898
+ #
899
+ # record_timestamps: true # Always set timestamps automatically
900
+ # record_timestamps: false # Never set timestamps automatically
901
+ #
902
+ # ==== Examples
903
+ #
904
+ # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
905
+ # # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
906
+ #
907
+ # Book.upsert_all([
908
+ # { title: "Rework", author: "David", isbn: "1" },
909
+ # { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
910
+ # ], unique_by: :isbn)
911
+ #
912
+ # Book.find_by(isbn: "1").title # => "Eloquent Ruby"
913
+ def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
914
+ InsertAll.execute(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
915
+ end
916
+
917
+ # Updates the counters of the records in the current relation.
918
+ #
919
+ # ==== Parameters
920
+ #
921
+ # * +counter+ - A Hash containing the names of the fields to update as keys and the amount to update as values.
922
+ # * <tt>:touch</tt> option - Touch the timestamp columns when updating.
923
+ # * If attributes names are passed, they are updated along with update_at/on attributes.
924
+ #
925
+ # ==== Examples
926
+ #
927
+ # # For Posts by a given author increment the comment_count by 1.
928
+ # Post.where(author_id: author.id).update_counters(comment_count: 1)
929
+ def update_counters(counters)
472
930
  touch = counters.delete(:touch)
473
931
 
474
932
  updates = {}
475
933
  counters.each do |counter_name, value|
476
- attr = arel_attribute(counter_name)
934
+ attr = table[counter_name]
477
935
  updates[attr.name] = _increment_attribute(attr, value)
478
936
  end
479
937
 
480
938
  if touch
481
939
  names = touch if touch != true
482
- touch_updates = klass.touch_attributes_with_time(*names)
940
+ names = Array.wrap(names)
941
+ options = names.extract_options!
942
+ touch_updates = klass.touch_attributes_with_time(*names, **options)
483
943
  updates.merge!(touch_updates) unless touch_updates.empty?
484
944
  end
485
945
 
486
946
  update_all updates
487
947
  end
488
948
 
489
- # Touches all records in the current relation without instantiating records first with the +updated_at+/+updated_on+ attributes
490
- # set to the current time or the time specified.
949
+ # Touches all records in the current relation, setting the +updated_at+/+updated_on+ attributes to the current time or the time specified.
950
+ # It does not instantiate the involved models, and it does not trigger Active Record callbacks or validations.
491
951
  # This method can be passed attribute names and an optional time argument.
492
952
  # If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
493
953
  # If no time argument is passed, the current time is used as default.
494
954
  #
495
- # === Examples
955
+ # ==== Examples
496
956
  #
497
957
  # # Touch all records
498
958
  # Person.all.touch_all
@@ -552,6 +1012,8 @@ module ActiveRecord
552
1012
  # Post.distinct.delete_all
553
1013
  # # => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
554
1014
  def delete_all
1015
+ return 0 if @none
1016
+
555
1017
  invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
556
1018
  value = @values[method]
557
1019
  method == :distinct ? value : value&.any?
@@ -560,23 +1022,79 @@ module ActiveRecord
560
1022
  raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
561
1023
  end
562
1024
 
563
- if eager_loading?
564
- relation = apply_join_dependency
565
- return relation.delete_all
1025
+ klass.with_connection do |c|
1026
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
1027
+ arel.source.left = table
1028
+
1029
+ group_values_arel_columns = arel_columns(group_values.uniq)
1030
+ having_clause_ast = having_clause.ast unless having_clause.empty?
1031
+ key = if klass.composite_primary_key?
1032
+ primary_key.map { |pk| table[pk] }
1033
+ else
1034
+ table[primary_key]
1035
+ end
1036
+ stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
1037
+
1038
+ c.delete(stmt, "#{klass} Delete All").tap { reset }
566
1039
  end
1040
+ end
1041
+
1042
+ # Deletes the row with a primary key matching the +id+ argument, using an
1043
+ # SQL +DELETE+ statement, and returns the number of rows deleted. Active
1044
+ # Record objects are not instantiated, so the object's callbacks are not
1045
+ # executed, including any <tt>:dependent</tt> association options.
1046
+ #
1047
+ # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
1048
+ #
1049
+ # Note: Although it is often much faster than the alternative, #destroy,
1050
+ # skipping callbacks might bypass business logic in your application
1051
+ # that ensures referential integrity or performs other essential jobs.
1052
+ #
1053
+ # ==== Examples
1054
+ #
1055
+ # # Delete a single row
1056
+ # Todo.delete(1)
1057
+ #
1058
+ # # Delete multiple rows
1059
+ # Todo.delete([2,3,4])
1060
+ def delete(id_or_array)
1061
+ return 0 if id_or_array.nil? || (id_or_array.is_a?(Array) && id_or_array.empty?)
567
1062
 
568
- stmt = Arel::DeleteManager.new
569
- stmt.from(arel.join_sources.empty? ? table : arel.source)
570
- stmt.key = arel_attribute(primary_key)
571
- stmt.take(arel.limit)
572
- stmt.offset(arel.offset)
573
- stmt.order(*arel.orders)
574
- stmt.wheres = arel.constraints
1063
+ where(model.primary_key => id_or_array).delete_all
1064
+ end
575
1065
 
576
- affected = @klass.connection.delete(stmt, "#{@klass} Destroy")
577
1066
 
578
- reset
579
- affected
1067
+ # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
1068
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
1069
+ # less efficient than #delete but allows cleanup methods and other actions to be run.
1070
+ #
1071
+ # This essentially finds the object (or multiple objects) with the given id, creates a new object
1072
+ # from the attributes, and then calls destroy on it.
1073
+ #
1074
+ # ==== Parameters
1075
+ #
1076
+ # * +id+ - This should be the id or an array of ids to be destroyed.
1077
+ #
1078
+ # ==== Examples
1079
+ #
1080
+ # # Destroy a single object
1081
+ # Todo.destroy(1)
1082
+ #
1083
+ # # Destroy multiple objects
1084
+ # todos = [1,2,3]
1085
+ # Todo.destroy(todos)
1086
+ def destroy(id)
1087
+ multiple_ids = if model.composite_primary_key?
1088
+ id.first.is_a?(Array)
1089
+ else
1090
+ id.is_a?(Array)
1091
+ end
1092
+
1093
+ if multiple_ids
1094
+ find(id).each(&:destroy)
1095
+ else
1096
+ find(id).destroy
1097
+ end
580
1098
  end
581
1099
 
582
1100
  # Finds and destroys all records matching the specified conditions.
@@ -605,6 +1123,49 @@ module ActiveRecord
605
1123
  where(*args).delete_all
606
1124
  end
607
1125
 
1126
+ # Schedule the query to be performed from a background thread pool.
1127
+ #
1128
+ # Post.where(published: true).load_async # => #<ActiveRecord::Relation>
1129
+ #
1130
+ # When the +Relation+ is iterated, if the background query wasn't executed yet,
1131
+ # it will be performed by the foreground thread.
1132
+ #
1133
+ # Note that {config.active_record.async_query_executor}[https://guides.rubyonrails.org/configuring.html#config-active-record-async-query-executor] must be configured
1134
+ # for queries to actually be executed concurrently. Otherwise it defaults to
1135
+ # executing them in the foreground.
1136
+ #
1137
+ # +load_async+ will also fall back to executing in the foreground in the test environment when transactional
1138
+ # fixtures are enabled.
1139
+ #
1140
+ # If the query was actually executed in the background, the Active Record logs will show
1141
+ # it by prefixing the log line with <tt>ASYNC</tt>:
1142
+ #
1143
+ # ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
1144
+ def load_async
1145
+ with_connection do |c|
1146
+ return load if !c.async_enabled?
1147
+
1148
+ unless loaded?
1149
+ result = exec_main_query(async: c.current_transaction.closed?)
1150
+
1151
+ if result.is_a?(Array)
1152
+ @records = result
1153
+ else
1154
+ @future_result = result
1155
+ end
1156
+ @loaded = true
1157
+ end
1158
+ end
1159
+
1160
+ self
1161
+ end
1162
+
1163
+ # Returns <tt>true</tt> if the relation was scheduled on the background
1164
+ # thread pool.
1165
+ def scheduled?
1166
+ !!@future_result
1167
+ end
1168
+
608
1169
  # Causes the records to be loaded from the database if they have not
609
1170
  # been loaded already. You can use this if for some reason you need
610
1171
  # to explicitly load some records before actually using them. The
@@ -612,7 +1173,10 @@ module ActiveRecord
612
1173
  #
613
1174
  # Post.where(published: true).load # => #<ActiveRecord::Relation>
614
1175
  def load(&block)
615
- exec_queries(&block) unless loaded?
1176
+ if !loaded? || scheduled?
1177
+ @records = exec_queries(&block)
1178
+ @loaded = true
1179
+ end
616
1180
 
617
1181
  self
618
1182
  end
@@ -624,27 +1188,29 @@ module ActiveRecord
624
1188
  end
625
1189
 
626
1190
  def reset
1191
+ @future_result&.cancel
1192
+ @future_result = nil
627
1193
  @delegate_to_klass = false
628
- @_deprecated_scope_source = nil
629
1194
  @to_sql = @arel = @loaded = @should_eager_load = nil
630
- @records = [].freeze
631
- @offsets = {}
1195
+ @offsets = @take = nil
1196
+ @cache_keys = nil
1197
+ @cache_versions = nil
1198
+ @records = nil
632
1199
  self
633
1200
  end
634
1201
 
635
1202
  # Returns sql statement for the relation.
636
1203
  #
637
1204
  # User.where(name: 'Oscar').to_sql
638
- # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
1205
+ # # SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
639
1206
  def to_sql
640
- @to_sql ||= begin
641
- if eager_loading?
642
- apply_join_dependency do |relation, join_dependency|
643
- relation = join_dependency.apply_column_aliases(relation)
644
- relation.to_sql
645
- end
646
- else
647
- conn = klass.connection
1207
+ @to_sql ||= if eager_loading?
1208
+ apply_join_dependency do |relation, join_dependency|
1209
+ relation = join_dependency.apply_column_aliases(relation)
1210
+ relation.to_sql
1211
+ end
1212
+ else
1213
+ klass.with_connection do |conn|
648
1214
  conn.unprepared_statement { conn.to_sql(arel) }
649
1215
  end
650
1216
  end
@@ -654,12 +1220,14 @@ module ActiveRecord
654
1220
  #
655
1221
  # User.where(name: 'Oscar').where_values_hash
656
1222
  # # => {name: "Oscar"}
657
- def where_values_hash(relation_table_name = klass.table_name)
1223
+ def where_values_hash(relation_table_name = klass.table_name) # :nodoc:
658
1224
  where_clause.to_h(relation_table_name)
659
1225
  end
660
1226
 
661
1227
  def scope_for_create
662
- where_values_hash.merge!(create_with_value.stringify_keys)
1228
+ hash = where_clause.to_h(klass.table_name, equality_only: true)
1229
+ create_with_value.each { |k, v| hash[k.to_s] = v } unless create_with_value.empty?
1230
+ hash
663
1231
  end
664
1232
 
665
1233
  # Returns true if relation needs eager loading.
@@ -672,7 +1240,7 @@ module ActiveRecord
672
1240
  # Joins that are also marked for preloading. In which case we should just eager load them.
673
1241
  # Note that this is a naive implementation because we could have strings and symbols which
674
1242
  # represent the same association, but that aren't matched by this. Also, we could have
675
- # nested hashes which partially match, e.g. { a: :b } & { a: [:b, :c] }
1243
+ # nested hashes which partially match, e.g. <tt>{ a: :b } & { a: [:b, :c] }</tt>
676
1244
  def joined_includes_values
677
1245
  includes_values & joins_values
678
1246
  end
@@ -689,8 +1257,13 @@ module ActiveRecord
689
1257
  end
690
1258
  end
691
1259
 
692
- def pretty_print(q)
693
- q.pp(records)
1260
+ def pretty_print(pp)
1261
+ subject = loaded? ? records : annotate("loading for pp")
1262
+ entries = subject.take([limit_value, 11].compact.min)
1263
+
1264
+ entries[10] = "..." if entries.size == 11
1265
+
1266
+ pp.pp(entries)
694
1267
  end
695
1268
 
696
1269
  # Returns true if relation is blank.
@@ -702,8 +1275,12 @@ module ActiveRecord
702
1275
  @values.dup
703
1276
  end
704
1277
 
1278
+ def values_for_queries # :nodoc:
1279
+ @values.except(:extending, :skip_query_cache, :strict_loading)
1280
+ end
1281
+
705
1282
  def inspect
706
- subject = loaded? ? records : self
1283
+ subject = loaded? ? records : annotate("loading for inspect")
707
1284
  entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
708
1285
 
709
1286
  entries[10] = "..." if entries.size == 11
@@ -720,64 +1297,87 @@ module ActiveRecord
720
1297
  end
721
1298
 
722
1299
  def alias_tracker(joins = [], aliases = nil) # :nodoc:
723
- joins += [aliases] if aliases
724
- ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
1300
+ ActiveRecord::Associations::AliasTracker.create(connection_pool, table.name, joins, aliases)
1301
+ end
1302
+
1303
+ class StrictLoadingScope # :nodoc:
1304
+ def self.empty_scope?
1305
+ true
1306
+ end
1307
+
1308
+ def self.strict_loading_value
1309
+ true
1310
+ end
725
1311
  end
726
1312
 
727
1313
  def preload_associations(records) # :nodoc:
728
1314
  preload = preload_values
729
1315
  preload += includes_values unless eager_loading?
730
- preloader = nil
1316
+ scope = strict_loading_value ? StrictLoadingScope : nil
731
1317
  preload.each do |associations|
732
- preloader ||= build_preloader
733
- preloader.preload records, associations
1318
+ ActiveRecord::Associations::Preloader.new(records: records, associations: associations, scope: scope).call
734
1319
  end
735
1320
  end
736
1321
 
737
- attr_reader :_deprecated_scope_source # :nodoc:
738
-
739
1322
  protected
740
- attr_writer :_deprecated_scope_source # :nodoc:
741
-
742
1323
  def load_records(records)
743
1324
  @records = records.freeze
744
1325
  @loaded = true
745
1326
  end
746
1327
 
747
- def null_relation? # :nodoc:
748
- is_a?(NullRelation)
749
- end
750
-
751
1328
  private
752
- def already_in_scope?
753
- @delegate_to_klass && begin
754
- scope = klass.current_scope(true)
755
- scope && !scope._deprecated_scope_source
756
- end
1329
+ def already_in_scope?(registry)
1330
+ @delegate_to_klass && registry.current_scope(klass, true)
757
1331
  end
758
1332
 
759
- def _deprecated_spawn(name)
760
- spawn.tap { |scope| scope._deprecated_scope_source = name }
1333
+ def global_scope?(registry)
1334
+ registry.global_current_scope(klass, true)
761
1335
  end
762
1336
 
763
- def _deprecated_scope_block(name, &block)
1337
+ def current_scope_restoring_block(&block)
1338
+ current_scope = klass.current_scope(true)
764
1339
  -> record do
765
- klass.current_scope = _deprecated_spawn(name)
1340
+ klass.current_scope = current_scope
766
1341
  yield record if block_given?
767
1342
  end
768
1343
  end
769
1344
 
770
- def _scoping(scope)
771
- previous, klass.current_scope = klass.current_scope(true), scope
1345
+ def _new(attributes, &block)
1346
+ klass.new(attributes, &block)
1347
+ end
1348
+
1349
+ def _create(attributes, &block)
1350
+ klass.create(attributes, &block)
1351
+ end
1352
+
1353
+ def _create!(attributes, &block)
1354
+ klass.create!(attributes, &block)
1355
+ end
1356
+
1357
+ def _scoping(scope, registry, all_queries = false)
1358
+ previous = registry.current_scope(klass, true)
1359
+ registry.set_current_scope(klass, scope)
1360
+
1361
+ if all_queries
1362
+ previous_global = registry.global_current_scope(klass, true)
1363
+ registry.set_global_current_scope(klass, scope)
1364
+ end
772
1365
  yield
773
1366
  ensure
774
- klass.current_scope = previous
1367
+ registry.set_current_scope(klass, previous)
1368
+ if all_queries
1369
+ registry.set_global_current_scope(klass, previous_global)
1370
+ end
775
1371
  end
776
1372
 
777
1373
  def _substitute_values(values)
778
1374
  values.map do |name, value|
779
- attr = arel_attribute(name)
780
- unless Arel.arel_node?(value)
1375
+ attr = table[name]
1376
+ if Arel.arel_node?(value)
1377
+ if value.is_a?(Arel::Nodes::SqlLiteral)
1378
+ value = Arel::Nodes::Grouping.new(value)
1379
+ end
1380
+ else
781
1381
  type = klass.type_for_attribute(attr.name)
782
1382
  value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
783
1383
  end
@@ -794,66 +1394,101 @@ module ActiveRecord
794
1394
 
795
1395
  def exec_queries(&block)
796
1396
  skip_query_cache_if_necessary do
797
- @records =
798
- if eager_loading?
1397
+ rows = if scheduled?
1398
+ future = @future_result
1399
+ @future_result = nil
1400
+ future.result
1401
+ else
1402
+ exec_main_query
1403
+ end
1404
+
1405
+ records = instantiate_records(rows, &block)
1406
+ preload_associations(records) unless skip_preloading_value
1407
+
1408
+ records.each(&:readonly!) if readonly_value
1409
+ records.each { |record| record.strict_loading!(strict_loading_value) } unless strict_loading_value.nil?
1410
+
1411
+ records
1412
+ end
1413
+ end
1414
+
1415
+ def exec_main_query(async: false)
1416
+ if @none
1417
+ if async
1418
+ return FutureResult.wrap([])
1419
+ else
1420
+ return []
1421
+ end
1422
+ end
1423
+
1424
+ skip_query_cache_if_necessary do
1425
+ if where_clause.contradiction?
1426
+ [].freeze
1427
+ elsif eager_loading?
1428
+ klass.with_connection do |c|
799
1429
  apply_join_dependency do |relation, join_dependency|
800
1430
  if relation.null_relation?
801
- []
1431
+ [].freeze
802
1432
  else
803
1433
  relation = join_dependency.apply_column_aliases(relation)
804
- rows = connection.select_all(relation.arel, "SQL")
805
- join_dependency.instantiate(rows, &block)
806
- end.freeze
1434
+ @_join_dependency = join_dependency
1435
+ c.select_all(relation.arel, "SQL", async: async)
1436
+ end
807
1437
  end
808
- else
809
- klass.find_by_sql(arel, &block).freeze
810
1438
  end
1439
+ else
1440
+ klass.with_connection do |c|
1441
+ klass._query_by_sql(c, arel, async: async)
1442
+ end
1443
+ end
1444
+ end
1445
+ end
811
1446
 
812
- preload_associations(@records) unless skip_preloading_value
813
-
814
- @records.each(&:readonly!) if readonly_value
815
-
816
- @loaded = true
817
- @records
1447
+ def instantiate_records(rows, &block)
1448
+ return [].freeze if rows.empty?
1449
+ if eager_loading?
1450
+ records = @_join_dependency.instantiate(rows, strict_loading_value, &block).freeze
1451
+ @_join_dependency = nil
1452
+ records
1453
+ else
1454
+ klass._load_from_sql(rows, &block).freeze
818
1455
  end
819
1456
  end
820
1457
 
821
- def skip_query_cache_if_necessary
1458
+ def skip_query_cache_if_necessary(&block)
822
1459
  if skip_query_cache_value
823
- uncached do
824
- yield
825
- end
1460
+ uncached(&block)
826
1461
  else
827
1462
  yield
828
1463
  end
829
1464
  end
830
1465
 
831
- def build_preloader
832
- ActiveRecord::Associations::Preloader.new
833
- end
834
-
835
1466
  def references_eager_loaded_tables?
836
- joined_tables = arel.join_sources.map do |join|
1467
+ joined_tables = build_joins([]).flat_map do |join|
837
1468
  if join.is_a?(Arel::Nodes::StringJoin)
838
1469
  tables_in_string(join.left)
839
1470
  else
840
- [join.left.table_name, join.left.table_alias]
1471
+ join.left.name
841
1472
  end
842
1473
  end
843
1474
 
844
- joined_tables += [table.name, table.table_alias]
1475
+ joined_tables << table.name
845
1476
 
846
1477
  # always convert table names to downcase as in Oracle quoted table names are in uppercase
847
- joined_tables = joined_tables.flatten.compact.map(&:downcase).uniq
1478
+ joined_tables.map!(&:downcase)
848
1479
 
849
- (references_values - joined_tables).any?
1480
+ !(references_values.map(&:to_s) - joined_tables).empty?
850
1481
  end
851
1482
 
852
1483
  def tables_in_string(string)
853
1484
  return [] if string.blank?
854
1485
  # always convert table names to downcase as in Oracle quoted table names are in uppercase
855
1486
  # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
856
- string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ["raw_sql_"]
1487
+ string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
1488
+ end
1489
+
1490
+ def limited_count
1491
+ limit_value ? count : limit(2).count
857
1492
  end
858
1493
  end
859
1494
  end