activerecord 7.0.0 → 7.2.1

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 (289) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +515 -1268
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +31 -31
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +23 -8
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +28 -17
  20. data/lib/active_record/associations/collection_proxy.rb +36 -13
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +28 -18
  24. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
  27. data/lib/active_record/associations/join_dependency.rb +18 -14
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +2 -4
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +378 -491
  36. data/lib/active_record/attribute_assignment.rb +1 -13
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +153 -70
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +153 -40
  47. data/lib/active_record/attributes.rb +63 -48
  48. data/lib/active_record/autosave_association.rb +70 -38
  49. data/lib/active_record/base.rb +12 -8
  50. data/lib/active_record/callbacks.rb +16 -32
  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 -34
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +124 -132
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +297 -88
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +215 -63
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +83 -65
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +319 -135
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +512 -126
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +282 -119
  70. data/lib/active_record/connection_adapters/column.rb +9 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +27 -140
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +64 -52
  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 +45 -14
  78. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
  80. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  81. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +16 -3
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +101 -48
  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/hstore.rb +2 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +4 -2
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +94 -61
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +379 -66
  98. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  99. data/lib/active_record/connection_adapters/postgresql_adapter.rb +370 -203
  100. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  101. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  102. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  103. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +61 -46
  104. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  105. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  106. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  107. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +64 -22
  108. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +321 -110
  109. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  110. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  111. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  112. data/lib/active_record/connection_adapters.rb +124 -1
  113. data/lib/active_record/connection_handling.rb +98 -106
  114. data/lib/active_record/core.rb +220 -177
  115. data/lib/active_record/counter_cache.rb +68 -34
  116. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -2
  117. data/lib/active_record/database_configurations/database_config.rb +26 -5
  118. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  119. data/lib/active_record/database_configurations/url_config.rb +37 -12
  120. data/lib/active_record/database_configurations.rb +88 -35
  121. data/lib/active_record/delegated_type.rb +40 -11
  122. data/lib/active_record/deprecator.rb +7 -0
  123. data/lib/active_record/destroy_association_async_job.rb +3 -1
  124. data/lib/active_record/disable_joins_association_relation.rb +1 -1
  125. data/lib/active_record/dynamic_matchers.rb +2 -2
  126. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  127. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  128. data/lib/active_record/encryption/config.rb +25 -1
  129. data/lib/active_record/encryption/configurable.rb +13 -14
  130. data/lib/active_record/encryption/context.rb +10 -3
  131. data/lib/active_record/encryption/contexts.rb +8 -4
  132. data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
  133. data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
  134. data/lib/active_record/encryption/encryptable_record.rb +47 -25
  135. data/lib/active_record/encryption/encrypted_attribute_type.rb +49 -14
  136. data/lib/active_record/encryption/encryptor.rb +25 -10
  137. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
  138. data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -86
  139. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  140. data/lib/active_record/encryption/key_generator.rb +12 -1
  141. data/lib/active_record/encryption/message.rb +1 -1
  142. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  143. data/lib/active_record/encryption/message_serializer.rb +6 -0
  144. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  145. data/lib/active_record/encryption/properties.rb +4 -4
  146. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  147. data/lib/active_record/encryption/scheme.rb +23 -22
  148. data/lib/active_record/encryption.rb +1 -0
  149. data/lib/active_record/enum.rb +131 -27
  150. data/lib/active_record/errors.rb +151 -31
  151. data/lib/active_record/explain.rb +21 -12
  152. data/lib/active_record/explain_subscriber.rb +1 -1
  153. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  154. data/lib/active_record/fixture_set/render_context.rb +2 -0
  155. data/lib/active_record/fixture_set/table_row.rb +29 -8
  156. data/lib/active_record/fixtures.rb +169 -99
  157. data/lib/active_record/future_result.rb +47 -8
  158. data/lib/active_record/gem_version.rb +3 -3
  159. data/lib/active_record/inheritance.rb +34 -18
  160. data/lib/active_record/insert_all.rb +72 -22
  161. data/lib/active_record/integration.rb +13 -10
  162. data/lib/active_record/internal_metadata.rb +124 -20
  163. data/lib/active_record/locking/optimistic.rb +39 -24
  164. data/lib/active_record/locking/pessimistic.rb +8 -5
  165. data/lib/active_record/log_subscriber.rb +28 -27
  166. data/lib/active_record/marshalling.rb +56 -0
  167. data/lib/active_record/message_pack.rb +124 -0
  168. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  169. data/lib/active_record/middleware/database_selector.rb +18 -13
  170. data/lib/active_record/middleware/shard_selector.rb +7 -5
  171. data/lib/active_record/migration/command_recorder.rb +110 -13
  172. data/lib/active_record/migration/compatibility.rb +174 -64
  173. data/lib/active_record/migration/default_strategy.rb +22 -0
  174. data/lib/active_record/migration/execution_strategy.rb +19 -0
  175. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  176. data/lib/active_record/migration.rb +292 -125
  177. data/lib/active_record/model_schema.rb +113 -112
  178. data/lib/active_record/nested_attributes.rb +35 -9
  179. data/lib/active_record/normalization.rb +163 -0
  180. data/lib/active_record/persistence.rb +177 -345
  181. data/lib/active_record/promise.rb +84 -0
  182. data/lib/active_record/query_cache.rb +19 -25
  183. data/lib/active_record/query_logs.rb +102 -51
  184. data/lib/active_record/query_logs_formatter.rb +41 -0
  185. data/lib/active_record/querying.rb +34 -9
  186. data/lib/active_record/railtie.rb +153 -100
  187. data/lib/active_record/railties/controller_runtime.rb +24 -10
  188. data/lib/active_record/railties/databases.rake +148 -152
  189. data/lib/active_record/railties/job_runtime.rb +23 -0
  190. data/lib/active_record/readonly_attributes.rb +32 -5
  191. data/lib/active_record/reflection.rb +278 -69
  192. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  193. data/lib/active_record/relation/batches.rb +198 -63
  194. data/lib/active_record/relation/calculations.rb +293 -108
  195. data/lib/active_record/relation/delegation.rb +31 -20
  196. data/lib/active_record/relation/finder_methods.rb +93 -18
  197. data/lib/active_record/relation/merger.rb +6 -6
  198. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  199. data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
  200. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  201. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  202. data/lib/active_record/relation/predicate_builder.rb +28 -16
  203. data/lib/active_record/relation/query_attribute.rb +25 -1
  204. data/lib/active_record/relation/query_methods.rb +625 -107
  205. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  206. data/lib/active_record/relation/spawn_methods.rb +5 -4
  207. data/lib/active_record/relation/where_clause.rb +7 -19
  208. data/lib/active_record/relation.rb +602 -96
  209. data/lib/active_record/result.rb +55 -52
  210. data/lib/active_record/runtime_registry.rb +63 -1
  211. data/lib/active_record/sanitization.rb +76 -30
  212. data/lib/active_record/schema.rb +39 -23
  213. data/lib/active_record/schema_dumper.rb +82 -30
  214. data/lib/active_record/schema_migration.rb +75 -24
  215. data/lib/active_record/scoping/default.rb +20 -12
  216. data/lib/active_record/scoping/named.rb +3 -2
  217. data/lib/active_record/scoping.rb +2 -1
  218. data/lib/active_record/secure_password.rb +60 -0
  219. data/lib/active_record/secure_token.rb +21 -3
  220. data/lib/active_record/serialization.rb +5 -0
  221. data/lib/active_record/signed_id.rb +29 -8
  222. data/lib/active_record/statement_cache.rb +7 -7
  223. data/lib/active_record/store.rb +16 -11
  224. data/lib/active_record/suppressor.rb +3 -1
  225. data/lib/active_record/table_metadata.rb +7 -3
  226. data/lib/active_record/tasks/database_tasks.rb +191 -121
  227. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  228. data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
  229. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  230. data/lib/active_record/test_fixtures.rb +174 -152
  231. data/lib/active_record/testing/query_assertions.rb +121 -0
  232. data/lib/active_record/timestamp.rb +31 -17
  233. data/lib/active_record/token_for.rb +123 -0
  234. data/lib/active_record/touch_later.rb +12 -7
  235. data/lib/active_record/transaction.rb +132 -0
  236. data/lib/active_record/transactions.rb +109 -27
  237. data/lib/active_record/translation.rb +1 -3
  238. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  239. data/lib/active_record/type/internal/timezone.rb +7 -2
  240. data/lib/active_record/type/serialized.rb +9 -7
  241. data/lib/active_record/type/time.rb +4 -0
  242. data/lib/active_record/type_caster/connection.rb +4 -4
  243. data/lib/active_record/validations/absence.rb +1 -1
  244. data/lib/active_record/validations/associated.rb +12 -6
  245. data/lib/active_record/validations/numericality.rb +5 -4
  246. data/lib/active_record/validations/presence.rb +5 -28
  247. data/lib/active_record/validations/uniqueness.rb +63 -14
  248. data/lib/active_record/validations.rb +12 -5
  249. data/lib/active_record/version.rb +1 -1
  250. data/lib/active_record.rb +266 -30
  251. data/lib/arel/alias_predication.rb +1 -1
  252. data/lib/arel/collectors/bind.rb +2 -0
  253. data/lib/arel/collectors/composite.rb +7 -0
  254. data/lib/arel/collectors/sql_string.rb +1 -1
  255. data/lib/arel/collectors/substitute_binds.rb +1 -1
  256. data/lib/arel/errors.rb +10 -0
  257. data/lib/arel/factory_methods.rb +4 -0
  258. data/lib/arel/filter_predications.rb +1 -1
  259. data/lib/arel/nodes/binary.rb +6 -7
  260. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  261. data/lib/arel/nodes/cte.rb +36 -0
  262. data/lib/arel/nodes/filter.rb +1 -1
  263. data/lib/arel/nodes/fragments.rb +35 -0
  264. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  265. data/lib/arel/nodes/leading_join.rb +8 -0
  266. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  267. data/lib/arel/nodes/node.rb +115 -5
  268. data/lib/arel/nodes/sql_literal.rb +13 -0
  269. data/lib/arel/nodes/table_alias.rb +4 -0
  270. data/lib/arel/nodes.rb +6 -2
  271. data/lib/arel/predications.rb +3 -1
  272. data/lib/arel/select_manager.rb +1 -1
  273. data/lib/arel/table.rb +9 -5
  274. data/lib/arel/tree_manager.rb +8 -3
  275. data/lib/arel/update_manager.rb +2 -1
  276. data/lib/arel/visitors/dot.rb +1 -0
  277. data/lib/arel/visitors/mysql.rb +17 -5
  278. data/lib/arel/visitors/postgresql.rb +1 -12
  279. data/lib/arel/visitors/to_sql.rb +112 -34
  280. data/lib/arel/visitors/visitor.rb +2 -2
  281. data/lib/arel.rb +21 -3
  282. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  283. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  284. data/lib/rails/generators/active_record/migration.rb +3 -1
  285. data/lib/rails/generators/active_record/model/USAGE +113 -0
  286. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  287. metadata +59 -17
  288. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  289. data/lib/active_record/null_relation.rb +0 -63
