activerecord 7.0.8.7 → 7.2.3

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 (283) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +781 -1777
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +30 -30
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +31 -23
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +40 -9
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +35 -21
  20. data/lib/active_record/associations/collection_proxy.rb +29 -11
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +21 -14
  24. data/lib/active_record/associations/has_many_through_association.rb +17 -7
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +4 -3
  27. data/lib/active_record/associations/join_dependency.rb +10 -10
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +354 -485
  36. data/lib/active_record/attribute_assignment.rb +0 -4
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +131 -32
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +153 -33
  47. data/lib/active_record/attributes.rb +96 -71
  48. data/lib/active_record/autosave_association.rb +81 -39
  49. data/lib/active_record/base.rb +11 -7
  50. data/lib/active_record/callbacks.rb +11 -25
  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 -42
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +343 -91
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +229 -64
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +142 -12
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -129
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +539 -111
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +289 -128
  69. data/lib/active_record/connection_adapters/column.rb +9 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +60 -55
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +108 -68
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  89. data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
  90. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  91. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  92. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -2
  93. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +371 -64
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +374 -203
  96. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  97. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -45
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  101. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
  102. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  103. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +51 -8
  104. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +298 -113
  105. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  106. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  107. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  108. data/lib/active_record/connection_adapters.rb +124 -1
  109. data/lib/active_record/connection_handling.rb +101 -105
  110. data/lib/active_record/core.rb +273 -178
  111. data/lib/active_record/counter_cache.rb +69 -35
  112. data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -3
  113. data/lib/active_record/database_configurations/database_config.rb +26 -5
  114. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  115. data/lib/active_record/database_configurations/url_config.rb +37 -12
  116. data/lib/active_record/database_configurations.rb +87 -34
  117. data/lib/active_record/delegated_type.rb +56 -27
  118. data/lib/active_record/deprecator.rb +7 -0
  119. data/lib/active_record/destroy_association_async_job.rb +3 -1
  120. data/lib/active_record/dynamic_matchers.rb +2 -2
  121. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  122. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  123. data/lib/active_record/encryption/config.rb +25 -1
  124. data/lib/active_record/encryption/configurable.rb +12 -19
  125. data/lib/active_record/encryption/context.rb +10 -3
  126. data/lib/active_record/encryption/contexts.rb +5 -1
  127. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  128. data/lib/active_record/encryption/encryptable_record.rb +46 -22
  129. data/lib/active_record/encryption/encrypted_attribute_type.rb +48 -13
  130. data/lib/active_record/encryption/encryptor.rb +35 -19
  131. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  132. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  133. data/lib/active_record/encryption/key_generator.rb +12 -1
  134. data/lib/active_record/encryption/key_provider.rb +1 -1
  135. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  136. data/lib/active_record/encryption/message_serializer.rb +6 -0
  137. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  138. data/lib/active_record/encryption/properties.rb +3 -3
  139. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  140. data/lib/active_record/encryption/scheme.rb +22 -21
  141. data/lib/active_record/encryption.rb +3 -0
  142. data/lib/active_record/enum.rb +130 -28
  143. data/lib/active_record/errors.rb +154 -34
  144. data/lib/active_record/explain.rb +21 -12
  145. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  146. data/lib/active_record/fixture_set/render_context.rb +2 -0
  147. data/lib/active_record/fixture_set/table_row.rb +48 -10
  148. data/lib/active_record/fixtures.rb +167 -97
  149. data/lib/active_record/future_result.rb +47 -8
  150. data/lib/active_record/gem_version.rb +4 -4
  151. data/lib/active_record/inheritance.rb +34 -18
  152. data/lib/active_record/insert_all.rb +72 -22
  153. data/lib/active_record/integration.rb +11 -8
  154. data/lib/active_record/internal_metadata.rb +124 -20
  155. data/lib/active_record/locking/optimistic.rb +8 -7
  156. data/lib/active_record/locking/pessimistic.rb +5 -2
  157. data/lib/active_record/log_subscriber.rb +18 -22
  158. data/lib/active_record/marshalling.rb +59 -0
  159. data/lib/active_record/message_pack.rb +124 -0
  160. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  161. data/lib/active_record/middleware/database_selector.rb +6 -8
  162. data/lib/active_record/middleware/shard_selector.rb +3 -1
  163. data/lib/active_record/migration/command_recorder.rb +106 -8
  164. data/lib/active_record/migration/compatibility.rb +147 -5
  165. data/lib/active_record/migration/default_strategy.rb +22 -0
  166. data/lib/active_record/migration/execution_strategy.rb +19 -0
  167. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  168. data/lib/active_record/migration.rb +236 -118
  169. data/lib/active_record/model_schema.rb +90 -102
  170. data/lib/active_record/nested_attributes.rb +48 -11
  171. data/lib/active_record/normalization.rb +163 -0
  172. data/lib/active_record/persistence.rb +168 -339
  173. data/lib/active_record/promise.rb +84 -0
  174. data/lib/active_record/query_cache.rb +18 -25
  175. data/lib/active_record/query_logs.rb +96 -52
  176. data/lib/active_record/query_logs_formatter.rb +41 -0
  177. data/lib/active_record/querying.rb +35 -10
  178. data/lib/active_record/railtie.rb +131 -87
  179. data/lib/active_record/railties/controller_runtime.rb +22 -7
  180. data/lib/active_record/railties/databases.rake +147 -155
  181. data/lib/active_record/railties/job_runtime.rb +23 -0
  182. data/lib/active_record/readonly_attributes.rb +32 -5
  183. data/lib/active_record/reflection.rb +267 -69
  184. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  185. data/lib/active_record/relation/batches.rb +198 -63
  186. data/lib/active_record/relation/calculations.rb +270 -108
  187. data/lib/active_record/relation/delegation.rb +30 -19
  188. data/lib/active_record/relation/finder_methods.rb +97 -21
  189. data/lib/active_record/relation/merger.rb +6 -6
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -3
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  193. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  194. data/lib/active_record/relation/predicate_builder.rb +28 -16
  195. data/lib/active_record/relation/query_attribute.rb +3 -2
  196. data/lib/active_record/relation/query_methods.rb +585 -109
  197. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  198. data/lib/active_record/relation/spawn_methods.rb +5 -4
  199. data/lib/active_record/relation/where_clause.rb +15 -21
  200. data/lib/active_record/relation.rb +592 -92
  201. data/lib/active_record/result.rb +49 -48
  202. data/lib/active_record/runtime_registry.rb +63 -1
  203. data/lib/active_record/sanitization.rb +70 -25
  204. data/lib/active_record/schema.rb +8 -7
  205. data/lib/active_record/schema_dumper.rb +90 -23
  206. data/lib/active_record/schema_migration.rb +75 -24
  207. data/lib/active_record/scoping/default.rb +15 -5
  208. data/lib/active_record/scoping/named.rb +3 -2
  209. data/lib/active_record/scoping.rb +2 -1
  210. data/lib/active_record/secure_password.rb +60 -0
  211. data/lib/active_record/secure_token.rb +21 -3
  212. data/lib/active_record/signed_id.rb +33 -11
  213. data/lib/active_record/statement_cache.rb +7 -7
  214. data/lib/active_record/store.rb +8 -8
  215. data/lib/active_record/suppressor.rb +3 -1
  216. data/lib/active_record/table_metadata.rb +1 -1
  217. data/lib/active_record/tasks/database_tasks.rb +190 -118
  218. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  219. data/lib/active_record/tasks/postgresql_database_tasks.rb +23 -13
  220. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  221. data/lib/active_record/test_fixtures.rb +170 -155
  222. data/lib/active_record/testing/query_assertions.rb +121 -0
  223. data/lib/active_record/timestamp.rb +31 -17
  224. data/lib/active_record/token_for.rb +123 -0
  225. data/lib/active_record/touch_later.rb +12 -7
  226. data/lib/active_record/transaction.rb +132 -0
  227. data/lib/active_record/transactions.rb +108 -24
  228. data/lib/active_record/translation.rb +0 -2
  229. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  230. data/lib/active_record/type/internal/timezone.rb +7 -2
  231. data/lib/active_record/type/serialized.rb +1 -3
  232. data/lib/active_record/type/time.rb +4 -0
  233. data/lib/active_record/type_caster/connection.rb +4 -4
  234. data/lib/active_record/validations/absence.rb +1 -1
  235. data/lib/active_record/validations/associated.rb +9 -3
  236. data/lib/active_record/validations/numericality.rb +5 -4
  237. data/lib/active_record/validations/presence.rb +5 -28
  238. data/lib/active_record/validations/uniqueness.rb +61 -11
  239. data/lib/active_record/validations.rb +12 -5
  240. data/lib/active_record/version.rb +1 -1
  241. data/lib/active_record.rb +247 -33
  242. data/lib/arel/alias_predication.rb +1 -1
  243. data/lib/arel/collectors/bind.rb +3 -1
  244. data/lib/arel/collectors/composite.rb +7 -0
  245. data/lib/arel/collectors/sql_string.rb +1 -1
  246. data/lib/arel/collectors/substitute_binds.rb +1 -1
  247. data/lib/arel/crud.rb +2 -0
  248. data/lib/arel/delete_manager.rb +5 -0
  249. data/lib/arel/errors.rb +10 -0
  250. data/lib/arel/factory_methods.rb +4 -0
  251. data/lib/arel/nodes/binary.rb +6 -7
  252. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  253. data/lib/arel/nodes/cte.rb +36 -0
  254. data/lib/arel/nodes/delete_statement.rb +4 -2
  255. data/lib/arel/nodes/fragments.rb +35 -0
  256. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  257. data/lib/arel/nodes/leading_join.rb +8 -0
  258. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  259. data/lib/arel/nodes/node.rb +115 -5
  260. data/lib/arel/nodes/sql_literal.rb +13 -0
  261. data/lib/arel/nodes/table_alias.rb +4 -0
  262. data/lib/arel/nodes/update_statement.rb +4 -2
  263. data/lib/arel/nodes.rb +6 -2
  264. data/lib/arel/predications.rb +3 -1
  265. data/lib/arel/select_manager.rb +7 -3
  266. data/lib/arel/table.rb +9 -5
  267. data/lib/arel/tree_manager.rb +8 -3
  268. data/lib/arel/update_manager.rb +7 -1
  269. data/lib/arel/visitors/dot.rb +3 -0
  270. data/lib/arel/visitors/mysql.rb +17 -5
  271. data/lib/arel/visitors/postgresql.rb +1 -12
  272. data/lib/arel/visitors/sqlite.rb +25 -0
  273. data/lib/arel/visitors/to_sql.rb +114 -34
  274. data/lib/arel/visitors/visitor.rb +2 -2
  275. data/lib/arel.rb +21 -3
  276. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  277. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  278. data/lib/rails/generators/active_record/migration.rb +3 -1
  279. data/lib/rails/generators/active_record/model/USAGE +113 -0
  280. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  281. metadata +56 -17
  282. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  283. data/lib/active_record/null_relation.rb +0 -63
