activerecord 6.0.5 → 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 +764 -888
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +22 -14
  7. data/lib/active_record/associations/alias_tracker.rb +19 -15
  8. data/lib/active_record/associations/association.rb +39 -27
  9. data/lib/active_record/associations/association_scope.rb +11 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  12. data/lib/active_record/associations/builder/association.rb +9 -3
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +19 -13
  20. data/lib/active_record/associations/collection_proxy.rb +12 -5
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -2
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +29 -14
  26. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +63 -49
  28. data/lib/active_record/associations/preloader/association.rb +13 -5
  29. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  30. data/lib/active_record/associations/preloader.rb +5 -3
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations.rb +114 -11
  33. data/lib/active_record/attribute_assignment.rb +10 -8
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  35. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  36. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  37. data/lib/active_record/attribute_methods/query.rb +3 -6
  38. data/lib/active_record/attribute_methods/read.rb +8 -11
  39. data/lib/active_record/attribute_methods/serialization.rb +4 -4
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  41. data/lib/active_record/attribute_methods/write.rb +12 -20
  42. data/lib/active_record/attribute_methods.rb +52 -48
  43. data/lib/active_record/attributes.rb +27 -7
  44. data/lib/active_record/autosave_association.rb +47 -30
  45. data/lib/active_record/base.rb +2 -14
  46. data/lib/active_record/callbacks.rb +32 -22
  47. data/lib/active_record/coders/yaml_column.rb +1 -1
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +180 -134
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  53. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +110 -30
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -24
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +31 -70
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
  61. data/lib/active_record/connection_adapters/column.rb +15 -1
  62. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  63. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -24
  65. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +33 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  69. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -3
  71. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  73. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  74. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  75. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +12 -53
  77. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  78. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -10
  80. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  88. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  91. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
  93. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  94. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  95. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +30 -5
  96. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  97. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  98. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
  99. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
  100. data/lib/active_record/connection_adapters.rb +50 -0
  101. data/lib/active_record/connection_handling.rb +210 -71
  102. data/lib/active_record/core.rb +215 -49
  103. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  104. data/lib/active_record/database_configurations/database_config.rb +52 -9
  105. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  106. data/lib/active_record/database_configurations/url_config.rb +15 -40
  107. data/lib/active_record/database_configurations.rb +124 -85
  108. data/lib/active_record/delegated_type.rb +209 -0
  109. data/lib/active_record/destroy_association_async_job.rb +36 -0
  110. data/lib/active_record/enum.rb +33 -23
  111. data/lib/active_record/errors.rb +47 -12
  112. data/lib/active_record/explain.rb +9 -4
  113. data/lib/active_record/explain_subscriber.rb +1 -1
  114. data/lib/active_record/fixture_set/file.rb +10 -17
  115. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  116. data/lib/active_record/fixture_set/render_context.rb +1 -1
  117. data/lib/active_record/fixture_set/table_row.rb +2 -2
  118. data/lib/active_record/fixtures.rb +54 -8
  119. data/lib/active_record/gem_version.rb +3 -3
  120. data/lib/active_record/inheritance.rb +40 -18
  121. data/lib/active_record/insert_all.rb +32 -5
  122. data/lib/active_record/integration.rb +3 -5
  123. data/lib/active_record/internal_metadata.rb +15 -4
  124. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  125. data/lib/active_record/locking/optimistic.rb +13 -16
  126. data/lib/active_record/locking/pessimistic.rb +6 -2
  127. data/lib/active_record/log_subscriber.rb +26 -8
  128. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  129. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  130. data/lib/active_record/middleware/database_selector.rb +4 -1
  131. data/lib/active_record/migration/command_recorder.rb +47 -27
  132. data/lib/active_record/migration/compatibility.rb +67 -17
  133. data/lib/active_record/migration.rb +113 -83
  134. data/lib/active_record/model_schema.rb +88 -42
  135. data/lib/active_record/nested_attributes.rb +2 -3
  136. data/lib/active_record/no_touching.rb +1 -1
  137. data/lib/active_record/persistence.rb +50 -45
  138. data/lib/active_record/query_cache.rb +15 -5
  139. data/lib/active_record/querying.rb +11 -6
  140. data/lib/active_record/railtie.rb +64 -44
  141. data/lib/active_record/railties/databases.rake +253 -98
  142. data/lib/active_record/readonly_attributes.rb +4 -0
  143. data/lib/active_record/reflection.rb +59 -44
  144. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  145. data/lib/active_record/relation/batches.rb +38 -31
  146. data/lib/active_record/relation/calculations.rb +100 -43
  147. data/lib/active_record/relation/finder_methods.rb +44 -14
  148. data/lib/active_record/relation/from_clause.rb +1 -1
  149. data/lib/active_record/relation/merger.rb +20 -23
  150. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  151. data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -2
  152. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  153. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  154. data/lib/active_record/relation/predicate_builder.rb +57 -33
  155. data/lib/active_record/relation/query_methods.rb +319 -196
  156. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  157. data/lib/active_record/relation/spawn_methods.rb +6 -5
  158. data/lib/active_record/relation/where_clause.rb +104 -57
  159. data/lib/active_record/relation.rb +90 -64
  160. data/lib/active_record/result.rb +41 -33
  161. data/lib/active_record/runtime_registry.rb +2 -2
  162. data/lib/active_record/sanitization.rb +6 -17
  163. data/lib/active_record/schema_dumper.rb +34 -4
  164. data/lib/active_record/schema_migration.rb +0 -4
  165. data/lib/active_record/scoping/named.rb +1 -17
  166. data/lib/active_record/secure_token.rb +16 -8
  167. data/lib/active_record/serialization.rb +5 -3
  168. data/lib/active_record/signed_id.rb +116 -0
  169. data/lib/active_record/statement_cache.rb +20 -4
  170. data/lib/active_record/store.rb +2 -2
  171. data/lib/active_record/suppressor.rb +2 -2
  172. data/lib/active_record/table_metadata.rb +36 -52
  173. data/lib/active_record/tasks/database_tasks.rb +139 -113
  174. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  175. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  176. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  177. data/lib/active_record/test_databases.rb +5 -4
  178. data/lib/active_record/test_fixtures.rb +36 -33
  179. data/lib/active_record/timestamp.rb +4 -6
  180. data/lib/active_record/touch_later.rb +21 -21
  181. data/lib/active_record/transactions.rb +15 -64
  182. data/lib/active_record/type/serialized.rb +6 -2
  183. data/lib/active_record/type.rb +8 -1
  184. data/lib/active_record/type_caster/connection.rb +0 -1
  185. data/lib/active_record/type_caster/map.rb +8 -5
  186. data/lib/active_record/validations/associated.rb +1 -1
  187. data/lib/active_record/validations/numericality.rb +35 -0
  188. data/lib/active_record/validations/uniqueness.rb +24 -4
  189. data/lib/active_record/validations.rb +1 -0
  190. data/lib/active_record.rb +7 -14
  191. data/lib/arel/attributes/attribute.rb +4 -0
  192. data/lib/arel/collectors/bind.rb +5 -0
  193. data/lib/arel/collectors/composite.rb +8 -0
  194. data/lib/arel/collectors/sql_string.rb +7 -0
  195. data/lib/arel/collectors/substitute_binds.rb +7 -0
  196. data/lib/arel/nodes/binary.rb +82 -8
  197. data/lib/arel/nodes/bind_param.rb +8 -0
  198. data/lib/arel/nodes/casted.rb +21 -9
  199. data/lib/arel/nodes/equality.rb +6 -9
  200. data/lib/arel/nodes/grouping.rb +3 -0
  201. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  202. data/lib/arel/nodes/in.rb +8 -1
  203. data/lib/arel/nodes/infix_operation.rb +13 -1
  204. data/lib/arel/nodes/join_source.rb +1 -1
  205. data/lib/arel/nodes/node.rb +7 -6
  206. data/lib/arel/nodes/ordering.rb +27 -0
  207. data/lib/arel/nodes/sql_literal.rb +3 -0
  208. data/lib/arel/nodes/table_alias.rb +7 -3
  209. data/lib/arel/nodes/unary.rb +0 -1
  210. data/lib/arel/nodes.rb +3 -1
  211. data/lib/arel/predications.rb +12 -18
  212. data/lib/arel/select_manager.rb +1 -2
  213. data/lib/arel/table.rb +13 -5
  214. data/lib/arel/visitors/dot.rb +14 -2
  215. data/lib/arel/visitors/mysql.rb +11 -1
  216. data/lib/arel/visitors/postgresql.rb +15 -4
  217. data/lib/arel/visitors/to_sql.rb +89 -78
  218. data/lib/arel/visitors.rb +0 -7
  219. data/lib/arel.rb +5 -13
  220. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  221. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  222. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  223. data/lib/rails/generators/active_record/migration.rb +6 -1
  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 +30 -32
  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
