activerecord 5.2.8.1 → 6.1.6.1

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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1255 -596
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +9 -8
  7. data/lib/active_record/association_relation.rb +30 -10
  8. data/lib/active_record/associations/alias_tracker.rb +19 -16
  9. data/lib/active_record/associations/association.rb +100 -41
  10. data/lib/active_record/associations/association_scope.rb +23 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +55 -48
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
  13. data/lib/active_record/associations/builder/association.rb +45 -22
  14. data/lib/active_record/associations/builder/belongs_to.rb +29 -59
  15. data/lib/active_record/associations/builder/collection_association.rb +8 -17
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -2
  18. data/lib/active_record/associations/builder/has_one.rb +33 -2
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -1
  20. data/lib/active_record/associations/collection_association.rb +44 -34
  21. data/lib/active_record/associations/collection_proxy.rb +25 -21
  22. data/lib/active_record/associations/foreign_association.rb +20 -0
  23. data/lib/active_record/associations/has_many_association.rb +26 -13
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -18
  25. data/lib/active_record/associations/has_one_association.rb +43 -31
  26. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  27. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  28. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  29. data/lib/active_record/associations/join_dependency.rb +91 -60
  30. data/lib/active_record/associations/preloader/association.rb +69 -43
  31. data/lib/active_record/associations/preloader/through_association.rb +49 -40
  32. data/lib/active_record/associations/preloader.rb +47 -34
  33. data/lib/active_record/associations/singular_association.rb +3 -17
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/associations.rb +137 -25
  36. data/lib/active_record/attribute_assignment.rb +17 -19
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
  38. data/lib/active_record/attribute_methods/dirty.rb +101 -40
  39. data/lib/active_record/attribute_methods/primary_key.rb +20 -25
  40. data/lib/active_record/attribute_methods/query.rb +4 -8
  41. data/lib/active_record/attribute_methods/read.rb +14 -56
  42. data/lib/active_record/attribute_methods/serialization.rb +12 -7
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +18 -34
  45. data/lib/active_record/attribute_methods.rb +81 -143
  46. data/lib/active_record/attributes.rb +46 -9
  47. data/lib/active_record/autosave_association.rb +57 -42
  48. data/lib/active_record/base.rb +4 -17
  49. data/lib/active_record/callbacks.rb +158 -43
  50. data/lib/active_record/coders/yaml_column.rb +1 -2
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +211 -90
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +167 -69
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -99
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
  64. data/lib/active_record/connection_adapters/column.rb +30 -12
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  67. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +88 -32
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +142 -19
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
  77. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -54
  81. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  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 +1 -2
  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 +3 -4
  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 +3 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  94. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  96. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
  99. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  100. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
  101. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  102. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
  104. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  105. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  106. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
  107. data/lib/active_record/connection_adapters/schema_cache.rb +159 -21
  108. data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
  109. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -0
  110. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  111. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  112. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
  113. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
  114. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  115. data/lib/active_record/connection_adapters.rb +52 -0
  116. data/lib/active_record/connection_handling.rb +293 -33
  117. data/lib/active_record/core.rb +333 -98
  118. data/lib/active_record/counter_cache.rb +8 -30
  119. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  120. data/lib/active_record/database_configurations/database_config.rb +80 -0
  121. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  122. data/lib/active_record/database_configurations/url_config.rb +53 -0
  123. data/lib/active_record/database_configurations.rb +273 -0
  124. data/lib/active_record/delegated_type.rb +209 -0
  125. data/lib/active_record/destroy_association_async_job.rb +36 -0
  126. data/lib/active_record/dynamic_matchers.rb +3 -4
  127. data/lib/active_record/enum.rb +108 -36
  128. data/lib/active_record/errors.rb +62 -19
  129. data/lib/active_record/explain.rb +10 -6
  130. data/lib/active_record/explain_subscriber.rb +1 -1
  131. data/lib/active_record/fixture_set/file.rb +10 -17
  132. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  133. data/lib/active_record/fixture_set/render_context.rb +17 -0
  134. data/lib/active_record/fixture_set/table_row.rb +152 -0
  135. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  136. data/lib/active_record/fixtures.rb +200 -481
  137. data/lib/active_record/gem_version.rb +3 -3
  138. data/lib/active_record/inheritance.rb +53 -24
  139. data/lib/active_record/insert_all.rb +212 -0
  140. data/lib/active_record/integration.rb +67 -17
  141. data/lib/active_record/internal_metadata.rb +28 -9
  142. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  143. data/lib/active_record/locking/optimistic.rb +37 -23
  144. data/lib/active_record/locking/pessimistic.rb +9 -5
  145. data/lib/active_record/log_subscriber.rb +35 -35
  146. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  147. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  148. data/lib/active_record/middleware/database_selector.rb +77 -0
  149. data/lib/active_record/migration/command_recorder.rb +96 -44
  150. data/lib/active_record/migration/compatibility.rb +145 -64
  151. data/lib/active_record/migration/join_table.rb +0 -1
  152. data/lib/active_record/migration.rb +206 -157
  153. data/lib/active_record/model_schema.rb +148 -22
  154. data/lib/active_record/nested_attributes.rb +4 -7
  155. data/lib/active_record/no_touching.rb +8 -1
  156. data/lib/active_record/null_relation.rb +0 -1
  157. data/lib/active_record/persistence.rb +267 -59
  158. data/lib/active_record/query_cache.rb +21 -4
  159. data/lib/active_record/querying.rb +40 -23
  160. data/lib/active_record/railtie.rb +116 -59
  161. data/lib/active_record/railties/console_sandbox.rb +2 -4
  162. data/lib/active_record/railties/controller_runtime.rb +30 -35
  163. data/lib/active_record/railties/databases.rake +411 -80
  164. data/lib/active_record/readonly_attributes.rb +4 -0
  165. data/lib/active_record/reflection.rb +109 -93
  166. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  167. data/lib/active_record/relation/batches.rb +44 -35
  168. data/lib/active_record/relation/calculations.rb +157 -90
  169. data/lib/active_record/relation/delegation.rb +35 -50
  170. data/lib/active_record/relation/finder_methods.rb +64 -39
  171. data/lib/active_record/relation/from_clause.rb +5 -1
  172. data/lib/active_record/relation/merger.rb +32 -40
  173. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  174. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  179. data/lib/active_record/relation/predicate_builder.rb +62 -45
  180. data/lib/active_record/relation/query_attribute.rb +13 -8
  181. data/lib/active_record/relation/query_methods.rb +476 -187
  182. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  183. data/lib/active_record/relation/spawn_methods.rb +9 -9
  184. data/lib/active_record/relation/where_clause.rb +115 -62
  185. data/lib/active_record/relation.rb +379 -115
  186. data/lib/active_record/result.rb +64 -38
  187. data/lib/active_record/runtime_registry.rb +2 -2
  188. data/lib/active_record/sanitization.rb +22 -41
  189. data/lib/active_record/schema.rb +2 -11
  190. data/lib/active_record/schema_dumper.rb +54 -9
  191. data/lib/active_record/schema_migration.rb +7 -9
  192. data/lib/active_record/scoping/default.rb +4 -8
  193. data/lib/active_record/scoping/named.rb +17 -24
  194. data/lib/active_record/scoping.rb +8 -9
  195. data/lib/active_record/secure_token.rb +16 -8
  196. data/lib/active_record/serialization.rb +5 -3
  197. data/lib/active_record/signed_id.rb +116 -0
  198. data/lib/active_record/statement_cache.rb +49 -6
  199. data/lib/active_record/store.rb +88 -9
  200. data/lib/active_record/suppressor.rb +2 -2
  201. data/lib/active_record/table_metadata.rb +42 -43
  202. data/lib/active_record/tasks/database_tasks.rb +277 -81
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  206. data/lib/active_record/test_databases.rb +24 -0
  207. data/lib/active_record/test_fixtures.rb +287 -0
  208. data/lib/active_record/timestamp.rb +43 -32
  209. data/lib/active_record/touch_later.rb +23 -22
  210. data/lib/active_record/transactions.rb +62 -118
  211. data/lib/active_record/translation.rb +1 -1
  212. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  213. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  214. data/lib/active_record/type/serialized.rb +6 -3
  215. data/lib/active_record/type/time.rb +10 -0
  216. data/lib/active_record/type/type_map.rb +0 -1
  217. data/lib/active_record/type/unsigned_integer.rb +0 -1
  218. data/lib/active_record/type.rb +10 -5
  219. data/lib/active_record/type_caster/connection.rb +15 -15
  220. data/lib/active_record/type_caster/map.rb +8 -8
  221. data/lib/active_record/validations/associated.rb +1 -2
  222. data/lib/active_record/validations/numericality.rb +35 -0
  223. data/lib/active_record/validations/uniqueness.rb +38 -30
  224. data/lib/active_record/validations.rb +4 -3
  225. data/lib/active_record.rb +13 -12
  226. data/lib/arel/alias_predication.rb +9 -0
  227. data/lib/arel/attributes/attribute.rb +41 -0
  228. data/lib/arel/collectors/bind.rb +29 -0
  229. data/lib/arel/collectors/composite.rb +39 -0
  230. data/lib/arel/collectors/plain_string.rb +20 -0
  231. data/lib/arel/collectors/sql_string.rb +27 -0
  232. data/lib/arel/collectors/substitute_binds.rb +35 -0
  233. data/lib/arel/crud.rb +42 -0
  234. data/lib/arel/delete_manager.rb +18 -0
  235. data/lib/arel/errors.rb +9 -0
  236. data/lib/arel/expressions.rb +29 -0
  237. data/lib/arel/factory_methods.rb +49 -0
  238. data/lib/arel/insert_manager.rb +49 -0
  239. data/lib/arel/math.rb +45 -0
  240. data/lib/arel/nodes/and.rb +32 -0
  241. data/lib/arel/nodes/ascending.rb +23 -0
  242. data/lib/arel/nodes/binary.rb +126 -0
  243. data/lib/arel/nodes/bind_param.rb +44 -0
  244. data/lib/arel/nodes/case.rb +55 -0
  245. data/lib/arel/nodes/casted.rb +62 -0
  246. data/lib/arel/nodes/comment.rb +29 -0
  247. data/lib/arel/nodes/count.rb +12 -0
  248. data/lib/arel/nodes/delete_statement.rb +45 -0
  249. data/lib/arel/nodes/descending.rb +23 -0
  250. data/lib/arel/nodes/equality.rb +15 -0
  251. data/lib/arel/nodes/extract.rb +24 -0
  252. data/lib/arel/nodes/false.rb +16 -0
  253. data/lib/arel/nodes/full_outer_join.rb +8 -0
  254. data/lib/arel/nodes/function.rb +44 -0
  255. data/lib/arel/nodes/grouping.rb +11 -0
  256. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  257. data/lib/arel/nodes/in.rb +15 -0
  258. data/lib/arel/nodes/infix_operation.rb +92 -0
  259. data/lib/arel/nodes/inner_join.rb +8 -0
  260. data/lib/arel/nodes/insert_statement.rb +37 -0
  261. data/lib/arel/nodes/join_source.rb +20 -0
  262. data/lib/arel/nodes/matches.rb +18 -0
  263. data/lib/arel/nodes/named_function.rb +23 -0
  264. data/lib/arel/nodes/node.rb +51 -0
  265. data/lib/arel/nodes/node_expression.rb +13 -0
  266. data/lib/arel/nodes/ordering.rb +27 -0
  267. data/lib/arel/nodes/outer_join.rb +8 -0
  268. data/lib/arel/nodes/over.rb +15 -0
  269. data/lib/arel/nodes/regexp.rb +16 -0
  270. data/lib/arel/nodes/right_outer_join.rb +8 -0
  271. data/lib/arel/nodes/select_core.rb +67 -0
  272. data/lib/arel/nodes/select_statement.rb +41 -0
  273. data/lib/arel/nodes/sql_literal.rb +19 -0
  274. data/lib/arel/nodes/string_join.rb +11 -0
  275. data/lib/arel/nodes/table_alias.rb +31 -0
  276. data/lib/arel/nodes/terminal.rb +16 -0
  277. data/lib/arel/nodes/true.rb +16 -0
  278. data/lib/arel/nodes/unary.rb +44 -0
  279. data/lib/arel/nodes/unary_operation.rb +20 -0
  280. data/lib/arel/nodes/unqualified_column.rb +22 -0
  281. data/lib/arel/nodes/update_statement.rb +41 -0
  282. data/lib/arel/nodes/values_list.rb +9 -0
  283. data/lib/arel/nodes/window.rb +126 -0
  284. data/lib/arel/nodes/with.rb +11 -0
  285. data/lib/arel/nodes.rb +70 -0
  286. data/lib/arel/order_predications.rb +13 -0
  287. data/lib/arel/predications.rb +250 -0
  288. data/lib/arel/select_manager.rb +270 -0
  289. data/lib/arel/table.rb +118 -0
  290. data/lib/arel/tree_manager.rb +72 -0
  291. data/lib/arel/update_manager.rb +34 -0
  292. data/lib/arel/visitors/dot.rb +308 -0
  293. data/lib/arel/visitors/mysql.rb +93 -0
  294. data/lib/arel/visitors/postgresql.rb +120 -0
  295. data/lib/arel/visitors/sqlite.rb +38 -0
  296. data/lib/arel/visitors/to_sql.rb +899 -0
  297. data/lib/arel/visitors/visitor.rb +45 -0
  298. data/lib/arel/visitors.rb +13 -0
  299. data/lib/arel/window_predications.rb +9 -0
  300. data/lib/arel.rb +54 -0
  301. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  302. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  303. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  304. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  305. data/lib/rails/generators/active_record/migration.rb +19 -2
  306. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  307. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  308. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  309. metadata +116 -30
  310. data/lib/active_record/attribute_decorators.rb +0 -90
  311. data/lib/active_record/collection_cache_key.rb +0 -53
  312. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  313. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  314. data/lib/active_record/define_callbacks.rb +0 -22
  315. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  316. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -11,8 +11,6 @@ require "active_record/connection_adapters/mysql/schema_dumper"
