activerecord 6.1.7 → 7.1.5

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.
Files changed (311) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2030 -1020
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  5. data/lib/active_record/aggregations.rb +17 -14
  6. data/lib/active_record/association_relation.rb +1 -11
  7. data/lib/active_record/associations/association.rb +51 -19
  8. data/lib/active_record/associations/association_scope.rb +17 -12
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +11 -5
  12. data/lib/active_record/associations/builder/belongs_to.rb +40 -14
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  15. data/lib/active_record/associations/builder/has_many.rb +3 -2
  16. data/lib/active_record/associations/builder/has_one.rb +2 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  18. data/lib/active_record/associations/collection_association.rb +39 -35
  19. data/lib/active_record/associations/collection_proxy.rb +30 -15
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  21. data/lib/active_record/associations/foreign_association.rb +10 -3
  22. data/lib/active_record/associations/has_many_association.rb +28 -18
  23. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  24. data/lib/active_record/associations/has_one_association.rb +20 -10
  25. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  27. data/lib/active_record/associations/join_dependency.rb +28 -20
  28. data/lib/active_record/associations/preloader/association.rb +210 -52
  29. data/lib/active_record/associations/preloader/batch.rb +48 -0
  30. data/lib/active_record/associations/preloader/branch.rb +147 -0
  31. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  32. data/lib/active_record/associations/preloader.rb +50 -121
  33. data/lib/active_record/associations/singular_association.rb +9 -3
  34. data/lib/active_record/associations/through_association.rb +25 -14
  35. data/lib/active_record/associations.rb +446 -306
  36. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  37. data/lib/active_record/attribute_assignment.rb +1 -3
  38. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  39. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  40. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  41. data/lib/active_record/attribute_methods/query.rb +31 -19
  42. data/lib/active_record/attribute_methods/read.rb +27 -12
  43. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  45. data/lib/active_record/attribute_methods/write.rb +12 -15
  46. data/lib/active_record/attribute_methods.rb +161 -40
  47. data/lib/active_record/attributes.rb +27 -38
  48. data/lib/active_record/autosave_association.rb +65 -31
  49. data/lib/active_record/base.rb +25 -2
  50. data/lib/active_record/callbacks.rb +18 -34
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -46
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +113 -597
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -27
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +367 -141
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -150
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +317 -164
  70. data/lib/active_record/connection_adapters/column.rb +13 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +39 -14
  78. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +112 -55
  80. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  81. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  89. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +397 -75
  101. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +508 -246
  103. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  104. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  105. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
  106. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  107. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  108. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  109. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +296 -104
  110. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  111. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  112. data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
  113. data/lib/active_record/connection_adapters.rb +9 -6
  114. data/lib/active_record/connection_handling.rb +108 -137
  115. data/lib/active_record/core.rb +242 -233
  116. data/lib/active_record/counter_cache.rb +52 -27
  117. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -2
  118. data/lib/active_record/database_configurations/database_config.rb +21 -12
  119. data/lib/active_record/database_configurations/hash_config.rb +88 -16
  120. data/lib/active_record/database_configurations/url_config.rb +18 -12
  121. data/lib/active_record/database_configurations.rb +95 -59
  122. data/lib/active_record/delegated_type.rb +66 -20
  123. data/lib/active_record/deprecator.rb +7 -0
  124. data/lib/active_record/destroy_association_async_job.rb +4 -2
  125. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  126. data/lib/active_record/dynamic_matchers.rb +1 -1
  127. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  128. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  129. data/lib/active_record/encryption/cipher.rb +53 -0
  130. data/lib/active_record/encryption/config.rb +68 -0
  131. data/lib/active_record/encryption/configurable.rb +60 -0
  132. data/lib/active_record/encryption/context.rb +42 -0
  133. data/lib/active_record/encryption/contexts.rb +76 -0
  134. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  135. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  136. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  137. data/lib/active_record/encryption/encrypted_attribute_type.rb +155 -0
  138. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  139. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  140. data/lib/active_record/encryption/encryptor.rb +155 -0
  141. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  142. data/lib/active_record/encryption/errors.rb +15 -0
  143. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  144. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  145. data/lib/active_record/encryption/key.rb +28 -0
  146. data/lib/active_record/encryption/key_generator.rb +53 -0
  147. data/lib/active_record/encryption/key_provider.rb +46 -0
  148. data/lib/active_record/encryption/message.rb +33 -0
  149. data/lib/active_record/encryption/message_serializer.rb +92 -0
  150. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  151. data/lib/active_record/encryption/properties.rb +76 -0
  152. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  153. data/lib/active_record/encryption/scheme.rb +100 -0
  154. data/lib/active_record/encryption.rb +58 -0
  155. data/lib/active_record/enum.rb +154 -63
  156. data/lib/active_record/errors.rb +172 -15
  157. data/lib/active_record/explain.rb +23 -3
  158. data/lib/active_record/explain_registry.rb +11 -6
  159. data/lib/active_record/explain_subscriber.rb +1 -1
  160. data/lib/active_record/fixture_set/file.rb +15 -1
  161. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  162. data/lib/active_record/fixture_set/render_context.rb +2 -0
  163. data/lib/active_record/fixture_set/table_row.rb +70 -14
  164. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  165. data/lib/active_record/fixtures.rb +147 -86
  166. data/lib/active_record/future_result.rb +174 -0
  167. data/lib/active_record/gem_version.rb +3 -3
  168. data/lib/active_record/inheritance.rb +81 -29
  169. data/lib/active_record/insert_all.rb +135 -22
  170. data/lib/active_record/integration.rb +11 -10
  171. data/lib/active_record/internal_metadata.rb +119 -33
  172. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  173. data/lib/active_record/locking/optimistic.rb +37 -22
  174. data/lib/active_record/locking/pessimistic.rb +15 -6
  175. data/lib/active_record/log_subscriber.rb +52 -19
  176. data/lib/active_record/marshalling.rb +59 -0
  177. data/lib/active_record/message_pack.rb +124 -0
  178. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  179. data/lib/active_record/middleware/database_selector.rb +23 -13
  180. data/lib/active_record/middleware/shard_selector.rb +62 -0
  181. data/lib/active_record/migration/command_recorder.rb +112 -14
  182. data/lib/active_record/migration/compatibility.rb +233 -46
  183. data/lib/active_record/migration/default_strategy.rb +23 -0
  184. data/lib/active_record/migration/execution_strategy.rb +19 -0
  185. data/lib/active_record/migration/join_table.rb +1 -1
  186. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  187. data/lib/active_record/migration.rb +361 -173
  188. data/lib/active_record/model_schema.rb +125 -101
  189. data/lib/active_record/nested_attributes.rb +50 -20
  190. data/lib/active_record/no_touching.rb +3 -3
  191. data/lib/active_record/normalization.rb +167 -0
  192. data/lib/active_record/persistence.rb +409 -88
  193. data/lib/active_record/promise.rb +84 -0
  194. data/lib/active_record/query_cache.rb +4 -22
  195. data/lib/active_record/query_logs.rb +174 -0
  196. data/lib/active_record/query_logs_formatter.rb +41 -0
  197. data/lib/active_record/querying.rb +29 -6
  198. data/lib/active_record/railtie.rb +220 -44
  199. data/lib/active_record/railties/controller_runtime.rb +15 -10
  200. data/lib/active_record/railties/databases.rake +188 -252
  201. data/lib/active_record/railties/job_runtime.rb +23 -0
  202. data/lib/active_record/readonly_attributes.rb +41 -3
  203. data/lib/active_record/reflection.rb +248 -81
  204. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  205. data/lib/active_record/relation/batches.rb +192 -63
  206. data/lib/active_record/relation/calculations.rb +246 -90
  207. data/lib/active_record/relation/delegation.rb +28 -14
  208. data/lib/active_record/relation/finder_methods.rb +108 -51
  209. data/lib/active_record/relation/merger.rb +22 -13
  210. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  211. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  212. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  213. data/lib/active_record/relation/predicate_builder.rb +27 -20
  214. data/lib/active_record/relation/query_attribute.rb +30 -12
  215. data/lib/active_record/relation/query_methods.rb +670 -129
  216. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  217. data/lib/active_record/relation/spawn_methods.rb +20 -3
  218. data/lib/active_record/relation/where_clause.rb +10 -19
  219. data/lib/active_record/relation.rb +287 -120
  220. data/lib/active_record/result.rb +37 -11
  221. data/lib/active_record/runtime_registry.rb +32 -13
  222. data/lib/active_record/sanitization.rb +65 -20
  223. data/lib/active_record/schema.rb +36 -22
  224. data/lib/active_record/schema_dumper.rb +73 -24
  225. data/lib/active_record/schema_migration.rb +68 -33
  226. data/lib/active_record/scoping/default.rb +72 -15
  227. data/lib/active_record/scoping/named.rb +5 -13
  228. data/lib/active_record/scoping.rb +65 -34
  229. data/lib/active_record/secure_password.rb +60 -0
  230. data/lib/active_record/secure_token.rb +21 -3
  231. data/lib/active_record/serialization.rb +6 -1
  232. data/lib/active_record/signed_id.rb +10 -8
  233. data/lib/active_record/store.rb +10 -10
  234. data/lib/active_record/suppressor.rb +13 -15
  235. data/lib/active_record/table_metadata.rb +16 -3
  236. data/lib/active_record/tasks/database_tasks.rb +251 -140
  237. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  238. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  239. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  240. data/lib/active_record/test_databases.rb +1 -1
  241. data/lib/active_record/test_fixtures.rb +117 -96
  242. data/lib/active_record/timestamp.rb +32 -19
  243. data/lib/active_record/token_for.rb +113 -0
  244. data/lib/active_record/touch_later.rb +11 -6
  245. data/lib/active_record/transactions.rb +48 -27
  246. data/lib/active_record/translation.rb +3 -3
  247. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  248. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  249. data/lib/active_record/type/internal/timezone.rb +7 -2
  250. data/lib/active_record/type/serialized.rb +9 -5
  251. data/lib/active_record/type/time.rb +4 -0
  252. data/lib/active_record/type/type_map.rb +17 -20
  253. data/lib/active_record/type.rb +1 -2
  254. data/lib/active_record/validations/absence.rb +1 -1
  255. data/lib/active_record/validations/associated.rb +4 -4
  256. data/lib/active_record/validations/numericality.rb +5 -4
  257. data/lib/active_record/validations/presence.rb +5 -28
  258. data/lib/active_record/validations/uniqueness.rb +51 -6
  259. data/lib/active_record/validations.rb +8 -4
  260. data/lib/active_record/version.rb +1 -1
  261. data/lib/active_record.rb +335 -32
  262. data/lib/arel/attributes/attribute.rb +0 -8
  263. data/lib/arel/crud.rb +28 -22
  264. data/lib/arel/delete_manager.rb +18 -4
  265. data/lib/arel/errors.rb +10 -0
  266. data/lib/arel/factory_methods.rb +4 -0
  267. data/lib/arel/filter_predications.rb +9 -0
  268. data/lib/arel/insert_manager.rb +2 -3
  269. data/lib/arel/nodes/and.rb +4 -0
  270. data/lib/arel/nodes/binary.rb +6 -1
  271. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  272. data/lib/arel/nodes/casted.rb +1 -1
  273. data/lib/arel/nodes/cte.rb +36 -0
  274. data/lib/arel/nodes/delete_statement.rb +12 -13
  275. data/lib/arel/nodes/filter.rb +10 -0
  276. data/lib/arel/nodes/fragments.rb +35 -0
  277. data/lib/arel/nodes/function.rb +1 -0
  278. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  279. data/lib/arel/nodes/insert_statement.rb +2 -2
  280. data/lib/arel/nodes/leading_join.rb +8 -0
  281. data/lib/arel/nodes/node.rb +111 -2
  282. data/lib/arel/nodes/select_core.rb +2 -2
  283. data/lib/arel/nodes/select_statement.rb +2 -2
  284. data/lib/arel/nodes/sql_literal.rb +6 -0
  285. data/lib/arel/nodes/table_alias.rb +4 -0
  286. data/lib/arel/nodes/update_statement.rb +8 -3
  287. data/lib/arel/nodes.rb +5 -0
  288. data/lib/arel/predications.rb +13 -3
  289. data/lib/arel/select_manager.rb +10 -4
  290. data/lib/arel/table.rb +9 -6
  291. data/lib/arel/tree_manager.rb +5 -13
  292. data/lib/arel/update_manager.rb +18 -4
  293. data/lib/arel/visitors/dot.rb +80 -90
  294. data/lib/arel/visitors/mysql.rb +16 -3
  295. data/lib/arel/visitors/postgresql.rb +0 -10
  296. data/lib/arel/visitors/to_sql.rb +141 -20
  297. data/lib/arel/visitors/visitor.rb +2 -2
  298. data/lib/arel.rb +18 -3
  299. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  300. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  301. data/lib/rails/generators/active_record/migration.rb +3 -1
  302. data/lib/rails/generators/active_record/model/USAGE +113 -0
  303. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  304. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  305. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  306. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  307. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  308. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  309. metadata +96 -16
  310. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  311. data/lib/active_record/null_relation.rb +0 -67
