activerecord 5.2.3 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (268) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +624 -548
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +4 -2
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +9 -2
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/associations.rb +19 -14
  9. data/lib/active_record/associations/association.rb +52 -19
  10. data/lib/active_record/associations/association_scope.rb +4 -6
  11. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  13. data/lib/active_record/associations/builder/association.rb +14 -18
  14. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  15. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  17. data/lib/active_record/associations/builder/has_many.rb +2 -0
  18. data/lib/active_record/associations/builder/has_one.rb +35 -1
  19. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  20. data/lib/active_record/associations/collection_association.rb +6 -21
  21. data/lib/active_record/associations/collection_proxy.rb +12 -15
  22. data/lib/active_record/associations/foreign_association.rb +7 -0
  23. data/lib/active_record/associations/has_many_association.rb +2 -10
  24. data/lib/active_record/associations/has_many_through_association.rb +18 -25
  25. data/lib/active_record/associations/has_one_association.rb +28 -30
  26. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  27. data/lib/active_record/associations/join_dependency.rb +24 -28
  28. data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
  29. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +39 -31
  31. data/lib/active_record/associations/preloader/association.rb +38 -36
  32. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  33. data/lib/active_record/associations/singular_association.rb +2 -16
  34. data/lib/active_record/attribute_assignment.rb +7 -10
  35. data/lib/active_record/attribute_methods.rb +28 -100
  36. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  37. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  38. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  39. data/lib/active_record/attribute_methods/query.rb +2 -3
  40. data/lib/active_record/attribute_methods/read.rb +15 -53
  41. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  42. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  43. data/lib/active_record/attribute_methods/write.rb +17 -24
  44. data/lib/active_record/attributes.rb +13 -0
  45. data/lib/active_record/autosave_association.rb +16 -6
  46. data/lib/active_record/base.rb +2 -3
  47. data/lib/active_record/callbacks.rb +5 -19
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +116 -19
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +95 -123
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +20 -11
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  53. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  54. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  55. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  56. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +133 -54
  57. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  58. data/lib/active_record/connection_adapters/abstract_adapter.rb +180 -47
  59. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +128 -194
  60. data/lib/active_record/connection_adapters/column.rb +17 -13
  61. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  62. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  63. data/lib/active_record/connection_adapters/mysql/database_statements.rb +73 -13
  64. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  65. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  67. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  69. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  70. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  75. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  78. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  79. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  81. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  82. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  83. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
  86. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  87. data/lib/active_record/connection_adapters/postgresql_adapter.rb +160 -74
  88. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  89. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  90. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  91. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
  92. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +127 -143
  94. data/lib/active_record/connection_handling.rb +149 -27
  95. data/lib/active_record/core.rb +100 -60
  96. data/lib/active_record/counter_cache.rb +4 -29
  97. data/lib/active_record/database_configurations.rb +233 -0
  98. data/lib/active_record/database_configurations/database_config.rb +37 -0
  99. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  100. data/lib/active_record/database_configurations/url_config.rb +79 -0
  101. data/lib/active_record/dynamic_matchers.rb +1 -1
  102. data/lib/active_record/enum.rb +37 -7
  103. data/lib/active_record/errors.rb +15 -7
  104. data/lib/active_record/explain.rb +1 -1
  105. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  106. data/lib/active_record/fixture_set/render_context.rb +17 -0
  107. data/lib/active_record/fixture_set/table_row.rb +153 -0
  108. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  109. data/lib/active_record/fixtures.rb +145 -472
  110. data/lib/active_record/gem_version.rb +3 -3
  111. data/lib/active_record/inheritance.rb +13 -3
  112. data/lib/active_record/insert_all.rb +179 -0
  113. data/lib/active_record/integration.rb +68 -16
  114. data/lib/active_record/internal_metadata.rb +10 -2
  115. data/lib/active_record/locking/optimistic.rb +5 -6
  116. data/lib/active_record/locking/pessimistic.rb +3 -3
  117. data/lib/active_record/log_subscriber.rb +7 -26
  118. data/lib/active_record/middleware/database_selector.rb +75 -0
  119. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  120. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  121. data/lib/active_record/migration.rb +100 -81
  122. data/lib/active_record/migration/command_recorder.rb +50 -6
  123. data/lib/active_record/migration/compatibility.rb +76 -49
  124. data/lib/active_record/model_schema.rb +30 -9
  125. data/lib/active_record/nested_attributes.rb +2 -2
  126. data/lib/active_record/no_touching.rb +7 -0
  127. data/lib/active_record/persistence.rb +228 -24
  128. data/lib/active_record/query_cache.rb +11 -4
  129. data/lib/active_record/querying.rb +32 -20
  130. data/lib/active_record/railtie.rb +80 -43
  131. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  132. data/lib/active_record/railties/controller_runtime.rb +30 -35
  133. data/lib/active_record/railties/databases.rake +196 -46
  134. data/lib/active_record/reflection.rb +42 -44
  135. data/lib/active_record/relation.rb +310 -80
  136. data/lib/active_record/relation/batches.rb +13 -10
  137. data/lib/active_record/relation/calculations.rb +58 -51
  138. data/lib/active_record/relation/delegation.rb +26 -43
  139. data/lib/active_record/relation/finder_methods.rb +14 -27
  140. data/lib/active_record/relation/merger.rb +11 -20
  141. data/lib/active_record/relation/predicate_builder.rb +4 -6
  142. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  143. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  144. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  145. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  146. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  147. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  148. data/lib/active_record/relation/query_attribute.rb +13 -8
  149. data/lib/active_record/relation/query_methods.rb +206 -78
  150. data/lib/active_record/relation/spawn_methods.rb +1 -1
  151. data/lib/active_record/relation/where_clause.rb +14 -10
  152. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  153. data/lib/active_record/result.rb +30 -11
  154. data/lib/active_record/sanitization.rb +32 -40
  155. data/lib/active_record/schema.rb +2 -11
  156. data/lib/active_record/schema_dumper.rb +22 -7
  157. data/lib/active_record/schema_migration.rb +5 -1
  158. data/lib/active_record/scoping.rb +8 -8
  159. data/lib/active_record/scoping/default.rb +6 -7
  160. data/lib/active_record/scoping/named.rb +19 -15
  161. data/lib/active_record/statement_cache.rb +32 -5
  162. data/lib/active_record/store.rb +87 -8
  163. data/lib/active_record/table_metadata.rb +10 -17
  164. data/lib/active_record/tasks/database_tasks.rb +194 -25
  165. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  166. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  167. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  168. data/lib/active_record/test_databases.rb +23 -0
  169. data/lib/active_record/test_fixtures.rb +224 -0
  170. data/lib/active_record/timestamp.rb +39 -25
  171. data/lib/active_record/touch_later.rb +4 -2
  172. data/lib/active_record/transactions.rb +56 -65
  173. data/lib/active_record/translation.rb +1 -1
  174. data/lib/active_record/type.rb +3 -4
  175. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  176. data/lib/active_record/type_caster/connection.rb +15 -14
  177. data/lib/active_record/type_caster/map.rb +1 -4
  178. data/lib/active_record/validations.rb +1 -0
  179. data/lib/active_record/validations/uniqueness.rb +15 -27
  180. data/lib/arel.rb +51 -0
  181. data/lib/arel/alias_predication.rb +9 -0
  182. data/lib/arel/attributes.rb +22 -0
  183. data/lib/arel/attributes/attribute.rb +37 -0
  184. data/lib/arel/collectors/bind.rb +24 -0
  185. data/lib/arel/collectors/composite.rb +31 -0
  186. data/lib/arel/collectors/plain_string.rb +20 -0
  187. data/lib/arel/collectors/sql_string.rb +20 -0
  188. data/lib/arel/collectors/substitute_binds.rb +28 -0
  189. data/lib/arel/crud.rb +42 -0
  190. data/lib/arel/delete_manager.rb +18 -0
  191. data/lib/arel/errors.rb +9 -0
  192. data/lib/arel/expressions.rb +29 -0
  193. data/lib/arel/factory_methods.rb +49 -0
  194. data/lib/arel/insert_manager.rb +49 -0
  195. data/lib/arel/math.rb +45 -0
  196. data/lib/arel/nodes.rb +68 -0
  197. data/lib/arel/nodes/and.rb +32 -0
  198. data/lib/arel/nodes/ascending.rb +23 -0
  199. data/lib/arel/nodes/binary.rb +52 -0
  200. data/lib/arel/nodes/bind_param.rb +36 -0
  201. data/lib/arel/nodes/case.rb +55 -0
  202. data/lib/arel/nodes/casted.rb +50 -0
  203. data/lib/arel/nodes/comment.rb +29 -0
  204. data/lib/arel/nodes/count.rb +12 -0
  205. data/lib/arel/nodes/delete_statement.rb +45 -0
  206. data/lib/arel/nodes/descending.rb +23 -0
  207. data/lib/arel/nodes/equality.rb +18 -0
  208. data/lib/arel/nodes/extract.rb +24 -0
  209. data/lib/arel/nodes/false.rb +16 -0
  210. data/lib/arel/nodes/full_outer_join.rb +8 -0
  211. data/lib/arel/nodes/function.rb +44 -0
  212. data/lib/arel/nodes/grouping.rb +8 -0
  213. data/lib/arel/nodes/in.rb +8 -0
  214. data/lib/arel/nodes/infix_operation.rb +80 -0
  215. data/lib/arel/nodes/inner_join.rb +8 -0
  216. data/lib/arel/nodes/insert_statement.rb +37 -0
  217. data/lib/arel/nodes/join_source.rb +20 -0
  218. data/lib/arel/nodes/matches.rb +18 -0
  219. data/lib/arel/nodes/named_function.rb +23 -0
  220. data/lib/arel/nodes/node.rb +50 -0
  221. data/lib/arel/nodes/node_expression.rb +13 -0
  222. data/lib/arel/nodes/outer_join.rb +8 -0
  223. data/lib/arel/nodes/over.rb +15 -0
  224. data/lib/arel/nodes/regexp.rb +16 -0
  225. data/lib/arel/nodes/right_outer_join.rb +8 -0
  226. data/lib/arel/nodes/select_core.rb +67 -0
  227. data/lib/arel/nodes/select_statement.rb +41 -0
  228. data/lib/arel/nodes/sql_literal.rb +16 -0
  229. data/lib/arel/nodes/string_join.rb +11 -0
  230. data/lib/arel/nodes/table_alias.rb +27 -0
  231. data/lib/arel/nodes/terminal.rb +16 -0
  232. data/lib/arel/nodes/true.rb +16 -0
  233. data/lib/arel/nodes/unary.rb +45 -0
  234. data/lib/arel/nodes/unary_operation.rb +20 -0
  235. data/lib/arel/nodes/unqualified_column.rb +22 -0
  236. data/lib/arel/nodes/update_statement.rb +41 -0
  237. data/lib/arel/nodes/values_list.rb +9 -0
  238. data/lib/arel/nodes/window.rb +126 -0
  239. data/lib/arel/nodes/with.rb +11 -0
  240. data/lib/arel/order_predications.rb +13 -0
  241. data/lib/arel/predications.rb +257 -0
  242. data/lib/arel/select_manager.rb +271 -0
  243. data/lib/arel/table.rb +110 -0
  244. data/lib/arel/tree_manager.rb +72 -0
  245. data/lib/arel/update_manager.rb +34 -0
  246. data/lib/arel/visitors.rb +20 -0
  247. data/lib/arel/visitors/depth_first.rb +204 -0
  248. data/lib/arel/visitors/dot.rb +297 -0
  249. data/lib/arel/visitors/ibm_db.rb +34 -0
  250. data/lib/arel/visitors/informix.rb +62 -0
  251. data/lib/arel/visitors/mssql.rb +157 -0
  252. data/lib/arel/visitors/mysql.rb +83 -0
  253. data/lib/arel/visitors/oracle.rb +159 -0
  254. data/lib/arel/visitors/oracle12.rb +66 -0
  255. data/lib/arel/visitors/postgresql.rb +110 -0
  256. data/lib/arel/visitors/sqlite.rb +39 -0
  257. data/lib/arel/visitors/to_sql.rb +889 -0
  258. data/lib/arel/visitors/visitor.rb +46 -0
  259. data/lib/arel/visitors/where_sql.rb +23 -0
  260. data/lib/arel/window_predications.rb +9 -0
  261. data/lib/rails/generators/active_record/migration.rb +14 -1
  262. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  263. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  264. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  265. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  266. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  267. metadata +107 -25
  268. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/hash/compact"
