activerecord 6.0.3 → 6.1.3

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 (246) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +968 -682
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record.rb +7 -14
  6. data/lib/active_record/aggregations.rb +5 -5
  7. data/lib/active_record/association_relation.rb +30 -12
  8. data/lib/active_record/associations.rb +118 -11
  9. data/lib/active_record/associations/alias_tracker.rb +19 -15
  10. data/lib/active_record/associations/association.rb +44 -28
  11. data/lib/active_record/associations/association_scope.rb +19 -15
  12. data/lib/active_record/associations/belongs_to_association.rb +22 -8
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  14. data/lib/active_record/associations/builder/association.rb +32 -5
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
  18. data/lib/active_record/associations/builder/has_many.rb +6 -2
  19. data/lib/active_record/associations/builder/has_one.rb +11 -14
  20. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  21. data/lib/active_record/associations/collection_association.rb +19 -6
  22. data/lib/active_record/associations/collection_proxy.rb +13 -5
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +24 -2
  25. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  26. data/lib/active_record/associations/has_one_association.rb +15 -1
  27. data/lib/active_record/associations/join_dependency.rb +72 -50
  28. data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
  29. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  30. data/lib/active_record/associations/preloader.rb +11 -5
  31. data/lib/active_record/associations/preloader/association.rb +51 -25
  32. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  33. data/lib/active_record/associations/singular_association.rb +1 -1
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/attribute_assignment.rb +10 -8
  36. data/lib/active_record/attribute_methods.rb +64 -54
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  38. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  39. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  40. data/lib/active_record/attribute_methods/query.rb +3 -6
  41. data/lib/active_record/attribute_methods/read.rb +8 -11
  42. data/lib/active_record/attribute_methods/serialization.rb +11 -5
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  44. data/lib/active_record/attribute_methods/write.rb +12 -20
  45. data/lib/active_record/attributes.rb +33 -8
  46. data/lib/active_record/autosave_association.rb +57 -40
  47. data/lib/active_record/base.rb +2 -14
  48. data/lib/active_record/callbacks.rb +152 -22
  49. data/lib/active_record/coders/yaml_column.rb +1 -1
  50. data/lib/active_record/connection_adapters.rb +50 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +191 -134
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -23
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -8
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +116 -27
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +228 -83
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +54 -72
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +129 -88
  64. data/lib/active_record/connection_adapters/column.rb +15 -1
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  67. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -25
  68. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  70. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
  71. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  72. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  73. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +11 -7
  74. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  75. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  76. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  77. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +13 -54
  80. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  82. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
  90. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  91. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  92. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  95. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  96. data/lib/active_record/connection_adapters/postgresql_adapter.rb +74 -63
  97. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  98. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  99. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +31 -6
  100. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  101. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  102. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +37 -4
  103. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +49 -50
  104. data/lib/active_record/connection_handling.rb +218 -71
  105. data/lib/active_record/core.rb +245 -61
  106. data/lib/active_record/database_configurations.rb +124 -85
  107. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  108. data/lib/active_record/database_configurations/database_config.rb +52 -9
  109. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  110. data/lib/active_record/database_configurations/url_config.rb +15 -40
  111. data/lib/active_record/delegated_type.rb +209 -0
  112. data/lib/active_record/destroy_association_async_job.rb +36 -0
  113. data/lib/active_record/enum.rb +82 -38
  114. data/lib/active_record/errors.rb +47 -12
  115. data/lib/active_record/explain.rb +9 -4
  116. data/lib/active_record/explain_subscriber.rb +1 -1
  117. data/lib/active_record/fixture_set/file.rb +10 -17
  118. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  119. data/lib/active_record/fixture_set/render_context.rb +1 -1
  120. data/lib/active_record/fixture_set/table_row.rb +2 -2
  121. data/lib/active_record/fixtures.rb +58 -9
  122. data/lib/active_record/gem_version.rb +1 -1
  123. data/lib/active_record/inheritance.rb +40 -18
  124. data/lib/active_record/insert_all.rb +35 -6
  125. data/lib/active_record/integration.rb +3 -5
  126. data/lib/active_record/internal_metadata.rb +16 -7
  127. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  128. data/lib/active_record/locking/optimistic.rb +33 -17
  129. data/lib/active_record/locking/pessimistic.rb +6 -2
  130. data/lib/active_record/log_subscriber.rb +27 -8
  131. data/lib/active_record/middleware/database_selector.rb +4 -1
  132. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  133. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  134. data/lib/active_record/migration.rb +114 -84
  135. data/lib/active_record/migration/command_recorder.rb +47 -27
  136. data/lib/active_record/migration/compatibility.rb +68 -17
  137. data/lib/active_record/model_schema.rb +117 -13
  138. data/lib/active_record/nested_attributes.rb +2 -3
  139. data/lib/active_record/no_touching.rb +1 -1
  140. data/lib/active_record/persistence.rb +50 -45
  141. data/lib/active_record/query_cache.rb +15 -5
  142. data/lib/active_record/querying.rb +11 -6
  143. data/lib/active_record/railtie.rb +64 -44
  144. data/lib/active_record/railties/console_sandbox.rb +2 -4
  145. data/lib/active_record/railties/databases.rake +276 -99
  146. data/lib/active_record/readonly_attributes.rb +4 -0
  147. data/lib/active_record/reflection.rb +71 -57
  148. data/lib/active_record/relation.rb +95 -67
  149. data/lib/active_record/relation/batches.rb +38 -31
  150. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  151. data/lib/active_record/relation/calculations.rb +101 -44
  152. data/lib/active_record/relation/delegation.rb +2 -1
  153. data/lib/active_record/relation/finder_methods.rb +45 -15
  154. data/lib/active_record/relation/from_clause.rb +1 -1
  155. data/lib/active_record/relation/merger.rb +27 -25
  156. data/lib/active_record/relation/predicate_builder.rb +61 -38
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  158. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  159. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  160. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  161. data/lib/active_record/relation/query_methods.rb +333 -195
  162. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  163. data/lib/active_record/relation/spawn_methods.rb +8 -7
  164. data/lib/active_record/relation/where_clause.rb +107 -60
  165. data/lib/active_record/result.rb +41 -33
  166. data/lib/active_record/runtime_registry.rb +2 -2
  167. data/lib/active_record/sanitization.rb +6 -17
  168. data/lib/active_record/schema_dumper.rb +34 -4
  169. data/lib/active_record/schema_migration.rb +2 -8
  170. data/lib/active_record/scoping/named.rb +6 -17
  171. data/lib/active_record/secure_token.rb +16 -8
  172. data/lib/active_record/serialization.rb +5 -3
  173. data/lib/active_record/signed_id.rb +116 -0
  174. data/lib/active_record/statement_cache.rb +20 -4
  175. data/lib/active_record/store.rb +2 -2
  176. data/lib/active_record/suppressor.rb +2 -2
  177. data/lib/active_record/table_metadata.rb +42 -51
  178. data/lib/active_record/tasks/database_tasks.rb +140 -113
  179. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  180. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  181. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  182. data/lib/active_record/test_databases.rb +5 -4
  183. data/lib/active_record/test_fixtures.rb +37 -16
  184. data/lib/active_record/timestamp.rb +4 -6
  185. data/lib/active_record/touch_later.rb +21 -21
  186. data/lib/active_record/transactions.rb +19 -66
  187. data/lib/active_record/type.rb +8 -1
  188. data/lib/active_record/type/serialized.rb +6 -2
  189. data/lib/active_record/type/time.rb +10 -0
  190. data/lib/active_record/type_caster/connection.rb +0 -1
  191. data/lib/active_record/type_caster/map.rb +8 -5
  192. data/lib/active_record/validations.rb +1 -0
  193. data/lib/active_record/validations/numericality.rb +35 -0
  194. data/lib/active_record/validations/uniqueness.rb +24 -4
  195. data/lib/arel.rb +5 -13
  196. data/lib/arel/attributes/attribute.rb +4 -0
  197. data/lib/arel/collectors/bind.rb +5 -0
  198. data/lib/arel/collectors/composite.rb +8 -0
  199. data/lib/arel/collectors/sql_string.rb +7 -0
  200. data/lib/arel/collectors/substitute_binds.rb +7 -0
  201. data/lib/arel/nodes.rb +3 -1
  202. data/lib/arel/nodes/binary.rb +82 -8
  203. data/lib/arel/nodes/bind_param.rb +8 -0
  204. data/lib/arel/nodes/casted.rb +21 -9
  205. data/lib/arel/nodes/equality.rb +6 -9
  206. data/lib/arel/nodes/grouping.rb +3 -0
  207. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  208. data/lib/arel/nodes/in.rb +8 -1
  209. data/lib/arel/nodes/infix_operation.rb +13 -1
  210. data/lib/arel/nodes/join_source.rb +1 -1
  211. data/lib/arel/nodes/node.rb +7 -6
  212. data/lib/arel/nodes/ordering.rb +27 -0
  213. data/lib/arel/nodes/sql_literal.rb +3 -0
  214. data/lib/arel/nodes/table_alias.rb +7 -3
  215. data/lib/arel/nodes/unary.rb +0 -1
  216. data/lib/arel/predications.rb +12 -18
  217. data/lib/arel/select_manager.rb +1 -2
  218. data/lib/arel/table.rb +13 -5
  219. data/lib/arel/visitors.rb +0 -7
  220. data/lib/arel/visitors/dot.rb +14 -2
  221. data/lib/arel/visitors/mysql.rb +11 -1
  222. data/lib/arel/visitors/postgresql.rb +15 -4
  223. data/lib/arel/visitors/to_sql.rb +89 -78
  224. data/lib/rails/generators/active_record/migration.rb +6 -1
  225. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  226. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  227. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  228. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  229. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  230. metadata +28 -29
  231. data/lib/active_record/advisory_lock_base.rb +0 -18
  232. data/lib/active_record/attribute_decorators.rb +0 -88
  233. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  234. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  235. data/lib/active_record/define_callbacks.rb +0 -22
  236. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  237. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  238. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  239. data/lib/arel/attributes.rb +0 -22
  240. data/lib/arel/visitors/depth_first.rb +0 -203
  241. data/lib/arel/visitors/ibm_db.rb +0 -34
  242. data/lib/arel/visitors/informix.rb +0 -62
  243. data/lib/arel/visitors/mssql.rb +0 -156
  244. data/lib/arel/visitors/oracle.rb +0 -158
  245. data/lib/arel/visitors/oracle12.rb +0 -65
  246. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -5,10 +5,11 @@ module ActiveRecord
