activerecord 6.0.1 → 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 +843 -626
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record/aggregations.rb +1 -2
  6. data/lib/active_record/association_relation.rb +18 -17
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +49 -37
  9. data/lib/active_record/associations/association_scope.rb +17 -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 -3
  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 +25 -8
  20. data/lib/active_record/associations/collection_proxy.rb +14 -7
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -3
  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 +36 -14
  26. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  27. data/lib/active_record/associations/join_dependency.rb +73 -42
  28. data/lib/active_record/associations/preloader/association.rb +51 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +12 -7
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +115 -12
  34. data/lib/active_record/attribute_assignment.rb +10 -9
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  36. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  37. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  38. data/lib/active_record/attribute_methods/query.rb +3 -6
  39. data/lib/active_record/attribute_methods/read.rb +8 -12
  40. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  42. data/lib/active_record/attribute_methods/write.rb +12 -21
  43. data/lib/active_record/attribute_methods.rb +64 -54
  44. data/lib/active_record/attributes.rb +32 -8
  45. data/lib/active_record/autosave_association.rb +56 -41
  46. data/lib/active_record/base.rb +2 -14
  47. data/lib/active_record/callbacks.rb +153 -24
  48. data/lib/active_record/coders/yaml_column.rb +1 -2
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +190 -136
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +82 -37
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -8
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -52
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +263 -107
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +60 -73
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +136 -111
  62. data/lib/active_record/connection_adapters/column.rb +15 -1
  63. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  64. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  65. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +28 -36
  67. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  68. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  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 +17 -13
  73. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  74. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  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 +19 -56
  79. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  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 +10 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  91. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  93. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  95. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  96. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  97. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  98. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  99. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  100. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  101. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +77 -57
  103. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  104. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  105. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +36 -12
  106. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  107. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  108. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  109. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -57
  110. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  111. data/lib/active_record/connection_adapters.rb +50 -0
  112. data/lib/active_record/connection_handling.rb +210 -87
  113. data/lib/active_record/core.rb +229 -65
  114. data/lib/active_record/counter_cache.rb +4 -1
  115. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  116. data/lib/active_record/database_configurations/database_config.rb +52 -9
  117. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  118. data/lib/active_record/database_configurations/url_config.rb +15 -41
  119. data/lib/active_record/database_configurations.rb +124 -85
  120. data/lib/active_record/delegated_type.rb +209 -0
  121. data/lib/active_record/destroy_association_async_job.rb +36 -0
  122. data/lib/active_record/dynamic_matchers.rb +2 -3
  123. data/lib/active_record/enum.rb +40 -16
  124. data/lib/active_record/errors.rb +47 -12
  125. data/lib/active_record/explain.rb +9 -5
  126. data/lib/active_record/explain_subscriber.rb +1 -1
  127. data/lib/active_record/fixture_set/file.rb +10 -17
  128. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  129. data/lib/active_record/fixture_set/render_context.rb +1 -1
  130. data/lib/active_record/fixture_set/table_row.rb +2 -3
  131. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  132. data/lib/active_record/fixtures.rb +54 -11
  133. data/lib/active_record/gem_version.rb +2 -2
  134. data/lib/active_record/inheritance.rb +40 -21
  135. data/lib/active_record/insert_all.rb +38 -9
  136. data/lib/active_record/integration.rb +3 -5
  137. data/lib/active_record/internal_metadata.rb +16 -7
  138. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  139. data/lib/active_record/locking/optimistic.rb +22 -17
  140. data/lib/active_record/locking/pessimistic.rb +6 -2
  141. data/lib/active_record/log_subscriber.rb +27 -9
  142. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  143. data/lib/active_record/middleware/database_selector/resolver.rb +6 -2
  144. data/lib/active_record/middleware/database_selector.rb +4 -2
  145. data/lib/active_record/migration/command_recorder.rb +53 -45
  146. data/lib/active_record/migration/compatibility.rb +70 -20
  147. data/lib/active_record/migration/join_table.rb +0 -1
  148. data/lib/active_record/migration.rb +114 -84
  149. data/lib/active_record/model_schema.rb +117 -15
  150. data/lib/active_record/nested_attributes.rb +2 -5
  151. data/lib/active_record/no_touching.rb +1 -1
  152. data/lib/active_record/null_relation.rb +0 -1
  153. data/lib/active_record/persistence.rb +50 -46
  154. data/lib/active_record/query_cache.rb +15 -5
  155. data/lib/active_record/querying.rb +12 -7
  156. data/lib/active_record/railtie.rb +65 -45
  157. data/lib/active_record/railties/databases.rake +267 -93
  158. data/lib/active_record/readonly_attributes.rb +4 -0
  159. data/lib/active_record/reflection.rb +77 -63
  160. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  161. data/lib/active_record/relation/batches.rb +38 -32
  162. data/lib/active_record/relation/calculations.rb +102 -45
  163. data/lib/active_record/relation/delegation.rb +9 -7
  164. data/lib/active_record/relation/finder_methods.rb +45 -16
  165. data/lib/active_record/relation/from_clause.rb +5 -1
  166. data/lib/active_record/relation/merger.rb +27 -26
  167. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  168. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  169. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  170. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  171. data/lib/active_record/relation/predicate_builder.rb +55 -35
  172. data/lib/active_record/relation/query_methods.rb +335 -187
  173. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  174. data/lib/active_record/relation/spawn_methods.rb +8 -8
  175. data/lib/active_record/relation/where_clause.rb +104 -58
  176. data/lib/active_record/relation.rb +108 -68
  177. data/lib/active_record/result.rb +41 -34
  178. data/lib/active_record/runtime_registry.rb +2 -2
  179. data/lib/active_record/sanitization.rb +6 -17
  180. data/lib/active_record/schema_dumper.rb +34 -4
  181. data/lib/active_record/schema_migration.rb +2 -8
  182. data/lib/active_record/scoping/default.rb +0 -1
  183. data/lib/active_record/scoping/named.rb +7 -18
  184. data/lib/active_record/scoping.rb +0 -1
  185. data/lib/active_record/secure_token.rb +16 -8
  186. data/lib/active_record/serialization.rb +5 -3
  187. data/lib/active_record/signed_id.rb +116 -0
  188. data/lib/active_record/statement_cache.rb +20 -4
  189. data/lib/active_record/store.rb +3 -3
  190. data/lib/active_record/suppressor.rb +2 -2
  191. data/lib/active_record/table_metadata.rb +39 -36
  192. data/lib/active_record/tasks/database_tasks.rb +139 -113
  193. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  194. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  195. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  196. data/lib/active_record/test_databases.rb +5 -4
  197. data/lib/active_record/test_fixtures.rb +38 -16
  198. data/lib/active_record/timestamp.rb +4 -7
  199. data/lib/active_record/touch_later.rb +20 -21
  200. data/lib/active_record/transactions.rb +21 -70
  201. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  202. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  203. data/lib/active_record/type/serialized.rb +6 -3
  204. data/lib/active_record/type/time.rb +10 -0
  205. data/lib/active_record/type/type_map.rb +0 -1
  206. data/lib/active_record/type/unsigned_integer.rb +0 -1
  207. data/lib/active_record/type.rb +8 -2
  208. data/lib/active_record/type_caster/connection.rb +0 -1
  209. data/lib/active_record/type_caster/map.rb +8 -5
  210. data/lib/active_record/validations/associated.rb +1 -2
  211. data/lib/active_record/validations/numericality.rb +35 -0
  212. data/lib/active_record/validations/uniqueness.rb +24 -4
  213. data/lib/active_record/validations.rb +3 -3
  214. data/lib/active_record.rb +7 -13
  215. data/lib/arel/attributes/attribute.rb +4 -0
  216. data/lib/arel/collectors/bind.rb +5 -0
  217. data/lib/arel/collectors/composite.rb +8 -0
  218. data/lib/arel/collectors/sql_string.rb +7 -0
  219. data/lib/arel/collectors/substitute_binds.rb +7 -0
  220. data/lib/arel/nodes/binary.rb +82 -8
  221. data/lib/arel/nodes/bind_param.rb +8 -0
  222. data/lib/arel/nodes/casted.rb +21 -9
  223. data/lib/arel/nodes/equality.rb +6 -9
  224. data/lib/arel/nodes/grouping.rb +3 -0
  225. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  226. data/lib/arel/nodes/in.rb +8 -1
  227. data/lib/arel/nodes/infix_operation.rb +13 -1
  228. data/lib/arel/nodes/join_source.rb +1 -1
  229. data/lib/arel/nodes/node.rb +7 -6
  230. data/lib/arel/nodes/ordering.rb +27 -0
  231. data/lib/arel/nodes/sql_literal.rb +3 -0
  232. data/lib/arel/nodes/table_alias.rb +7 -3
  233. data/lib/arel/nodes/unary.rb +0 -1
  234. data/lib/arel/nodes.rb +3 -1
  235. data/lib/arel/predications.rb +17 -24
  236. data/lib/arel/select_manager.rb +1 -2
  237. data/lib/arel/table.rb +13 -5
  238. data/lib/arel/visitors/dot.rb +14 -3
  239. data/lib/arel/visitors/mysql.rb +11 -1
  240. data/lib/arel/visitors/postgresql.rb +15 -5
  241. data/lib/arel/visitors/sqlite.rb +0 -1
  242. data/lib/arel/visitors/to_sql.rb +89 -79
  243. data/lib/arel/visitors/visitor.rb +0 -1
  244. data/lib/arel/visitors.rb +0 -7
  245. data/lib/arel.rb +5 -9
  246. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  247. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  248. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  249. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  250. data/lib/rails/generators/active_record/migration.rb +6 -2
  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 +26 -26
  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,7 +112,7 @@ 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