11
11
  require "active_record/connection_adapters/mysql/schema_statements"
12
12
  require "active_record/connection_adapters/mysql/type_metadata"
13
13
 
14
- require "active_support/core_ext/string/strip"
15
-
16
14
  module ActiveRecord
17
15
  module ConnectionAdapters
18
16
  class AbstractMysqlAdapter < AbstractAdapter
@@ -31,7 +29,7 @@ module ActiveRecord
31
29
  NATIVE_DATABASE_TYPES = {
32
30
  primary_key: "bigint auto_increment PRIMARY KEY",
33
31
  string: { name: "varchar", limit: 255 },
34
- text: { name: "text", limit: 65535 },
32
+ text: { name: "text" },
35
33
  integer: { name: "int", limit: 4 },
36
34
  float: { name: "float", limit: 24 },
37
35
  decimal: { name: "decimal" },
@@ -39,41 +37,43 @@ module ActiveRecord
39
37
  timestamp: { name: "timestamp" },
40
38
  time: { name: "time" },
41
39
  date: { name: "date" },
42
- binary: { name: "blob", limit: 65535 },
40
+ binary: { name: "blob" },
41
+ blob: { name: "blob" },
43
42
  boolean: { name: "tinyint", limit: 1 },
44
43
  json: { name: "json" },
45
44
  }
