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
@@ -1,62 +1,80 @@
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
10
  module ConnectionHandling # :nodoc:
11
+ def mysql2_adapter_class
12
+ ConnectionAdapters::Mysql2Adapter
13
+ end
14
+
11
15
  # Establishes a connection to the database that's used by all Active Record objects.
12
16
  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
- )
17
+ mysql2_adapter_class.new(config)
28
18
  end
29
19
  end
30
20
 
31
21
  module ConnectionAdapters
22
+ # = Active Record MySQL2 Adapter
32
23
  class Mysql2Adapter < AbstractMysqlAdapter
33
- ER_BAD_DB_ERROR = 1049
24
+ ER_BAD_DB_ERROR = 1049
25
+ ER_DBACCESS_DENIED_ERROR = 1044
26
+ ER_ACCESS_DENIED_ERROR = 1045
27
+ ER_CONN_HOST_ERROR = 2003
28
+ ER_UNKNOWN_HOST_ERROR = 2005
29
+
34
30
  ADAPTER_NAME = "Mysql2"
35
31
 
36
- include MySQL::DatabaseStatements
32
+ include Mysql2::DatabaseStatements
37
33
 
38
34
  class << self
39
35
  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
36
+ ::Mysql2::Client.new(config)
37
+ rescue ::Mysql2::Error => error
38
+ case error.error_number
39
+ when ER_BAD_DB_ERROR
40
+ raise ActiveRecord::NoDatabaseError.db_error(config[:database])
41
+ when ER_DBACCESS_DENIED_ERROR, ER_ACCESS_DENIED_ERROR
42
+ raise ActiveRecord::DatabaseConnectionError.username_error(config[:username])
43
+ when ER_CONN_HOST_ERROR, ER_UNKNOWN_HOST_ERROR
44
+ raise ActiveRecord::DatabaseConnectionError.hostname_error(config[:host])
44
45
  else
45
46
  raise ActiveRecord::ConnectionNotEstablished, error.message
46
47
  end
47
48
  end
48
- end
49
49
 
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
50
+ private
51
+ def initialize_type_map(m)
52
+ super
53
+
54
+ m.register_type(%r(char)i) do |sql_type|
55
+ limit = extract_limit(sql_type)
56
+ Type.lookup(:string, adapter: :mysql2, limit: limit)
57
+ end
58
+
59
+ m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
60
+ m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
61
+ end
54
62
  end
55
63
 
56
- def self.database_exists?(config)
57
- !!ActiveRecord::Base.mysql2_connection(config)
58
- rescue ActiveRecord::NoDatabaseError
59
- false
64
+ TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
65
+
66
+ def initialize(...)
67
+ super
68
+
69
+ @config[:flags] ||= 0
70
+
71
+ if @config[:flags].kind_of? Array
72
+ @config[:flags].push "FOUND_ROWS"
73
+ else
74
+ @config[:flags] |= ::Mysql2::Client::FOUND_ROWS
75
+ end
76
+
77
+ @connection_parameters ||= @config
60
78
  end
61
79
 
62
80
  def supports_json?
@@ -75,17 +93,19 @@ module ActiveRecord
75
93
  true
76
94
  end
77
95
 
96
+ def savepoint_errors_invalidate_transactions?
97
+ true
98
+ end
99
+
78
100
  def supports_lazy_transactions?
79
101
  true
80
102
  end
81
103
 
82
104
  # HELPER METHODS ===========================================
83
105
 
84
- def each_hash(result) # :nodoc:
106
+ def each_hash(result, &block) # :nodoc:
85
107
  if block_given?
86
- result.each(as: :hash, symbolize_keys: true) do |row|
87
- yield row
88
- end
108
+ result.each(as: :hash, symbolize_keys: true, &block)
89
109
  else
90
110
  to_enum(:each_hash, result)
91
111
  end
@@ -99,10 +119,11 @@ module ActiveRecord
99
119
  # QUOTING ==================================================
100
120
  #++
101
121
 
122
+ # Quotes strings for use in SQL input.
102
123
  def quote_string(string)
103
- @connection.escape(string)
104
- rescue Mysql2::Error => error
105
- raise translate_exception(error, message: error.message, sql: "<escape>", binds: [])
124
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
125
+ connection.escape(string)
126
+ end
106
127
  end
107
128
 
