activerecord 4.2.11.3 → 6.0.2.2

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 (372) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +675 -1587
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +13 -12
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record.rb +41 -22
  8. data/lib/active_record/aggregations.rb +267 -251
  9. data/lib/active_record/association_relation.rb +26 -12
  10. data/lib/active_record/associations.rb +1737 -1597
  11. data/lib/active_record/associations/alias_tracker.rb +29 -35
  12. data/lib/active_record/associations/association.rb +133 -58
  13. data/lib/active_record/associations/association_scope.rb +103 -132
  14. data/lib/active_record/associations/belongs_to_association.rb +65 -60
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -40
  17. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  18. data/lib/active_record/associations/builder/collection_association.rb +10 -33
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +52 -66
  20. data/lib/active_record/associations/builder/has_many.rb +8 -4
  21. data/lib/active_record/associations/builder/has_one.rb +46 -5
  22. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  23. data/lib/active_record/associations/collection_association.rb +136 -288
  24. data/lib/active_record/associations/collection_proxy.rb +241 -146
  25. data/lib/active_record/associations/foreign_association.rb +10 -1
  26. data/lib/active_record/associations/has_many_association.rb +34 -97
  27. data/lib/active_record/associations/has_many_through_association.rb +60 -87
  28. data/lib/active_record/associations/has_one_association.rb +61 -49
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +141 -167
  31. data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
  32. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  33. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  34. data/lib/active_record/associations/preloader.rb +90 -92
  35. data/lib/active_record/associations/preloader/association.rb +90 -123
  36. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  37. data/lib/active_record/associations/singular_association.rb +18 -39
  38. data/lib/active_record/associations/through_association.rb +38 -18
  39. data/lib/active_record/attribute_assignment.rb +56 -183
  40. data/lib/active_record/attribute_decorators.rb +39 -15
  41. data/lib/active_record/attribute_methods.rb +120 -135
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -8
  43. data/lib/active_record/attribute_methods/dirty.rb +174 -144
  44. data/lib/active_record/attribute_methods/primary_key.rb +91 -83
  45. data/lib/active_record/attribute_methods/query.rb +6 -5
  46. data/lib/active_record/attribute_methods/read.rb +20 -76
  47. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  49. data/lib/active_record/attribute_methods/write.rb +32 -54
  50. data/lib/active_record/attributes.rb +214 -82
  51. data/lib/active_record/autosave_association.rb +96 -38
  52. data/lib/active_record/base.rb +57 -45
  53. data/lib/active_record/callbacks.rb +100 -74
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +24 -12
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +806 -296
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +238 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -23
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +356 -227
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +664 -243
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -83
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +469 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +517 -633
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +202 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -180
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +66 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  100. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +470 -290
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +555 -356
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -345
  127. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  128. data/lib/active_record/connection_handling.rb +183 -41
  129. data/lib/active_record/core.rb +253 -229
  130. data/lib/active_record/counter_cache.rb +67 -49
  131. data/lib/active_record/database_configurations.rb +233 -0
  132. data/lib/active_record/database_configurations/database_config.rb +37 -0
  133. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  134. data/lib/active_record/database_configurations/url_config.rb +79 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -105
  137. data/lib/active_record/enum.rb +163 -86
  138. data/lib/active_record/errors.rb +188 -53
  139. data/lib/active_record/explain.rb +23 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +10 -5
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +153 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  147. data/lib/active_record/fixtures.rb +228 -499
  148. data/lib/active_record/gem_version.rb +6 -4
  149. data/lib/active_record/inheritance.rb +158 -112
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +87 -96
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +621 -303
  162. data/lib/active_record/migration/command_recorder.rb +177 -90
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +8 -6
  165. data/lib/active_record/model_schema.rb +315 -112
  166. data/lib/active_record/nested_attributes.rb +264 -222
  167. data/lib/active_record/no_touching.rb +14 -1
  168. data/lib/active_record/null_relation.rb +24 -37
  169. data/lib/active_record/persistence.rb +557 -125
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +43 -29
  172. data/lib/active_record/railtie.rb +143 -44
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +331 -185
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +428 -279
  179. data/lib/active_record/relation.rb +519 -341
  180. data/lib/active_record/relation/batches.rb +207 -55
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +267 -253
  183. data/lib/active_record/relation/delegation.rb +70 -80
  184. data/lib/active_record/relation/finder_methods.rb +286 -241
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +78 -87
  187. data/lib/active_record/relation/predicate_builder.rb +114 -119
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +597 -393
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +11 -13
  199. data/lib/active_record/relation/where_clause.rb +190 -0
  200. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  201. data/lib/active_record/result.rb +79 -42
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -17
  207. data/lib/active_record/scoping.rb +45 -26
  208. data/lib/active_record/scoping/default.rb +101 -85
  209. data/lib/active_record/scoping/named.rb +87 -33
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +75 -0
  216. data/lib/active_record/tasks/database_tasks.rb +307 -100
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +55 -99
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -41
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +225 -0
  222. data/lib/active_record/timestamp.rb +86 -40
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +217 -151
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type.rb +78 -23
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +4 -45
  229. data/lib/active_record/type/date_time.rb +4 -49
  230. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  231. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  232. data/lib/active_record/type/internal/timezone.rb +17 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +24 -15
  235. data/lib/active_record/type/text.rb +2 -2
  236. data/lib/active_record/type/time.rb +11 -16
  237. data/lib/active_record/type/type_map.rb +15 -17
  238. data/lib/active_record/type/unsigned_integer.rb +9 -7
  239. data/lib/active_record/type_caster.rb +9 -0
  240. data/lib/active_record/type_caster/connection.rb +34 -0
  241. data/lib/active_record/type_caster/map.rb +20 -0
  242. data/lib/active_record/validations.rb +39 -35
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +13 -4
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +14 -13
  247. data/lib/active_record/validations/uniqueness.rb +42 -55
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/arel.rb +58 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +257 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +204 -0
  317. data/lib/arel/visitors/dot.rb +297 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +157 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +159 -0
  323. data/lib/arel/visitors/oracle12.rb +66 -0
  324. data/lib/arel/visitors/postgresql.rb +110 -0
  325. data/lib/arel/visitors/sqlite.rb +39 -0
  326. data/lib/arel/visitors/to_sql.rb +889 -0
  327. data/lib/arel/visitors/visitor.rb +46 -0
  328. data/lib/arel/visitors/where_sql.rb +23 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +7 -5
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration.rb +31 -1
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  335. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  336. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
  337. data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  340. metadata +166 -58
  341. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  342. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  343. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  344. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  345. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  346. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  347. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  348. data/lib/active_record/attribute.rb +0 -163
  349. data/lib/active_record/attribute_set.rb +0 -81
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  352. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  353. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  354. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  355. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  356. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  359. data/lib/active_record/type/big_integer.rb +0 -13
  360. data/lib/active_record/type/binary.rb +0 -50
  361. data/lib/active_record/type/boolean.rb +0 -31
  362. data/lib/active_record/type/decimal.rb +0 -64
  363. data/lib/active_record/type/decorator.rb +0 -14
  364. data/lib/active_record/type/float.rb +0 -19
  365. data/lib/active_record/type/integer.rb +0 -59
  366. data/lib/active_record/type/mutable.rb +0 -16
  367. data/lib/active_record/type/numeric.rb +0 -36
  368. data/lib/active_record/type/string.rb +0 -40
  369. data/lib/active_record/type/time_value.rb +0 -38
  370. data/lib/active_record/type/value.rb +0 -110
  371. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  372. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,209 +1,88 @@