@@ -3,6 +3,7 @@
3
3
  require "active_record/connection_adapters/abstract_adapter"
4
4
  require "active_record/connection_adapters/statement_pool"
5
5
  require "active_record/connection_adapters/mysql/column"
6
+ require "active_record/connection_adapters/mysql/database_statements"
6
7
  require "active_record/connection_adapters/mysql/explain_pretty_printer"
7
8
  require "active_record/connection_adapters/mysql/quoting"
8
9
  require "active_record/connection_adapters/mysql/schema_creation"
@@ -14,6 +15,7 @@ require "active_record/connection_adapters/mysql/type_metadata"
14
15
  module ActiveRecord
15
16
  module ConnectionAdapters
16
17
  class AbstractMysqlAdapter < AbstractAdapter
18
+ include MySQL::DatabaseStatements
17
19
  include MySQL::Quoting
18
20
  include MySQL::SchemaStatements
19
21
 
@@ -31,6 +33,7 @@ module ActiveRecord
31
33
  string: { name: "varchar", limit: 255 },
32
34
  text: { name: "text" },
33
35
  integer: { name: "int", limit: 4 },
36
+ bigint: { name: "bigint" },
34
37
  float: { name: "float", limit: 24 },
35
38
  decimal: { name: "decimal" },
36
39
  datetime: { name: "datetime" },
