activerecord 6.1.6 → 7.1.2

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 (309) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1627 -983
  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 +50 -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 +35 -31
  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.rb +26 -16
  27. data/lib/active_record/associations/preloader/association.rb +207 -52
  28. data/lib/active_record/associations/preloader/batch.rb +48 -0
  29. data/lib/active_record/associations/preloader/branch.rb +147 -0
  30. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  31. data/lib/active_record/associations/preloader.rb +50 -121
  32. data/lib/active_record/associations/singular_association.rb +9 -3
  33. data/lib/active_record/associations/through_association.rb +25 -14
  34. data/lib/active_record/associations.rb +439 -305
  35. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  36. data/lib/active_record/attribute_assignment.rb +1 -3
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  38. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  39. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  40. data/lib/active_record/attribute_methods/query.rb +31 -19
  41. data/lib/active_record/attribute_methods/read.rb +25 -10
  42. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  44. data/lib/active_record/attribute_methods/write.rb +10 -13
  45. data/lib/active_record/attribute_methods.rb +121 -40
  46. data/lib/active_record/attributes.rb +27 -38
  47. data/lib/active_record/autosave_association.rb +61 -30
  48. data/lib/active_record/base.rb +25 -2
  49. data/lib/active_record/callbacks.rb +18 -34
  50. data/lib/active_record/coders/column_serializer.rb +61 -0
  51. data/lib/active_record/coders/json.rb +1 -1
  52. data/lib/active_record/coders/yaml_column.rb +70 -34
  53. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  54. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +96 -590
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +77 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +360 -138
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -149
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +285 -156
  69. data/lib/active_record/connection_adapters/column.rb +13 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +104 -53
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
  83. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  87. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  89. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  93. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  94. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  95. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  96. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  97. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  98. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +394 -74
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +509 -247
  101. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  102. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  103. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
  104. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  105. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  106. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  107. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
  108. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  109. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  110. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  111. data/lib/active_record/connection_adapters.rb +9 -6
  112. data/lib/active_record/connection_handling.rb +107 -136
  113. data/lib/active_record/core.rb +202 -223
  114. data/lib/active_record/counter_cache.rb +46 -25
  115. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  116. data/lib/active_record/database_configurations/database_config.rb +21 -12
  117. data/lib/active_record/database_configurations/hash_config.rb +84 -16
  118. data/lib/active_record/database_configurations/url_config.rb +18 -12
  119. data/lib/active_record/database_configurations.rb +95 -59
  120. data/lib/active_record/delegated_type.rb +61 -15
  121. data/lib/active_record/deprecator.rb +7 -0
  122. data/lib/active_record/destroy_association_async_job.rb +3 -1
  123. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  124. data/lib/active_record/dynamic_matchers.rb +1 -1
  125. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  126. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  127. data/lib/active_record/encryption/cipher.rb +53 -0
  128. data/lib/active_record/encryption/config.rb +68 -0
  129. data/lib/active_record/encryption/configurable.rb +60 -0
  130. data/lib/active_record/encryption/context.rb +42 -0
  131. data/lib/active_record/encryption/contexts.rb +76 -0
  132. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  133. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  134. data/lib/active_record/encryption/encryptable_record.rb +224 -0
  135. data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -0
  136. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  137. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  138. data/lib/active_record/encryption/encryptor.rb +155 -0
  139. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  140. data/lib/active_record/encryption/errors.rb +15 -0
  141. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  142. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  143. data/lib/active_record/encryption/key.rb +28 -0
  144. data/lib/active_record/encryption/key_generator.rb +53 -0
  145. data/lib/active_record/encryption/key_provider.rb +46 -0
  146. data/lib/active_record/encryption/message.rb +33 -0
  147. data/lib/active_record/encryption/message_serializer.rb +92 -0
  148. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  149. data/lib/active_record/encryption/properties.rb +76 -0
  150. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  151. data/lib/active_record/encryption/scheme.rb +96 -0
  152. data/lib/active_record/encryption.rb +56 -0
  153. data/lib/active_record/enum.rb +154 -63
  154. data/lib/active_record/errors.rb +171 -15
  155. data/lib/active_record/explain.rb +23 -3
  156. data/lib/active_record/explain_registry.rb +11 -6
  157. data/lib/active_record/explain_subscriber.rb +1 -1
  158. data/lib/active_record/fixture_set/file.rb +15 -1
  159. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  160. data/lib/active_record/fixture_set/render_context.rb +2 -0
  161. data/lib/active_record/fixture_set/table_row.rb +70 -14
  162. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  163. data/lib/active_record/fixtures.rb +131 -86
  164. data/lib/active_record/future_result.rb +164 -0
  165. data/lib/active_record/gem_version.rb +3 -3
  166. data/lib/active_record/inheritance.rb +81 -29
  167. data/lib/active_record/insert_all.rb +135 -22
  168. data/lib/active_record/integration.rb +11 -10
  169. data/lib/active_record/internal_metadata.rb +119 -33
  170. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  171. data/lib/active_record/locking/optimistic.rb +36 -21
  172. data/lib/active_record/locking/pessimistic.rb +15 -6
  173. data/lib/active_record/log_subscriber.rb +52 -19
  174. data/lib/active_record/marshalling.rb +56 -0
  175. data/lib/active_record/message_pack.rb +124 -0
  176. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  177. data/lib/active_record/middleware/database_selector.rb +23 -13
  178. data/lib/active_record/middleware/shard_selector.rb +62 -0
  179. data/lib/active_record/migration/command_recorder.rb +112 -14
  180. data/lib/active_record/migration/compatibility.rb +221 -48
  181. data/lib/active_record/migration/default_strategy.rb +23 -0
  182. data/lib/active_record/migration/execution_strategy.rb +19 -0
  183. data/lib/active_record/migration/join_table.rb +1 -1
  184. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  185. data/lib/active_record/migration.rb +358 -171
  186. data/lib/active_record/model_schema.rb +120 -101
  187. data/lib/active_record/nested_attributes.rb +37 -18
  188. data/lib/active_record/no_touching.rb +3 -3
  189. data/lib/active_record/normalization.rb +167 -0
  190. data/lib/active_record/persistence.rb +405 -85
  191. data/lib/active_record/promise.rb +84 -0
  192. data/lib/active_record/query_cache.rb +3 -21
  193. data/lib/active_record/query_logs.rb +174 -0
  194. data/lib/active_record/query_logs_formatter.rb +41 -0
  195. data/lib/active_record/querying.rb +29 -6
  196. data/lib/active_record/railtie.rb +219 -43
  197. data/lib/active_record/railties/controller_runtime.rb +13 -9
  198. data/lib/active_record/railties/databases.rake +188 -252
  199. data/lib/active_record/railties/job_runtime.rb +23 -0
  200. data/lib/active_record/readonly_attributes.rb +41 -3
  201. data/lib/active_record/reflection.rb +241 -80
  202. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  203. data/lib/active_record/relation/batches.rb +192 -63
  204. data/lib/active_record/relation/calculations.rb +219 -90
  205. data/lib/active_record/relation/delegation.rb +27 -13
  206. data/lib/active_record/relation/finder_methods.rb +108 -51
  207. data/lib/active_record/relation/merger.rb +22 -13
  208. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  209. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  210. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  211. data/lib/active_record/relation/predicate_builder.rb +27 -20
  212. data/lib/active_record/relation/query_attribute.rb +30 -12
  213. data/lib/active_record/relation/query_methods.rb +654 -127
  214. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  215. data/lib/active_record/relation/spawn_methods.rb +20 -3
  216. data/lib/active_record/relation/where_clause.rb +10 -19
  217. data/lib/active_record/relation.rb +262 -120
  218. data/lib/active_record/result.rb +37 -11
  219. data/lib/active_record/runtime_registry.rb +18 -13
  220. data/lib/active_record/sanitization.rb +65 -20
  221. data/lib/active_record/schema.rb +36 -22
  222. data/lib/active_record/schema_dumper.rb +73 -24
  223. data/lib/active_record/schema_migration.rb +68 -33
  224. data/lib/active_record/scoping/default.rb +72 -15
  225. data/lib/active_record/scoping/named.rb +5 -13
  226. data/lib/active_record/scoping.rb +65 -34
  227. data/lib/active_record/secure_password.rb +60 -0
  228. data/lib/active_record/secure_token.rb +21 -3
  229. data/lib/active_record/serialization.rb +6 -1
  230. data/lib/active_record/signed_id.rb +10 -8
  231. data/lib/active_record/store.rb +16 -11
  232. data/lib/active_record/suppressor.rb +13 -15
  233. data/lib/active_record/table_metadata.rb +16 -3
  234. data/lib/active_record/tasks/database_tasks.rb +225 -136
  235. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  236. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  237. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  238. data/lib/active_record/test_databases.rb +1 -1
  239. data/lib/active_record/test_fixtures.rb +123 -99
  240. data/lib/active_record/timestamp.rb +29 -18
  241. data/lib/active_record/token_for.rb +113 -0
  242. data/lib/active_record/touch_later.rb +11 -6
  243. data/lib/active_record/transactions.rb +48 -27
  244. data/lib/active_record/translation.rb +3 -3
  245. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  246. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  247. data/lib/active_record/type/internal/timezone.rb +7 -2
  248. data/lib/active_record/type/serialized.rb +9 -5
  249. data/lib/active_record/type/time.rb +4 -0
  250. data/lib/active_record/type/type_map.rb +17 -20
  251. data/lib/active_record/type.rb +1 -2
  252. data/lib/active_record/validations/absence.rb +1 -1
  253. data/lib/active_record/validations/associated.rb +4 -4
  254. data/lib/active_record/validations/numericality.rb +5 -4
  255. data/lib/active_record/validations/presence.rb +5 -28
  256. data/lib/active_record/validations/uniqueness.rb +51 -6
  257. data/lib/active_record/validations.rb +8 -4
  258. data/lib/active_record/version.rb +1 -1
  259. data/lib/active_record.rb +335 -32
  260. data/lib/arel/attributes/attribute.rb +0 -8
  261. data/lib/arel/crud.rb +28 -22
  262. data/lib/arel/delete_manager.rb +18 -4
  263. data/lib/arel/errors.rb +10 -0
  264. data/lib/arel/factory_methods.rb +4 -0
  265. data/lib/arel/filter_predications.rb +9 -0
  266. data/lib/arel/insert_manager.rb +2 -3
  267. data/lib/arel/nodes/and.rb +4 -0
  268. data/lib/arel/nodes/binary.rb +6 -1
  269. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  270. data/lib/arel/nodes/casted.rb +1 -1
  271. data/lib/arel/nodes/cte.rb +36 -0
  272. data/lib/arel/nodes/delete_statement.rb +12 -13
  273. data/lib/arel/nodes/filter.rb +10 -0
  274. data/lib/arel/nodes/fragments.rb +35 -0
  275. data/lib/arel/nodes/function.rb +1 -0
  276. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  277. data/lib/arel/nodes/insert_statement.rb +2 -2
  278. data/lib/arel/nodes/leading_join.rb +8 -0
  279. data/lib/arel/nodes/node.rb +111 -2
  280. data/lib/arel/nodes/select_core.rb +2 -2
  281. data/lib/arel/nodes/select_statement.rb +2 -2
  282. data/lib/arel/nodes/sql_literal.rb +6 -0
  283. data/lib/arel/nodes/table_alias.rb +4 -0
  284. data/lib/arel/nodes/update_statement.rb +8 -3
  285. data/lib/arel/nodes.rb +5 -0
  286. data/lib/arel/predications.rb +13 -3
  287. data/lib/arel/select_manager.rb +10 -4
  288. data/lib/arel/table.rb +9 -6
  289. data/lib/arel/tree_manager.rb +0 -12
  290. data/lib/arel/update_manager.rb +18 -4
  291. data/lib/arel/visitors/dot.rb +80 -90
  292. data/lib/arel/visitors/mysql.rb +16 -3
  293. data/lib/arel/visitors/postgresql.rb +0 -10
  294. data/lib/arel/visitors/to_sql.rb +139 -19
  295. data/lib/arel/visitors/visitor.rb +2 -2
  296. data/lib/arel.rb +18 -3
  297. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  298. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  299. data/lib/rails/generators/active_record/migration.rb +3 -1
  300. data/lib/rails/generators/active_record/model/USAGE +113 -0
  301. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  302. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  303. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  304. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  305. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  306. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  307. metadata +93 -13
  308. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  309. data/lib/active_record/null_relation.rb +0 -67