1
- require 'arel/visitors/bind_visitor'
2
- require 'active_support/core_ext/string/strip'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/connection_adapters/abstract_adapter"
4
+ require "active_record/connection_adapters/statement_pool"
5
+ require "active_record/connection_adapters/mysql/column"
6
+ require "active_record/connection_adapters/mysql/explain_pretty_printer"
7
+ require "active_record/connection_adapters/mysql/quoting"
8
+ require "active_record/connection_adapters/mysql/schema_creation"
9
+ require "active_record/connection_adapters/mysql/schema_definitions"
10
+ require "active_record/connection_adapters/mysql/schema_dumper"
11
+ require "active_record/connection_adapters/mysql/schema_statements"
12
+ require "active_record/connection_adapters/mysql/type_metadata"
3
13
 
4
14
  module ActiveRecord
5
15
  module ConnectionAdapters
6
16
  class AbstractMysqlAdapter < AbstractAdapter
7
- include Savepoints
8
-
9
- class SchemaCreation < AbstractAdapter::SchemaCreation
10
- def visit_AddColumn(o)
11
- add_column_position!(super, column_options(o))
12
- end
13
-
14
- private
15
-
16
- def visit_DropForeignKey(name)
17
- "DROP FOREIGN KEY #{name}"
18
- end
19
-
20
- def visit_TableDefinition(o)
21
- name = o.name
22
- create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(name)} "
23
-
24
- statements = o.columns.map { |c| accept c }
25
- statements.concat(o.indexes.map { |column_name, options| index_in_create(name, column_name, options) })
26
-
27
- create_sql << "(#{statements.join(', ')}) " if statements.present?
28
- create_sql << "#{o.options}"
29
- create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
30
- create_sql
31
- end
32
-
33
- def visit_ChangeColumnDefinition(o)
34
- column = o.column
35
- options = o.options
36
- sql_type = type_to_sql(o.type, options[:limit], options[:precision], options[:scale])
37
- change_column_sql = "CHANGE #{quote_column_name(column.name)} #{quote_column_name(options[:name])} #{sql_type}"
38
- add_column_options!(change_column_sql, options.merge(column: column))
39
- add_column_position!(change_column_sql, options)
40
- end
41
-
42
- def add_column_position!(sql, options)
43
- if options[:first]
44
- sql << " FIRST"
45
- elsif options[:after]
46
- sql << " AFTER #{quote_column_name(options[:after])}"
47
- end
48
- sql
49
- end
50
-
51
- def index_in_create(table_name, column_name, options)
52
- index_name, index_type, index_columns, index_options, index_algorithm, index_using = @conn.add_index_options(table_name, column_name, options)
53
- "#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_options} #{index_algorithm}"
54
- end
55
- end
56
-
57
- def schema_creation
58
- SchemaCreation.new self
59
- end
60
-
61
- def prepare_column_options(column, types) # :nodoc:
62
- spec = super
63
- spec.delete(:limit) if :boolean === column.type
64
- spec
65
- end
66
-
67
- class Column < ConnectionAdapters::Column # :nodoc:
68
- attr_reader :collation, :strict, :extra
69
-
70
- def initialize(name, default, cast_type, sql_type = nil, null = true, collation = nil, strict = false, extra = "")
71
- @strict = strict
72
- @collation = collation
73
- @extra = extra
74
- super(name, default, cast_type, sql_type, null)
75
- assert_valid_default(default)
76
- extract_default
77
- end
78
-
79
- def extract_default
80
- if blob_or_text_column?
81
- @default = null || strict ? nil : ''
82
- elsif missing_default_forged_as_empty_string?(@default)
83
- @default = nil
84
- end
85
- end
86
-
87
- def has_default?
88
- return false if blob_or_text_column? # MySQL forbids defaults on blob and text columns
89
- super
90
- end
91
-
92
- def blob_or_text_column?
93
- sql_type =~ /blob/i || type == :text
94
- end
95
-
96
- def case_sensitive?
97
- collation && !collation.match(/_ci$/)
98
- end
99
-
100
- def ==(other)
101
- super &&
102
- collation == other.collation &&
103
- strict == other.strict &&
104
- extra == other.extra
105
- end
106
-
107
- private
108
-
109
- # MySQL misreports NOT NULL column default when none is given.
110
- # We can't detect this for columns which may have a legitimate ''
111
- # default (string) but we can for others (integer, datetime, boolean,
112
- # and the rest).
113
- #
114
- # Test whether the column has default '', is not null, and is not
115
- # a type allowing default ''.
116
- def missing_default_forged_as_empty_string?(default)
117
- type != :string && !null && default == ''
118
- end
119
-
120
- def assert_valid_default(default)
121
- if blob_or_text_column? && default.present?
122
- raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
123
- end
124
- end
125
-
126
- def attributes_for_hash
127
- super + [collation, strict, extra]
128
- end
129
- end
17
+ include MySQL::Quoting
18
+ include MySQL::SchemaStatements
130
19
 
131
20
  ##
132
21
  # :singleton-method:
133
- # By default, the MysqlAdapter will consider all columns of type <tt>tinyint(1)</tt>
134
- # as boolean. If you wish to disable this emulation (which was the default
135
- # behavior in versions 0.13.1 and earlier) you can add the following line
22
+ # By default, the Mysql2Adapter will consider all columns of type <tt>tinyint(1)</tt>
23
+ # as boolean. If you wish to disable this emulation you can add the following line
136
24
  # to your application.rb file:
137
25
  #
138
- # ActiveRecord::ConnectionAdapters::Mysql[2]Adapter.emulate_booleans = false
139
- class_attribute :emulate_booleans
140
- self.emulate_booleans = true
141
-
142
- LOST_CONNECTION_ERROR_MESSAGES = [
143
- "Server shutdown in progress",
144
- "Broken pipe",
145
- "Lost connection to MySQL server during query",
146
- "MySQL server has gone away" ]
147
-
148
- QUOTED_TRUE, QUOTED_FALSE = '1', '0'
26
+ # ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans = false
27
+ class_attribute :emulate_booleans, default: true
149
28
 
150
29
  NATIVE_DATABASE_TYPES = {
151
- :primary_key => "int(11) auto_increment PRIMARY KEY",
152
- :string => { :name => "varchar", :limit => 255 },
153
- :text => { :name => "text" },
154
- :integer => { :name => "int", :limit => 4 },
155
- :float => { :name => "float" },
156
- :decimal => { :name => "decimal" },
157
- :datetime => { :name => "datetime" },
158
- :time => { :name => "time" },
159
- :date => { :name => "date" },
160
- :binary => { :name => "blob" },
161
- :boolean => { :name => "tinyint", :limit => 1 }
30
+ primary_key: "bigint auto_increment PRIMARY KEY",
31
+ string: { name: "varchar", limit: 255 },
32
+ text: { name: "text" },
33
+ integer: { name: "int", limit: 4 },
34
+ float: { name: "float", limit: 24 },
35
+ decimal: { name: "decimal" },
36
+ datetime: { name: "datetime" },
37
+ timestamp: { name: "timestamp" },
38
+ time: { name: "time" },
39
+ date: { name: "date" },
40
+ binary: { name: "blob" },
41
+ blob: { name: "blob" },
42
+ boolean: { name: "tinyint", limit: 1 },
43
+ json: { name: "json" },
162
44
  }