46
45
 
47
46
  class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
48
- private def dealloc(stmt)
49
- stmt[:stmt].close
50
- end
47
+ private
48
+ def dealloc(stmt)
49
+ stmt.close
50
+ end
51
51
  end
52
52
 
53
53
  def initialize(connection, logger, connection_options, config)
54
54
  super(connection, logger, config)
55
-
56
- @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
57
-
58
- if version < "5.1.10"
59
- raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.1.10."
60
- end
61
55
  end
62
56
 
63
- def version #:nodoc:
64
- @version ||= Version.new(version_string)
57
+ def get_database_version #:nodoc:
58
+ full_version_string = get_full_version
59
+ version_string = version_string(full_version_string)
60
+ Version.new(version_string, full_version_string)
65
61
  end
66
62
 
67
63
  def mariadb? # :nodoc:
68
64
  /mariadb/i.match?(full_version)
69
65
  end
70
66
 
71
- def supports_bulk_alter? #:nodoc:
67
+ def supports_bulk_alter?
72
68
  true
73
69
  end
74
70
 
75
71
  def supports_index_sort_order?
76
- !mariadb? && version >= "8.0.1"
72
+ !mariadb? && database_version >= "8.0.1"
73
+ end
74
+
75
+ def supports_expression_index?
76
+ !mariadb? && database_version >= "8.0.13"
77
77
  end
