activerecord 6.1.7 → 7.1.0

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 (307) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1516 -1019
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +17 -18
  5. data/lib/active_record/aggregations.rb +17 -14
  6. data/lib/active_record/association_relation.rb +1 -11
  7. data/lib/active_record/associations/association.rb +50 -19
  8. data/lib/active_record/associations/association_scope.rb +17 -12
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +11 -5
  12. data/lib/active_record/associations/builder/belongs_to.rb +40 -14
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  15. data/lib/active_record/associations/builder/has_many.rb +3 -2
  16. data/lib/active_record/associations/builder/has_one.rb +2 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  18. data/lib/active_record/associations/collection_association.rb +35 -31
  19. data/lib/active_record/associations/collection_proxy.rb +30 -15
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  21. data/lib/active_record/associations/foreign_association.rb +10 -3
  22. data/lib/active_record/associations/has_many_association.rb +28 -18
  23. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  24. data/lib/active_record/associations/has_one_association.rb +20 -10
  25. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency.rb +26 -16
  27. data/lib/active_record/associations/preloader/association.rb +207 -52
  28. data/lib/active_record/associations/preloader/batch.rb +48 -0
  29. data/lib/active_record/associations/preloader/branch.rb +147 -0
  30. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  31. data/lib/active_record/associations/preloader.rb +50 -121
  32. data/lib/active_record/associations/singular_association.rb +9 -3
  33. data/lib/active_record/associations/through_association.rb +25 -14
  34. data/lib/active_record/associations.rb +423 -289
  35. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  36. data/lib/active_record/attribute_assignment.rb +1 -3
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  38. data/lib/active_record/attribute_methods/dirty.rb +61 -14
  39. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  40. data/lib/active_record/attribute_methods/query.rb +31 -19
  41. data/lib/active_record/attribute_methods/read.rb +25 -10
  42. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  44. data/lib/active_record/attribute_methods/write.rb +10 -13
  45. data/lib/active_record/attribute_methods.rb +121 -40
  46. data/lib/active_record/attributes.rb +27 -38
  47. data/lib/active_record/autosave_association.rb +61 -30
  48. data/lib/active_record/base.rb +25 -2
  49. data/lib/active_record/callbacks.rb +18 -34
  50. data/lib/active_record/coders/column_serializer.rb +61 -0
  51. data/lib/active_record/coders/json.rb +1 -1
  52. data/lib/active_record/coders/yaml_column.rb +70 -46
  53. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  54. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +96 -590
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -51
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +77 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +360 -136
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +622 -149
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +285 -156
  69. data/lib/active_record/connection_adapters/column.rb +13 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +104 -53
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +18 -1
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -52
  83. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  87. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  88. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  91. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +381 -69
  98. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  99. data/lib/active_record/connection_adapters/postgresql_adapter.rb +492 -230
  100. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  101. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  102. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +65 -53
  103. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  104. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  105. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
  107. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  108. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  109. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  110. data/lib/active_record/connection_adapters.rb +9 -6
  111. data/lib/active_record/connection_handling.rb +107 -136
  112. data/lib/active_record/core.rb +194 -224
  113. data/lib/active_record/counter_cache.rb +46 -25
  114. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  115. data/lib/active_record/database_configurations/database_config.rb +21 -12
  116. data/lib/active_record/database_configurations/hash_config.rb +84 -16
  117. data/lib/active_record/database_configurations/url_config.rb +18 -12
  118. data/lib/active_record/database_configurations.rb +95 -59
  119. data/lib/active_record/delegated_type.rb +61 -15
  120. data/lib/active_record/deprecator.rb +7 -0
  121. data/lib/active_record/destroy_association_async_job.rb +3 -1
  122. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  123. data/lib/active_record/dynamic_matchers.rb +1 -1
  124. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  125. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  126. data/lib/active_record/encryption/cipher.rb +53 -0
  127. data/lib/active_record/encryption/config.rb +68 -0
  128. data/lib/active_record/encryption/configurable.rb +60 -0
  129. data/lib/active_record/encryption/context.rb +42 -0
  130. data/lib/active_record/encryption/contexts.rb +76 -0
  131. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  132. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  133. data/lib/active_record/encryption/encryptable_record.rb +224 -0
  134. data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -0
  135. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  136. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  137. data/lib/active_record/encryption/encryptor.rb +155 -0
  138. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  139. data/lib/active_record/encryption/errors.rb +15 -0
  140. data/lib/active_record/encryption/extended_deterministic_queries.rb +172 -0
  141. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  142. data/lib/active_record/encryption/key.rb +28 -0
  143. data/lib/active_record/encryption/key_generator.rb +53 -0
  144. data/lib/active_record/encryption/key_provider.rb +46 -0
  145. data/lib/active_record/encryption/message.rb +33 -0
  146. data/lib/active_record/encryption/message_serializer.rb +92 -0
  147. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  148. data/lib/active_record/encryption/properties.rb +76 -0
  149. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  150. data/lib/active_record/encryption/scheme.rb +96 -0
  151. data/lib/active_record/encryption.rb +56 -0
  152. data/lib/active_record/enum.rb +156 -62
  153. data/lib/active_record/errors.rb +171 -15
  154. data/lib/active_record/explain.rb +23 -3
  155. data/lib/active_record/explain_registry.rb +11 -6
  156. data/lib/active_record/explain_subscriber.rb +1 -1
  157. data/lib/active_record/fixture_set/file.rb +15 -1
  158. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  159. data/lib/active_record/fixture_set/render_context.rb +2 -0
  160. data/lib/active_record/fixture_set/table_row.rb +70 -14
  161. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  162. data/lib/active_record/fixtures.rb +131 -86
  163. data/lib/active_record/future_result.rb +164 -0
  164. data/lib/active_record/gem_version.rb +3 -3
  165. data/lib/active_record/inheritance.rb +81 -29
  166. data/lib/active_record/insert_all.rb +133 -20
  167. data/lib/active_record/integration.rb +11 -10
  168. data/lib/active_record/internal_metadata.rb +117 -33
  169. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  170. data/lib/active_record/locking/optimistic.rb +36 -21
  171. data/lib/active_record/locking/pessimistic.rb +15 -6
  172. data/lib/active_record/log_subscriber.rb +52 -19
  173. data/lib/active_record/marshalling.rb +56 -0
  174. data/lib/active_record/message_pack.rb +124 -0
  175. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  176. data/lib/active_record/middleware/database_selector.rb +23 -13
  177. data/lib/active_record/middleware/shard_selector.rb +62 -0
  178. data/lib/active_record/migration/command_recorder.rb +108 -13
  179. data/lib/active_record/migration/compatibility.rb +221 -48
  180. data/lib/active_record/migration/default_strategy.rb +23 -0
  181. data/lib/active_record/migration/execution_strategy.rb +19 -0
  182. data/lib/active_record/migration/join_table.rb +1 -1
  183. data/lib/active_record/migration.rb +355 -171
  184. data/lib/active_record/model_schema.rb +116 -97
  185. data/lib/active_record/nested_attributes.rb +36 -15
  186. data/lib/active_record/no_touching.rb +3 -3
  187. data/lib/active_record/normalization.rb +159 -0
  188. data/lib/active_record/persistence.rb +405 -85
  189. data/lib/active_record/promise.rb +84 -0
  190. data/lib/active_record/query_cache.rb +3 -21
  191. data/lib/active_record/query_logs.rb +174 -0
  192. data/lib/active_record/query_logs_formatter.rb +41 -0
  193. data/lib/active_record/querying.rb +29 -6
  194. data/lib/active_record/railtie.rb +219 -43
  195. data/lib/active_record/railties/controller_runtime.rb +13 -9
  196. data/lib/active_record/railties/databases.rake +185 -249
  197. data/lib/active_record/railties/job_runtime.rb +23 -0
  198. data/lib/active_record/readonly_attributes.rb +41 -3
  199. data/lib/active_record/reflection.rb +229 -80
  200. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  201. data/lib/active_record/relation/batches.rb +192 -63
  202. data/lib/active_record/relation/calculations.rb +211 -90
  203. data/lib/active_record/relation/delegation.rb +27 -13
  204. data/lib/active_record/relation/finder_methods.rb +108 -51
  205. data/lib/active_record/relation/merger.rb +22 -13
  206. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  207. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  208. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  209. data/lib/active_record/relation/predicate_builder.rb +27 -20
  210. data/lib/active_record/relation/query_attribute.rb +30 -12
  211. data/lib/active_record/relation/query_methods.rb +654 -127
  212. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  213. data/lib/active_record/relation/spawn_methods.rb +20 -3
  214. data/lib/active_record/relation/where_clause.rb +10 -19
  215. data/lib/active_record/relation.rb +262 -120
  216. data/lib/active_record/result.rb +37 -11
  217. data/lib/active_record/runtime_registry.rb +18 -13
  218. data/lib/active_record/sanitization.rb +65 -20
  219. data/lib/active_record/schema.rb +36 -22
  220. data/lib/active_record/schema_dumper.rb +73 -24
  221. data/lib/active_record/schema_migration.rb +68 -33
  222. data/lib/active_record/scoping/default.rb +72 -15
  223. data/lib/active_record/scoping/named.rb +5 -13
  224. data/lib/active_record/scoping.rb +65 -34
  225. data/lib/active_record/secure_password.rb +60 -0
  226. data/lib/active_record/secure_token.rb +21 -3
  227. data/lib/active_record/serialization.rb +6 -1
  228. data/lib/active_record/signed_id.rb +10 -8
  229. data/lib/active_record/store.rb +10 -10
  230. data/lib/active_record/suppressor.rb +13 -15
  231. data/lib/active_record/table_metadata.rb +16 -3
  232. data/lib/active_record/tasks/database_tasks.rb +225 -136
  233. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  234. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  235. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  236. data/lib/active_record/test_databases.rb +1 -1
  237. data/lib/active_record/test_fixtures.rb +116 -96
  238. data/lib/active_record/timestamp.rb +28 -17
  239. data/lib/active_record/token_for.rb +113 -0
  240. data/lib/active_record/touch_later.rb +11 -6
  241. data/lib/active_record/transactions.rb +48 -27
  242. data/lib/active_record/translation.rb +3 -3
  243. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  244. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  245. data/lib/active_record/type/internal/timezone.rb +7 -2
  246. data/lib/active_record/type/serialized.rb +9 -5
  247. data/lib/active_record/type/time.rb +4 -0
  248. data/lib/active_record/type/type_map.rb +17 -20
  249. data/lib/active_record/type.rb +1 -2
  250. data/lib/active_record/validations/absence.rb +1 -1
  251. data/lib/active_record/validations/associated.rb +4 -4
  252. data/lib/active_record/validations/numericality.rb +5 -4
  253. data/lib/active_record/validations/presence.rb +5 -28
  254. data/lib/active_record/validations/uniqueness.rb +51 -6
  255. data/lib/active_record/validations.rb +8 -4
  256. data/lib/active_record/version.rb +1 -1
  257. data/lib/active_record.rb +335 -32
  258. data/lib/arel/attributes/attribute.rb +0 -8
  259. data/lib/arel/crud.rb +28 -22
  260. data/lib/arel/delete_manager.rb +18 -4
  261. data/lib/arel/errors.rb +10 -0
  262. data/lib/arel/factory_methods.rb +4 -0
  263. data/lib/arel/filter_predications.rb +9 -0
  264. data/lib/arel/insert_manager.rb +2 -3
  265. data/lib/arel/nodes/and.rb +4 -0
  266. data/lib/arel/nodes/binary.rb +6 -1
  267. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  268. data/lib/arel/nodes/casted.rb +1 -1
  269. data/lib/arel/nodes/cte.rb +36 -0
  270. data/lib/arel/nodes/delete_statement.rb +12 -13
  271. data/lib/arel/nodes/filter.rb +10 -0
  272. data/lib/arel/nodes/fragments.rb +35 -0
  273. data/lib/arel/nodes/function.rb +1 -0
  274. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  275. data/lib/arel/nodes/insert_statement.rb +2 -2
  276. data/lib/arel/nodes/leading_join.rb +8 -0
  277. data/lib/arel/nodes/node.rb +111 -2
  278. data/lib/arel/nodes/select_core.rb +2 -2
  279. data/lib/arel/nodes/select_statement.rb +2 -2
  280. data/lib/arel/nodes/sql_literal.rb +6 -0
  281. data/lib/arel/nodes/table_alias.rb +4 -0
  282. data/lib/arel/nodes/update_statement.rb +8 -3
  283. data/lib/arel/nodes.rb +5 -0
  284. data/lib/arel/predications.rb +13 -3
  285. data/lib/arel/select_manager.rb +10 -4
  286. data/lib/arel/table.rb +9 -6
  287. data/lib/arel/tree_manager.rb +0 -12
  288. data/lib/arel/update_manager.rb +18 -4
  289. data/lib/arel/visitors/dot.rb +80 -90
  290. data/lib/arel/visitors/mysql.rb +16 -3
  291. data/lib/arel/visitors/postgresql.rb +0 -10
  292. data/lib/arel/visitors/to_sql.rb +139 -19
  293. data/lib/arel/visitors/visitor.rb +2 -2
  294. data/lib/arel.rb +18 -3
  295. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  296. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  297. data/lib/rails/generators/active_record/migration.rb +3 -1
  298. data/lib/rails/generators/active_record/model/USAGE +113 -0
  299. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  300. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  301. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  302. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  303. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  304. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  305. metadata +92 -13
  306. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  307. data/lib/active_record/null_relation.rb +0 -67