@@ -143,7 +150,12 @@ module ActiveRecord
143
150
  end
144
151
 
145
152
  def index_algorithms
146
- { 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
+ }
147
159
  end
148
160
 
149
161
  # HELPER METHODS ===========================================
@@ -184,18 +196,10 @@ module ActiveRecord
184
196
  # DATABASE STATEMENTS ======================================
185
197
  #++
186
198
 
187
- def explain(arel, binds = [])
188
- sql = "EXPLAIN #{to_sql(arel, binds)}"
189
- start = Concurrent.monotonic_time
190
- result = exec_query(sql, "EXPLAIN", binds)
191
- elapsed = Concurrent.monotonic_time - start
192
-
193
- MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
194
- end
195
-
196
199
  # Executes the SQL statement in the context of this connection.
197
200
  def execute(sql, name = nil)
198
201
  materialize_transactions
202
+ mark_transaction_written_if_write(sql)
199
203
 
200
204
  log(sql, name) do
201
205
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -212,7 +216,7 @@ module ActiveRecord
212
216
  end
213
217
 
214
218
  def begin_db_transaction
215
- execute "BEGIN"
219
+ execute("BEGIN", "TRANSACTION")
216
220
  end
217
221
 
218
222
  def begin_isolated_db_transaction(isolation)