4
-
5
3
  module ActiveRecord
6
4
  module ConnectionAdapters # :nodoc:
7
5
  class SchemaDumper < SchemaDumper # :nodoc:
@@ -17,7 +15,7 @@ module ActiveRecord
17
15
  def column_spec_for_primary_key(column)
18
16
  return {} if default_primary_key?(column)
19
17
  spec = { id: schema_type(column).inspect }
20
- spec.merge!(prepare_column_options(column).except!(:null))
18
+ spec.merge!(prepare_column_options(column).except!(:null, :comment))
21
19
  spec[:default] ||= "nil" if explicit_primary_key_default?(column)
22
20
  spec
23
21
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "active_record/migration/join_table"
4
4
  require "active_support/core_ext/string/access"
5
+ require "active_support/deprecation"
5
6
  require "digest/sha2"
6
7
 
7
8
  module ActiveRecord
@@ -100,7 +101,7 @@ module ActiveRecord
100
101
  def index_exists?(table_name, column_name, options = {})
101
102
  column_names = Array(column_name).map(&:to_s)
102
103
  checks = []
103
- checks << lambda { |i| i.columns == column_names }
104
+ checks << lambda { |i| Array(i.columns) == column_names }
104
105
  checks << lambda { |i| i.unique } if options[:unique]
