activerecord 5.0.7.2 → 6.0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

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