@@ -7,33 +7,21 @@ module ActiveRecord
7
7
  64
8
8
  end
9
9
 
10
- # Returns the maximum length of a table alias.
11
- def table_alias_length
10
+ # Returns the maximum length of a table name.
11
+ def table_name_length
12
12
  max_identifier_length
13
13
  end
14
14
 
15
- # Returns the maximum allowed length for an index name. This
16
- # limit is enforced by \Rails and is less than or equal to
17
- # #index_name_length. The gap between
18
- # #index_name_length is to allow internal \Rails
19
- # operations to use prefixes in temporary operations.
20
- def allowed_index_name_length
21
- index_name_length
15
+ # Returns the maximum length of a table alias.
16
+ def table_alias_length
17
+ max_identifier_length
22
18
  end
23
- deprecate :allowed_index_name_length
24
19
 
25
20
  # Returns the maximum length of an index name.
26
21
  def index_name_length
27
22
  max_identifier_length
28
23
  end
29
24
 
30
- # Returns the maximum number of elements in an IN (x,y,z) clause.
31
- # +nil+ means no limit.
32
- def in_clause_length
33
- nil
34
- end
35
- deprecate :in_clause_length
36
-
37
25
  private
38
26
  def bind_params_length
39
27
  65535
@@ -15,7 +15,12 @@ module ActiveRecord
15
15
  end