105
106
  checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
106
107
 
@@ -129,11 +130,11 @@ module ActiveRecord
129
130
  # column_exists?(:suppliers, :name, :string, null: false)
130
131
  # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
131
132
  #
132
- def column_exists?(table_name, column_name, type = nil, options = {})
133
+ def column_exists?(table_name, column_name, type = nil, **options)
133
134
  column_name = column_name.to_s
134
135
  checks = []
135
136
  checks << lambda { |c| c.name == column_name }
136
- checks << lambda { |c| c.type == type } if type
137
+ checks << lambda { |c| c.type == type.to_sym rescue nil } if type
137
138
  column_options_keys.each do |attr|
138
139
  checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
139
140
  end
@@ -205,19 +206,22 @@ module ActiveRecord
205
206
  # Set to true to drop the table before creating it.
206
207
  # Set to +:cascade+ to drop dependent objects as well.
207
208
  # Defaults to false.
209
+ # [<tt>:if_not_exists</tt>]
210
+ # Set to true to avoid raising an error when the table already exists.
211
+ # Defaults to false.
208
212
  # [<tt>:as</tt>]
209
213
  # SQL to use to generate the table. When this option is used, the block is
210
214
  # ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
211
215
  #
212
216
  # ====== Add a backend specific option to the generated SQL (MySQL)