@@ -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
@@ -20,7 +23,7 @@ module ActiveRecord
20
23
  # and +:limit+ options, etc.
21
24
  #
22
25
  # All the concrete database adapters follow the interface laid down in this class.
23
- # {ActiveRecord::Base.connection}[rdoc-ref:ConnectionHandling#connection] returns an AbstractAdapter object, which
26
+ # {ActiveRecord::Base.lease_connection}[rdoc-ref:ConnectionHandling#lease_connection] returns an AbstractAdapter object, which
24
27
  # you can use.
25
28
  #
26
29
  # Most of the methods in the adapter are useful during migrations. Most
@@ -36,12 +39,18 @@ 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
+ end
53
+
45
54
  set_callback :checkin, :after, :enable_lazy_transactions!
46
55
 
47
56
  def self.type_cast_config_to_integer(config)
@@ -62,6 +71,16 @@ module ActiveRecord
62
71
  end
63
72
  end
64
73
 
74
+ def self.validate_default_timezone(config)
75
+ case config
76
+ when nil
77
+ when "utc", "local"
78
+ config.to_sym
79
+ else
80
+ raise ArgumentError, "default_timezone must be either 'utc' or 'local'"
81
+ end
82
+ end
83
+
65
84
  DEFAULT_READ_QUERY = [:begin, :commit, :explain, :release, :rollback, :savepoint, :select, :with] # :nodoc:
66
85
  private_constant :DEFAULT_READ_QUERY
67
86
 
@@ -71,35 +90,105 @@ module ActiveRecord
71
90
  /\A(?:[(\s]|#{COMMENT_REGEX})*#{Regexp.union(*parts)}/
72
91
  end
73
92
 
74
- def self.quoted_column_names # :nodoc:
75
- @quoted_column_names ||= {}
93
+ def self.find_cmd_and_exec(commands, *args) # :doc:
94
+ commands = Array(commands)
95
+
96
+ dirs_on_path = ENV["PATH"].to_s.split(File::PATH_SEPARATOR)
97
+ unless (ext = RbConfig::CONFIG["EXEEXT"]).empty?
98
+ commands = commands.map { |cmd| "#{cmd}#{ext}" }
99
+ end
100
+
101
+ full_path_command = nil
102
+ found = commands.detect do |cmd|
103
+ dirs_on_path.detect do |path|
104
+ full_path_command = File.join(path, cmd)
105
+ begin
106
+ stat = File.stat(full_path_command)
107
+ rescue SystemCallError
108
+ else
109
+ stat.file? && stat.executable?
110
+ end
111
+ end
112
+ end
113
+
114
+ if found
115
+ exec full_path_command, *args
116
+ else
117
+ abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
118
+ end
76
119
  end
77
120
 
78
- def self.quoted_table_names # :nodoc:
79
- @quoted_table_names ||= {}
121
+ # Opens a database console session.
122
+ def self.dbconsole(config, options = {})
123
+ raise NotImplementedError
80
124
  end
81
125
 
82
- def initialize(connection, logger = nil, config = {}) # :nodoc:
126
+ def initialize(config_or_deprecated_connection, deprecated_logger = nil, deprecated_connection_options = nil, deprecated_config = nil) # :nodoc:
83
127
  super()
84
128
 
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 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
129
+ @raw_connection = nil
130
+ @unconfigured_connection = nil
131
+
132
+ if config_or_deprecated_connection.is_a?(Hash)
133
+ @config = config_or_deprecated_connection.symbolize_keys
134
+ @logger = ActiveRecord::Base.logger
135
+
136
+ if deprecated_logger || deprecated_connection_options || deprecated_config
137
+ raise ArgumentError, "when initializing an Active Record adapter with a config hash, that should be the only argument"
138
+ end
139
+ else
140
+ # Soft-deprecated for now; we'll probably warn in future.
141
+
142
+ @unconfigured_connection = config_or_deprecated_connection
143
+ @logger = deprecated_logger || ActiveRecord::Base.logger
144
+ if deprecated_config
145
+ @config = (deprecated_config || {}).symbolize_keys
146
+ @connection_parameters = deprecated_connection_options
147
+ else
148
+ @config = (deprecated_connection_options || {}).symbolize_keys
149
+ @connection_parameters = nil
150
+ end
151
+ end
152
+
153
+ @owner = nil
154
+ @instrumenter = ActiveSupport::Notifications.instrumenter
155
+ @pool = ActiveRecord::ConnectionAdapters::NullPool.new
156
+ @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
92
157
  @visitor = arel_visitor
93
158
  @statements = build_statement_pool
94
- @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
159
+ self.lock_thread = nil
95
160
 
96
- @prepared_statements = self.class.type_cast_config_to_boolean(
97
- config.fetch(:prepared_statements, true)
161
+ @prepared_statements = !ActiveRecord.disable_prepared_statements && self.class.type_cast_config_to_boolean(
162
+ @config.fetch(:prepared_statements) { default_prepared_statements }
98
163
  )
99
164
 
100
165
  @advisory_locks_enabled = self.class.type_cast_config_to_boolean(
101
- config.fetch(:advisory_locks, true)
166
+ @config.fetch(:advisory_locks, true)
102
167
  )
168
+
169
+ @default_timezone = self.class.validate_default_timezone(@config[:default_timezone])
170
+
171
+ @raw_connection_dirty = false
172
+ @verified = false
173
+ end
174
+
175
+ def inspect # :nodoc:
176
+ name_field = " name=#{pool.db_config.name.inspect}" unless pool.db_config.name == "primary"
177
+ shard_field = " shard=#{shard.inspect}" unless shard == :default
178
+
179
+ "#<#{self.class.name}:#{'%#016x' % (object_id << 1)} env_name=#{pool.db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
180
+ end
181
+
182
+ def lock_thread=(lock_thread) # :nodoc:
183
+ @lock =
184
+ case lock_thread
185
+ when Thread
186
+ ActiveSupport::Concurrency::ThreadLoadInterlockAwareMonitor.new
187
+ when Fiber
188
+ ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
189
+ else
190
+ ActiveSupport::Concurrency::NullLock
191
+ end
103
192
  end
104
193
 
105
194
  EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
@@ -125,53 +214,33 @@ module ActiveRecord
125
214
  @config[:replica] || false
126
215
  end
127
216
 
128
- def use_metadata_table?
129
- @config.fetch(:use_metadata_table, true)
217
+ def connection_retries
218
+ (@config[:connection_retries] || 1).to_i
219
+ end
220
+
221
+ def retry_deadline
222
+ if @config[:retry_deadline]
223
+ @config[:retry_deadline].to_f
224
+ else
225
+ nil
226
+ end
227
+ end
228
+
229
+ def default_timezone
230
+ @default_timezone || ActiveRecord.default_timezone
130
231
  end
131
232
 
132
233
  # Determines whether writes are currently being prevented.
133
234
  #
134
- # Returns true if the connection is a replica.
135
- #
136
- # If the application is using legacy handling, returns
137
- # true if +connection_handler.prevent_writes+ is set.
138
- #
139
- # If the application is using the new connection handling
140
- # will return true based on +current_preventing_writes+.
235
+ # Returns true if the connection is a replica or returns
236
+ # the value of +current_preventing_writes+.
141
237
  def preventing_writes?
142
238
  return true if replica?
143
- return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord.legacy_connection_handling
144
239
  return false if connection_class.nil?
145
240
 
146
241
  connection_class.current_preventing_writes
147
242
  end
148
243
 
149
- def migrations_paths # :nodoc:
150
- @config[:migrations_paths] || Migrator.migrations_paths
151
- end
152
-
153
- def migration_context # :nodoc:
154
- MigrationContext.new(migrations_paths, schema_migration)
155
- end
156
-
157
- def schema_migration # :nodoc:
158
- @schema_migration ||= begin
159
- conn = self
160
- spec_name = conn.pool.pool_config.connection_specification_name
161
-
162
- return ActiveRecord::SchemaMigration if spec_name == "ActiveRecord::Base"
163
-
164
- schema_migration_name = "#{spec_name}::SchemaMigration"
165
-
166
- Class.new(ActiveRecord::SchemaMigration) do
167
- define_singleton_method(:name) { schema_migration_name }
168
- define_singleton_method(:to_s) { schema_migration_name }
169
-
170
- self.connection_specification_name = spec_name
171
- end
172
- end
173
- end
174
-
175
244
  def prepared_statements?
176
245
  @prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
177
246
  end
@@ -208,50 +277,45 @@ module ActiveRecord
208
277
  def lease
209
278
  if in_use?
210
279
  msg = +"Cannot lease connection, "
211
- if @owner == Thread.current
280
+ if @owner == ActiveSupport::IsolatedExecutionState.context
212
281
  msg << "it is already leased by the current thread."
213
282
  else
214
283
  msg << "it is already in use by a different thread: #{@owner}. " \
215
- "Current thread: #{Thread.current}."
284
+ "Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
216
285
  end
217
286
  raise ActiveRecordError, msg
218
287
  end
219
288
 
220
- @owner = Thread.current
289
+ @owner = ActiveSupport::IsolatedExecutionState.context
221
290
  end
222
291
 
223
292
  def connection_class # :nodoc:
224
293
  @pool.connection_class
225
294
  end
226
295
 
227
- # The role (ie :writing) for the current connection. In a
228
- # non-multi role application, `:writing` is returned.
296
+ # The role (e.g. +:writing+) for the current connection. In a
297
+ # non-multi role application, +:writing+ is returned.
229
298
  def role
230
299
  @pool.role
231
300
  end
232
301
 
233
- # The shard (ie :default) for the current connection. In
234
- # a non-sharded application, `:default` is returned.
302
+ # The shard (e.g. +:default+) for the current connection. In
303
+ # a non-sharded application, +:default+ is returned.
235
304
  def shard
236
305
  @pool.shard
237
306
  end
238
307
 
239
308
  def schema_cache
240
- @pool.get_schema_cache(self)
241
- end
242
-
243
- def schema_cache=(cache)
244
- cache.connection = self
245
- @pool.set_schema_cache(cache)
309
+ @pool.schema_cache || (@schema_cache ||= BoundSchemaReflection.for_lone_connection(@pool.schema_reflection, self))
246
310
  end
247
311
 
248
312
  # this method must only be called while holding connection pool's mutex
249
313
  def expire
250
314
  if in_use?
251
- if @owner != Thread.current
315
+ if @owner != ActiveSupport::IsolatedExecutionState.context
252
316
  raise ActiveRecordError, "Cannot expire connection, " \
253
317
  "it is owned by a different thread: #{@owner}. " \
254
- "Current thread: #{Thread.current}."
318
+ "Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
255
319
  end
256
320
 
257
321
  @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@@ -264,10 +328,10 @@ module ActiveRecord
264
328
  # this method must only be called while holding connection pool's mutex (and a desire for segfaults)
265
329
  def steal! # :nodoc:
266
330
  if in_use?
267
- if @owner != Thread.current
331
+ if @owner != ActiveSupport::IsolatedExecutionState.context
268
332
  pool.send :remove_connection_from_thread_cache, self, @owner
269
333
 
270
- @owner = Thread.current
334
+ @owner = ActiveSupport::IsolatedExecutionState.context
271
335
  end
272
336
  else
273
337
  raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
@@ -295,7 +359,14 @@ module ActiveRecord
295
359
 
296
360
  # Does the database for this adapter exist?
297
361
  def self.database_exists?(config)
298
- raise NotImplementedError
362
+ new(config).database_exists?
363
+ end
364
+
365
+ def database_exists?
366
+ connect!
367
+ true
368
+ rescue ActiveRecord::NoDatabaseError
369
+ false
299
370
  end
300
371
 
301
372
  # Does this adapter support DDL rollbacks in transactions? That is, would
@@ -313,6 +384,16 @@ module ActiveRecord
313
384
  false
314
385
  end
315
386
 
387
+ # Do TransactionRollbackErrors on savepoints affect the parent
388
+ # transaction?
389
+ def savepoint_errors_invalidate_transactions?
390
+ false
391
+ end
392
+
393
+ def supports_restart_db_transaction?
394
+ false
395
+ end
396
+
316
397
  # Does this adapter support application-enforced advisory locking?
317
398
  def supports_advisory_locks?
318
399
  false
@@ -339,6 +420,11 @@ module ActiveRecord
339
420
  false
340
421
  end
341
422
 
423
+ # Does this adapter support including non-key columns?
424
+ def supports_index_include?
425
+ false
426
+ end
427
+
342
428
  # Does this adapter support expression indices?
343
429
  def supports_expression_index?
344
430
  false
@@ -385,6 +471,16 @@ module ActiveRecord
385
471
  false
386
472
  end
387
473
 
474
+ # Does this adapter support creating exclusion constraints?
475
+ def supports_exclusion_constraints?
476
+ false
477
+ end
478
+
479
+ # Does this adapter support creating unique constraints?
480
+ def supports_unique_constraints?
481
+ false
482
+ end
483
+
388
484
  # Does this adapter support views?
389
485
  def supports_views?
390
486
  false
@@ -400,7 +496,7 @@ module ActiveRecord
400
496
  false
401
497
  end
402
498
 
403
- # Does this adapter support json data type?
499
+ # Does this adapter support JSON data type?
404
500
  def supports_json?
405
501
  false
406
502
  end
@@ -458,23 +554,47 @@ module ActiveRecord
458
554
  true
459
555
  end
460
556
 
557
+ def supports_nulls_not_distinct?
558
+ false
559
+ end
560
+
561
+ def return_value_after_insert?(column) # :nodoc:
562
+ column.auto_populated?
563
+ end
564
+
461
565
  def async_enabled? # :nodoc:
462
566
  supports_concurrent_connections? &&
463
567
  !ActiveRecord.async_query_executor.nil? && !pool.async_executor.nil?
464
568
  end
465
569
 
466
570
  # This is meant to be implemented by the adapters that support extensions
467
- def disable_extension(name)
571
+ def disable_extension(name, **)
468
572
  end
469
573
 
470
574
  # This is meant to be implemented by the adapters that support extensions
471
- def enable_extension(name)
575
+ def enable_extension(name, **)
472
576
  end
473
577
 
474
578
  # This is meant to be implemented by the adapters that support custom enum types
475
579
  def create_enum(*) # :nodoc:
476
580
  end
477
581
 
582
+ # This is meant to be implemented by the adapters that support custom enum types
583
+ def drop_enum(*) # :nodoc:
584
+ end
585
+
586
+ # This is meant to be implemented by the adapters that support custom enum types
587
+ def rename_enum(*) # :nodoc:
588
+ end
589
+
590
+ # This is meant to be implemented by the adapters that support custom enum types
591
+ def add_enum_value(*) # :nodoc:
592
+ end
593
+
594
+ # This is meant to be implemented by the adapters that support custom enum types
595
+ def rename_enum_value(*) # :nodoc:
596
+ end
597
+
478
598
  def advisory_locks_enabled? # :nodoc:
479
599
  supports_advisory_locks? && @advisory_locks_enabled
480
600
  end
@@ -511,31 +631,71 @@ module ActiveRecord
511
631
  end
512
632
 
513
633
  # Override to check all foreign key constraints in a database.
514
- def all_foreign_keys_valid?
515
- true
634
+ # The adapter should raise a +ActiveRecord::StatementInvalid+ if foreign key
635
+ # constraints are not met.
636
+ def check_all_foreign_keys_valid!
516
637
  end
517
638
 
518
639
  # CONNECTION MANAGEMENT ====================================
519
640
 
641
+ # Checks whether the connection to the database was established. This doesn't
642
+ # include checking whether the database is actually capable of responding, i.e.
643
+ # whether the connection is stale.
644
+ def connected?
645
+ !@raw_connection.nil?
646
+ end
647
+
520
648
  # Checks whether the connection to the database is still active. This includes
521
649
  # checking whether the database is actually capable of responding, i.e. whether
522
650
  # the connection isn't stale.
523
651
  def active?
524
652
  end
525
653
 
526
- # Disconnects from the database if already connected, and establishes a
527
- # new connection with the database. Implementors should call super if they
528
- # override the default implementation.
529
- def reconnect!
530
- clear_cache!
531
- reset_transaction
654
+ # Disconnects from the database if already connected, and establishes a new
655
+ # connection with the database. Implementors should define private #reconnect
656
+ # instead.
657
+ def reconnect!(restore_transactions: false)
658
+ retries_available = connection_retries
659
+ deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
660
+
661
+ @lock.synchronize do
662
+ reconnect
663
+
664
+ enable_lazy_transactions!
665
+ @raw_connection_dirty = false
666
+ @verified = true
667
+
668
+ reset_transaction(restore: restore_transactions) do
669
+ clear_cache!(new_connection: true)
670
+ configure_connection
671
+ end
672
+ rescue => original_exception
673
+ translated_exception = translate_exception_class(original_exception, nil, nil)
674
+ retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
675
+
676
+ if !retry_deadline_exceeded && retries_available > 0
677
+ retries_available -= 1
678
+
679
+ if retryable_connection_error?(translated_exception)
680
+ backoff(connection_retries - retries_available)
681
+ retry
682
+ end
683
+ end
684
+
685
+ @verified = false
686
+
687
+ raise translated_exception
688
+ end
532
689
  end
533
690
 
534
691
  # Disconnects from the database if already connected. Otherwise, this
535
692
  # method does nothing.
536
693
  def disconnect!
537
- clear_cache!
538
- reset_transaction
694
+ @lock.synchronize do
695
+ clear_cache!(new_connection: true)
696
+ reset_transaction
697
+ @raw_connection_dirty = false
698
+ end
539
699
  end
540
700
 
541
701
  # Immediately forget this connection ever existed. Unlike disconnect!,
@@ -546,22 +706,20 @@ module ActiveRecord
546
706
  # rid of a connection that belonged to its parent.
547
707
  def discard!
548
708
  # This should be overridden by concrete adapters.
549
- #
550
- # Prevent @connection's finalizer from touching the socket, or
551
- # otherwise communicating with its server, when it is collected.
552
- if schema_cache.connection == self
553
- schema_cache.connection = nil
554
- end
555
709
  end
556
710
 
557
711
  # Reset the state of this connection, directing the DBMS to clear
558
712
  # transactions and other connection-related server-side state. Usually a
559
713
  # database-dependent operation.
560
714
  #
561
- # The default implementation does nothing; the implementation should be
562
- # overridden by concrete adapters.
715
+ # If a database driver or protocol does not support such a feature,
716
+ # implementors may alias this to #reconnect!. Otherwise, implementors
717
+ # should call super immediately after resetting the connection (and while
718
+ # still holding @lock).
563
719
  def reset!
564
- # this should be overridden by concrete adapters
720
+ clear_cache!(new_connection: true)
721
+ reset_transaction
722
+ configure_connection
565
723
  end
566
724
 
567
725
  # Removes the connection from the pool and disconnect it.
@@ -571,8 +729,16 @@ module ActiveRecord
571
729
  end
572
730
 
573
731
  # Clear any caching the database adapter may be doing.
574
- def clear_cache!
575
- @lock.synchronize { @statements.clear } if @statements
732
+ def clear_cache!(new_connection: false)
733
+ if @statements
734
+ @lock.synchronize do
735
+ if new_connection
736
+ @statements.reset
737
+ else
738
+ @statements.clear
739
+ end
740
+ end
741
+ end
576
742
  end
577
743
 
578
744
  # Returns true if its required to reload the connection between requests for development mode.
@@ -584,7 +750,31 @@ module ActiveRecord
584
750
  # This is done under the hood by calling #active?. If the connection
585
751
  # is no longer active, then this method will reconnect to the database.
586
752
  def verify!
587
- reconnect! unless active?
753
+ unless active?
754
+ @lock.synchronize do
755
+ if @unconfigured_connection
756
+ @raw_connection = @unconfigured_connection
757
+ @unconfigured_connection = nil
758
+ configure_connection
759
+ @verified = true
760
+ return
761
+ end
762
+
763
+ reconnect!(restore_transactions: true)
764
+ end
765
+ end
766
+
767
+ @verified = true
768
+ end
769
+
770
+ def connect!
771
+ verify!
772
+ self
773
+ end
774
+
775
+ def clean! # :nodoc:
776
+ @raw_connection_dirty = false
777
+ @verified = nil
588
778
  end
589
779
 
590
780
  # Provides access to the underlying database driver for this adapter. For
@@ -593,9 +783,16 @@ module ActiveRecord
593
783
  #
594
784
  # This is useful for when you need to call a proprietary method such as
595
785
  # PostgreSQL's lo_* methods.
786
+ #
787
+ # Active Record cannot track if the database is getting modified using
788
+ # this client. If that is the case, generally you'll want to invalidate
789
+ # the query cache using +ActiveRecord::Base.clear_query_cache+.
596
790
  def raw_connection
597
- disable_lazy_transactions!
598
- @connection
791
+ with_raw_connection do |conn|
792
+ disable_lazy_transactions!
793
+ @raw_connection_dirty = true
794
+ conn
795
+ end
599
796
  end
600
797
 
601
798
  def default_uniqueness_comparison(attribute, value) # :nodoc:
@@ -647,7 +844,7 @@ module ActiveRecord
647
844
  end
648
845
 
649
846
  def database_version # :nodoc:
650
- schema_cache.database_version
847
+ pool.server_version(self)
651
848
  end
652
849
 
653
850
  def check_version # :nodoc:
@@ -658,19 +855,25 @@ module ActiveRecord
658
855
  # numbered migration that has been executed, or 0 if no schema
659
856
  # information is present / the database is empty.
660
857
  def schema_version
661
- migration_context.current_version
858
+ pool.migration_context.current_version
662
859
  end
663
860
 
664
- def field_ordered_value(column, values) # :nodoc:
665
- node = Arel::Nodes::Case.new(column)
666
- values.each.with_index(1) do |value, order|
667
- node.when(value).then(order)
861
+ class << self
862
+ def register_class_with_precision(mapping, key, klass, **kwargs) # :nodoc:
863
+ mapping.register_type(key) do |*args|
864
+ precision = extract_precision(args.last)
865
+ klass.new(precision: precision, **kwargs)
866
+ end
668
867
  end
669
868
 
670
- Arel::Nodes::Ascending.new(node.else(values.length + 1))
671
- end
869
+ def extended_type_map(default_timezone:) # :nodoc:
870
+ Type::TypeMap.new(self::TYPE_MAP).tap do |m|
871
+ register_class_with_precision m, %r(\A[^\(]*time)i, Type::Time, timezone: default_timezone
872
+ register_class_with_precision m, %r(\A[^\(]*datetime)i, Type::DateTime, timezone: default_timezone
873
+ m.alias_type %r(\A[^\(]*timestamp)i, "datetime"
874
+ end
875
+ end
672
876
 
673
- class << self
674
877
  private
675
878
  def initialize_type_map(m)
676
879
  register_class_with_limit m, %r(boolean)i, Type::Boolean
@@ -712,13 +915,6 @@ module ActiveRecord
712
915
  end
713
916
  end
714
917
 
715
- def register_class_with_precision(mapping, key, klass)
716
- mapping.register_type(key) do |*args|
717
- precision = extract_precision(args.last)
718
- klass.new(precision: precision)
719
- end
720
- end
721
-
722
918
  def extract_scale(sql_type)
723
919
  case sql_type
724
920
  when /\((\d+)\)/ then 0
@@ -736,10 +932,177 @@ module ActiveRecord
736
932
  end
737
933
 
738
934
  TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
935
+ EXTENDED_TYPE_MAPS = Concurrent::Map.new
739
936
 
740
937
  private
938
+ def reconnect_can_restore_state?
939
+ transaction_manager.restorable? && !@raw_connection_dirty
940
+ end
941
+
942
+ # Lock the monitor, ensure we're properly connected and
943
+ # transactions are materialized, and then yield the underlying
944
+ # raw connection object.
945
+ #
946
+ # If +allow_retry+ is true, a connection-related exception will
947
+ # cause an automatic reconnect and re-run of the block, up to
948
+ # the connection's configured +connection_retries+ setting
949
+ # and the configured +retry_deadline+ limit. (Note that when
950
+ # +allow_retry+ is true, it's possible to return without having marked
951
+ # the connection as verified. If the block is guaranteed to exercise the
952
+ # connection, consider calling `verified!` to avoid needless
953
+ # verification queries in subsequent calls.)
954
+ #
955
+ # If +materialize_transactions+ is false, the block will be run without
956
+ # ensuring virtual transactions have been materialized in the DB
957
+ # server's state. The active transaction will also remain clean
958
+ # (if it is not already dirty), meaning it's able to be restored
959
+ # by reconnecting and opening an equivalent-depth set of new
960
+ # transactions. This should only be used by transaction control
961
+ # methods, and internal transaction-agnostic queries.
962
+ #
963
+ ###
964
+ #
965
+ # It's not the primary use case, so not something to optimize
966
+ # for, but note that this method does need to be re-entrant:
967
+ # +materialize_transactions+ will re-enter if it has work to do,
968
+ # and the yield block can also do so under some circumstances.
969
+ #
970
+ # In the latter case, we really ought to guarantee the inner
971
+ # call will not reconnect (which would interfere with the
972
+ # still-yielded connection in the outer block), but we currently
973
+ # provide no special enforcement there.
974
+ #
975
+ def with_raw_connection(allow_retry: false, materialize_transactions: true)
976
+ @lock.synchronize do
977
+ connect! if @raw_connection.nil? && reconnect_can_restore_state?
978
+
979
+ self.materialize_transactions if materialize_transactions
980
+
981
+ retries_available = allow_retry ? connection_retries : 0
982
+ deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
983
+ reconnectable = reconnect_can_restore_state?
984
+
985
+ if @verified
986
+ # Cool, we're confident the connection's ready to use. (Note this might have
987
+ # become true during the above #materialize_transactions.)
988
+ elsif reconnectable
989
+ if allow_retry
990
+ # Not sure about the connection yet, but if anything goes wrong we can
991
+ # just reconnect and re-run our query
992
+ else
993
+ # We can reconnect if needed, but we don't trust the upcoming query to be
994
+ # safely re-runnable: let's verify the connection to be sure
995
+ verify!
996
+ end
997
+ else
998
+ # We don't know whether the connection is okay, but it also doesn't matter:
999
+ # we wouldn't be able to reconnect anyway. We're just going to run our query
1000
+ # and hope for the best.
1001
+ end
1002
+
1003
+ begin
1004
+ yield @raw_connection
1005
+ rescue => original_exception
1006
+ translated_exception = translate_exception_class(original_exception, nil, nil)
1007
+ invalidate_transaction(translated_exception)
1008
+ retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
1009
+
1010
+ if !retry_deadline_exceeded && retries_available > 0
1011
+ retries_available -= 1
1012
+
1013
+ if retryable_query_error?(translated_exception)
1014
+ backoff(connection_retries - retries_available)
1015
+ retry
1016
+ elsif reconnectable && retryable_connection_error?(translated_exception)
1017
+ reconnect!(restore_transactions: true)
1018
+ # Only allowed to reconnect once, because reconnect! has its own retry
1019
+ # loop
1020
+ reconnectable = false
1021
+ retry
1022
+ end
1023
+ end
1024
+
1025
+ unless retryable_query_error?(translated_exception)
1026
+ # Barring a known-retryable error inside the query (regardless of
1027
+ # whether we were in a _position_ to retry it), we should infer that
1028
+ # there's likely a real problem with the connection.
1029
+ @verified = false
1030
+ end
1031
+
1032
+ raise translated_exception
1033
+ ensure
1034
+ dirty_current_transaction if materialize_transactions
1035
+ end
1036
+ end
1037
+ end
1038
+
1039
+ # Mark the connection as verified. Call this inside a
1040
+ # `with_raw_connection` block only when the block is guaranteed to
1041
+ # exercise the raw connection.
1042
+ def verified!
1043
+ @verified = true
1044
+ end
1045
+
1046
+ def retryable_connection_error?(exception)
1047
+ exception.is_a?(ConnectionNotEstablished) || exception.is_a?(ConnectionFailed)
1048
+ end
1049
+
1050
+ def invalidate_transaction(exception)
1051
+ return unless exception.is_a?(TransactionRollbackError)
1052
+ return unless savepoint_errors_invalidate_transactions?
1053
+
1054
+ current_transaction.invalidate!
1055
+ end
1056
+
1057
+ def retryable_query_error?(exception)
1058
+ # We definitely can't retry if we were inside an invalidated transaction.
1059
+ return false if current_transaction.invalidated?
1060
+
1061
+ exception.is_a?(Deadlocked) || exception.is_a?(LockWaitTimeout)
1062
+ end
1063
+
1064
+ def backoff(counter)
1065
+ sleep 0.1 * counter
1066
+ end
1067
+
1068
+ def reconnect
1069
+ raise NotImplementedError
1070
+ end
1071
+
1072
+ # Returns a raw connection for internal use with methods that are known
1073
+ # to both be thread-safe and not rely upon actual server communication.
1074
+ # This is useful for e.g. string escaping methods.
1075
+ def any_raw_connection
1076
+ @raw_connection || valid_raw_connection
1077
+ end
1078
+
1079
+ # Similar to any_raw_connection, but ensures it is validated and
1080
+ # connected. Any method called on this result still needs to be
1081
+ # independently thread-safe, so it probably shouldn't talk to the
1082
+ # server... but some drivers fail if they know the connection has gone
1083
+ # away.
1084
+ def valid_raw_connection
1085
+ (@verified && @raw_connection) ||
1086
+ # `allow_retry: false`, to force verification: the block won't
1087
+ # raise, so a retry wouldn't help us get the valid connection we
1088
+ # need.
1089
+ with_raw_connection(allow_retry: false, materialize_transactions: false) { |conn| conn }
1090
+ end
1091
+
1092
+ def extended_type_map_key
1093
+ if @default_timezone
1094
+ { default_timezone: @default_timezone }
1095
+ end
1096
+ end
1097
+
741
1098
  def type_map
742
- TYPE_MAP
1099
+ if key = extended_type_map_key
1100
+ self.class::EXTENDED_TYPE_MAPS.compute_if_absent(key) do
1101
+ self.class.extended_type_map(**key)
1102
+ end
1103
+ else
1104
+ self.class::TYPE_MAP
1105
+ end
743
1106
  end
744
1107
 
745
1108
  def translate_exception_class(e, sql, binds)
@@ -761,16 +1124,18 @@ module ActiveRecord
761
1124
  type_casted_binds: type_casted_binds,
762
1125
  statement_name: statement_name,
763
1126
  async: async,
764
- connection: self) do
765
- @lock.synchronize(&block)
766
- rescue => e
767
- raise translate_exception_class(e, sql, binds)
768
- end
1127
+ connection: self,
1128
+ transaction: current_transaction.user_transaction.presence,
1129
+ row_count: 0,
1130
+ &block
1131
+ )
1132
+ rescue ActiveRecord::StatementInvalid => ex
1133
+ raise ex.set_query(sql, binds)
769
1134
  end
770
1135
 
771
1136
  def transform_query(sql)
772
1137
  ActiveRecord.query_transformers.each do |transformer|
773
- sql = transformer.call(sql)
1138
+ sql = transformer.call(sql, self)
774
1139
  end
775
1140
  sql
776
1141
  end
@@ -778,10 +1143,10 @@ module ActiveRecord
778
1143
  def translate_exception(exception, message:, sql:, binds:)
779
1144
  # override in derived class
780
1145
  case exception
781
- when RuntimeError
1146
+ when RuntimeError, ActiveRecord::ActiveRecordError
782
1147
  exception
783
1148
  else
784
- ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
1149
+ ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
785
1150
  end
786
1151
  end
787
1152
 
@@ -825,9 +1190,30 @@ module ActiveRecord
825
1190
  #
826
1191
  # This is an internal hook to make possible connection adapters to build
827
1192
  # custom result objects with connection-specific data.
828
- def build_result(columns:, rows:, column_types: {})
1193
+ def build_result(columns:, rows:, column_types: nil)
829
1194
  ActiveRecord::Result.new(columns, rows, column_types)
830
1195
  end
1196
+
1197
+ # Perform any necessary initialization upon the newly-established
1198
+ # @raw_connection -- this is the place to modify the adapter's
1199
+ # connection settings, run queries to configure any application-global
1200
+ # "session" variables, etc.
1201
+ #
1202
+ # Implementations may assume this method will only be called while
1203
+ # holding @lock (or from #initialize).
1204
+ def configure_connection
1205
+ check_version
1206
+ end
1207
+
1208
+ def default_prepared_statements
1209
+ true
1210
+ end
1211
+
1212
+ def warning_ignored?(warning)
1213
+ ActiveRecord.db_warnings_ignore.any? do |warning_matcher|
1214
+ warning.message.match?(warning_matcher) || warning.code.to_s.match?(warning_matcher)
1215
+ end
1216
+ end
831
1217
  end
832
1218
  end
833
1219
  end