activerecord 6.0.0 → 6.1.0

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

Potentially problematic release.


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

Files changed (268) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +872 -582
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record.rb +7 -13
  6. data/lib/active_record/aggregations.rb +1 -2
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations.rb +116 -13
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +49 -29
  11. data/lib/active_record/associations/association_scope.rb +17 -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 -3
  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 +25 -8
  22. data/lib/active_record/associations/collection_proxy.rb +14 -7
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +24 -3
  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 +77 -42
  28. data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
  29. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  30. data/lib/active_record/associations/preloader.rb +13 -8
  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 -9
  36. data/lib/active_record/attribute_methods.rb +64 -54
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  38. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  39. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  40. data/lib/active_record/attribute_methods/query.rb +3 -6
  41. data/lib/active_record/attribute_methods/read.rb +8 -12
  42. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +12 -21
  45. data/lib/active_record/attributes.rb +32 -8
  46. data/lib/active_record/autosave_association.rb +63 -44
  47. data/lib/active_record/base.rb +2 -14
  48. data/lib/active_record/callbacks.rb +153 -24
  49. data/lib/active_record/coders/yaml_column.rb +1 -2
  50. data/lib/active_record/connection_adapters.rb +50 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +86 -37
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +4 -9
  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 +152 -116
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -52
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +263 -107
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +74 -76
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
  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/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  77. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  93. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  94. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  98. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  99. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  100. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  101. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  102. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  103. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  104. data/lib/active_record/connection_adapters/postgresql_adapter.rb +81 -57
  105. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  106. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  107. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
  108. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  109. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  110. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  111. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
  112. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  113. data/lib/active_record/connection_handling.rb +211 -81
  114. data/lib/active_record/core.rb +237 -69
  115. data/lib/active_record/counter_cache.rb +4 -1
  116. data/lib/active_record/database_configurations.rb +124 -85
  117. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  118. data/lib/active_record/database_configurations/database_config.rb +52 -9
  119. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  120. data/lib/active_record/database_configurations/url_config.rb +15 -41
  121. data/lib/active_record/delegated_type.rb +209 -0
  122. data/lib/active_record/destroy_association_async_job.rb +36 -0
  123. data/lib/active_record/dynamic_matchers.rb +2 -3
  124. data/lib/active_record/enum.rb +40 -16
  125. data/lib/active_record/errors.rb +47 -12
  126. data/lib/active_record/explain.rb +9 -5
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +10 -17
  129. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  130. data/lib/active_record/fixture_set/render_context.rb +1 -1
  131. data/lib/active_record/fixture_set/table_row.rb +2 -3
  132. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  133. data/lib/active_record/fixtures.rb +54 -11
  134. data/lib/active_record/gem_version.rb +1 -1
  135. data/lib/active_record/inheritance.rb +40 -21
  136. data/lib/active_record/insert_all.rb +39 -10
  137. data/lib/active_record/integration.rb +3 -5
  138. data/lib/active_record/internal_metadata.rb +16 -7
  139. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  140. data/lib/active_record/locking/optimistic.rb +22 -17
  141. data/lib/active_record/locking/pessimistic.rb +6 -2
  142. data/lib/active_record/log_subscriber.rb +27 -9
  143. data/lib/active_record/middleware/database_selector.rb +4 -2
  144. data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
  145. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  146. data/lib/active_record/migration.rb +114 -84
  147. data/lib/active_record/migration/command_recorder.rb +53 -45
  148. data/lib/active_record/migration/compatibility.rb +70 -20
  149. data/lib/active_record/migration/join_table.rb +0 -1
  150. data/lib/active_record/model_schema.rb +120 -15
  151. data/lib/active_record/nested_attributes.rb +2 -5
  152. data/lib/active_record/no_touching.rb +1 -1
  153. data/lib/active_record/null_relation.rb +0 -1
  154. data/lib/active_record/persistence.rb +50 -46
  155. data/lib/active_record/query_cache.rb +15 -5
  156. data/lib/active_record/querying.rb +12 -7
  157. data/lib/active_record/railtie.rb +65 -45
  158. data/lib/active_record/railties/databases.rake +267 -93
  159. data/lib/active_record/readonly_attributes.rb +4 -0
  160. data/lib/active_record/reflection.rb +77 -63
  161. data/lib/active_record/relation.rb +108 -67
  162. data/lib/active_record/relation/batches.rb +38 -32
  163. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  164. data/lib/active_record/relation/calculations.rb +102 -45
  165. data/lib/active_record/relation/delegation.rb +9 -7
  166. data/lib/active_record/relation/finder_methods.rb +55 -17
  167. data/lib/active_record/relation/from_clause.rb +5 -1
  168. data/lib/active_record/relation/merger.rb +27 -26
  169. data/lib/active_record/relation/predicate_builder.rb +55 -35
  170. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  171. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  172. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  173. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  174. data/lib/active_record/relation/query_methods.rb +340 -180
  175. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  176. data/lib/active_record/relation/spawn_methods.rb +8 -8
  177. data/lib/active_record/relation/where_clause.rb +104 -58
  178. data/lib/active_record/result.rb +41 -34
  179. data/lib/active_record/runtime_registry.rb +2 -2
  180. data/lib/active_record/sanitization.rb +6 -17
  181. data/lib/active_record/schema_dumper.rb +34 -4
  182. data/lib/active_record/schema_migration.rb +2 -8
  183. data/lib/active_record/scoping.rb +0 -1
  184. data/lib/active_record/scoping/default.rb +0 -1
  185. data/lib/active_record/scoping/named.rb +7 -18
  186. data/lib/active_record/secure_token.rb +16 -8
  187. data/lib/active_record/serialization.rb +5 -3
  188. data/lib/active_record/signed_id.rb +116 -0
  189. data/lib/active_record/statement_cache.rb +20 -4
  190. data/lib/active_record/store.rb +3 -3
  191. data/lib/active_record/suppressor.rb +2 -2
  192. data/lib/active_record/table_metadata.rb +39 -36
  193. data/lib/active_record/tasks/database_tasks.rb +139 -113
  194. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  195. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  196. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  197. data/lib/active_record/test_databases.rb +5 -4
  198. data/lib/active_record/test_fixtures.rb +38 -16
  199. data/lib/active_record/timestamp.rb +4 -7
  200. data/lib/active_record/touch_later.rb +20 -21
  201. data/lib/active_record/transactions.rb +22 -71
  202. data/lib/active_record/type.rb +8 -2
  203. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  204. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  205. data/lib/active_record/type/serialized.rb +6 -3
  206. data/lib/active_record/type/time.rb +10 -0
  207. data/lib/active_record/type/type_map.rb +0 -1
  208. data/lib/active_record/type/unsigned_integer.rb +0 -1
  209. data/lib/active_record/type_caster/connection.rb +0 -1
  210. data/lib/active_record/type_caster/map.rb +8 -5
  211. data/lib/active_record/validations.rb +3 -3
  212. data/lib/active_record/validations/associated.rb +1 -2
  213. data/lib/active_record/validations/numericality.rb +35 -0
  214. data/lib/active_record/validations/uniqueness.rb +24 -4
  215. data/lib/arel.rb +15 -12
  216. data/lib/arel/attributes/attribute.rb +4 -0
  217. data/lib/arel/collectors/bind.rb +5 -0
  218. data/lib/arel/collectors/composite.rb +8 -0
  219. data/lib/arel/collectors/sql_string.rb +7 -0
  220. data/lib/arel/collectors/substitute_binds.rb +7 -0
  221. data/lib/arel/nodes.rb +3 -1
  222. data/lib/arel/nodes/binary.rb +82 -8
  223. data/lib/arel/nodes/bind_param.rb +8 -0
  224. data/lib/arel/nodes/casted.rb +21 -9
  225. data/lib/arel/nodes/equality.rb +6 -9
  226. data/lib/arel/nodes/grouping.rb +3 -0
  227. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  228. data/lib/arel/nodes/in.rb +8 -1
  229. data/lib/arel/nodes/infix_operation.rb +13 -1
  230. data/lib/arel/nodes/join_source.rb +1 -1
  231. data/lib/arel/nodes/node.rb +7 -6
  232. data/lib/arel/nodes/ordering.rb +27 -0
  233. data/lib/arel/nodes/sql_literal.rb +3 -0
  234. data/lib/arel/nodes/table_alias.rb +7 -3
  235. data/lib/arel/nodes/unary.rb +0 -1
  236. data/lib/arel/predications.rb +17 -24
  237. data/lib/arel/select_manager.rb +1 -2
  238. data/lib/arel/table.rb +13 -5
  239. data/lib/arel/visitors.rb +0 -7
  240. data/lib/arel/visitors/dot.rb +14 -3
  241. data/lib/arel/visitors/mysql.rb +11 -1
  242. data/lib/arel/visitors/postgresql.rb +15 -5
  243. data/lib/arel/visitors/sqlite.rb +0 -1
  244. data/lib/arel/visitors/to_sql.rb +89 -79
  245. data/lib/arel/visitors/visitor.rb +0 -1
  246. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  247. data/lib/rails/generators/active_record/migration.rb +6 -2
  248. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  249. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  250. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  251. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  252. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  253. metadata +27 -24
  254. data/lib/active_record/attribute_decorators.rb +0 -90
  255. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  256. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  257. data/lib/active_record/define_callbacks.rb +0 -22
  258. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  259. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  260. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  261. data/lib/arel/attributes.rb +0 -22
  262. data/lib/arel/visitors/depth_first.rb +0 -204
  263. data/lib/arel/visitors/ibm_db.rb +0 -34
  264. data/lib/arel/visitors/informix.rb +0 -62
  265. data/lib/arel/visitors/mssql.rb +0 -157
  266. data/lib/arel/visitors/oracle.rb +0 -159
  267. data/lib/arel/visitors/oracle12.rb +0 -66
  268. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -45,7 +45,6 @@ module ActiveRecord