213
217
  #
214
- # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
218
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
215
219
  #
216
220
  # generates:
217
221
  #
218
222
  # CREATE TABLE suppliers (
219
223
  # id bigint auto_increment PRIMARY KEY
220
- # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
224
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
221
225
  #
222
226
  # ====== Rename the primary key column
223
227
  #
@@ -287,8 +291,8 @@ module ActiveRecord
287
291
  # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
288
292
  #
289
293
  # See also TableDefinition#column for details on how to create columns.
290
- def create_table(table_name, comment: nil, **options)
291
- td = create_table_definition table_name, options[:temporary], options[:options], options[:as], comment: comment
294
+ def create_table(table_name, **options)
295
+ td = create_table_definition(table_name, options)
292
296
 
293
297
  if options[:id] != false && !options[:as]
294
298
  pk = options.fetch(:primary_key) do
@@ -298,7 +302,7 @@ module ActiveRecord
298
302
  if pk.is_a?(Array)
299
303
  td.primary_keys pk
300
304
  else
301
- td.primary_key pk, options.fetch(:id, :primary_key), options
305
+ td.primary_key pk, options.fetch(:id, :primary_key), options.except(:comment)
302
306
  end
303
307
  end
304
308
 
@@ -317,7 +321,9 @@ module ActiveRecord
317
321
  end
318
322
 
319
323
  if supports_comments? && !supports_comments_in_create?
320
- change_table_comment(table_name, comment) if comment.present?
324
+ if table_comment = options[:comment].presence
325
+ change_table_comment(table_name, table_comment)
326
+ end
321
327
 
322
328
  td.columns.each do |column|
323
329
  change_column_comment(table_name, column.name, column.comment) if column.comment.present?
@@ -512,16 +518,20 @@ module ActiveRecord
512
518
  # Available options are (none of these exists by default):
513
519
  # * <tt>:limit</tt> -
514
520
  # Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
515
- # and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns.
521
+ # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
516
522
  # This option is ignored by some backends.
517
523
  # * <tt>:default</tt> -
518
524
  # The column's default value. Use +nil+ for +NULL+.
519
525
  # * <tt>:null</tt> -
520
526
  # Allows or disallows +NULL+ values in the column.
521
527
  # * <tt>:precision</tt> -
522
- # Specifies the precision for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
528
+ # Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
529
+ # <tt>:datetime</tt>, and <tt>:time</tt> columns.
523
530
  # * <tt>:scale</tt> -
524
531
  # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
532
+ # * <tt>:collation</tt> -
533
+ # Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column. If not specified, the
534
+ # column will have the same collation as the table.
525
535
  # * <tt>:comment</tt> -
526
536
  # Specifies the comment for the column. This option is ignored by some backends.
527
537
  #
@@ -575,7 +585,7 @@ module ActiveRecord
575
585
  # # Defines a column with a database-specific type.
576
586
  # add_column(:shapes, :triangle, 'polygon')
577
587
  # # ALTER TABLE "shapes" ADD "triangle" polygon
578
- def add_column(table_name, column_name, type, options = {})
588
+ def add_column(table_name, column_name, type, **options)
579
589
  at = create_alter_table table_name
580
590
  at.add_column(column_name, type, options)
581
591
  execute schema_creation.accept at
@@ -599,6 +609,7 @@ module ActiveRecord
599
609
  # The +type+ and +options+ parameters will be ignored if present. It can be helpful
600
610
  # to provide these in a migration's +change+ method so it can be reverted.
601
611
  # In that case, +type+ and +options+ will be used by #add_column.
612
+ # Indexes on the column are automatically removed.
602
613
  def remove_column(table_name, column_name, type = nil, options = {})
603
614
  execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, options)}"
604
615
  end
@@ -760,6 +771,17 @@ module ActiveRecord
760
771
  # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
761
772
  #
762
773
  # Note: only supported by MySQL.
774
+ #
775
+ # ====== Creating an index with a specific algorithm
776
+ #
777
+ # add_index(:developers, :name, algorithm: :concurrently)
778
+ # # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
779
+ #
780
+ # Note: only supported by PostgreSQL.
781
+ #
782
+ # Concurrently adding an index is not supported in a transaction.
783
+ #
784
+ # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
763
785
  def add_index(table_name, column_name, options = {})
764
786
  index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
765
787
  execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
@@ -783,6 +805,15 @@ module ActiveRecord
783
805
  #
784
806
  # remove_index :accounts, name: :by_branch_party
785
807
  #
808
+ # Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
809
+ #
810
+ # remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
811
+ #
812
+ # Note: only supported by PostgreSQL.
813
+ #
814
+ # Concurrently removing an index is not supported in a transaction.
815
+ #
816
+ # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
786
817
  def remove_index(table_name, options = {})
787
818
  index_name = index_name_for_remove(table_name, options)
788
819
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
@@ -842,17 +873,17 @@ module ActiveRecord
842
873
  # [<tt>:null</tt>]
