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
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class Promise < BasicObject
5
+ undef_method :==, :!, :!=
6
+
7
+ def initialize(future_result, block) # :nodoc:
8
+ @future_result = future_result
9
+ @block = block
10
+ end
11
+
12
+ # Returns whether the associated query is still being executed or not.
13
+ def pending?
14
+ @future_result.pending?
15
+ end
16
+
17
+ # Returns the query result.
18
+ # If the query wasn't completed yet, accessing +#value+ will block until the query completes.
19
+ # If the query failed, +#value+ will raise the corresponding error.
20
+ def value
21
+ return @value if defined? @value
22
+
23
+ result = @future_result.result
24
+ @value = if @block
25
+ @block.call(result)
26
+ else
27
+ result
28
+ end
29
+ end
30
+
31
+ # Returns a new +ActiveRecord::Promise+ that will apply the passed block
32
+ # when the value is accessed:
33
+ #
34
+ # Post.async_pick(:title).then { |title| title.upcase }.value
35
+ # # => "POST TITLE"
36
+ def then(&block)
37
+ Promise.new(@future_result, @block ? @block >> block : block)
38
+ end
39
+
40
+ [:class, :respond_to?, :is_a?].each do |method|
41
+ define_method(method, ::Object.instance_method(method))
42
+ end
43
+
44
+ def inspect # :nodoc:
45
+ "#<ActiveRecord::Promise status=#{status}>"
46
+ end
47
+
48
+ def pretty_print(q) # :nodoc:
49
+ q.text(inspect)
50
+ end
51
+
52
+ private
53
+ def status
54
+ if @future_result.pending?
55
+ :pending
56
+ elsif @future_result.canceled?
57
+ :canceled
58
+ else
59
+ :complete
60
+ end
61
+ end
62
+
63
+ class Complete < self # :nodoc:
64
+ attr_reader :value
65
+
66
+ def initialize(value)
67
+ @value = value
68
+ end
69
+
70
+ def then
71
+ Complete.new(yield @value)
72
+ end
73
+
74
+ def pending?
75
+ false
76
+ end
77
+
78
+ private
79
+ def status
80
+ :complete
81
+ end
82
+ end
83
+ end
84
+ end
@@ -8,7 +8,13 @@ module ActiveRecord
8
8
  # If it's not, it will execute the given block.
9
9
  def cache(&block)
10
10
  if connected? || !configurations.empty?
11
- connection.cache(&block)
11
+ pool = connection_pool
12
+ was_enabled = pool.query_cache_enabled
13
+ begin
14
+ pool.enable_query_cache(&block)
15
+ ensure
16
+ pool.clear_query_cache unless was_enabled
17
+ end
12
18
  else
13
19
  yield
14
20
  end
@@ -16,9 +22,12 @@ module ActiveRecord
16
22
 
17
23
  # Disable the query cache within the block if Active Record is configured.
18
24
  # If it's not, it will execute the given block.
19
- def uncached(&block)
25
+ #
26
+ # Set <tt>dirties: false</tt> to prevent query caches on all connections from being cleared by write operations.
27
+ # (By default, write operations dirty all connections' query caches in case they are replicas whose cache would now be outdated.)
28
+ def uncached(dirties: true, &block)
20
29
  if connected? || !configurations.empty?
21
- connection.uncached(&block)
30
+ connection_pool.disable_query_cache(dirties: dirties, &block)
22
31
  else
23
32
  yield
24
33
  end
@@ -26,22 +35,16 @@ module ActiveRecord
26
35
  end
27
36
 
28
37
  def self.run
29
- pools = []
30
-
31
- ActiveRecord::Base.connection_handlers.each do |key, handler|
32
- pools << handler.connection_pool_list.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! }
38
+ ActiveRecord::Base.connection_handler.each_connection_pool.reject(&:query_cache_enabled).each do |pool|
39
+ next if pool.db_config&.query_cache == false
40
+ pool.enable_query_cache!
33
41
  end
34
-
35
- pools.flatten
36
42
  end
37
43
 
38
44
  def self.complete(pools)
39
- pools.each { |pool| pool.disable_query_cache! }
40
-
41
- ActiveRecord::Base.connection_handlers.each do |_, handler|
42
- handler.connection_pool_list.each do |pool|
43
- pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
44
- end
45
+ pools.each do |pool|
46
+ pool.disable_query_cache!
47
+ pool.clear_query_cache
45
48
  end
46
49
  end
