activerecord 6.1.7 → 7.2.0

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 (332) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +520 -1385
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +31 -31
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +17 -14
  7. data/lib/active_record/association_relation.rb +2 -12
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +60 -21
  10. data/lib/active_record/associations/association_scope.rb +17 -12
  11. data/lib/active_record/associations/belongs_to_association.rb +37 -11
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
  13. data/lib/active_record/associations/builder/association.rb +11 -5
  14. data/lib/active_record/associations/builder/belongs_to.rb +41 -14
  15. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +4 -4
  19. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  20. data/lib/active_record/associations/collection_association.rb +46 -36
  21. data/lib/active_record/associations/collection_proxy.rb +44 -16
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +10 -3
  25. data/lib/active_record/associations/has_many_association.rb +29 -19
  26. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  27. data/lib/active_record/associations/has_one_association.rb +20 -10
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
  30. data/lib/active_record/associations/join_dependency.rb +23 -15
  31. data/lib/active_record/associations/nested_error.rb +47 -0
  32. data/lib/active_record/associations/preloader/association.rb +212 -53
  33. data/lib/active_record/associations/preloader/batch.rb +48 -0
  34. data/lib/active_record/associations/preloader/branch.rb +153 -0
  35. data/lib/active_record/associations/preloader/through_association.rb +50 -16
  36. data/lib/active_record/associations/preloader.rb +50 -121
  37. data/lib/active_record/associations/singular_association.rb +15 -3
  38. data/lib/active_record/associations/through_association.rb +25 -14
  39. data/lib/active_record/associations.rb +404 -509
  40. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  41. data/lib/active_record/attribute_assignment.rb +2 -14
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  43. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  44. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  45. data/lib/active_record/attribute_methods/primary_key.rb +47 -27
  46. data/lib/active_record/attribute_methods/query.rb +31 -19
  47. data/lib/active_record/attribute_methods/read.rb +14 -11
  48. data/lib/active_record/attribute_methods/serialization.rb +174 -37
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -9
  50. data/lib/active_record/attribute_methods/write.rb +12 -15
  51. data/lib/active_record/attribute_methods.rb +164 -52
  52. data/lib/active_record/attributes.rb +51 -49
  53. data/lib/active_record/autosave_association.rb +74 -57
  54. data/lib/active_record/base.rb +27 -5
  55. data/lib/active_record/callbacks.rb +18 -34
  56. data/lib/active_record/coders/column_serializer.rb +61 -0
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +70 -46
  59. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +327 -612
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -64
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +377 -142
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +345 -166
  75. data/lib/active_record/connection_adapters/column.rb +13 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
  78. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
  79. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  81. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  82. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
  83. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
  85. data/lib/active_record/connection_adapters/pool_config.rb +26 -16
  86. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  87. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  88. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
  89. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  94. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  100. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
  103. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  104. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  105. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
  106. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  107. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +401 -77
  108. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  109. data/lib/active_record/connection_adapters/postgresql_adapter.rb +518 -251
  110. data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
  111. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  112. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
  113. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
  114. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
  119. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  120. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  121. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  122. data/lib/active_record/connection_adapters.rb +130 -6
  123. data/lib/active_record/connection_handling.rb +132 -146
  124. data/lib/active_record/core.rb +276 -251
  125. data/lib/active_record/counter_cache.rb +68 -34
  126. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -3
  127. data/lib/active_record/database_configurations/database_config.rb +34 -10
  128. data/lib/active_record/database_configurations/hash_config.rb +107 -31
  129. data/lib/active_record/database_configurations/url_config.rb +38 -13
  130. data/lib/active_record/database_configurations.rb +96 -60
  131. data/lib/active_record/delegated_type.rb +90 -20
  132. data/lib/active_record/deprecator.rb +7 -0
  133. data/lib/active_record/destroy_association_async_job.rb +4 -2
  134. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  135. data/lib/active_record/dynamic_matchers.rb +3 -3
  136. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +68 -0
  140. data/lib/active_record/encryption/configurable.rb +60 -0
  141. data/lib/active_record/encryption/context.rb +42 -0
  142. data/lib/active_record/encryption/contexts.rb +76 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  147. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  148. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  149. data/lib/active_record/encryption/encryptor.rb +170 -0
  150. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  151. data/lib/active_record/encryption/errors.rb +15 -0
  152. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  153. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  154. data/lib/active_record/encryption/key.rb +28 -0
  155. data/lib/active_record/encryption/key_generator.rb +53 -0
  156. data/lib/active_record/encryption/key_provider.rb +46 -0
  157. data/lib/active_record/encryption/message.rb +33 -0
  158. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  159. data/lib/active_record/encryption/message_serializer.rb +96 -0
  160. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  161. data/lib/active_record/encryption/properties.rb +76 -0
  162. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  163. data/lib/active_record/encryption/scheme.rb +100 -0
  164. data/lib/active_record/encryption.rb +56 -0
  165. data/lib/active_record/enum.rb +163 -63
  166. data/lib/active_record/errors.rb +210 -27
  167. data/lib/active_record/explain.rb +21 -12
  168. data/lib/active_record/explain_registry.rb +11 -6
  169. data/lib/active_record/explain_subscriber.rb +1 -1
  170. data/lib/active_record/fixture_set/file.rb +15 -1
  171. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  172. data/lib/active_record/fixture_set/render_context.rb +2 -0
  173. data/lib/active_record/fixture_set/table_row.rb +70 -14
  174. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  175. data/lib/active_record/fixtures.rb +179 -112
  176. data/lib/active_record/future_result.rb +178 -0
  177. data/lib/active_record/gem_version.rb +4 -4
  178. data/lib/active_record/inheritance.rb +85 -31
  179. data/lib/active_record/insert_all.rb +148 -32
  180. data/lib/active_record/integration.rb +14 -10
  181. data/lib/active_record/internal_metadata.rb +123 -23
  182. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  183. data/lib/active_record/locking/optimistic.rb +43 -27
  184. data/lib/active_record/locking/pessimistic.rb +15 -6
  185. data/lib/active_record/log_subscriber.rb +41 -29
  186. data/lib/active_record/marshalling.rb +56 -0
  187. data/lib/active_record/message_pack.rb +124 -0
  188. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  189. data/lib/active_record/middleware/database_selector.rb +23 -13
  190. data/lib/active_record/middleware/shard_selector.rb +62 -0
  191. data/lib/active_record/migration/command_recorder.rb +113 -16
  192. data/lib/active_record/migration/compatibility.rb +235 -46
  193. data/lib/active_record/migration/default_strategy.rb +22 -0
  194. data/lib/active_record/migration/execution_strategy.rb +19 -0
  195. data/lib/active_record/migration/join_table.rb +1 -1
  196. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  197. data/lib/active_record/migration.rb +374 -177
  198. data/lib/active_record/model_schema.rb +143 -159
  199. data/lib/active_record/nested_attributes.rb +48 -21
  200. data/lib/active_record/no_touching.rb +3 -3
  201. data/lib/active_record/normalization.rb +163 -0
  202. data/lib/active_record/persistence.rb +282 -283
  203. data/lib/active_record/promise.rb +84 -0
  204. data/lib/active_record/query_cache.rb +19 -25
  205. data/lib/active_record/query_logs.rb +189 -0
  206. data/lib/active_record/query_logs_formatter.rb +41 -0
  207. data/lib/active_record/querying.rb +44 -9
  208. data/lib/active_record/railtie.rb +234 -71
  209. data/lib/active_record/railties/controller_runtime.rb +25 -11
  210. data/lib/active_record/railties/databases.rake +189 -256
  211. data/lib/active_record/railties/job_runtime.rb +23 -0
  212. data/lib/active_record/readonly_attributes.rb +41 -3
  213. data/lib/active_record/reflection.rb +325 -103
  214. data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
  215. data/lib/active_record/relation/batches.rb +198 -63
  216. data/lib/active_record/relation/calculations.rb +300 -111
  217. data/lib/active_record/relation/delegation.rb +33 -22
  218. data/lib/active_record/relation/finder_methods.rb +123 -52
  219. data/lib/active_record/relation/merger.rb +26 -19
  220. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  221. data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
  222. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  223. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  224. data/lib/active_record/relation/predicate_builder.rb +29 -22
  225. data/lib/active_record/relation/query_attribute.rb +30 -12
  226. data/lib/active_record/relation/query_methods.rb +842 -150
  227. data/lib/active_record/relation/record_fetch_warning.rb +10 -9
  228. data/lib/active_record/relation/spawn_methods.rb +7 -6
  229. data/lib/active_record/relation/where_clause.rb +15 -36
  230. data/lib/active_record/relation.rb +736 -145
  231. data/lib/active_record/result.rb +67 -54
  232. data/lib/active_record/runtime_registry.rb +71 -13
  233. data/lib/active_record/sanitization.rb +84 -34
  234. data/lib/active_record/schema.rb +39 -23
  235. data/lib/active_record/schema_dumper.rb +90 -31
  236. data/lib/active_record/schema_migration.rb +74 -23
  237. data/lib/active_record/scoping/default.rb +72 -15
  238. data/lib/active_record/scoping/named.rb +5 -13
  239. data/lib/active_record/scoping.rb +65 -34
  240. data/lib/active_record/secure_password.rb +60 -0
  241. data/lib/active_record/secure_token.rb +21 -3
  242. data/lib/active_record/serialization.rb +6 -1
  243. data/lib/active_record/signed_id.rb +30 -9
  244. data/lib/active_record/statement_cache.rb +7 -7
  245. data/lib/active_record/store.rb +10 -10
  246. data/lib/active_record/suppressor.rb +13 -15
  247. data/lib/active_record/table_metadata.rb +7 -3
  248. data/lib/active_record/tasks/database_tasks.rb +277 -149
  249. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  250. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  251. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  252. data/lib/active_record/test_databases.rb +1 -1
  253. data/lib/active_record/test_fixtures.rb +173 -155
  254. data/lib/active_record/testing/query_assertions.rb +121 -0
  255. data/lib/active_record/timestamp.rb +32 -19
  256. data/lib/active_record/token_for.rb +123 -0
  257. data/lib/active_record/touch_later.rb +12 -7
  258. data/lib/active_record/transaction.rb +132 -0
  259. data/lib/active_record/transactions.rb +118 -41
  260. data/lib/active_record/translation.rb +3 -5
  261. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  262. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  263. data/lib/active_record/type/internal/timezone.rb +7 -2
  264. data/lib/active_record/type/serialized.rb +9 -7
  265. data/lib/active_record/type/time.rb +4 -0
  266. data/lib/active_record/type/type_map.rb +17 -20
  267. data/lib/active_record/type.rb +1 -2
  268. data/lib/active_record/type_caster/connection.rb +4 -4
  269. data/lib/active_record/validations/absence.rb +1 -1
  270. data/lib/active_record/validations/associated.rb +13 -7
  271. data/lib/active_record/validations/numericality.rb +5 -4
  272. data/lib/active_record/validations/presence.rb +5 -28
  273. data/lib/active_record/validations/uniqueness.rb +64 -15
  274. data/lib/active_record/validations.rb +12 -5
  275. data/lib/active_record/version.rb +1 -1
  276. data/lib/active_record.rb +444 -32
  277. data/lib/arel/alias_predication.rb +1 -1
  278. data/lib/arel/attributes/attribute.rb +0 -8
  279. data/lib/arel/collectors/bind.rb +2 -0
  280. data/lib/arel/collectors/composite.rb +7 -0
  281. data/lib/arel/collectors/sql_string.rb +1 -1
  282. data/lib/arel/collectors/substitute_binds.rb +1 -1
  283. data/lib/arel/crud.rb +28 -22
  284. data/lib/arel/delete_manager.rb +18 -4
  285. data/lib/arel/errors.rb +10 -0
  286. data/lib/arel/factory_methods.rb +4 -0
  287. data/lib/arel/filter_predications.rb +9 -0
  288. data/lib/arel/insert_manager.rb +2 -3
  289. data/lib/arel/nodes/binary.rb +6 -7
  290. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  291. data/lib/arel/nodes/casted.rb +1 -1
  292. data/lib/arel/nodes/cte.rb +36 -0
  293. data/lib/arel/nodes/delete_statement.rb +12 -13
  294. data/lib/arel/nodes/filter.rb +10 -0
  295. data/lib/arel/nodes/fragments.rb +35 -0
  296. data/lib/arel/nodes/function.rb +1 -0
  297. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  298. data/lib/arel/nodes/insert_statement.rb +2 -2
  299. data/lib/arel/nodes/leading_join.rb +8 -0
  300. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  301. data/lib/arel/nodes/node.rb +115 -5
  302. data/lib/arel/nodes/select_core.rb +2 -2
  303. data/lib/arel/nodes/select_statement.rb +2 -2
  304. data/lib/arel/nodes/sql_literal.rb +13 -0
  305. data/lib/arel/nodes/table_alias.rb +4 -0
  306. data/lib/arel/nodes/update_statement.rb +8 -3
  307. data/lib/arel/nodes.rb +7 -2
  308. data/lib/arel/predications.rb +14 -4
  309. data/lib/arel/select_manager.rb +11 -5
  310. data/lib/arel/table.rb +9 -6
  311. data/lib/arel/tree_manager.rb +8 -15
  312. data/lib/arel/update_manager.rb +20 -5
  313. data/lib/arel/visitors/dot.rb +81 -90
  314. data/lib/arel/visitors/mysql.rb +23 -5
  315. data/lib/arel/visitors/postgresql.rb +1 -22
  316. data/lib/arel/visitors/to_sql.rb +170 -36
  317. data/lib/arel/visitors/visitor.rb +2 -2
  318. data/lib/arel.rb +23 -4
  319. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  320. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  321. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  322. data/lib/rails/generators/active_record/migration.rb +3 -1
  323. data/lib/rails/generators/active_record/model/USAGE +113 -0
  324. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  325. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  326. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  327. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  328. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  329. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  330. metadata +100 -14
  331. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  332. data/lib/active_record/null_relation.rb +0 -67
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module Mysql2
6
+ module DatabaseStatements
7
+ # Returns an ActiveRecord::Result instance.
8
+ def select_all(*, **) # :nodoc:
9
+ if ExplainRegistry.collect? && prepared_statements
10
+ unprepared_statement { super }
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false) # :nodoc:
17
+ if without_prepared_statement?(binds)
18
+ execute_and_free(sql, name, async: async, allow_retry: allow_retry) do |result|
19
+ if result
20
+ build_result(columns: result.fields, rows: result.to_a)
21
+ else
22
+ build_result(columns: [], rows: [])
23
+ end
24
+ end
25
+ else
26
+ exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async) do |_, result|
27
+ if result
28
+ build_result(columns: result.fields, rows: result.to_a)
29
+ else
30
+ build_result(columns: [], rows: [])
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ def exec_delete(sql, name = nil, binds = []) # :nodoc:
37
+ if without_prepared_statement?(binds)
38
+ with_raw_connection do |conn|
39
+ @affected_rows_before_warnings = nil
40
+ execute_and_free(sql, name) { @affected_rows_before_warnings || conn.affected_rows }
41
+ end
42
+ else
43
+ exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
44
+ end
45
+ end
46
+ alias :exec_update :exec_delete
47
+
48
+ private
49
+ def sync_timezone_changes(raw_connection)
50
+ raw_connection.query_options[:database_timezone] = default_timezone
51
+ end
52
+
53
+ def execute_batch(statements, name = nil)
54
+ statements = statements.map { |sql| transform_query(sql) }
55
+ combine_multi_statements(statements).each do |statement|
56
+ with_raw_connection do |conn|
57
+ raw_execute(statement, name)
58
+ end
59
+ end
60
+ end
61
+
62
+ def last_inserted_id(result)
63
+ if supports_insert_returning?
64
+ super
65
+ else
66
+ @raw_connection&.last_id
67
+ end
68
+ end
69
+
70
+ def multi_statements_enabled?
71
+ flags = @config[:flags]
72
+
73
+ if flags.is_a?(Array)
74
+ flags.include?("MULTI_STATEMENTS")
75
+ else
76
+ flags.anybits?(::Mysql2::Client::MULTI_STATEMENTS)
77
+ end
78
+ end
79
+
80
+ def with_multi_statements
81
+ if multi_statements_enabled?
82
+ return yield
83
+ end
84
+
85
+ with_raw_connection do |conn|
86
+ conn.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
87
+
88
+ yield
89
+ ensure
90
+ conn.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
91
+ end
92
+ end
93
+
94
+ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
95
+ log(sql, name, async: async) do |notification_payload|
96
+ with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
97
+ sync_timezone_changes(conn)
98
+ result = conn.query(sql)
99
+ conn.abandon_results!
100
+ verified!
101
+ handle_warnings(sql)
102
+ notification_payload[:row_count] = result&.size || 0
103
+ result
104
+ end
105
+ end
106
+ end
107
+
108
+ def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
109
+ sql = transform_query(sql)
110
+ check_if_write_query(sql)
111
+
112
+ mark_transaction_written_if_write(sql)
113
+
114
+ type_casted_binds = type_casted_binds(binds)
115
+
116
+ log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
117
+ with_raw_connection do |conn|
118
+ sync_timezone_changes(conn)
119
+
120
+ if cache_stmt
121
+ stmt = @statements[sql] ||= conn.prepare(sql)
122
+ else
123
+ stmt = conn.prepare(sql)
124
+ end
125
+
126
+ begin
127
+ result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
128
+ stmt.execute(*type_casted_binds)
129
+ end
130
+ verified!
131
+ result
132
+ rescue ::Mysql2::Error => e
133
+ if cache_stmt
134
+ @statements.delete(sql)
135
+ else
136
+ stmt.close
137
+ end
138
+ raise e
139
+ end
140
+
141
+ ret = yield stmt, result
142
+ notification_payload[:row_count] = result&.size || 0
143
+ result.free if result
144
+ stmt.close unless cache_stmt
145
+ ret
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -1,62 +1,69 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_record/connection_adapters/abstract_mysql_adapter"
4
- require "active_record/connection_adapters/mysql/database_statements"
4
+ require "active_record/connection_adapters/mysql2/database_statements"
5
5
 