843
874
  # Whether the column allows nulls. Defaults to true.
844
875
  #
845
- # ====== Create a user_id bigint column
876
+ # ====== Create a user_id bigint column without an index
846
877
  #
847
- # add_reference(:products, :user)
878
+ # add_reference(:products, :user, index: false)
848
879
  #
849
880
  # ====== Create a user_id string column
850
881
  #
851
882
  # add_reference(:products, :user, type: :string)
852
883
  #
853
- # ====== Create supplier_id, supplier_type columns and appropriate index
884
+ # ====== Create supplier_id, supplier_type columns
854
885
  #
855
- # add_reference(:products, :supplier, polymorphic: true, index: true)
886
+ # add_reference(:products, :supplier, polymorphic: true)
856
887
  #
857
888
  # ====== Create a supplier_id column with a unique index
858
889
  #
@@ -880,7 +911,7 @@ module ActiveRecord
880
911
  #
881
912
  # ====== Remove the reference
882
913
  #
883
- # remove_reference(:products, :user, index: true)
914
+ # remove_reference(:products, :user, index: false)
884
915
  #
885
916
  # ====== Remove polymorphic reference
886
917
  #
@@ -888,7 +919,7 @@ module ActiveRecord
888
919
  #
889
920
  # ====== Remove the reference with a foreign key
890
921
  #
891
- # remove_reference(:products, :user, index: true, foreign_key: true)
922
+ # remove_reference(:products, :user, foreign_key: true)
892
923
  #
893
924
  def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
894
925
  if foreign_key
@@ -956,7 +987,7 @@ module ActiveRecord
956
987
  # [<tt>:on_update</tt>]
957
988
  # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
958
989
  # [<tt>:validate</tt>]
959
- # (Postgres only) Specify whether or not the constraint should be validated. Defaults to +true+.
990
+ # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
960
991
  def add_foreign_key(from_table, to_table, options = {})
961
992
  return unless supports_foreign_keys?
962
993
 
@@ -980,15 +1011,22 @@ module ActiveRecord
980
1011
  #
981
1012
  # remove_foreign_key :accounts, column: :owner_id
982
1013
  #
1014
+ # Removes the foreign key on +accounts.owner_id+.
1015
+ #
1016
+ # remove_foreign_key :accounts, to_table: :owners
1017
+ #
983
1018
  # Removes the foreign key named +special_fk_name+ on the +accounts+ table.
984
1019
  #
985
1020
  # remove_foreign_key :accounts, name: :special_fk_name
986
1021
  #
987
- # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
988
- def remove_foreign_key(from_table, options_or_to_table = {})
1022
+ # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
1023
+ # with an addition of
1024
+ # [<tt>:to_table</tt>]
1025
+ # The name of the table that contains the referenced primary key.
1026
+ def remove_foreign_key(from_table, to_table = nil, **options)
989
1027
  return unless supports_foreign_keys?
990
1028
 
991
- fk_name_to_delete = foreign_key_for!(from_table, options_or_to_table).name
1029
+ fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
992
1030
 
993
1031
  at = create_alter_table from_table
994
1032
  at.drop_foreign_key fk_name_to_delete
@@ -1007,14 +1045,12 @@ module ActiveRecord
1007
1045
  # # Checks to see if a foreign key with a custom name exists.
1008
1046
  # foreign_key_exists?(:accounts, name: "special_fk_name")
1009
1047
  #
1010
- def foreign_key_exists?(from_table, options_or_to_table = {})
1011
- foreign_key_for(from_table, options_or_to_table).present?
1048
+ def foreign_key_exists?(from_table, to_table = nil, **options)
1049
+ foreign_key_for(from_table, to_table: to_table, **options).present?
1012
1050
  end
1013
1051
 
1014
1052
  def foreign_key_column_for(table_name) # :nodoc:
1015
- prefix = Base.table_name_prefix
1016
- suffix = Base.table_name_suffix
1017
- name = table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
1053
+ name = strip_table_name_prefix_and_suffix(table_name)
1018
1054
  "#{name.singularize}_id"
1019
1055
  end
1020
1056
 
@@ -1025,8 +1061,8 @@ module ActiveRecord
1025
1061
  options
1026
1062
  end
1027
1063
 
1028
- def dump_schema_information #:nodoc:
1029
- versions = ActiveRecord::SchemaMigration.all_versions
1064
+ def dump_schema_information # :nodoc:
1065
+ versions = schema_migration.all_versions
1030
1066
  insert_versions_sql(versions) if versions.any?
1031
1067
  end
1032
1068
 
@@ -1034,15 +1070,18 @@ module ActiveRecord
1034
1070
  { primary_key: true }
1035
1071
  end
1036
1072
 
1037
- def assume_migrated_upto_version(version, migrations_paths)
1038
- migrations_paths = Array(migrations_paths)
1073
+ def assume_migrated_upto_version(version, migrations_paths = nil)
1074
+ unless migrations_paths.nil?
1075
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
1076
+ Passing migrations_paths to #assume_migrated_upto_version is deprecated and will be removed in Rails 6.1.
1077
+ MSG
1078
+ end
1079
+
1039
1080
  version = version.to_i