163
45
 
164
- INDEX_TYPES = [:fulltext, :spatial]
165
- INDEX_USINGS = [:btree, :hash]
46
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
47
+ private
48
+
49
+ def dealloc(stmt)
50
+ stmt.close
51
+ end
52
+ end
166
53
 
167
- # FIXME: Make the first parameter more similar for the two adapters
168
54
  def initialize(connection, logger, connection_options, config)
169
- super(connection, logger)
170
- @connection_options, @config = connection_options, config
171
- @quoted_column_names, @quoted_table_names = {}, {}
55
+ super(connection, logger, config)
56
+ end
172
57
 
173
- @visitor = Arel::Visitors::MySQL.new self
58
+ def get_database_version #:nodoc:
59
+ full_version_string = get_full_version
60
+ version_string = version_string(full_version_string)
61
+ Version.new(version_string, full_version_string)
62
+ end
174
63
 
175
- if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
176
- @prepared_statements = true
177
- else
178
- @prepared_statements = false
179
- end
64
+ def mariadb? # :nodoc:
65
+ /mariadb/i.match?(full_version)
180
66
  end
181
67
 
182
- # Returns true, since this connection adapter supports migrations.
183
- def supports_migrations?
68
+ def supports_bulk_alter?
184
69
  true
185
70
  end
186
71
 
187
- def supports_primary_key?
188
- true
72
+ def supports_index_sort_order?
73
+ !mariadb? && database_version >= "8.0.1"
189
74
  end
190
75
 
191
- def supports_bulk_alter? #:nodoc:
192
- true
76
+ def supports_expression_index?
77
+ !mariadb? && database_version >= "8.0.13"
193
78
  end
194
79
 
195
- # Technically MySQL allows to create indexes with the sort order syntax
196
- # but at the moment (5.5) it doesn't yet implement them
197
- def supports_index_sort_order?
80
+ def supports_transaction_isolation?
198
81
  true
199
82
  end
200
83
 
201
- # MySQL 4 technically support transaction isolation, but it is affected by a bug
202
- # where the transaction level gets persisted for the whole session:
203
- #
204
- # http://bugs.mysql.com/bug.php?id=39170
205
- def supports_transaction_isolation?
206
- version >= '5.0.0'
84
+ def supports_explain?
85
+ true
207
86
  end
208
87
 
209
88
  def supports_indexes_in_create?
@@ -215,85 +94,76 @@ module ActiveRecord
215
94
  end
216
95
 
217
96
  def supports_views?
218
- version >= '5.0.0'
97
+ true
219
98
  end
220
99
 
221
100
  def supports_datetime_with_precision?
222
- version >= '5.6.4'
101
+ mariadb? || database_version >= "5.6.4"
223
102
  end
224
103
 
225
- def native_database_types
226
- NATIVE_DATABASE_TYPES
104
+ def supports_virtual_columns?
105
+ mariadb? || database_version >= "5.7.5"
227
106
  end
228
107
 
229
- def index_algorithms
230
- { default: 'ALGORITHM = DEFAULT', copy: 'ALGORITHM = COPY', inplace: 'ALGORITHM = INPLACE' }
108
+ # See https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html for more details.
109
+ def supports_optimizer_hints?
110
+ !mariadb? && database_version >= "5.7.7"
231
111
  end
232
112
 
233
- # HELPER METHODS ===========================================
234
-
235
- # The two drivers have slightly different ways of yielding hashes of results, so
236
- # this method must be implemented to provide a uniform interface.
237
- def each_hash(result) # :nodoc:
238
- raise NotImplementedError
113
+ def supports_common_table_expressions?
114
+ if mariadb?
115
+ database_version >= "10.2.1"
116
+ else
117
+ database_version >= "8.0.1"
118
+ end
239
119
  end
240
120
 
241
- def new_column(field, default, cast_type, sql_type = nil, null = true, collation = "", extra = "") # :nodoc:
242
- Column.new(field, default, cast_type, sql_type, null, collation, strict_mode?, extra)
121
+ def supports_advisory_locks?
122
+ true
243
123
  end
244
124
 
245
- # Must return the MySQL error number from the exception, if the exception has an
246
- # error number.
247
- def error_number(exception) # :nodoc:
248
- raise NotImplementedError
125
+ def supports_insert_on_duplicate_skip?
126
+ true
249
127
  end
250
128
 
251
- # QUOTING ==================================================
252
-
253
- def _quote(value) # :nodoc:
254
- if value.is_a?(Type::Binary::Data)
255
- "x'#{value.hex}'"
256
- else
257
- super
258
- end
129
+ def supports_insert_on_duplicate_update?
130
+ true
259
131
  end
260
132
 
261
- def quote_column_name(name) #:nodoc:
262
- @quoted_column_names[name] ||= "`#{name.to_s.gsub('`', '``')}`"
133
+ def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
134
+ query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
263
135
  end
264
136
 
265
- def quote_table_name(name) #:nodoc:
266
- @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
137
+ def release_advisory_lock(lock_name) # :nodoc:
138
+ query_value("SELECT RELEASE_LOCK(#{quote(lock_name.to_s)})") == 1
267
139
  end
268
140
 
269
- def quoted_true
270
- QUOTED_TRUE
141
+ def native_database_types
142
+ NATIVE_DATABASE_TYPES
271
143
  end
272
144
 
273
- def unquoted_true
274
- 1
145
+ def index_algorithms
146
+ { default: +"ALGORITHM = DEFAULT", copy: +"ALGORITHM = COPY", inplace: +"ALGORITHM = INPLACE" }
275
147
  end
276
148
 
277
- def quoted_false
278
- QUOTED_FALSE
279
- end
149
+ # HELPER METHODS ===========================================
280
150
 
281
- def unquoted_false
282
- 0
151
+ # The two drivers have slightly different ways of yielding hashes of results, so
152
+ # this method must be implemented to provide a uniform interface.
153
+ def each_hash(result) # :nodoc:
154
+ raise NotImplementedError
283
155
  end
284
156
 
285
- def quoted_date(value)
286
- if supports_datetime_with_precision? && value.acts_like?(:time) && value.respond_to?(:usec)
287
- "#{super}.#{sprintf("%06d", value.usec)}"
288
- else
289
- super
290
- end
157
+ # Must return the MySQL error number from the exception, if the exception has an
158
+ # error number.
159
+ def error_number(exception) # :nodoc:
160
+ raise NotImplementedError
291
161
  end
292
162
 
293
163
  # REFERENTIAL INTEGRITY ====================================
294
164
 
295
165
  def disable_referential_integrity #:nodoc:
296
- old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
166
+ old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
297
167
 
298
168
  begin