108
129
  #--
@@ -110,37 +131,51 @@ module ActiveRecord
110
131
  #++
111
132
 
112
133
  def active?
113
- @connection.ping
134
+ !(@raw_connection.nil? || @raw_connection.closed?) && @lock.synchronize { @raw_connection&.ping } || false
114
135
  end
115
136
 
116
- def reconnect!
117
- super
118
- disconnect!
119
- connect
120
- end
121
137
  alias :reset! :reconnect!
122
138
 
123
139
  # Disconnects from the database if already connected.
124
140
  # Otherwise, this method does nothing.
125
141
  def disconnect!
126
- super
127
- @connection.close
142
+ @lock.synchronize do
143
+ super
144
+ @raw_connection&.close
145
+ @raw_connection = nil
146
+ end
128
147
  end
129
148
 
130
149
  def discard! # :nodoc:
131
- super
132
- @connection.automatic_close = false
133
- @connection = nil
150
+ @lock.synchronize do
151
+ super
152
+ @raw_connection&.automatic_close = false
153
+ @raw_connection = nil
154
+ end
134
155
  end
135
156
 
136
157
  private
158
+ def text_type?(type)
159
+ TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
160
+ end
161
+
137
162
  def connect
138
- @connection = self.class.new_client(@config)
139
- configure_connection
163
+ @raw_connection = self.class.new_client(@connection_parameters)
164
+ rescue ConnectionNotEstablished => ex
165
+ raise ex.set_pool(@pool)
166
+ end
167
+
168
+ def reconnect
169
+ @lock.synchronize do
170
+ @raw_connection&.close
171
+ @raw_connection = nil
172
+ connect
173
+ end
140
174
  end
141
175
 
142
176
  def configure_connection
143
- @connection.query_options[:as] = :array
177
+ @raw_connection.query_options[:as] = :array
178
+ @raw_connection.query_options[:database_timezone] = default_timezone
144
179
  super
145
180
  end
146
181
 
@@ -149,16 +184,38 @@ module ActiveRecord
149
184
  end
150
185
 
151
186
  def get_full_version
152
- @connection.server_info[:version]
187
+ any_raw_connection.server_info[:version]
153
188
  end
154
189
 
155
190
  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)
191
+ if exception.is_a?(::Mysql2::Error::TimeoutError) && !exception.error_number
192
+ ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
193
+ elsif exception.is_a?(::Mysql2::Error::ConnectionError)
194
+ if exception.message.match?(/MySQL client is not connected/i)
195
+ ActiveRecord::ConnectionNotEstablished.new(exception, connection_pool: @pool)
196
+ else
197
+ ActiveRecord::ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
198
+ end
158
199
  else
159
200
  super
160
201
  end
161
202
  end
203
+
204
+ def default_prepared_statements
205
+ false
206
+ end
207
+
208
+ ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
209
+ Type::ImmutableString.new(true: "1", false: "0", **args)
210
+ end
211
+
212
+ ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
213
+ Type::String.new(true: "1", false: "0", **args)
214
+ end
215
+
216
+ ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
162
217
  end
218
+
219
+ ActiveSupport.run_load_hooks(:active_record_mysql2adapter, Mysql2Adapter)
163
220
  end
164
221
  end
@@ -5,8 +5,13 @@ module ActiveRecord
5
5
  class PoolConfig # :nodoc:
6
6
  include Mutex_m
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
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,27 +20,31 @@ 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
+ @connection_class = connection_class
23
32
  @db_config = db_config
33
+ @role = role
34
+ @shard = shard
24
35
  @pool = nil
25
36
  INSTANCES[self] = self
26
37
  end
27
38
 
28
- def connection_specification_name
29
- if connection_klass.is_a?(String)
30
- connection_klass
31
- elsif connection_klass.primary_class?
39
+ def connection_name
40
+ if connection_class.primary_class?
32
41
  "ActiveRecord::Base"
33
42
  else
34
- connection_klass.name
43
+ connection_class.name
35
44
  end
36
45
  end
37
46
 
38
- def disconnect!
47
+ def disconnect!(automatic_reconnect: false)
39
48
  ActiveSupport::ForkTracker.check!
40
49
 
41
50
  return unless @pool
@@ -43,7 +52,7 @@ module ActiveRecord
43
52
  synchronize do
