activerecord 7.0.8.7 → 7.2.2.1

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 (279) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +631 -1944
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +29 -29
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +23 -8
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +26 -14
  20. data/lib/active_record/associations/collection_proxy.rb +29 -11
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +21 -14
  24. data/lib/active_record/associations/has_many_through_association.rb +17 -7
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  27. data/lib/active_record/associations/join_dependency.rb +10 -10
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +354 -485
  36. data/lib/active_record/attribute_assignment.rb +0 -4
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +131 -32
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +148 -33
  47. data/lib/active_record/attributes.rb +64 -50
  48. data/lib/active_record/autosave_association.rb +69 -37
  49. data/lib/active_record/base.rb +9 -5
  50. data/lib/active_record/callbacks.rb +11 -25
  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 -42
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +323 -88
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +217 -63
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
  69. data/lib/active_record/connection_adapters/column.rb +9 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  89. data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
  90. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  91. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  92. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
  93. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
  96. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  97. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -46
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  101. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
  102. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  103. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
  104. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
  105. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  106. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  107. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  108. data/lib/active_record/connection_adapters.rb +124 -1
  109. data/lib/active_record/connection_handling.rb +96 -104
  110. data/lib/active_record/core.rb +251 -176
  111. data/lib/active_record/counter_cache.rb +68 -34
  112. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  113. data/lib/active_record/database_configurations/database_config.rb +26 -5
  114. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  115. data/lib/active_record/database_configurations/url_config.rb +37 -12
  116. data/lib/active_record/database_configurations.rb +87 -34
  117. data/lib/active_record/delegated_type.rb +39 -10
  118. data/lib/active_record/deprecator.rb +7 -0
  119. data/lib/active_record/destroy_association_async_job.rb +3 -1
  120. data/lib/active_record/dynamic_matchers.rb +2 -2
  121. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  122. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  123. data/lib/active_record/encryption/config.rb +25 -1
  124. data/lib/active_record/encryption/configurable.rb +12 -19
  125. data/lib/active_record/encryption/context.rb +10 -3
  126. data/lib/active_record/encryption/contexts.rb +5 -1
  127. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  128. data/lib/active_record/encryption/encryptable_record.rb +45 -21
  129. data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
  130. data/lib/active_record/encryption/encryptor.rb +18 -3
  131. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  132. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  133. data/lib/active_record/encryption/key_generator.rb +12 -1
  134. data/lib/active_record/encryption/key_provider.rb +1 -1
  135. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  136. data/lib/active_record/encryption/message_serializer.rb +6 -0
  137. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  138. data/lib/active_record/encryption/properties.rb +3 -3
  139. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  140. data/lib/active_record/encryption/scheme.rb +22 -21
  141. data/lib/active_record/encryption.rb +3 -0
  142. data/lib/active_record/enum.rb +129 -28
  143. data/lib/active_record/errors.rb +151 -31
  144. data/lib/active_record/explain.rb +21 -12
  145. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  146. data/lib/active_record/fixture_set/render_context.rb +2 -0
  147. data/lib/active_record/fixture_set/table_row.rb +29 -8
  148. data/lib/active_record/fixtures.rb +167 -97
  149. data/lib/active_record/future_result.rb +47 -8
  150. data/lib/active_record/gem_version.rb +4 -4
  151. data/lib/active_record/inheritance.rb +34 -18
  152. data/lib/active_record/insert_all.rb +72 -22
  153. data/lib/active_record/integration.rb +11 -8
  154. data/lib/active_record/internal_metadata.rb +124 -20
  155. data/lib/active_record/locking/optimistic.rb +8 -7
  156. data/lib/active_record/locking/pessimistic.rb +5 -2
  157. data/lib/active_record/log_subscriber.rb +18 -22
  158. data/lib/active_record/marshalling.rb +59 -0
  159. data/lib/active_record/message_pack.rb +124 -0
  160. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  161. data/lib/active_record/middleware/database_selector.rb +6 -8
  162. data/lib/active_record/middleware/shard_selector.rb +3 -1
  163. data/lib/active_record/migration/command_recorder.rb +106 -8
  164. data/lib/active_record/migration/compatibility.rb +147 -5
  165. data/lib/active_record/migration/default_strategy.rb +22 -0
  166. data/lib/active_record/migration/execution_strategy.rb +19 -0
  167. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  168. data/lib/active_record/migration.rb +234 -117
  169. data/lib/active_record/model_schema.rb +90 -102
  170. data/lib/active_record/nested_attributes.rb +48 -11
  171. data/lib/active_record/normalization.rb +163 -0
  172. data/lib/active_record/persistence.rb +168 -339
  173. data/lib/active_record/promise.rb +84 -0
  174. data/lib/active_record/query_cache.rb +18 -25
  175. data/lib/active_record/query_logs.rb +92 -52
  176. data/lib/active_record/query_logs_formatter.rb +41 -0
  177. data/lib/active_record/querying.rb +33 -8
  178. data/lib/active_record/railtie.rb +129 -85
  179. data/lib/active_record/railties/controller_runtime.rb +22 -7
  180. data/lib/active_record/railties/databases.rake +145 -154
  181. data/lib/active_record/railties/job_runtime.rb +23 -0
  182. data/lib/active_record/readonly_attributes.rb +32 -5
  183. data/lib/active_record/reflection.rb +267 -69
  184. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  185. data/lib/active_record/relation/batches.rb +198 -63
  186. data/lib/active_record/relation/calculations.rb +250 -93
  187. data/lib/active_record/relation/delegation.rb +30 -19
  188. data/lib/active_record/relation/finder_methods.rb +93 -18
  189. data/lib/active_record/relation/merger.rb +6 -6
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +18 -3
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  193. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  194. data/lib/active_record/relation/predicate_builder.rb +28 -16
  195. data/lib/active_record/relation/query_attribute.rb +2 -1
  196. data/lib/active_record/relation/query_methods.rb +576 -107
  197. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  198. data/lib/active_record/relation/spawn_methods.rb +5 -4
  199. data/lib/active_record/relation/where_clause.rb +7 -19
  200. data/lib/active_record/relation.rb +580 -90
  201. data/lib/active_record/result.rb +49 -48
  202. data/lib/active_record/runtime_registry.rb +63 -1
  203. data/lib/active_record/sanitization.rb +70 -25
  204. data/lib/active_record/schema.rb +8 -7
  205. data/lib/active_record/schema_dumper.rb +63 -14
  206. data/lib/active_record/schema_migration.rb +75 -24
  207. data/lib/active_record/scoping/default.rb +15 -5
  208. data/lib/active_record/scoping/named.rb +3 -2
  209. data/lib/active_record/scoping.rb +2 -1
  210. data/lib/active_record/secure_password.rb +60 -0
  211. data/lib/active_record/secure_token.rb +21 -3
  212. data/lib/active_record/signed_id.rb +27 -6
  213. data/lib/active_record/statement_cache.rb +7 -7
  214. data/lib/active_record/store.rb +8 -8
  215. data/lib/active_record/suppressor.rb +3 -1
  216. data/lib/active_record/table_metadata.rb +1 -1
  217. data/lib/active_record/tasks/database_tasks.rb +190 -118
  218. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  219. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  220. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  221. data/lib/active_record/test_fixtures.rb +170 -155
  222. data/lib/active_record/testing/query_assertions.rb +121 -0
  223. data/lib/active_record/timestamp.rb +31 -17
  224. data/lib/active_record/token_for.rb +123 -0
  225. data/lib/active_record/touch_later.rb +12 -7
  226. data/lib/active_record/transaction.rb +132 -0
  227. data/lib/active_record/transactions.rb +106 -24
  228. data/lib/active_record/translation.rb +0 -2
  229. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  230. data/lib/active_record/type/internal/timezone.rb +7 -2
  231. data/lib/active_record/type/serialized.rb +1 -3
  232. data/lib/active_record/type/time.rb +4 -0
  233. data/lib/active_record/type_caster/connection.rb +4 -4
  234. data/lib/active_record/validations/absence.rb +1 -1
  235. data/lib/active_record/validations/associated.rb +9 -3
  236. data/lib/active_record/validations/numericality.rb +5 -4
  237. data/lib/active_record/validations/presence.rb +5 -28
  238. data/lib/active_record/validations/uniqueness.rb +61 -11
  239. data/lib/active_record/validations.rb +12 -5
  240. data/lib/active_record/version.rb +1 -1
  241. data/lib/active_record.rb +247 -33
  242. data/lib/arel/alias_predication.rb +1 -1
  243. data/lib/arel/collectors/bind.rb +2 -0
  244. data/lib/arel/collectors/composite.rb +7 -0
  245. data/lib/arel/collectors/sql_string.rb +1 -1
  246. data/lib/arel/collectors/substitute_binds.rb +1 -1
  247. data/lib/arel/errors.rb +10 -0
  248. data/lib/arel/factory_methods.rb +4 -0
  249. data/lib/arel/nodes/binary.rb +6 -7
  250. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  251. data/lib/arel/nodes/cte.rb +36 -0
  252. data/lib/arel/nodes/fragments.rb +35 -0
  253. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  254. data/lib/arel/nodes/leading_join.rb +8 -0
  255. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  256. data/lib/arel/nodes/node.rb +115 -5
  257. data/lib/arel/nodes/sql_literal.rb +13 -0
  258. data/lib/arel/nodes/table_alias.rb +4 -0
  259. data/lib/arel/nodes.rb +6 -2
  260. data/lib/arel/predications.rb +3 -1
  261. data/lib/arel/select_manager.rb +1 -1
  262. data/lib/arel/table.rb +9 -5
  263. data/lib/arel/tree_manager.rb +8 -3
  264. data/lib/arel/update_manager.rb +2 -1
  265. data/lib/arel/visitors/dot.rb +1 -0
  266. data/lib/arel/visitors/mysql.rb +17 -5
  267. data/lib/arel/visitors/postgresql.rb +1 -12
  268. data/lib/arel/visitors/sqlite.rb +25 -0
  269. data/lib/arel/visitors/to_sql.rb +112 -34
  270. data/lib/arel/visitors/visitor.rb +2 -2
  271. data/lib/arel.rb +21 -3
  272. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  273. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  274. data/lib/rails/generators/active_record/migration.rb +3 -1
  275. data/lib/rails/generators/active_record/model/USAGE +113 -0
  276. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  277. metadata +54 -12
  278. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  279. data/lib/active_record/null_relation.rb +0 -63