47
50
 
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors_per_thread"
4
+ require "active_record/query_logs_formatter"
5
+
6
+ module ActiveRecord
7
+ # = Active Record Query Logs
8
+ #
9
+ # Automatically append comments to SQL queries with runtime information tags. This can be used to trace troublesome
10
+ # SQL statements back to the application code that generated these statements.
11
+ #
12
+ # Query logs can be enabled via \Rails configuration in <tt>config/application.rb</tt> or an initializer:
13
+ #
14
+ # config.active_record.query_log_tags_enabled = true
15
+ #
16
+ # By default the name of the application, the name and action of the controller, or the name of the job are logged.
17
+ # The default format is {SQLCommenter}[https://open-telemetry.github.io/opentelemetry-sqlcommenter/].
18
+ # The tags shown in a query comment can be configured via \Rails configuration:
19
+ #
20
+ # config.active_record.query_log_tags = [ :application, :controller, :action, :job ]
21
+ #
22
+ # Active Record defines default tags available for use:
23
+ #
24
+ # * +application+
25
+ # * +pid+
26
+ # * +socket+
27
+ # * +db_host+
28
+ # * +database+
29
+ # * +source_location+
30
+ #
31
+ # WARNING: Calculating the +source_location+ of a query can be slow, so you should consider its impact if using it in a production environment.
32
+ #
33
+ # Also see {config.active_record.verbose_query_logs}[https://guides.rubyonrails.org/debugging_rails_applications.html#verbose-query-logs].
34
+ #
35
+ # Action Controller adds default tags when loaded:
36
+ #
37
+ # * +controller+
38
+ # * +action+
39
+ # * +namespaced_controller+
40
+ #
41
+ # Active Job adds default tags when loaded:
42
+ #
43
+ # * +job+
44
+ #
45
+ # New comment tags can be defined by adding them in a +Hash+ to the tags +Array+. Tags can have dynamic content by
46
+ # setting a +Proc+ or lambda value in the +Hash+, and can reference any value stored by \Rails in the +context+ object.
47
+ # ActiveSupport::CurrentAttributes can be used to store application values. Tags with +nil+ values are
48
+ # omitted from the query comment.
49
+ #
50
+ # Escaping is performed on the string returned, however untrusted user input should not be used.
51
+ #
52
+ # Example:
53
+ #
54
+ # config.active_record.query_log_tags = [
55
+ # :namespaced_controller,
56
+ # :action,
57
+ # :job,
58
+ # {
59
+ # request_id: ->(context) { context[:controller]&.request&.request_id },
60
+ # job_id: ->(context) { context[:job]&.job_id },
61
+ # tenant_id: -> { Current.tenant&.id },
62
+ # static: "value",
63
+ # },
64
+ # ]
65
+ #
66
+ # By default the name of the application, the name and action of the controller, or the name of the job are logged
67
+ # using the {SQLCommenter}[https://open-telemetry.github.io/opentelemetry-sqlcommenter/] format. This can be changed
68
+ # via {config.active_record.query_log_tags_format}[https://guides.rubyonrails.org/configuring.html#config-active-record-query-log-tags-format]
69
+ #
70
+ # Tag comments can be prepended to the query:
71
+ #
72
+ # ActiveRecord::QueryLogs.prepend_comment = true
73
+ #
74
+ # For applications where the content will not change during the lifetime of
75
+ # the request or job execution, the tags can be cached for reuse in every query:
76
+ #
77
+ # config.active_record.cache_query_log_tags = true
78
+ module QueryLogs
79
+ mattr_accessor :taggings, instance_accessor: false, default: {}
80
+ mattr_accessor :tags, instance_accessor: false, default: [ :application ]
81
+ mattr_accessor :prepend_comment, instance_accessor: false, default: false
82
+ mattr_accessor :cache_query_log_tags, instance_accessor: false, default: false
83
+ mattr_accessor :tags_formatter, instance_accessor: false
84
+ thread_mattr_accessor :cached_comment, instance_accessor: false
85
+
86
+ class << self
87
+ def call(sql, connection) # :nodoc:
88
+ comment = self.comment(connection)
89
+
90
+ if comment.blank?
91
+ sql
92
+ elsif prepend_comment
93
+ "#{comment} #{sql}"
94
+ else
95
+ "#{sql} #{comment}"
96
+ end
97
+ end
98
+
99
+ def clear_cache # :nodoc:
100
+ self.cached_comment = nil
101
+ end
102
+
103
+ # Updates the formatter to be what the passed in format is.
104
+ def update_formatter(format)
105
+ self.tags_formatter =
106
+ case format
107
+ when :legacy
108
+ LegacyFormatter.new
109
+ when :sqlcommenter
110
+ SQLCommenter.new
111
+ else
112
+ raise ArgumentError, "Formatter is unsupported: #{formatter}"
113
+ end
114
+ end
115
+
116
+ if Thread.respond_to?(:each_caller_location)
117
+ def query_source_location # :nodoc:
118
+ Thread.each_caller_location do |location|
119
+ frame = LogSubscriber.backtrace_cleaner.clean_frame(location)
120
+ return frame if frame
121
+ end
122
+ nil
123
+ end
124
+ else
125
+ def query_source_location # :nodoc:
126
+ LogSubscriber.backtrace_cleaner.clean(caller_locations(1).each).first
127
+ end
128
+ end
129
+
130
+ ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
131
+
132
+ private
133
+ # Returns an SQL comment +String+ containing the query log tags.
134
+ # Sets and returns a cached comment if <tt>cache_query_log_tags</tt> is +true+.
135
+ def comment(connection)
136
+ if cache_query_log_tags
137
+ self.cached_comment ||= uncached_comment(connection)
138
+ else
139
+ uncached_comment(connection)
140
+ end
141
+ end
142
+
143
+ def formatter
144
+ self.tags_formatter || self.update_formatter(:legacy)
145
+ end
146
+
147
+ def uncached_comment(connection)
148
+ content = tag_content(connection)
149
+
150
+ if content.present?
151
+ "/*#{escape_sql_comment(content)}*/"
152
+ end
153
+ end
154
+
155
+ def escape_sql_comment(content)
156
+ # Sanitize a string to appear within a SQL comment
157
+ # For compatibility, this also surrounding "/*+", "/*", and "*/"
158
+ # characters, possibly with single surrounding space.
159
+ # Then follows that by replacing any internal "*/" or "/ *" with
160
+ # "* /" or "/ *"
161
+ comment = content.to_s.dup
162
+ comment.gsub!(%r{\A\s*/\*\+?\s?|\s?\*/\s*\Z}, "")
163
+ comment.gsub!("*/", "* /")
164
+ comment.gsub!("/*", "/ *")
165
+ comment
166
+ end
167
+
168
+ def tag_content(connection)
169
+ context = ActiveSupport::ExecutionContext.to_h
170
+ context[:connection] ||= connection
171
+
172
+ pairs = tags.flat_map { |i| [*i] }.filter_map do |tag|
173
+ key, handler = tag
174
+ handler ||= taggings[key]
175
+
176
+ val = if handler.nil?
177
+ context[key]
178
+ elsif handler.respond_to?(:call)
179
+ if handler.arity == 0
180
+ handler.call
181
+ else
182
+ handler.call(context)
183
+ end
184
+ else
185
+ handler
186
+ end
187
+ [key, val] unless val.nil?
188
+ end
189
+ self.formatter.format(pairs)
190
+ end
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module QueryLogs
5
+ class LegacyFormatter # :nodoc:
6
+ def initialize
7
+ @key_value_separator = ":"
8
+ end
9
+
10
+ # Formats the key value pairs into a string.
11
+ def format(pairs)
12
+ pairs.map! do |key, value|
13
+ "#{key}#{key_value_separator}#{format_value(value)}"
14
+ end.join(",")
15
+ end
16
+
17
+ private
18
+ attr_reader :key_value_separator
19
+
20
+ def format_value(value)
21
+ value
22
+ end
23
+ end
24
+
25
+ class SQLCommenter < LegacyFormatter # :nodoc:
26
+ def initialize
27
+ @key_value_separator = "="
28
+ end
29
+
30
+ def format(pairs)
31
+ pairs.sort_by! { |pair| pair.first.to_s }
32
+ super
33
+ end
34
+
35
+ private
36
+ def format_value(value)
37
+ "'#{ERB::Util.url_encode(value)}'"
38
+ end
39
+ end
40
+ end
41
+ end
@@ -3,20 +3,23 @@
3
3
  module ActiveRecord