5
5
  class TransactionState
6
6
  def initialize(state = nil)
7
7
  @state = state
8
- @children = []
8
+ @children = nil
9
9
  end
10
10
 
11
11
  def add_child(state)
12
+ @children ||= []
12
13
  @children << state
13
14
  end
14
15
 
@@ -41,12 +42,12 @@ module ActiveRecord
41
42
  end
42
43
 
43
44
  def rollback!
44
- @children.each { |c| c.rollback! }
45
+ @children&.each { |c| c.rollback! }
45
46
  @state = :rolledback
46
47
  end
47
48
 
48
49
  def full_rollback!
49
- @children.each { |c| c.rollback! }
50
+ @children&.each { |c| c.rollback! }
50
51
  @state = :fully_rolledback
51
52
  end
52
53
 
@@ -69,24 +70,40 @@ module ActiveRecord
69
70
  def closed?; true; end
70
71
  def open?; false; end
71
72
  def joinable?; false; end
72
- def add_record(record); end
73
+ def add_record(record, _ = true); end
73
74
  end
74
75
 
75
76
  class Transaction #:nodoc:
76
- attr_reader :connection, :state, :records, :savepoint_name, :isolation_level
77
+ attr_reader :connection, :state, :savepoint_name, :isolation_level
78
+ attr_accessor :written
77
79
 