@@ -14,18 +14,24 @@ module ActiveRecord
14
14
  sql
15
15
  end
16
16
 
17
- def to_sql_and_binds(arel_or_sql_string, binds = [], preparable = nil) # :nodoc:
17
+ def to_sql_and_binds(arel_or_sql_string, binds = [], preparable = nil, allow_retry = false) # :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"
22
27
  end
23
28
 
24
29
  collector = collector()
30
+ collector.retryable = true
25
31
 
26
32
  if prepared_statements
27
33
  collector.preparable = true
28
- sql, binds = visitor.compile(arel_or_sql_string.ast, collector)
34
+ sql, binds = visitor.compile(arel_or_sql_string, collector)
29
35
 
30
36
  if binds.length > bind_params_length
31
37
  unprepared_statement do
@@ -34,12 +40,13 @@ module ActiveRecord
34
40
  end
35
41
  preparable = collector.preparable
36
42
  else
37
- sql = visitor.compile(arel_or_sql_string.ast, collector)
43
+ sql = visitor.compile(arel_or_sql_string, collector)
38
44
  end
39
- [sql.freeze, binds, preparable]
45
+ allow_retry = collector.retryable
46
+ [sql.freeze, binds, preparable, allow_retry]
40
47
  else
41
48
  arel_or_sql_string = arel_or_sql_string.dup.freeze unless arel_or_sql_string.frozen?