16
16
 
17
17
  def to_sql_and_binds(arel_or_sql_string, binds = [], preparable = nil) # :nodoc:
18
+ # Arel::TreeManager -> Arel::Node
18
19
  if arel_or_sql_string.respond_to?(:ast)
20
+ arel_or_sql_string = arel_or_sql_string.ast
21
+ end
22
+
23
+ if Arel.arel_node?(arel_or_sql_string) && !(String === arel_or_sql_string)
19
24
  unless binds.empty?
20
25
  raise "Passing bind parameters with an arel AST is forbidden. " \
21
26
  "The values must be stored on the AST directly"
@@ -25,7 +30,7 @@ module ActiveRecord
25
30
 
26
31
  if prepared_statements
27
32
  collector.preparable = true
28
- sql, binds = visitor.compile(arel_or_sql_string.ast, collector)
33
+ sql, binds = visitor.compile(arel_or_sql_string, collector)
29
34
 
30
35
  if binds.length > bind_params_length
31
36
  unprepared_statement do
@@ -34,7 +39,7 @@ module ActiveRecord
34
39
  end
35
40
  preparable = collector.preparable
36
41
  else
37
- sql = visitor.compile(arel_or_sql_string.ast, collector)
42
+ sql = visitor.compile(arel_or_sql_string, collector)
38
43
  end
