activerecord 6.1.7 → 7.1.5

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