@@ -50,11 +53,37 @@ module ActiveRecord
50
53
  end
51
54
  end
52
55
 
53
- def initialize(connection, logger, connection_options, config)
54
- super(connection, logger, config)
56
+ class << self
57
+ def dbconsole(config, options = {})
58
+ mysql_config = config.configuration_hash
59
+
60
+ args = {
61
+ host: "--host",
62
+ port: "--port",
63
+ socket: "--socket",
64
+ username: "--user",
65
+ encoding: "--default-character-set",
66
+ sslca: "--ssl-ca",
67
+ sslcert: "--ssl-cert",
68
+ sslcapath: "--ssl-capath",
69
+ sslcipher: "--ssl-cipher",
70
+ sslkey: "--ssl-key",
71
+ ssl_mode: "--ssl-mode"
72
+ }.filter_map { |opt, arg| "#{arg}=#{mysql_config[opt]}" if mysql_config[opt] }
73
+
74
+ if mysql_config[:password] && options[:include_password]
75
+ args << "--password=#{mysql_config[:password]}"
76
+ elsif mysql_config[:password] && !mysql_config[:password].to_s.empty?
77
+ args << "-p"
78
+ end
79
+
80
+ args << config.database
81
+
82
+ find_cmd_and_exec(["mysql", "mysql5"], *args)
83
+ end
55
84
  end