39
44
  [sql.freeze, binds, preparable]
40
45
  else
@@ -59,28 +64,24 @@ module ActiveRecord
59
64
  end
60
65
 
61
66
  # Returns an ActiveRecord::Result instance.
62
- def select_all(arel, name = nil, binds = [], preparable: nil)
67
+ def select_all(arel, name = nil, binds = [], preparable: nil, async: false)
63
68
  arel = arel_from_relation(arel)
64
69
  sql, binds, preparable = to_sql_and_binds(arel, binds, preparable)
65
70
 
66
- if prepared_statements && preparable
67
- select_prepared(sql, name, binds)
68
- else
69
- select(sql, name, binds)
70
- end
71
+ select(sql, name, binds, prepare: prepared_statements && preparable, async: async && FutureResult::SelectAll)
71
72
  rescue ::RangeError
72
- ActiveRecord::Result.new([], [])
73
+ ActiveRecord::Result.empty(async: async)
73
74
  end
74
75
 
75
76
  # Returns a record hash with the column names as keys and column values
76
77
  # as values.
77
- def select_one(arel, name = nil, binds = [])
78
- select_all(arel, name, binds).first
78
+ def select_one(arel, name = nil, binds = [], async: false)
79
+ select_all(arel, name, binds, async: async).then(&:first)
79
80
  end
80
81
 
81
82
  # Returns a single value from a record
82
- def select_value(arel, name = nil, binds = [])
83
- single_value_from_rows(select_rows(arel, name, binds))
83
+ def select_value(arel, name = nil, binds = [], async: false)
84
+ select_rows(arel, name, binds, async: async).then { |rows| single_value_from_rows(rows) }
84
85
  end
85
86
 
86
87
  # Returns an array of the values of the first column in a select:
@@ -91,8 +92,8 @@ module ActiveRecord
91
92
 
92
93
  # Returns an array of arrays containing the field values.
93
94
  # Order is the same as that returned by +columns+.
94
- def select_rows(arel, name = nil, binds = [])
95
- select_all(arel, name, binds).rows
95
+ def select_rows(arel, name = nil, binds = [], async: false)
96
+ select_all(arel, name, binds, async: async).then(&:rows)
96
97
  end
97
98
 
98
99
  def query_value(sql, name = nil) # :nodoc:
@@ -104,7 +105,7 @@ module ActiveRecord
104
105
  end
105
106
 
106
107
  def query(sql, name = nil) # :nodoc:
107
- exec_query(sql, name).rows
108
+ internal_exec_query(sql, name).rows
108
109
  end
109
110
 
110
111
  # Determines whether the SQL statement is a write query.
@@ -114,47 +115,63 @@ module ActiveRecord
114
115
 
115
116
  # Executes the SQL statement in the context of this connection and returns
116
117
  # the raw result from the connection adapter.