@@ -221,11 +225,11 @@ module ActiveRecord
221
225
  end
222
226
 
223
227
  def commit_db_transaction #:nodoc:
224
- execute "COMMIT"
228
+ execute("COMMIT", "TRANSACTION")
225
229
  end
226
230
 
227
231
  def exec_rollback_db_transaction #:nodoc:
228
- execute "ROLLBACK"
232
+ execute("ROLLBACK", "TRANSACTION")
229
233
  end
230
234
 
231
235
  def empty_insert_statement_value(primary_key = nil)
@@ -306,6 +310,8 @@ module ActiveRecord
306
310
  # Example:
307
311
  # rename_table('octopuses', 'octopi')
308
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)
309
315
  execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
310
316
  rename_table_indexes(table_name, new_name)
311
317
  end
@@ -325,7 +331,8 @@ module ActiveRecord
325
331
  # Although this command ignores most +options+ and the block if one is given,
326
332
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
327
333
  # In that case, +options+ and the block will be used by create_table.
328
- def drop_table(table_name, options = {})
334
+ def drop_table(table_name, **options)
335
+ schema_cache.clear_data_source_cache!(table_name.to_s)
329
336
  execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
330
337
  end
331
338
 
@@ -357,8 +364,8 @@ module ActiveRecord
357
364
  change_column table_name, column_name, nil, comment: comment
