activerecord 6.0.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 (340) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1086 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +219 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record.rb +195 -0
  8. data/lib/active_record/aggregations.rb +285 -0
  9. data/lib/active_record/association_relation.rb +49 -0
  10. data/lib/active_record/associations.rb +1865 -0
  11. data/lib/active_record/associations/alias_tracker.rb +81 -0
  12. data/lib/active_record/associations/association.rb +340 -0
  13. data/lib/active_record/associations/association_scope.rb +166 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +124 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +36 -0
  16. data/lib/active_record/associations/builder/association.rb +136 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +130 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +72 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +114 -0
  20. data/lib/active_record/associations/builder/has_many.rb +19 -0
  21. data/lib/active_record/associations/builder/has_one.rb +64 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +44 -0
  23. data/lib/active_record/associations/collection_association.rb +498 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1128 -0
  25. data/lib/active_record/associations/foreign_association.rb +20 -0
  26. data/lib/active_record/associations/has_many_association.rb +136 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +220 -0
  28. data/lib/active_record/associations/has_one_association.rb +118 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency.rb +262 -0
  31. data/lib/active_record/associations/join_dependency/join_association.rb +80 -0
  32. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  33. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  34. data/lib/active_record/associations/preloader.rb +201 -0
  35. data/lib/active_record/associations/preloader/association.rb +133 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +116 -0
  37. data/lib/active_record/associations/singular_association.rb +59 -0
  38. data/lib/active_record/associations/through_association.rb +121 -0
  39. data/lib/active_record/attribute_assignment.rb +85 -0
  40. data/lib/active_record/attribute_decorators.rb +90 -0
  41. data/lib/active_record/attribute_methods.rb +420 -0
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +81 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +221 -0
  44. data/lib/active_record/attribute_methods/primary_key.rb +136 -0
  45. data/lib/active_record/attribute_methods/query.rb +41 -0
  46. data/lib/active_record/attribute_methods/read.rb +47 -0
  47. data/lib/active_record/attribute_methods/serialization.rb +90 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +91 -0
  49. data/lib/active_record/attribute_methods/write.rb +61 -0
  50. data/lib/active_record/attributes.rb +279 -0
  51. data/lib/active_record/autosave_association.rb +512 -0
  52. data/lib/active_record/base.rb +328 -0
  53. data/lib/active_record/callbacks.rb +339 -0
  54. data/lib/active_record/coders/json.rb +15 -0
  55. data/lib/active_record/coders/yaml_column.rb +50 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1175 -0
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +85 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +516 -0
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +155 -0
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +251 -0
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +713 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1475 -0
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +772 -0
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +830 -0
  69. data/lib/active_record/connection_adapters/column.rb +95 -0
  70. data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
  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 +146 -0
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +184 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  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 +41 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +953 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +141 -0
  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 +561 -0
  127. data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
  128. data/lib/active_record/connection_handling.rb +274 -0
  129. data/lib/active_record/core.rb +603 -0
  130. data/lib/active_record/counter_cache.rb +193 -0
  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 +122 -0
  137. data/lib/active_record/enum.rb +274 -0
  138. data/lib/active_record/errors.rb +388 -0
  139. data/lib/active_record/explain.rb +50 -0
  140. data/lib/active_record/explain_registry.rb +32 -0
  141. data/lib/active_record/explain_subscriber.rb +34 -0
  142. data/lib/active_record/fixture_set/file.rb +82 -0
  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 +738 -0
  148. data/lib/active_record/gem_version.rb +17 -0
  149. data/lib/active_record/inheritance.rb +293 -0
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +207 -0
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  154. data/lib/active_record/locale/en.yml +48 -0
  155. data/lib/active_record/locking/optimistic.rb +197 -0
  156. data/lib/active_record/locking/pessimistic.rb +89 -0
  157. data/lib/active_record/log_subscriber.rb +118 -0
  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 +1397 -0
  162. data/lib/active_record/migration/command_recorder.rb +284 -0
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +17 -0
  165. data/lib/active_record/model_schema.rb +545 -0
  166. data/lib/active_record/nested_attributes.rb +600 -0
  167. data/lib/active_record/no_touching.rb +65 -0
  168. data/lib/active_record/null_relation.rb +68 -0
  169. data/lib/active_record/persistence.rb +967 -0
  170. data/lib/active_record/query_cache.rb +52 -0
  171. data/lib/active_record/querying.rb +82 -0
  172. data/lib/active_record/railtie.rb +263 -0
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +7 -0
  175. data/lib/active_record/railties/controller_runtime.rb +51 -0
  176. data/lib/active_record/railties/databases.rake +527 -0
  177. data/lib/active_record/readonly_attributes.rb +24 -0
  178. data/lib/active_record/reflection.rb +1042 -0
  179. data/lib/active_record/relation.rb +860 -0
  180. data/lib/active_record/relation/batches.rb +290 -0
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +424 -0
  183. data/lib/active_record/relation/delegation.rb +130 -0
  184. data/lib/active_record/relation/finder_methods.rb +561 -0
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +184 -0
  187. data/lib/active_record/relation/predicate_builder.rb +150 -0
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
  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 +19 -0
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +1371 -0
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +77 -0
  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 +168 -0
  202. data/lib/active_record/runtime_registry.rb +24 -0
  203. data/lib/active_record/sanitization.rb +214 -0
  204. data/lib/active_record/schema.rb +61 -0
  205. data/lib/active_record/schema_dumper.rb +270 -0
  206. data/lib/active_record/schema_migration.rb +60 -0
  207. data/lib/active_record/scoping.rb +106 -0
  208. data/lib/active_record/scoping/default.rb +151 -0
  209. data/lib/active_record/scoping/named.rb +217 -0
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +22 -0
  212. data/lib/active_record/statement_cache.rb +148 -0
  213. data/lib/active_record/store.rb +290 -0
  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 +506 -0
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +224 -0
  222. data/lib/active_record/timestamp.rb +167 -0
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +493 -0
  225. data/lib/active_record/translation.rb +24 -0
  226. data/lib/active_record/type.rb +78 -0
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +9 -0
  229. data/lib/active_record/type/date_time.rb +9 -0
  230. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  231. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  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 +71 -0
  235. data/lib/active_record/type/text.rb +11 -0
  236. data/lib/active_record/type/time.rb +21 -0
  237. data/lib/active_record/type/type_map.rb +62 -0
  238. data/lib/active_record/type/unsigned_integer.rb +17 -0
  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 +94 -0
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +60 -0
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +68 -0
  247. data/lib/active_record/validations/uniqueness.rb +226 -0
  248. data/lib/active_record/version.rb +10 -0
  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 +19 -0
  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 +48 -0
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +75 -0
  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.tt +48 -0
  337. data/lib/rails/generators/active_record/model/model_generator.rb +49 -0
  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.tt +7 -0
  340. metadata +418 -0