118
+ #
119
+ # Setting +allow_retry+ to true causes the db to reconnect and retry
120
+ # executing the SQL statement in case of a connection-related exception.
121
+ # This option should only be enabled for known idempotent queries.
122
+ #
123
+ # Note: the query is assumed to have side effects and the query cache
124
+ # will be cleared. If the query is read-only, consider using #select_all
125
+ # instead.
126
+ #
117
127
  # Note: depending on your database connector, the result returned by this
118
- # method may be manually memory managed. Consider using the exec_query
128
+ # method may be manually memory managed. Consider using #exec_query
119
129
  # wrapper instead.
120
- def execute(sql, name = nil)
121
- raise NotImplementedError
130
+ def execute(sql, name = nil, allow_retry: false)
131
+ internal_execute(sql, name, allow_retry: allow_retry)
122
132
  end
123
133
 
124
134
  # Executes +sql+ statement in the context of this connection using
125
135
  # +binds+ as the bind substitutes. +name+ is logged along with
126
136
  # the executed +sql+ statement.
137
+ #
138
+ # Note: the query is assumed to have side effects and the query cache
139
+ # will be cleared. If the query is read-only, consider using #select_all
140
+ # instead.
127
141
  def exec_query(sql, name = "SQL", binds = [], prepare: false)
128
- raise NotImplementedError
142
+ internal_exec_query(sql, name, binds, prepare: prepare)
129
143
  end
130
144
 
131
145
  # Executes insert +sql+ statement in the context of this connection using
132
146
  # +binds+ as the bind substitutes. +name+ is logged along with
133
147
  # the executed +sql+ statement.
134
- def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
135
- sql, binds = sql_for_insert(sql, pk, binds)
136
- exec_query(sql, name, binds)
148
+ # Some adapters support the `returning` keyword argument which allows to control the result of the query:
149
+ # `nil` is the default value and maintains default behavior. If an array of column names is passed -
150
+ # the result will contain values of the specified columns from the inserted row.
151
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)
152
+ sql, binds = sql_for_insert(sql, pk, binds, returning)
153
+ internal_exec_query(sql, name, binds)
137
154
  end
138
155
 
139
156
  # Executes delete +sql+ statement in the context of this connection using
140
157
  # +binds+ as the bind substitutes. +name+ is logged along with
141
158
  # the executed +sql+ statement.
142
159
  def exec_delete(sql, name = nil, binds = [])
143
- exec_query(sql, name, binds)
160
+ internal_exec_query(sql, name, binds)
144
161
  end
145
162
 
146
163
  # Executes update +sql+ statement in the context of this connection using
147
164
  # +binds+ as the bind substitutes. +name+ is logged along with
148
165
  # the executed +sql+ statement.
149
166
  def exec_update(sql, name = nil, binds = [])
150
- exec_query(sql, name, binds)
167
+ internal_exec_query(sql, name, binds)
151
168
  end
152
169
 
153
170
  def exec_insert_all(sql, name) # :nodoc:
154
- exec_query(sql, name)
171
+ internal_exec_query(sql, name)
155
172
  end
156
173
 
157
- def explain(arel, binds = []) # :nodoc:
174
+ def explain(arel, binds = [], options = []) # :nodoc:
158
175
  raise NotImplementedError
159
176
  end
160
177
 
@@ -166,10 +183,14 @@ module ActiveRecord
166
183
  #
167
184
  # If the next id was calculated in advance (as in Oracle), it should be
168
185
  # passed in as +id_value+.
169
- def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
186
+ # Some adapters support the `returning` keyword argument which allows defining the return value of the method:
187
+ # `nil` is the default value and maintains default behavior. If an array of column names is passed -
188
+ # an array of is returned from the method representing values of the specified columns from the inserted row.
189
+ def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
170
190
  sql, binds = to_sql_and_binds(arel, binds)
171
- value = exec_insert(sql, name, binds, pk, sequence_name)
172
- id_value || last_inserted_id(value)
191
+ value = exec_insert(sql, name, binds, pk, sequence_name, returning: returning)
192
+ return id_value if id_value
193
+ returning.nil? ? last_inserted_id(value) : returning_column_values(value)
173
194
  end
174
195
  alias create insert
175
196
 
@@ -191,7 +212,7 @@ module ActiveRecord
191
212
  end
192
213
 
193
214
  def truncate_tables(*table_names) # :nodoc:
194
- table_names -= [schema_migration.table_name, InternalMetadata.table_name]
215
+ table_names -= [schema_migration.table_name, internal_metadata.table_name]
195
216
 
196
217
  return if table_names.empty?
197
218
 
@@ -308,26 +329,28 @@ module ActiveRecord
308
329
  # * You are joining an existing open transaction
309
330
  # * You are creating a nested (savepoint) transaction
310
331
  #
311
- # The mysql2 and postgresql adapters support setting the transaction
332
+ # The mysql2, trilogy, and postgresql adapters support setting the transaction
312
333
  # isolation level.
313
- def transaction(requires_new: nil, isolation: nil, joinable: true)
334
+ # :args: (requires_new: nil, isolation: nil, &block)
335
+ def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
314
336
  if !requires_new && current_transaction.joinable?