@@ -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
 
@@ -51,8 +53,34 @@ module ActiveRecord
51
53
  end
52
54
  end
53
55
 
54
- def initialize(connection, logger, connection_options, config)
55
- 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
56
84
  end
57
85
 
58
86
  def get_database_version # :nodoc:
@@ -81,6 +109,10 @@ module ActiveRecord
81
109
  true
82
110
  end
83
111
 
112
+ def supports_restart_db_transaction?
113
+ true
114
+ end
115
+
84
116
  def supports_explain?
85
117
  true
86
118
  end
@@ -95,7 +127,7 @@ module ActiveRecord
95
127
 
96
128
  def supports_check_constraints?
97
129
  if mariadb?
98
- database_version >= "10.2.1"
130
+ database_version >= "10.3.10" || (database_version < "10.3" && database_version >= "10.2.22")
99
131
  else
100
132
  database_version >= "8.0.16"
101
133
  end
@@ -138,6 +170,14 @@ module ActiveRecord
138
170
  true
139
171
  end
140
172
 
173
+ def supports_insert_returning?
174
+ mariadb? && database_version >= "10.5.0"
175
+ end
176
+
177
+ def return_value_after_insert?(column) # :nodoc:
178
+ supports_insert_returning? ? column.auto_populated? : column.auto_increment?
179
+ end
180
+
141
181
  def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