56
85
 
57
- def get_database_version #:nodoc:
86
+ def get_database_version # :nodoc:
58
87
  full_version_string = get_full_version
59
88
  version_string = version_string(full_version_string)
60
89
  Version.new(version_string, full_version_string)
@@ -80,6 +109,10 @@ module ActiveRecord
80
109
  true
81
110
  end
82
111
 
112
+ def supports_restart_db_transaction?
113
+ true
114
+ end
115
+
83
116
  def supports_explain?
84
117
  true
85
118
  end
@@ -94,7 +127,7 @@ module ActiveRecord
94
127
 
95
128
  def supports_check_constraints?
96
129
  if mariadb?
97
- database_version >= "10.2.1"
130
+ database_version >= "10.3.10" || (database_version < "10.3" && database_version >= "10.2.22")
98
131
  else
99
132
  database_version >= "8.0.16"
100
133
  end
@@ -174,7 +207,7 @@ module ActiveRecord
174
207
 
175
208
  # REFERENTIAL INTEGRITY ====================================
176
209
 
177
- def disable_referential_integrity #:nodoc:
210
+ def disable_referential_integrity # :nodoc:
178
211
  old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
179
212
 
180
213
  begin
@@ -185,54 +218,43 @@ module ActiveRecord
185
218
  end
186
219
  end
187
220
 
188
- # CONNECTION MANAGEMENT ====================================
189
-
190
- def clear_cache! # :nodoc:
191
- reload_type_map
192
- super
193
- end
194
-
195
221
  #--
196
222
  # DATABASE STATEMENTS ======================================
197
223
  #++
198
224
 
199
- # Executes the SQL statement in the context of this connection.
200
- def execute(sql, name = nil)
201
- materialize_transactions
202
- mark_transaction_written_if_write(sql)
203
-
204
- log(sql, name) do
205
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
206
- @connection.query(sql)
207
- end
208
- end
209
- end
210
-
211
225
  # Mysql2Adapter doesn't have to free a result after using it, but we use this method
212
226
  # to write stuff in an abstract way without concerning ourselves about whether it
213
227
  # needs to be explicitly freed or not.
214
- def execute_and_free(sql, name = nil) # :nodoc:
215
- yield execute(sql, name)
228
+ def execute_and_free(sql, name = nil, async: false) # :nodoc:
229
+ sql = transform_query(sql)
230
+ check_if_write_query(sql)
231
+
232
+ mark_transaction_written_if_write(sql)
233
+ yield raw_execute(sql, name, async: async)
216
234
  end
217
235
 
218
- def begin_db_transaction
219
- execute("BEGIN", "TRANSACTION")
236
+ def begin_db_transaction # :nodoc:
237
+ internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
220
238
  end
221
239
 
222
- def begin_isolated_db_transaction(isolation)
223
- execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
240
+ def begin_isolated_db_transaction(isolation) # :nodoc:
241
+ internal_execute("SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
224
242
  begin_db_transaction
225
243
  end
226
244
 
227
- def commit_db_transaction #:nodoc:
228
- execute("COMMIT", "TRANSACTION")
245
+ def commit_db_transaction # :nodoc:
246
+ internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
229
247
  end
230
248
 
231
- def exec_rollback_db_transaction #:nodoc:
232
- execute("ROLLBACK", "TRANSACTION")
249
+ def exec_rollback_db_transaction # :nodoc:
250
+ internal_execute("ROLLBACK", "TRANSACTION", allow_retry: false, materialize_transactions: true)
233
251
  end
234
252
 
235
- def empty_insert_statement_value(primary_key = nil)
253
+ def exec_restart_db_transaction # :nodoc:
254
+ internal_execute("ROLLBACK AND CHAIN", "TRANSACTION", allow_retry: false, materialize_transactions: true)
255
+ end
256
+
257
+ def empty_insert_statement_value(primary_key = nil) # :nodoc:
236
258
  "VALUES ()"
237
259
  end
238
260
 
@@ -270,7 +292,7 @@ module ActiveRecord
270
292
  #
271
293
  # Example:
272
294
  # drop_database('sebastian_development')
273
- def drop_database(name) #:nodoc:
295
+ def drop_database(name) # :nodoc:
274
296
  execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
275
297
  end
276
298
 
@@ -309,11 +331,12 @@ module ActiveRecord
309
331
  #
310
332
  # Example:
311
333
  # rename_table('octopuses', 'octopi')
312
- def rename_table(table_name, new_name)
334
+ def rename_table(table_name, new_name, **options)
335
+ validate_table_length!(new_name) unless options[:_uses_legacy_table_name]
313
336
  schema_cache.clear_data_source_cache!(table_name.to_s)
314
337
  schema_cache.clear_data_source_cache!(new_name.to_s)
315
338
  execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
316
- rename_table_indexes(table_name, new_name)
339
+ rename_table_indexes(table_name, new_name, **options)
317
340
  end
318
341
 
319
342
  # Drops a table from the database.
@@ -346,12 +369,21 @@ module ActiveRecord
346
369
  end
347
370
  end
348
371
 
349
- def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
372
+ def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
373
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
374
+ end
375
+
376
+ def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
377
+ column = column_for(table_name, column_name)
378
+ return unless column
379
+
350
380
  default = extract_new_default_value(default_or_changes)
351
- change_column table_name, column_name, nil, default: default
381
+ ChangeColumnDefaultDefinition.new(column, default)
352
382
  end
353
383
 
354
- def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
384
+ def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
385
+ validate_change_column_null_argument!(null)
386
+
355
387
  unless null || default.nil?
356
388
  execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
357
389
  end
@@ -364,22 +396,64 @@ module ActiveRecord
364
396
  change_column table_name, column_name, nil, comment: comment
365
397
  end
366
398
 
367
- def change_column(table_name, column_name, type, **options) #:nodoc:
399
+ def change_column(table_name, column_name, type, **options) # :nodoc:
368
400
  execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, **options)}")