78
78
 
79
79
  def supports_transaction_isolation?
@@ -92,23 +92,36 @@ 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
98
106
 
99
107
  def supports_datetime_with_precision?
100
- if mariadb?
101
- version >= "5.3.0"
102
- else
103
- version >= "5.6.4"
104
- end
108
+ mariadb? || database_version >= "5.6.4"
105
109
  end
106
110
 
107
111
  def supports_virtual_columns?
112
+ mariadb? || database_version >= "5.7.5"
113
+ end
114
+
115
+ # See https://dev.mysql.com/doc/refman/en/optimizer-hints.html for more details.
116
+ def supports_optimizer_hints?
117
+ !mariadb? && database_version >= "5.7.7"
118
+ end
119
+
120
+ def supports_common_table_expressions?
108
121
  if mariadb?
109
- version >= "5.2.0"
122
+ database_version >= "10.2.1"
110
123
  else
111
- version >= "5.7.5"
124
+ database_version >= "8.0.1"
112
125
  end
113
126
  end
114
127
 
@@ -116,6 +129,14 @@ module ActiveRecord
116
129
  true
117
130
  end
118
131
 
132
+ def supports_insert_on_duplicate_skip?
133
+ true
134
+ end
135
+
136
+ def supports_insert_on_duplicate_update?
137
+ true
138
+ end
139
+
119
140
  def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
120
141
  query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
121
142
  end
@@ -129,7 +150,12 @@ module ActiveRecord
129
150
  end
130
151
 
131
152
  def index_algorithms
132
- { default: "ALGORITHM = DEFAULT".dup, copy: "ALGORITHM = COPY".dup, inplace: "ALGORITHM = INPLACE".dup }
153
+ {
154
+ default: "ALGORITHM = DEFAULT",
155
+ copy: "ALGORITHM = COPY",
156
+ inplace: "ALGORITHM = INPLACE",
157
+ instant: "ALGORITHM = INSTANT",
158
+ }
133
159
  end
134
160
 
135
161
  # HELPER METHODS ===========================================
@@ -161,27 +187,20 @@ module ActiveRecord
161
187
 
162
188
  # CONNECTION MANAGEMENT ====================================
163
189
 
164
- # Clears the prepared statements cache.
165
- def clear_cache!
190
+ def clear_cache! # :nodoc:
166
191
  reload_type_map
167
- @statements.clear
192
+ super
168
193
  end
169
194
 
170
195
  #--
171
196
  # DATABASE STATEMENTS ======================================
172
197
  #++
173
198
 
174
- def explain(arel, binds = [])
175
- sql = "EXPLAIN #{to_sql(arel, binds)}"
176
- start = Time.now
177
- result = exec_query(sql, "EXPLAIN", binds)
178
- elapsed = Time.now - start
179
-
180
- MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
181
- end
182
-
183
199
  # Executes the SQL statement in the context of this connection.
184
200
  def execute(sql, name = nil)
201
+ materialize_transactions
202
+ mark_transaction_written_if_write(sql)
203
+
185
204
  log(sql, name) do
186
205
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
187
206
  @connection.query(sql)
@@ -197,7 +216,7 @@ module ActiveRecord
197
216
  end
198
217
 
199
218
  def begin_db_transaction
200
- execute "BEGIN"
219
+ execute("BEGIN", "TRANSACTION")
201
220
  end
202
221
 
203
222
  def begin_isolated_db_transaction(isolation)
@@ -206,26 +225,14 @@ module ActiveRecord
206
225
  end
207
226
 
208
227
  def commit_db_transaction #:nodoc:
209
- execute "COMMIT"
228
+ execute("COMMIT", "TRANSACTION")
210
229
  end
211
230
 
212
231
  def exec_rollback_db_transaction #:nodoc:
213
- execute "ROLLBACK"
232
+ execute("ROLLBACK", "TRANSACTION")
214
233
  end
215
234
 
216
- # In the simple case, MySQL allows us to place JOINs directly into the UPDATE
217
- # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
218
- # these, we must use a subquery.
219
- def join_to_update(update, select, key) # :nodoc:
220
- if select.limit || select.offset || select.orders.any?
221
- super
222
- else
223
- update.table select.source
224
- update.wheres = select.constraints
225
- end
226
- end
227
-
228
- def empty_insert_statement_value
235
+ def empty_insert_statement_value(primary_key = nil)
229
236
  "VALUES ()"
230
237
  end
231
238
 
@@ -241,7 +248,7 @@ module ActiveRecord
241
248
  end
242
249
 
243
250
  # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
244
- # Charset defaults to utf8.
251
+ # Charset defaults to utf8mb4.
245
252
  #
246
253
  # Example:
247
254
  # create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
@@ -250,8 +257,12 @@ module ActiveRecord
250
257
  def create_database(name, options = {})