42
- [arel_or_sql_string, binds, preparable]
49
+ [arel_or_sql_string, binds, preparable, allow_retry]
43
50
  end
44
51
  end
45
52
  private :to_sql_and_binds
@@ -59,24 +66,28 @@ module ActiveRecord
59
66
  end
60
67
 
61
68
  # Returns an ActiveRecord::Result instance.
62
- def select_all(arel, name = nil, binds = [], preparable: nil, async: false)
69
+ def select_all(arel, name = nil, binds = [], preparable: nil, async: false, allow_retry: false)
63
70
  arel = arel_from_relation(arel)
64
- sql, binds, preparable = to_sql_and_binds(arel, binds, preparable)
71
+ sql, binds, preparable, allow_retry = to_sql_and_binds(arel, binds, preparable, allow_retry)
65
72
 
66
- select(sql, name, binds, prepare: prepared_statements && preparable, async: async && FutureResult::SelectAll)
73
+ select(sql, name, binds,
74
+ prepare: prepared_statements && preparable,
75
+ async: async && FutureResult::SelectAll,
76
+ allow_retry: allow_retry
77
+ )
67
78
  rescue ::RangeError
68
- ActiveRecord::Result.empty
79
+ ActiveRecord::Result.empty(async: async)
69
80
  end
70
81
 