369
401
  end
370
402
 
371
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
403
+ # Builds a ChangeColumnDefinition object.
404
+ #
405
+ # This definition object contains information about the column change that would occur
406
+ # if the same arguments were passed to #change_column. See #change_column for information about
407
+ # passing a +table_name+, +column_name+, +type+ and other options that can be passed.
408
+ def build_change_column_definition(table_name, column_name, type, **options) # :nodoc:
409
+ column = column_for(table_name, column_name)
410
+ type ||= column.sql_type
411
+
412
+ unless options.key?(:default)
413
+ options[:default] = column.default
414
+ end
415
+
416
+ unless options.key?(:null)
417
+ options[:null] = column.null
418
+ end
419
+
420
+ unless options.key?(:comment)
421
+ options[:comment] = column.comment
422
+ end
423
+
424
+ if options[:collation] == :no_collation
425
+ options.delete(:collation)
426
+ else
427
+ options[:collation] ||= column.collation if text_type?(type)
428
+ end
429
+
430
+ unless options.key?(:auto_increment)
431
+ options[:auto_increment] = column.auto_increment?
432
+ end
433
+
434
+ td = create_table_definition(table_name)
435
+ cd = td.new_column_definition(column.name, type, **options)
436
+ ChangeColumnDefinition.new(cd, column.name)
437
+ end
438
+
439
+ def rename_column(table_name, column_name, new_column_name) # :nodoc:
372
440
  execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
373
441
  rename_column_indexes(table_name, column_name, new_column_name)
374
442
  end
375
443
 
376
- def add_index(table_name, column_name, **options) #:nodoc:
444
+ def add_index(table_name, column_name, **options) # :nodoc:
445
+ create_index = build_create_index_definition(table_name, column_name, **options)
446
+ return unless create_index
447
+
448
+ execute schema_creation.accept(create_index)
449
+ end
450
+
451
+ def build_create_index_definition(table_name, column_name, **options) # :nodoc:
377
452
  index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
378
453
 
379
454
  return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
380
455
 
381
- create_index = CreateIndexDefinition.new(index, algorithm)
382
- execute schema_creation.accept(create_index)
456
+ CreateIndexDefinition.new(index, algorithm)
383
457
  end
384
458
 
385
459
  def add_sql_comment!(sql, comment) # :nodoc:
@@ -392,11 +466,13 @@ module ActiveRecord
392
466
 
393
467
  scope = quoted_scope(table_name)
394
468
 
395
- fk_info = exec_query(<<~SQL, "SCHEMA")
469
+ # MySQL returns 1 row for each column of composite foreign keys.
470
+ fk_info = internal_exec_query(<<~SQL, "SCHEMA")
396
471
  SELECT fk.referenced_table_name AS 'to_table',
397
472
  fk.referenced_column_name AS 'primary_key',
398
473
  fk.column_name AS 'column',
399
474
  fk.constraint_name AS 'name',
475
+ fk.ordinal_position AS 'position',
400
476
  rc.update_rule AS 'on_update',
401
477
  rc.delete_rule AS 'on_delete'
402
478
  FROM information_schema.referential_constraints rc
@@ -409,17 +485,24 @@ module ActiveRecord
409
485
  AND rc.table_name = #{scope[:name]}
410
486
  SQL
411
487
 
412
- fk_info.map do |row|
488
+ grouped_fk = fk_info.group_by { |row| row["name"] }.values.each { |group| group.sort_by! { |row| row["position"] } }
489
+ grouped_fk.map do |group|
490
+ row = group.first
413
491
  options = {
414
- column: row["column"],
415
492
  name: row["name"],
416
- primary_key: row["primary_key"]
493
+ on_update: extract_foreign_key_action(row["on_update"]),
494
+ on_delete: extract_foreign_key_action(row["on_delete"])
417
495
  }
418
496
 
419
- options[:on_update] = extract_foreign_key_action(row["on_update"])
420
- options[:on_delete] = extract_foreign_key_action(row["on_delete"])
497
+ if group.one?
498
+ options[:column] = unquote_identifier(row["column"])
499
+ options[:primary_key] = row["primary_key"]
500
+ else
501
+ options[:column] = group.map { |row| unquote_identifier(row["column"]) }
502
+ options[:primary_key] = group.map { |row| row["primary_key"] }
503
+ end
421
504
 
422
- ForeignKeyDefinition.new(table_name, row["to_table"], options)
505
+ ForeignKeyDefinition.new(table_name, unquote_identifier(row["to_table"]), options)
423
506
  end
424
507
  end
425
508
 
@@ -427,7 +510,7 @@ module ActiveRecord
427
510
  if supports_check_constraints?
428
511
  scope = quoted_scope(table_name)
