activerecord 7.0.8.7 → 7.2.3

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