6
6
  gem "mysql2", "~> 0.5"
7
7
  require "mysql2"
8
8
 
9
9
  module ActiveRecord
10
- module ConnectionHandling # :nodoc:
11
- # Establishes a connection to the database that's used by all Active Record objects.
12
- def mysql2_connection(config)
13
- config = config.symbolize_keys
14
- config[:flags] ||= 0
15
-
16
- if config[:flags].kind_of? Array
17
- config[:flags].push "FOUND_ROWS"
18
- else
19
- config[:flags] |= Mysql2::Client::FOUND_ROWS
20
- end
21
-
22
- ConnectionAdapters::Mysql2Adapter.new(
23
- ConnectionAdapters::Mysql2Adapter.new_client(config),
24
- logger,
25
- nil,
26
- config,
27
- )
28
- end
29
- end
30
-
31
10
  module ConnectionAdapters
11
+ # = Active Record MySQL2 Adapter
32
12
  class Mysql2Adapter < AbstractMysqlAdapter
33
- ER_BAD_DB_ERROR = 1049
13
+ ER_BAD_DB_ERROR = 1049
14
+ ER_DBACCESS_DENIED_ERROR = 1044
15
+ ER_ACCESS_DENIED_ERROR = 1045
16
+ ER_CONN_HOST_ERROR = 2003
17
+ ER_UNKNOWN_HOST_ERROR = 2005
18
+
34
19
  ADAPTER_NAME = "Mysql2"