429
512
 
430
- chk_info = exec_query(<<~SQL, "SCHEMA")
513
+ sql = <<~SQL
431
514
  SELECT cc.constraint_name AS 'name',
432
515
  cc.check_clause AS 'expression'
433
516
  FROM information_schema.check_constraints cc
@@ -437,13 +520,24 @@ module ActiveRecord
437
520
  AND tc.table_name = #{scope[:name]}
438
521
  AND cc.constraint_schema = #{scope[:schema]}
439
522
  SQL
523
+ sql += " AND cc.table_name = #{scope[:name]}" if mariadb?
524
+
525
+ chk_info = internal_exec_query(sql, "SCHEMA")
440
526
 
441
527
  chk_info.map do |row|
442
528
  options = {
443
529
  name: row["name"]
444
530
  }
445
531
  expression = row["expression"]
446
- expression = expression[1..-2] unless mariadb? # remove parentheses added by mysql
532
+ expression = expression[1..-2] if expression.start_with?("(") && expression.end_with?(")")
533
+ expression = strip_whitespace_characters(expression)
534
+
535
+ unless mariadb?
536
+ # MySQL returns check constraints expression in an already escaped form.
537
+ # This leads to duplicate escaping later (e.g. when the expression is used in the SchemaDumper).
538
+ expression = expression.gsub("\\'", "'")
539
+ end
540
+
447
541
  CheckConstraintDefinition.new(table_name, expression, options)
448
542
  end
449
543
  else
@@ -541,15 +635,39 @@ module ActiveRecord
541
635
  end
542
636
 
543
637
  def build_insert_sql(insert) # :nodoc:
544
- sql = +"INSERT #{insert.into} #{insert.values_list}"
545
-
546
- if insert.skip_duplicates?
547
- no_op_column = quote_column_name(insert.keys.first)
548
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
549
- elsif insert.update_duplicates?
550
- sql << " ON DUPLICATE KEY UPDATE "
551
- sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
552
- sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
638
+ no_op_column = quote_column_name(insert.keys.first)
639
+
640
+ # MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
641
+ # then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
642
+ if supports_insert_raw_alias_syntax?
643
+ values_alias = quote_table_name("#{insert.model.table_name}_values")
644
+ sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
645
+
646
+ if insert.skip_duplicates?
647
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
648
+ elsif insert.update_duplicates?
649
+ if insert.raw_update_sql?
650
+ sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
651
+ else
652
+ sql << " ON DUPLICATE KEY UPDATE "
653
+ sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
654
+ sql << insert.updatable_columns.map { |column| "#{column}=#{values_alias}.#{column}" }.join(",")
655
+ end
656
+ end
657
+ else
658
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
659
+
660
+ if insert.skip_duplicates?
661
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
662
+ elsif insert.update_duplicates?
663
+ sql << " ON DUPLICATE KEY UPDATE "
664
+ if insert.raw_update_sql?
665
+ sql << insert.raw_update_sql
666
+ else
667
+ sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
668
+ sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
669
+ end
670
+ end
553
671
  end
554
672
 
555
673
  sql
@@ -561,58 +679,99 @@ module ActiveRecord
561
679
  end
562
680
  end
563
681
 
564
- private
565
- def initialize_type_map(m = type_map)
566
- super
682
+ class << self
683
+ def extended_type_map(default_timezone: nil, emulate_booleans:) # :nodoc:
684
+ super(default_timezone: default_timezone).tap do |m|
685
+ if emulate_booleans
686
+ m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
687
+ end
688
+ end
689
+ end
567
690
 
568
- m.register_type(%r(char)i) do |sql_type|
569
- limit = extract_limit(sql_type)
570
- Type.lookup(:string, adapter: :mysql2, limit: limit)
691
+ private
692
+ def initialize_type_map(m)
693
+ super
694
+
695
+ m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
696
+ m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
697
+ m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
698
+ m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
699
+ m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
700
+ m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
701
+ m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
702
+ m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
703
+ m.register_type %r(^float)i, Type::Float.new(limit: 24)
704
+ m.register_type %r(^double)i, Type::Float.new(limit: 53)
705
+
706
+ register_integer_type m, %r(^bigint)i, limit: 8
707
+ register_integer_type m, %r(^int)i, limit: 4
708
+ register_integer_type m, %r(^mediumint)i, limit: 3
709
+ register_integer_type m, %r(^smallint)i, limit: 2
710
+ register_integer_type m, %r(^tinyint)i, limit: 1
711
+
712
+ m.alias_type %r(year)i, "integer"
713
+ m.alias_type %r(bit)i, "binary"
571
714
  end
572
715
 