142
182
  query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
143
183
  end
@@ -182,7 +222,7 @@ module ActiveRecord
182
222
  update("SET FOREIGN_KEY_CHECKS = 0")
183
223
  yield
184
224
  ensure
185
- update("SET FOREIGN_KEY_CHECKS = #{old}")
225
+ update("SET FOREIGN_KEY_CHECKS = #{old}") if active?
186
226
  end
187
227
  end
188
228
 
@@ -190,33 +230,36 @@ module ActiveRecord
190
230
  # DATABASE STATEMENTS ======================================
191
231
  #++
192
232
 
193
- # Executes the SQL statement in the context of this connection.
194
- def execute(sql, name = nil, async: false)
195
- raw_execute(sql, name, async: async)
196
- end
197
-
198
233
  # Mysql2Adapter doesn't have to free a result after using it, but we use this method
199
234
  # to write stuff in an abstract way without concerning ourselves about whether it
200
235
  # needs to be explicitly freed or not.
201
- def execute_and_free(sql, name = nil, async: false) # :nodoc:
202
- yield execute(sql, name, async: async)
236
+ def execute_and_free(sql, name = nil, async: false, allow_retry: false) # :nodoc:
237
+ sql = transform_query(sql)
238
+ check_if_write_query(sql)
239
+
240
+ mark_transaction_written_if_write(sql)
241
+ yield raw_execute(sql, name, async: async, allow_retry: allow_retry)
203
242
  end