@@ -4,6 +4,7 @@ require "set"
4
4
  require "active_record/connection_adapters/sql_type_metadata"
5
5
  require "active_record/connection_adapters/abstract/schema_dumper"
6
6
  require "active_record/connection_adapters/abstract/schema_creation"
7
+ require "active_support/concurrency/null_lock"
7
8
  require "active_support/concurrency/load_interlock_aware_monitor"
8
9
  require "arel/collectors/bind"
9
10
  require "arel/collectors/composite"
@@ -12,6 +13,8 @@ require "arel/collectors/substitute_binds"
12
13
 
13
14
  module ActiveRecord
14
15
  module ConnectionAdapters # :nodoc:
16
+ # = Active Record Abstract Adapter
17
+ #
15
18
  # Active Record supports multiple database systems. AbstractAdapter and
16
19
  # related classes form the abstraction layer which makes this possible.
17
20
  # An AbstractAdapter represents a connection to a database, and provides an
@@ -36,12 +39,20 @@ module ActiveRecord
36
39
  include Savepoints
37
40
 
38
41
  SIMPLE_INT = /\A\d+\z/
39
- COMMENT_REGEX = %r{(?:\-\-.*\n)*|/\*(?:[^\*]|\*[^/])*\*/}m
42
+ COMMENT_REGEX = %r{(?:--.*\n)|/\*(?:[^*]|\*[^/])*\*/}
40
43
 