251
258
  if options[:collation]
252
259
  execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT COLLATE #{quote_table_name(options[:collation])}"
260
+ elsif options[:charset]
261
+ execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset])}"
262
+ elsif row_format_dynamic_by_default?
263
+ execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET `utf8mb4`"
253
264
  else
254
- execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8')}"
265
+ raise "Configure a supported :charset and ensure innodb_large_prefix is enabled to support indexes on varchar(255) string columns."
255
266
  end
256
267
  end
257
268
 
@@ -277,14 +288,10 @@ module ActiveRecord
277
288
  show_variable "collation_database"
278
289
  end
279
290
 
280
- def truncate(table_name, name = nil)
281
- execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
282
- end
283
-
284
291
  def table_comment(table_name) # :nodoc:
285
292
  scope = quoted_scope(table_name)
286
293
 
287
- query_value(<<-SQL.strip_heredoc, "SCHEMA").presence
294
+ query_value(<<~SQL, "SCHEMA").presence
288
295
  SELECT table_comment
289
296
  FROM information_schema.tables
290
297
  WHERE table_schema = #{scope[:schema]}
@@ -292,22 +299,8 @@ module ActiveRecord
292
299
  SQL
293
300
  end
294
301
 
295
- def bulk_change_table(table_name, operations) #:nodoc:
296
- sqls = operations.flat_map do |command, args|
297
- table, arguments = args.shift, args
298
- method = :"#{command}_for_alter"
299
-
300
- if respond_to?(method, true)
301
- send(method, table, *arguments)
302
- else
303
- raise "Unknown method called : #{method}(#{arguments.inspect})"
304
- end
305
- end.join(", ")
306
-
307
- execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
308
- end
309
-
310
- def change_table_comment(table_name, comment) #:nodoc:
302
+ def change_table_comment(table_name, comment_or_changes) # :nodoc:
303
+ comment = extract_new_comment_value(comment_or_changes)
311
304
  comment = "" if comment.nil?
312
305
  execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
313
306
  end
@@ -317,6 +310,8 @@ module ActiveRecord
317
310
  # Example:
318
311
  # rename_table('octopuses', 'octopi')
319
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)
320
315
  execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
321
316
  rename_table_indexes(table_name, new_name)
322
317
  end
@@ -336,7 +331,8 @@ module ActiveRecord
336
331
  # Although this command ignores most +options+ and the block if one is given,
337
332
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
338
333
  # In that case, +options+ and the block will be used by create_table.
339
- def drop_table(table_name, options = {})
334
+ def drop_table(table_name, **options)
335
+ schema_cache.clear_data_source_cache!(table_name.to_s)
340
336
  execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
341
337
  end
342
338
 
@@ -363,12 +359,13 @@ module ActiveRecord
363
359
  change_column table_name, column_name, nil, null: null
364
360
  end
365
361
 
366
- def change_column_comment(table_name, column_name, comment) #:nodoc:
362
+ def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
363
+ comment = extract_new_comment_value(comment_or_changes)
367
364
  change_column table_name, column_name, nil, comment: comment
368
365
  end
369
366
 
370
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
371
- 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)}")
372
369
  end
373
370
 
374
371
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
@@ -376,10 +373,13 @@ module ActiveRecord
376
373
  rename_column_indexes(table_name, column_name, new_column_name)
377
374
  end
378
375
 
379
- def add_index(table_name, column_name, options = {}) #:nodoc:
380
- index_name, index_type, index_columns, _, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
381
- sql = "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}".dup
382
- 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)
383
383
  end
384
384
 
385
385
  def add_sql_comment!(sql, comment) # :nodoc:
@@ -392,7 +392,7 @@ module ActiveRecord
392
392
 
393
393
  scope = quoted_scope(table_name)
394
394
 
395
- fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
395
+ fk_info = exec_query(<<~SQL, "SCHEMA")
396
396
  SELECT fk.referenced_table_name AS 'to_table',
397
397
  fk.referenced_column_name AS 'primary_key',
398
398
  fk.column_name AS 'column',
@@ -423,51 +423,63 @@ module ActiveRecord
423
423
  end
424
424
  end
425
425
 
426
- def table_options(table_name) # :nodoc:
427
- 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
428
453
 
454
+ def table_options(table_name) # :nodoc:
429
455
  create_table_info = create_table_info(table_name)
430
456
 
431
457
  # strip create_definitions and partition_options
432
- 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
433
470
 
434
471
  # strip AUTO_INCREMENT
435
472
  raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
436
473
 
437
- table_options[:options] = raw_table_options
438
-
439
474
  # strip COMMENT
440
475
  if raw_table_options.sub!(/ COMMENT='.+'/, "")
441
476
  table_options[:comment] = table_comment(table_name)
442
477
  end
443
478
 
479
+ table_options[:options] = raw_table_options unless raw_table_options == "ENGINE=InnoDB"
444
480
  table_options
445
481
  end
446
482
 
447
- # Maps logical Rails types to MySQL-specific data types.
448
- def type_to_sql(type, limit: nil, precision: nil, scale: nil, unsigned: nil, **) # :nodoc:
449
- sql = \
450
- case type.to_s
451
- when "integer"
452
- integer_to_sql(limit)
453
- when "text"
454
- text_to_sql(limit)
455
- when "blob"
456
- binary_to_sql(limit)
457
- when "binary"
458
- if (0..0xfff) === limit
459
- "varbinary(#{limit})"
460
- else
461
- binary_to_sql(limit)
462
- end
463
- else
464
- super
465
- end
466
-
467
- sql = "#{sql} unsigned" if unsigned && type != :primary_key
468
- sql
469
- end
470
-
471
483
  # SHOW VARIABLES LIKE 'name'