204
243
 
205
244
  def begin_db_transaction # :nodoc:
206
- execute("BEGIN", "TRANSACTION")
245
+ internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
207
246
  end
208
247
 
209
248
  def begin_isolated_db_transaction(isolation) # :nodoc:
210
- execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
249
+ internal_execute("SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
211
250
  begin_db_transaction
212
251
  end
213
252
 
214
253
  def commit_db_transaction # :nodoc:
215
- execute("COMMIT", "TRANSACTION")
254
+ internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
216
255
  end
217
256
 
218
257
  def exec_rollback_db_transaction # :nodoc:
219
- execute("ROLLBACK", "TRANSACTION")
258
+ internal_execute("ROLLBACK", "TRANSACTION", allow_retry: false, materialize_transactions: true)
259
+ end
260
+
261
+ def exec_restart_db_transaction # :nodoc:
262
+ internal_execute("ROLLBACK AND CHAIN", "TRANSACTION", allow_retry: false, materialize_transactions: true)
220
263
  end
221
264
 
222
265
  def empty_insert_statement_value(primary_key = nil) # :nodoc:
@@ -296,11 +339,12 @@ module ActiveRecord
296
339
  #
297
340
  # Example:
298
341
  # rename_table('octopuses', 'octopi')
299
- def rename_table(table_name, new_name)
342
+ def rename_table(table_name, new_name, **options)
343
+ validate_table_length!(new_name) unless options[:_uses_legacy_table_name]
300
344
  schema_cache.clear_data_source_cache!(table_name.to_s)
301
345
  schema_cache.clear_data_source_cache!(new_name.to_s)
302
346
  execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
303
- rename_table_indexes(table_name, new_name)
347
+ rename_table_indexes(table_name, new_name, **options)
304
348
  end
305
349
 
306
350
  # Drops a table from the database.
@@ -334,11 +378,20 @@ module ActiveRecord
334
378
  end
335
379
 
336
380
  def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
381
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
382
+ end
383
+
384
+ def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
385
+ column = column_for(table_name, column_name)
386
+ return unless column
387
+
337
388
  default = extract_new_default_value(default_or_changes)
338
- change_column table_name, column_name, nil, default: default
389
+ ChangeColumnDefaultDefinition.new(column, default)
339
390
  end
340
391
 
341
392
  def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
393
+ validate_change_column_null_argument!(null)
394
+
342
395
  unless null || default.nil?
343
396
  execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
344
397
  end
@@ -355,18 +408,64 @@ module ActiveRecord
355
408
  execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, **options)}")
356
409
  end
357
410
 
411
+ # Builds a ChangeColumnDefinition object.
412
+ #
413
+ # This definition object contains information about the column change that would occur
414
+ # if the same arguments were passed to #change_column. See #change_column for information about
415
+ # passing a +table_name+, +column_name+, +type+ and other options that can be passed.
416
+ def build_change_column_definition(table_name, column_name, type, **options) # :nodoc:
417
+ column = column_for(table_name, column_name)
418
+ type ||= column.sql_type
419
+
420
+ unless options.key?(:default)
421
+ options[:default] = if column.default_function
422
+ -> { column.default_function }
423
+ else
424
+ column.default
425
+ end
426
+ end
427
+
428
+ unless options.key?(:null)
429
+ options[:null] = column.null
430
+ end
431
+
432
+ unless options.key?(:comment)
433
+ options[:comment] = column.comment
434
+ end
435
+
436
+ if options[:collation] == :no_collation
437
+ options.delete(:collation)
438
+ else
439
+ options[:collation] ||= column.collation if text_type?(type)
440
+ end
441
+
442
+ unless options.key?(:auto_increment)
443
+ options[:auto_increment] = column.auto_increment?
444
+ end
445
+
446
+ td = create_table_definition(table_name)
447
+ cd = td.new_column_definition(column.name, type, **options)
448
+ ChangeColumnDefinition.new(cd, column.name)
449
+ end
450
+
358
451
  def rename_column(table_name, column_name, new_column_name) # :nodoc:
359
452
  execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
360
453
  rename_column_indexes(table_name, column_name, new_column_name)
361
454
  end
362
455
 
363
456
  def add_index(table_name, column_name, **options) # :nodoc:
457
+ create_index = build_create_index_definition(table_name, column_name, **options)
458
+ return unless create_index
459
+
460
+ execute schema_creation.accept(create_index)
461
+ end
462
+
463
+ def build_create_index_definition(table_name, column_name, **options) # :nodoc:
364
464
  index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
365
465
 
366
466
  return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
367
467
 
368
- create_index = CreateIndexDefinition.new(index, algorithm)
369
- execute schema_creation.accept(create_index)
468
+ CreateIndexDefinition.new(index, algorithm)
370
469
  end
371
470
 
372
471
  def add_sql_comment!(sql, comment) # :nodoc:
@@ -379,11 +478,13 @@ module ActiveRecord
379
478
 
380
479
  scope = quoted_scope(table_name)
381
480
 
382
- fk_info = exec_query(<<~SQL, "SCHEMA")
481
+ # MySQL returns 1 row for each column of composite foreign keys.
482
+ fk_info = internal_exec_query(<<~SQL, "SCHEMA")
383
483
  SELECT fk.referenced_table_name AS 'to_table',
384
484
  fk.referenced_column_name AS 'primary_key',
385
485
  fk.column_name AS 'column',
386
486
  fk.constraint_name AS 'name',
487
+ fk.ordinal_position AS 'position',
387
488
  rc.update_rule AS 'on_update',
388
489
  rc.delete_rule AS 'on_delete'
389
490
  FROM information_schema.referential_constraints rc
@@ -396,15 +497,22 @@ module ActiveRecord
396
497
  AND rc.table_name = #{scope[:name]}
397
498
  SQL
398
499
 
399
- fk_info.map do |row|
500
+ grouped_fk = fk_info.group_by { |row| row["name"] }.values.each { |group| group.sort_by! { |row| row["position"] } }
501
+ grouped_fk.map do |group|
502
+ row = group.first
400
503
  options = {
401
- column: unquote_identifier(row["column"]),
402
504
  name: row["name"],
403
- primary_key: row["primary_key"]
505
+ on_update: extract_foreign_key_action(row["on_update"]),
506
+ on_delete: extract_foreign_key_action(row["on_delete"])
404
507
  }
405
508
 
406
- options[:on_update] = extract_foreign_key_action(row["on_update"])
407
- options[:on_delete] = extract_foreign_key_action(row["on_delete"])
509
+ if group.one?
510
+ options[:column] = unquote_identifier(row["column"])
511
+ options[:primary_key] = row["primary_key"]
512
+ else
513
+ options[:column] = group.map { |row| unquote_identifier(row["column"]) }
514
+ options[:primary_key] = group.map { |row| row["primary_key"] }
515
+ end
408
516
 
409
517
  ForeignKeyDefinition.new(table_name, unquote_identifier(row["to_table"]), options)
410
518
  end
@@ -426,7 +534,7 @@ module ActiveRecord
426
534
  SQL
427
535
  sql += " AND cc.table_name = #{scope[:name]}" if mariadb?
428
536
 
429
- chk_info = exec_query(sql, "SCHEMA")
537
+ chk_info = internal_exec_query(sql, "SCHEMA")
430
538
 
431
539
  chk_info.map do |row|