299
169
  update("SET FOREIGN_KEY_CHECKS = 0")
@@ -303,30 +173,42 @@ module ActiveRecord
303
173
  end
304
174
  end
305
175
 
176
+ # CONNECTION MANAGEMENT ====================================
177
+
178
+ def clear_cache! # :nodoc:
179
+ reload_type_map
180
+ super
181
+ end
182
+
306
183
  #--
307
184
  # DATABASE STATEMENTS ======================================
308
185
  #++
309
186
 
310
- def clear_cache!
311
- super
312
- reload_type_map
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)
313
194
  end
314
195
 
315
196
  # Executes the SQL statement in the context of this connection.
316
197
  def execute(sql, name = nil)
317
- log(sql, name) { @connection.query(sql) }
318
- end
198
+ materialize_transactions
319
199
 
320
- # MysqlAdapter has to free a result after using it, so we use this method to write
321
- # stuff in an abstract way without concerning ourselves about whether it needs to be
322
- # explicitly freed or not.
323
- def execute_and_free(sql, name = nil) #:nodoc:
324
- yield execute(sql, name)
200
+ log(sql, name) do
201
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
202
+ @connection.query(sql)
203
+ end
204
+ end
325
205
  end
326
206
 
327
- def update_sql(sql, name = nil) #:nodoc:
328
- super
329
- @connection.affected_rows
207
+ # Mysql2Adapter doesn't have to free a result after using it, but we use this method
208
+ # to write stuff in an abstract way without concerning ourselves about whether it
209
+ # needs to be explicitly freed or not.
210
+ def execute_and_free(sql, name = nil) # :nodoc:
211
+ yield execute(sql, name)
330
212
  end
331
213
 
332
214
  def begin_db_transaction
@@ -346,19 +228,7 @@ module ActiveRecord
346
228
  execute "ROLLBACK"
347
229
  end
348
230
 
349
- # In the simple case, MySQL allows us to place JOINs directly into the UPDATE
350
- # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
351
- # these, we must use a subquery.
352
- def join_to_update(update, select) #:nodoc:
353
- if select.limit || select.offset || select.orders.any?
354
- super
355
- else
356
- update.table select.source
357
- update.wheres = select.constraints
358
- end
359
- end
360
-
361
- def empty_insert_statement_value
231
+ def empty_insert_statement_value(primary_key = nil)
362
232
  "VALUES ()"
363
233
  end
364
234
 
@@ -374,7 +244,7 @@ module ActiveRecord
374
244
  end
375
245
 
376
246
  # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
377
- # Charset defaults to utf8.
247
+ # Charset defaults to utf8mb4.
378
248
  #
379
249
  # Example:
380
250
  # create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
@@ -382,9 +252,13 @@ module ActiveRecord
382
252
  # create_database 'matt_development', charset: :big5
383
253
  def create_database(name, options = {})
384
254
  if options[:collation]
385
- execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
255
+ execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT COLLATE #{quote_table_name(options[:collation])}"
256
+ elsif options[:charset]
257
+ execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset])}"
258
+ elsif row_format_dynamic_by_default?
259
+ execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET `utf8mb4`"
386
260
  else
387
- execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
261
+ raise "Configure a supported :charset and ensure innodb_large_prefix is enabled to support indexes on varchar(255) string columns."
388
262
  end
389
263
  end
390
264
 
@@ -393,108 +267,38 @@ module ActiveRecord
393
267
  # Example:
394
268
  # drop_database('sebastian_development')
395
269
  def drop_database(name) #:nodoc:
396
- execute "DROP DATABASE IF EXISTS `#{name}`"
270
+ execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
397
271
  end
398
272
 
399
273
  def current_database
400
- select_value 'SELECT DATABASE() as db'
274
+ query_value("SELECT database()", "SCHEMA")
401
275
  end
402
276
 
403
277
  # Returns the database character set.
404
278
  def charset
405
- show_variable 'character_set_database'
279
+ show_variable "character_set_database"
406
280
  end
407
281
 
408
282
  # Returns the database collation strategy.
409
283
  def collation
410
- show_variable 'collation_database'
411
- end
412
-
413
- def tables(name = nil, database = nil, like = nil) #:nodoc:
414
- sql = "SHOW TABLES "
415
- sql << "IN #{quote_table_name(database)} " if database
416
- sql << "LIKE #{quote(like)}" if like
417
-
418
- execute_and_free(sql, 'SCHEMA') do |result|
419
- result.collect { |field| field.first }
420
- end
421
- end
422
- alias data_sources tables
423
-
424
- def truncate(table_name, name = nil)
425
- execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
284
+ show_variable "collation_database"
426
285
  end
427
286
 
428
- def table_exists?(name)
429
- return false unless name.present?
430
- return true if tables(nil, nil, name).any?
431
-
432
- name = name.to_s
433
- schema, table = name.split('.', 2)
287
+ def table_comment(table_name) # :nodoc:
288
+ scope = quoted_scope(table_name)
434
289
 
435
- unless table # A table was provided without a schema
436
- table = schema
437
- schema = nil
438
- end
439
-
440
- tables(nil, schema, table).any?
441
- end
442
- alias data_source_exists? table_exists?
443
-
444
- # Returns an array of indexes for the given table.
445
- def indexes(table_name, name = nil) #:nodoc:
446
- indexes = []
447
- current_index = nil
448
- execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
449
- each_hash(result) do |row|
450
- if current_index != row[:Key_name]
451
- next if row[:Key_name] == 'PRIMARY' # skip the primary key
452
- current_index = row[:Key_name]
453
-
454
- mysql_index_type = row[:Index_type].downcase.to_sym
455
- index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
456
- index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
457
- indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [], nil, nil, index_type, index_using)
458
- end
459
-
460
- indexes.last.columns << row[:Column_name]
461
- indexes.last.lengths << row[:Sub_part]
462
- end
463
- end
464
-
465
- indexes
466
- end
467
-
468
- # Returns an array of +Column+ objects for the table specified by +table_name+.
469
- def columns(table_name)#:nodoc:
470
- sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
471
- execute_and_free(sql, 'SCHEMA') do |result|
472
- each_hash(result).map do |field|
473
- field_name = set_field_encoding(field[:Field])
474
- sql_type = field[:Type]
475
- cast_type = lookup_cast_type(sql_type)
476
- new_column(field_name, field[:Default], cast_type, sql_type, field[:Null] == "YES", field[:Collation], field[:Extra])
477
- end
478
- end
290
+ query_value(<<~SQL, "SCHEMA").presence
291
+ SELECT table_comment
292
+ FROM information_schema.tables
293
+ WHERE table_schema = #{scope[:schema]}
294
+ AND table_name = #{scope[:name]}
295
+ SQL
479
296
  end
480
297
 
481
- def create_table(table_name, options = {}) #:nodoc:
482
- super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
483
- end
484
-
485
- def bulk_change_table(table_name, operations) #:nodoc:
486
- sqls = operations.flat_map do |command, args|
487
- table, arguments = args.shift, args
488
- method = :"#{command}_sql"
489
-
490
- if respond_to?(method, true)
491
- send(method, table, *arguments)
492
- else
493
- raise "Unknown method called : #{method}(#{arguments.inspect})"
494
- end
495
- end.join(", ")
496
-
497
- execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
298
+ def change_table_comment(table_name, comment_or_changes) # :nodoc:
299
+ comment = extract_new_comment_value(comment_or_changes)
300
+ comment = "" if comment.nil?
301
+ execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
498
302
  end