472
484
  def show_variable(name)
473
485
  query_value("SELECT @@#{name}", "SCHEMA")
@@ -480,19 +492,21 @@ module ActiveRecord
480
492
 
481
493
  scope = quoted_scope(table_name)
482
494
 
483
- query_values(<<-SQL.strip_heredoc, "SCHEMA")
495
+ query_values(<<~SQL, "SCHEMA")
484
496
  SELECT column_name
485
- FROM information_schema.key_column_usage
486
- WHERE constraint_name = 'PRIMARY'
497
+ FROM information_schema.statistics
498
+ WHERE index_name = 'PRIMARY'
487
499
  AND table_schema = #{scope[:schema]}
488
500
  AND table_name = #{scope[:name]}
489
- ORDER BY ordinal_position
501
+ ORDER BY seq_in_index
490
502
  SQL
491
503
  end
492
504
 
493
- def case_sensitive_comparison(table, attribute, column, value) # :nodoc:
505
+ def case_sensitive_comparison(attribute, value) # :nodoc:
506
+ column = column_for_attribute(attribute)
507
+
494
508
  if column.collation && !column.case_sensitive?
495
- table[attribute].eq(Arel::Nodes::Bin.new(value))
509
+ attribute.eq(Arel::Nodes::Bin.new(value))
496
510
  else
497
511
  super
498
512
  end
@@ -506,14 +520,14 @@ module ActiveRecord
506
520
  # In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
507
521
  # DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
508
522
  # distinct queries, and requires that the ORDER BY include the distinct column.
509
- # 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
510
524
  def columns_for_distinct(columns, orders) # :nodoc:
511
- order_columns = orders.reject(&:blank?).map { |s|
525
+ order_columns = orders.compact_blank.map { |s|
512
526
  # Convert Arel node to string
513
- s = s.to_sql unless s.is_a?(String)
527
+ s = visitor.compile(s) unless s.is_a?(String)
514
528
  # Remove any ASC/DESC modifiers
515
529
  s.gsub(/\s+(?:ASC|DESC)\b/i, "")
516
- }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
530
+ }.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
517
531
 
518
532
  (order_columns << super).join(", ")
519
533
  end
@@ -526,44 +540,35 @@ module ActiveRecord
526
540
  index.using == :btree || super
527
541
  end
528
542
 
529
- def insert_fixtures_set(fixture_set, tables_to_delete = [])
530
- with_multi_statements do
531
- super { discard_remaining_results }
532
- end
533
- end
543
+ def build_insert_sql(insert) # :nodoc:
544
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
534
545
 
535
- private
536
- def combine_multi_statements(total_sql)
537
- total_sql.each_with_object([]) do |sql, total_sql_chunks|
538
- previous_packet = total_sql_chunks.last
539
- sql << ";\n"
540
- if max_allowed_packet_reached?(sql, previous_packet) || total_sql_chunks.empty?
541
- total_sql_chunks << sql
542
- else
543
- previous_packet << sql
544
- end
545
- end
546
+ if insert.skip_duplicates?
547
+ no_op_column = quote_column_name(insert.keys.first)
548
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
549
+ elsif insert.update_duplicates?
550
+ sql << " ON DUPLICATE KEY UPDATE "
551
+ sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
552
+ sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
546
553
  end
547
554
 
548
- def max_allowed_packet_reached?(current_packet, previous_packet)
549
- if current_packet.bytesize > max_allowed_packet
550
- raise ActiveRecordError, "Fixtures set is too large #{current_packet.bytesize}. Consider increasing the max_allowed_packet variable."
551
- elsif previous_packet.nil?
552
- false
553
- else
554
- (current_packet.bytesize + previous_packet.bytesize) > max_allowed_packet
555
- end
556
- end
555
+ sql
556
+ end
557
557
 
558
- def max_allowed_packet
559
- bytes_margin = 2
560
- @max_allowed_packet ||= (show_variable("max_allowed_packet") - bytes_margin)
558
+ def check_version # :nodoc:
559
+ if database_version < "5.5.8"
560
+ raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
561
561
  end
562
+ end
562
563
 
564
+ private
563
565
  def initialize_type_map(m = type_map)
564
566
  super
565
567
 
566
- 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
567
572
 
568
573
  m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
569
574
  m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
@@ -583,28 +588,19 @@ module ActiveRecord
583
588
  register_integer_type m, %r(^tinyint)i, limit: 1
584
589
 
585
590
  m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
586
- m.alias_type %r(year)i, "integer"
587
- m.alias_type %r(bit)i, "binary"
588
-
589
- m.register_type(%r(enum)i) do |sql_type|
590
- limit = sql_type[/^enum\((.+)\)/i, 1]
591
- .split(",").map { |enum| enum.strip.length - 2 }.max
592
- MysqlString.new(limit: limit)
593
- end
591
+ m.alias_type %r(year)i, "integer"
592
+ m.alias_type %r(bit)i, "binary"
594
593
 
595
- m.register_type(%r(^set)i) do |sql_type|
596
- limit = sql_type[/^set\((.+)\)/i, 1]
597
- .split(",").map { |set| set.strip.length - 1 }.sum - 1
598
- MysqlString.new(limit: limit)
599
- 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)
600
596
  end