35
20
 
36
- include MySQL::DatabaseStatements
21
+ include Mysql2::DatabaseStatements
37
22
 
38
23
  class << self
39
24
  def new_client(config)
40
- Mysql2::Client.new(config)
41
- rescue Mysql2::Error => error
42
- if error.error_number == ConnectionAdapters::Mysql2Adapter::ER_BAD_DB_ERROR
43
- raise ActiveRecord::NoDatabaseError
25
+ ::Mysql2::Client.new(config)
26
+ rescue ::Mysql2::Error => error
27
+ case error.error_number
28
+ when ER_BAD_DB_ERROR
29
+ raise ActiveRecord::NoDatabaseError.db_error(config[:database])
30
+ when ER_DBACCESS_DENIED_ERROR, ER_ACCESS_DENIED_ERROR
31
+ raise ActiveRecord::DatabaseConnectionError.username_error(config[:username])
32
+ when ER_CONN_HOST_ERROR, ER_UNKNOWN_HOST_ERROR
33
+ raise ActiveRecord::DatabaseConnectionError.hostname_error(config[:host])
44
34
  else
45
35
  raise ActiveRecord::ConnectionNotEstablished, error.message
46
36
  end
47
37
  end
48
- end
49
38
 
50
- def initialize(connection, logger, connection_options, config)
51
- superclass_config = config.reverse_merge(prepared_statements: false)
52
- super(connection, logger, connection_options, superclass_config)
53
- configure_connection
39
+ private
40
+ def initialize_type_map(m)
41
+ super
42
+
43
+ m.register_type(%r(char)i) do |sql_type|
44
+ limit = extract_limit(sql_type)
45
+ Type.lookup(:string, adapter: :mysql2, limit: limit)
46
+ end
47
+
48
+ m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
49
+ m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
50
+ end
54
51
  end