71
82
  # Returns a record hash with the column names as keys and column values
72
83
  # as values.
73
- def select_one(arel, name = nil, binds = [])
74
- select_all(arel, name, binds).first
84
+ def select_one(arel, name = nil, binds = [], async: false)
85
+ select_all(arel, name, binds, async: async).then(&:first)
75
86
  end
76
87
 
77
88
  # Returns a single value from a record
78
- def select_value(arel, name = nil, binds = [])
79
- single_value_from_rows(select_rows(arel, name, binds))
89
+ def select_value(arel, name = nil, binds = [], async: false)
90
+ select_rows(arel, name, binds, async: async).then { |rows| single_value_from_rows(rows) }
80
91
  end
81
92
 
82
93
  # Returns an array of the values of the first column in a select:
@@ -87,8 +98,8 @@ module ActiveRecord
87
98
 
88
99
  # Returns an array of arrays containing the field values.
89
100
  # Order is the same as that returned by +columns+.
90
- def select_rows(arel, name = nil, binds = [])
91
- select_all(arel, name, binds).rows
101
+ def select_rows(arel, name = nil, binds = [], async: false)
102
+ select_all(arel, name, binds, async: async).then(&:rows)
92
103
  end
93
104
 
94
105
  def query_value(sql, name = nil) # :nodoc:
@@ -100,7 +111,7 @@ module ActiveRecord
100
111
  end
101
112
 
102
113
  def query(sql, name = nil) # :nodoc:
103
- exec_query(sql, name).rows
114
+ internal_exec_query(sql, name).rows
104
115
  end
105
116
 
106
117
  # Determines whether the SQL statement is a write query.
@@ -110,47 +121,63 @@ module ActiveRecord
110
121
 
111
122
  # Executes the SQL statement in the context of this connection and returns
112
123
  # the raw result from the connection adapter.
124
+ #
125
+ # Setting +allow_retry+ to true causes the db to reconnect and retry
126
+ # executing the SQL statement in case of a connection-related exception.
127
+ # This option should only be enabled for known idempotent queries.
128
+ #
129
+ # Note: the query is assumed to have side effects and the query cache
130
+ # will be cleared. If the query is read-only, consider using #select_all
131
+ # instead.
132
+ #
113
133
  # Note: depending on your database connector, the result returned by this
114
- # method may be manually memory managed. Consider using the exec_query
134
+ # method may be manually memory managed. Consider using #exec_query
115
135
  # wrapper instead.
116
- def execute(sql, name = nil)
117
- raise NotImplementedError
136
+ def execute(sql, name = nil, allow_retry: false)
137
+ internal_execute(sql, name, allow_retry: allow_retry)
118
138
  end
119
139
 
120
140
  # Executes +sql+ statement in the context of this connection using
121
141
  # +binds+ as the bind substitutes. +name+ is logged along with
122
142
  # the executed +sql+ statement.
143
+ #
144
+ # Note: the query is assumed to have side effects and the query cache
145
+ # will be cleared. If the query is read-only, consider using #select_all
146
+ # instead.
123
147
  def exec_query(sql, name = "SQL", binds = [], prepare: false)