@@ -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
@@ -5,6 +5,8 @@ module ActiveRecord
5
5
  module ConnectionAdapters
6
6
  # An abstract definition of a column in a table.
7
7
  class Column
8
+ include Deduplicable
9
+
8
10
  attr_reader :name, :default, :sql_type_metadata, :null, :default_function, :collation, :comment
9
11
 
10
12
  delegate :precision, :scale, :limit, :type, :sql_type, to: :sql_type_metadata, allow_nil: true
@@ -76,6 +78,7 @@ module ActiveRecord
76
78
  def hash
77
79
  Column.hash ^
78
80
  name.hash ^
81
+ name.encoding.hash ^
79
82
  default.hash ^
80
83
  sql_type_metadata.hash ^
81
84
  null.hash ^
@@ -83,10 +86,21 @@ module ActiveRecord
83
86
  collation.hash ^
84
87
  comment.hash
85
88
  end
89
+
90
+ private
91
+ def deduplicated
92
+ @name = -name
93
+ @sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata
94
+ @default = -default if default
95
+ @default_function = -default_function if default_function
96
+ @collation = -collation if collation
97
+ @comment = -comment if comment
98
+ super
99
+ end
86
100
  end
87
101
 
88
102
  class NullColumn < Column
89
- def initialize(name)
103
+ def initialize(name, **)
90
104
  super(name, nil)