55
52
 
56
- def self.database_exists?(config)
57
- !!ActiveRecord::Base.mysql2_connection(config)
58
- rescue ActiveRecord::NoDatabaseError
59
- false
53
+ TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
54
+
55
+ def initialize(...)
56
+ super
57
+
58
+ @config[:flags] ||= 0
59
+
60
+ if @config[:flags].kind_of? Array
61
+ @config[:flags].push "FOUND_ROWS"
62
+ else
63
+ @config[:flags] |= ::Mysql2::Client::FOUND_ROWS
64
+ end
65
+
66
+ @connection_parameters ||= @config
60
67
  end
61
68
 
62
69
  def supports_json?
@@ -75,17 +82,19 @@ module ActiveRecord
75
82
  true
76
83
  end
77
84
 
85
+ def savepoint_errors_invalidate_transactions?
86
+ true
87
+ end
88
+
78
89
  def supports_lazy_transactions?
79
90
  true
80
91
  end
81
92
 
82
93
  # HELPER METHODS ===========================================
83
94
 
84
- def each_hash(result) # :nodoc:
95
+ def each_hash(result, &block) # :nodoc:
85
96
  if block_given?
86
- result.each(as: :hash, symbolize_keys: true) do |row|
87
- yield row
88
- end
97
+ result.each(as: :hash, symbolize_keys: true, &block)
89
98
  else