315
337
  if isolation
316
338
  raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
317
339
  end
318
340
  yield
319
341
  else
320
- transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable) { yield }
342
+ transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable, &block)
321
343
  end
322
344
  rescue ActiveRecord::Rollback
323
345
  # rollbacks are silently swallowed
324
346
  end
325
347
 
326
- attr_reader :transaction_manager #:nodoc:
348
+ attr_reader :transaction_manager # :nodoc:
327
349
 
328
350
  delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction,
329
351
  :commit_transaction, :rollback_transaction, :materialize_transactions,
330
- :disable_lazy_transactions!, :enable_lazy_transactions!, to: :transaction_manager
352
+ :disable_lazy_transactions!, :enable_lazy_transactions!, :dirty_current_transaction,
353
+ to: :transaction_manager
331
354
 
332
355
  def mark_transaction_written_if_write(sql) # :nodoc:
333
356
  transaction = current_transaction
@@ -340,8 +363,24 @@ module ActiveRecord
340
363
  current_transaction.open?
341
364
  end
342
365
 
343
- def reset_transaction #:nodoc:
366
+ def reset_transaction(restore: false) # :nodoc:
367
+ # Store the existing transaction state to the side
368
+ old_state = @transaction_manager if restore && @transaction_manager&.restorable?
369
+
344
370
  @transaction_manager = ConnectionAdapters::TransactionManager.new(self)
371
+
372
+ if block_given?
373
+ # Reconfigure the connection without any transaction state in the way
374
+ result = yield
375
+
376
+ # Now the connection's fully established, we can swap back
377
+ if old_state
378
+ @transaction_manager = old_state
379
+ @transaction_manager.restore_transactions
380
+ end
381
+
382
+ result
383
+ end
345
384
  end
346
385
 
347
386
  # Register a record with the current transaction so that its after_commit and after_rollback callbacks
@@ -376,9 +415,17 @@ module ActiveRecord
376
415
  # done if the transaction block raises an exception or returns false.
377
416
  def rollback_db_transaction
378
417
  exec_rollback_db_transaction
418
+ rescue ActiveRecord::ConnectionNotEstablished, ActiveRecord::ConnectionFailed
419
+ # Connection's gone; that counts as a rollback
420
+ end
421
+
422
+ def exec_rollback_db_transaction() end # :nodoc:
423
+
424
+ def restart_db_transaction
425
+ exec_restart_db_transaction
379
426
  end
380
427
 
381
- def exec_rollback_db_transaction() end #:nodoc:
428
+ def exec_restart_db_transaction() end # :nodoc:
382
429
 
383
430
  def rollback_to_savepoint(name = nil)
384
431
  exec_rollback_to_savepoint(name)
@@ -397,7 +444,7 @@ module ActiveRecord
397
444
  # something beyond a simple insert (e.g. Oracle).
398
445
  # Most of adapters should implement +insert_fixtures_set+ that leverages bulk SQL insert.
399
446
  # We keep this method to provide fallback
400
- # for databases like sqlite that do not support bulk inserts.
447
+ # for databases like SQLite that do not support bulk inserts.
401
448
  def insert_fixture(fixture, table_name)
402
449
  execute(build_fixture_sql(Array.wrap(fixture), table_name), "Fixture Insert")
403
450
  end
@@ -445,13 +492,43 @@ module ActiveRecord
445
492
  end
446
493
  end
447
494
 
495
+ # This is a safe default, even if not high precision on all databases
496
+ HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
497
+ private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
498
+
499
+ # Returns an Arel SQL literal for the CURRENT_TIMESTAMP for usage with
500
+ # arbitrary precision date/time columns.
501
+ #
502
+ # Adapters supporting datetime with precision should override this to
503
+ # provide as much precision as is available.
504
+ def high_precision_current_timestamp
505
+ HIGH_PRECISION_CURRENT_TIMESTAMP
506
+ end
507
+
508
+ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
509
+ raise NotImplementedError
510
+ end
511
+
448
512
  private