124
- raise NotImplementedError
148
+ internal_exec_query(sql, name, binds, prepare: prepare)
125
149
  end
126
150
 
127
151
  # Executes insert +sql+ statement in the context of this connection using
128
152
  # +binds+ as the bind substitutes. +name+ is logged along with
129
153
  # the executed +sql+ statement.
130
- def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
131
- sql, binds = sql_for_insert(sql, pk, binds)
132
- exec_query(sql, name, binds)
154
+ # Some adapters support the `returning` keyword argument which allows to control the result of the query:
155
+ # `nil` is the default value and maintains default behavior. If an array of column names is passed -
156
+ # the result will contain values of the specified columns from the inserted row.
157
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)
158
+ sql, binds = sql_for_insert(sql, pk, binds, returning)
159
+ internal_exec_query(sql, name, binds)
133
160
  end
134
161
 
135
162
  # Executes delete +sql+ statement in the context of this connection using
136
163
  # +binds+ as the bind substitutes. +name+ is logged along with
137
164
  # the executed +sql+ statement.
138
165
  def exec_delete(sql, name = nil, binds = [])
139
- exec_query(sql, name, binds)
166
+ internal_exec_query(sql, name, binds)
140
167
  end
141
168
 
142
169
  # Executes update +sql+ statement in the context of this connection using
143
170
  # +binds+ as the bind substitutes. +name+ is logged along with
144
171
  # the executed +sql+ statement.
145
172
  def exec_update(sql, name = nil, binds = [])
146
- exec_query(sql, name, binds)
173
+ internal_exec_query(sql, name, binds)
147
174
  end
148
175
 
149
176
  def exec_insert_all(sql, name) # :nodoc:
150
- exec_query(sql, name)
177
+ internal_exec_query(sql, name)
151
178
  end
152
179
 
153
- def explain(arel, binds = []) # :nodoc:
180
+ def explain(arel, binds = [], options = []) # :nodoc:
154
181
  raise NotImplementedError
155
182
  end
156
183
 
@@ -162,9 +189,15 @@ module ActiveRecord
162
189
  #
163
190
  # If the next id was calculated in advance (as in Oracle), it should be
164
191
  # passed in as +id_value+.
165
- def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
192
+ # Some adapters support the `returning` keyword argument which allows defining the return value of the method:
193
+ # `nil` is the default value and maintains default behavior. If an array of column names is passed -
194
+ # an array of is returned from the method representing values of the specified columns from the inserted row.
195
+ def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
166
196
  sql, binds = to_sql_and_binds(arel, binds)
167
- value = exec_insert(sql, name, binds, pk, sequence_name)
197
+ value = exec_insert(sql, name, binds, pk, sequence_name, returning: returning)
198
+
199
+ return returning_column_values(value) unless returning.nil?
200
+
168
201
  id_value || last_inserted_id(value)
169
202
  end
170
203
  alias create insert
@@ -187,7 +220,7 @@ module ActiveRecord
187
220
  end
188
221
 
189
222
  def truncate_tables(*table_names) # :nodoc:
190
- table_names -= [schema_migration.table_name, InternalMetadata.table_name]
223
+ table_names -= [pool.schema_migration.table_name, pool.internal_metadata.table_name]
191
224
 
192
225
  return if table_names.empty?
193
226
 
@@ -202,6 +235,17 @@ module ActiveRecord
202
235
  # Runs the given block in a database transaction, and returns the result
203
236
  # of the block.
204
237
  #
238
+ # == Transaction callbacks
239
+ #
240
+ # #transaction yields an ActiveRecord::Transaction object on which it is
241
+ # possible to register callback:
242
+ #
243
+ # ActiveRecord::Base.transaction do |transaction|
244
+ # transaction.before_commit { puts "before commit!" }
245
+ # transaction.after_commit { puts "after commit!" }
246
+ # transaction.after_rollback { puts "after rollback!" }
247
+ # end
248
+ #
205
249
  # == Nested transactions support
206
250
  #
207
251
  # #transaction calls can be nested. By default, this makes all database
@@ -269,9 +313,9 @@ module ActiveRecord
269
313
  # #transaction will raise exceptions when it tries to release the