41
- attr_accessor :pool
44
+ attr_reader :pool
42
45
  attr_reader :visitor, :owner, :logger, :lock
43
46
  alias :in_use? :owner
44
47
 
48
+ def pool=(value)
49
+ return if value.eql?(@pool)
50
+ @schema_cache = nil
51
+ @pool = value
52
+
53
+ @pool.schema_reflection.load!(self) if ActiveRecord.lazily_load_schema_cache
54
+ end
55
+
45
56
  set_callback :checkin, :after, :enable_lazy_transactions!
46
57
 
47
58
  def self.type_cast_config_to_integer(config)
@@ -62,44 +73,142 @@ module ActiveRecord
62
73
  end
63
74
  end
64
75
 
76
+ def self.validate_default_timezone(config)
77
+ case config
78
+ when nil
79
+ when "utc", "local"
80
+ config.to_sym
81
+ else
82
+ raise ArgumentError, "default_timezone must be either 'utc' or 'local'"
83
+ end
84
+ end
85
+
65
86
  DEFAULT_READ_QUERY = [:begin, :commit, :explain, :release, :rollback, :savepoint, :select, :with] # :nodoc:
66
87
  private_constant :DEFAULT_READ_QUERY
67
88
 
68
89
  def self.build_read_query_regexp(*parts) # :nodoc:
69
90
  parts += DEFAULT_READ_QUERY
