activerecord 6.0.4 → 6.1.0.rc1

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 (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +767 -846
  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 +1 -1
  7. data/lib/active_record/association_relation.rb +22 -14
  8. data/lib/active_record/associations.rb +114 -11
  9. data/lib/active_record/associations/alias_tracker.rb +19 -15
  10. data/lib/active_record/associations/association.rb +39 -27
  11. data/lib/active_record/associations/association_scope.rb +11 -15
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  14. data/lib/active_record/associations/builder/association.rb +9 -3
  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 -13
  22. data/lib/active_record/associations/collection_proxy.rb +12 -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 +63 -49
  28. data/lib/active_record/associations/join_dependency/join_association.rb +29 -14
  29. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  30. data/lib/active_record/associations/preloader.rb +5 -3
  31. data/lib/active_record/associations/preloader/association.rb +13 -5
  32. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  33. data/lib/active_record/associations/singular_association.rb +1 -1
  34. data/lib/active_record/attribute_assignment.rb +10 -8
  35. data/lib/active_record/attribute_methods.rb +52 -48
  36. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  37. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  38. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  39. data/lib/active_record/attribute_methods/query.rb +3 -6
  40. data/lib/active_record/attribute_methods/read.rb +8 -11
  41. data/lib/active_record/attribute_methods/serialization.rb +4 -4
  42. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  43. data/lib/active_record/attribute_methods/write.rb +12 -20
  44. data/lib/active_record/attributes.rb +27 -7
  45. data/lib/active_record/autosave_association.rb +47 -30
  46. data/lib/active_record/base.rb +2 -14
  47. data/lib/active_record/callbacks.rb +32 -22
  48. data/lib/active_record/coders/yaml_column.rb +1 -1
  49. data/lib/active_record/connection_adapters.rb +50 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +180 -134
  51. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  55. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +110 -30
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -24
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +31 -70
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
  63. data/lib/active_record/connection_adapters/column.rb +15 -1
  64. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  65. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -24
  67. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +33 -6
  70. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  71. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  72. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -3
  73. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  74. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  75. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  76. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +12 -53
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  81. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -10
  83. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  89. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  90. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  91. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  92. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  93. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  94. data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
  95. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  96. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  97. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +30 -5
  98. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  99. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  100. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
  101. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
  102. data/lib/active_record/connection_handling.rb +210 -71
  103. data/lib/active_record/core.rb +215 -49
  104. data/lib/active_record/database_configurations.rb +124 -85
  105. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  106. data/lib/active_record/database_configurations/database_config.rb +52 -9
  107. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  108. data/lib/active_record/database_configurations/url_config.rb +15 -40
  109. data/lib/active_record/delegated_type.rb +209 -0
  110. data/lib/active_record/destroy_association_async_job.rb +36 -0
  111. data/lib/active_record/enum.rb +33 -23
  112. data/lib/active_record/errors.rb +47 -12
  113. data/lib/active_record/explain.rb +9 -4
  114. data/lib/active_record/explain_subscriber.rb +1 -1
  115. data/lib/active_record/fixture_set/file.rb +10 -17
  116. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  117. data/lib/active_record/fixture_set/render_context.rb +1 -1
  118. data/lib/active_record/fixture_set/table_row.rb +2 -2
  119. data/lib/active_record/fixtures.rb +54 -8
  120. data/lib/active_record/gem_version.rb +3 -3
  121. data/lib/active_record/inheritance.rb +40 -18
  122. data/lib/active_record/insert_all.rb +32 -5
  123. data/lib/active_record/integration.rb +3 -5
  124. data/lib/active_record/internal_metadata.rb +15 -4
  125. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  126. data/lib/active_record/locking/optimistic.rb +13 -16
  127. data/lib/active_record/locking/pessimistic.rb +6 -2
  128. data/lib/active_record/log_subscriber.rb +26 -8
  129. data/lib/active_record/middleware/database_selector.rb +4 -1
  130. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  131. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  132. data/lib/active_record/migration.rb +113 -83
  133. data/lib/active_record/migration/command_recorder.rb +47 -27
  134. data/lib/active_record/migration/compatibility.rb +67 -17
  135. data/lib/active_record/model_schema.rb +88 -42
  136. data/lib/active_record/nested_attributes.rb +2 -3
  137. data/lib/active_record/no_touching.rb +1 -1
  138. data/lib/active_record/persistence.rb +50 -45
  139. data/lib/active_record/query_cache.rb +15 -5
  140. data/lib/active_record/querying.rb +11 -6
  141. data/lib/active_record/railtie.rb +64 -44
  142. data/lib/active_record/railties/databases.rake +253 -98
  143. data/lib/active_record/readonly_attributes.rb +4 -0
  144. data/lib/active_record/reflection.rb +59 -44
  145. data/lib/active_record/relation.rb +90 -64
  146. data/lib/active_record/relation/batches.rb +38 -31
  147. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  148. data/lib/active_record/relation/calculations.rb +100 -43
  149. data/lib/active_record/relation/finder_methods.rb +44 -14
  150. data/lib/active_record/relation/from_clause.rb +1 -1
  151. data/lib/active_record/relation/merger.rb +20 -23
  152. data/lib/active_record/relation/predicate_builder.rb +57 -33
  153. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  154. data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -2
  155. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  156. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  157. data/lib/active_record/relation/query_methods.rb +319 -196
  158. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  159. data/lib/active_record/relation/spawn_methods.rb +6 -5
  160. data/lib/active_record/relation/where_clause.rb +104 -57
  161. data/lib/active_record/result.rb +41 -33
  162. data/lib/active_record/runtime_registry.rb +2 -2
  163. data/lib/active_record/sanitization.rb +6 -17
  164. data/lib/active_record/schema_dumper.rb +34 -4
  165. data/lib/active_record/schema_migration.rb +0 -4
  166. data/lib/active_record/scoping/named.rb +1 -17
  167. data/lib/active_record/secure_token.rb +16 -8
  168. data/lib/active_record/serialization.rb +5 -3
  169. data/lib/active_record/signed_id.rb +116 -0
  170. data/lib/active_record/statement_cache.rb +20 -4
  171. data/lib/active_record/store.rb +2 -2
  172. data/lib/active_record/suppressor.rb +2 -2
  173. data/lib/active_record/table_metadata.rb +36 -52
  174. data/lib/active_record/tasks/database_tasks.rb +139 -113
  175. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  176. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  177. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  178. data/lib/active_record/test_databases.rb +5 -4
  179. data/lib/active_record/test_fixtures.rb +36 -33
  180. data/lib/active_record/timestamp.rb +4 -6
  181. data/lib/active_record/touch_later.rb +21 -21
  182. data/lib/active_record/transactions.rb +15 -64
  183. data/lib/active_record/type.rb +8 -1
  184. data/lib/active_record/type/serialized.rb +6 -2
  185. data/lib/active_record/type_caster/connection.rb +0 -1
  186. data/lib/active_record/type_caster/map.rb +8 -5
  187. data/lib/active_record/validations.rb +1 -0
  188. data/lib/active_record/validations/associated.rb +1 -1
  189. data/lib/active_record/validations/numericality.rb +35 -0
  190. data/lib/active_record/validations/uniqueness.rb +24 -4
  191. data/lib/arel.rb +5 -13
  192. data/lib/arel/attributes/attribute.rb +4 -0
  193. data/lib/arel/collectors/bind.rb +5 -0
  194. data/lib/arel/collectors/composite.rb +8 -0
  195. data/lib/arel/collectors/sql_string.rb +7 -0
  196. data/lib/arel/collectors/substitute_binds.rb +7 -0
  197. data/lib/arel/nodes.rb +3 -1
  198. data/lib/arel/nodes/binary.rb +82 -8
  199. data/lib/arel/nodes/bind_param.rb +8 -0
  200. data/lib/arel/nodes/casted.rb +21 -9
  201. data/lib/arel/nodes/equality.rb +6 -9
  202. data/lib/arel/nodes/grouping.rb +3 -0
  203. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  204. data/lib/arel/nodes/in.rb +8 -1
  205. data/lib/arel/nodes/infix_operation.rb +13 -1
  206. data/lib/arel/nodes/join_source.rb +1 -1
  207. data/lib/arel/nodes/node.rb +7 -6
  208. data/lib/arel/nodes/ordering.rb +27 -0
  209. data/lib/arel/nodes/sql_literal.rb +3 -0
  210. data/lib/arel/nodes/table_alias.rb +7 -3
  211. data/lib/arel/nodes/unary.rb +0 -1
  212. data/lib/arel/predications.rb +12 -18
  213. data/lib/arel/select_manager.rb +1 -2
  214. data/lib/arel/table.rb +13 -5
  215. data/lib/arel/visitors.rb +0 -7
  216. data/lib/arel/visitors/dot.rb +14 -2
  217. data/lib/arel/visitors/mysql.rb +11 -1
  218. data/lib/arel/visitors/postgresql.rb +15 -4
  219. data/lib/arel/visitors/to_sql.rb +89 -78
  220. data/lib/rails/generators/active_record/migration.rb +6 -1
  221. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  222. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  223. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  224. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  225. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  226. metadata +27 -28
  227. data/lib/active_record/advisory_lock_base.rb +0 -18
  228. data/lib/active_record/attribute_decorators.rb +0 -88
  229. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  230. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  231. data/lib/active_record/define_callbacks.rb +0 -22
  232. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  233. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  234. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  235. data/lib/arel/attributes.rb +0 -22
  236. data/lib/arel/visitors/depth_first.rb +0 -203
  237. data/lib/arel/visitors/ibm_db.rb +0 -34
  238. data/lib/arel/visitors/informix.rb +0 -62
  239. data/lib/arel/visitors/mssql.rb +0 -156
  240. data/lib/arel/visitors/oracle.rb +0 -158
  241. data/lib/arel/visitors/oracle12.rb +0 -65
  242. 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
@@ -289,6 +321,16 @@ module ActiveRecord
289
321
  if Thread.current.status == "aborting"
290
322
  rollback_transaction
291
323
  else
324
+ if !completed && transaction.written
325
+ ActiveSupport::Deprecation.warn(<<~EOW)
326
+ Using `return`, `break` or `throw` to exit a transaction block is
327
+ deprecated without replacement. If the `throw` came from
328
+ `Timeout.timeout(duration)`, pass an exception class as a second
329
+ argument so it doesn't use `throw` to abort its block. This results
330
+ in the transaction being committed, but in the next release of Rails
331
+ it will rollback.
332
+ EOW
333
+ end
292
334
  begin
293
335
  commit_transaction
294
336
  rescue Exception
@@ -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
@@ -134,12 +94,9 @@ module ActiveRecord
134
94
  @statements = build_statement_pool
135
95
  @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
136
96
 
137
- if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
138
- @prepared_statements = true
139
- @visitor.extend(DetermineIfPreparableVisitor)
140
- else
141
- @prepared_statements = false
142
- end
97
+ @prepared_statements = self.class.type_cast_config_to_boolean(
98
+ config.fetch(:prepared_statements, true)
99
+ )
143
100
 
144
101
  @advisory_locks_enabled = self.class.type_cast_config_to_boolean(
145
102
  config.fetch(:advisory_locks, true)
@@ -150,12 +107,20 @@ module ActiveRecord
150
107
  @config[:replica] || false
151
108
  end
152
109
 
110
+ def use_metadata_table?
111
+ @config.fetch(:use_metadata_table, true)
112
+ end
113
+
153
114
  # Determines whether writes are currently being prevents.
154
115
  #
155
116
  # Returns true if the connection is a replica, or if +prevent_writes+
156
117
  # is set to true.
157
118
  def preventing_writes?
158
- replica? || ActiveRecord::Base.connection_handler.prevent_writes
119
+ if ActiveRecord::Base.legacy_connection_handling
120
+ replica? || ActiveRecord::Base.connection_handler.prevent_writes
121
+ else
122
+ replica? || ActiveRecord::Base.current_preventing_writes
123
+ end
159
124
  end
160
125
 
161
126
  def migrations_paths # :nodoc:
@@ -169,14 +134,15 @@ module ActiveRecord
169
134
  def schema_migration # :nodoc:
170
135
  @schema_migration ||= begin
171
136
  conn = self
172
- spec_name = conn.pool.spec.name
173
- name = "#{spec_name}::SchemaMigration"
137
+ spec_name = conn.pool.pool_config.connection_specification_name
138
+
139
+ return ActiveRecord::SchemaMigration if spec_name == "ActiveRecord::Base"
174
140
 
175
- return ActiveRecord::SchemaMigration if spec_name == "primary"
141
+ schema_migration_name = "#{spec_name}::SchemaMigration"
176
142
 
177
143
  Class.new(ActiveRecord::SchemaMigration) do
178
- define_singleton_method(:name) { name }
179
- define_singleton_method(:to_s) { name }
144
+ define_singleton_method(:name) { schema_migration_name }
145
+ define_singleton_method(:to_s) { schema_migration_name }
180
146
 
181
147
  self.connection_specification_name = spec_name
182
148
  end
@@ -369,12 +335,10 @@ module ActiveRecord
369
335
  false
370
336
  end
371
337
 
372
- # Does this adapter support creating foreign key constraints
373
- # in the same statement as creating the table?
374
- def supports_foreign_keys_in_create?
375
- supports_foreign_keys?
338
+ # Does this adapter support creating check constraints?
339
+ def supports_check_constraints?
340
+ false
376
341
  end
377
- deprecate :supports_foreign_keys_in_create?
378
342
 
379
343
  # Does this adapter support views?
380
344
  def supports_views?
@@ -406,12 +370,6 @@ module ActiveRecord
406
370
  false
407
371
  end
408
372
 
409
- # Does this adapter support multi-value insert?
410
- def supports_multi_insert?
411
- true
412
- end
413
- deprecate :supports_multi_insert?
414
-
415
373
  # Does this adapter support virtual columns?
416
374
  def supports_virtual_columns?
417
375
  false
@@ -571,7 +529,7 @@ module ActiveRecord
571
529
  @connection
572
530
  end
573
531
 
574
- def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
532
+ def default_uniqueness_comparison(attribute, value) # :nodoc:
575
533
  attribute.eq(value)
576
534
  end
577
535
 
@@ -599,10 +557,6 @@ module ActiveRecord
599
557
  pool.checkin self
600
558
  end
601
559
 
602
- def column_name_for_operation(operation, node) # :nodoc:
603
- visitor.compile(node)
604
- end
605
-
606
560
  def default_index_type?(index) # :nodoc:
607
561
  index.using.nil?
608
562
  end
@@ -722,7 +676,6 @@ module ActiveRecord
722
676
  binds: binds,
723
677
  type_casted_binds: type_casted_binds,
724
678
  statement_name: statement_name,
725
- connection_id: object_id,
726
679
  connection: self) do
727
680
  @lock.synchronize do
728
681
  yield
@@ -777,6 +730,14 @@ module ActiveRecord
777
730
 
778
731
  def build_statement_pool
779
732
  end
733
+
734
+ # Builds the result object.
735
+ #
736
+ # This is an internal hook to make possible connection adapters to build
737
+ # custom result objects with connection-specific data.
738
+ def build_result(columns:, rows:, column_types: {})
739
+ ActiveRecord::Result.new(columns, rows, column_types)
740
+ end
780
741
  end
781
742
  end
782
743
  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,15 +712,15 @@ 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, comment = add_index_options(table_name, column_name, **options)
679
- index_algorithm[0, 0] = ", " if index_algorithm.present?
680
- sql = +"ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
681
- add_sql_comment!(sql, comment)
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}"
682
720
  end