90
99
  to_enum(:each_hash, result)
91
100
  end
@@ -96,69 +105,99 @@ module ActiveRecord
96
105
  end
97
106
 
98
107
  #--
99
- # QUOTING ==================================================
108
+ # CONNECTION MANAGEMENT ====================================
100
109
  #++
101
110
 
102
- def quote_string(string)
103
- @connection.escape(string)
104
- rescue Mysql2::Error => error
105
- raise translate_exception(error, message: error.message, sql: "<escape>", binds: [])
111
+ def connected?
112
+ !(@raw_connection.nil? || @raw_connection.closed?)
106
113
  end
107
114
 
108
- #--
109
- # CONNECTION MANAGEMENT ====================================
110
- #++
111
-
112
115
  def active?
113
- @connection.ping
116
+ connected? && @lock.synchronize { @raw_connection&.ping } || false
114
117
  end
115
118
 
116
- def reconnect!
117
- super
118
- disconnect!
119
- connect
120
- end
121
119
  alias :reset! :reconnect!
122
120
 
123
121
  # Disconnects from the database if already connected.
124
122
  # Otherwise, this method does nothing.
125
123
  def disconnect!
126
- super
127
- @connection.close
124
+ @lock.synchronize do
125
+ super
126
+ @raw_connection&.close
127
+ @raw_connection = nil
128
+ end
128
129
  end