@@ -0,0 +1,830 @@
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"
13
+
14
+ module ActiveRecord
15
+ module ConnectionAdapters
16
+ class AbstractMysqlAdapter < AbstractAdapter
17
+ include MySQL::Quoting
18
+ include MySQL::SchemaStatements
19
+
20
+ ##
21
+ # :singleton-method:
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
24
+ # to your application.rb file:
25
+ #
26
+ # ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans = false
27
+ class_attribute :emulate_booleans, default: true
28
+
29
+ NATIVE_DATABASE_TYPES = {
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" },
44
+ }
45
+
46
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
47
+ private
48
+
49
+ def dealloc(stmt)
50
+ stmt.close
51
+ end
52
+ end
53
+
54
+ def initialize(connection, logger, connection_options, config)
55
+ super(connection, logger, config)
56
+ end
57
+
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
63
+
64
+ def mariadb? # :nodoc:
65
+ /mariadb/i.match?(full_version)
66
+ end
67
+
68
+ def supports_bulk_alter?
69
+ true
70
+ end
71
+
72
+ def supports_index_sort_order?
73
+ !mariadb? && database_version >= "8.0.1"
74
+ end
75
+
76
+ def supports_expression_index?
77
+ !mariadb? && database_version >= "8.0.13"
78
+ end
79
+
80
+ def supports_transaction_isolation?
81
+ true
82
+ end
83
+
84
+ def supports_explain?
85
+ true
86
+ end
87
+
88
+ def supports_indexes_in_create?
89
+ true
90
+ end
91
+
92
+ def supports_foreign_keys?
93
+ true
94
+ end
95
+
96
+ def supports_views?
97
+ true
98
+ end
99
+
100
+ def supports_datetime_with_precision?
101
+ mariadb? || database_version >= "5.6.4"
102
+ end
103
+
104
+ def supports_virtual_columns?
105
+ mariadb? || database_version >= "5.7.5"
106
+ end
107
+
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"
111
+ end
112
+
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
119
+ end
120
+
121
+ def supports_advisory_locks?
122
+ true
123
+ end
124
+
125
+ def supports_insert_on_duplicate_skip?
126
+ true
127
+ end
128
+
129
+ def supports_insert_on_duplicate_update?
130
+ true
131
+ end
132
+
133
+ def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
134
+ query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
135
+ end
136
+
137
+ def release_advisory_lock(lock_name) # :nodoc:
138
+ query_value("SELECT RELEASE_LOCK(#{quote(lock_name.to_s)})") == 1
139
+ end
140
+
141
+ def native_database_types
142
+ NATIVE_DATABASE_TYPES
143
+ end
144
+
145
+ def index_algorithms
146
+ { default: +"ALGORITHM = DEFAULT", copy: +"ALGORITHM = COPY", inplace: +"ALGORITHM = INPLACE" }
147
+ end
148
+
149
+ # HELPER METHODS ===========================================
150
+
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
155
+ end
156
+
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
161
+ end
162
+
163
+ # REFERENTIAL INTEGRITY ====================================
164
+
165
+ def disable_referential_integrity #:nodoc:
166
+ old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
167
+
168
+ begin
169
+ update("SET FOREIGN_KEY_CHECKS = 0")
170
+ yield
171
+ ensure
172
+ update("SET FOREIGN_KEY_CHECKS = #{old}")
173
+ end
174
+ end
175
+
176
+ # CONNECTION MANAGEMENT ====================================
177
+
178
+ def clear_cache! # :nodoc:
179
+ reload_type_map
180
+ super
181
+ end
182
+
183
+ #--
184
+ # DATABASE STATEMENTS ======================================
185
+ #++
186
+
187
+ def explain(arel, binds = [])
188
+ sql = "EXPLAIN #{to_sql(arel, binds)}"
189
+ start = Concurrent.monotonic_time
190
+ result = exec_query(sql, "EXPLAIN", binds)
191
+ elapsed = Concurrent.monotonic_time - start
192
+
193
+ MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
194
+ end
195
+
196
+ # Executes the SQL statement in the context of this connection.
197
+ def execute(sql, name = nil)
198
+ materialize_transactions
199
+
200
+ log(sql, name) do
201
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
202
+ @connection.query(sql)
203
+ end
204
+ end
205
+ end
206
+
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)
212
+ end
213
+
214
+ def begin_db_transaction
215
+ execute "BEGIN"
216
+ end
217
+
218
+ def begin_isolated_db_transaction(isolation)
219
+ execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
220
+ begin_db_transaction
221
+ end
222
+
223
+ def commit_db_transaction #:nodoc:
224
+ execute "COMMIT"
225
+ end
226
+
227
+ def exec_rollback_db_transaction #:nodoc:
228
+ execute "ROLLBACK"
229
+ end
230
+
231
+ def empty_insert_statement_value(primary_key = nil)
232
+ "VALUES ()"
233
+ end
234
+
235
+ # SCHEMA STATEMENTS ========================================
236
+
237
+ # Drops the database specified on the +name+ attribute
238
+ # and creates it again using the provided +options+.
239
+ def recreate_database(name, options = {})
240
+ drop_database(name)
241
+ sql = create_database(name, options)
242
+ reconnect!
243
+ sql
244
+ end
245
+
246
+ # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
247
+ # Charset defaults to utf8mb4.
248
+ #
249
+ # Example:
250
+ # create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
251
+ # create_database 'matt_development'
252
+ # create_database 'matt_development', charset: :big5
253
+ def create_database(name, options = {})
254
+ if 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`"
260
+ else
261
+ raise "Configure a supported :charset and ensure innodb_large_prefix is enabled to support indexes on varchar(255) string columns."
262
+ end
263
+ end
264
+
265
+ # Drops a MySQL database.
266
+ #
267
+ # Example:
268
+ # drop_database('sebastian_development')
269
+ def drop_database(name) #:nodoc:
270
+ execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
271
+ end
272
+
273
+ def current_database
274
+ query_value("SELECT database()", "SCHEMA")
275
+ end
276
+
277
+ # Returns the database character set.
278
+ def charset
279
+ show_variable "character_set_database"
280
+ end
281
+
282
+ # Returns the database collation strategy.
283
+ def collation
284
+ show_variable "collation_database"
285
+ end
286
+
287
+ def table_comment(table_name) # :nodoc:
288
+ scope = quoted_scope(table_name)
289
+
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
296
+ end
297
+
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)}")
302
+ end
303
+
304
+ # Renames a table.
305
+ #
306
+ # Example:
307
+ # rename_table('octopuses', 'octopi')
308
+ def rename_table(table_name, new_name)
309
+ execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
310
+ rename_table_indexes(table_name, new_name)
311
+ end
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.
328
+ def drop_table(table_name, options = {})
329
+ execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
330
+ end
331
+
332
+ def rename_index(table_name, old_name, new_name)
333
+ if supports_rename_index?
334
+ validate_index_length!(table_name, new_name)
335
+
336
+ execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
337
+ else
338
+ super
339
+ end
340
+ end
341
+
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
345
+ end
346
+
347
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
348
+ unless null || default.nil?
349
+ execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
350
+ end
351
+
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
358
+ end
359
+
360
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
361
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, options)}")
362
+ end
363
+
364
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
365
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
366
+ rename_column_indexes(table_name, column_name, new_column_name)
367
+ end
368
+
369
+ def add_index(table_name, column_name, options = {}) #:nodoc:
370
+ index_name, index_type, index_columns, _, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
371
+ sql = +"CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
372
+ execute add_sql_comment!(sql, comment)
373
+ end
374
+
375
+ def add_sql_comment!(sql, comment) # :nodoc:
376
+ sql << " COMMENT #{quote(comment)}" if comment.present?
377
+ sql
378
+ end
379
+
380
+ def foreign_keys(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]}
400
+ SQL
401
+
402
+ fk_info.map do |row|
403
+ options = {
404
+ column: row["column"],
405
+ name: row["name"],
406
+ primary_key: row["primary_key"]
407
+ }
408
+
409
+ options[:on_update] = extract_foreign_key_action(row["on_update"])
410
+ options[:on_delete] = extract_foreign_key_action(row["on_delete"])
411
+
412
+ ForeignKeyDefinition.new(table_name, row["to_table"], options)
413
+ end
414
+ end
415
+
416
+ def table_options(table_name) # :nodoc:
417
+ table_options = {}
418
+
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)
432
+ end
433
+
434
+ table_options
435
+ end
436
+
437
+ # SHOW VARIABLES LIKE 'name'
438
+ def show_variable(name)
439
+ query_value("SELECT @@#{name}", "SCHEMA")
440
+ rescue ActiveRecord::StatementInvalid
441
+ nil
442
+ end
443
+
444
+ def primary_keys(table_name) # :nodoc:
445
+ raise ArgumentError unless table_name.present?
446
+
447
+ scope = quoted_scope(table_name)
448
+
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
457
+ end
458
+
459
+ def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
460
+ column = column_for_attribute(attribute)
461
+
462
+ if column.collation && !column.case_sensitive? && !value.nil?
463
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
464
+ Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1.
465
+ To continue case sensitive comparison on the :#{attribute.name} attribute in #{klass} model,
466
+ pass `case_sensitive: true` option explicitly to the uniqueness validator.
467
+ MSG
468
+ attribute.eq(Arel::Nodes::Bin.new(value))
469
+ else
470
+ super
471
+ end
472
+ end
473
+
474
+ 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))
479
+ else
480
+ super
481
+ end
482
+ end
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
+
489
+ # In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
490
+ # DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
491
+ # distinct queries, and requires that the ORDER BY include the distinct column.
492
+ # See https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
493
+ def columns_for_distinct(columns, orders) # :nodoc:
494
+ order_columns = orders.reject(&:blank?).map { |s|
495
+ # Convert Arel node to string
496
+ s = s.to_sql unless s.is_a?(String)
497
+ # Remove any ASC/DESC modifiers
498
+ s.gsub(/\s+(?:ASC|DESC)\b/i, "")
499
+ }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
500
+
501
+ (order_columns << super).join(", ")
502
+ end
503
+
504
+ def strict_mode?
505
+ self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
506
+ end
507
+
508
+ def default_index_type?(index) # :nodoc:
509
+ index.using == :btree || super
510
+ end
511
+
512
+ def build_insert_sql(insert) # :nodoc:
513
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
514
+
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(",")
521
+ end
522
+
523
+ sql
524
+ end
525
+
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."
529
+ end
530
+ end
531
+
532
+ private
533
+
534
+ def initialize_type_map(m = type_map)
535
+ super
536
+
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
565
+
566
+ m.register_type(%r(^set)i) do |sql_type|
567
+ limit = sql_type[/^set\s*\((.+)\)/i, 1]
568
+ .split(",").map { |set| set.strip.length - 1 }.sum - 1
569
+ MysqlString.new(limit: limit)
570
+ end
571
+ end
572
+
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
582
+
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
590
+
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
642
+
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
646
+
647
+ unless options.key?(:default)
648
+ options[:default] = column.default
649
+ end
650
+
651
+ unless options.key?(:null)
652
+ options[:null] = column.null
653
+ end
654
+
655
+ unless options.key?(:comment)
656
+ options[:comment] = column.comment
657
+ end
658
+
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))
662
+ end
663
+
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))
676
+ end
677
+
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
683
+
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
688
+
689
+ def add_timestamps_for_alter(table_name, options = {})
690
+ options[:null] = false if options[:null].nil?
691
+
692
+ if !options.key?(:precision) && supports_datetime_with_precision?
693
+ options[:precision] = 6
694
+ end
695
+
696
+ [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
697
+ end
698
+
699
+ def remove_timestamps_for_alter(table_name, options = {})
700
+ [remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
701
+ end
702
+
703
+ def supports_rename_index?
704
+ mariadb? ? false : database_version >= "5.7.6"
705
+ end
706
+
707
+ def configure_connection
708
+ variables = @config.fetch(:variables, {}).stringify_keys
709
+
710
+ # By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
711
+ variables["sql_auto_is_null"] = 0
712
+
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
717
+
718
+ defaults = [":default", :default].to_set
719
+
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
745
+
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(", ")
755
+
756
+ # ...and send them all in one query
757
+ execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
758
+ end
759
+
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
765
+
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
769
+
770
+ def arel_visitor
771
+ Arel::Visitors::MySQL.new(self)
772
+ end
773
+
774
+ def build_statement_pool
775
+ StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
776
+ end
777
+
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)
784
+
785
+ options = {
786
+ message: message,
787
+ sql: sql,
788
+ binds: binds,
789
+ }
790
+
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])
797
+ end
798
+
799
+ MismatchedForeignKey.new(options)
800
+ end
801
+
802
+ def version_string(full_version_string)
803
+ full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
804
+ end
805
+
806
+ class MysqlString < Type::String # :nodoc:
807
+ def serialize(value)
808
+ case value
809
+ when true then "1"
810
+ when false then "0"
811
+ else super
812
+ end
813
+ end
814
+
815
+ private
816
+
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
824
+ end
825
+
826
+ ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
827
+ ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
828
+ end
829
+ end
830
+ end