91
105
  end
92
106
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters # :nodoc:
5
+ module Deduplicable
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ def registry
10
+ @registry ||= {}
11
+ end
12
+
13
+ def new(*, **)
14
+ super.deduplicate
15
+ end
16
+ end
17
+
18
+ def deduplicate
19
+ self.class.registry[self] ||= deduplicated
20
+ end
21
+ alias :-@ :deduplicate
22
+
23
+ private
24
+ def deduplicated
25
+ freeze
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ class LegacyPoolManager # :nodoc:
6
+ def initialize
7
+ @name_to_pool_config = {}
8
+ end
9
+
10
+ def shard_names
11
+ @name_to_pool_config.keys
12
+ end
13
+
14
+ def pool_configs(_ = nil)
15
+ @name_to_pool_config.values
16
+ end
17
+
18
+ def remove_pool_config(_, shard)
19
+ @name_to_pool_config.delete(shard)
20
+ end
21
+
22
+ def get_pool_config(_, shard)
23
+ @name_to_pool_config[shard]
24
+ end
25
+
26
+ def set_pool_config(_, shard, pool_config)
27
+ @name_to_pool_config[shard] = pool_config
28
+ end
29
+ end
30
+ end
31
+ end
@@ -28,6 +28,15 @@ module ActiveRecord
28
28
  !READ_QUERY.match?(sql)
29
29
  end
30
30
 