129
130
 
130
131
  def discard! # :nodoc:
131
- super
132
- @connection.automatic_close = false
133
- @connection = nil
132
+ @lock.synchronize do
133
+ super
134
+ @raw_connection&.automatic_close = false
135
+ @raw_connection = nil
136
+ end
134
137
  end
135
138
 
136
139
  private
140
+ def text_type?(type)
141
+ TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
142
+ end
143
+
137
144
  def connect
138
- @connection = self.class.new_client(@config)
139
- configure_connection
145
+ @raw_connection = self.class.new_client(@connection_parameters)
146
+ rescue ConnectionNotEstablished => ex
147
+ raise ex.set_pool(@pool)
148
+ end
149
+
150
+ def reconnect
151
+ @lock.synchronize do
152
+ @raw_connection&.close
153
+ @raw_connection = nil
154
+ connect
155
+ end
140
156
  end
141
157
 
142
158
  def configure_connection
143
- @connection.query_options[:as] = :array
159
+ @raw_connection.query_options[:as] = :array
160
+ @raw_connection.query_options[:database_timezone] = default_timezone
144
161
  super
145
162
  end
146
163
 
147
164
  def full_version
148
- schema_cache.database_version.full_version_string
165
+ database_version.full_version_string
149
166
  end
150
167
 
151
168
  def get_full_version
152
- @connection.server_info[:version]
169
+ any_raw_connection.server_info[:version]
153
170
  end