358
365
  end
359
366
 
360
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
361
- 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)}")
362
369
  end
363
370
 
364
371
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
@@ -366,10 +373,13 @@ module ActiveRecord
366
373
  rename_column_indexes(table_name, column_name, new_column_name)
367
374
  end
368
375
 
369
- def add_index(table_name, column_name, options = {}) #:nodoc:
370
- index_name, index_type, index_columns, _, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
371
- sql = +"CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
372
- 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)
373
383
  end
374
384
 
375
385
  def add_sql_comment!(sql, comment) # :nodoc:
@@ -413,24 +423,60 @@ module ActiveRecord
413
423
  end
414
424
  end
415
425
 
416
- def table_options(table_name) # :nodoc:
417
- 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
418
453
 
454
+ def table_options(table_name) # :nodoc:
419
455
  create_table_info = create_table_info(table_name)
420
456
 
421
457
  # strip create_definitions and partition_options
422
- 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
423
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
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
- s = s.to_sql unless s.is_a?(String)
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
 
@@ -530,11 +562,13 @@ module ActiveRecord
530
562
  end
531
563
 
532
564
  private
533
-
534
565
  def initialize_type_map(m = type_map)
535
566
  super
536
567
 
537
- 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
538
572
 
539
573
  m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
540
574
  m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
@@ -554,28 +588,19 @@ module ActiveRecord
554
588
  register_integer_type m, %r(^tinyint)i, limit: 1
555
589
 
556
590
  m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
557
- m.alias_type %r(year)i, "integer"
558
- m.alias_type %r(bit)i, "binary"
559
-
560
- m.register_type(%r(enum)i) do |sql_type|
561
- limit = sql_type[/^enum\s*\((.+)\)/i, 1]
562
- .split(",").map { |enum| enum.strip.length - 2 }.max
563
- MysqlString.new(limit: limit)
564
- end
591
+ m.alias_type %r(year)i, "integer"
592
+ m.alias_type %r(bit)i, "binary"
565
593
 
566
- m.register_type(%r(^set)i) do |sql_type|
567
- limit = sql_type[/^set\s*\((.+)\)/i, 1]
568
- .split(",").map { |set| set.strip.length - 1 }.sum - 1
569
- MysqlString.new(limit: limit)
570
- 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)
571
596
  end
572
597
 
573
- def register_integer_type(mapping, key, options)
598
+ def register_integer_type(mapping, key, **options)
574
599
  mapping.register_type(key) do |sql_type|
575
600
  if /\bunsigned\b/.match?(sql_type)
576
- Type::UnsignedInteger.new(options)
601
+ Type::UnsignedInteger.new(**options)
577
602
  else
578
- Type::Integer.new(options)
603
+ Type::Integer.new(**options)
579
604
  end
580
605
  end
581
606
  end
@@ -588,7 +613,8 @@ module ActiveRecord
588
613
  end
589
614
  end
590
615
 
591
- # 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
592
618
  ER_FILSORT_ABORT = 1028
593
619
  ER_DUP_ENTRY = 1062
594
620
  ER_NOT_NULL_VIOLATION = 1048
@@ -609,6 +635,14 @@ module ActiveRecord
609
635
 
610
636
  def translate_exception(exception, message:, sql:, binds:)
611
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)
612
646
  when ER_DUP_ENTRY
613
647
  RecordNotUnique.new(message, sql: sql, binds: binds)
614
648
  when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
@@ -640,7 +674,7 @@ module ActiveRecord
640
674
  end
641
675
  end
642
676
 