1040
- sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
1081
+ sm_table = quote_table_name(schema_migration.table_name)
1041
1082
 
1042
- migrated = ActiveRecord::SchemaMigration.all_versions.map(&:to_i)
1043
- versions = migration_context.migration_files.map do |file|
1044
- migration_context.parse_migration_filename(file).first.to_i
1045
- end
1083
+ migrated = migration_context.get_all_versions
1084
+ versions = migration_context.migrations.map(&:version)
1046
1085
 
1047
1086
  unless migrated.include?(version)
1048
1087
  execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
@@ -1053,13 +1092,7 @@ module ActiveRecord
1053
1092
  if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
1054
1093
  raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
1055
1094
  end
1056
- if supports_multi_insert?
1057
- execute insert_versions_sql(inserting)
1058
- else
1059
- inserting.each do |v|
1060
- execute insert_versions_sql(v)
1061
- end
1062
- end
1095
+ execute insert_versions_sql(inserting)
1063
1096
  end
1064
1097
  end
1065
1098
 
@@ -1085,7 +1118,7 @@ module ActiveRecord
1085
1118
  if (0..6) === precision
1086
1119
  column_type_sql << "(#{precision})"
1087
1120
  else
1088
- raise(ActiveRecordError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6")
1121
+ raise ArgumentError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6"
1089
1122
  end
1090
1123
  elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
1091
1124
  column_type_sql << "(#{limit})"
@@ -1115,6 +1148,10 @@ module ActiveRecord
1115
1148
  def add_timestamps(table_name, options = {})
1116
1149
  options[:null] = false if options[:null].nil?
1117
1150
 
1151
+ if !options.key?(:precision) && supports_datetime_with_precision?
1152
+ options[:precision] = 6
1153
+ end
1154
+
1118
1155
  add_column table_name, :created_at, :datetime, options
1119
1156
  add_column table_name, :updated_at, :datetime, options
1120
1157
  end
@@ -1169,12 +1206,22 @@ module ActiveRecord
1169
1206
  end
1170
1207
 
1171
1208
  # Changes the comment for a table or removes it if +nil+.
1172
- def change_table_comment(table_name, comment)
1209
+ #
1210
+ # Passing a hash containing +:from+ and +:to+ will make this change
1211
+ # reversible in migration:
1212
+ #
1213
+ # change_table_comment(:posts, from: "old_comment", to: "new_comment")
1214
+ def change_table_comment(table_name, comment_or_changes)
1173
1215
  raise NotImplementedError, "#{self.class} does not support changing table comments"
1174
1216
  end
1175
1217
 
1176
1218
  # Changes the comment for a column or removes it if +nil+.
1177
- def change_column_comment(table_name, column_name, comment)
1219
+ #
1220
+ # Passing a hash containing +:from+ and +:to+ will make this change
1221
+ # reversible in migration:
1222
+ #
1223
+ # change_column_comment(:posts, :state, from: "old_comment", to: "new_comment")
1224
+ def change_column_comment(table_name, column_name, comment_or_changes)
1178
1225
  raise NotImplementedError, "#{self.class} does not support changing column comments"
1179
1226
  end
1180
1227
 
@@ -1276,7 +1323,7 @@ module ActiveRecord
1276
1323
  end
1277
1324
 
1278
1325
  def create_table_definition(*args)
1279
- TableDefinition.new(*args)
1326
+ TableDefinition.new(self, *args)
1280
1327
  end
1281
1328
 
1282
1329
  def create_alter_table(name)
@@ -1310,6 +1357,12 @@ module ActiveRecord
1310
1357
  { column: column_names }
1311
1358
  end
1312
1359
 
1360
+ def strip_table_name_prefix_and_suffix(table_name)
1361
+ prefix = Base.table_name_prefix
1362
+ suffix = Base.table_name_suffix
1363
+ table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
1364
+ end
1365
+
1313
1366
  def foreign_key_name(table_name, options)
1314
1367
  options.fetch(:name) do
1315
1368
  identifier = "#{table_name}_#{options.fetch(:column)}_fk"
@@ -1319,14 +1372,14 @@ module ActiveRecord
1319
1372
  end
1320
1373
  end
1321
1374
 
1322
- def foreign_key_for(from_table, options_or_to_table = {})
1375
+ def foreign_key_for(from_table, **options)
1323
1376
  return unless supports_foreign_keys?
1324
- foreign_keys(from_table).detect { |fk| fk.defined_for? options_or_to_table }
1377
+ foreign_keys(from_table).detect { |fk| fk.defined_for?(options) }
1325
1378
  end
1326
1379
 
1327
- def foreign_key_for!(from_table, options_or_to_table = {})
1328
- foreign_key_for(from_table, options_or_to_table) || \
1329
- raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}")
1380
+ def foreign_key_for!(from_table, to_table: nil, **options)
1381
+ foreign_key_for(from_table, to_table: to_table, **options) ||
1382
+ raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
1330
1383
  end