513
+ def internal_execute(sql, name = "SCHEMA", allow_retry: false, materialize_transactions: true)
514
+ sql = transform_query(sql)
515
+ check_if_write_query(sql)
516
+
517
+ mark_transaction_written_if_write(sql)
518
+
519
+ raw_execute(sql, name, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
520
+ end
521
+
449
522
  def execute_batch(statements, name = nil)
450
523
  statements.each do |statement|
451
- execute(statement, name)
524
+ internal_execute(statement, name)
452
525
  end
453
526
  end
454
527
 
528
+ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
529
+ raise NotImplementedError
530
+ end
531
+
455
532
  DEFAULT_INSERT_VALUE = Arel.sql("DEFAULT").freeze
456
533
  private_constant :DEFAULT_INSERT_VALUE
457
534
 
@@ -460,7 +537,7 @@ module ActiveRecord
460
537
  end
461
538
 
462
539
  def build_fixture_sql(fixtures, table_name)
463
- columns = schema_cache.columns_hash(table_name)
540
+ columns = schema_cache.columns_hash(table_name).reject { |_, column| supports_virtual_columns? && column.virtual? }
464
541
 
465
542
  values_list = fixtures.map do |fixture|
466
543
  fixture = fixture.stringify_keys
@@ -481,8 +558,7 @@ module ActiveRecord
481
558
  end
482
559
 
483
560
  table = Arel::Table.new(table_name)
484
- manager = Arel::InsertManager.new
485
- manager.into(table)
561
+ manager = Arel::InsertManager.new(table)
486
562
 
487
563
  if values_list.size == 1
488
564
  values = values_list.shift
@@ -503,10 +579,10 @@ module ActiveRecord
503
579
  end
504
580
 
505
581
  def build_fixture_statements(fixture_set)
506
- fixture_set.map do |table_name, fixtures|
582
+ fixture_set.filter_map do |table_name, fixtures|
507
583
  next if fixtures.empty?
508
584
  build_fixture_sql(fixtures, table_name)
509
- end.compact
585
+ end
510
586
  end
511
587
 
512
588
  def build_truncate_statement(table_name)
@@ -528,15 +604,49 @@ module ActiveRecord
528
604
  end
529
605
 
530
606
  # Returns an ActiveRecord::Result instance.
531
- def select(sql, name = nil, binds = [])
532
- exec_query(sql, name, binds, prepare: false)
533
- end
607
+ def select(sql, name = nil, binds = [], prepare: false, async: false)
608
+ if async && async_enabled?
609
+ if current_transaction.joinable?
610
+ raise AsynchronousQueryInsideTransactionError, "Asynchronous queries are not allowed inside transactions"
611
+ end
612
+
613
+ future_result = async.new(
614
+ pool,
615
+ sql,
616
+ name,
617
+ binds,
618
+ prepare: prepare,
619
+ )
620
+ if supports_concurrent_connections? && current_transaction.closed?
621
+ future_result.schedule!(ActiveRecord::Base.asynchronous_queries_session)
622
+ else
623
+ future_result.execute!(self)
624
+ end
625
+ return future_result
626
+ end
534
627
 
535
- def select_prepared(sql, name = nil, binds = [])
536
- exec_query(sql, name, binds, prepare: true)
628
+ result = internal_exec_query(sql, name, binds, prepare: prepare)
629
+ if async
630
+ FutureResult::Complete.new(result)
631
+ else
632
+ result
633
+ end
537
634
  end
538
635
 
539
- def sql_for_insert(sql, pk, binds)
636
+ def sql_for_insert(sql, pk, binds, returning) # :nodoc:
637
+ if supports_insert_returning?
638
+ if pk.nil?
639
+ # Extract the table from the insert sql. Yuck.
640
+ table_ref = extract_table_ref_from_insert_sql(sql)
641
+ pk = primary_key(table_ref) if table_ref
642
+ end
643
+
644
+ returning_columns = returning || Array(pk)
645
+
646
+ returning_columns_statement = returning_columns.map { |c| quote_column_name(c) }.join(", ")
647
+ sql = "#{sql} RETURNING #{returning_columns_statement}" if returning_columns.any?
648
+ end
649
+
540
650
  [sql, binds]
541
651
  end
542
652
 
@@ -544,6 +654,10 @@ module ActiveRecord
544
654
  single_value_from_rows(result.rows)
545
655
  end
546
656
 
657
+ def returning_column_values(result)
658
+ [last_inserted_id(result)]
659
+ end
660
+
547
661
  def single_value_from_rows(rows)
548
662
  row = rows.first
549
663
  row && row.first
@@ -556,6 +670,12 @@ module ActiveRecord
556
670
  relation
557
671
  end
558
672
  end
673
+
674
+ def extract_table_ref_from_insert_sql(sql)
675
+ if sql =~ /into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im
676
+ $1.strip
677
+ end
678
+ end
559
679
  end
560
680
  end
561
681
  end
@@ -5,10 +5,13 @@ require "concurrent/map"
5
5
  module ActiveRecord
6
6
  module ConnectionAdapters # :nodoc:
7
7
  module QueryCache
8
+ DEFAULT_SIZE = 100 # :nodoc:
9
+
8
10
  class << self
9
- def included(base) #:nodoc:
10
- dirties_query_cache base, :create, :insert, :update, :delete, :truncate, :truncate_tables,
11
- :rollback_to_savepoint, :rollback_db_transaction, :exec_insert_all
11
+ def included(base) # :nodoc:
12
+ dirties_query_cache base, :exec_query, :execute, :create, :insert, :update, :delete, :truncate,
13
+ :truncate_tables, :rollback_to_savepoint, :rollback_db_transaction, :restart_db_transaction,
14
+ :exec_insert_all
12
15
 
13
16
  base.set_callback :checkout, :after, :configure_query_cache!
14
17
  base.set_callback :checkin, :after, :disable_query_cache!
@@ -17,7 +20,7 @@ module ActiveRecord
17
20
  def dirties_query_cache(base, *method_names)
18
21
  method_names.each do |method_name|
19
22
  base.class_eval <<-end_code, __FILE__, __LINE__ + 1
20
- def #{method_name}(*)
23
+ def #{method_name}(...)
21
24
  ActiveRecord::Base.clear_query_caches_for_current_thread
22
25
  super
23
26
  end
@@ -51,8 +54,9 @@ module ActiveRecord
51
54
 
52
55
  def initialize(*)
53
56
  super
54
- @query_cache = Hash.new { |h, sql| h[sql] = {} }
57
+ @query_cache = {}
55
58
  @query_cache_enabled = false
59
+ @query_cache_max_size = nil
56
60
  end
57
61
 
58
62
  # Enable the query cache within the block.
@@ -93,32 +97,72 @@ module ActiveRecord
93
97
  end
94
98
  end
95
99
 
96
- def select_all(arel, name = nil, binds = [], preparable: nil)
97
- if @query_cache_enabled && !locked?(arel)
98
- arel = arel_from_relation(arel)
100
+ def select_all(arel, name = nil, binds = [], preparable: nil, async: false) # :nodoc:
101
+ arel = arel_from_relation(arel)
102
+
103
+ # If arel is locked this is a SELECT ... FOR UPDATE or somesuch.
104
+ # Such queries should not be cached.
105
+ if @query_cache_enabled && !(arel.respond_to?(:locked) && arel.locked)
99
106
  sql, binds, preparable = to_sql_and_binds(arel, binds, preparable)
100
107
 
101
- cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable) }
108
+ if async
109
+ lookup_sql_cache(sql, name, binds) || super(sql, name, binds, preparable: preparable, async: async)
110
+ else
111
+ cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable, async: async) }
112
+ end
102
113
  else