154
171
 
155
172
  def translate_exception(exception, message:, sql:, binds:)
156
- if exception.is_a?(Mysql2::Error::TimeoutError) && !exception.error_number
157
- ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds)
173
+ if exception.is_a?(::Mysql2::Error::TimeoutError) && !exception.error_number
174
+ ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
175
+ elsif exception.is_a?(::Mysql2::Error::ConnectionError)
176
+ if exception.message.match?(/MySQL client is not connected/i)
177
+ ActiveRecord::ConnectionNotEstablished.new(exception, connection_pool: @pool)
178
+ else
179
+ ActiveRecord::ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
180
+ end
158
181
  else
159
182
  super
160
183
  end
161
184
  end
185
+
186
+ def default_prepared_statements
187
+ false
188
+ end
189
+
190
+ ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
191
+ Type::ImmutableString.new(true: "1", false: "0", **args)
192
+ end
193
+
194
+ ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
195
+ Type::String.new(true: "1", false: "0", **args)
196
+ end
197
+
198
+ ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
162
199
  end
200
+
201
+ ActiveSupport.run_load_hooks(:active_record_mysql2adapter, Mysql2Adapter)
163
202
  end
164
203
  end
@@ -3,10 +3,15 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  class PoolConfig # :nodoc:
6
- include Mutex_m
6
+ include MonitorMixin
7
7
 
8
- attr_reader :db_config, :connection_klass
9
- attr_accessor :schema_cache
8
+ attr_reader :db_config, :role, :shard
9
+ attr_writer :schema_reflection, :server_version
10
+ attr_accessor :connection_class
11
+
12
+ def schema_reflection
13
+ @schema_reflection ||= SchemaReflection.new(db_config.lazy_schema_cache_path)
14
+ end
10
15
 
11
16
  INSTANCES = ObjectSpace::WeakMap.new
12
17
  private_constant :INSTANCES
@@ -15,35 +20,42 @@ module ActiveRecord
15
20
  def discard_pools!
16
21
  INSTANCES.each_key(&:discard_pool!)
17
22
  end
23
+
24
+ def disconnect_all!
25
+ INSTANCES.each_key { |c| c.disconnect!(automatic_reconnect: true) }
26
+ end
18
27
  end
19
28
 
20
- def initialize(connection_klass, db_config)
29
+ def initialize(connection_class, db_config, role, shard)
21
30
  super()
22
- @connection_klass = connection_klass
31
+ @server_version = nil
32
+ @connection_class = connection_class
23
33
  @db_config = db_config
34
+ @role = role
35
+ @shard = shard
24
36
  @pool = nil
25
37
  INSTANCES[self] = self
26
38
  end
27
39
 
28
- def connection_specification_name
29
- if connection_klass.is_a?(String)
30
- connection_klass
31
- elsif connection_klass.primary_class?
40
+ def server_version(connection)
41
+ @server_version || synchronize { @server_version ||= connection.get_database_version }
42
+ end
43
+
44
+ def connection_name
45
+ if connection_class.primary_class?
32
46
  "ActiveRecord::Base"
33
47
  else
34
- connection_klass.name
48
+ connection_class.name
35
49
  end
36
50
  end
37
51
 
38
- def disconnect!
39
- ActiveSupport::ForkTracker.check!
40
-
52
+ def disconnect!(automatic_reconnect: false)
41
53
  return unless @pool
42
54
 
43
55
  synchronize do
44
56
  return unless @pool
45
57
 
46
- @pool.automatic_reconnect = false
58
+ @pool.automatic_reconnect = automatic_reconnect
47
59
  @pool.disconnect!
48
60
  end
49
61
 
@@ -51,8 +63,6 @@ module ActiveRecord
51
63
  end
52
64
 
53
65
  def pool
54
- ActiveSupport::ForkTracker.check!
55
-
56
66
  @pool || synchronize { @pool ||= ConnectionAdapters::ConnectionPool.new(self) }
57
67
  end
58
68
 
@@ -4,40 +4,50 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  class PoolManager # :nodoc:
6
6
  def initialize
7
- @name_to_role_mapping = Hash.new { |h, k| h[k] = {} }
7
+ @role_to_shard_mapping = Hash.new { |h, k| h[k] = {} }
8
8
  end
9
9
 
10
10
  def shard_names