499
303
 
500
304
  # Renames a table.
@@ -506,8 +310,23 @@ module ActiveRecord
506
310
  rename_table_indexes(table_name, new_name)
507
311
  end
508
312
 
313
+ # Drops a table from the database.
314
+ #
315
+ # [<tt>:force</tt>]
316
+ # Set to +:cascade+ to drop dependent objects as well.
317
+ # Defaults to false.
318
+ # [<tt>:if_exists</tt>]
319
+ # Set to +true+ to only drop the table if it exists.
320
+ # Defaults to false.
321
+ # [<tt>:temporary</tt>]
322
+ # Set to +true+ to drop temporary table.
323
+ # Defaults to false.
324
+ #
325
+ # Although this command ignores most +options+ and the block if one is given,
326
+ # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
327
+ # In that case, +options+ and the block will be used by create_table.
509
328
  def drop_table(table_name, options = {})
510
- execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
329
+ execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
511
330
  end
512
331
 
513
332
  def rename_index(table_name, old_name, new_name)
@@ -520,150 +339,153 @@ module ActiveRecord
520
339
  end
521
340
  end
522
341
 
523
- def change_column_default(table_name, column_name, default) #:nodoc:
524
- column = column_for(table_name, column_name)
525
- change_column table_name, column_name, column.sql_type, :default => default
342
+ def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
343
+ default = extract_new_default_value(default_or_changes)
344
+ change_column table_name, column_name, nil, default: default
526
345
  end
527
346
 
528
- def change_column_null(table_name, column_name, null, default = nil)
529
- column = column_for(table_name, column_name)
530
-
347
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
531
348
  unless null || default.nil?
532
349
  execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
533
350
  end
534
351
 
535
- change_column table_name, column_name, column.sql_type, :null => null
352
+ change_column table_name, column_name, nil, null: null
353
+ end
354
+
355
+ def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
356
+ comment = extract_new_comment_value(comment_or_changes)
357
+ change_column table_name, column_name, nil, comment: comment
536
358
  end
537
359
 
538
360
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
539
- execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_sql(table_name, column_name, type, options)}")
361
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, options)}")
540
362
  end
541
363
 
542
364
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
543
- execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
365
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
544
366
  rename_column_indexes(table_name, column_name, new_column_name)
545
367
  end
546
368
 
547
369
  def add_index(table_name, column_name, options = {}) #:nodoc:
548
- index_name, index_type, index_columns, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
549
- execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options} #{index_algorithm}"
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)
373
+ end
374
+
375
+ def add_sql_comment!(sql, comment) # :nodoc:
376
+ sql << " COMMENT #{quote(comment)}" if comment.present?
377
+ sql
550
378
  end
551
379
 
552
380
  def foreign_keys(table_name)
553
- fk_info = select_all <<-SQL.strip_heredoc
554
- SELECT fk.referenced_table_name as 'to_table'
555
- ,fk.referenced_column_name as 'primary_key'
556
- ,fk.column_name as 'column'
557
- ,fk.constraint_name as 'name'
558
- FROM information_schema.key_column_usage fk
559
- WHERE fk.referenced_column_name is not null
560
- AND fk.table_schema = '#{@config[:database]}'
561
- AND fk.table_name = '#{table_name}'
381
+ raise ArgumentError unless table_name.present?
382
+
383
+ scope = quoted_scope(table_name)
384
+
385
+ fk_info = exec_query(<<~SQL, "SCHEMA")
386
+ SELECT fk.referenced_table_name AS 'to_table',
387
+ fk.referenced_column_name AS 'primary_key',
388
+ fk.column_name AS 'column',
389
+ fk.constraint_name AS 'name',
390
+ rc.update_rule AS 'on_update',
391
+ rc.delete_rule AS 'on_delete'
392
+ FROM information_schema.referential_constraints rc
393
+ JOIN information_schema.key_column_usage fk
394
+ USING (constraint_schema, constraint_name)
395
+ WHERE fk.referenced_column_name IS NOT NULL
396
+ AND fk.table_schema = #{scope[:schema]}
397
+ AND fk.table_name = #{scope[:name]}
398
+ AND rc.constraint_schema = #{scope[:schema]}
399
+ AND rc.table_name = #{scope[:name]}
562
400
  SQL
563
401
 
564
- create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
565
-
566
402
  fk_info.map do |row|
567
403
  options = {
568
- column: row['column'],
569
- name: row['name'],
570
- primary_key: row['primary_key']
404
+ column: row["column"],
405
+ name: row["name"],
406
+ primary_key: row["primary_key"]
571
407
  }
572
408
 
573
- options[:on_update] = extract_foreign_key_action(create_table_info, row['name'], "UPDATE")
574
- options[:on_delete] = extract_foreign_key_action(create_table_info, row['name'], "DELETE")
409
+ options[:on_update] = extract_foreign_key_action(row["on_update"])
410
+ options[:on_delete] = extract_foreign_key_action(row["on_delete"])
575
411
 
576
- ForeignKeyDefinition.new(table_name, row['to_table'], options)
412
+ ForeignKeyDefinition.new(table_name, row["to_table"], options)
577
413
  end
578
414
  end
579
415
 
580
- # Maps logical Rails types to MySQL-specific data types.
581
- def type_to_sql(type, limit = nil, precision = nil, scale = nil)
582
- case type.to_s
583
- when 'binary'
584
- case limit
585
- when 0..0xfff; "varbinary(#{limit})"
586
- when nil; "blob"
587
- when 0x1000..0xffffffff; "blob(#{limit})"
588
- else raise(ActiveRecordError, "No binary type has character length #{limit}")
589
- end
590
- when 'integer'
591
- case limit
592
- when 1; 'tinyint'
593
- when 2; 'smallint'
594
- when 3; 'mediumint'
595
- when nil, 4, 11; 'int(11)' # compatibility with MySQL default
596
- when 5..8; 'bigint'
597
- else raise(ActiveRecordError, "No integer type has byte size #{limit}")
598
- end
599
- when 'text'
600
- case limit
601
- when 0..0xff; 'tinytext'
602
- when nil, 0x100..0xffff; 'text'
603
- when 0x10000..0xffffff; 'mediumtext'
604
- when 0x1000000..0xffffffff; 'longtext'
605
- else raise(ActiveRecordError, "No text type has character length #{limit}")
606
- end
607
- when 'datetime'
608
- return super unless precision
416
+ def table_options(table_name) # :nodoc:
417
+ table_options = {}
609
418
 