45
45
 
46
46
  class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
47
47
  private
48
-
49
48
  def dealloc(stmt)
50
49
  stmt.close
51
50
  end
@@ -93,6 +92,14 @@ module ActiveRecord
93
92
  true
94
93
  end
95
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
+
96
103
  def supports_views?
97
104
  true
98
105
  end
@@ -105,11 +112,19 @@ module ActiveRecord
105
112
  mariadb? || database_version >= "5.7.5"
106
113
  end
107
114
 
108
- # 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.
109
116
  def supports_optimizer_hints?
110
117
  !mariadb? && database_version >= "5.7.7"
111
118
  end
112
119
 
120
+ def supports_common_table_expressions?
121
+ if mariadb?
122
+ database_version >= "10.2.1"
123
+ else
124
+ database_version >= "8.0.1"
125
+ end
126
+ end
127
+
113
128
  def supports_advisory_locks?
114
129
  true
115
130
  end
@@ -135,7 +150,12 @@ module ActiveRecord
135
150
  end
136
151
 
137
152
  def index_algorithms
138
- { 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
+ }
139
159
  end
140
160
 
141
161
  # HELPER METHODS ===========================================
@@ -176,18 +196,10 @@ module ActiveRecord
176
196
  # DATABASE STATEMENTS ======================================