1331
1384
 
1332
1385
  def extract_foreign_key_action(specifier)
@@ -1352,11 +1405,37 @@ module ActiveRecord
1352
1405
  default_or_changes
1353
1406
  end
1354
1407
  end
1408
+ alias :extract_new_comment_value :extract_new_default_value
1355
1409
 
1356
1410
  def can_remove_index_by_name?(options)
1357
1411
  options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
1358
1412
  end
1359
1413
 
1414
+ def bulk_change_table(table_name, operations)
1415
+ sql_fragments = []
1416
+ non_combinable_operations = []
1417
+
1418
+ operations.each do |command, args|
1419
+ table, arguments = args.shift, args
1420
+ method = :"#{command}_for_alter"
1421
+
1422
+ if respond_to?(method, true)
1423
+ sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1424
+ sql_fragments << sqls
1425
+ non_combinable_operations.concat(procs)
1426
+ else
1427
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1428
+ non_combinable_operations.each(&:call)
1429
+ sql_fragments = []
1430
+ non_combinable_operations = []
1431
+ send(command, table, *arguments)
1432
+ end
1433
+ end
1434
+
1435
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1436
+ non_combinable_operations.each(&:call)
1437
+ end
1438
+
1360
1439
  def add_column_for_alter(table_name, column_name, type, options = {})
1361
1440
  td = create_table_definition(table_name)
1362
1441
  cd = td.new_column_definition(column_name, type, options)
@@ -1372,10 +1451,10 @@ module ActiveRecord
1372
1451
  end
1373
1452
 
1374
1453
  def insert_versions_sql(versions)
1375
- sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
1454
+ sm_table = quote_table_name(schema_migration.table_name)
1376
1455
 
1377
1456
  if versions.is_a?(Array)
1378
- sql = "INSERT INTO #{sm_table} (version) VALUES\n".dup
1457
+ sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1379
1458
  sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
1380
1459
  sql << ";\n\n"
1381
1460
  sql
@@ -40,24 +40,6 @@ module ActiveRecord
40
40
  committed? || rolledback?
41
41
  end
42
42
 
43
- def set_state(state)
44
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
45
- The set_state method is deprecated and will be removed in
46
- Rails 6.0. Please use rollback! or commit! to set transaction
47
- state directly.
48
- MSG
49
- case state
50
- when :rolledback
51
- rollback!
52
- when :committed
53
- commit!
54
- when nil
55
- nullify!
56
- else
57
- raise ArgumentError, "Invalid transaction state: #{state}"
58
- end
59
- end
60
-
61
43
  def rollback!
62
44
  @children.each { |c| c.rollback! }
63
45
  @state = :rolledback
@@ -91,13 +73,14 @@ module ActiveRecord
91
73
  end
92
74
 
93
75
  class Transaction #:nodoc:
94
- attr_reader :connection, :state, :records, :savepoint_name
95
- attr_writer :joinable
76
+ attr_reader :connection, :state, :records, :savepoint_name, :isolation_level
96
77
 
97
78
  def initialize(connection, options, run_commit_callbacks: false)
98
79
  @connection = connection
99
80
  @state = TransactionState.new
100
81
  @records = []
82
+ @isolation_level = options[:isolation]
83
+ @materialized = false
101
84
  @joinable = options.fetch(:joinable, true)
102
85
  @run_commit_callbacks = run_commit_callbacks
103
86
  end
@@ -106,10 +89,22 @@ module ActiveRecord
106
89
  records << record
107
90
  end
108
91
 
92
+ def materialize!
93
+ @materialized = true
94
+ end
95
+
96
+ def materialized?
97
+ @materialized
98
+ end
99
+
109
100
  def rollback_records
110
- ite = records.uniq
101
+ ite = records.uniq(&:object_id)
102
+ already_run_callbacks = {}
111
103
  while record = ite.shift
112
- record.rolledback!(force_restore_state: full_rollback?)
104
+ trigger_callbacks = record.trigger_transactional_callbacks?
105
+ should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
106
+ already_run_callbacks[record] ||= trigger_callbacks
107
+ record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
113
108
  end
114
109
  ensure
115
110
  ite.each do |i|
@@ -122,13 +117,17 @@ module ActiveRecord
122
117
  end
123
118
 
124
119
  def commit_records
125
- ite = records.uniq
120
+ ite = records.uniq(&:object_id)
121
+ already_run_callbacks = {}
126
122
  while record = ite.shift
127
123
  if @run_commit_callbacks
128
- record.committed!
124
+ trigger_callbacks = record.trigger_transactional_callbacks?
125
+ should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
126
+ already_run_callbacks[record] ||= trigger_callbacks
127
+ record.committed!(should_run_callbacks: should_run_callbacks)
129
128
  else
130
129
  # if not running callbacks, only adds the record to the parent transaction
131
- record.add_to_transaction
130
+ connection.add_transaction_record(record)
132
131
  end
133
132
  end
134
133
  ensure
@@ -142,24 +141,30 @@ module ActiveRecord
142
141
  end
143
142
 
144
143
  class SavepointTransaction < Transaction