610
- case precision
611
- when 0..6; "datetime(#{precision})"
612
- else raise(ActiveRecordError, "No datetime type has precision of #{precision}. The allowed range of precision is from 0 to 6.")
613
- end
614
- else
615
- super
419
+ create_table_info = create_table_info(table_name)
420
+
421
+ # strip create_definitions and partition_options
422
+ raw_table_options = create_table_info.sub(/\A.*\n\) /m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
423
+
424
+ # strip AUTO_INCREMENT
425
+ raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
426
+
427
+ table_options[:options] = raw_table_options
428
+
429
+ # strip COMMENT
430
+ if raw_table_options.sub!(/ COMMENT='.+'/, "")
431
+ table_options[:comment] = table_comment(table_name)
616
432
  end
433
+
434
+ table_options
617
435
  end
618
436
 
619
437
  # SHOW VARIABLES LIKE 'name'
620
438
  def show_variable(name)
621
- variables = select_all("select @@#{name} as 'Value'", 'SCHEMA')
622
- variables.first['Value'] unless variables.empty?
439
+ query_value("SELECT @@#{name}", "SCHEMA")
623
440
  rescue ActiveRecord::StatementInvalid
624
441
  nil
625
442
  end
626
443
 
627
- # Returns a table's primary key and belonging sequence.
628
- def pk_and_sequence_for(table)
629
- execute_and_free("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA') do |result|
630
- create_table = each_hash(result).first[:"Create Table"]
631
- if create_table.to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
632
- keys = $1.split(",").map { |key| key.delete('`"') }
633
- keys.length == 1 ? [keys.first, nil] : nil
634
- else
635
- nil
636
- end
637
- end
638
- end
444
+ def primary_keys(table_name) # :nodoc:
445
+ raise ArgumentError unless table_name.present?
639
446
 
640
- # Returns just a table's primary key
641
- def primary_key(table)
642
- pk_and_sequence = pk_and_sequence_for(table)
643
- pk_and_sequence && pk_and_sequence.first
644
- end
447
+ scope = quoted_scope(table_name)
645
448
 
646
- def case_sensitive_modifier(node, table_attribute)
647
- node = Arel::Nodes.build_quoted node, table_attribute
648
- Arel::Nodes::Bin.new(node)
449
+ query_values(<<~SQL, "SCHEMA")
450
+ SELECT column_name
451
+ FROM information_schema.statistics
452
+ WHERE index_name = 'PRIMARY'
453
+ AND table_schema = #{scope[:schema]}
454
+ AND table_name = #{scope[:name]}
455
+ ORDER BY seq_in_index
456
+ SQL
649
457
  end
650
458
 
651
- def case_sensitive_comparison(table, attribute, column, value)
652
- if column.case_sensitive?
653
- table[attribute].eq(value)
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))
654
469
  else
655
470
  super
656
471
  end
657
472
  end
658
473
 
659
- def case_insensitive_comparison(table, attribute, column, value)
660
- if column.case_sensitive?
661
- super
474
+ def case_sensitive_comparison(attribute, value) # :nodoc:
475
+ column = column_for_attribute(attribute)
476
+
477
+ if column.collation && !column.case_sensitive?
478
+ attribute.eq(Arel::Nodes::Bin.new(value))
662
479
  else
663
- table[attribute].eq(value)
480
+ super
664
481
  end
665
482
  end
666
483
 
484
+ def can_perform_case_insensitive_comparison_for?(column)
485
+ column.case_sensitive?
486
+ end
487
+ private :can_perform_case_insensitive_comparison_for?
488
+
667
489
  # In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
668
490
  # DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
669
491
  # distinct queries, and requires that the ORDER BY include the distinct column.
@@ -673,274 +495,336 @@ module ActiveRecord
673
495
  # Convert Arel node to string
674
496
  s = s.to_sql unless s.is_a?(String)
675
497
  # Remove any ASC/DESC modifiers
676
- s.gsub(/\s+(?:ASC|DESC)\b/i, '')
498
+ s.gsub(/\s+(?:ASC|DESC)\b/i, "")
677
499
  }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
678
500
 
679
- [super, *order_columns].join(', ')
501
+ (order_columns << super).join(", ")
680
502
  end
681
503
 
682
504
  def strict_mode?
683
505
  self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
684
506
  end
685
507
 
686
- def valid_type?(type)
687
- !native_database_types[type].nil?
508
+ def default_index_type?(index) # :nodoc:
509
+ index.using == :btree || super
688
510
  end
689
511
 
690
- protected
512
+ def build_insert_sql(insert) # :nodoc:
513
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
691
514
 
692
- def initialize_type_map(m) # :nodoc:
693
- super
694
-
695
- register_class_with_limit m, %r(char)i, MysqlString
696
-
697
- m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
698
- m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
699
- m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
700
- m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
701
- m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
702
- m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
703
- m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
704
- m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
705
- m.register_type %r(^float)i, Type::Float.new(limit: 24)
706
- m.register_type %r(^double)i, Type::Float.new(limit: 53)
707
-
708
- register_integer_type m, %r(^bigint)i, limit: 8
709
- register_integer_type m, %r(^int)i, limit: 4
710
- register_integer_type m, %r(^mediumint)i, limit: 3
711
- register_integer_type m, %r(^smallint)i, limit: 2
712
- register_integer_type m, %r(^tinyint)i, limit: 1
713
-
714
- m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans
715
- m.alias_type %r(set)i, 'varchar'
716
- m.alias_type %r(year)i, 'integer'
717
- m.alias_type %r(bit)i, 'binary'
718
-
719
- m.register_type(%r(datetime)i) do |sql_type|
720
- precision = extract_precision(sql_type)
721
- MysqlDateTime.new(precision: precision)
515
+ if insert.skip_duplicates?
516
+ no_op_column = quote_column_name(insert.keys.first)
517
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
518
+ elsif insert.update_duplicates?
519
+ sql << " ON DUPLICATE KEY UPDATE "
520
+ sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
722
521
  end
723
522
 
724
- m.register_type(%r(enum)i) do |sql_type|
725
- limit = sql_type[/^enum\((.+)\)/i, 1]
726
- .split(',').map{|enum| enum.strip.length - 2}.max
727
- MysqlString.new(limit: limit)
728
- end
523
+ sql
729
524
  end
730
525
 
731
- def register_integer_type(mapping, key, options) # :nodoc:
732
- mapping.register_type(key) do |sql_type|
733
- if /unsigned/i =~ sql_type
734
- Type::UnsignedInteger.new(options)
735
- else
736
- Type::Integer.new(options)
737
- end
526
+ def check_version # :nodoc:
527
+ if database_version < "5.5.8"
528
+ raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
738
529
  end
739
530
  end
740
531
 
741
- # MySQL is too stupid to create a temporary table for use subquery, so we have
742
- # to give it some prompting in the form of a subsubquery. Ugh!
743
- def subquery_for(key, select)
744
- subsubselect = select.clone
745
- subsubselect.projections = [key]
532
+ private
746
533
 
747
- # Materialize subquery by adding distinct
748
- # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
749
- subsubselect.distinct unless select.limit || select.offset || select.orders.any?
534
+ def initialize_type_map(m = type_map)
535
+ super
750
536
 