44
53
  return unless @pool
45
54
 
46
- @pool.automatic_reconnect = false
55
+ @pool.automatic_reconnect = automatic_reconnect
47
56
  @pool.disconnect!
48
57
  end
49
58
 
@@ -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
@@ -4,19 +4,21 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module PostgreSQL
6
6
  module DatabaseStatements
7
- def explain(arel, binds = [])
8
- sql = "EXPLAIN #{to_sql(arel, binds)}"
9
- PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", binds))
7
+ def explain(arel, binds = [], options = [])
8
+ sql = build_explain_clause(options) + " " + to_sql(arel, binds)
9
+ result = internal_exec_query(sql, "EXPLAIN", binds)
10
+ PostgreSQL::ExplainPrettyPrinter.new.pp(result)
10
11
  end
11
12
 
12
13
  # Queries the database and returns the results in an Array-like object
13
- def query(sql, name = nil) #:nodoc:
14
- materialize_transactions
14
+ def query(sql, name = nil) # :nodoc:
15
15
  mark_transaction_written_if_write(sql)
16
16
 
17
17
  log(sql, name) do
18
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
19
- @connection.async_exec(sql).map_types!(@type_map_for_results).values
18
+ with_raw_connection do |conn|
19
+ result = conn.async_exec(sql).map_types!(@type_map_for_results).values
20
+ verified!
21
+ result
20
22
  end
21
23
  end
22
24
  end
@@ -34,65 +36,53 @@ module ActiveRecord
34
36
 
35
37
  # Executes an SQL statement, returning a PG::Result object on success
36
38
  # or raising a PG::Error exception otherwise.
39
+ #
40
+ # Setting +allow_retry+ to true causes the db to reconnect and retry
41
+ # executing the SQL statement in case of a connection-related exception.
42
+ # This option should only be enabled for known idempotent queries.
43
+ #
37
44
  # Note: the PG::Result object is manually memory managed; if you don't
38
45
  # need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
39
- def execute(sql, name = nil)
40
- if preventing_writes? && write_query?(sql)
41
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
42
- end
43
-
44
- materialize_transactions
45
- mark_transaction_written_if_write(sql)
46
+ def execute(...) # :nodoc:
47
+ super
48
+ ensure
49
+ @notice_receiver_sql_warnings = []
50
+ end
46
51
 
47
- log(sql, name) do
48
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
49
- @connection.async_exec(sql)
52
+ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
53
+ log(sql, name, async: async) do
54
+ with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
55
+ result = conn.async_exec(sql)
56
+ verified!
57
+ handle_warnings(result)
58
+ result
50
59
  end
51
60
  end
52
61
  end
53
62
 