270
314
  # already-automatically-released savepoints:
271
315
  #
272
- # Model.connection.transaction do # BEGIN
273
- # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
274
- # Model.connection.create_table(...)
316
+ # Model.lease_connection.transaction do # BEGIN
317
+ # Model.lease_connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
318
+ # Model.lease_connection.create_table(...)
275
319
  # # active_record_1 now automatically released
276
320
  # end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
277
321
  # end
@@ -304,14 +348,15 @@ module ActiveRecord
304
348
  # * You are joining an existing open transaction
305
349
  # * You are creating a nested (savepoint) transaction
306
350
  #
307
- # The mysql2 and postgresql adapters support setting the transaction
351
+ # The mysql2, trilogy, and postgresql adapters support setting the transaction
308
352
  # isolation level.
353
+ # :args: (requires_new: nil, isolation: nil, &block)
309
354
  def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
310
355
  if !requires_new && current_transaction.joinable?
311
356
  if isolation
312
357
  raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
313
358
  end
314
- yield
359
+ yield current_transaction.user_transaction
315
360
  else
316
361
  transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable, &block)
317
362
  end
@@ -323,7 +368,8 @@ module ActiveRecord
323
368
 
324
369
  delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction,
325
370
  :commit_transaction, :rollback_transaction, :materialize_transactions,
326
- :disable_lazy_transactions!, :enable_lazy_transactions!, to: :transaction_manager
371
+ :disable_lazy_transactions!, :enable_lazy_transactions!, :dirty_current_transaction,
372
+ to: :transaction_manager
327
373
 
328
374
  def mark_transaction_written_if_write(sql) # :nodoc:
329
375
  transaction = current_transaction
@@ -336,8 +382,24 @@ module ActiveRecord
336
382
  current_transaction.open?
337
383
  end
338
384
 
339
- def reset_transaction # :nodoc:
385
+ def reset_transaction(restore: false) # :nodoc:
386
+ # Store the existing transaction state to the side
387
+ old_state = @transaction_manager if restore && @transaction_manager&.restorable?
388
+
340
389
  @transaction_manager = ConnectionAdapters::TransactionManager.new(self)
390
+
391
+ if block_given?
392
+ # Reconfigure the connection without any transaction state in the way
393
+ result = yield
394
+
395
+ # Now the connection's fully established, we can swap back
396
+ if old_state
397
+ @transaction_manager = old_state
398
+ @transaction_manager.restore_transactions
399
+ end
400
+
401
+ result
402
+ end
341
403
  end
342
404
 
343
405
  # Register a record with the current transaction so that its after_commit and after_rollback callbacks
@@ -372,10 +434,18 @@ module ActiveRecord
372
434
  # done if the transaction block raises an exception or returns false.
373
435
  def rollback_db_transaction
374
436
  exec_rollback_db_transaction
437
+ rescue ActiveRecord::ConnectionNotEstablished, ActiveRecord::ConnectionFailed
438
+ # Connection's gone; that counts as a rollback
375
439
  end
376
440
 
377
441
  def exec_rollback_db_transaction() end # :nodoc:
378
442
 
443
+ def restart_db_transaction
444
+ exec_restart_db_transaction
445
+ end
446
+
447
+ def exec_restart_db_transaction() end # :nodoc:
448
+
379
449
  def rollback_to_savepoint(name = nil)
380
450
  exec_rollback_to_savepoint(name)
381
451
  end
@@ -393,7 +463,7 @@ module ActiveRecord
393
463
  # something beyond a simple insert (e.g. Oracle).
394
464
  # Most of adapters should implement +insert_fixtures_set+ that leverages bulk SQL insert.
395
465
  # We keep this method to provide fallback
396
- # for databases like sqlite that do not support bulk inserts.
466
+ # for databases like SQLite that do not support bulk inserts.
397
467
  def insert_fixture(fixture, table_name)
398
468
  execute(build_fixture_sql(Array.wrap(fixture), table_name), "Fixture Insert")
399
469
  end
@@ -404,8 +474,8 @@ module ActiveRecord
404
474
  statements = table_deletes + fixture_inserts