751
- subselect = Arel::SelectManager.new(select.engine)
752
- subselect.project Arel.sql(key.name)
753
- subselect.from subsubselect.as('__active_record_temp')
754
- end
537
+ register_class_with_limit m, %r(char)i, MysqlString
538
+
539
+ m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
540
+ m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
541
+ m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
542
+ m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
543
+ m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
544
+ m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
545
+ m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
546
+ m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
547
+ m.register_type %r(^float)i, Type::Float.new(limit: 24)
548
+ m.register_type %r(^double)i, Type::Float.new(limit: 53)
549
+
550
+ register_integer_type m, %r(^bigint)i, limit: 8
551
+ register_integer_type m, %r(^int)i, limit: 4
552
+ register_integer_type m, %r(^mediumint)i, limit: 3
553
+ register_integer_type m, %r(^smallint)i, limit: 2
554
+ register_integer_type m, %r(^tinyint)i, limit: 1
555
+
556
+ 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
755
565
 
756
- def add_index_length(option_strings, column_names, options = {})
757
- if options.is_a?(Hash) && length = options[:length]
758
- case length
759
- when Hash
760
- column_names.each {|name| option_strings[name] += "(#{length[name]})" if length.has_key?(name) && length[name].present?}
761
- when Integer
762
- column_names.each {|name| option_strings[name] += "(#{length})"}
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)
763
570
  end
764
571
  end
765
572
 
766
- return option_strings
767
- end
768
-
769
- def quoted_columns_for_index(column_names, options = {})
770
- option_strings = Hash[column_names.map {|name| [name, '']}]
573
+ def register_integer_type(mapping, key, options)
574
+ mapping.register_type(key) do |sql_type|
575
+ if /\bunsigned\b/.match?(sql_type)
576
+ Type::UnsignedInteger.new(options)
577
+ else
578
+ Type::Integer.new(options)
579
+ end
580
+ end
581
+ end
771
582
 
772
- # add index length
773
- option_strings = add_index_length(option_strings, column_names, options)
583
+ def extract_precision(sql_type)
584
+ if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
585
+ super || 0
586
+ else
587
+ super
588
+ end
589
+ end
774
590
 
775
- # add index sort order
776
- option_strings = add_index_sort_order(option_strings, column_names, options)
591
+ # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
592
+ ER_FILSORT_ABORT = 1028
593
+ ER_DUP_ENTRY = 1062
594
+ ER_NOT_NULL_VIOLATION = 1048
595
+ ER_NO_REFERENCED_ROW = 1216
596
+ ER_ROW_IS_REFERENCED = 1217
597
+ ER_DO_NOT_HAVE_DEFAULT = 1364
598
+ ER_ROW_IS_REFERENCED_2 = 1451
599
+ ER_NO_REFERENCED_ROW_2 = 1452
600
+ ER_DATA_TOO_LONG = 1406
601
+ ER_OUT_OF_RANGE = 1264
602
+ ER_LOCK_DEADLOCK = 1213
603
+ ER_CANNOT_ADD_FOREIGN = 1215
604
+ ER_CANNOT_CREATE_TABLE = 1005
605
+ ER_LOCK_WAIT_TIMEOUT = 1205
606
+ ER_QUERY_INTERRUPTED = 1317
607
+ ER_QUERY_TIMEOUT = 3024
608
+ ER_FK_INCOMPATIBLE_COLUMNS = 3780
609
+
610
+ def translate_exception(exception, message:, sql:, binds:)
611
+ case error_number(exception)
612
+ when ER_DUP_ENTRY
613
+ RecordNotUnique.new(message, sql: sql, binds: binds)
614
+ when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
615
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
616
+ when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
617
+ mismatched_foreign_key(message, sql: sql, binds: binds)
618
+ when ER_CANNOT_CREATE_TABLE
619
+ if message.include?("errno: 150")
620
+ mismatched_foreign_key(message, sql: sql, binds: binds)
621
+ else
622
+ super
623
+ end
624
+ when ER_DATA_TOO_LONG
625
+ ValueTooLong.new(message, sql: sql, binds: binds)
626
+ when ER_OUT_OF_RANGE
627
+ RangeError.new(message, sql: sql, binds: binds)
628
+ when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
629
+ NotNullViolation.new(message, sql: sql, binds: binds)
630
+ when ER_LOCK_DEADLOCK
631
+ Deadlocked.new(message, sql: sql, binds: binds)
632
+ when ER_LOCK_WAIT_TIMEOUT
633
+ LockWaitTimeout.new(message, sql: sql, binds: binds)
634
+ when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
635
+ StatementTimeout.new(message, sql: sql, binds: binds)
636
+ when ER_QUERY_INTERRUPTED
637
+ QueryCanceled.new(message, sql: sql, binds: binds)
638
+ else
639
+ super
640
+ end
641
+ end
777
642
 
778
- column_names.map {|name| quote_column_name(name) + option_strings[name]}
779
- end
643
+ def change_column_for_alter(table_name, column_name, type, options = {})
644
+ column = column_for(table_name, column_name)
645
+ type ||= column.sql_type
780
646
 
781
- def translate_exception(exception, message)
782
- case error_number(exception)
783
- when 1062
784
- RecordNotUnique.new(message, exception)
785
- when 1452
786
- InvalidForeignKey.new(message, exception)
787
- else
788
- super
789
- end
790
- end
647
+ unless options.key?(:default)
648
+ options[:default] = column.default
649
+ end
791
650
 
792
- def add_column_sql(table_name, column_name, type, options = {})
793
- td = create_table_definition table_name, options[:temporary], options[:options]
794
- cd = td.new_column_definition(column_name, type, options)
795
- schema_creation.visit_AddColumn cd
796
- end
651
+ unless options.key?(:null)
652
+ options[:null] = column.null
653
+ end
797
654
 
798
- def change_column_sql(table_name, column_name, type, options = {})
799
- column = column_for(table_name, column_name)
655
+ unless options.key?(:comment)
656
+ options[:comment] = column.comment
657
+ end
800
658
 
801
- unless options_include_default?(options)
802
- options[:default] = column.default
659
+ td = create_table_definition(table_name)
660
+ cd = td.new_column_definition(column.name, type, options)
661
+ schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
803
662
  end
804
663
 
805
- unless options.has_key?(:null)
806
- options[:null] = column.null
664
+ def rename_column_for_alter(table_name, column_name, new_column_name)
665
+ column = column_for(table_name, column_name)
666
+ options = {
667
+ default: column.default,
668
+ null: column.null,
669
+ auto_increment: column.auto_increment?
670
+ }
671
+
672
+ current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
673
+ td = create_table_definition(table_name)
674
+ cd = td.new_column_definition(new_column_name, current_type, options)
675
+ schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
807
676
  end
808
677
 
809
- options[:name] = column.name
810
- schema_creation.accept ChangeColumnDefinition.new column, type, options
811
- end
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}"
682
+ end
812
683
 
813
- def rename_column_sql(table_name, column_name, new_column_name)
814
- column = column_for(table_name, column_name)
815
- options = {
816
- name: new_column_name,
817
- default: column.default,
818
- null: column.null,
819
- auto_increment: column.extra == "auto_increment"
820
- }
684
+ def remove_index_for_alter(table_name, options = {})
685
+ index_name = index_name_for_remove(table_name, options)
686
+ "DROP INDEX #{quote_column_name(index_name)}"
687
+ end
821
688
 