177
197
  #++
178
198
 
179
- def explain(arel, binds = [])
180
- sql = "EXPLAIN #{to_sql(arel, binds)}"
181
- start = Concurrent.monotonic_time
182
- result = exec_query(sql, "EXPLAIN", binds)
183
- elapsed = Concurrent.monotonic_time - start
184
-
185
- MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
186
- end
187
-
188
199
  # Executes the SQL statement in the context of this connection.
189
200
  def execute(sql, name = nil)
190
201
  materialize_transactions
202
+ mark_transaction_written_if_write(sql)
191
203
 
192
204
  log(sql, name) do
193
205
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -204,7 +216,7 @@ module ActiveRecord
204
216
  end
205
217
 
206
218
  def begin_db_transaction
207
- execute "BEGIN"
219
+ execute("BEGIN", "TRANSACTION")
208
220
  end
209
221
 
210
222
  def begin_isolated_db_transaction(isolation)
@@ -213,11 +225,11 @@ module ActiveRecord
213
225
  end
214
226
 
215
227
  def commit_db_transaction #:nodoc:
216
- execute "COMMIT"
228
+ execute("COMMIT", "TRANSACTION")
217
229
  end
218
230
 
219
231
  def exec_rollback_db_transaction #:nodoc:
220
- execute "ROLLBACK"
232
+ execute("ROLLBACK", "TRANSACTION")
221
233
  end