643
- def change_column_for_alter(table_name, column_name, type, options = {})
677
+ def change_column_for_alter(table_name, column_name, type, **options)
644
678
  column = column_for(table_name, column_name)
645
679
  type ||= column.sql_type
646
680
 
@@ -657,51 +691,53 @@ module ActiveRecord
657
691
  end
658
692
 
659
693
  td = create_table_definition(table_name)
660
- cd = td.new_column_definition(column.name, type, options)
694
+ cd = td.new_column_definition(column.name, type, **options)
661
695
  schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
662
696
  end
663
697
 
664
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
+
665
701
  column = column_for(table_name, column_name)
666
702
  options = {
667
703
  default: column.default,
668
704
  null: column.null,
669
- auto_increment: column.auto_increment?
705
+ auto_increment: column.auto_increment?,
706
+ comment: column.comment
670
707
  }
671
708
 
672
709
  current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
673
710
  td = create_table_definition(table_name)
674
- cd = td.new_column_definition(new_column_name, current_type, options)
711
+ cd = td.new_column_definition(new_column_name, current_type, **options)
675
712
  schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
676
713
  end
677
714
 
678
- def add_index_for_alter(table_name, column_name, options = {})
679
- index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
680
- index_algorithm[0, 0] = ", " if index_algorithm.present?
681
- "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}"
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
 
689
- def add_timestamps_for_alter(table_name, options = {})
690
- options[:null] = false if options[:null].nil?
691
-
692
- if !options.key?(:precision) && supports_datetime_with_precision?
693
- 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"
694
732
  end
695
-
696
- [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
697
733
  end
698
734
 
699
- def remove_timestamps_for_alter(table_name, options = {})
700
- [remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
701
- end
702
-
703
- def supports_rename_index?
704
- 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
705
741
  end
706
742
 
707
743
  def configure_connection
@@ -718,7 +754,7 @@ module ActiveRecord
718
754
  defaults = [":default", :default].to_set
719
755
 
720
756
  # Make MySQL reject illegal values rather than truncating or blanking them, see
721
- # 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
722
758
  # If the user has provided another value for sql_mode, don't replace it.
723
759
  if sql_mode = variables.delete("sql_mode")
724
760
  sql_mode = quote(sql_mode)
@@ -735,7 +771,7 @@ module ActiveRecord
735
771
  sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
736
772
 
737
773
  # NAMES does not have an equals sign, see
738
- # https://dev.mysql.com/doc/refman/5.7/en/set-names.html
774
+ # https://dev.mysql.com/doc/refman/en/set-names.html
739
775
  # (trailing comma because variable_assignments will always have content)
740
776
  if @config[:encoding]
741
777
  encoding = +"NAMES #{@config[:encoding]}"
@@ -754,7 +790,7 @@ module ActiveRecord
754
790
  end.compact.join(", ")
755
791
 
756
792
  # ...and send them all in one query
757
- execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
793
+ execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
758
794
  end
759
795
 
760
796
  def column_definitions(table_name) # :nodoc:
@@ -796,34 +832,23 @@ module ActiveRecord
796
832
  options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
797
833
  end
798
834
 
799
- MismatchedForeignKey.new(options)
835
+ MismatchedForeignKey.new(**options)
800
836
  end
801
837
 
802
838
  def version_string(full_version_string)
803
839
  full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
804
840
  end
805
841
 
806
- class MysqlString < Type::String # :nodoc:
807
- def serialize(value)
808
- case value
809
- when true then "1"
810
- when false then "0"
811
- else super
812
- end
813
- end
814
-
815
- 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:
816
845
 
817
- def cast_value(value)
818
- case value
819
- when true then "1"
820
- when false then "0"
821
- else super
822
- end
823
- 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)
824
851
  end
825
-
826
- ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
827
852
  ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
828
853
  end
829
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
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  end
12
12
 
13
13
  def case_sensitive?
14
- collation && !/_ci\z/.match?(collation)
14
+ collation && !collation.end_with?("_ci")
15
15
  end
16
16
 
17
17
  def auto_increment?