103
114
  super
104
115
  end
105
116
  end
106
117
 
107
118
  private
119
+ def lookup_sql_cache(sql, name, binds)
120
+ key = binds.empty? ? sql : [sql, binds]
121
+ hit = false
122
+ result = nil
123
+
124
+ @lock.synchronize do
125
+ if (result = @query_cache.delete(key))
126
+ hit = true
127
+ @query_cache[key] = result
128
+ end
129
+ end
130
+
131
+ if hit
132
+ ActiveSupport::Notifications.instrument(
133
+ "sql.active_record",
134
+ cache_notification_info(sql, name, binds)
135
+ )
136
+
137
+ result
138
+ end
139
+ end
140
+
108
141
  def cache_sql(sql, name, binds)
142
+ key = binds.empty? ? sql : [sql, binds]
143
+ result = nil
144
+ hit = false
145
+
109
146
  @lock.synchronize do
110
- result =
111
- if @query_cache[sql].key?(binds)
112
- ActiveSupport::Notifications.instrument(
113
- "sql.active_record",
114
- cache_notification_info(sql, name, binds)
115
- )
116
- @query_cache[sql][binds]
117
- else
118
- @query_cache[sql][binds] = yield
147
+ if (result = @query_cache.delete(key))
148
+ hit = true
149
+ @query_cache[key] = result
150
+ else
151
+ result = @query_cache[key] = yield
152
+ if @query_cache_max_size && @query_cache.size > @query_cache_max_size
153
+ @query_cache.shift
119
154
  end
120
- result.dup
155
+ end
121
156
  end
157
+
158
+ if hit
159
+ ActiveSupport::Notifications.instrument(
160
+ "sql.active_record",
161
+ cache_notification_info(sql, name, binds)
162
+ )
163
+ end
164
+
165
+ result.dup
122
166
  end
123
167
 
124
168
  # Database adapters can override this method to
@@ -134,15 +178,21 @@ module ActiveRecord
134
178
  }
135
179
  end
136
180
 
137
- # If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
138
- # queries should not be cached.
139
- def locked?(arel)
140
- arel = arel.arel if arel.is_a?(Relation)
141
- arel.respond_to?(:locked) && arel.locked
142
- end
143
-
144
181
  def configure_query_cache!
145
- enable_query_cache! if pool.query_cache_enabled
182
+ case query_cache = pool.db_config.query_cache
183
+ when 0, false
184
+ return
185
+ when Integer
186
+ @query_cache_max_size = query_cache
187
+ when nil
188
+ @query_cache_max_size = DEFAULT_SIZE
189
+ else
190
+ @query_cache_max_size = nil # no limit
191
+ end
192
+
193
+ if pool.query_cache_enabled
194
+ enable_query_cache!
195
+ end
146
196
  end
147
197
  end
148
198
  end