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,1495 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ # = Active Record \Relation
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
+
54
+ MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
55
+ :order, :joins, :left_outer_joins, :references,
56
+ :extending, :unscope, :optimizer_hints, :annotate,
57
+ :with]
58
+
59
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering, :strict_loading,
60
+ :reverse_order, :distinct, :create_with, :skip_query_cache]
61
+
62
+ CLAUSE_METHODS = [:where, :having, :from]
63
+ INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :with, :with_recursive]
64
+
65
+ VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
66
+
67
+ include Enumerable
68
+ include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
69
+ include SignedId::RelationMethods, TokenFor::RelationMethods
70
+
71
+ attr_reader :table, :model, :loaded, :predicate_builder
72
+ attr_accessor :skip_preloading_value
73
+ alias :klass :model
74
+ alias :loaded? :loaded
75
+ alias :locked? :lock_value
76
+
77
+ def initialize(model, table: model.arel_table, predicate_builder: model.predicate_builder, values: {})
78
+ @model = model
79
+ @table = table
80
+ @values = values
81
+ @loaded = false
82
+ @predicate_builder = predicate_builder
83
+ @delegate_to_model = false
84
+ @future_result = nil
85
+ @records = nil
86
+ @async = false
87
+ @none = false
88
+ end
89
+
90
+ def initialize_copy(other)
91
+ @values = @values.dup
92
+ reset
93
+ end
94
+
95
+ def bind_attribute(name, value) # :nodoc:
96
+ if reflection = model._reflect_on_association(name)
97
+ name = reflection.foreign_key
98
+ value = value.read_attribute(reflection.association_primary_key) unless value.nil?
99
+ end
100
+
101
+ attr = table[name]
102
+ bind = predicate_builder.build_bind_attribute(attr.name, value)
103
+ yield attr, bind
104
+ end
105
+
106
+ # Initializes new record from relation while maintaining the current
107
+ # scope.
108
+ #
109
+ # Expects arguments in the same format as {ActiveRecord::Base.new}[rdoc-ref:Core.new].
110
+ #
111
+ # users = User.where(name: 'DHH')
112
+ # user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>
113
+ #
114
+ # You can also pass a block to new with the new record as argument:
115
+ #
116
+ # user = users.new { |user| user.name = 'Oscar' }
117
+ # user.name # => Oscar
118
+ def new(attributes = nil, &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
125
+ end
126
+ alias build new
127
+
128
+ # Tries to create a new record with the same scoped attributes
129
+ # defined in the relation. Returns the initialized object if validation fails.
130
+ #
131
+ # Expects arguments in the same format as
132
+ # {ActiveRecord::Base.create}[rdoc-ref:Persistence::ClassMethods#create].
133
+ #
134
+ # ==== Examples
135
+ #
136
+ # users = User.where(name: 'Oscar')
137
+ # users.create # => #<User id: 3, name: "Oscar", ...>
138
+ #
139
+ # users.create(name: 'fxn')
140
+ # users.create # => #<User id: 4, name: "fxn", ...>
141
+ #
142
+ # users.create { |user| user.name = 'tenderlove' }
143
+ # # => #<User id: 5, name: "tenderlove", ...>
144
+ #
145
+ # users.create(name: nil) # validation on name
146
+ # # => #<User id: nil, name: nil, ...>
147
+ def create(attributes = nil, &block)
148
+ if attributes.is_a?(Array)
149
+ attributes.collect { |attr| create(attr, &block) }
150
+ else
151
+ block = current_scope_restoring_block(&block)
152
+ scoping { _create(attributes, &block) }
153
+ end
154
+ end
155
+
156
+ # Similar to #create, but calls
157
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!]
158
+ # on the base class. Raises an exception if a validation error occurs.
159
+ #
160
+ # Expects arguments in the same format as
161
+ # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!].
162
+ def create!(attributes = nil, &block)
163
+ if attributes.is_a?(Array)
164
+ attributes.collect { |attr| create!(attr, &block) }
165
+ else
166
+ block = current_scope_restoring_block(&block)
167
+ scoping { _create!(attributes, &block) }
168
+ end
169
+ end
170
+
171
+ def first_or_create(attributes = nil, &block) # :nodoc:
172
+ first || create(attributes, &block)
173
+ end
174
+
175
+ def first_or_create!(attributes = nil, &block) # :nodoc:
176
+ first || create!(attributes, &block)
177
+ end
178
+
179
+ def first_or_initialize(attributes = nil, &block) # :nodoc:
180
+ first || new(attributes, &block)
181
+ end
182
+
183
+ # Finds the first record with the given attributes, or creates a record
184
+ # with the attributes if one is not found:
185
+ #
186
+ # # Find the first user named "Penélope" or create a new one.
187
+ # User.find_or_create_by(first_name: 'Penélope')
188
+ # # => #<User id: 1, first_name: "Penélope", last_name: nil>
189
+ #
190
+ # # Find the first user named "Penélope" or create a new one.
191
+ # # We already have one so the existing record will be returned.
192
+ # User.find_or_create_by(first_name: 'Penélope')
193
+ # # => #<User id: 1, first_name: "Penélope", last_name: nil>
194
+ #
195
+ # # Find the first user named "Scarlett" or create a new one with
196
+ # # a particular last name.
197
+ # User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
198
+ # # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
199
+ #
200
+ # This method accepts a block, which is passed down to #create. The last example
201
+ # above can be alternatively written this way:
202
+ #
203
+ # # Find the first user named "Scarlett" or create a new one with a
204
+ # # particular last name.
205
+ # User.find_or_create_by(first_name: 'Scarlett') do |user|
206
+ # user.last_name = 'Johansson'
207
+ # end
208
+ # # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
209
+ #
210
+ # This method always returns a record, but if creation was attempted and
211
+ # failed due to validation errors it won't be persisted, you get what
212
+ # #create returns in such situation.
213
+ #
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.
219
+ #
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.
224
+ def find_or_create_by(attributes, &block)
225
+ find_by(attributes) || create_or_find_by(attributes, &block)
226
+ end
227
+
228
+ # Like #find_or_create_by, but calls
229
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
230
+ # is raised if the created record is invalid.
231
+ def find_or_create_by!(attributes, &block)
232
+ find_by(attributes) || create_or_find_by!(attributes, &block)
233
+ end
234
+
235
+ # Attempts to create a record with the given attributes in a table that has a unique database constraint
236
+ # on one or several of its columns. If a row already exists with one or several of these
237
+ # unique constraints, the exception such an insertion would normally raise is caught,
238
+ # and the existing record with those attributes is found using #find_by!.
239
+ #
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.
242
+ #
243
+ # There are several drawbacks to #create_or_find_by, though:
244
+ #
245
+ # * The underlying table must have the relevant columns defined with unique database constraints.
246
+ # * A unique constraint violation may be triggered by only one, or at least less than all,
247
+ # of the given attributes. This means that the subsequent #find_by! may fail to find a
248
+ # matching record, which will then raise an ActiveRecord::RecordNotFound exception,
249
+ # rather than a record with the given attributes.
250
+ # * While we avoid the race condition between SELECT -> INSERT from #find_or_create_by,
251
+ # we actually have another race condition between INSERT -> SELECT, which can be triggered
252
+ # if a DELETE between those two statements is run by another client. But for most applications,
253
+ # that's a significantly less likely condition to hit.
254
+ # * It relies on exception handling to handle control flow, which may be marginally slower.
255
+ # * The primary key may auto-increment on each create, even if it fails. This can accelerate
256
+ # the problem of running out of integers, if the underlying table is still stuck on a primary
257
+ # key of type int (note: All \Rails apps since 5.1+ have defaulted to bigint, which is not liable
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.
261
+ #
262
+ # This method will return a record if all given attributes are covered by unique constraints
263
+ # (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
264
+ # and failed due to validation errors it won't be persisted, you get what #create returns in
265
+ # such situation.
266
+ def create_or_find_by(attributes, &block)
267
+ with_connection do |connection|
268
+ transaction(requires_new: true) { create(attributes, &block) }
269
+ rescue ActiveRecord::RecordNotUnique
270
+ if connection.transaction_open?
271
+ where(attributes).lock.find_by!(attributes)
272
+ else
273
+ find_by!(attributes)
274
+ end
275
+ end
276
+ end
277
+
278
+ # Like #create_or_find_by, but calls
279
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
280
+ # is raised if the created record is invalid.
281
+ def create_or_find_by!(attributes, &block)
282
+ with_connection do |connection|
283
+ transaction(requires_new: true) { create!(attributes, &block) }
284
+ rescue ActiveRecord::RecordNotUnique
285
+ if connection.transaction_open?
286
+ where(attributes).lock.find_by!(attributes)
287
+ else
288
+ find_by!(attributes)
289
+ end
290
+ end
291
+ end
292
+
293
+ # Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
294
+ # instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
295
+ def find_or_initialize_by(attributes, &block)
296
+ find_by(attributes) || new(attributes, &block)
297
+ end
298
+
299
+ # Runs EXPLAIN on the query or queries triggered by this relation and
300
+ # returns the result as a string. The string is formatted imitating the
301
+ # ones printed by the database shell.
302
+ #
303
+ # User.all.explain
304
+ # # EXPLAIN SELECT `users`.* FROM `users`
305
+ # # ...
306
+ #
307
+ # Note that this method actually runs the queries, since the results of some
308
+ # are needed by the next ones when eager loading is going on.
309
+ #
310
+ # To run EXPLAIN on queries created by +first+, +pluck+ and +count+, call
311
+ # these methods on +explain+:
312
+ #
313
+ # User.all.explain.count
314
+ # # EXPLAIN SELECT COUNT(*) FROM `users`
315
+ # # ...
316
+ #
317
+ # The column name can be passed if required:
318
+ #
319
+ # User.all.explain.maximum(:id)
320
+ # # EXPLAIN SELECT MAX(`users`.`id`) FROM `users`
321
+ # # ...
322
+ #
323
+ # Please see further details in the
324
+ # {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
325
+ def explain(*options)
326
+ ExplainProxy.new(self, options)
327
+ end
328
+
329
+ # Converts relation objects to Array.
330
+ def to_ary
331
+ records.dup
332
+ end
333
+ alias to_a to_ary
334
+
335
+ def records # :nodoc:
336
+ load
337
+ @records
338
+ end
339
+
340
+ # Serializes the relation objects Array.
341
+ def encode_with(coder)
342
+ coder.represent_seq(nil, records)
343
+ end
344
+
345
+ # Returns size of the records.
346
+ def size
347
+ if loaded?
348
+ records.length
349
+ else
350
+ count(:all)
351
+ end
352
+ end
353
+
354
+ # Returns true if there are no records.
355
+ def empty?
356
+ return true if @none
357
+
358
+ if loaded?
359
+ records.empty?
360
+ else
361
+ !exists?
362
+ end
363
+ end
364
+
365
+ # Returns true if there are no records.
366
+ #
367
+ # When a pattern argument is given, this method checks whether elements in
368
+ # the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
369
+ #
370
+ # posts.none?(Comment) # => true or false
371
+ def none?(*args)
372
+ return true if @none
373
+
374
+ return super if args.present? || block_given?
375
+ empty?
376
+ end
377
+
378
+ # Returns true if there are any records.
379
+ #
380
+ # When a pattern argument is given, this method checks whether elements in
381
+ # the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
382
+ #
383
+ # posts.any?(Post) # => true or false
384
+ def any?(*args)
385
+ return false if @none
386
+
387
+ return super if args.present? || block_given?
388
+ !empty?
389
+ end
390
+
391
+ # Returns true if there is exactly one record.
392
+ #
393
+ # When a pattern argument is given, this method checks whether elements in
394
+ # the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
395
+ #
396
+ # posts.one?(Post) # => true or false
397
+ def one?(*args)
398
+ return false if @none
399
+
400
+ return super if args.present? || block_given?
401
+ return records.one? if loaded?
402
+ limited_count == 1
403
+ end
404
+
405
+ # Returns true if there is more than one record.
406
+ def many?
407
+ return false if @none
408
+
409
+ return super if block_given?
410
+ return records.many? if loaded?
411
+ limited_count > 1
412
+ end
413
+
414
+ # Returns a stable cache key that can be used to identify this query.
415
+ # The cache key is built with a fingerprint of the SQL query.
416
+ #
417
+ # Product.where("name like ?", "%Cosmic Encounter%").cache_key
418
+ # # => "products/query-1850ab3d302391b85b8693e941286659"
419
+ #
420
+ # If ActiveRecord::Base.collection_cache_versioning is turned off, as it was
421
+ # in \Rails 6.0 and earlier, the cache key will also include a version.
422
+ #
423
+ # ActiveRecord::Base.collection_cache_versioning = false
424
+ # Product.where("name like ?", "%Cosmic Encounter%").cache_key
425
+ # # => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"
426
+ #
427
+ # You can also pass a custom timestamp column to fetch the timestamp of the
428
+ # last updated record.
429
+ #
430
+ # Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
431
+ def cache_key(timestamp_column = "updated_at")
432
+ @cache_keys ||= {}
433
+ @cache_keys[timestamp_column] ||= model.collection_cache_key(self, timestamp_column)
434
+ end
435
+
436
+ def compute_cache_key(timestamp_column = :updated_at) # :nodoc:
437
+ query_signature = ActiveSupport::Digest.hexdigest(to_sql)
438
+ key = "#{model.model_name.cache_key}/query-#{query_signature}"
439
+
440
+ if model.collection_cache_versioning
441
+ key
442
+ else
443
+ "#{key}-#{compute_cache_version(timestamp_column)}"
444
+ end
445
+ end
446
+ private :compute_cache_key
447
+
448
+ # Returns a cache version that can be used together with the cache key to form
449
+ # a recyclable caching scheme. The cache version is built with the number of records
450
+ # matching the query, and the timestamp of the last updated record. When a new record
451
+ # comes to match the query, or any of the existing records is updated or deleted,
452
+ # the cache version changes.
453
+ #
454
+ # If the collection is loaded, the method will iterate through the records
455
+ # to generate the timestamp, otherwise it will trigger one SQL query like:
456
+ #
457
+ # SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
458
+ def cache_version(timestamp_column = :updated_at)
459
+ if model.collection_cache_versioning
460
+ @cache_versions ||= {}
461
+ @cache_versions[timestamp_column] ||= compute_cache_version(timestamp_column)
462
+ end
463
+ end
464
+
465
+ def compute_cache_version(timestamp_column) # :nodoc:
466
+ timestamp_column = timestamp_column.to_s
467
+
468
+ if loaded?
469
+ size = records.size
470
+ if size > 0
471
+ timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
472
+ end
473
+ else
474
+ collection = eager_loading? ? apply_join_dependency : self
475
+
476
+ with_connection do |c|
477
+ column = c.visitor.compile(table[timestamp_column])
478
+ select_values = "COUNT(*) AS #{model.adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
479
+
480
+ if collection.has_limit_or_offset?
481
+ query = collection.select("#{column} AS collection_cache_key_timestamp")
482
+ query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
483
+ subquery_alias = "subquery_for_cache_key"
484
+ subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
485
+ arel = query.build_subquery(subquery_alias, select_values % subquery_column)
486
+ else
487
+ query = collection.unscope(:order)
488
+ query.select_values = [select_values % column]
489
+ arel = query.arel
490
+ end
491
+
492
+ size, timestamp = c.select_rows(arel, nil).first
493
+
494
+ if size
495
+ column_type = model.type_for_attribute(timestamp_column)
496
+ timestamp = column_type.deserialize(timestamp)
497
+ else
498
+ size = 0
499
+ end
500
+ end
501
+ end
502
+
503
+ if timestamp
504
+ "#{size}-#{timestamp.utc.to_fs(model.cache_timestamp_format)}"
505
+ else
506
+ "#{size}"
507
+ end
508
+ end
509
+ private :compute_cache_version
510
+
511
+ # Returns a cache key along with the version.
512
+ def cache_key_with_version
513
+ if version = cache_version
514
+ "#{cache_key}-#{version}"
515
+ else
516
+ cache_key
517
+ end
518
+ end
519
+
520
+ # Scope all queries to the current scope.
521
+ #
522
+ # Comment.where(post_id: 1).scoping do
523
+ # Comment.first
524
+ # end
525
+ # # SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
526
+ #
527
+ # If <tt>all_queries: true</tt> is passed, scoping will apply to all queries
528
+ # for the relation including +update+ and +delete+ on instances.
529
+ # Once +all_queries+ is set to true it cannot be set to false in a
530
+ # nested block.
531
+ #
532
+ # Please check unscoped if you want to remove all previous scopes (including
533
+ # the default_scope) during the execution of a block.
534
+ def scoping(all_queries: nil, &block)
535
+ registry = model.scope_registry
536
+ if global_scope?(registry) && all_queries == false
537
+ raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
538
+ elsif already_in_scope?(registry)
539
+ yield
540
+ else
541
+ _scoping(self, registry, all_queries, &block)
542
+ end
543
+ end
544
+
545
+ def _exec_scope(...) # :nodoc:
546
+ @delegate_to_model = true
547
+ registry = model.scope_registry
548
+ _scoping(nil, registry) { instance_exec(...) || self }
549
+ ensure
550
+ @delegate_to_model = false
551
+ end
552
+
553
+ # Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
554
+ # statement and sends it straight to the database. It does not instantiate the involved models and it does not
555
+ # trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
556
+ # Active Record's normal type casting and serialization. Returns the number of rows affected.
557
+ #
558
+ # Note: As Active Record callbacks are not triggered, this method will not automatically update +updated_at+/+updated_on+ columns.
559
+ #
560
+ # ==== Parameters
561
+ #
562
+ # * +updates+ - A string, array, or hash representing the SET part of an SQL statement. Any strings provided will
563
+ # be type cast, unless you use +Arel.sql+. (Don't pass user-provided values to +Arel.sql+.)
564
+ #
565
+ # ==== Examples
566
+ #
567
+ # # Update all customers with the given attributes
568
+ # Customer.update_all wants_email: true
569
+ #
570
+ # # Update all books with 'Rails' in their title
571
+ # Book.where('title LIKE ?', '%Rails%').update_all(author: 'David')
572
+ #
573
+ # # Update all books that match conditions, but limit it to 5 ordered by date
574
+ # Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David')
575
+ #
576
+ # # Update all invoices and set the number column to its id value.
577
+ # Invoice.update_all('number = id')
578
+ #
579
+ # # Update all books with 'Rails' in their title
580
+ # Book.where('title LIKE ?', '%Rails%').update_all(title: Arel.sql("title + ' - volume 1'"))
581
+ def update_all(updates)
582
+ raise ArgumentError, "Empty list of attributes to change" if updates.blank?
583
+
584
+ return 0 if @none
585
+
586
+ if updates.is_a?(Hash)
587
+ if model.locking_enabled? &&
588
+ !updates.key?(model.locking_column) &&
589
+ !updates.key?(model.locking_column.to_sym)
590
+ attr = table[model.locking_column]
591
+ updates[attr.name] = _increment_attribute(attr)
592
+ end
593
+ values = _substitute_values(updates)
594
+ else
595
+ values = Arel.sql(model.sanitize_sql_for_assignment(updates, table.name))
596
+ end
597
+
598
+ model.with_connection do |c|
599
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
600
+ arel.source.left = table
601
+
602
+ group_values_arel_columns = arel_columns(group_values.uniq)
603
+ having_clause_ast = having_clause.ast unless having_clause.empty?
604
+ key = if model.composite_primary_key?
605
+ primary_key.map { |pk| table[pk] }
606
+ else
607
+ table[primary_key]
608
+ end
609
+ stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
610
+ c.update(stmt, "#{model} Update All").tap { reset }
611
+ end
612
+ end
613
+
614
+ def update(id = :all, attributes) # :nodoc:
615
+ if id == :all
616
+ each { |record| record.update(attributes) }
617
+ else
618
+ model.update(id, attributes)
619
+ end
620
+ end
621
+
622
+ def update!(id = :all, attributes) # :nodoc:
623
+ if id == :all
624
+ each { |record| record.update!(attributes) }
625
+ else
626
+ model.update!(id, attributes)
627
+ end
628
+ end
629
+
630
+
631
+ # Inserts a single record into the database in a single SQL INSERT
632
+ # statement. It does not instantiate any models nor does it trigger
633
+ # Active Record callbacks or validations. Though passed values
634
+ # go through Active Record's type casting and serialization.
635
+ #
636
+ # See #insert_all for documentation.
637
+ def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
638
+ insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
639
+ end
640
+
641
+ # Inserts multiple records 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
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
647
+ # the attributes for a single row and must have the same keys.
648
+ #
649
+ # Rows are considered to be unique by every unique index on the table. Any
650
+ # duplicate rows are skipped.
651
+ # Override with <tt>:unique_by</tt> (see below).
652
+ #
653
+ # Returns an ActiveRecord::Result with its contents based on
654
+ # <tt>:returning</tt> (see below).
655
+ #
656
+ # ==== Options
657
+ #
658
+ # [:returning]
659
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
660
+ # inserted records, which by default is the primary key.
661
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
662
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
663
+ # clause entirely.
664
+ #
665
+ # You can also pass an SQL string if you need more control on the return values
666
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
667
+ #
668
+ # [:unique_by]
669
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
670
+ # by every unique index on the table. Any duplicate rows are skipped.
671
+ #
672
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
673
+ #
674
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
675
+ # row has an existing id, or is not unique by another unique index,
676
+ # ActiveRecord::RecordNotUnique is raised.
677
+ #
678
+ # Unique indexes can be identified by columns or name:
679
+ #
680
+ # unique_by: :isbn
681
+ # unique_by: %i[ author_id name ]
682
+ # unique_by: :index_books_on_isbn
683
+ #
684
+ # [:record_timestamps]
685
+ # By default, automatic setting of timestamp columns is controlled by
686
+ # the model's <tt>record_timestamps</tt> config, matching typical
687
+ # behavior.
688
+ #
689
+ # To override this and force automatic setting of timestamp columns one
690
+ # way or the other, pass <tt>:record_timestamps</tt>:
691
+ #
692
+ # record_timestamps: true # Always set timestamps automatically
693
+ # record_timestamps: false # Never set timestamps automatically
694
+ #
695
+ # Because it relies on the index information from the database
696
+ # <tt>:unique_by</tt> is recommended to be paired with
697
+ # Active Record's schema_cache.
698
+ #
699
+ # ==== Example
700
+ #
701
+ # # Insert records and skip inserting any duplicates.
702
+ # # Here "Eloquent Ruby" is skipped because its id is not unique.
703
+ #
704
+ # Book.insert_all([
705
+ # { id: 1, title: "Rework", author: "David" },
706
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
707
+ # ])
708
+ #
709
+ # # insert_all works on chained scopes, and you can use create_with
710
+ # # to set default attributes for all inserted records.
711
+ #
712
+ # author.books.create_with(created_at: Time.now).insert_all([
713
+ # { id: 1, title: "Rework" },
714
+ # { id: 2, title: "Eloquent Ruby" }
715
+ # ])
716
+ def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
717
+ InsertAll.execute(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
718
+ end
719
+
720
+ # Inserts a single record into the database in a single SQL INSERT
721
+ # statement. It does not instantiate any models nor does it trigger
722
+ # Active Record callbacks or validations. Though passed values
723
+ # go through Active Record's type casting and serialization.
724
+ #
725
+ # See #insert_all! for more.
726
+ def insert!(attributes, returning: nil, record_timestamps: nil)
727
+ insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
728
+ end
729
+
730
+ # Inserts multiple records 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
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
736
+ # the attributes for a single row and must have the same keys.
737
+ #
738
+ # Raises ActiveRecord::RecordNotUnique if any rows violate a
739
+ # unique index on the table. In that case, no rows are inserted.
740
+ #
741
+ # To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
742
+ #
743
+ # Returns an ActiveRecord::Result with its contents based on
744
+ # <tt>:returning</tt> (see below).
745
+ #
746
+ # ==== Options
747
+ #
748
+ # [:returning]
749
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
750
+ # inserted records, which by default is the primary key.
751
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
752
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
753
+ # clause entirely.
754
+ #
755
+ # You can also pass an SQL string if you need more control on the return values
756
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
757
+ #
758
+ # [:record_timestamps]
759
+ # By default, automatic setting of timestamp columns is controlled by
760
+ # the model's <tt>record_timestamps</tt> config, matching typical
761
+ # behavior.
762
+ #
763
+ # To override this and force automatic setting of timestamp columns one
764
+ # way or the other, pass <tt>:record_timestamps</tt>:
765
+ #
766
+ # record_timestamps: true # Always set timestamps automatically
767
+ # record_timestamps: false # Never set timestamps automatically
768
+ #
769
+ # ==== Examples
770
+ #
771
+ # # Insert multiple records
772
+ # Book.insert_all!([
773
+ # { title: "Rework", author: "David" },
774
+ # { title: "Eloquent Ruby", author: "Russ" }
775
+ # ])
776
+ #
777
+ # # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
778
+ # # does not have a unique id.
779
+ # Book.insert_all!([
780
+ # { id: 1, title: "Rework", author: "David" },
781
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
782
+ # ])
783
+ def insert_all!(attributes, returning: nil, record_timestamps: nil)
784
+ InsertAll.execute(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps)
785
+ end
786
+
787
+ # Updates or inserts (upserts) a single record into the database in a
788
+ # single SQL INSERT statement. It does not instantiate any models nor does
789
+ # it trigger Active Record callbacks or validations. Though passed values
790
+ # go through Active Record's type casting and serialization.
791
+ #
792
+ # See #upsert_all for documentation.
793
+ def upsert(attributes, **kwargs)
794
+ upsert_all([ attributes ], **kwargs)
795
+ end
796
+
797
+ # Updates or inserts (upserts) multiple records 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
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
803
+ # the attributes for a single row and must have the same keys.
804
+ #
805
+ # Returns an ActiveRecord::Result with its contents based on
806
+ # <tt>:returning</tt> (see below).
807
+ #
808
+ # By default, +upsert_all+ will update all the columns that can be updated when
809
+ # there is a conflict. These are all the columns except primary keys, read-only
810
+ # columns, and columns covered by the optional +unique_by+.
811
+ #
812
+ # ==== Options
813
+ #
814
+ # [:returning]
815
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
816
+ # inserted records, which by default is the primary key.
817
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
818
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
819
+ # clause entirely.
820
+ #
821
+ # You can also pass an SQL string if you need more control on the return values
822
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
823
+ #
824
+ # [:unique_by]
825
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
826
+ # by every unique index on the table. Any duplicate rows are skipped.
827
+ #
828
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
829
+ #
830
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
831
+ # row has an existing id, or is not unique by another unique index,
832
+ # ActiveRecord::RecordNotUnique is raised.
833
+ #
834
+ # Unique indexes can be identified by columns or name:
835
+ #
836
+ # unique_by: :isbn
837
+ # unique_by: %i[ author_id name ]
838
+ # unique_by: :index_books_on_isbn
839
+ #
840
+ # Because it relies on the index information from the database
841
+ # <tt>:unique_by</tt> is recommended to be paired with
842
+ # Active Record's schema_cache.
843
+ #
844
+ # [:on_duplicate]
845
+ # Configure the SQL update sentence that will be used in case of conflict.
846
+ #
847
+ # NOTE: If you use this option you must provide all the columns you want to update
848
+ # by yourself.
849
+ #
850
+ # Example:
851
+ #
852
+ # Commodity.upsert_all(
853
+ # [
854
+ # { id: 2, name: "Copper", price: 4.84 },
855
+ # { id: 4, name: "Gold", price: 1380.87 },
856
+ # { id: 6, name: "Aluminium", price: 0.35 }
857
+ # ],
858
+ # on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
859
+ # )
860
+ #
861
+ # See the related +:update_only+ option. Both options can't be used at the same time.
862
+ #
863
+ # [:update_only]
864
+ # Provide a list of column names that will be updated in case of conflict. If not provided,
865
+ # +upsert_all+ will update all the columns that can be updated. These are all the columns
866
+ # except primary keys, read-only columns, and columns covered by the optional +unique_by+
867
+ #
868
+ # Example:
869
+ #
870
+ # Commodity.upsert_all(
871
+ # [
872
+ # { id: 2, name: "Copper", price: 4.84 },
873
+ # { id: 4, name: "Gold", price: 1380.87 },
874
+ # { id: 6, name: "Aluminium", price: 0.35 }
875
+ # ],
876
+ # update_only: [:price] # Only prices will be updated
877
+ # )
878
+ #
879
+ # See the related +:on_duplicate+ option. Both options can't be used at the same time.
880
+ #
881
+ # [:record_timestamps]
882
+ # By default, automatic setting of timestamp columns is controlled by
883
+ # the model's <tt>record_timestamps</tt> config, matching typical
884
+ # behavior.
885
+ #
886
+ # To override this and force automatic setting of timestamp columns one
887
+ # way or the other, pass <tt>:record_timestamps</tt>:
888
+ #
889
+ # record_timestamps: true # Always set timestamps automatically
890
+ # record_timestamps: false # Never set timestamps automatically
891
+ #
892
+ # ==== Examples
893
+ #
894
+ # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
895
+ # # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
896
+ #
897
+ # Book.upsert_all([
898
+ # { title: "Rework", author: "David", isbn: "1" },
899
+ # { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
900
+ # ], unique_by: :isbn)
901
+ #
902
+ # Book.find_by(isbn: "1").title # => "Eloquent Ruby"
903
+ def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
904
+ InsertAll.execute(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
905
+ end
906
+
907
+ # Updates the counters of the records in the current relation.
908
+ #
909
+ # ==== Parameters
910
+ #
911
+ # * +counter+ - A Hash containing the names of the fields to update as keys and the amount to update as values.
912
+ # * <tt>:touch</tt> option - Touch the timestamp columns when updating.
913
+ # * If attributes names are passed, they are updated along with update_at/on attributes.
914
+ #
915
+ # ==== Examples
916
+ #
917
+ # # For Posts by a given author increment the comment_count by 1.
918
+ # Post.where(author_id: author.id).update_counters(comment_count: 1)
919
+ def update_counters(counters)
920
+ touch = counters.delete(:touch)
921
+
922
+ updates = {}
923
+ counters.each do |counter_name, value|
924
+ attr = table[counter_name]
925
+ updates[attr.name] = _increment_attribute(attr, value)
926
+ end
927
+
928
+ if touch
929
+ names = touch if touch != true
930
+ names = Array.wrap(names)
931
+ options = names.extract_options!
932
+ touch_updates = model.touch_attributes_with_time(*names, **options)
933
+ updates.merge!(touch_updates) unless touch_updates.empty?
934
+ end
935
+
936
+ update_all updates
937
+ end
938
+
939
+ # Touches all records in the current relation, setting the +updated_at+/+updated_on+ attributes to the current time or the time specified.
940
+ # It does not instantiate the involved models, and it does not trigger Active Record callbacks or validations.
941
+ # This method can be passed attribute names and an optional time argument.
942
+ # If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
943
+ # If no time argument is passed, the current time is used as default.
944
+ #
945
+ # === Examples
946
+ #
947
+ # # Touch all records
948
+ # Person.all.touch_all
949
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670'"
950
+ #
951
+ # # Touch multiple records with a custom attribute
952
+ # Person.all.touch_all(:created_at)
953
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670', \"created_at\" = '2018-01-04 22:55:23.132670'"
954
+ #
955
+ # # Touch multiple records with a specified time
956
+ # Person.all.touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
957
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2020-05-16 00:00:00'"
958
+ #
959
+ # # Touch records with scope
960
+ # Person.where(name: 'David').touch_all
961
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
962
+ def touch_all(*names, time: nil)
963
+ update_all model.touch_attributes_with_time(*names, time: time)
964
+ end
965
+
966
+ # Destroys the records by instantiating each
967
+ # record and calling its {#destroy}[rdoc-ref:Persistence#destroy] method.
968
+ # Each object's callbacks are executed (including <tt>:dependent</tt> association options).
969
+ # Returns the collection of objects that were destroyed; each will be frozen, to
970
+ # reflect that no changes should be made (since they can't be persisted).
971
+ #
972
+ # Note: Instantiation, callback execution, and deletion of each
973
+ # record can be time consuming when you're removing many records at
974
+ # once. It generates at least one SQL +DELETE+ query per record (or
975
+ # possibly more, to enforce your callbacks). If you want to delete many
976
+ # rows quickly, without concern for their associations or callbacks, use
977
+ # #delete_all instead.
978
+ #
979
+ # ==== Examples
980
+ #
981
+ # Person.where(age: 0..18).destroy_all
982
+ def destroy_all
983
+ records.each(&:destroy).tap { reset }
984
+ end
985
+
986
+ # Deletes the records without instantiating the records
987
+ # first, and hence not calling the {#destroy}[rdoc-ref:Persistence#destroy]
988
+ # method nor invoking callbacks.
989
+ # This is a single SQL DELETE statement that goes straight to the database, much more
990
+ # efficient than #destroy_all. Be careful with relations though, in particular
991
+ # <tt>:dependent</tt> rules defined on associations are not honored. Returns the
992
+ # number of rows affected.
993
+ #
994
+ # Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
995
+ #
996
+ # Both calls delete the affected posts all at once with a single DELETE statement.
997
+ # If you need to destroy dependent associations or call your <tt>before_*</tt> or
998
+ # +after_destroy+ callbacks, use the #destroy_all method instead.
999
+ #
1000
+ # If an invalid method is supplied, #delete_all raises an ActiveRecordError:
1001
+ #
1002
+ # Post.distinct.delete_all
1003
+ # # => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
1004
+ def delete_all
1005
+ return 0 if @none
1006
+
1007
+ invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
1008
+ value = @values[method]
1009
+ method == :distinct ? value : value&.any?
1010
+ end
1011
+ if invalid_methods.any?
1012
+ raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
1013
+ end
1014
+
1015
+ model.with_connection do |c|
1016
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
1017
+ arel.source.left = table
1018
+
1019
+ group_values_arel_columns = arel_columns(group_values.uniq)
1020
+ having_clause_ast = having_clause.ast unless having_clause.empty?
1021
+ key = if model.composite_primary_key?
1022
+ primary_key.map { |pk| table[pk] }
1023
+ else
1024
+ table[primary_key]
1025
+ end
1026
+ stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
1027
+
1028
+ c.delete(stmt, "#{model} Delete All").tap { reset }
1029
+ end
1030
+ end
1031
+
1032
+ # Deletes the row with a primary key matching the +id+ argument, using an
1033
+ # SQL +DELETE+ statement, and returns the number of rows deleted. Active
1034
+ # Record objects are not instantiated, so the object's callbacks are not
1035
+ # executed, including any <tt>:dependent</tt> association options.
1036
+ #
1037
+ # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
1038
+ #
1039
+ # Note: Although it is often much faster than the alternative, #destroy,
1040
+ # skipping callbacks might bypass business logic in your application
1041
+ # that ensures referential integrity or performs other essential jobs.
1042
+ #
1043
+ # ==== Examples
1044
+ #
1045
+ # # Delete a single row
1046
+ # Todo.delete(1)
1047
+ #
1048
+ # # Delete multiple rows
1049
+ # Todo.delete([2,3,4])
1050
+ def delete(id_or_array)
1051
+ return 0 if id_or_array.nil? || (id_or_array.is_a?(Array) && id_or_array.empty?)
1052
+
1053
+ where(model.primary_key => id_or_array).delete_all
1054
+ end
1055
+
1056
+
1057
+ # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
1058
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
1059
+ # less efficient than #delete but allows cleanup methods and other actions to be run.
1060
+ #
1061
+ # This essentially finds the object (or multiple objects) with the given id, creates a new object
1062
+ # from the attributes, and then calls destroy on it.
1063
+ #
1064
+ # ==== Parameters
1065
+ #
1066
+ # * +id+ - This should be the id or an array of ids to be destroyed.
1067
+ #
1068
+ # ==== Examples
1069
+ #
1070
+ # # Destroy a single object
1071
+ # Todo.destroy(1)
1072
+ #
1073
+ # # Destroy multiple objects
1074
+ # todos = [1,2,3]
1075
+ # Todo.destroy(todos)
1076
+ def destroy(id)
1077
+ multiple_ids = if model.composite_primary_key?
1078
+ id.first.is_a?(Array)
1079
+ else
1080
+ id.is_a?(Array)
1081
+ end
1082
+
1083
+ if multiple_ids
1084
+ find(id).each(&:destroy)
1085
+ else
1086
+ find(id).destroy
1087
+ end
1088
+ end
1089
+
1090
+ # Finds and destroys all records matching the specified conditions.
1091
+ # This is short-hand for <tt>relation.where(condition).destroy_all</tt>.
1092
+ # Returns the collection of objects that were destroyed.
1093
+ #
1094
+ # If no record is found, returns empty array.
1095
+ #
1096
+ # Person.destroy_by(id: 13)
1097
+ # Person.destroy_by(name: 'Spartacus', rating: 4)
1098
+ # Person.destroy_by("published_at < ?", 2.weeks.ago)
1099
+ def destroy_by(*args)
1100
+ where(*args).destroy_all
1101
+ end
1102
+
1103
+ # Finds and deletes all records matching the specified conditions.
1104
+ # This is short-hand for <tt>relation.where(condition).delete_all</tt>.
1105
+ # Returns the number of rows affected.
1106
+ #
1107
+ # If no record is found, returns <tt>0</tt> as zero rows were affected.
1108
+ #
1109
+ # Person.delete_by(id: 13)
1110
+ # Person.delete_by(name: 'Spartacus', rating: 4)
1111
+ # Person.delete_by("published_at < ?", 2.weeks.ago)
1112
+ def delete_by(*args)
1113
+ where(*args).delete_all
1114
+ end
1115
+
1116
+ # Schedule the query to be performed from a background thread pool.
1117
+ #
1118
+ # Post.where(published: true).load_async # => #<ActiveRecord::Relation>
1119
+ #
1120
+ # When the +Relation+ is iterated, if the background query wasn't executed yet,
1121
+ # it will be performed by the foreground thread.
1122
+ #
1123
+ # Note that {config.active_record.async_query_executor}[https://guides.rubyonrails.org/configuring.html#config-active-record-async-query-executor] must be configured
1124
+ # for queries to actually be executed concurrently. Otherwise it defaults to
1125
+ # executing them in the foreground.
1126
+ #
1127
+ # If the query was actually executed in the background, the Active Record logs will show
1128
+ # it by prefixing the log line with <tt>ASYNC</tt>:
1129
+ #
1130
+ # ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
1131
+ def load_async
1132
+ with_connection do |c|
1133
+ return load if !c.async_enabled?
1134
+
1135
+ unless loaded?
1136
+ result = exec_main_query(async: !c.current_transaction.joinable?)
1137
+
1138
+ if result.is_a?(Array)
1139
+ @records = result
1140
+ else
1141
+ @future_result = result
1142
+ end
1143
+ @loaded = true
1144
+ end
1145
+ end
1146
+
1147
+ self
1148
+ end
1149
+
1150
+ def then(&block) # :nodoc:
1151
+ if @future_result
1152
+ @future_result.then do
1153
+ yield self
1154
+ end
1155
+ else
1156
+ super
1157
+ end
1158
+ end
1159
+
1160
+ # Returns <tt>true</tt> if the relation was scheduled on the background
1161
+ # thread pool.
1162
+ def scheduled?
1163
+ !!@future_result
1164
+ end
1165
+
1166
+ # Causes the records to be loaded from the database if they have not
1167
+ # been loaded already. You can use this if for some reason you need
1168
+ # to explicitly load some records before actually using them. The
1169
+ # return value is the relation itself, not the records.
1170
+ #
1171
+ # Post.where(published: true).load # => #<ActiveRecord::Relation>
1172
+ def load(&block)
1173
+ if !loaded? || scheduled?
1174
+ @records = exec_queries(&block)
1175
+ @loaded = true
1176
+ end
1177
+
1178
+ self
1179
+ end
1180
+
1181
+ # Forces reloading of relation.
1182
+ def reload
1183
+ reset
1184
+ load
1185
+ end
1186
+
1187
+ def reset
1188
+ @future_result&.cancel
1189
+ @future_result = nil
1190
+ @delegate_to_model = false
1191
+ @to_sql = @arel = @loaded = @should_eager_load = nil
1192
+ @offsets = @take = nil
1193
+ @cache_keys = nil
1194
+ @cache_versions = nil
1195
+ @records = nil
1196
+ self
1197
+ end
1198
+
1199
+ # Returns sql statement for the relation.
1200
+ #
1201
+ # User.where(name: 'Oscar').to_sql
1202
+ # # SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
1203
+ def to_sql
1204
+ @to_sql ||= if eager_loading?
1205
+ apply_join_dependency do |relation, join_dependency|
1206
+ relation = join_dependency.apply_column_aliases(relation)
1207
+ relation.to_sql
1208
+ end
1209
+ else
1210
+ model.with_connection do |conn|
1211
+ conn.unprepared_statement { conn.to_sql(arel) }
1212
+ end
1213
+ end
1214
+ end
1215
+
1216
+ # Returns a hash of where conditions.
1217
+ #
1218
+ # User.where(name: 'Oscar').where_values_hash
1219
+ # # => {name: "Oscar"}
1220
+ def where_values_hash(relation_table_name = model.table_name) # :nodoc:
1221
+ where_clause.to_h(relation_table_name)
1222
+ end
1223
+
1224
+ def scope_for_create
1225
+ hash = where_clause.to_h(model.table_name, equality_only: true)
1226
+ create_with_value.each { |k, v| hash[k.to_s] = v } unless create_with_value.empty?
1227
+ hash
1228
+ end
1229
+
1230
+ # Returns true if relation needs eager loading.
1231
+ def eager_loading?
1232
+ @should_eager_load ||=
1233
+ eager_load_values.any? ||
1234
+ includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
1235
+ end
1236
+
1237
+ # Joins that are also marked for preloading. In which case we should just eager load them.
1238
+ # Note that this is a naive implementation because we could have strings and symbols which
1239
+ # represent the same association, but that aren't matched by this. Also, we could have
1240
+ # nested hashes which partially match, e.g. <tt>{ a: :b } & { a: [:b, :c] }</tt>
1241
+ def joined_includes_values
1242
+ includes_values & joins_values
1243
+ end
1244
+
1245
+ # Compares two relations for equality.
1246
+ def ==(other)
1247
+ case other
1248
+ when Associations::CollectionProxy, AssociationRelation
1249
+ self == other.records
1250
+ when Relation
1251
+ other.to_sql == to_sql
1252
+ when Array
1253
+ records == other
1254
+ end
1255
+ end
1256
+
1257
+ def pretty_print(pp)
1258
+ subject = loaded? ? records : annotate("loading for pp")
1259
+ entries = subject.take([limit_value, 11].compact.min)
1260
+
1261
+ entries[10] = "..." if entries.size == 11
1262
+
1263
+ pp.pp(entries)
1264
+ end
1265
+
1266
+ # Returns true if relation is blank.
1267
+ def blank?
1268
+ records.blank?
1269
+ end
1270
+
1271
+ def readonly?
1272
+ readonly_value
1273
+ end
1274
+
1275
+ def values
1276
+ @values.dup
1277
+ end
1278
+
1279
+ def values_for_queries # :nodoc:
1280
+ @values.except(:extending, :skip_query_cache, :strict_loading)
1281
+ end
1282
+
1283
+ def inspect
1284
+ subject = loaded? ? records : annotate("loading for inspect")
1285
+ entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
1286
+
1287
+ entries[10] = "..." if entries.size == 11
1288
+
1289
+ "#<#{self.class.name} [#{entries.join(', ')}]>"
1290
+ end
1291
+
1292
+ def empty_scope? # :nodoc:
1293
+ @values == model.unscoped.values
1294
+ end
1295
+
1296
+ def has_limit_or_offset? # :nodoc:
1297
+ limit_value || offset_value
1298
+ end
1299
+
1300
+ def alias_tracker(joins = [], aliases = nil) # :nodoc:
1301
+ ActiveRecord::Associations::AliasTracker.create(model.connection_pool, table.name, joins, aliases)
1302
+ end
1303
+
1304
+ class StrictLoadingScope # :nodoc:
1305
+ def self.empty_scope?
1306
+ true
1307
+ end
1308
+
1309
+ def self.strict_loading_value
1310
+ true
1311
+ end
1312
+ end
1313
+
1314
+ def preload_associations(records) # :nodoc:
1315
+ preload = preload_values
1316
+ preload += includes_values unless eager_loading?
1317
+ scope = strict_loading_value ? StrictLoadingScope : nil
1318
+ preload.each do |associations|
1319
+ ActiveRecord::Associations::Preloader.new(records: records, associations: associations, scope: scope).call
1320
+ end
1321
+ end
1322
+
1323
+ protected
1324
+ def load_records(records)
1325
+ @records = records.freeze
1326
+ @loaded = true
1327
+ end
1328
+
1329
+ private
1330
+ def already_in_scope?(registry)
1331
+ @delegate_to_model && registry.current_scope(model, true)
1332
+ end
1333
+
1334
+ def global_scope?(registry)
1335
+ registry.global_current_scope(model, true)
1336
+ end
1337
+
1338
+ def current_scope_restoring_block(&block)
1339
+ current_scope = model.current_scope(true)
1340
+ -> record do
1341
+ model.current_scope = current_scope
1342
+ yield record if block_given?
1343
+ end
1344
+ end
1345
+
1346
+ def _new(attributes, &block)
1347
+ model.new(attributes, &block)
1348
+ end
1349
+
1350
+ def _create(attributes, &block)
1351
+ model.create(attributes, &block)
1352
+ end
1353
+
1354
+ def _create!(attributes, &block)
1355
+ model.create!(attributes, &block)
1356
+ end
1357
+
1358
+ def _scoping(scope, registry, all_queries = false)
1359
+ previous = registry.current_scope(model, true)
1360
+ registry.set_current_scope(model, scope)
1361
+
1362
+ if all_queries
1363
+ previous_global = registry.global_current_scope(model, true)
1364
+ registry.set_global_current_scope(model, scope)
1365
+ end
1366
+ yield
1367
+ ensure
1368
+ registry.set_current_scope(model, previous)
1369
+ if all_queries
1370
+ registry.set_global_current_scope(model, previous_global)
1371
+ end
1372
+ end
1373
+
1374
+ def _substitute_values(values)
1375
+ values.map do |name, value|
1376
+ attr = table[name]
1377
+ if Arel.arel_node?(value)
1378
+ if value.is_a?(Arel::Nodes::SqlLiteral)
1379
+ value = Arel::Nodes::Grouping.new(value)
1380
+ end
1381
+ else
1382
+ type = model.type_for_attribute(attr.name)
1383
+ value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
1384
+ end
1385
+ [attr, value]
1386
+ end
1387
+ end
1388
+
1389
+ def _increment_attribute(attribute, value = 1)
1390
+ bind = predicate_builder.build_bind_attribute(attribute.name, value.abs)
1391
+ expr = table.coalesce(Arel::Nodes::UnqualifiedColumn.new(attribute), 0)
1392
+ expr = value < 0 ? expr - bind : expr + bind
1393
+ expr.expr
1394
+ end
1395
+
1396
+ def exec_queries(&block)
1397
+ skip_query_cache_if_necessary do
1398
+ rows = if scheduled?
1399
+ future = @future_result
1400
+ @future_result = nil
1401
+ future.result
1402
+ else
1403
+ exec_main_query
1404
+ end
1405
+
1406
+ records = instantiate_records(rows, &block)
1407
+ preload_associations(records) unless skip_preloading_value
1408
+
1409
+ records.each(&:readonly!) if readonly_value
1410
+ records.each { |record| record.strict_loading!(strict_loading_value) } unless strict_loading_value.nil?
1411
+
1412
+ records
1413
+ end
1414
+ end
1415
+
1416
+ def exec_main_query(async: false)
1417
+ if @none
1418
+ if async
1419
+ return FutureResult.wrap([])
1420
+ else
1421
+ return []
1422
+ end
1423
+ end
1424
+
1425
+ skip_query_cache_if_necessary do
1426
+ if where_clause.contradiction?
1427
+ [].freeze
1428
+ elsif eager_loading?
1429
+ model.with_connection do |c|
1430
+ apply_join_dependency do |relation, join_dependency|
1431
+ if relation.null_relation?
1432
+ [].freeze
1433
+ else
1434
+ relation = join_dependency.apply_column_aliases(relation)
1435
+ @_join_dependency = join_dependency
1436
+ c.select_all(relation.arel, "SQL", async: async)
1437
+ end
1438
+ end
1439
+ end
1440
+ else
1441
+ model.with_connection do |c|
1442
+ model._query_by_sql(c, arel, async: async)
1443
+ end
1444
+ end
1445
+ end
1446
+ end
1447
+
1448
+ def instantiate_records(rows, &block)
1449
+ return [].freeze if rows.empty?
1450
+ if eager_loading?
1451
+ records = @_join_dependency.instantiate(rows, strict_loading_value, &block).freeze
1452
+ @_join_dependency = nil
1453
+ records
1454
+ else
1455
+ model._load_from_sql(rows, &block).freeze
1456
+ end
1457
+ end
1458
+
1459
+ def skip_query_cache_if_necessary(&block)
1460
+ if skip_query_cache_value
1461
+ model.uncached(&block)
1462
+ else
1463
+ yield
1464
+ end
1465
+ end
1466
+
1467
+ def references_eager_loaded_tables?
1468
+ joined_tables = build_joins([]).flat_map do |join|
1469
+ if join.is_a?(Arel::Nodes::StringJoin)
1470
+ tables_in_string(join.left)
1471
+ else
1472
+ join.left.name
1473
+ end
1474
+ end
1475
+
1476
+ joined_tables << table.name
1477
+
1478
+ # always convert table names to downcase as in Oracle quoted table names are in uppercase
1479
+ joined_tables.map!(&:downcase)
1480
+
1481
+ !(references_values.map(&:to_s) - joined_tables).empty?
1482
+ end
1483
+
1484
+ def tables_in_string(string)
1485
+ return [] if string.blank?
1486
+ # always convert table names to downcase as in Oracle quoted table names are in uppercase
1487
+ # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
1488
+ string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
1489
+ end
1490
+
1491
+ def limited_count
1492
+ limit_value ? count : limit(2).count
1493
+ end
1494
+ end
1495
+ end