11
- @name_to_role_mapping.values.flat_map { |shard_map| shard_map.keys }
11
+ @role_to_shard_mapping.values.flat_map { |shard_map| shard_map.keys }.uniq
12
12
  end
13
13
 
14
14
  def role_names
15
- @name_to_role_mapping.keys
15
+ @role_to_shard_mapping.keys
16
16
  end
17
17
 
18
18
  def pool_configs(role = nil)
19
19
  if role
20
- @name_to_role_mapping[role].values
20
+ @role_to_shard_mapping[role].values
21
21
  else
22
- @name_to_role_mapping.flat_map { |_, shard_map| shard_map.values }
22
+ @role_to_shard_mapping.flat_map { |_, shard_map| shard_map.values }
23
+ end
24
+ end
25
+
26
+ def each_pool_config(role = nil, &block)
27
+ if role
28
+ @role_to_shard_mapping[role].each_value(&block)
29
+ else
30
+ @role_to_shard_mapping.each_value do |shard_map|
31
+ shard_map.each_value(&block)
32
+ end
23
33
  end
24
34
  end
25
35
 
26
36
  def remove_role(role)
27
- @name_to_role_mapping.delete(role)
37
+ @role_to_shard_mapping.delete(role)
28
38
  end
29
39
 
30
40
  def remove_pool_config(role, shard)
31
- @name_to_role_mapping[role].delete(shard)
41
+ @role_to_shard_mapping[role].delete(shard)
32
42
  end
33
43
 
34
44
  def get_pool_config(role, shard)
35
- @name_to_role_mapping[role][shard]
45
+ @role_to_shard_mapping[role][shard]
36
46
  end
37
47
 
38
48
  def set_pool_config(role, shard, pool_config)
39
49
  if pool_config
40
- @name_to_role_mapping[role][shard] = pool_config
50
+ @role_to_shard_mapping[role][shard] = pool_config
41
51
  else
42
52
  raise ArgumentError, "The `pool_config` for the :#{role} role and :#{shard} shard was `nil`. Please check your configuration. If you want your writing role to be something other than `:writing` set `config.active_record.writing_role` in your application configuration. The same setting should be applied for the `reading_role` if applicable."
43
53
  end
@@ -6,37 +6,65 @@ module ActiveRecord
6
6
  class Column < ConnectionAdapters::Column # :nodoc:
7
7
  delegate :oid, :fmod, to: :sql_type_metadata
8
8
 
9
- def initialize(*, serial: nil, **)
9
+ def initialize(*, serial: nil, identity: nil, generated: nil, **)
10
10
  super
11
11
  @serial = serial
12
+ @identity = identity
13
+ @generated = generated
14
+ end
15
+
16
+ def identity?
17
+ @identity
12
18
  end
13
19
 
14
20
  def serial?
15
21
  @serial
16
22
  end
17
23
 
24
+ def auto_incremented_by_db?
25
+ serial? || identity?
26
+ end
27
+
28
+ def virtual?
29
+ # We assume every generated column is virtual, no matter the concrete type
30
+ @generated.present?
31
+ end
32
+
33
+ def has_default?
34
+ super && !virtual?
35
+ end
36
+
18
37
  def array
19
38
  sql_type_metadata.sql_type.end_with?("[]")
20
39
  end
21
40
  alias :array? :array
22
41
 
42
+ def enum?
43
+ type == :enum
44
+ end
45
+
23
46
  def sql_type
24
47
  super.delete_suffix("[]")
25
48
  end
26
49
 
27
50
  def init_with(coder)
28
51
  @serial = coder["serial"]
52
+ @identity = coder["identity"]
53
+ @generated = coder["generated"]
29
54
  super
30
55
  end
31
56
 
32
57
  def encode_with(coder)
33
58
  coder["serial"] = @serial
59
+ coder["identity"] = @identity
60
+ coder["generated"] = @generated
34
61
  super
35
62
  end
36
63
 
37
64
  def ==(other)
38
65
  other.is_a?(Column) &&
39
66
  super &&
67
+ identity? == other.identity? &&
40
68
  serial? == other.serial?
41
69
  end
42
70
  alias :eql? :==
@@ -44,6 +72,7 @@ module ActiveRecord
44
72
  def hash
45
73
  Column.hash ^
46
74
  super.hash ^
75
+ identity?.hash ^
47
76
  serial?.hash
48
77
  end
49
78
  end