683
721
 
684
- def remove_index_for_alter(table_name, options = {})
685
- 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)
686
724
  "DROP INDEX #{quote_column_name(index_name)}"
687
725
  end
688
726
 
@@ -694,6 +732,14 @@ module ActiveRecord
694
732
  end
695
733
  end
696
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
741
+ end
742
+
697
743
  def configure_connection
698
744
  variables = @config.fetch(:variables, {}).stringify_keys
699
745
 
@@ -708,7 +754,7 @@ module ActiveRecord
708
754
  defaults = [":default", :default].to_set
709
755
 
710
756
  # Make MySQL reject illegal values rather than truncating or blanking them, see
711
- # 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
712
758
  # If the user has provided another value for sql_mode, don't replace it.
713
759
  if sql_mode = variables.delete("sql_mode")
714
760
  sql_mode = quote(sql_mode)
@@ -725,7 +771,7 @@ module ActiveRecord
725
771
  sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
726
772
 
727
773
  # NAMES does not have an equals sign, see
728
- # https://dev.mysql.com/doc/refman/5.7/en/set-names.html
774
+ # https://dev.mysql.com/doc/refman/en/set-names.html
729
775
  # (trailing comma because variable_assignments will always have content)
730
776
  if @config[:encoding]
731
777
  encoding = +"NAMES #{@config[:encoding]}"
@@ -793,26 +839,16 @@ module ActiveRecord
793
839
  full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
794
840
  end
795
841
 
796
- class MysqlString < Type::String # :nodoc:
797
- def serialize(value)
798
- case value
799
- when true then "1"
800
- when false then "0"
801
- else super
802
- end
803
- 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:
804
845
 
805
- private
806
- def cast_value(value)
807
- case value
808
- when true then "1"
809
- when false then "0"
810
- else super
811
- end
812
- 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)
813
851
  end
814
-
815
- ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
816
852
  ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
817
853
  end
818
854
  end