573
- m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
574
- m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
575
- m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
576
- m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
577
- m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
578
- m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
579
- m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
580
- m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
581
- m.register_type %r(^float)i, Type::Float.new(limit: 24)
582
- m.register_type %r(^double)i, Type::Float.new(limit: 53)
583
-
584
- register_integer_type m, %r(^bigint)i, limit: 8
585
- register_integer_type m, %r(^int)i, limit: 4
586
- register_integer_type m, %r(^mediumint)i, limit: 3
587
- register_integer_type m, %r(^smallint)i, limit: 2
588
- register_integer_type m, %r(^tinyint)i, limit: 1
589
-
590
- m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
591
- m.alias_type %r(year)i, "integer"
592
- m.alias_type %r(bit)i, "binary"
593
-
594
- m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
595
- m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
596
- end
597
-
598
- def register_integer_type(mapping, key, **options)
599
- mapping.register_type(key) do |sql_type|
600
- if /\bunsigned\b/.match?(sql_type)
601
- Type::UnsignedInteger.new(**options)
716
+ def register_integer_type(mapping, key, **options)
717
+ mapping.register_type(key) do |sql_type|
718
+ if /\bunsigned\b/.match?(sql_type)
719
+ Type::UnsignedInteger.new(**options)
720
+ else
721
+ Type::Integer.new(**options)
722
+ end
723
+ end
724
+ end
725
+
726
+ def extract_precision(sql_type)
727
+ if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
728
+ super || 0
602
729
  else
603
- Type::Integer.new(**options)
730
+ super
604
731
  end
605
732
  end
733
+ end
734
+
735
+ EXTENDED_TYPE_MAPS = Concurrent::Map.new
736
+ EMULATE_BOOLEANS_TRUE = { emulate_booleans: true }.freeze
737
+
738
+ private
739
+ def strip_whitespace_characters(expression)
740
+ expression = expression.gsub(/\\n|\\\\/, "")
741
+ expression = expression.gsub(/\s{2,}/, " ")
742
+ expression
743
+ end
744
+
745
+ def extended_type_map_key
746
+ if @default_timezone
747
+ { default_timezone: @default_timezone, emulate_booleans: emulate_booleans }
748
+ elsif emulate_booleans
749
+ EMULATE_BOOLEANS_TRUE
750
+ end
606
751
  end
607
752
 
608
- def extract_precision(sql_type)
609
- if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
610
- super || 0
611
- else
612
- super
753
+ def handle_warnings(sql)
754
+ return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
755
+
756
+ @affected_rows_before_warnings = @raw_connection.affected_rows
757
+ result = @raw_connection.query("SHOW WARNINGS")
758
+ result.each do |level, code, message|
759
+ warning = SQLWarning.new(message, code, level, sql, @pool)
760
+ next if warning_ignored?(warning)
761
+
762
+ ActiveRecord.db_warnings_action.call(warning)
613
763
  end
614
764
  end
615
765
 
766
+ def warning_ignored?(warning)
767
+ warning.level == "Note" || super
768
+ end
769
+
770
+ # Make sure we carry over any changes to ActiveRecord.default_timezone that have been
771
+ # made since we established the connection
772
+ def sync_timezone_changes(raw_connection)
773
+ end
774
+
616
775
  # See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
617
776
  ER_DB_CREATE_EXISTS = 1007
618
777
  ER_FILSORT_ABORT = 1028
@@ -630,69 +789,59 @@ module ActiveRecord
630
789
  ER_CANNOT_CREATE_TABLE = 1005
631
790
  ER_LOCK_WAIT_TIMEOUT = 1205
632
791
  ER_QUERY_INTERRUPTED = 1317
792
+ ER_CONNECTION_KILLED = 1927
793
+ CR_SERVER_GONE_ERROR = 2006
794
+ CR_SERVER_LOST = 2013
633
795
  ER_QUERY_TIMEOUT = 3024
634
796
  ER_FK_INCOMPATIBLE_COLUMNS = 3780
797
+ ER_CLIENT_INTERACTION_TIMEOUT = 4031
635
798
 
636
799
  def translate_exception(exception, message:, sql:, binds:)
637
800
  case error_number(exception)
638
801
  when nil
639
802
  if exception.message.match?(/MySQL client is not connected/i)
640
- ConnectionNotEstablished.new(exception)
803
+ ConnectionNotEstablished.new(exception, connection_pool: @pool)
641
804
  else
642
805
  super
643
806
  end
807
+ when ER_CONNECTION_KILLED, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT
808
+ ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
644
809
  when ER_DB_CREATE_EXISTS
645
- DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
810
+ DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
646
811
  when ER_DUP_ENTRY
647
- RecordNotUnique.new(message, sql: sql, binds: binds)
812
+ RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
648
813
  when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
649
- InvalidForeignKey.new(message, sql: sql, binds: binds)
814
+ InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
650
815
  when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
651
- mismatched_foreign_key(message, sql: sql, binds: binds)
816
+ mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
652
817
  when ER_CANNOT_CREATE_TABLE
653
818
  if message.include?("errno: 150")
654
- mismatched_foreign_key(message, sql: sql, binds: binds)
819
+ mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
655
820
  else
656
821
  super
657
822
  end
658
823
  when ER_DATA_TOO_LONG
659
- ValueTooLong.new(message, sql: sql, binds: binds)
824
+ ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
660
825
  when ER_OUT_OF_RANGE
661
- RangeError.new(message, sql: sql, binds: binds)
826
+ RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
662
827
  when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
663
- NotNullViolation.new(message, sql: sql, binds: binds)
828
+ NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
664
829
  when ER_LOCK_DEADLOCK
665
- Deadlocked.new(message, sql: sql, binds: binds)
830
+ Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
666
831
  when ER_LOCK_WAIT_TIMEOUT
667
- LockWaitTimeout.new(message, sql: sql, binds: binds)
832
+ LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
668
833
  when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
669
- StatementTimeout.new(message, sql: sql, binds: binds)
834
+ StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
670
835
  when ER_QUERY_INTERRUPTED