78
- def initialize(connection, options, run_commit_callbacks: false)
80
+ def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
79
81
  @connection = connection
80
82
  @state = TransactionState.new
81
- @records = []
82
- @isolation_level = options[:isolation]
83
+ @records = nil
84
+ @isolation_level = isolation
83
85
  @materialized = false
84
- @joinable = options.fetch(:joinable, true)
86
+ @joinable = joinable
85
87
  @run_commit_callbacks = run_commit_callbacks
88
+ @lazy_enrollment_records = nil
86
89
  end
87
90
 
88
- def add_record(record)
89
- records << record
91
+ def add_record(record, ensure_finalize = true)
92
+ @records ||= []
93
+ if ensure_finalize
94
+ @records << record
95
+ else
96
+ @lazy_enrollment_records ||= ObjectSpace::WeakMap.new
97
+ @lazy_enrollment_records[record] = record
98
+ end
99
+ end
100
+
101
+ def records
102
+ if @lazy_enrollment_records
103
+ @records.concat @lazy_enrollment_records.values
104
+ @lazy_enrollment_records = nil
105
+ end
106
+ @records
90
107
  end
91
108
 
92
109
  def materialize!
@@ -98,6 +115,7 @@ module ActiveRecord
98
115
  end
99
116
 
100
117
  def rollback_records
118
+ return unless records
101
119
  ite = records.uniq(&:__id__)
102
120
  already_run_callbacks = {}
103
121
  while record = ite.shift
@@ -107,16 +125,17 @@ module ActiveRecord
107
125
  record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
108
126
  end
109
127
  ensure
110
- ite.each do |i|
128
+ ite&.each do |i|
111
129
  i.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: false)
112
130
  end
113
131
  end
114
132
 
115
133
  def before_commit_records
116
- records.uniq.each(&:before_committed!) if @run_commit_callbacks
134
+ records.uniq.each(&:before_committed!) if records && @run_commit_callbacks
117
135
  end
118
136
 
119
137
  def commit_records
138
+ return unless records
120
139
  ite = records.uniq(&:__id__)
121
140
  already_run_callbacks = {}
122
141
  while record = ite.shift
@@ -131,7 +150,7 @@ module ActiveRecord
131
150
  end
132
151
  end
133
152
  ensure
134
- ite.each { |i| i.committed!(should_run_callbacks: false) }
153
+ ite&.each { |i| i.committed!(should_run_callbacks: false) }
135
154
  end
136
155
 
137
156
  def full_rollback?; true; end
@@ -141,8 +160,8 @@ module ActiveRecord
141
160
  end
142
161
 
143
162
  class SavepointTransaction < Transaction
144
- def initialize(connection, savepoint_name, parent_transaction, *args, **options)
145
- super(connection, *args, **options)
163
+ def initialize(connection, savepoint_name, parent_transaction, **options)
164
+ super(connection, **options)
146
165
 
147
166
  parent_transaction.state.add_child(@state)
148
167
 
@@ -202,18 +221,29 @@ module ActiveRecord
202
221
  @lazy_transactions_enabled = true
203
222
  end
204
223
 
205
- def begin_transaction(options = {})
224
+ def begin_transaction(isolation: nil, joinable: true, _lazy: true)
206
225
  @connection.lock.synchronize do
207
226
  run_commit_callbacks = !current_transaction.joinable?
208
227
  transaction =
209
228
  if @stack.empty?
210
- RealTransaction.new(@connection, options, run_commit_callbacks: run_commit_callbacks)
229
+ RealTransaction.new(
230
+ @connection,
231
+ isolation: isolation,
232
+ joinable: joinable,
233
+ run_commit_callbacks: run_commit_callbacks
234
+ )
211
235
  else
212
- SavepointTransaction.new(@connection, "active_record_#{@stack.size}", @stack.last, options,
213
- run_commit_callbacks: run_commit_callbacks)
236
+ SavepointTransaction.new(
237
+ @connection,
238
+ "active_record_#{@stack.size}",
239
+ @stack.last,
240
+ isolation: isolation,
241
+ joinable: joinable,
242
+ run_commit_callbacks: run_commit_callbacks
243
+ )
214
244
  end
215
245
 
216
- if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && options[:_lazy] != false
246
+ if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && _lazy
217
247
  @has_unmaterialized_transactions = true
218
248
  else
219
249
  transaction.materialize!
@@ -274,10 +304,12 @@ module ActiveRecord
274
304
  end
275
305
  end
276
306
 
277
- def within_new_transaction(options = {})
307
+ def within_new_transaction(isolation: nil, joinable: true)
278
308
  @connection.lock.synchronize do