31
+ def explain(arel, binds = [])
32
+ sql = "EXPLAIN #{to_sql(arel, binds)}"
33
+ start = Concurrent.monotonic_time
34
+ result = exec_query(sql, "EXPLAIN", binds)
35
+ elapsed = Concurrent.monotonic_time - start
36
+
37
+ MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
38
+ end
39
+
31
40
  # Executes the SQL statement in the context of this connection.
32
41
  def execute(sql, name = nil)
33
42
  if preventing_writes? && write_query?(sql)
@@ -45,17 +54,17 @@ module ActiveRecord
45
54
  if without_prepared_statement?(binds)
46
55
  execute_and_free(sql, name) do |result|
47
56
  if result
48
- ActiveRecord::Result.new(result.fields, result.to_a)
57
+ build_result(columns: result.fields, rows: result.to_a)
49
58
  else
50
- ActiveRecord::Result.new([], [])
59
+ build_result(columns: [], rows: [])
51
60
  end
52
61
  end
53
62
  else
54
63
  exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
55
64
  if result
56
- ActiveRecord::Result.new(result.fields, result.to_a)
65
+ build_result(columns: result.fields, rows: result.to_a)
57
66
  else
58
- ActiveRecord::Result.new([], [])
67
+ build_result(columns: [], rows: [])
59
68
  end
60
69
  end
61
70
  end
@@ -88,39 +97,27 @@ module ActiveRecord
88
97
  @connection.last_id
89
98
  end
90
99
 
91
- def supports_set_server_option?
92
- @connection.respond_to?(:set_server_option)
93
- end
100
+ def multi_statements_enabled?
101
+ flags = @config[:flags]
94
102
 
95
- def multi_statements_enabled?(flags)
96
103
  if flags.is_a?(Array)
97
104
  flags.include?("MULTI_STATEMENTS")
98
105
  else
99
- (flags & Mysql2::Client::MULTI_STATEMENTS) != 0
106
+ flags.anybits?(Mysql2::Client::MULTI_STATEMENTS)
100
107
  end
101
108
  end
102
109
 
103
110
  def with_multi_statements
104
- previous_flags = @config[:flags]
111
+ multi_statements_was = multi_statements_enabled?
105
112
 
106
- unless multi_statements_enabled?(previous_flags)
107
- if supports_set_server_option?
108
- @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
109
- else
110
- @config[:flags] = Mysql2::Client::MULTI_STATEMENTS
111
- reconnect!
112
- end
113
+ unless multi_statements_was
114
+ @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
113
115
  end
114
116
 
115
117
  yield
116
118
  ensure
117
- unless multi_statements_enabled?(previous_flags)
118
- if supports_set_server_option?
119
- @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
120
- else
121
- @config[:flags] = previous_flags
122
- reconnect!
123
- end
119
+ unless multi_statements_was
120
+ @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
124
121
  end
125
122
  end
126
123
 
@@ -157,6 +154,7 @@ module ActiveRecord
157
154
  end
158
155
 
159
156
  materialize_transactions
157
+ mark_transaction_written_if_write(sql)
160
158
 
161
159
  # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
162
160
  # made since we established the connection
@@ -56,7 +56,7 @@ module ActiveRecord
56
56
  items.each_with_index do |item, i|
57
57
  item = "NULL" if item.nil?
58
58
  justifier = item.is_a?(Numeric) ? "rjust" : "ljust"
59
- cells << item.to_s.send(justifier, widths[i])
59
+ cells << item.to_s.public_send(justifier, widths[i])
60
60
  end
61
61
  "| " + cells.join(" | ") + " |"
62
62
  end
@@ -47,7 +47,7 @@ module ActiveRecord
47
47
  # `table_name`.`column_name` | function(one or no argument)
48
48
  ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
49
49
  )
50
- (?:\s+AS\s+(?:\w+|`\w+`))?
50
+ (?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
51
51
  )
52
52
  (?:\s*,\s*\g<1>)*
53
53
  \z