671
- QueryCanceled.new(message, sql: sql, binds: binds)
836
+ QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
672
837
  else
673
838
  super
674
839
  end
675
840
  end
676
841
 
677
842
  def change_column_for_alter(table_name, column_name, type, **options)
678
- column = column_for(table_name, column_name)
679
- type ||= column.sql_type
680
-
681
- unless options.key?(:default)
682
- options[:default] = column.default
683
- end
684
-
685
- unless options.key?(:null)
686
- options[:null] = column.null
687
- end
688
-
689
- unless options.key?(:comment)
690
- options[:comment] = column.comment
691
- end
692
-
693
- td = create_table_definition(table_name)
694
- cd = td.new_column_definition(column.name, type, **options)
695
- schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
843
+ cd = build_change_column_definition(table_name, column_name, type, **options)
844
+ schema_creation.accept(cd)
696
845
  end
697
846
 
698
847
  def rename_column_for_alter(table_name, column_name, new_column_name)
@@ -706,7 +855,7 @@ module ActiveRecord
706
855
  comment: column.comment
707
856
  }
708
857
 
709
- current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
858
+ current_type = internal_exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
710
859
  td = create_table_definition(table_name)
711
860
  cd = td.new_column_definition(new_column_name, current_type, **options)
712
861
  schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
@@ -724,6 +873,10 @@ module ActiveRecord
724
873
  "DROP INDEX #{quote_column_name(index_name)}"
725
874
  end
726
875
 
876
+ def supports_insert_raw_alias_syntax?
877
+ !mariadb? && database_version >= "8.0.19"
878
+ end
879
+
727
880
  def supports_rename_index?
728
881
  if mariadb?
729
882
  database_version >= "10.5.2"
@@ -743,9 +896,6 @@ module ActiveRecord
743
896
  def configure_connection
744
897
  variables = @config.fetch(:variables, {}).stringify_keys
745
898
 
746
- # By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
747
- variables["sql_auto_is_null"] = 0
748
-
749
899
  # Increase timeout so the server doesn't disconnect us.
750
900
  wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
751
901
  wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
@@ -780,17 +930,16 @@ module ActiveRecord
780
930
  end
781
931
 
782
932
  # Gather up all of the SET variables...
783
- variable_assignments = variables.map do |k, v|
933
+ variable_assignments = variables.filter_map do |k, v|
784
934
  if defaults.include?(v)
785
935
  "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
786
936
  elsif !v.nil?
787
937
  "@@SESSION.#{k} = #{quote(v)}"
788
938
  end
789
- # or else nil; compact to clear nils out
790
- end.compact.join(", ")
939
+ end.join(", ")
791
940
 
792
941
  # ...and send them all in one query
793
- execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
942
+ internal_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}")
794
943
  end
795
944
 
796
945
  def column_definitions(table_name) # :nodoc:
@@ -800,7 +949,7 @@ module ActiveRecord
800
949
  end
801
950
 
802
951
  def create_table_info(table_name) # :nodoc:
803
- exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
952
+ internal_exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
804
953
  end
805
954
 
806
955
  def arel_visitor
@@ -811,18 +960,17 @@ module ActiveRecord
811
960
  StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
812
961
  end
813
962
 
814
- def mismatched_foreign_key(message, sql:, binds:)
963
+ def mismatched_foreign_key_details(message:, sql:)
964
+ foreign_key_pat =
965
+ /Referencing column '(\w+)' and referenced/i =~ message ? $1 : '\w+'
966
+
815
967
  match = %r/
816
968
  (?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
817
- FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
969
+ FOREIGN\s+KEY\s*\(`?(?<foreign_key>#{foreign_key_pat})`?\)\s*
818
970
  REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
819
971
  /xmi.match(sql)
820
972
 
821
- options = {
822
- message: message,
823
- sql: sql,
824
- binds: binds,
825
- }
973
+ options = {}
826
974
 
827
975
  if match
828
976
  options[:table] = match[:table]
@@ -832,24 +980,29 @@ module ActiveRecord
832
980
  options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
833
981
  end
834
982
 
835
- MismatchedForeignKey.new(**options)
983
+ options
836
984
  end
837
985
 
838
- def version_string(full_version_string)
839
- full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
840
- end
986
+ def mismatched_foreign_key(message, sql:, binds:, connection_pool:)
987
+ options = {
988
+ message: message,
989
+ sql: sql,
990
+ binds: binds,
991
+ connection_pool: connection_pool
992
+ }
841
993
 
842
- # Alias MysqlString to work Mashal.load(File.read("legacy_record.dump")).
843
- # TODO: Remove the constant alias once Rails 6.1 has released.
844
- MysqlString = Type::String # :nodoc:
994
+ if sql
995
+ options.update mismatched_foreign_key_details(message: message, sql: sql)
996
+ else
997
+ options[:query_parser] = ->(sql) { mismatched_foreign_key_details(message: message, sql: sql) }
998
+ end
845
999
 
846
- ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
847
- Type::ImmutableString.new(true: "1", false: "0", **args)
1000
+ MismatchedForeignKey.new(**options)
848
1001
  end
849
- ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
850
- Type::String.new(true: "1", false: "0", **args)
1002
+
1003
+ def version_string(full_version_string)
1004
+ full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
851
1005
  end
852
- ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
853
1006
  end
854
1007
  end
855
1008
  end