279
- transaction = begin_transaction options
280
- yield
309
+ transaction = begin_transaction(isolation: isolation, joinable: joinable)
310
+ ret = yield
311
+ completed = true
312
+ ret
281
313
  rescue Exception => error
282
314
  if transaction
283
315
  rollback_transaction
@@ -285,15 +317,31 @@ module ActiveRecord
285
317
  end
286
318
  raise
287
319
  ensure
288
- if !error && transaction
289
- if Thread.current.status == "aborting"
290
- rollback_transaction
320
+ if transaction
321
+ if error
322
+ # @connection still holds an open transaction, so we must not
323
+ # put it back in the pool for reuse
324
+ @connection.throw_away! unless transaction.state.rolledback?
291
325
  else
292
- begin
293
- commit_transaction
294
- rescue Exception
295
- rollback_transaction(transaction) unless transaction.state.completed?
296
- raise
326
+ if Thread.current.status == "aborting"
327
+ rollback_transaction
328
+ else
329
+ if !completed && transaction.written
330
+ ActiveSupport::Deprecation.warn(<<~EOW)
331
+ Using `return`, `break` or `throw` to exit a transaction block is
332
+ deprecated without replacement. If the `throw` came from
333
+ `Timeout.timeout(duration)`, pass an exception class as a second
334
+ argument so it doesn't use `throw` to abort its block. This results
335
+ in the transaction being committed, but in the next release of Rails
336
+ it will rollback.
337
+ EOW
338
+ end
339
+ begin
340
+ commit_transaction
341
+ rescue Exception
342
+ rollback_transaction(transaction) unless transaction.state.completed?
343
+ raise
344
+ end
297
345
  end
298
346
  end
299
347
  end
@@ -1,13 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "set"
4
- require "active_record/connection_adapters/determine_if_preparable_visitor"
5
4
  require "active_record/connection_adapters/schema_cache"
6
5
  require "active_record/connection_adapters/sql_type_metadata"
7
6
  require "active_record/connection_adapters/abstract/schema_dumper"
8
7
  require "active_record/connection_adapters/abstract/schema_creation"
9
8
  require "active_support/concurrency/load_interlock_aware_monitor"
10
- require "active_support/deprecation"
11
9
  require "arel/collectors/bind"
12
10
  require "arel/collectors/composite"
13
11
  require "arel/collectors/sql_string"
@@ -15,44 +13,6 @@ require "arel/collectors/substitute_binds"
15
13
 
16
14
  module ActiveRecord
17
15
  module ConnectionAdapters # :nodoc:
18
- extend ActiveSupport::Autoload
19
-
20
- autoload :Column
21
- autoload :ConnectionSpecification
22
-
23
- autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
24
- autoload :IndexDefinition
25
- autoload :ColumnDefinition
26
- autoload :ChangeColumnDefinition
27
- autoload :ForeignKeyDefinition
28
- autoload :TableDefinition
29
- autoload :Table
30
- autoload :AlterTable
31
- autoload :ReferenceDefinition
32
- end
33
-
34
- autoload_at "active_record/connection_adapters/abstract/connection_pool" do
35
- autoload :ConnectionHandler
36
- end
37
-
38
- autoload_under "abstract" do
39
- autoload :SchemaStatements
40
- autoload :DatabaseStatements
41
- autoload :DatabaseLimits
42
- autoload :Quoting
43
- autoload :ConnectionPool
44
- autoload :QueryCache
45
- autoload :Savepoints
46
- end
47
-
48
- autoload_at "active_record/connection_adapters/abstract/transaction" do
49
- autoload :TransactionManager
50
- autoload :NullTransaction
51
- autoload :RealTransaction
52
- autoload :SavepointTransaction
53
- autoload :TransactionState
54
- end
55
-
56
16
  # Active Record supports multiple database systems. AbstractAdapter and
57
17
  # related classes form the abstraction layer which makes this possible.
58
18
  # An AbstractAdapter represents a connection to a database, and provides an
@@ -77,7 +37,7 @@ module ActiveRecord
77
37
  include Savepoints
78
38
 
79
39
  SIMPLE_INT = /\A\d+\z/
80
- COMMENT_REGEX = %r{/\*(?:[^\*]|\*[^/])*\*/}m
40
+ COMMENT_REGEX = %r{(?:\-\-.*\n)*|/\*(?:[^\*]|\*[^/])*\*/}m
81
41
 
82
42
  attr_accessor :pool
83
43
  attr_reader :visitor, :owner, :logger, :lock
@@ -103,7 +63,11 @@ module ActiveRecord
103
63
  end
104
64
  end
105
65
 
66
+ DEFAULT_READ_QUERY = [:begin, :commit, :explain, :release, :rollback, :savepoint, :select, :with] # :nodoc:
67
+ private_constant :DEFAULT_READ_QUERY
68
+
106
69
  def self.build_read_query_regexp(*parts) # :nodoc:
70
+ parts += DEFAULT_READ_QUERY
107
71
  parts = parts.map { |part| /#{part}/i }
108
72
  /\A(?:[\(\s]|#{COMMENT_REGEX})*#{Regexp.union(*parts)}/
109
73
  end
@@ -130,12 +94,9 @@ module ActiveRecord
130
94
  @statements = build_statement_pool
131
95
  @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
132
96
 
133
- if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
134
- @prepared_statements = true
135
- @visitor.extend(DetermineIfPreparableVisitor)
136
- else
137
- @prepared_statements = false
138
- end
97
+ @prepared_statements = self.class.type_cast_config_to_boolean(
98
+ config.fetch(:prepared_statements, true)
99
+ )
139
100
 
140
101
  @advisory_locks_enabled = self.class.type_cast_config_to_boolean(
141
102
  config.fetch(:advisory_locks, true)
@@ -146,12 +107,25 @@ module ActiveRecord
146
107
  @config[:replica] || false
147
108
  end
148
109
 
149
- # Determines whether writes are currently being prevents.
110
+ def use_metadata_table?
111
+ @config.fetch(:use_metadata_table, true)
112
+ end
113
+
114
+ # Determines whether writes are currently being prevented.
115
+ #
116
+ # Returns true if the connection is a replica.
117
+ #
118
+ # If the application is using legacy handling, returns
119
+ # true if +connection_handler.prevent_writes+ is set.
150
120
  #
151
- # Returns true if the connection is a replica, or if +prevent_writes+
152
- # is set to true.
121
+ # If the application is using the new connection handling
122
+ # will return true based on +current_preventing_writes+.
153
123
  def preventing_writes?
154
- replica? || ActiveRecord::Base.connection_handler.prevent_writes
124
+ return true if replica?
125
+ return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord::Base.legacy_connection_handling
126
+ return false if connection_klass.nil?
127
+
128
+ connection_klass.current_preventing_writes
155
129
  end
156
130
 
157
131
  def migrations_paths # :nodoc:
@@ -165,12 +139,15 @@ module ActiveRecord
165
139
  def schema_migration # :nodoc:
166
140
  @schema_migration ||= begin
167
141
  conn = self
168
- spec_name = conn.pool.spec.name
169
- name = "#{spec_name}::SchemaMigration"
142
+ spec_name = conn.pool.pool_config.connection_specification_name
143
+
144
+ return ActiveRecord::SchemaMigration if spec_name == "ActiveRecord::Base"
145
+
146
+ schema_migration_name = "#{spec_name}::SchemaMigration"
170
147
 
171
148
  Class.new(ActiveRecord::SchemaMigration) do
172
- define_singleton_method(:name) { name }
173
- define_singleton_method(:to_s) { name }
149
+ define_singleton_method(:name) { schema_migration_name }
150
+ define_singleton_method(:to_s) { schema_migration_name }
174
151
 
175
152
  self.connection_specification_name = spec_name
176
153
  end
@@ -224,6 +201,10 @@ module ActiveRecord
224
201
  @owner = Thread.current
225
202
  end
226
203
 
204
+ def connection_klass # :nodoc:
205
+ @pool.connection_klass
206
+ end
207
+
227
208
  def schema_cache
228
209
  @pool.get_schema_cache(self)
229
210
  end
@@ -363,12 +344,10 @@ module ActiveRecord
363
344
  false
364
345
  end
365
346
 
366
- # Does this adapter support creating foreign key constraints
367
- # in the same statement as creating the table?
368
- def supports_foreign_keys_in_create?
369
- supports_foreign_keys?
347
+ # Does this adapter support creating check constraints?
348
+ def supports_check_constraints?
349
+ false
370
350
  end
371
- deprecate :supports_foreign_keys_in_create?
372
351
 
373
352
  # Does this adapter support views?
374
353
  def supports_views?
@@ -400,12 +379,6 @@ module ActiveRecord
400
379
  false
401
380
  end
402
381
 
403
- # Does this adapter support multi-value insert?
404
- def supports_multi_insert?
405
- true
406
- end
407
- deprecate :supports_multi_insert?
408
-
409
382
  # Does this adapter support virtual columns?
410
383
  def supports_virtual_columns?
411
384
  false
@@ -537,6 +510,12 @@ module ActiveRecord
537
510
  # this should be overridden by concrete adapters
538
511
  end
539
512
 
513
+ # Removes the connection from the pool and disconnect it.
514
+ def throw_away!
515
+ pool.remove self
516
+ disconnect!
517
+ end
518
+
540
519
  # Clear any caching the database adapter may be doing.
541
520
  def clear_cache!
542
521
  @lock.synchronize { @statements.clear } if @statements
@@ -565,7 +544,7 @@ module ActiveRecord
565
544
  @connection
566
545
  end
567
546
 
568
- def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
547
+ def default_uniqueness_comparison(attribute, value) # :nodoc:
569
548
  attribute.eq(value)
570
549
  end
571
550
 
@@ -593,10 +572,6 @@ module ActiveRecord
593
572
  pool.checkin self
594
573
  end
595
574
 
596
- def column_name_for_operation(operation, node) # :nodoc:
597
- visitor.compile(node)
598
- end
599
-
600
575
  def default_index_type?(index) # :nodoc:
601
576
  index.using.nil?
602
577
  end
@@ -716,7 +691,6 @@ module ActiveRecord
716
691
  binds: binds,
717
692
  type_casted_binds: type_casted_binds,
718
693
  statement_name: statement_name,
719
- connection_id: object_id,
720
694
  connection: self) do
721
695
  @lock.synchronize do
722
696
  yield
@@ -771,6 +745,14 @@ module ActiveRecord
771
745
 
772
746
  def build_statement_pool
773
747
  end
748
+
749
+ # Builds the result object.
750
+ #
751
+ # This is an internal hook to make possible connection adapters to build
752
+ # custom result objects with connection-specific data.
753
+ def build_result(columns:, rows:, column_types: {})
754
+ ActiveRecord::Result.new(columns, rows, column_types)
755
+ end
774
756
  end
775
757
  end
776
758
  end
@@ -92,6 +92,14 @@ module ActiveRecord
92
92
  true
93
93
  end
94
94
 
95
+ def supports_check_constraints?
96
+ if mariadb?
97
+ database_version >= "10.2.1"
98
+ else
99
+ database_version >= "8.0.16"
100
+ end
101
+ end
102
+
95
103
  def supports_views?
96
104
  true
97
105
  end
@@ -104,7 +112,7 @@ module ActiveRecord
104
112
  mariadb? || database_version >= "5.7.5"
105
113
  end
106
114
 
107
- # See https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html for more details.
115
+ # See https://dev.mysql.com/doc/refman/en/optimizer-hints.html for more details.
108
116
  def supports_optimizer_hints?
109
117
  !mariadb? && database_version >= "5.7.7"
110
118
  end
@@ -142,7 +150,12 @@ module ActiveRecord
142
150
  end
143
151
 
144
152
  def index_algorithms
145
- { default: +"ALGORITHM = DEFAULT", copy: +"ALGORITHM = COPY", inplace: +"ALGORITHM = INPLACE" }
153
+ {
154
+ default: "ALGORITHM = DEFAULT",
155
+ copy: "ALGORITHM = COPY",
156
+ inplace: "ALGORITHM = INPLACE",
157
+ instant: "ALGORITHM = INSTANT",
158
+ }
146
159
  end
147
160
 
148
161
  # HELPER METHODS ===========================================
@@ -183,18 +196,10 @@ module ActiveRecord
183
196
  # DATABASE STATEMENTS ======================================
184
197
  #++
185
198
 
186
- def explain(arel, binds = [])
187
- sql = "EXPLAIN #{to_sql(arel, binds)}"
188
- start = Concurrent.monotonic_time
189
- result = exec_query(sql, "EXPLAIN", binds)
190
- elapsed = Concurrent.monotonic_time - start
191
-
192
- MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
193
- end
194
-
195
199
  # Executes the SQL statement in the context of this connection.
196
200
  def execute(sql, name = nil)
197
201
  materialize_transactions
202
+ mark_transaction_written_if_write(sql)
198
203
 
199
204
  log(sql, name) do
200
205
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -211,7 +216,7 @@ module ActiveRecord
211
216
  end
212
217
 
213
218
  def begin_db_transaction
214
- execute "BEGIN"
219
+ execute("BEGIN", "TRANSACTION")
215
220
  end
216
221
 
217
222
  def begin_isolated_db_transaction(isolation)
@@ -220,11 +225,11 @@ module ActiveRecord
220
225
  end
221
226
 
222
227
  def commit_db_transaction #:nodoc:
223
- execute "COMMIT"
228
+ execute("COMMIT", "TRANSACTION")
224
229
  end
225
230
 
226
231
  def exec_rollback_db_transaction #:nodoc:
227
- execute "ROLLBACK"
232
+ execute("ROLLBACK", "TRANSACTION")
228
233
  end
229
234
 
230
235
  def empty_insert_statement_value(primary_key = nil)
@@ -305,6 +310,8 @@ module ActiveRecord
305
310
  # Example:
306
311
  # rename_table('octopuses', 'octopi')
307
312
  def rename_table(table_name, new_name)
313
+ schema_cache.clear_data_source_cache!(table_name.to_s)
314
+ schema_cache.clear_data_source_cache!(new_name.to_s)
308
315
  execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
309
316
  rename_table_indexes(table_name, new_name)
310
317
  end
@@ -324,7 +331,8 @@ module ActiveRecord
324
331
  # Although this command ignores most +options+ and the block if one is given,
325
332
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
326
333
  # In that case, +options+ and the block will be used by create_table.
327
- def drop_table(table_name, options = {})
334
+ def drop_table(table_name, **options)
335
+ schema_cache.clear_data_source_cache!(table_name.to_s)
328
336
  execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
329
337
  end
330
338
 
@@ -356,8 +364,8 @@ module ActiveRecord
356
364
  change_column table_name, column_name, nil, comment: comment
357
365
  end
358
366
 
359
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
360
- execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, options)}")
367
+ def change_column(table_name, column_name, type, **options) #:nodoc:
368
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, **options)}")
361
369
  end