601
597
 
602
- def register_integer_type(mapping, key, options)
598
+ def register_integer_type(mapping, key, **options)
603
599
  mapping.register_type(key) do |sql_type|
604
600
  if /\bunsigned\b/.match?(sql_type)
605
- Type::UnsignedInteger.new(options)
601
+ Type::UnsignedInteger.new(**options)
606
602
  else
607
- Type::Integer.new(options)
603
+ Type::Integer.new(**options)
608
604
  end
609
605
  end
610
606
  end
@@ -617,10 +613,15 @@ module ActiveRecord
617
613
  end
618
614
  end
619
615
 
620
- # 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
621
619
  ER_DUP_ENTRY = 1062
622
620
  ER_NOT_NULL_VIOLATION = 1048
621
+ ER_NO_REFERENCED_ROW = 1216
622
+ ER_ROW_IS_REFERENCED = 1217
623
623
  ER_DO_NOT_HAVE_DEFAULT = 1364
624
+ ER_ROW_IS_REFERENCED_2 = 1451
624
625
  ER_NO_REFERENCED_ROW_2 = 1452
625
626
  ER_DATA_TOO_LONG = 1406
626
627
  ER_OUT_OF_RANGE = 1264
@@ -630,41 +631,50 @@ module ActiveRecord
630
631
  ER_LOCK_WAIT_TIMEOUT = 1205
631
632
  ER_QUERY_INTERRUPTED = 1317
632
633
  ER_QUERY_TIMEOUT = 3024
634
+ ER_FK_INCOMPATIBLE_COLUMNS = 3780
633
635
 
634
- def translate_exception(exception, message)
636
+ def translate_exception(exception, message:, sql:, binds:)
635
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)
636
646
  when ER_DUP_ENTRY
637
- RecordNotUnique.new(message)
638
- when ER_NO_REFERENCED_ROW_2
639
- InvalidForeignKey.new(message)
640
- when ER_CANNOT_ADD_FOREIGN
641
- mismatched_foreign_key(message)
647
+ RecordNotUnique.new(message, sql: sql, binds: binds)
648
+ when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
649
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
650
+ when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
651
+ mismatched_foreign_key(message, sql: sql, binds: binds)
642
652
  when ER_CANNOT_CREATE_TABLE
643
653
  if message.include?("errno: 150")
644
- mismatched_foreign_key(message)
654
+ mismatched_foreign_key(message, sql: sql, binds: binds)
645
655
  else
646
656
  super
647
657
  end
648
658
  when ER_DATA_TOO_LONG
649
- ValueTooLong.new(message)
659
+ ValueTooLong.new(message, sql: sql, binds: binds)
650
660
  when ER_OUT_OF_RANGE
651
- RangeError.new(message)
661
+ RangeError.new(message, sql: sql, binds: binds)
652
662
  when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
653
- NotNullViolation.new(message)
663
+ NotNullViolation.new(message, sql: sql, binds: binds)
654
664
  when ER_LOCK_DEADLOCK
655
- Deadlocked.new(message)
665
+ Deadlocked.new(message, sql: sql, binds: binds)
656
666
  when ER_LOCK_WAIT_TIMEOUT
657
- LockWaitTimeout.new(message)
658
- when ER_QUERY_TIMEOUT
659
- StatementTimeout.new(message)
667
+ LockWaitTimeout.new(message, sql: sql, binds: binds)
668
+ when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
669
+ StatementTimeout.new(message, sql: sql, binds: binds)
660
670
  when ER_QUERY_INTERRUPTED
661
- QueryCanceled.new(message)
671
+ QueryCanceled.new(message, sql: sql, binds: binds)
662
672
  else
663
673
  super
664
674
  end
665
675
  end
666
676
 
667
- def change_column_for_alter(table_name, column_name, type, options = {})
677
+ def change_column_for_alter(table_name, column_name, type, **options)
668
678
  column = column_for(table_name, column_name)
669
679
  type ||= column.sql_type
670
680
 
@@ -681,59 +691,53 @@ module ActiveRecord
681
691
  end
682
692
 
683
693
  td = create_table_definition(table_name)
684
- cd = td.new_column_definition(column.name, type, options)
694
+ cd = td.new_column_definition(column.name, type, **options)
685
695
  schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
686
696
  end
687
697
 
688
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
+
689
701
  column = column_for(table_name, column_name)
690
702
  options = {
691
703
  default: column.default,
692
704
  null: column.null,
693
- auto_increment: column.auto_increment?
705
+ auto_increment: column.auto_increment?,
706
+ comment: column.comment
694
707
  }
695
708
 
696
709
  current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
697
710
  td = create_table_definition(table_name)
698
- cd = td.new_column_definition(new_column_name, current_type, options)
711
+ cd = td.new_column_definition(new_column_name, current_type, **options)
699
712
  schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
700
713
  end
701
714
 
702
- def add_index_for_alter(table_name, column_name, options = {})
703
- index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
704
- index_algorithm[0, 0] = ", " if index_algorithm.present?
705
- "ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
706
- end
707
-
708
- def remove_index_for_alter(table_name, options = {})
709
- index_name = index_name_for_remove(table_name, options)
710
- "DROP INDEX #{quote_column_name(index_name)}"
711
- end
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
712
718
 