70
91
  parts = parts.map { |part| /#{part}/i }
71
- /\A(?:[\(\s]|#{COMMENT_REGEX})*#{Regexp.union(*parts)}/
92
+ /\A(?:[(\s]|#{COMMENT_REGEX})*#{Regexp.union(*parts)}/
72
93
  end
73
94
 
74
- def self.quoted_column_names # :nodoc:
75
- @quoted_column_names ||= {}
95
+ def self.find_cmd_and_exec(commands, *args) # :doc:
96
+ commands = Array(commands)
97
+
98
+ dirs_on_path = ENV["PATH"].to_s.split(File::PATH_SEPARATOR)
99
+ unless (ext = RbConfig::CONFIG["EXEEXT"]).empty?
100
+ commands = commands.map { |cmd| "#{cmd}#{ext}" }
101
+ end
102
+
103
+ full_path_command = nil
104
+ found = commands.detect do |cmd|
105
+ dirs_on_path.detect do |path|
106
+ full_path_command = File.join(path, cmd)
107
+ begin
108
+ stat = File.stat(full_path_command)
109
+ rescue SystemCallError
110
+ else
111
+ stat.file? && stat.executable?
112
+ end
113
+ end
114
+ end
115
+
116
+ if found
117
+ exec full_path_command, *args
118
+ else
119
+ abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
120
+ end
76
121
  end
77
122
 
78
- def self.quoted_table_names # :nodoc:
79
- @quoted_table_names ||= {}
123
+ # Opens a database console session.
124
+ def self.dbconsole(config, options = {})
125
+ raise NotImplementedError
80
126
  end
81
127
 
82
- def initialize(connection, logger = nil, config = {}) # :nodoc:
128
+ def initialize(config_or_deprecated_connection, deprecated_logger = nil, deprecated_connection_options = nil, deprecated_config = nil) # :nodoc:
83
129
  super()
84
130
 
85
- @connection = connection
86
- @owner = nil
87
- @instrumenter = ActiveSupport::Notifications.instrumenter
88
- @logger = logger
89
- @config = config
90
- @pool = ActiveRecord::ConnectionAdapters::NullPool.new
91
- @idle_since = Concurrent.monotonic_time
131
+ @raw_connection = nil
132
+ @unconfigured_connection = nil
133
+
134
+ if config_or_deprecated_connection.is_a?(Hash)
135
+ @config = config_or_deprecated_connection.symbolize_keys
136
+ @logger = ActiveRecord::Base.logger
137
+
138
+ if deprecated_logger || deprecated_connection_options || deprecated_config
139
+ raise ArgumentError, "when initializing an ActiveRecord adapter with a config hash, that should be the only argument"
140
+ end
141
+ else
142
+ # Soft-deprecated for now; we'll probably warn in future.
143
+
144
+ @unconfigured_connection = config_or_deprecated_connection
145
+ @logger = deprecated_logger || ActiveRecord::Base.logger
146
+ if deprecated_config
147
+ @config = (deprecated_config || {}).symbolize_keys
148
+ @connection_parameters = deprecated_connection_options
149
+ else
150
+ @config = (deprecated_connection_options || {}).symbolize_keys
151
+ @connection_parameters = nil
152
+ end
153
+ end
154
+
155
+ @owner = nil
156
+ @instrumenter = ActiveSupport::Notifications.instrumenter
157
+ @pool = ActiveRecord::ConnectionAdapters::NullPool.new
158
+ @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
92
159
  @visitor = arel_visitor
93
160
  @statements = build_statement_pool
94
- @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
161
+ self.lock_thread = nil
95
162
 
96
- @prepared_statements = self.class.type_cast_config_to_boolean(
97
- config.fetch(:prepared_statements, true)
163
+ @prepared_statements = !ActiveRecord.disable_prepared_statements && self.class.type_cast_config_to_boolean(
164
+ @config.fetch(:prepared_statements) { default_prepared_statements }
98
165
  )
99
166
 
100
167
  @advisory_locks_enabled = self.class.type_cast_config_to_boolean(
101
- config.fetch(:advisory_locks, true)
168
+ @config.fetch(:advisory_locks, true)
102
169
  )
170
+
171
+ @default_timezone = self.class.validate_default_timezone(@config[:default_timezone])
172
+
173
+ @raw_connection_dirty = false
174
+ @verified = false
175
+ end
176
+
177
+ THREAD_LOCK = ActiveSupport::Concurrency::ThreadLoadInterlockAwareMonitor.new
178
+ private_constant :THREAD_LOCK
179
+
180
+ FIBER_LOCK = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
181
+ private_constant :FIBER_LOCK
182
+
183
+ def lock_thread=(lock_thread) # :nodoc:
184
+ @lock =
185
+ case lock_thread
186
+ when Thread
187
+ THREAD_LOCK
188
+ when Fiber
189
+ FIBER_LOCK
190
+ else
191
+ ActiveSupport::Concurrency::NullLock
192
+ end
193
+ end
194
+
195
+ EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
196
+ EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze # :nodoc:
197
+ private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
198
+ def with_instrumenter(instrumenter, &block) # :nodoc:
199
+ Thread.handle_interrupt(EXCEPTION_NEVER) do
200
+ previous_instrumenter = @instrumenter
201
+ @instrumenter = instrumenter
202
+ Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
203
+ ensure
204
+ @instrumenter = previous_instrumenter
205
+ end
206
+ end
207
+
208
+ def check_if_write_query(sql) # :nodoc:
209
+ if preventing_writes? && write_query?(sql)
210
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
211
+ end
103
212
  end
104
213
 
105
214
  def replica?
@@ -110,21 +219,31 @@ module ActiveRecord
110
219
  @config.fetch(:use_metadata_table, true)
111
220
  end
112
221
 
222
+ def connection_retries
223
+ (@config[:connection_retries] || 1).to_i
224
+ end
225
+
226
+ def retry_deadline
227
+ if @config[:retry_deadline]
228
+ @config[:retry_deadline].to_f
229
+ else
230
+ nil
231
+ end
232
+ end
233
+
234
+ def default_timezone
235
+ @default_timezone || ActiveRecord.default_timezone
236
+ end
237
+
113
238
  # Determines whether writes are currently being prevented.
114
239
  #
115
- # Returns true if the connection is a replica.
116
- #
117
- # If the application is using legacy handling, returns
118
- # true if +connection_handler.prevent_writes+ is set.
119
- #
120
- # If the application is using the new connection handling
121
- # will return true based on +current_preventing_writes+.
240
+ # Returns true if the connection is a replica or returns
241
+ # the value of +current_preventing_writes+.
122
242
  def preventing_writes?
123
243
  return true if replica?
124
- return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord::Base.legacy_connection_handling
125
- return false if connection_klass.nil?
244
+ return false if connection_class.nil?
126
245
 
127
- connection_klass.current_preventing_writes
246
+ connection_class.current_preventing_writes
128
247
  end
129
248
 
130
249
  def migrations_paths # :nodoc:
@@ -132,25 +251,15 @@ module ActiveRecord
132
251
  end
133
252
 
134
253
  def migration_context # :nodoc:
135
- MigrationContext.new(migrations_paths, schema_migration)
254
+ MigrationContext.new(migrations_paths, schema_migration, internal_metadata)
136
255
  end
137
256
 
138
257
  def schema_migration # :nodoc:
139
- @schema_migration ||= begin
140
- conn = self
141
- spec_name = conn.pool.pool_config.connection_specification_name
142
-
143
- return ActiveRecord::SchemaMigration if spec_name == "ActiveRecord::Base"
144
-
145
- schema_migration_name = "#{spec_name}::SchemaMigration"
146
-
147
- Class.new(ActiveRecord::SchemaMigration) do
148
- define_singleton_method(:name) { schema_migration_name }
149
- define_singleton_method(:to_s) { schema_migration_name }
258
+ SchemaMigration.new(self)
259
+ end
150
260
 
151
- self.connection_specification_name = spec_name
152
- end
153
- end
261
+ def internal_metadata # :nodoc:
262
+ InternalMetadata.new(self)
154
263
  end
155
264
 
156
265
  def prepared_statements?
@@ -159,7 +268,7 @@ module ActiveRecord
159
268
  alias :prepared_statements :prepared_statements?
160
269
 
161
270
  def prepared_statements_disabled_cache # :nodoc:
162
- Thread.current[:ar_prepared_statements_disabled_cache] ||= Set.new
271
+ ActiveSupport::IsolatedExecutionState[:active_record_prepared_statements_disabled_cache] ||= Set.new
163
272
  end
164
273
 
165
274
  class Version
@@ -189,41 +298,48 @@ module ActiveRecord
189
298
  def lease
190
299
  if in_use?
191
300
  msg = +"Cannot lease connection, "
192
- if @owner == Thread.current
301
+ if @owner == ActiveSupport::IsolatedExecutionState.context
193
302
  msg << "it is already leased by the current thread."
194
303
  else
195
304
  msg << "it is already in use by a different thread: #{@owner}. " \
196
- "Current thread: #{Thread.current}."
305
+ "Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
197
306
  end
198
307
  raise ActiveRecordError, msg
199
308
  end
200
309
 
201
- @owner = Thread.current
310
+ @owner = ActiveSupport::IsolatedExecutionState.context
202
311
  end
203
312
 
204
- def connection_klass # :nodoc:
205
- @pool.connection_klass
313
+ def connection_class # :nodoc:
314
+ @pool.connection_class
206
315
  end
207
316
 
208
- def schema_cache
209
- @pool.get_schema_cache(self)
317
+ # The role (e.g. +:writing+) for the current connection. In a
318
+ # non-multi role application, +:writing+ is returned.
319
+ def role
320
+ @pool.role
210
321
  end
211
322
 
212
- def schema_cache=(cache)
213
- cache.connection = self
214
- @pool.set_schema_cache(cache)
323
+ # The shard (e.g. +:default+) for the current connection. In
324
+ # a non-sharded application, +:default+ is returned.
325
+ def shard
326
+ @pool.shard
327
+ end
328
+
329
+ def schema_cache
330
+ @schema_cache ||= BoundSchemaReflection.new(@pool.schema_reflection, self)
215
331
  end
216
332
 
217
333
  # this method must only be called while holding connection pool's mutex
218
334
  def expire
219
335
  if in_use?
220
- if @owner != Thread.current
336
+ if @owner != ActiveSupport::IsolatedExecutionState.context
221
337
  raise ActiveRecordError, "Cannot expire connection, " \
222
338
  "it is owned by a different thread: #{@owner}. " \
223
- "Current thread: #{Thread.current}."
339
+ "Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
224
340
  end
225
341
 
226
- @idle_since = Concurrent.monotonic_time
342
+ @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
227
343
  @owner = nil
228
344
  else
229
345
  raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
@@ -233,10 +349,10 @@ module ActiveRecord
233
349
  # this method must only be called while holding connection pool's mutex (and a desire for segfaults)
234
350
  def steal! # :nodoc:
235
351
  if in_use?
236
- if @owner != Thread.current
352
+ if @owner != ActiveSupport::IsolatedExecutionState.context
237
353
  pool.send :remove_connection_from_thread_cache, self, @owner
238
354
 
239
- @owner = Thread.current
355
+ @owner = ActiveSupport::IsolatedExecutionState.context
240
356
  end
241
357
  else
242
358
  raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
@@ -246,7 +362,7 @@ module ActiveRecord
246
362
  # Seconds since this connection was returned to the pool
247
363
  def seconds_idle # :nodoc:
248
364
  return 0 if in_use?
249
- Concurrent.monotonic_time - @idle_since
365
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - @idle_since
250
366
  end
251
367
 
252
368
  def unprepared_statement
@@ -264,7 +380,14 @@ module ActiveRecord
264
380
 
265
381
  # Does the database for this adapter exist?
266
382
  def self.database_exists?(config)
267
- raise NotImplementedError
383
+ new(config).database_exists?
384
+ end
385
+
386
+ def database_exists?
387
+ connect!
388
+ true
389
+ rescue ActiveRecord::NoDatabaseError
390
+ false
268
391
  end
269
392
 
270
393
  # Does this adapter support DDL rollbacks in transactions? That is, would
@@ -282,6 +405,16 @@ module ActiveRecord
282
405
  false
283
406
  end
284
407
 
408
+ # Do TransactionRollbackErrors on savepoints affect the parent
409
+ # transaction?
410
+ def savepoint_errors_invalidate_transactions?
411
+ false
412
+ end
413
+
414
+ def supports_restart_db_transaction?
415
+ false
416
+ end
417
+
285
418
  # Does this adapter support application-enforced advisory locking?
286
419
  def supports_advisory_locks?
287
420
  false
@@ -308,6 +441,11 @@ module ActiveRecord
308
441
  false
309
442
  end
310
443
 
444
+ # Does this adapter support including non-key columns?
445
+ def supports_index_include?
446
+ false
447
+ end
448
+
311
449
  # Does this adapter support expression indices?
312
450
  def supports_expression_index?
313
451
  false
@@ -344,11 +482,26 @@ module ActiveRecord
344
482
  false
345
483
  end
346
484
 
485
+ # Does this adapter support creating deferrable constraints?
486
+ def supports_deferrable_constraints?
487
+ false
488
+ end
489
+
347
490
  # Does this adapter support creating check constraints?
348
491
  def supports_check_constraints?
349
492
  false
350
493
  end
351
494
 
495
+ # Does this adapter support creating exclusion constraints?
496
+ def supports_exclusion_constraints?
497
+ false
498
+ end
499
+
500
+ # Does this adapter support creating unique constraints?
501
+ def supports_unique_constraints?
502
+ false
503
+ end
504
+
352
505
  # Does this adapter support views?
353
506
  def supports_views?
354
507
  false
@@ -364,7 +517,7 @@ module ActiveRecord
364
517
  false
365
518
  end
366
519
 
367
- # Does this adapter support json data type?
520
+ # Does this adapter support JSON data type?
368
521
  def supports_json?
369
522
  false
370
523
  end
@@ -418,12 +571,49 @@ module ActiveRecord
418
571
  false
419
572
  end
420
573
 
574
+ def supports_concurrent_connections?
575
+ true
576
+ end
577
+
578
+ def supports_nulls_not_distinct?
579
+ false
580
+ end
581
+
582
+ def return_value_after_insert?(column) # :nodoc:
583
+ column.auto_incremented_by_db?
584
+ end
585
+
586
+ def async_enabled? # :nodoc:
587
+ supports_concurrent_connections? &&
588
+ !ActiveRecord.async_query_executor.nil? && !pool.async_executor.nil?
589
+ end
590
+
421
591
  # This is meant to be implemented by the adapters that support extensions
422
- def disable_extension(name)
592
+ def disable_extension(name, **)
423
593
  end
424
594
 
425
595
  # This is meant to be implemented by the adapters that support extensions
426
- def enable_extension(name)
596
+ def enable_extension(name, **)
597
+ end
598
+
599
+ # This is meant to be implemented by the adapters that support custom enum types
600
+ def create_enum(*) # :nodoc:
601
+ end
602
+
603
+ # This is meant to be implemented by the adapters that support custom enum types
604
+ def drop_enum(*) # :nodoc:
605
+ end
606
+
607
+ # This is meant to be implemented by the adapters that support custom enum types
608
+ def rename_enum(*) # :nodoc:
609
+ end
610
+
611
+ # This is meant to be implemented by the adapters that support custom enum types
612
+ def add_enum_value(*) # :nodoc:
613
+ end
614
+
615
+ # This is meant to be implemented by the adapters that support custom enum types
616
+ def rename_enum_value(*) # :nodoc:
427
617
  end
428
618
 
429
619
  def advisory_locks_enabled? # :nodoc:
@@ -461,6 +651,21 @@ module ActiveRecord
461
651
  yield
462
652
  end
463
653
 
654
+ # Override to check all foreign key constraints in a database.
655
+ def all_foreign_keys_valid?
656
+ check_all_foreign_keys_valid!
657
+ true
658
+ rescue ActiveRecord::StatementInvalid
659
+ false
660
+ end
661
+ deprecate :all_foreign_keys_valid?, deprecator: ActiveRecord.deprecator
662
+
663
+ # Override to check all foreign key constraints in a database.
664
+ # The adapter should raise a +ActiveRecord::StatementInvalid+ if foreign key
665
+ # constraints are not met.
666
+ def check_all_foreign_keys_valid!
667
+ end
668
+
464
669
  # CONNECTION MANAGEMENT ====================================
465
670
 
466
671
  # Checks whether the connection to the database is still active. This includes
@@ -469,19 +674,50 @@ module ActiveRecord
469
674
  def active?
470
675
  end
471
676
 
472
- # Disconnects from the database if already connected, and establishes a
473
- # new connection with the database. Implementors should call super if they
474
- # override the default implementation.
475
- def reconnect!
476
- clear_cache!
477
- reset_transaction
677
+ # Disconnects from the database if already connected, and establishes a new
678
+ # connection with the database. Implementors should define private #reconnect
679
+ # instead.
680
+ def reconnect!(restore_transactions: false)
681
+ retries_available = connection_retries
682
+ deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
683
+
684
+ @lock.synchronize do
685
+ reconnect
686
+
687
+ enable_lazy_transactions!
688
+ @raw_connection_dirty = false
689
+ @verified = true
690
+
691
+ reset_transaction(restore: restore_transactions) do
692
+ clear_cache!(new_connection: true)
693
+ configure_connection
694
+ end
695
+ rescue => original_exception
696
+ translated_exception = translate_exception_class(original_exception, nil, nil)
697
+ retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
698
+
699
+ if !retry_deadline_exceeded && retries_available > 0
700
+ retries_available -= 1
701
+
702
+ if retryable_connection_error?(translated_exception)
703
+ backoff(connection_retries - retries_available)
704
+ retry
705
+ end
706
+ end
707
+
708
+ @verified = false
709
+
710
+ raise translated_exception
711
+ end
478
712
  end
479
713
 
714
+
480
715
  # Disconnects from the database if already connected. Otherwise, this
481
716
  # method does nothing.
482
717
  def disconnect!
483
- clear_cache!
718
+ clear_cache!(new_connection: true)
484
719
  reset_transaction
720
+ @raw_connection_dirty = false
485
721
  end
486
722
 
487
723
  # Immediately forget this connection ever existed. Unlike disconnect!,
@@ -492,22 +728,20 @@ module ActiveRecord
492
728
  # rid of a connection that belonged to its parent.
493
729
  def discard!
494
730
  # This should be overridden by concrete adapters.
495
- #
496
- # Prevent @connection's finalizer from touching the socket, or
497
- # otherwise communicating with its server, when it is collected.
498
- if schema_cache.connection == self
499
- schema_cache.connection = nil
500
- end
501
731
  end
502
732
 
503
733
  # Reset the state of this connection, directing the DBMS to clear
504
734
  # transactions and other connection-related server-side state. Usually a
505
735
  # database-dependent operation.
506
736
  #
507
- # The default implementation does nothing; the implementation should be
508
- # overridden by concrete adapters.
737
+ # If a database driver or protocol does not support such a feature,
738
+ # implementors may alias this to #reconnect!. Otherwise, implementors
739
+ # should call super immediately after resetting the connection (and while
740
+ # still holding @lock).
509
741
  def reset!
510
- # this should be overridden by concrete adapters
742
+ clear_cache!(new_connection: true)
743
+ reset_transaction
744
+ configure_connection
511
745
  end
512
746
 
513
747
  # Removes the connection from the pool and disconnect it.
@@ -517,8 +751,16 @@ module ActiveRecord
517
751
  end
518
752
 
519
753
  # Clear any caching the database adapter may be doing.
520
- def clear_cache!
521
- @lock.synchronize { @statements.clear } if @statements
754
+ def clear_cache!(new_connection: false)
755
+ if @statements
756
+ @lock.synchronize do
757
+ if new_connection
758
+ @statements.reset
759
+ else
760
+ @statements.clear
761
+ end
762
+ end
763
+ end
522
764
  end
523
765
 
524
766
  # Returns true if its required to reload the connection between requests for development mode.
@@ -530,7 +772,33 @@ module ActiveRecord
530
772
  # This is done under the hood by calling #active?. If the connection
531
773
  # is no longer active, then this method will reconnect to the database.
532
774
  def verify!
533
- reconnect! unless active?
775
+ unless active?
776
+ if @unconfigured_connection
777
+ @lock.synchronize do
778
+ if @unconfigured_connection
779
+ @raw_connection = @unconfigured_connection
780
+ @unconfigured_connection = nil
781
+ configure_connection
782
+ @verified = true
783
+ return
784
+ end
785
+ end
786
+ end
787
+
788
+ reconnect!(restore_transactions: true)
789
+ end
790
+
791
+ @verified = true
792
+ end
793
+
794
+ def connect!
795
+ verify!
796
+ self
797
+ end
798
+
799
+ def clean! # :nodoc:
800
+ @raw_connection_dirty = false
801
+ @verified = nil
534
802
  end
535
803
 
536
804
  # Provides access to the underlying database driver for this adapter. For
@@ -539,9 +807,16 @@ module ActiveRecord
539
807
  #
540
808
  # This is useful for when you need to call a proprietary method such as
541
809
  # PostgreSQL's lo_* methods.
810
+ #
811
+ # Active Record cannot track if the database is getting modified using
812
+ # this client. If that is the case, generally you'll want to invalidate
813
+ # the query cache using +ActiveRecord::Base.clear_query_cache+.
542
814
  def raw_connection
543
- disable_lazy_transactions!
544
- @connection
815
+ with_raw_connection do |conn|
816
+ disable_lazy_transactions!
817
+ @raw_connection_dirty = true
818
+ conn
819
+ end
545
820
  end
546
821
 
547
822
  def default_uniqueness_comparison(attribute, value) # :nodoc:
@@ -599,78 +874,259 @@ module ActiveRecord
599
874
  def check_version # :nodoc:
600
875
  end
601
876
 
602
- private
603
- def type_map
604
- @type_map ||= Type::TypeMap.new.tap do |mapping|
605
- initialize_type_map(mapping)
877
+ # Returns the version identifier of the schema currently available in
878
+ # the database. This is generally equal to the number of the highest-
879
+ # numbered migration that has been executed, or 0 if no schema
880
+ # information is present / the database is empty.
881
+ def schema_version
882
+ migration_context.current_version
883
+ end
884
+
885
+ class << self
886
+ def register_class_with_precision(mapping, key, klass, **kwargs) # :nodoc:
887
+ mapping.register_type(key) do |*args|
888
+ precision = extract_precision(args.last)
889
+ klass.new(precision: precision, **kwargs)
606
890
  end
607
891
  end
608
892
 
609
- def initialize_type_map(m = type_map)
610
- register_class_with_limit m, %r(boolean)i, Type::Boolean
611
- register_class_with_limit m, %r(char)i, Type::String
612
- register_class_with_limit m, %r(binary)i, Type::Binary
613
- register_class_with_limit m, %r(text)i, Type::Text
614
- register_class_with_precision m, %r(date)i, Type::Date
615
- register_class_with_precision m, %r(time)i, Type::Time
616
- register_class_with_precision m, %r(datetime)i, Type::DateTime
617
- register_class_with_limit m, %r(float)i, Type::Float
618
- register_class_with_limit m, %r(int)i, Type::Integer
619
-
620
- m.alias_type %r(blob)i, "binary"
621
- m.alias_type %r(clob)i, "text"
622
- m.alias_type %r(timestamp)i, "datetime"
623
- m.alias_type %r(numeric)i, "decimal"
624
- m.alias_type %r(number)i, "decimal"
625
- m.alias_type %r(double)i, "float"
626
-
627
- m.register_type %r(^json)i, Type::Json.new
628
-
629
- m.register_type(%r(decimal)i) do |sql_type|
630
- scale = extract_scale(sql_type)
631
- precision = extract_precision(sql_type)
632
-
633
- if scale == 0
634
- # FIXME: Remove this class as well
635
- Type::DecimalWithoutScale.new(precision: precision)
893
+ def extended_type_map(default_timezone:) # :nodoc:
894
+ Type::TypeMap.new(self::TYPE_MAP).tap do |m|
895
+ register_class_with_precision m, %r(\A[^\(]*time)i, Type::Time, timezone: default_timezone
896
+ register_class_with_precision m, %r(\A[^\(]*datetime)i, Type::DateTime, timezone: default_timezone
897
+ m.alias_type %r(\A[^\(]*timestamp)i, "datetime"
898
+ end
899
+ end
900
+
901
+ private
902
+ def initialize_type_map(m)
903
+ register_class_with_limit m, %r(boolean)i, Type::Boolean
904
+ register_class_with_limit m, %r(char)i, Type::String
905
+ register_class_with_limit m, %r(binary)i, Type::Binary
906
+ register_class_with_limit m, %r(text)i, Type::Text
907
+ register_class_with_precision m, %r(date)i, Type::Date
908
+ register_class_with_precision m, %r(time)i, Type::Time
909
+ register_class_with_precision m, %r(datetime)i, Type::DateTime
910
+ register_class_with_limit m, %r(float)i, Type::Float
911
+ register_class_with_limit m, %r(int)i, Type::Integer
912
+
913
+ m.alias_type %r(blob)i, "binary"
914
+ m.alias_type %r(clob)i, "text"
915
+ m.alias_type %r(timestamp)i, "datetime"
916
+ m.alias_type %r(numeric)i, "decimal"
917
+ m.alias_type %r(number)i, "decimal"
918
+ m.alias_type %r(double)i, "float"
919
+
920
+ m.register_type %r(^json)i, Type::Json.new
921
+
922
+ m.register_type(%r(decimal)i) do |sql_type|
923
+ scale = extract_scale(sql_type)
924
+ precision = extract_precision(sql_type)
925
+
926
+ if scale == 0
927
+ # FIXME: Remove this class as well
928
+ Type::DecimalWithoutScale.new(precision: precision)
929
+ else
930
+ Type::Decimal.new(precision: precision, scale: scale)
931
+ end
932
+ end
933
+ end
934
+
935
+ def register_class_with_limit(mapping, key, klass)
936
+ mapping.register_type(key) do |*args|
937
+ limit = extract_limit(args.last)
938
+ klass.new(limit: limit)
939
+ end
940
+ end
941
+
942
+ def extract_scale(sql_type)
943
+ case sql_type
944
+ when /\((\d+)\)/ then 0
945
+ when /\((\d+)(,(\d+))\)/ then $3.to_i
946
+ end
947
+ end
948
+
949
+ def extract_precision(sql_type)
950
+ $1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
951
+ end
952
+
953
+ def extract_limit(sql_type)
954
+ $1.to_i if sql_type =~ /\((.*)\)/
955
+ end
956
+ end
957
+
958
+ TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
959
+ EXTENDED_TYPE_MAPS = Concurrent::Map.new
960
+
961
+ private
962
+ def reconnect_can_restore_state?
963
+ transaction_manager.restorable? && !@raw_connection_dirty
964
+ end
965
+
966
+ # Lock the monitor, ensure we're properly connected and
967
+ # transactions are materialized, and then yield the underlying
968
+ # raw connection object.
969
+ #
970
+ # If +allow_retry+ is true, a connection-related exception will
971
+ # cause an automatic reconnect and re-run of the block, up to
972
+ # the connection's configured +connection_retries+ setting
973
+ # and the configured +retry_deadline+ limit. (Note that when
974
+ # +allow_retry+ is true, it's possible to return without having marked
975
+ # the connection as verified. If the block is guaranteed to exercise the
976
+ # connection, consider calling `verified!` to avoid needless
977
+ # verification queries in subsequent calls.)
978
+ #
979
+ # If +materialize_transactions+ is false, the block will be run without
980
+ # ensuring virtual transactions have been materialized in the DB
981
+ # server's state. The active transaction will also remain clean
982
+ # (if it is not already dirty), meaning it's able to be restored
983
+ # by reconnecting and opening an equivalent-depth set of new
984
+ # transactions. This should only be used by transaction control
985
+ # methods, and internal transaction-agnostic queries.
986
+ #
987
+ ###
988
+ #
989
+ # It's not the primary use case, so not something to optimize
990
+ # for, but note that this method does need to be re-entrant:
991
+ # +materialize_transactions+ will re-enter if it has work to do,
992
+ # and the yield block can also do so under some circumstances.
993
+ #
994
+ # In the latter case, we really ought to guarantee the inner
995
+ # call will not reconnect (which would interfere with the
996
+ # still-yielded connection in the outer block), but we currently
997
+ # provide no special enforcement there.
998
+ #
999
+ def with_raw_connection(allow_retry: false, materialize_transactions: true)
1000
+ @lock.synchronize do
1001
+ connect! if @raw_connection.nil? && reconnect_can_restore_state?
1002
+
1003
+ self.materialize_transactions if materialize_transactions
1004
+
1005
+ retries_available = allow_retry ? connection_retries : 0
1006
+ deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
1007
+ reconnectable = reconnect_can_restore_state?
1008
+
1009
+ if @verified
1010
+ # Cool, we're confident the connection's ready to use. (Note this might have
1011
+ # become true during the above #materialize_transactions.)
1012
+ elsif reconnectable
1013
+ if allow_retry
1014
+ # Not sure about the connection yet, but if anything goes wrong we can
1015
+ # just reconnect and re-run our query
1016
+ else
1017
+ # We can reconnect if needed, but we don't trust the upcoming query to be
1018
+ # safely re-runnable: let's verify the connection to be sure
1019
+ verify!
1020
+ end
636
1021
  else
637
- Type::Decimal.new(precision: precision, scale: scale)
1022
+ # We don't know whether the connection is okay, but it also doesn't matter:
1023
+ # we wouldn't be able to reconnect anyway. We're just going to run our query
1024
+ # and hope for the best.
1025
+ end
1026
+
1027
+ begin
1028
+ yield @raw_connection
1029
+ rescue => original_exception
1030
+ translated_exception = translate_exception_class(original_exception, nil, nil)
1031
+ invalidate_transaction(translated_exception)
1032
+ retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
1033
+
1034
+ if !retry_deadline_exceeded && retries_available > 0
1035
+ retries_available -= 1
1036
+
1037
+ if retryable_query_error?(translated_exception)
1038
+ backoff(connection_retries - retries_available)
1039
+ retry
1040
+ elsif reconnectable && retryable_connection_error?(translated_exception)
1041
+ reconnect!(restore_transactions: true)
1042
+ # Only allowed to reconnect once, because reconnect! has its own retry
1043
+ # loop
1044
+ reconnectable = false
1045
+ retry
1046
+ end
1047
+ end
1048
+
1049
+ unless retryable_query_error?(translated_exception)
1050
+ # Barring a known-retryable error inside the query (regardless of
1051
+ # whether we were in a _position_ to retry it), we should infer that
1052
+ # there's likely a real problem with the connection.
1053
+ @verified = false
1054
+ end
1055
+
1056
+ raise translated_exception
1057
+ ensure
1058
+ dirty_current_transaction if materialize_transactions
638
1059
  end
639
1060
  end
640
1061
  end
641
1062
 
642
- def reload_type_map
643
- type_map.clear
644
- initialize_type_map
1063
+ # Mark the connection as verified. Call this inside a
1064
+ # `with_raw_connection` block only when the block is guaranteed to
1065
+ # exercise the raw connection.
1066
+ def verified!
1067
+ @verified = true
645
1068
  end
646
1069
 
647
- def register_class_with_limit(mapping, key, klass)
648
- mapping.register_type(key) do |*args|
649
- limit = extract_limit(args.last)
650
- klass.new(limit: limit)
651
- end
1070
+ def retryable_connection_error?(exception)
1071
+ exception.is_a?(ConnectionNotEstablished) || exception.is_a?(ConnectionFailed)
652
1072
  end
653
1073
 
654
- def register_class_with_precision(mapping, key, klass)
655
- mapping.register_type(key) do |*args|
656
- precision = extract_precision(args.last)
657
- klass.new(precision: precision)
658
- end
1074
+ def invalidate_transaction(exception)
1075
+ return unless exception.is_a?(TransactionRollbackError)
1076
+ return unless savepoint_errors_invalidate_transactions?
1077
+
1078
+ current_transaction.invalidate!
659
1079
  end
660
1080
 
661
- def extract_scale(sql_type)
662
- case sql_type
663
- when /\((\d+)\)/ then 0
664
- when /\((\d+)(,(\d+))\)/ then $3.to_i
665
- end
1081
+ def retryable_query_error?(exception)
1082
+ # We definitely can't retry if we were inside an invalidated transaction.
1083
+ return false if current_transaction.invalidated?
1084
+
1085
+ exception.is_a?(Deadlocked) || exception.is_a?(LockWaitTimeout)
666
1086
  end
667
1087
 
668
- def extract_precision(sql_type)
669
- $1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
1088
+ def backoff(counter)
1089
+ sleep 0.1 * counter
670
1090
  end
671
1091
 
672
- def extract_limit(sql_type)
673
- $1.to_i if sql_type =~ /\((.*)\)/
1092
+ def reconnect
1093
+ raise NotImplementedError
1094
+ end
1095
+
1096
+ # Returns a raw connection for internal use with methods that are known
1097
+ # to both be thread-safe and not rely upon actual server communication.
1098
+ # This is useful for e.g. string escaping methods.
1099
+ def any_raw_connection
1100
+ @raw_connection || valid_raw_connection
1101
+ end
1102
+
1103
+ # Similar to any_raw_connection, but ensures it is validated and
1104
+ # connected. Any method called on this result still needs to be
1105
+ # independently thread-safe, so it probably shouldn't talk to the
1106
+ # server... but some drivers fail if they know the connection has gone
1107
+ # away.
1108
+ def valid_raw_connection
1109
+ (@verified && @raw_connection) ||
1110
+ # `allow_retry: false`, to force verification: the block won't
1111
+ # raise, so a retry wouldn't help us get the valid connection we
1112
+ # need.
1113
+ with_raw_connection(allow_retry: false, materialize_transactions: false) { |conn| conn }
1114
+ end
1115
+
1116
+ def extended_type_map_key
1117
+ if @default_timezone
1118
+ { default_timezone: @default_timezone }
1119
+ end
1120
+ end
1121
+
1122
+ def type_map
1123
+ if key = extended_type_map_key
1124
+ self.class::EXTENDED_TYPE_MAPS.compute_if_absent(key) do
1125
+ self.class.extended_type_map(**key)
1126
+ end
1127
+ else
1128
+ self.class::TYPE_MAP
1129
+ end
674
1130
  end
675
1131
 
676
1132
  def translate_exception_class(e, sql, binds)
@@ -683,7 +1139,7 @@ module ActiveRecord
683
1139
  exception
684
1140
  end
685
1141
 
686
- def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) # :doc:
1142
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false, &block) # :doc:
687
1143
  @instrumenter.instrument(
688
1144
  "sql.active_record",
689
1145
  sql: sql,
@@ -691,22 +1147,28 @@ module ActiveRecord
691
1147
  binds: binds,
692
1148
  type_casted_binds: type_casted_binds,
693
1149
  statement_name: statement_name,
694
- connection: self) do
695
- @lock.synchronize do
696
- yield
697
- end
698
- rescue => e
699
- raise translate_exception_class(e, sql, binds)
1150
+ async: async,
1151
+ connection: self,
1152
+ &block
1153
+ )
1154
+ rescue ActiveRecord::StatementInvalid => ex
1155
+ raise ex.set_query(sql, binds)
1156
+ end
1157
+
1158
+ def transform_query(sql)
1159
+ ActiveRecord.query_transformers.each do |transformer|
1160
+ sql = transformer.call(sql, self)
700
1161
  end
1162
+ sql
701
1163
  end
702
1164
 
703
1165
  def translate_exception(exception, message:, sql:, binds:)
704
1166
  # override in derived class
705
1167
  case exception
706
- when RuntimeError
1168
+ when RuntimeError, ActiveRecord::ActiveRecordError
707
1169
  exception
708
1170
  else
709
- ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
1171
+ ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
710
1172
  end
711
1173
  end
712
1174
 
@@ -753,6 +1215,26 @@ module ActiveRecord
753
1215
  def build_result(columns:, rows:, column_types: {})
754
1216
  ActiveRecord::Result.new(columns, rows, column_types)
755
1217
  end
1218
+
1219
+ # Perform any necessary initialization upon the newly-established
1220
+ # @raw_connection -- this is the place to modify the adapter's
1221
+ # connection settings, run queries to configure any application-global
1222
+ # "session" variables, etc.
1223
+ #
1224
+ # Implementations may assume this method will only be called while
1225
+ # holding @lock (or from #initialize).
1226
+ def configure_connection
1227
+ end
1228
+
1229
+ def default_prepared_statements
1230
+ true
1231
+ end
1232
+
1233
+ def warning_ignored?(warning)
1234
+ ActiveRecord.db_warnings_ignore.any? do |warning_matcher|
1235
+ warning.message.match?(warning_matcher) || warning.code.to_s.match?(warning_matcher)
1236
+ end
1237
+ end
756
1238
  end
757
1239
  end
758
1240
  end