405
475
 
406
476
  with_multi_statements do
407
- disable_referential_integrity do
408
- transaction(requires_new: true) do
477
+ transaction(requires_new: true) do
478
+ disable_referential_integrity do
409
479
  execute_batch(statements, "Fixtures Load")
410
480
  end
411
481
  end
@@ -442,7 +512,7 @@ module ActiveRecord
442
512
  end
443
513
 
444
514
  # This is a safe default, even if not high precision on all databases
445
- HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
515
+ HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP", retryable: true).freeze # :nodoc:
446
516
  private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
447
517
 
448
518
  # Returns an Arel SQL literal for the CURRENT_TIMESTAMP for usage with
@@ -454,13 +524,30 @@ module ActiveRecord
454
524
  HIGH_PRECISION_CURRENT_TIMESTAMP
455
525
  end
456
526
 
527
+ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false) # :nodoc:
528
+ raise NotImplementedError
529
+ end
530
+
457
531
  private
532
+ def internal_execute(sql, name = "SCHEMA", allow_retry: false, materialize_transactions: true)
533
+ sql = transform_query(sql)
534
+ check_if_write_query(sql)
535
+
536
+ mark_transaction_written_if_write(sql)
537
+
538
+ raw_execute(sql, name, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
539
+ end
540
+
458
541
  def execute_batch(statements, name = nil)
459
542
  statements.each do |statement|
460
- execute(statement, name)
543
+ internal_execute(statement, name)
461
544
  end
462
545
  end
463
546
 
547
+ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
548
+ raise NotImplementedError
549
+ end
550
+
464
551
  DEFAULT_INSERT_VALUE = Arel.sql("DEFAULT").freeze
465
552
  private_constant :DEFAULT_INSERT_VALUE
466
553
 
@@ -536,7 +623,7 @@ module ActiveRecord
536
623
  end
537
624
 
538
625
  # Returns an ActiveRecord::Result instance.
539
- def select(sql, name = nil, binds = [], prepare: false, async: false)
626
+ def select(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false)
540
627
  if async && async_enabled?
541
628
  if current_transaction.joinable?
542
629
  raise AsynchronousQueryInsideTransactionError, "Asynchronous queries are not allowed inside transactions"
@@ -557,10 +644,28 @@ module ActiveRecord
557
644
  return future_result
558
645
  end
559
646
 
560
- exec_query(sql, name, binds, prepare: prepare)
647
+ result = internal_exec_query(sql, name, binds, prepare: prepare, allow_retry: allow_retry)
648
+ if async
649
+ FutureResult.wrap(result)
650
+ else
651
+ result
652
+ end
561
653
  end
562
654
 
563
- def sql_for_insert(sql, pk, binds)
655
+ def sql_for_insert(sql, pk, binds, returning) # :nodoc:
656
+ if supports_insert_returning?
657
+ if pk.nil?
658
+ # Extract the table from the insert sql. Yuck.
659
+ table_ref = extract_table_ref_from_insert_sql(sql)
660
+ pk = primary_key(table_ref) if table_ref
661
+ end
662
+
663
+ returning_columns = returning || Array(pk)
664
+
665
+ returning_columns_statement = returning_columns.map { |c| quote_column_name(c) }.join(", ")
666
+ sql = "#{sql} RETURNING #{returning_columns_statement}" if returning_columns.any?
667
+ end
668
+
564
669
  [sql, binds]
565
670
  end
566
671
 
@@ -568,6 +673,10 @@ module ActiveRecord
568
673
  single_value_from_rows(result.rows)
569
674
  end
570
675
 
676
+ def returning_column_values(result)
677
+ [last_inserted_id(result)]
678
+ end
679
+
571
680
  def single_value_from_rows(rows)
572
681
  row = rows.first
573
682
  row && row.first
@@ -580,6 +689,12 @@ module ActiveRecord
580
689
  relation
581
690
  end
582
691
  end
692
+
693
+ def extract_table_ref_from_insert_sql(sql)
694
+ if sql =~ /into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im
695
+ $1.delete('"').strip
696
+ end
697
+ end
583
698
  end
584
699
  end
585
700
  end