362
370
 
363
371
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
@@ -365,10 +373,13 @@ module ActiveRecord
365
373
  rename_column_indexes(table_name, column_name, new_column_name)
366
374
  end
367
375
 
368
- def add_index(table_name, column_name, options = {}) #:nodoc:
369
- index_name, index_type, index_columns, _, index_algorithm, index_using, comment = add_index_options(table_name, column_name, **options)
370
- sql = +"CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
371
- execute add_sql_comment!(sql, comment)
376
+ def add_index(table_name, column_name, **options) #:nodoc:
377
+ index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
378
+
379
+ return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
380
+
381
+ create_index = CreateIndexDefinition.new(index, algorithm)
382
+ execute schema_creation.accept(create_index)
372
383
  end
373
384
 
374
385
  def add_sql_comment!(sql, comment) # :nodoc:
@@ -412,25 +423,60 @@ module ActiveRecord
412
423
  end
413
424
  end
414
425
 
415
- def table_options(table_name) # :nodoc:
416
- table_options = {}
426
+ def check_constraints(table_name)
427
+ if supports_check_constraints?
428
+ scope = quoted_scope(table_name)
429
+
430
+ chk_info = exec_query(<<~SQL, "SCHEMA")
431
+ SELECT cc.constraint_name AS 'name',
432
+ cc.check_clause AS 'expression'
433
+ FROM information_schema.check_constraints cc
434
+ JOIN information_schema.table_constraints tc
435
+ USING (constraint_schema, constraint_name)
436
+ WHERE tc.table_schema = #{scope[:schema]}
437
+ AND tc.table_name = #{scope[:name]}
438
+ AND cc.constraint_schema = #{scope[:schema]}
439
+ SQL
440
+
441
+ chk_info.map do |row|
442
+ options = {
443
+ name: row["name"]
444
+ }
445
+ expression = row["expression"]
446
+ expression = expression[1..-2] unless mariadb? # remove parentheses added by mysql
447
+ CheckConstraintDefinition.new(table_name, expression, options)
448
+ end
449
+ else
450
+ raise NotImplementedError
451
+ end
452
+ end
417
453
 