222
234
 
223
235
  def empty_insert_statement_value(primary_key = nil)
@@ -298,6 +310,8 @@ module ActiveRecord
298
310
  # Example:
299
311
  # rename_table('octopuses', 'octopi')
300
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)
301
315
  execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
302
316
  rename_table_indexes(table_name, new_name)
303
317
  end
@@ -317,7 +331,8 @@ module ActiveRecord
317
331
  # Although this command ignores most +options+ and the block if one is given,
318
332
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
319
333
  # In that case, +options+ and the block will be used by create_table.
320
- def drop_table(table_name, options = {})
334
+ def drop_table(table_name, **options)
335
+ schema_cache.clear_data_source_cache!(table_name.to_s)
321
336
  execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
322
337
  end
323
338
 
@@ -349,8 +364,8 @@ module ActiveRecord
349
364
  change_column table_name, column_name, nil, comment: comment
350
365
  end
351
366
 
352
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
353
- 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)}")
354
369
  end
355
370
 
356
371
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
@@ -358,10 +373,13 @@ module ActiveRecord
358
373
  rename_column_indexes(table_name, column_name, new_column_name)
359
374
  end
360
375
 
361
- def add_index(table_name, column_name, options = {}) #:nodoc:
362
- index_name, index_type, index_columns, _, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
363
- sql = +"CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
364
- 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)
365
383
  end
366
384
 
367
385
  def add_sql_comment!(sql, comment) # :nodoc:
@@ -405,24 +423,60 @@ module ActiveRecord
405
423
  end
406
424
  end
407
425
 
408
- def table_options(table_name) # :nodoc:
409
- 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
410
453
 
454
+ def table_options(table_name) # :nodoc:
411
455
  create_table_info = create_table_info(table_name)
412
456
 
413
457
  # strip create_definitions and partition_options
414
- raw_table_options = create_table_info.sub(/\A.*\n\) /m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
458
+ # Be aware that `create_table_info` might not include any table options due to `NO_TABLE_OPTIONS` sql mode.
459
+ raw_table_options = create_table_info.sub(/\A.*\n\) ?/m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
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
415
470
 
416
471
  # strip AUTO_INCREMENT
417
472
  raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
418
473
 
419
- table_options[:options] = raw_table_options
420
-
421
474
  # strip COMMENT
422
475
  if raw_table_options.sub!(/ COMMENT='.+'/, "")
423
476
  table_options[:comment] = table_comment(table_name)
424
477
  end
425
478
 
479
+ table_options[:options] = raw_table_options unless raw_table_options == "ENGINE=InnoDB"
426
480
  table_options
427
481
  end
428
482
 
@@ -440,29 +494,14 @@ module ActiveRecord
440
494
 
441
495
  query_values(<<~SQL, "SCHEMA")
442
496
  SELECT column_name
443
- FROM information_schema.key_column_usage
444
- WHERE constraint_name = 'PRIMARY'
497
+ FROM information_schema.statistics
498
+ WHERE index_name = 'PRIMARY'
445
499
  AND table_schema = #{scope[:schema]}
446
500
  AND table_name = #{scope[:name]}
447
- ORDER BY ordinal_position
501
+ ORDER BY seq_in_index
448
502
  SQL
449
503
  end
450
504
 
451
- def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
452
- column = column_for_attribute(attribute)
453
-
454
- if column.collation && !column.case_sensitive? && !value.nil?
455
- ActiveSupport::Deprecation.warn(<<~MSG.squish)
456
- Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1.
457
- To continue case sensitive comparison on the :#{attribute.name} attribute in #{klass} model,
458
- pass `case_sensitive: true` option explicitly to the uniqueness validator.
459
- MSG
460
- attribute.eq(Arel::Nodes::Bin.new(value))
461
- else
462
- super
463
- end
464
- end
465
-
466
505
  def case_sensitive_comparison(attribute, value) # :nodoc:
467
506
  column = column_for_attribute(attribute)
468
507
 
@@ -481,14 +520,14 @@ module ActiveRecord
481
520
  # In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
482
521
  # DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
483
522
  # distinct queries, and requires that the ORDER BY include the distinct column.