145
- def initialize(connection, savepoint_name, parent_transaction, options, *args)
146
- super(connection, options, *args)
144
+ def initialize(connection, savepoint_name, parent_transaction, *args)
145
+ super(connection, *args)
147
146
 
148
147
  parent_transaction.state.add_child(@state)
149
148
 
150
- if options[:isolation]
149
+ if isolation_level
151
150
  raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction"
152
151
  end
153
- connection.create_savepoint(@savepoint_name = savepoint_name)
152
+
153
+ @savepoint_name = savepoint_name
154
+ end
155
+
156
+ def materialize!
157
+ connection.create_savepoint(savepoint_name)
158
+ super
154
159
  end
155
160
 
156
161
  def rollback
157
- connection.rollback_to_savepoint(savepoint_name)
162
+ connection.rollback_to_savepoint(savepoint_name) if materialized?
158
163
  @state.rollback!
159
164
  end
160
165
 
161
166
  def commit
162
- connection.release_savepoint(savepoint_name)
167
+ connection.release_savepoint(savepoint_name) if materialized?
163
168
  @state.commit!
164
169
  end
165
170
 
@@ -167,22 +172,23 @@ module ActiveRecord
167
172
  end
168
173
 
169
174
  class RealTransaction < Transaction
170
- def initialize(connection, options, *args)
171
- super
172
- if options[:isolation]
173
- connection.begin_isolated_db_transaction(options[:isolation])
175
+ def materialize!
176
+ if isolation_level
177
+ connection.begin_isolated_db_transaction(isolation_level)
174
178
  else
175
179
  connection.begin_db_transaction
176
180
  end
181
+
182
+ super
177
183
  end
178
184
 
179
185
  def rollback
180
- connection.rollback_db_transaction
186
+ connection.rollback_db_transaction if materialized?
181
187
  @state.full_rollback!
182
188
  end
183
189
 
184
190
  def commit
185
- connection.commit_db_transaction
191
+ connection.commit_db_transaction if materialized?
186
192
  @state.full_commit!
187
193
  end
188
194
  end
@@ -191,6 +197,9 @@ module ActiveRecord
191
197
  def initialize(connection)
192
198
  @stack = []
193
199
  @connection = connection
200
+ @has_unmaterialized_transactions = false
201
+ @materializing_transactions = false
202
+ @lazy_transactions_enabled = true
194
203
  end
195
204
 
196
205
  def begin_transaction(options = {})
@@ -204,11 +213,44 @@ module ActiveRecord
204
213
  run_commit_callbacks: run_commit_callbacks)
205
214
  end
206
215
 
216
+ if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && options[:_lazy] != false
217
+ @has_unmaterialized_transactions = true
218
+ else
219
+ transaction.materialize!
220
+ end
207
221
  @stack.push(transaction)
208
222
  transaction
209
223
  end
210
224
  end
211
225
 
226
+ def disable_lazy_transactions!
227
+ materialize_transactions
228
+ @lazy_transactions_enabled = false
229
+ end
230
+
231
+ def enable_lazy_transactions!
232
+ @lazy_transactions_enabled = true
233
+ end
234
+
235
+ def lazy_transactions_enabled?
236
+ @lazy_transactions_enabled
237
+ end
238
+
239
+ def materialize_transactions
240
+ return if @materializing_transactions
241
+ return unless @has_unmaterialized_transactions
242
+
243
+ @connection.lock.synchronize do
244
+ begin
245
+ @materializing_transactions = true
246
+ @stack.each { |t| t.materialize! unless t.materialized? }
247
+ ensure
248
+ @materializing_transactions = false
249
+ end
250
+ @has_unmaterialized_transactions = false
251
+ end
252
+ end
253
+
212
254
  def commit_transaction
213
255
  @connection.lock.synchronize do
214
256
  transaction = @stack.last
@@ -234,26 +276,24 @@ module ActiveRecord
234
276
 
235
277
  def within_new_transaction(options = {})
236
278
  @connection.lock.synchronize do
237
- begin
238
- transaction = begin_transaction options
239
- yield
240
- rescue Exception => error
241
- if transaction
279
+ transaction = begin_transaction options
280
+ yield
281
+ rescue Exception => error
282
+ if transaction
283
+ rollback_transaction
284
+ after_failure_actions(transaction, error)
285
+ end
286
+ raise
287
+ ensure
288
+ if !error && transaction
289
+ if Thread.current.status == "aborting"
242
290
  rollback_transaction
243
- after_failure_actions(transaction, error)
244
- end
245
- raise
246
- ensure
247
- unless error
248
- if Thread.current.status == "aborting"
249
- rollback_transaction if transaction
250
- else
251
- begin
252
- commit_transaction if transaction
253
- rescue Exception
254
- rollback_transaction(transaction) unless transaction.state.completed?
255
- raise
256
- end
291
+ else
292
+ begin
293
+ commit_transaction
294
+ rescue Exception
295
+ rollback_transaction(transaction) unless transaction.state.completed?
296
+ raise
257
297
  end
258
298
  end
259
299
  end