432
540
  options = {
@@ -435,6 +543,13 @@ module ActiveRecord
435
543
  expression = row["expression"]
436
544
  expression = expression[1..-2] if expression.start_with?("(") && expression.end_with?(")")
437
545
  expression = strip_whitespace_characters(expression)
546
+
547
+ unless mariadb?
548
+ # MySQL returns check constraints expression in an already escaped form.
549
+ # This leads to duplicate escaping later (e.g. when the expression is used in the SchemaDumper).
550
+ expression = expression.gsub("\\'", "'")
551
+ end
552
+
438
553
  CheckConstraintDefinition.new(table_name, expression, options)
439
554
  end
440
555
  else
@@ -532,40 +647,81 @@ module ActiveRecord
532
647
  end
533
648
 
534
649
  def build_insert_sql(insert) # :nodoc:
535
- sql = +"INSERT #{insert.into} #{insert.values_list}"
536
-
537
- if insert.skip_duplicates?
538
- no_op_column = quote_column_name(insert.keys.first)
539
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
540
- elsif insert.update_duplicates?
541
- sql << " ON DUPLICATE KEY UPDATE "
542
- if insert.raw_update_sql?
543
- sql << insert.raw_update_sql
544
- else
545
- sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
546
- sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
650
+ # Can use any column as it will be assigned to itself.
651
+ no_op_column = quote_column_name(insert.keys.first) if insert.keys.first
652
+
653
+ # MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
654
+ # then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
655
+ if supports_insert_raw_alias_syntax?
656
+ quoted_table_name = insert.model.quoted_table_name
657
+ values_alias = quote_table_name("#{insert.model.table_name.parameterize}_values")
658
+ sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
659
+
660
+ if insert.skip_duplicates?
661
+ if no_op_column
662
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{quoted_table_name}.#{no_op_column}"
663
+ end
664
+ elsif insert.update_duplicates?
665
+ if insert.raw_update_sql?
666
+ sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
667
+ else
668
+ sql << " ON DUPLICATE KEY UPDATE "
669
+ sql << insert.touch_model_timestamps_unless { |column| "#{quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
670
+ sql << insert.updatable_columns.map { |column| "#{column}=#{values_alias}.#{column}" }.join(",")
671
+ end
672
+ end
673
+ else
674
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
675
+
676
+ if insert.skip_duplicates?
677
+ if no_op_column
678
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
679
+ end
680
+ elsif insert.update_duplicates?
681
+ sql << " ON DUPLICATE KEY UPDATE "
682
+ if insert.raw_update_sql?
683
+ sql << insert.raw_update_sql
684
+ else
685
+ sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
686
+ sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
687
+ end
547
688
  end
548
689
  end
549
690
 
691
+ sql << " RETURNING #{insert.returning}" if insert.returning
550
692
  sql
551
693
  end
552
694
 
553
695
  def check_version # :nodoc:
554
696
  if database_version < "5.5.8"
555
- raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
697
+ raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
698
+ end
699
+ end
700
+
701
+ #--
702
+ # QUOTING ==================================================
703
+ #++
704
+
705
+ # Quotes strings for use in SQL input.
706
+ def quote_string(string)
707
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
708
+ connection.escape(string)
556
709
  end
557
710
  end
558
711
 
559
712
  class << self
713
+ def extended_type_map(default_timezone: nil, emulate_booleans:) # :nodoc:
714
+ super(default_timezone: default_timezone).tap do |m|
715
+ if emulate_booleans
716
+ m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
717
+ end
718
+ end
719
+ end
720
+
560
721
  private
561
722
  def initialize_type_map(m)
562
723
  super
563
724
 
564
- m.register_type(%r(char)i) do |sql_type|
565
- limit = extract_limit(sql_type)
566
- Type.lookup(:string, adapter: :mysql2, limit: limit)
567
- end
568
-
569
725
  m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
570
726
  m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
571
727
  m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
@@ -585,9 +741,6 @@ module ActiveRecord
585
741
 
586
742
  m.alias_type %r(year)i, "integer"
587
743
  m.alias_type %r(bit)i, "binary"
588
-
589
- m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
590
- m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
591
744
  end
592
745
 
593
746
  def register_integer_type(mapping, key, **options)
@@ -609,41 +762,53 @@ module ActiveRecord
609
762
  end
610
763
  end
611
764
 
612
- TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
613
- TYPE_MAP_WITH_BOOLEAN = Type::TypeMap.new(TYPE_MAP).tap do |m|
614
- m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
615
- end
765
+ EXTENDED_TYPE_MAPS = Concurrent::Map.new
766
+ EMULATE_BOOLEANS_TRUE = { emulate_booleans: true }.freeze
616
767
 
617
768
  private
618
769
  def strip_whitespace_characters(expression)
619
- expression = expression.gsub(/\\n|\\\\/, "")
620
- expression = expression.gsub(/\s{2,}/, " ")
621
- expression
770
+ expression.gsub('\\\n', "").gsub("x0A", "").squish
622
771
  end
623
772
 
624
- def text_type?(type)
625
- TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
773
+ def extended_type_map_key
774
+ if @default_timezone
775
+ { default_timezone: @default_timezone, emulate_booleans: emulate_booleans }
776
+ elsif emulate_booleans
777
+ EMULATE_BOOLEANS_TRUE
778
+ end
626
779
  end
627
780
 
628
- def type_map
629
- emulate_booleans ? TYPE_MAP_WITH_BOOLEAN : TYPE_MAP
630
- end
781
+ def handle_warnings(sql)
782
+ return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
631
783
 
632
- def raw_execute(sql, name, async: false)
633
- materialize_transactions
634
- mark_transaction_written_if_write(sql)
784
+ @affected_rows_before_warnings = @raw_connection.affected_rows
785
+ warning_count = @raw_connection.warning_count
786
+ result = @raw_connection.query("SHOW WARNINGS")
787
+ result = [
788
+ ["Warning", nil, "Query had warning_count=#{warning_count} but ‘SHOW WARNINGS’ did not return the warnings. Check MySQL logs or database configuration."],
789
+ ] if result.count == 0
790
+ result.each do |level, code, message|
791
+ warning = SQLWarning.new(message, code, level, sql, @pool)
792
+ next if warning_ignored?(warning)
635
793
 
636
- log(sql, name, async: async) do
637
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
638
- @connection.query(sql)
639
- end
794
+ ActiveRecord.db_warnings_action.call(warning)
640
795
  end
641
796
  end
642
797
 
798
+ def warning_ignored?(warning)
799
+ warning.level == "Note" || super
800
+ end
801
+
802
+ # Make sure we carry over any changes to ActiveRecord.default_timezone that have been
803
+ # made since we established the connection
804
+ def sync_timezone_changes(raw_connection)
805
+ end
806
+
643
807
  # See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
644
808
  ER_DB_CREATE_EXISTS = 1007
645
809
  ER_FILSORT_ABORT = 1028
646
810
  ER_DUP_ENTRY = 1062
811
+ ER_SERVER_SHUTDOWN = 1053
647
812
  ER_NOT_NULL_VIOLATION = 1048
648
813
  ER_NO_REFERENCED_ROW = 1216
649
814
  ER_ROW_IS_REFERENCED = 1217
@@ -657,77 +822,59 @@ module ActiveRecord
657
822
  ER_CANNOT_CREATE_TABLE = 1005
658
823
  ER_LOCK_WAIT_TIMEOUT = 1205
659
824
  ER_QUERY_INTERRUPTED = 1317
825
+ ER_CONNECTION_KILLED = 1927
826
+ CR_SERVER_GONE_ERROR = 2006
827
+ CR_SERVER_LOST = 2013
660
828
  ER_QUERY_TIMEOUT = 3024
661
829
  ER_FK_INCOMPATIBLE_COLUMNS = 3780
830
+ ER_CLIENT_INTERACTION_TIMEOUT = 4031
662
831
 
663
832
  def translate_exception(exception, message:, sql:, binds:)
664
833
  case error_number(exception)
665
834
  when nil
666
835
  if exception.message.match?(/MySQL client is not connected/i)
667
- ConnectionNotEstablished.new(exception)
836
+ ConnectionNotEstablished.new(exception, connection_pool: @pool)
668
837
  else
669
838
  super
670
839
  end
840
+ when ER_CONNECTION_KILLED, ER_SERVER_SHUTDOWN, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT
841
+ ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
671
842
  when ER_DB_CREATE_EXISTS
672
- DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
843
+ DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
673
844
  when ER_DUP_ENTRY
674
- RecordNotUnique.new(message, sql: sql, binds: binds)
845
+ RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
675
846
  when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
676
- InvalidForeignKey.new(message, sql: sql, binds: binds)
847
+ InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
677
848
  when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
678
- mismatched_foreign_key(message, sql: sql, binds: binds)
849
+ mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
679
850
  when ER_CANNOT_CREATE_TABLE
680
851
  if message.include?("errno: 150")
681
- mismatched_foreign_key(message, sql: sql, binds: binds)
852
+ mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
682
853
  else
683
854
  super
684
855
  end
685
856
  when ER_DATA_TOO_LONG
686
- ValueTooLong.new(message, sql: sql, binds: binds)
857
+ ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
687
858
  when ER_OUT_OF_RANGE
688
- RangeError.new(message, sql: sql, binds: binds)
859
+ RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
689
860
  when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
690
- NotNullViolation.new(message, sql: sql, binds: binds)
861
+ NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
691
862
  when ER_LOCK_DEADLOCK
692
- Deadlocked.new(message, sql: sql, binds: binds)
863
+ Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
693
864
  when ER_LOCK_WAIT_TIMEOUT
694
- LockWaitTimeout.new(message, sql: sql, binds: binds)
865
+ LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
695
866
  when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
696
- StatementTimeout.new(message, sql: sql, binds: binds)
867
+ StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
697
868
  when ER_QUERY_INTERRUPTED
698
- QueryCanceled.new(message, sql: sql, binds: binds)
869
+ QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
699
870
  else
700
871
  super
701
872
  end
702
873
  end
703
874
 
704
875
  def change_column_for_alter(table_name, column_name, type, **options)
705
- column = column_for(table_name, column_name)
706
- type ||= column.sql_type
707
-
708
- unless options.key?(:default)
709
- options[:default] = column.default
710
- end
711
-
712
- unless options.key?(:null)
713
- options[:null] = column.null
714
- end
715
-
716
- unless options.key?(:comment)
717
- options[:comment] = column.comment
718
- end
719
-
720
- unless options.key?(:collation)
721
- options[:collation] = column.collation if text_type?(type)
722
- end
723
-
724
- unless options.key?(:auto_increment)
725
- options[:auto_increment] = column.auto_increment?
726
- end
727
-
728
- td = create_table_definition(table_name)
729
- cd = td.new_column_definition(column.name, type, **options)
730
- schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
876
+ cd = build_change_column_definition(table_name, column_name, type, **options)
877
+ schema_creation.accept(cd)
731
878
  end
732
879
 
733
880
  def rename_column_for_alter(table_name, column_name, new_column_name)
@@ -741,7 +888,7 @@ module ActiveRecord
741
888
  comment: column.comment
742
889
  }
743
890
 
744
- current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
891
+ current_type = internal_exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
745
892
  td = create_table_definition(table_name)
746
893
  cd = td.new_column_definition(new_column_name, current_type, **options)
747
894
  schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
@@ -759,6 +906,10 @@ module ActiveRecord
759
906
  "DROP INDEX #{quote_column_name(index_name)}"
760
907
  end
761
908
 
909
+ def supports_insert_raw_alias_syntax?
910
+ !mariadb? && database_version >= "8.0.19"
911
+ end
912
+
762
913
  def supports_rename_index?
763
914
  if mariadb?
764
915
  database_version >= "10.5.2"
@@ -776,11 +927,9 @@ module ActiveRecord
776
927
  end
777
928
 
778
929
  def configure_connection
930
+ super
779
931
  variables = @config.fetch(:variables, {}).stringify_keys
780
932
 
781
- # By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
782
- variables["sql_auto_is_null"] = 0
783
-
784
933
  # Increase timeout so the server doesn't disconnect us.
785
934
  wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
786
935
  wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
@@ -824,7 +973,7 @@ module ActiveRecord
824
973
  end.join(", ")
825
974
 
826
975
  # ...and send them all in one query
827
- execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
976
+ internal_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}")
828
977
  end