484
- # 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
485
524
  def columns_for_distinct(columns, orders) # :nodoc:
486
- order_columns = orders.reject(&:blank?).map { |s|
525
+ order_columns = orders.compact_blank.map { |s|
487
526
  # Convert Arel node to string
488
- s = s.to_sql unless s.is_a?(String)
527
+ s = visitor.compile(s) unless s.is_a?(String)
489
528
  # Remove any ASC/DESC modifiers
490
529
  s.gsub(/\s+(?:ASC|DESC)\b/i, "")
491
- }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
530
+ }.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
492
531
 
493
532
  (order_columns << super).join(", ")
494
533
  end
@@ -509,6 +548,7 @@ module ActiveRecord
509
548
  sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
510
549
  elsif insert.update_duplicates?
511
550
  sql << " ON DUPLICATE KEY UPDATE "
551
+ sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
512
552
  sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
513
553
  end
514
554
 
@@ -522,11 +562,13 @@ module ActiveRecord
522
562
  end
523
563
 
524
564
  private
525
-
526
565
  def initialize_type_map(m = type_map)
527
566
  super
528
567
 
529
- 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
530
572
 
531
573
  m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
532
574
  m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
@@ -546,28 +588,19 @@ module ActiveRecord
546
588
  register_integer_type m, %r(^tinyint)i, limit: 1
547
589
 
548
590
  m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
549
- m.alias_type %r(year)i, "integer"
550
- m.alias_type %r(bit)i, "binary"
591
+ m.alias_type %r(year)i, "integer"
592
+ m.alias_type %r(bit)i, "binary"
551
593
 
552
- m.register_type(%r(enum)i) do |sql_type|
553
- limit = sql_type[/^enum\s*\((.+)\)/i, 1]
554
- .split(",").map { |enum| enum.strip.length - 2 }.max
555
- MysqlString.new(limit: limit)
556
- end
557
-
558
- m.register_type(%r(^set)i) do |sql_type|
559
- limit = sql_type[/^set\s*\((.+)\)/i, 1]
560
- .split(",").map { |set| set.strip.length - 1 }.sum - 1
561
- MysqlString.new(limit: limit)
562
- 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)
563
596
  end
564
597
 
565
- def register_integer_type(mapping, key, options)
598
+ def register_integer_type(mapping, key, **options)
566
599
  mapping.register_type(key) do |sql_type|
567
600
  if /\bunsigned\b/.match?(sql_type)
568
- Type::UnsignedInteger.new(options)
601
+ Type::UnsignedInteger.new(**options)
569
602
  else
570
- Type::Integer.new(options)
603
+ Type::Integer.new(**options)
571
604
  end
572
605
  end
573
606
  end
@@ -580,7 +613,9 @@ module ActiveRecord
580
613
  end
581
614
  end
582
615
 
583
- # 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
618
+ ER_FILSORT_ABORT = 1028
584
619
  ER_DUP_ENTRY = 1062
585
620
  ER_NOT_NULL_VIOLATION = 1048
586
621
  ER_NO_REFERENCED_ROW = 1216
@@ -600,6 +635,14 @@ module ActiveRecord
600
635
 
601
636
  def translate_exception(exception, message:, sql:, binds:)
602
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)
603
646
  when ER_DUP_ENTRY
604
647
  RecordNotUnique.new(message, sql: sql, binds: binds)
605
648
  when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
@@ -622,7 +665,7 @@ module ActiveRecord
622
665
  Deadlocked.new(message, sql: sql, binds: binds)
623
666
  when ER_LOCK_WAIT_TIMEOUT
624
667
  LockWaitTimeout.new(message, sql: sql, binds: binds)
625
- when ER_QUERY_TIMEOUT
668
+ when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
626
669
  StatementTimeout.new(message, sql: sql, binds: binds)
627
670
  when ER_QUERY_INTERRUPTED
628
671
  QueryCanceled.new(message, sql: sql, binds: binds)
@@ -631,7 +674,7 @@ module ActiveRecord
631
674
  end
632
675
  end
633
676
 
634
- def change_column_for_alter(table_name, column_name, type, options = {})
677
+ def change_column_for_alter(table_name, column_name, type, **options)
635
678
  column = column_for(table_name, column_name)
636
679
  type ||= column.sql_type