713
- def add_timestamps_for_alter(table_name, options = {})
714
- [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
719
+ "ADD #{schema_creation.accept(index)}#{algorithm}"
715
720
  end
716
721
 
717
- def remove_timestamps_for_alter(table_name, options = {})
718
- [remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
722
+ def remove_index_for_alter(table_name, column_name = nil, **options)
723
+ index_name = index_name_for_remove(table_name, column_name, options)
724
+ "DROP INDEX #{quote_column_name(index_name)}"
719
725
  end
720
726
 
721
- # MySQL is too stupid to create a temporary table for use subquery, so we have
722
- # to give it some prompting in the form of a subsubquery. Ugh!
723
- def subquery_for(key, select)
724
- subselect = select.clone
725
- subselect.projections = [key]
726
-
727
- # Materialize subquery by adding distinct
728
- # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
729
- subselect.distinct unless select.limit || select.offset || select.orders.any?
730
-
731
- key_name = quote_column_name(key.name)
732
- Arel::SelectManager.new(subselect.as("__active_record_temp")).project(Arel.sql(key_name))
727
+ def supports_rename_index?
728
+ if mariadb?
729
+ database_version >= "10.5.2"
730
+ else
731
+ database_version >= "5.7.6"
732
+ end
733
733
  end
734
734
 
735
- def supports_rename_index?
736
- mariadb? ? false : 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
737
741
  end
738
742
 
739
743
  def configure_connection
@@ -750,7 +754,7 @@ module ActiveRecord
750
754
  defaults = [":default", :default].to_set
751
755
 
752
756
  # Make MySQL reject illegal values rather than truncating or blanking them, see
753
- # 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
754
758
  # If the user has provided another value for sql_mode, don't replace it.
755
759
  if sql_mode = variables.delete("sql_mode")
756
760
  sql_mode = quote(sql_mode)
@@ -767,10 +771,10 @@ module ActiveRecord
767
771
  sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
768
772
 
769
773
  # NAMES does not have an equals sign, see
770
- # https://dev.mysql.com/doc/refman/5.7/en/set-names.html
774
+ # https://dev.mysql.com/doc/refman/en/set-names.html
771
775
  # (trailing comma because variable_assignments will always have content)
772
776
  if @config[:encoding]
773
- encoding = "NAMES #{@config[:encoding]}".dup
777
+ encoding = +"NAMES #{@config[:encoding]}"
774
778
  encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
775
779
  encoding << ", "
776
780
  end
@@ -786,7 +790,7 @@ module ActiveRecord
786
790
  end.compact.join(", ")
787
791
 
788
792
  # ...and send them all in one query
789
- execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
793
+ execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
790
794
  end
791
795
 
792
796
  def column_definitions(table_name) # :nodoc:
@@ -803,15 +807,21 @@ module ActiveRecord
803
807
  Arel::Visitors::MySQL.new(self)
804
808
  end
805
809
 
806
- def mismatched_foreign_key(message)
810
+ def build_statement_pool
811
+ StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
812
+ end
813
+
814
+ def mismatched_foreign_key(message, sql:, binds:)
807
815
  match = %r/
808
816
  (?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
809
817
  FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
810
818
  REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
811
- /xmi.match(message)
819
+ /xmi.match(sql)
812
820
 
813
821
  options = {
814
822
  message: message,
823
+ sql: sql,
824
+ binds: binds,
815
825
  }
816
826
 
817
827
  if match
@@ -822,65 +832,23 @@ module ActiveRecord
822
832
  options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
823
833
  end
824
834
 
825
- MismatchedForeignKey.new(options)
835
+ MismatchedForeignKey.new(**options)
826
836
  end
827
837
 
828
- def integer_to_sql(limit) # :nodoc:
829
- case limit
830
- when 1; "tinyint"
831
- when 2; "smallint"
832
- when 3; "mediumint"
833
- when nil, 4; "int"
834
- when 5..8; "bigint"
835
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead.")
836
- end
838
+ def version_string(full_version_string)
839
+ full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
837
840
  end
838
841
 
839
- def text_to_sql(limit) # :nodoc:
840
- case limit
841
- when 0..0xff; "tinytext"
842
- when nil, 0x100..0xffff; "text"
843
- when 0x10000..0xffffff; "mediumtext"
844
- when 0x1000000..0xffffffff; "longtext"
845
- else raise(ActiveRecordError, "No text type has byte length #{limit}")
846
- end
847
- end
848
-
849
- def binary_to_sql(limit) # :nodoc:
850
- case limit
851
- when 0..0xff; "tinyblob"
852
- when nil, 0x100..0xffff; "blob"
853
- when 0x10000..0xffffff; "mediumblob"
854
- when 0x1000000..0xffffffff; "longblob"
855
- else raise(ActiveRecordError, "No binary type has byte length #{limit}")
856
- end
857
- 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:
858
845
 
859
- def version_string
860
- full_version.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
846
+ ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
847
+ Type::ImmutableString.new(true: "1", false: "0", **args)
861
848
  end
862
-
863
- class MysqlString < Type::String # :nodoc:
864
- def serialize(value)
865
- case value
866
- when true then "1"
867
- when false then "0"
868
- else super
869
- end
870
- end
871
-
872
- private
873
-
874
- def cast_value(value)
875
- case value
876
- when true then "1"
877
- when false then "0"
878
- else super
879
- end
880
- end
849
+ ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
850
+ Type::String.new(true: "1", false: "0", **args)
881
851
  end
882
-
883
- ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
884
852
  ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
885
853
  end
886
854
  end