829
978
 
830
979
  def column_definitions(table_name) # :nodoc:
@@ -834,7 +983,7 @@ module ActiveRecord
834
983
  end
835
984
 
836
985
  def create_table_info(table_name) # :nodoc:
837
- exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
986
+ internal_exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
838
987
  end
839
988
 
840
989
  def arel_visitor
@@ -845,18 +994,17 @@ module ActiveRecord
845
994
  StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
846
995
  end
847
996
 
848
- def mismatched_foreign_key(message, sql:, binds:)
997
+ def mismatched_foreign_key_details(message:, sql:)
998
+ foreign_key_pat =
999
+ /Referencing column '(\w+)' and referenced/i =~ message ? $1 : '\w+'
1000
+
849
1001
  match = %r/
850
1002
  (?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
851
- FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
1003
+ FOREIGN\s+KEY\s*\(`?(?<foreign_key>#{foreign_key_pat})`?\)\s*
852
1004
  REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
853
1005
  /xmi.match(sql)
854
1006
 
855
- options = {
856
- message: message,
857
- sql: sql,
858
- binds: binds,
859
- }
1007
+ options = {}
860
1008
 
861
1009
  if match
862
1010
  options[:table] = match[:table]
@@ -866,20 +1014,33 @@ module ActiveRecord
866
1014
  options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
867
1015
  end
868
1016
 
869
- MismatchedForeignKey.new(**options)
1017
+ options
870
1018
  end
871
1019
 
872
- def version_string(full_version_string)
873
- full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
874
- end
1020
+ def mismatched_foreign_key(message, sql:, binds:, connection_pool:)
1021
+ options = {
1022
+ message: message,
1023
+ sql: sql,
1024
+ binds: binds,
1025
+ connection_pool: connection_pool
1026
+ }
1027
+
1028
+ if sql
1029
+ options.update mismatched_foreign_key_details(message: message, sql: sql)
1030
+ else
1031
+ options[:query_parser] = ->(sql) { mismatched_foreign_key_details(message: message, sql: sql) }
1032
+ end
875
1033
 
876
- ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
877
- Type::ImmutableString.new(true: "1", false: "0", **args)
1034
+ MismatchedForeignKey.new(**options)
878
1035
  end
879
- ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
880
- Type::String.new(true: "1", false: "0", **args)
1036
+
1037
+ def version_string(full_version_string)
1038
+ if full_version_string && matches = full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)
1039
+ matches[1]
1040
+ else
1041
+ raise DatabaseVersionError, "Unable to parse MySQL version from #{full_version_string.inspect}"
1042
+ end
881
1043
  end
882
- ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
883
1044
  end
884
1045
  end
885
1046
  end