822
- current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
823
- schema_creation.accept ChangeColumnDefinition.new column, current_type, options
824
- end
689
+ def add_timestamps_for_alter(table_name, options = {})
690
+ options[:null] = false if options[:null].nil?
825
691
 
826
- def remove_column_sql(table_name, column_name, type = nil, options = {})
827
- "DROP #{quote_column_name(column_name)}"
828
- end
692
+ if !options.key?(:precision) && supports_datetime_with_precision?
693
+ options[:precision] = 6
694
+ end
829
695
 
830
- def remove_columns_sql(table_name, *column_names)
831
- column_names.map {|column_name| remove_column_sql(table_name, column_name) }
832
- end
696
+ [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
697
+ end
833
698
 
834
- def add_index_sql(table_name, column_name, options = {})
835
- index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
836
- "ADD #{index_type} INDEX #{index_name} (#{index_columns})"
837
- end
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
838
702
 
839
- def remove_index_sql(table_name, options = {})
840
- index_name = index_name_for_remove(table_name, options)
841
- "DROP INDEX #{index_name}"
842
- end
703
+ def supports_rename_index?
704
+ mariadb? ? false : database_version >= "5.7.6"
705
+ end
843
706
 
844
- def add_timestamps_sql(table_name, options = {})
845
- [add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)]
846
- end
707
+ def configure_connection
708
+ variables = @config.fetch(:variables, {}).stringify_keys
847
709
 
848
- def remove_timestamps_sql(table_name, options = {})
849
- [remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
850
- end
710
+ # By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
711
+ variables["sql_auto_is_null"] = 0
851
712
 
852
- private
713
+ # Increase timeout so the server doesn't disconnect us.
714
+ wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
715
+ wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
716
+ variables["wait_timeout"] = wait_timeout
853
717
 
854
- def version
855
- @version ||= Version.new(full_version.match(/^\d+\.\d+\.\d+/)[0])
856
- end
718
+ defaults = [":default", :default].to_set
857
719
 
858
- def mariadb?
859
- full_version =~ /mariadb/i
860
- end
720
+ # 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
722
+ # If the user has provided another value for sql_mode, don't replace it.
723
+ if sql_mode = variables.delete("sql_mode")
724
+ sql_mode = quote(sql_mode)
725
+ elsif !defaults.include?(strict_mode?)
726
+ if strict_mode?
727
+ sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
728
+ else
729
+ sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
730
+ sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
731
+ sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
732
+ end
733
+ sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
734
+ end
735
+ sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
736
+
737
+ # NAMES does not have an equals sign, see
738
+ # https://dev.mysql.com/doc/refman/5.7/en/set-names.html
739
+ # (trailing comma because variable_assignments will always have content)
740
+ if @config[:encoding]
741
+ encoding = +"NAMES #{@config[:encoding]}"
742
+ encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
743
+ encoding << ", "
744
+ end
861
745
 
862
- def supports_rename_index?
863
- mariadb? ? false : version >= '5.7.6'
864
- end
746
+ # Gather up all of the SET variables...
747
+ variable_assignments = variables.map do |k, v|
748
+ if defaults.include?(v)
749
+ "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
750
+ elsif !v.nil?
751
+ "@@SESSION.#{k} = #{quote(v)}"
752
+ end
753
+ # or else nil; compact to clear nils out
754
+ end.compact.join(", ")
865
755
 
866
- def configure_connection
867
- variables = @config.fetch(:variables, {}).stringify_keys
756
+ # ...and send them all in one query
757
+ execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
758
+ end
868
759
 
869
- # By default, MySQL 'where id is null' selects the last inserted id.
870
- # Turn this off. http://dev.rubyonrails.org/ticket/6778
871
- variables['sql_auto_is_null'] = 0
760
+ def column_definitions(table_name) # :nodoc:
761
+ execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
762
+ each_hash(result)
763
+ end
764
+ end
872
765
 
873
- # Increase timeout so the server doesn't disconnect us.
874
- wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
875
- wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
876
- variables["wait_timeout"] = wait_timeout
766
+ def create_table_info(table_name) # :nodoc:
767
+ exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
768
+ end
877
769
 
878
- # Make MySQL reject illegal values rather than truncating or blanking them, see
879
- # http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
880
- # If the user has provided another value for sql_mode, don't replace it.
881
- unless variables.has_key?('sql_mode')
882
- variables['sql_mode'] = strict_mode? ? 'STRICT_ALL_TABLES' : ''
770
+ def arel_visitor
771
+ Arel::Visitors::MySQL.new(self)
883
772
  end
884
773
 
885
- # NAMES does not have an equals sign, see
886
- # http://dev.mysql.com/doc/refman/5.0/en/set-statement.html#id944430
887
- # (trailing comma because variable_assignments will always have content)
888
- if @config[:encoding]
889
- encoding = "NAMES #{@config[:encoding]}"
890
- encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
891
- encoding << ", "
774
+ def build_statement_pool
775
+ StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
892
776
  end
893
777
 
894
- # Gather up all of the SET variables...
895
- variable_assignments = variables.map do |k, v|
896
- if v == ':default' || v == :default
897
- "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
898
- elsif !v.nil?
899
- "@@SESSION.#{k} = #{quote(v)}"
900
- end
901
- # or else nil; compact to clear nils out
902
- end.compact.join(', ')
778
+ def mismatched_foreign_key(message, sql:, binds:)
779
+ match = %r/
780
+ (?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
781
+ FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
782
+ REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
783
+ /xmi.match(sql)
903
784
 
904
- # ...and send them all in one query
905
- @connection.query "SET #{encoding} #{variable_assignments}"
906
- end
785
+ options = {
786
+ message: message,
787
+ sql: sql,
788
+ binds: binds,
789
+ }
907
790
 
908
- def extract_foreign_key_action(structure, name, action) # :nodoc:
909
- if structure =~ /CONSTRAINT #{quote_column_name(name)} FOREIGN KEY .* REFERENCES .* ON #{action} (CASCADE|SET NULL|RESTRICT)/
910
- case $1
911
- when 'CASCADE'; :cascade
912
- when 'SET NULL'; :nullify
791
+ if match
792
+ options[:table] = match[:table]
793
+ options[:foreign_key] = match[:foreign_key]
794
+ options[:target_table] = match[:target_table]
795
+ options[:primary_key] = match[:primary_key]
796
+ options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
913
797
  end
914
- end
915
- end
916
798
 
917
- class MysqlDateTime < Type::DateTime # :nodoc:
918
- private
799
+ MismatchedForeignKey.new(options)
800
+ end
919
801
 
920
- def has_precision?
921
- precision || 0
802
+ def version_string(full_version_string)
803
+ full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
922
804
  end
923
- end
924
805
 
925
- class MysqlString < Type::String # :nodoc:
926
- def type_cast_for_database(value)
927
- case value
928
- when true then "1"
929
- when false then "0"
930
- else super
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
931
813
  end
932
- end
933
814
 
934
- private
815
+ private
935
816
 
936
- def cast_value(value)
937
- case value
938
- when true then "1"
939
- when false then "0"
940
- else super
941
- end
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
942
824
  end
943
- end
825
+
826
+ ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
827
+ ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
944
828
  end
945
829
  end
946
830
  end