637
680
 
@@ -648,51 +691,53 @@ module ActiveRecord
648
691
  end
649
692
 
650
693
  td = create_table_definition(table_name)
651
- cd = td.new_column_definition(column.name, type, options)
694
+ cd = td.new_column_definition(column.name, type, **options)
652
695
  schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
653
696
  end
654
697
 
655
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
+
656
701
  column = column_for(table_name, column_name)
657
702
  options = {
658
703
  default: column.default,
659
704
  null: column.null,
660
- auto_increment: column.auto_increment?
705
+ auto_increment: column.auto_increment?,
706
+ comment: column.comment
661
707
  }
662
708
 
663
709
  current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
664
710
  td = create_table_definition(table_name)
665
- cd = td.new_column_definition(new_column_name, current_type, options)
711
+ cd = td.new_column_definition(new_column_name, current_type, **options)
666
712
  schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
667
713
  end
668
714
 
669
- def add_index_for_alter(table_name, column_name, options = {})
670
- index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
671
- index_algorithm[0, 0] = ", " if index_algorithm.present?
672
- "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}"
673
720
  end
674
721
 
675
- def remove_index_for_alter(table_name, options = {})
676
- 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)
677
724
  "DROP INDEX #{quote_column_name(index_name)}"
678
725
  end
679
726
 
680
- def add_timestamps_for_alter(table_name, options = {})
681
- options[:null] = false if options[:null].nil?
682
-
683
- if !options.key?(:precision) && supports_datetime_with_precision?
684
- options[:precision] = 6
727
+ def supports_rename_index?
728
+ if mariadb?
729
+ database_version >= "10.5.2"
730
+ else
731
+ database_version >= "5.7.6"
685
732
  end
686
-
687
- [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
688
733
  end
689
734
 
690
- def remove_timestamps_for_alter(table_name, options = {})
691
- [remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
692
- end
693
-
694
- def supports_rename_index?
695
- mariadb? ? false : database_version >= "5.7.6"
735
+ def supports_rename_column?
736
+ if mariadb?
737
+ database_version >= "10.5.2"
738
+ else
739
+ database_version >= "8.0.3"
740
+ end
696
741
  end
697
742
 
698
743
  def configure_connection
@@ -709,7 +754,7 @@ module ActiveRecord
709
754
  defaults = [":default", :default].to_set
710
755
 
711
756
  # Make MySQL reject illegal values rather than truncating or blanking them, see
712
- # 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
713
758
  # If the user has provided another value for sql_mode, don't replace it.
714
759
  if sql_mode = variables.delete("sql_mode")
715
760
  sql_mode = quote(sql_mode)
@@ -726,7 +771,7 @@ module ActiveRecord
726
771
  sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
727
772
 
728
773
  # NAMES does not have an equals sign, see
729
- # https://dev.mysql.com/doc/refman/5.7/en/set-names.html
774
+ # https://dev.mysql.com/doc/refman/en/set-names.html
730
775
  # (trailing comma because variable_assignments will always have content)
731
776
  if @config[:encoding]
732
777
  encoding = +"NAMES #{@config[:encoding]}"
@@ -745,7 +790,7 @@ module ActiveRecord
745
790
  end.compact.join(", ")
746
791
 
747
792
  # ...and send them all in one query
748
- execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
793
+ execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
749
794
  end
750
795
 
751
796
  def column_definitions(table_name) # :nodoc:
@@ -787,34 +832,23 @@ module ActiveRecord
787
832
  options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
788
833
  end
789
834
 
790
- MismatchedForeignKey.new(options)
835
+ MismatchedForeignKey.new(**options)
791
836
  end
792
837
 
793
838
  def version_string(full_version_string)
794
839
  full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
795
840
  end
796
841
 
797
- class MysqlString < Type::String # :nodoc:
798
- def serialize(value)
799
- case value
800
- when true then "1"
801
- when false then "0"
802
- else super
803
- end
804
- end
805
-
806
- private
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:
807
845
 
808
- def cast_value(value)
809
- case value
810
- when true then "1"
811
- when false then "0"
812
- else super
813
- end
814
- 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)
815
851
  end
816
-
817
- ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
818
852
  ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
819
853
  end
820
854
  end