454
+ def table_options(table_name) # :nodoc:
418
455
  create_table_info = create_table_info(table_name)
419
456
 
420
457
  # strip create_definitions and partition_options
421
458
  # Be aware that `create_table_info` might not include any table options due to `NO_TABLE_OPTIONS` sql mode.
422
459
  raw_table_options = create_table_info.sub(/\A.*\n\) ?/m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
423
460
 
461
+ return if raw_table_options.empty?
462
+
463
+ table_options = {}
464
+
465
+ if / DEFAULT CHARSET=(?<charset>\w+)(?: COLLATE=(?<collation>\w+))?/ =~ raw_table_options
466
+ raw_table_options = $` + $' # before part + after part
467
+ table_options[:charset] = charset
468
+ table_options[:collation] = collation if collation
469
+ end
470
+
424
471
  # strip AUTO_INCREMENT
425
472
  raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
426
473
 
427
- table_options[:options] = raw_table_options unless raw_table_options.blank?
428
-
429
474
  # strip COMMENT
430
475
  if raw_table_options.sub!(/ COMMENT='.+'/, "")
431
476
  table_options[:comment] = table_comment(table_name)
432
477
  end
433
478
 
479
+ table_options[:options] = raw_table_options unless raw_table_options == "ENGINE=InnoDB"
434
480
  table_options
435
481
  end
436
482
 
@@ -456,21 +502,6 @@ module ActiveRecord
456
502
  SQL
457
503
  end
458
504
 
459
- def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
460
- column = column_for_attribute(attribute)
461
-
462
- if column.collation && !column.case_sensitive? && !value.nil?
463
- ActiveSupport::Deprecation.warn(<<~MSG.squish)
464
- Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1.
465
- To continue case sensitive comparison on the :#{attribute.name} attribute in #{klass} model,
466
- pass `case_sensitive: true` option explicitly to the uniqueness validator.
467
- MSG
468
- attribute.eq(Arel::Nodes::Bin.new(value))
469
- else
470
- super
471
- end
472
- end
473
-
474
505
  def case_sensitive_comparison(attribute, value) # :nodoc:
475
506
  column = column_for_attribute(attribute)
476
507
 
@@ -489,14 +520,14 @@ module ActiveRecord
489
520
  # In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
490
521
  # DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
491
522
  # distinct queries, and requires that the ORDER BY include the distinct column.
492
- # See https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
523
+ # See https://dev.mysql.com/doc/refman/en/group-by-handling.html
493
524
  def columns_for_distinct(columns, orders) # :nodoc:
494
- order_columns = orders.reject(&:blank?).map { |s|
525
+ order_columns = orders.compact_blank.map { |s|
495
526
  # Convert Arel node to string
496
527
  s = visitor.compile(s) unless s.is_a?(String)
497
528
  # Remove any ASC/DESC modifiers
498
529
  s.gsub(/\s+(?:ASC|DESC)\b/i, "")
499
- }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
530
+ }.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
500
531
 
501
532
  (order_columns << super).join(", ")
502
533
  end
@@ -517,6 +548,7 @@ module ActiveRecord
517
548
  sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
518
549
  elsif insert.update_duplicates?
519
550
  sql << " ON DUPLICATE KEY UPDATE "
551
+ sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
520
552
  sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
521
553
  end
522
554
 
@@ -533,7 +565,10 @@ module ActiveRecord
533
565
  def initialize_type_map(m = type_map)
534
566
  super
535
567
 
536
- register_class_with_limit m, %r(char)i, MysqlString
568
+ m.register_type(%r(char)i) do |sql_type|
569
+ limit = extract_limit(sql_type)
570
+ Type.lookup(:string, adapter: :mysql2, limit: limit)
571
+ end
537
572
 
538
573
  m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
539
574
  m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
@@ -553,20 +588,11 @@ module ActiveRecord
553
588
  register_integer_type m, %r(^tinyint)i, limit: 1
554
589
 
555
590
  m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
556
- m.alias_type %r(year)i, "integer"
557
- m.alias_type %r(bit)i, "binary"
591
+ m.alias_type %r(year)i, "integer"
592
+ m.alias_type %r(bit)i, "binary"
558
593
 
559
- m.register_type(%r(enum)i) do |sql_type|
560
- limit = sql_type[/^enum\s*\((.+)\)/i, 1]
561
- .split(",").map { |enum| enum.strip.length - 2 }.max
562
- MysqlString.new(limit: limit)
563
- end
564
-
565
- m.register_type(%r(^set)i) do |sql_type|
566
- limit = sql_type[/^set\s*\((.+)\)/i, 1]
567
- .split(",").map { |set| set.strip.length - 1 }.sum - 1
568
- MysqlString.new(limit: limit)
569
- end
594
+ m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
595
+ m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
570
596
  end
571
597
 
572
598
  def register_integer_type(mapping, key, **options)
@@ -587,7 +613,8 @@ module ActiveRecord
587
613
  end
588
614
  end
589
615
 
590
- # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
616
+ # See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
617
+ ER_DB_CREATE_EXISTS = 1007
591
618
  ER_FILSORT_ABORT = 1028
592
619
  ER_DUP_ENTRY = 1062
593
620
  ER_NOT_NULL_VIOLATION = 1048
@@ -608,6 +635,14 @@ module ActiveRecord
608
635
 
609
636
  def translate_exception(exception, message:, sql:, binds:)
610
637
  case error_number(exception)
638
+ when nil
639
+ if exception.message.match?(/MySQL client is not connected/i)
640
+ ConnectionNotEstablished.new(exception)
641
+ else
642
+ super
643
+ end
644
+ when ER_DB_CREATE_EXISTS
645
+ DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
611
646
  when ER_DUP_ENTRY
612
647
  RecordNotUnique.new(message, sql: sql, binds: binds)
613
648
  when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
@@ -639,7 +674,7 @@ module ActiveRecord
639
674
  end
640
675
  end
641
676
 
642
- def change_column_for_alter(table_name, column_name, type, options = {})
677
+ def change_column_for_alter(table_name, column_name, type, **options)
643
678
  column = column_for(table_name, column_name)
644
679
  type ||= column.sql_type
645
680
 
@@ -661,11 +696,14 @@ module ActiveRecord
661
696
  end
662
697
 
663
698
  def rename_column_for_alter(table_name, column_name, new_column_name)
699
+ return rename_column_sql(table_name, column_name, new_column_name) if supports_rename_column?
700
+
664
701
  column = column_for(table_name, column_name)
665
702
  options = {
666
703
  default: column.default,
667
704
  null: column.null,
668
- auto_increment: column.auto_increment?
705
+ auto_increment: column.auto_increment?,
706
+ comment: column.comment
669
707
  }
670
708
 
671
709
  current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
@@ -674,19 +712,32 @@ module ActiveRecord
674
712
  schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
675
713
  end
676
714
 
677
- def add_index_for_alter(table_name, column_name, options = {})
678
- index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, **options)
679
- index_algorithm[0, 0] = ", " if index_algorithm.present?
680
- "ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
715
+ def add_index_for_alter(table_name, column_name, **options)
716
+ index, algorithm, _ = add_index_options(table_name, column_name, **options)
717
+ algorithm = ", #{algorithm}" if algorithm
718
+
719
+ "ADD #{schema_creation.accept(index)}#{algorithm}"
681
720
  end
682
721
 
683
- def remove_index_for_alter(table_name, options = {})
684
- index_name = index_name_for_remove(table_name, options)
722
+ def remove_index_for_alter(table_name, column_name = nil, **options)
723
+ index_name = index_name_for_remove(table_name, column_name, options)
685
724
  "DROP INDEX #{quote_column_name(index_name)}"
686
725
  end
687
726
 
688
727
  def supports_rename_index?
689
- mariadb? ? false : database_version >= "5.7.6"
728
+ if mariadb?
729
+ database_version >= "10.5.2"
730
+ else
731
+ database_version >= "5.7.6"
732
+ end
733
+ end
734
+
735
+ def supports_rename_column?
736
+ if mariadb?
737
+ database_version >= "10.5.2"
738
+ else
739
+ database_version >= "8.0.3"
740
+ end
690
741
  end
691
742
 
692
743
  def configure_connection
@@ -703,7 +754,7 @@ module ActiveRecord
703
754
  defaults = [":default", :default].to_set
704
755
 
705
756
  # Make MySQL reject illegal values rather than truncating or blanking them, see
706
- # https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_strict_all_tables
757
+ # https://dev.mysql.com/doc/refman/en/sql-mode.html#sqlmode_strict_all_tables
707
758
  # If the user has provided another value for sql_mode, don't replace it.
708
759
  if sql_mode = variables.delete("sql_mode")
709
760
  sql_mode = quote(sql_mode)
@@ -720,7 +771,7 @@ module ActiveRecord
720
771
  sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
721
772
 
722
773
  # NAMES does not have an equals sign, see
723
- # https://dev.mysql.com/doc/refman/5.7/en/set-names.html
774
+ # https://dev.mysql.com/doc/refman/en/set-names.html
724
775
  # (trailing comma because variable_assignments will always have content)
725
776
  if @config[:encoding]
726
777
  encoding = +"NAMES #{@config[:encoding]}"
@@ -739,7 +790,7 @@ module ActiveRecord
739
790
  end.compact.join(", ")
740
791
 
741
792
  # ...and send them all in one query
742
- execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
793
+ execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
743
794
  end
744
795
 
745
796
  def column_definitions(table_name) # :nodoc:
@@ -788,26 +839,16 @@ module ActiveRecord
788
839
  full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
789
840
  end
790
841
 
791
- class MysqlString < Type::String # :nodoc:
792
- def serialize(value)
793
- case value
794
- when true then "1"
795
- when false then "0"
796
- else super
797
- end
798
- end
842
+ # Alias MysqlString to work Mashal.load(File.read("legacy_record.dump")).
843
+ # TODO: Remove the constant alias once Rails 6.1 has released.
844
+ MysqlString = Type::String # :nodoc:
799
845
 
800
- private
801
- def cast_value(value)
802
- case value
803
- when true then "1"
804
- when false then "0"
805
- else super
806
- end
807
- end
846
+ ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
847
+ Type::ImmutableString.new(true: "1", false: "0", **args)
848
+ end
849
+ ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
850
+ Type::String.new(true: "1", false: "0", **args)
808
851
  end
809
-
810
- ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
811
852
  ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
812
853
  end
813
854
  end