54
- def exec_query(sql, name = "SQL", binds = [], prepare: false)
55
- execute_and_clear(sql, name, binds, prepare: prepare) do |result|
63
+ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true) # :nodoc:
64
+ execute_and_clear(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |result|
56
65
  types = {}
57
66
  fields = result.fields
58
67
  fields.each_with_index do |fname, i|
59
68
  ftype = result.ftype i
60
69
  fmod = result.fmod i
61
- case type = get_oid_type(ftype, fmod, fname)
62
- when Type::Integer, Type::Float, OID::Decimal, Type::String, Type::DateTime, Type::Boolean
63
- # skip if a column has already been type casted by pg decoders
64
- else types[fname] = type
65
- end
70
+ types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
66
71
  end
67
72
  build_result(columns: fields, rows: result.values, column_types: types)
68
73
  end
69
74
  end
70
75
 
71
- def exec_delete(sql, name = nil, binds = [])
76
+ def exec_delete(sql, name = nil, binds = []) # :nodoc:
72
77
  execute_and_clear(sql, name, binds) { |result| result.cmd_tuples }
73
78
  end
74
79
  alias :exec_update :exec_delete
75
80
 
76
- def sql_for_insert(sql, pk, binds) # :nodoc:
77
- if pk.nil?
78
- # Extract the table from the insert sql. Yuck.
79
- table_ref = extract_table_ref_from_insert_sql(sql)
80
- pk = primary_key(table_ref) if table_ref
81
- end
82
-
83
- if pk = suppress_composite_primary_key(pk)
84
- sql = "#{sql} RETURNING #{quote_column_name(pk)}"
85
- end
86
-
87
- super
88
- end
89
- private :sql_for_insert
90
-
91
- def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
81
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
92
82
  if use_insert_returning? || pk == false
93
83
  super
94
84
  else
95
- result = exec_query(sql, name, binds)
85
+ result = internal_exec_query(sql, name, binds)
96
86
  unless sequence_name
97
87
  table_ref = extract_table_ref_from_insert_sql(sql)
98
88
  if table_ref
@@ -107,26 +97,56 @@ module ActiveRecord
107
97
  end
108
98
 
109
99
  # Begins a transaction.
110
- def begin_db_transaction
111
- execute("BEGIN", "TRANSACTION")
100
+ def begin_db_transaction # :nodoc:
101
+ internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
112
102
  end
113
103
 
114
- def begin_isolated_db_transaction(isolation)
115
- begin_db_transaction
116
- execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
104
+ def begin_isolated_db_transaction(isolation) # :nodoc:
105
+ internal_execute("BEGIN ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
117
106
  end
118
107
 
119
108
  # Commits a transaction.
120
- def commit_db_transaction
121
- execute("COMMIT", "TRANSACTION")
109
+ def commit_db_transaction # :nodoc:
110
+ internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
122
111
  end
123
112
 
124
113
  # Aborts a transaction.
125
- def exec_rollback_db_transaction
126
- execute("ROLLBACK", "TRANSACTION")
114
+ def exec_rollback_db_transaction # :nodoc:
115
+ cancel_any_running_query
116
+ internal_execute("ROLLBACK", "TRANSACTION", allow_retry: false, materialize_transactions: true)
117
+ end
118
+
119
+ def exec_restart_db_transaction # :nodoc:
120
+ cancel_any_running_query
121
+ internal_execute("ROLLBACK AND CHAIN", "TRANSACTION", allow_retry: false, materialize_transactions: true)
122
+ end
123
+
124
+ # From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
125
+ HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
126
+ private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
127
+
128
+ def high_precision_current_timestamp
129
+ HIGH_PRECISION_CURRENT_TIMESTAMP
130
+ end
131
+
132
+ def build_explain_clause(options = [])
133
+ return "EXPLAIN" if options.empty?
134
+
135
+ "EXPLAIN (#{options.join(", ").upcase})"
127
136
  end
128
137
 
129
138
  private
139
+ IDLE_TRANSACTION_STATUSES = [PG::PQTRANS_IDLE, PG::PQTRANS_INTRANS, PG::PQTRANS_INERROR]
140
+ private_constant :IDLE_TRANSACTION_STATUSES
141
+
142
+ def cancel_any_running_query
143
+ return if @raw_connection.nil? || IDLE_TRANSACTION_STATUSES.include?(@raw_connection.transaction_status)
144
+
145
+ @raw_connection.cancel
146
+ @raw_connection.block
147
+ rescue PG::Error
148
+ end
149
+
130
150
  def execute_batch(statements, name = nil)
131
151
  execute(combine_multi_statements(statements))
132
152
  end
@@ -137,12 +157,29 @@ module ActiveRecord
137
157
 
138
158
  # Returns the current ID of a table's sequence.
139
159
  def last_insert_id_result(sequence_name)
140
- exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
160
+ internal_exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
161
+ end
162
+
163
+ def returning_column_values(result)
164
+ result.rows.first
141
165
  end
142
166
 
143
167
  def suppress_composite_primary_key(pk)
144
168
  pk unless pk.is_a?(Array)
145
169
  end
170
+
171
+ def handle_warnings(sql)
172
+ @notice_receiver_sql_warnings.each do |warning|
173
+ next if warning_ignored?(warning)
174
+
175
+ warning.sql = sql
176
+ ActiveRecord.db_warnings_action.call(warning)
177
+ end
178
+ end
179
+
180
+ def warning_ignored?(warning)
181
+ ["WARNING", "ERROR", "FATAL", "PANIC"].exclude?(warning.level) || super
182
+ end
146
183
  end
147
184
  end
148
185
  end
@@ -65,7 +65,7 @@ module ActiveRecord
65
65
  end
66
66
 
67
67
  def map(value, &block)
68
- value.map(&block)
68
+ value.map { |v| subtype.map(v, &block) }
69
69
  end
70
70
 
71
71
  def changed_in_place?(raw_old_value, new_value)