4
4
  module Querying
5
5
  QUERYING_METHODS = [
6
- :find, :find_by, :find_by!, :take, :take!, :first, :first!, :last, :last!,
6
+ :find, :find_by, :find_by!, :take, :take!, :sole, :find_sole_by, :first, :first!, :last, :last!,
7
7
  :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!,
8
8
  :forty_two, :forty_two!, :third_to_last, :third_to_last!, :second_to_last, :second_to_last!,
9
9
  :exists?, :any?, :many?, :none?, :one?,
10
10
  :first_or_create, :first_or_create!, :first_or_initialize,
11
11
  :find_or_create_by, :find_or_create_by!, :find_or_initialize_by,
12
12
  :create_or_find_by, :create_or_find_by!,
13
- :destroy_all, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
13
+ :destroy, :destroy_all, :delete, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
14
14
  :find_each, :find_in_batches, :in_batches,
15
- :select, :reselect, :order, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
16
- :where, :rewhere, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly, :extending, :or,
17
- :having, :create_with, :distinct, :references, :none, :unscope, :optimizer_hints, :merge, :except, :only,
18
- :count, :average, :minimum, :maximum, :sum, :calculate, :annotate,
19
- :pluck, :pick, :ids
15
+ :select, :reselect, :order, :regroup, :in_order_of, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
16
+ :where, :rewhere, :invert_where, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
17
+ :and, :or, :annotate, :optimizer_hints, :extending,
18
+ :having, :create_with, :distinct, :references, :none, :unscope, :merge, :except, :only,
19
+ :count, :average, :minimum, :maximum, :sum, :calculate,
20
+ :pluck, :pick, :ids, :async_ids, :strict_loading, :excluding, :without, :with, :with_recursive,
21
+ :async_count, :async_average, :async_minimum, :async_maximum, :async_sum, :async_pluck, :async_pick,
22
+ :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
20
23
  ].freeze # :nodoc:
21
24
  delegate(*QUERYING_METHODS, to: :all)
22
25
 
@@ -36,16 +39,44 @@ module ActiveRecord
36
39
  #
37
40
  # # A simple SQL query spanning multiple tables
38
41
  # Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
39
- # # => [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
42
+ # # => [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "author"=>"Quentin"}>, ...]
40
43
  #
41
- # You can use the same string replacement techniques as you can with <tt>ActiveRecord::QueryMethods#where</tt>:
44
+ # You can use the same string replacement techniques as you can with ActiveRecord::QueryMethods#where :
42
45
  #
43
46
  # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
44
47
  # Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
45
- def find_by_sql(sql, binds = [], preparable: nil, &block)
46
- result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable)
47
- column_types = result_set.column_types.dup
48
- attribute_types.each_key { |k| column_types.delete k }
48
+ #
49
+ # Note that building your own SQL query string from user input {may expose your application to
50
+ # injection attacks}[https://guides.rubyonrails.org/security.html#sql-injection].
51
+ def find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
52
+ result = with_connection do |c|
53
+ _query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry)
54
+ end
55
+ _load_from_sql(result, &block)
56
+ end
57
+
58
+ # Same as #find_by_sql but perform the query asynchronously and returns an ActiveRecord::Promise.
59
+ def async_find_by_sql(sql, binds = [], preparable: nil, &block)
60
+ result = with_connection do |c|
61
+ _query_by_sql(c, sql, binds, preparable: preparable, async: true)
62
+ end
63
+
64
+ result.then do |result|
65
+ _load_from_sql(result, &block)
66
+ end
67
+ end
68
+
69
+ def _query_by_sql(connection, sql, binds = [], preparable: nil, async: false, allow_retry: false) # :nodoc:
70
+ connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable, async: async, allow_retry: allow_retry)
71
+ end
72
+
73
+ def _load_from_sql(result_set, &block) # :nodoc:
74
+ column_types = result_set.column_types
75
+
76
+ unless column_types.empty?
77
+ column_types = column_types.reject { |k, _| attribute_types.key?(k) }
78
+ end
79
+
49
80
  message_bus = ActiveSupport::Notifications.instrumenter
50
81
 
51
82
  payload = {
@@ -76,7 +107,16 @@ module ActiveRecord
76
107
  #
77
108
  # * +sql+ - An SQL statement which should return a count query from the database, see the example above.
78
109
  def count_by_sql(sql)
79
- connection.select_value(sanitize_sql(sql), "#{name} Count").to_i
110
+ with_connection do |c|
111
+ c.select_value(sanitize_sql(sql), "#{name} Count").to_i
112
+ end
113
+ end
114
+
115
+ # Same as #count_by_sql but perform the query asynchronously and returns an ActiveRecord::Promise.
116
+ def async_count_by_sql(sql)
117
+ with_connection do |c|
118
+ c.select_value(sanitize_sql(sql), "#{name} Count", async: true).then(&:to_i)
119
+ end
80
120
  end
81
121
  end
82
122
  end