activerecord 7.0.0 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1607 -1040
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +17 -18
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +18 -3
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +17 -12
  15. data/lib/active_record/associations/collection_proxy.rb +22 -12
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +27 -17
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency.rb +20 -14
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  23. data/lib/active_record/associations/preloader.rb +13 -10
  24. data/lib/active_record/associations/singular_association.rb +1 -1
  25. data/lib/active_record/associations/through_association.rb +22 -11
  26. data/lib/active_record/associations.rb +345 -219
  27. data/lib/active_record/attribute_assignment.rb +0 -2
  28. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  29. data/lib/active_record/attribute_methods/dirty.rb +40 -26
  30. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  31. data/lib/active_record/attribute_methods/query.rb +28 -16
  32. data/lib/active_record/attribute_methods/read.rb +18 -5
  33. data/lib/active_record/attribute_methods/serialization.rb +172 -69
  34. data/lib/active_record/attribute_methods/write.rb +3 -3
  35. data/lib/active_record/attribute_methods.rb +110 -28
  36. data/lib/active_record/attributes.rb +3 -3
  37. data/lib/active_record/autosave_association.rb +56 -10
  38. data/lib/active_record/base.rb +10 -5
  39. data/lib/active_record/callbacks.rb +16 -32
  40. data/lib/active_record/coders/column_serializer.rb +61 -0
  41. data/lib/active_record/coders/json.rb +1 -1
  42. data/lib/active_record/coders/yaml_column.rb +70 -34
  43. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +164 -89
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  45. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  46. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  47. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  48. data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -32
  49. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  50. data/lib/active_record/connection_adapters/abstract/quoting.rb +52 -8
  51. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  52. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  53. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
  54. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  55. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +302 -129
  56. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  57. data/lib/active_record/connection_adapters/abstract_adapter.rb +504 -106
  58. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -104
  59. data/lib/active_record/connection_adapters/column.rb +9 -0
  60. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  61. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
  62. data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -12
  63. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  64. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  65. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  66. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
  67. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  69. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  70. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +3 -2
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -45
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +3 -1
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
  79. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  80. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  81. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +358 -57
  83. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +343 -181
  85. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  86. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  87. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +45 -39
  88. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +22 -5
  89. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +41 -22
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +242 -81
  92. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  93. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  94. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  95. data/lib/active_record/connection_adapters.rb +3 -1
  96. data/lib/active_record/connection_handling.rb +73 -96
  97. data/lib/active_record/core.rb +136 -148
  98. data/lib/active_record/counter_cache.rb +46 -25
  99. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -0
  100. data/lib/active_record/database_configurations/database_config.rb +9 -3
  101. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  102. data/lib/active_record/database_configurations/url_config.rb +17 -11
  103. data/lib/active_record/database_configurations.rb +87 -34
  104. data/lib/active_record/delegated_type.rb +9 -4
  105. data/lib/active_record/deprecator.rb +7 -0
  106. data/lib/active_record/destroy_association_async_job.rb +2 -0
  107. data/lib/active_record/disable_joins_association_relation.rb +1 -1
  108. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  109. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  110. data/lib/active_record/encryption/config.rb +25 -1
  111. data/lib/active_record/encryption/configurable.rb +13 -14
  112. data/lib/active_record/encryption/context.rb +10 -3
  113. data/lib/active_record/encryption/contexts.rb +8 -4
  114. data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
  115. data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
  116. data/lib/active_record/encryption/encryptable_record.rb +38 -22
  117. data/lib/active_record/encryption/encrypted_attribute_type.rb +19 -8
  118. data/lib/active_record/encryption/encryptor.rb +7 -7
  119. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
  120. data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -71
  121. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  122. data/lib/active_record/encryption/key_generator.rb +12 -1
  123. data/lib/active_record/encryption/message.rb +1 -1
  124. data/lib/active_record/encryption/message_serializer.rb +2 -0
  125. data/lib/active_record/encryption/properties.rb +4 -4
  126. data/lib/active_record/encryption/scheme.rb +20 -23
  127. data/lib/active_record/encryption.rb +1 -0
  128. data/lib/active_record/enum.rb +114 -27
  129. data/lib/active_record/errors.rb +108 -15
  130. data/lib/active_record/explain.rb +23 -3
  131. data/lib/active_record/explain_subscriber.rb +1 -1
  132. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  133. data/lib/active_record/fixture_set/render_context.rb +2 -0
  134. data/lib/active_record/fixture_set/table_row.rb +29 -8
  135. data/lib/active_record/fixtures.rb +121 -73
  136. data/lib/active_record/future_result.rb +30 -5
  137. data/lib/active_record/gem_version.rb +2 -2
  138. data/lib/active_record/inheritance.rb +30 -16
  139. data/lib/active_record/insert_all.rb +55 -8
  140. data/lib/active_record/integration.rb +10 -10
  141. data/lib/active_record/internal_metadata.rb +118 -30
  142. data/lib/active_record/locking/optimistic.rb +32 -18
  143. data/lib/active_record/locking/pessimistic.rb +8 -5
  144. data/lib/active_record/log_subscriber.rb +39 -17
  145. data/lib/active_record/marshalling.rb +56 -0
  146. data/lib/active_record/message_pack.rb +124 -0
  147. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  148. data/lib/active_record/middleware/database_selector.rb +18 -13
  149. data/lib/active_record/middleware/shard_selector.rb +7 -5
  150. data/lib/active_record/migration/command_recorder.rb +104 -9
  151. data/lib/active_record/migration/compatibility.rb +158 -64
  152. data/lib/active_record/migration/default_strategy.rb +23 -0
  153. data/lib/active_record/migration/execution_strategy.rb +19 -0
  154. data/lib/active_record/migration.rb +271 -117
  155. data/lib/active_record/model_schema.rb +82 -50
  156. data/lib/active_record/nested_attributes.rb +23 -3
  157. data/lib/active_record/normalization.rb +159 -0
  158. data/lib/active_record/persistence.rb +200 -47
  159. data/lib/active_record/promise.rb +84 -0
  160. data/lib/active_record/query_cache.rb +3 -21
  161. data/lib/active_record/query_logs.rb +87 -51
  162. data/lib/active_record/query_logs_formatter.rb +41 -0
  163. data/lib/active_record/querying.rb +16 -3
  164. data/lib/active_record/railtie.rb +127 -61
  165. data/lib/active_record/railties/controller_runtime.rb +12 -8
  166. data/lib/active_record/railties/databases.rake +142 -143
  167. data/lib/active_record/railties/job_runtime.rb +23 -0
  168. data/lib/active_record/readonly_attributes.rb +32 -5
  169. data/lib/active_record/reflection.rb +177 -45
  170. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  171. data/lib/active_record/relation/batches.rb +190 -61
  172. data/lib/active_record/relation/calculations.rb +200 -83
  173. data/lib/active_record/relation/delegation.rb +23 -9
  174. data/lib/active_record/relation/finder_methods.rb +77 -16
  175. data/lib/active_record/relation/merger.rb +2 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  177. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  179. data/lib/active_record/relation/predicate_builder.rb +26 -14
  180. data/lib/active_record/relation/query_attribute.rb +25 -1
  181. data/lib/active_record/relation/query_methods.rb +429 -76
  182. data/lib/active_record/relation/spawn_methods.rb +18 -1
  183. data/lib/active_record/relation.rb +98 -41
  184. data/lib/active_record/result.rb +25 -9
  185. data/lib/active_record/runtime_registry.rb +10 -1
  186. data/lib/active_record/sanitization.rb +57 -16
  187. data/lib/active_record/schema.rb +36 -22
  188. data/lib/active_record/schema_dumper.rb +65 -23
  189. data/lib/active_record/schema_migration.rb +68 -33
  190. data/lib/active_record/scoping/default.rb +20 -12
  191. data/lib/active_record/scoping/named.rb +2 -2
  192. data/lib/active_record/scoping.rb +2 -1
  193. data/lib/active_record/secure_password.rb +60 -0
  194. data/lib/active_record/secure_token.rb +21 -3
  195. data/lib/active_record/serialization.rb +5 -0
  196. data/lib/active_record/signed_id.rb +9 -7
  197. data/lib/active_record/store.rb +16 -11
  198. data/lib/active_record/suppressor.rb +3 -1
  199. data/lib/active_record/table_metadata.rb +16 -3
  200. data/lib/active_record/tasks/database_tasks.rb +138 -107
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
  203. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  204. data/lib/active_record/test_fixtures.rb +123 -99
  205. data/lib/active_record/timestamp.rb +26 -14
  206. data/lib/active_record/token_for.rb +113 -0
  207. data/lib/active_record/touch_later.rb +11 -6
  208. data/lib/active_record/transactions.rb +39 -13
  209. data/lib/active_record/translation.rb +1 -1
  210. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  211. data/lib/active_record/type/internal/timezone.rb +7 -2
  212. data/lib/active_record/type/serialized.rb +8 -4
  213. data/lib/active_record/type/time.rb +4 -0
  214. data/lib/active_record/validations/absence.rb +1 -1
  215. data/lib/active_record/validations/associated.rb +3 -3
  216. data/lib/active_record/validations/numericality.rb +5 -4
  217. data/lib/active_record/validations/presence.rb +5 -28
  218. data/lib/active_record/validations/uniqueness.rb +50 -5
  219. data/lib/active_record/validations.rb +8 -4
  220. data/lib/active_record/version.rb +1 -1
  221. data/lib/active_record.rb +143 -16
  222. data/lib/arel/errors.rb +10 -0
  223. data/lib/arel/factory_methods.rb +4 -0
  224. data/lib/arel/filter_predications.rb +1 -1
  225. data/lib/arel/nodes/and.rb +4 -0
  226. data/lib/arel/nodes/binary.rb +6 -1
  227. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  228. data/lib/arel/nodes/cte.rb +36 -0
  229. data/lib/arel/nodes/filter.rb +1 -1
  230. data/lib/arel/nodes/fragments.rb +35 -0
  231. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  232. data/lib/arel/nodes/leading_join.rb +8 -0
  233. data/lib/arel/nodes/node.rb +111 -2
  234. data/lib/arel/nodes/sql_literal.rb +6 -0
  235. data/lib/arel/nodes/table_alias.rb +4 -0
  236. data/lib/arel/nodes.rb +4 -0
  237. data/lib/arel/predications.rb +2 -0
  238. data/lib/arel/table.rb +9 -5
  239. data/lib/arel/visitors/mysql.rb +8 -1
  240. data/lib/arel/visitors/to_sql.rb +81 -17
  241. data/lib/arel/visitors/visitor.rb +2 -2
  242. data/lib/arel.rb +16 -2
  243. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  244. data/lib/rails/generators/active_record/migration.rb +3 -1
  245. data/lib/rails/generators/active_record/model/USAGE +113 -0
  246. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  247. metadata +50 -15
  248. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  249. data/lib/active_record/null_relation.rb +0 -63
@@ -21,28 +21,19 @@ require "active_record/connection_adapters/postgresql/utils"
21
21
 
22
22
  module ActiveRecord
23
23
  module ConnectionHandling # :nodoc:
24
+ def postgresql_adapter_class
25
+ ConnectionAdapters::PostgreSQLAdapter
26
+ end
27
+
24
28
  # Establishes a connection to the database that's used by all Active Record objects
25
29
  def postgresql_connection(config)
26
- conn_params = config.symbolize_keys.compact
27
-
28
- # Map ActiveRecords param names to PGs.
29
- conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
30
- conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
31
-
32
- # Forward only valid config params to PG::Connection.connect.
33
- valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
34
- conn_params.slice!(*valid_conn_param_keys)
35
-
36
- ConnectionAdapters::PostgreSQLAdapter.new(
37
- ConnectionAdapters::PostgreSQLAdapter.new_client(conn_params),
38
- logger,
39
- conn_params,
40
- config,
41
- )
30
+ postgresql_adapter_class.new(config)
42
31
  end
43
32
  end
44
33
 
45
34
  module ConnectionAdapters
35
+ # = Active Record PostgreSQL Adapter
36
+ #
46
37
  # The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
47
38
  #
48
39
  # Options:
@@ -75,18 +66,39 @@ module ActiveRecord
75
66
 
76
67
  class << self
77
68
  def new_client(conn_params)
78
- PG.connect(conn_params)
69
+ PG.connect(**conn_params)
79
70
  rescue ::PG::Error => error
80
- if conn_params && conn_params[:dbname] && error.message.include?(conn_params[:dbname])
71
+ if conn_params && conn_params[:dbname] == "postgres"
72
+ raise ActiveRecord::ConnectionNotEstablished, error.message
73
+ elsif conn_params && conn_params[:dbname] && error.message.include?(conn_params[:dbname])
81
74
  raise ActiveRecord::NoDatabaseError.db_error(conn_params[:dbname])
82
75
  elsif conn_params && conn_params[:user] && error.message.include?(conn_params[:user])
83
76
  raise ActiveRecord::DatabaseConnectionError.username_error(conn_params[:user])
84
- elsif conn_params && conn_params[:hostname] && error.message.include?(conn_params[:hostname])
85
- raise ActiveRecord::DatabaseConnectionError.hostname_error(conn_params[:hostname])
77
+ elsif conn_params && conn_params[:host] && error.message.include?(conn_params[:host])
78
+ raise ActiveRecord::DatabaseConnectionError.hostname_error(conn_params[:host])
86
79
  else
87
80
  raise ActiveRecord::ConnectionNotEstablished, error.message
88
81
  end
89
82
  end
83
+
84
+ def dbconsole(config, options = {})
85
+ pg_config = config.configuration_hash
86
+
87
+ ENV["PGUSER"] = pg_config[:username] if pg_config[:username]
88
+ ENV["PGHOST"] = pg_config[:host] if pg_config[:host]
89
+ ENV["PGPORT"] = pg_config[:port].to_s if pg_config[:port]
90
+ ENV["PGPASSWORD"] = pg_config[:password].to_s if pg_config[:password] && options[:include_password]
91
+ ENV["PGSSLMODE"] = pg_config[:sslmode].to_s if pg_config[:sslmode]
92
+ ENV["PGSSLCERT"] = pg_config[:sslcert].to_s if pg_config[:sslcert]
93
+ ENV["PGSSLKEY"] = pg_config[:sslkey].to_s if pg_config[:sslkey]
94
+ ENV["PGSSLROOTCERT"] = pg_config[:sslrootcert].to_s if pg_config[:sslrootcert]
95
+ if pg_config[:variables]
96
+ ENV["PGOPTIONS"] = pg_config[:variables].filter_map do |name, value|
97
+ "-c #{name}=#{value.to_s.gsub(/[ \\]/, '\\\\\0')}" unless value == ":default" || value == :default
98
+ end.join(" ")
99
+ end
100
+ find_cmd_and_exec("psql", config.database)
101
+ end
90
102
  end
91
103
 
92
104
  ##
@@ -104,8 +116,8 @@ module ActiveRecord
104
116
 
105
117
  ##
106
118
  # :singleton-method:
107
- # PostgreSQL supports multiple types for DateTimes. By default if you use `datetime`
108
- # in migrations, Rails will translate this to a PostgreSQL "timestamp without time zone".
119
+ # PostgreSQL supports multiple types for DateTimes. By default, if you use +datetime+
120
+ # in migrations, \Rails will translate this to a PostgreSQL "timestamp without time zone".
109
121
  # Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
110
122
  # store DateTimes as "timestamp with time zone":
111
123
  #
@@ -116,8 +128,8 @@ module ActiveRecord
116
128
  # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:my_custom_type] = { name: "my_custom_type_name" }
117
129
  # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :my_custom_type
118
130
  #
119
- # If you're using :ruby as your config.active_record.schema_format and you change this
120
- # setting, you should immediately run bin/rails db:migrate to update the types in your schema.rb.
131
+ # If you're using +:ruby+ as your +config.active_record.schema_format+ and you change this
132
+ # setting, you should immediately run <tt>bin/rails db:migrate</tt> to update the types in your schema.rb.
121
133
  class_attribute :datetime_type, default: :timestamp
122
134
 
123
135
  NATIVE_DATABASE_TYPES = {
@@ -183,13 +195,17 @@ module ActiveRecord
183
195
  end
184
196
 
185
197
  def supports_partitioned_indexes?
186
- database_version >= 110_000 # >= 11.0
198
+ database_version >= 11_00_00 # >= 11.0
187
199
  end
188
200
 
189
201
  def supports_partial_index?
190
202
  true
191
203
  end
192
204
 
205
+ def supports_index_include?
206
+ database_version >= 11_00_00 # >= 11.0
207
+ end
208
+
193
209
  def supports_expression_index?
194
210
  true
195
211
  end
@@ -206,6 +222,14 @@ module ActiveRecord
206
222
  true
207
223
  end
208
224
 
225
+ def supports_exclusion_constraints?
226
+ true
227
+ end
228
+
229
+ def supports_unique_constraints?
230
+ true
231
+ end
232
+
209
233
  def supports_validate_constraints?
210
234
  true
211
235
  end
@@ -234,25 +258,37 @@ module ActiveRecord
234
258
  true
235
259
  end
236
260
 
261
+ def supports_restart_db_transaction?
262
+ database_version >= 12_00_00 # >= 12.0
263
+ end
264
+
237
265
  def supports_insert_returning?
238
266
  true
239
267
  end
240
268
 
241
269
  def supports_insert_on_conflict?
242
- database_version >= 90500 # >= 9.5
270
+ database_version >= 9_05_00 # >= 9.5
243
271
  end
244
272
  alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
245
273
  alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
246
274
  alias supports_insert_conflict_target? supports_insert_on_conflict?
247
275
 
248
276
  def supports_virtual_columns?
249
- database_version >= 120_000 # >= 12.0
277
+ database_version >= 12_00_00 # >= 12.0
278
+ end
279
+
280
+ def supports_nulls_not_distinct?
281
+ database_version >= 15_00_00 # >= 15.0
250
282
  end
251
283
 
252
284
  def index_algorithms
253
285
  { concurrently: "CONCURRENTLY" }
254
286
  end
255
287
 
288
+ def return_value_after_insert?(column) # :nodoc:
289
+ column.auto_populated?
290
+ end
291
+
256
292
  class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
257
293
  def initialize(connection, max)
258
294
  super(max)
@@ -266,47 +302,46 @@ module ActiveRecord
266
302
 
267
303
  private
268
304
  def dealloc(key)
269
- @connection.query "DEALLOCATE #{key}" if connection_active?
270
- rescue PG::Error
271
- end
272
-
273
- def connection_active?
274
- @connection.status == PG::CONNECTION_OK
305
+ # This is ugly, but safe: the statement pool is only
306
+ # accessed while holding the connection's lock. (And we
307
+ # don't need the complication of with_raw_connection because
308
+ # a reconnect would invalidate the entire statement pool.)
309
+ if conn = @connection.instance_variable_get(:@raw_connection)
310
+ conn.query "DEALLOCATE #{key}" if conn.status == PG::CONNECTION_OK
311
+ end
275
312
  rescue PG::Error
276
- false
277
313
  end
278
314
  end
279
315
 
280
316
  # Initializes and connects a PostgreSQL adapter.
281
- def initialize(connection, logger, connection_parameters, config)
282
- super(connection, logger, config)
317
+ def initialize(...)
318
+ super
283
319
 
284
- @connection_parameters = connection_parameters
320
+ conn_params = @config.compact
285
321
 
286
- # @local_tz is initialized as nil to avoid warnings when connect tries to use it
287
- @local_tz = nil
288
- @max_identifier_length = nil
322
+ # Map ActiveRecords param names to PGs.
323
+ conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
324
+ conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
289
325
 
290
- configure_connection
291
- add_pg_encoders
292
- add_pg_decoders
326
+ # Forward only valid config params to PG::Connection.connect.
327
+ valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
328
+ conn_params.slice!(*valid_conn_param_keys)
293
329
 
294
- @type_map = Type::HashLookupTypeMap.new
295
- initialize_type_map
296
- @local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
297
- @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
298
- end
330
+ @connection_parameters = conn_params
299
331
 
300
- def self.database_exists?(config)
301
- !!ActiveRecord::Base.postgresql_connection(config)
302
- rescue ActiveRecord::NoDatabaseError
303
- false
332
+ @max_identifier_length = nil
333
+ @type_map = nil
334
+ @raw_connection = nil
335
+ @notice_receiver_sql_warnings = []
336
+
337
+ @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
304
338
  end
305
339
 
306
340
  # Is this connection alive and ready for queries?
307
341
  def active?
308
342
  @lock.synchronize do
309
- @connection.query ";"
343
+ return false unless @raw_connection
344
+ @raw_connection.query ";"
310
345
  end
311
346
  true
312
347
  rescue PG::Error
@@ -314,31 +349,27 @@ module ActiveRecord
314
349
  end
315
350
 
316
351
  def reload_type_map # :nodoc:
317
- type_map.clear
318
- initialize_type_map
319
- end
320
-
321
- # Close then reopen the connection.
322
- def reconnect!
323
352
  @lock.synchronize do
324
- super
325
- @connection.reset
326
- configure_connection
327
- reload_type_map
328
- rescue PG::ConnectionBad
329
- connect
353
+ if @type_map
354
+ type_map.clear
355
+ else
356
+ @type_map = Type::HashLookupTypeMap.new
357
+ end
358
+
359
+ initialize_type_map
330
360
  end
331
361
  end
332
362
 
333
363
  def reset!
334
364
  @lock.synchronize do
335
- clear_cache!
336
- reset_transaction
337
- unless @connection.transaction_status == ::PG::PQTRANS_IDLE
338
- @connection.query "ROLLBACK"
365
+ return connect! unless @raw_connection
366
+
367
+ unless @raw_connection.transaction_status == ::PG::PQTRANS_IDLE
368
+ @raw_connection.query "ROLLBACK"
339
369
  end
340
- @connection.query "DISCARD ALL"
341
- configure_connection
370
+ @raw_connection.query "DISCARD ALL"
371
+
372
+ super
342
373
  end
343
374
  end
344
375
 
@@ -347,14 +378,15 @@ module ActiveRecord
347
378
  def disconnect!
348
379
  @lock.synchronize do
349
380
  super
350
- @connection.close rescue nil
381
+ @raw_connection&.close rescue nil
382
+ @raw_connection = nil
351
383
  end
352
384
  end
353
385
 
354
386
  def discard! # :nodoc:
355
387
  super
356
- @connection.socket_io.reopen(IO::NULL) rescue nil
357
- @connection = nil
388
+ @raw_connection&.socket_io&.reopen(IO::NULL) rescue nil
389
+ @raw_connection = nil
358
390
  end
359
391
 
360
392
  def native_database_types # :nodoc:
@@ -370,7 +402,7 @@ module ActiveRecord
370
402
  end
371
403
 
372
404
  def set_standard_conforming_strings
373
- execute("SET standard_conforming_strings = on", "SCHEMA")
405
+ internal_execute("SET standard_conforming_strings = on")
374
406
  end
375
407
 
376
408
  def supports_ddl_transactions?
@@ -398,7 +430,7 @@ module ActiveRecord
398
430
  end
399
431
 
400
432
  def supports_pgcrypto_uuid?
401
- database_version >= 90400 # >= 9.4
433
+ database_version >= 9_04_00 # >= 9.4
402
434
  end
403
435
 
404
436
  def supports_optimizer_hints?
@@ -430,14 +462,21 @@ module ActiveRecord
430
462
  query_value("SELECT pg_advisory_unlock(#{lock_id})")
431
463
  end
432
464
 
433
- def enable_extension(name)
434
- exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
435
- reload_type_map
436
- }
465
+ def enable_extension(name, **)
466
+ schema, name = name.to_s.split(".").values_at(-2, -1)
467
+ sql = +"CREATE EXTENSION IF NOT EXISTS \"#{name}\""
468
+ sql << " SCHEMA #{schema}" if schema
469
+
470
+ internal_exec_query(sql).tap { reload_type_map }
437
471
  end
438
472
 
439
- def disable_extension(name)
440
- exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
473
+ # Removes an extension from the database.
474
+ #
475
+ # [<tt>:force</tt>]
476
+ # Set to +:cascade+ to drop dependent objects as well.
477
+ # Defaults to false.
478
+ def disable_extension(name, force: false)
479
+ internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
441
480
  reload_type_map
442
481
  }
443
482
  end
@@ -451,7 +490,7 @@ module ActiveRecord
451
490
  end
452
491
 
453
492
  def extensions
454
- exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
493
+ internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
455
494
  end
456
495
 
457
496
  # Returns a list of defined enum types, and their values.
@@ -459,31 +498,97 @@ module ActiveRecord
459
498
  query = <<~SQL
460
499
  SELECT
461
500
  type.typname AS name,
501
+ type.OID AS oid,
502
+ n.nspname AS schema,
462
503
  string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
463
504
  FROM pg_enum AS enum
464
- JOIN pg_type AS type
465
- ON (type.oid = enum.enumtypid)
466
- GROUP BY type.typname;
505
+ JOIN pg_type AS type ON (type.oid = enum.enumtypid)
506
+ JOIN pg_namespace n ON type.typnamespace = n.oid
507
+ WHERE n.nspname = ANY (current_schemas(false))
508
+ GROUP BY type.OID, n.nspname, type.typname;
467
509
  SQL
468
- exec_query(query, "SCHEMA").cast_values
510
+
511
+ internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.each_with_object({}) do |row, memo|
512
+ name, schema = row[0], row[2]
513
+ schema = nil if schema == current_schema
514
+ full_name = [schema, name].compact.join(".")
515
+ memo[full_name] = row.last
516
+ end.to_a
469
517
  end
470
518
 
471
519
  # Given a name and an array of values, creates an enum type.
472
- def create_enum(name, values)
473
- sql_values = values.map { |s| "'#{s}'" }.join(", ")
520
+ def create_enum(name, values, **options)
521
+ sql_values = values.map { |s| quote(s) }.join(", ")
522
+ scope = quoted_scope(name)
474
523
  query = <<~SQL
475
524
  DO $$
476
525
  BEGIN
477
526
  IF NOT EXISTS (
478
- SELECT 1 FROM pg_type t
479
- WHERE t.typname = '#{name}'
527
+ SELECT 1
528
+ FROM pg_type t
529
+ JOIN pg_namespace n ON t.typnamespace = n.oid
530
+ WHERE t.typname = #{scope[:name]}
531
+ AND n.nspname = #{scope[:schema]}
480
532
  ) THEN
481
- CREATE TYPE \"#{name}\" AS ENUM (#{sql_values});
533
+ CREATE TYPE #{quote_table_name(name)} AS ENUM (#{sql_values});
482
534
  END IF;
483
535
  END
484
536
  $$;
485
537
  SQL
486
- exec_query(query)
538
+ internal_exec_query(query)
539
+ end
540
+
541
+ # Drops an enum type.
542
+ #
543
+ # If the <tt>if_exists: true</tt> option is provided, the enum is dropped
544
+ # only if it exists. Otherwise, if the enum doesn't exist, an error is
545
+ # raised.
546
+ #
547
+ # The +values+ parameter will be ignored if present. It can be helpful
548
+ # to provide this in a migration's +change+ method so it can be reverted.
549
+ # In that case, +values+ will be used by #create_enum.
550
+ def drop_enum(name, values = nil, **options)
551
+ query = <<~SQL
552
+ DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
553
+ SQL
554
+ internal_exec_query(query)
555
+ end
556
+
557
+ # Rename an existing enum type to something else.
558
+ def rename_enum(name, options = {})
559
+ to = options.fetch(:to) { raise ArgumentError, ":to is required" }
560
+
561
+ exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
562
+ end
563
+
564
+ # Add enum value to an existing enum type.
565
+ def add_enum_value(type_name, value, options = {})
566
+ before, after = options.values_at(:before, :after)
567
+ sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE '#{value}'"
568
+
569
+ if before && after
570
+ raise ArgumentError, "Cannot have both :before and :after at the same time"
571
+ elsif before
572
+ sql << " BEFORE '#{before}'"
573
+ elsif after
574
+ sql << " AFTER '#{after}'"
575
+ end
576
+
577
+ execute(sql).tap { reload_type_map }
578
+ end
579
+
580
+ # Rename enum value on an existing enum type.
581
+ def rename_enum_value(type_name, options = {})
582
+ unless database_version >= 10_00_00 # >= 10.0
583
+ raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
584
+ end
585
+
586
+ from = options.fetch(:from) { raise ArgumentError, ":from is required" }
587
+ to = options.fetch(:to) { raise ArgumentError, ":to is required" }
588
+
589
+ execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE '#{from}' TO '#{to}'").tap {
590
+ reload_type_map
591
+ }
487
592
  end
488
593
 
489
594
  # Returns the configured supported identifier length supported by PostgreSQL
@@ -491,10 +596,18 @@ module ActiveRecord
491
596
  @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
492
597
  end
493
598
 
599
+ # Returns the maximum length of a table name.
600
+ def table_name_length
601
+ # PostgreSQL automatically creates an index for PRIMARY KEY with name consisting of
602
+ # truncated table name and "_pkey" suffix fitting into max_identifier_length number of characters.
603
+ # We allow smaller table names to be able to correctly rename this index when renaming the table.
604
+ max_identifier_length - "_pkey".length
605
+ end
606
+
494
607
  # Set the authorized user for this session
495
608
  def session_auth=(user)
496
609
  clear_cache!
497
- execute("SET SESSION AUTHORIZATION #{user}")
610
+ internal_execute("SET SESSION AUTHORIZATION #{user}", nil, materialize_transactions: true)
498
611
  end
499
612
 
500
613
  def use_insert_returning?
@@ -503,7 +616,7 @@ module ActiveRecord
503
616
 
504
617
  # Returns the version of the connected PostgreSQL server.
505
618
  def get_database_version # :nodoc:
506
- @connection.server_version
619
+ valid_raw_connection.server_version
507
620
  end
508
621
  alias :postgresql_version :database_version
509
622
 
@@ -531,7 +644,7 @@ module ActiveRecord
531
644
  end
532
645
 
533
646
  def check_version # :nodoc:
534
- if database_version < 90300 # < 9.3
647
+ if database_version < 9_03_00 # < 9.3
535
648
  raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
536
649
  end
537
650
  end
@@ -575,10 +688,6 @@ module ActiveRecord
575
688
  m.register_type "polygon", OID::SpecializedString.new(:polygon)
576
689
  m.register_type "circle", OID::SpecializedString.new(:circle)
577
690
 
578
- register_class_with_precision m, "time", Type::Time
579
- register_class_with_precision m, "timestamp", OID::Timestamp
580
- register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone
581
-
582
691
  m.register_type "numeric" do |_, fmod, sql_type|
583
692
  precision = extract_precision(sql_type)
584
693
  scale = extract_scale(sql_type)
@@ -607,12 +716,15 @@ module ActiveRecord
607
716
  end
608
717
 
609
718
  private
610
- def type_map
611
- @type_map ||= Type::HashLookupTypeMap.new
612
- end
719
+ attr_reader :type_map
613
720
 
614
721
  def initialize_type_map(m = type_map)
615
722
  self.class.initialize_type_map(m)
723
+
724
+ self.class.register_class_with_precision m, "time", Type::Time, timezone: @default_timezone
725
+ self.class.register_class_with_precision m, "timestamp", OID::Timestamp, timezone: @default_timezone
726
+ self.class.register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone
727
+
616
728
  load_additional_types
617
729
  end
618
730
 
@@ -669,35 +781,53 @@ module ActiveRecord
669
781
  case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
670
782
  when nil
671
783
  if exception.message.match?(/connection is closed/i)
672
- ConnectionNotEstablished.new(exception)
784
+ ConnectionNotEstablished.new(exception, connection_pool: @pool)
785
+ elsif exception.is_a?(PG::ConnectionBad)
786
+ # libpq message style always ends with a newline; the pg gem's internal
787
+ # errors do not. We separate these cases because a pg-internal
788
+ # ConnectionBad means it failed before it managed to send the query,
789
+ # whereas a libpq failure could have occurred at any time (meaning the
790
+ # server may have already executed part or all of the query).
791
+ if exception.message.end_with?("\n")
792
+ ConnectionFailed.new(exception, connection_pool: @pool)
793
+ else
794
+ ConnectionNotEstablished.new(exception, connection_pool: @pool)
795
+ end
673
796
  else
674
797
  super
675
798
  end
676
799
  when UNIQUE_VIOLATION
677
- RecordNotUnique.new(message, sql: sql, binds: binds)
800
+ RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
678
801
  when FOREIGN_KEY_VIOLATION
679
- InvalidForeignKey.new(message, sql: sql, binds: binds)
802
+ InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
680
803
  when VALUE_LIMIT_VIOLATION
681
- ValueTooLong.new(message, sql: sql, binds: binds)
804
+ ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
682
805
  when NUMERIC_VALUE_OUT_OF_RANGE
683
- RangeError.new(message, sql: sql, binds: binds)
806
+ RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
684
807
  when NOT_NULL_VIOLATION
685
- NotNullViolation.new(message, sql: sql, binds: binds)
808
+ NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
686
809
  when SERIALIZATION_FAILURE
687
- SerializationFailure.new(message, sql: sql, binds: binds)
810
+ SerializationFailure.new(message, sql: sql, binds: binds, connection_pool: @pool)
688
811
  when DEADLOCK_DETECTED
689
- Deadlocked.new(message, sql: sql, binds: binds)
812
+ Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
690
813
  when DUPLICATE_DATABASE
691
- DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
814
+ DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
692
815
  when LOCK_NOT_AVAILABLE
693
- LockWaitTimeout.new(message, sql: sql, binds: binds)
816
+ LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
694
817
  when QUERY_CANCELED
695
- QueryCanceled.new(message, sql: sql, binds: binds)
818
+ QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
696
819
  else
697
820
  super
698
821
  end
699
822
  end
700
823
 
824
+ def retryable_query_error?(exception)
825
+ # We cannot retry anything if we're inside a broken transaction; we need to at
826
+ # least raise until the innermost savepoint is rolled back
827
+ @raw_connection&.transaction_status != ::PG::PQTRANS_INERROR &&
828
+ super
829
+ end
830
+
701
831
  def get_oid_type(oid, fmod, column_name, sql_type = "")
702
832
  if !type_map.key?(oid)
703
833
  load_additional_types([oid])
@@ -714,7 +844,7 @@ module ActiveRecord
714
844
  def load_additional_types(oids = nil)
715
845
  initializer = OID::TypeMapInitializer.new(type_map)
716
846
  load_types_queries(initializer, oids) do |query|
717
- execute_and_clear(query, "SCHEMA", []) do |records|
847
+ execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |records|
718
848
  initializer.run(records)
719
849
  end
720
850
  end
@@ -737,14 +867,14 @@ module ActiveRecord
737
867
 
738
868
  FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
739
869
 
740
- def execute_and_clear(sql, name, binds, prepare: false, async: false)
870
+ def execute_and_clear(sql, name, binds, prepare: false, async: false, allow_retry: false, materialize_transactions: true)
741
871
  sql = transform_query(sql)
742
872
  check_if_write_query(sql)
743
873
 
744
874
  if !prepare || without_prepared_statement?(binds)
745
- result = exec_no_cache(sql, name, binds, async: async)
875
+ result = exec_no_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
746
876
  else
747
- result = exec_cache(sql, name, binds, async: async)
877
+ result = exec_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
748
878
  end
749
879
  begin
750
880
  ret = yield result
@@ -754,8 +884,7 @@ module ActiveRecord
754
884
  ret
755
885
  end
756
886
 
757
- def exec_no_cache(sql, name, binds, async: false)
758
- materialize_transactions
887
+ def exec_no_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
759
888
  mark_transaction_written_if_write(sql)
760
889
 
761
890
  # make sure we carry over any changes to ActiveRecord.default_timezone that have been
@@ -764,23 +893,23 @@ module ActiveRecord
764
893
 
765
894
  type_casted_binds = type_casted_binds(binds)
766
895
  log(sql, name, binds, type_casted_binds, async: async) do
767
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
768
- @connection.exec_params(sql, type_casted_binds)
896
+ with_raw_connection do |conn|
897
+ conn.exec_params(sql, type_casted_binds)
769
898
  end
770
899
  end
771
900
  end
772
901
 
773
- def exec_cache(sql, name, binds, async: false)
774
- materialize_transactions
902
+ def exec_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
775
903
  mark_transaction_written_if_write(sql)
904
+
776
905
  update_typemap_for_default_timezone
777
906
 
778
907
  stmt_key = prepare_statement(sql, binds)
779
908
  type_casted_binds = type_casted_binds(binds)
780
909
 
781
- log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
782
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
783
- @connection.exec_prepared(stmt_key, type_casted_binds)
910
+ with_raw_connection do |conn|
911
+ log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
912
+ conn.exec_prepared(stmt_key, type_casted_binds)
784
913
  end
785
914
  end
786
915
  rescue ActiveRecord::StatementInvalid => e
@@ -829,17 +958,17 @@ module ActiveRecord
829
958
  # Prepare the statement if it hasn't been prepared, return
830
959
  # the statement key.
831
960
  def prepare_statement(sql, binds)
832
- @lock.synchronize do
961
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
833
962
  sql_key = sql_key(sql)
834
963
  unless @statements.key? sql_key
835
964
  nextkey = @statements.next_key
836
965
  begin
837
- @connection.prepare nextkey, sql
966
+ conn.prepare nextkey, sql
838
967
  rescue => e
839
968
  raise translate_exception_class(e, sql, binds)
840
969
  end
841
970
  # Clear the queue
842
- @connection.get_last_result
971
+ conn.get_last_result
843
972
  @statements[sql_key] = nextkey
844
973
  end
845
974
  @statements[sql_key]
@@ -849,49 +978,79 @@ module ActiveRecord
849
978
  # Connects to a PostgreSQL server and sets up the adapter depending on the
850
979
  # connected server's characteristics.
851
980
  def connect
852
- @connection = self.class.new_client(@connection_parameters)
853
- configure_connection
854
- add_pg_encoders
855
- add_pg_decoders
981
+ @raw_connection = self.class.new_client(@connection_parameters)
982
+ rescue ConnectionNotEstablished => ex
983
+ raise ex.set_pool(@pool)
984
+ end
985
+
986
+ def reconnect
987
+ begin
988
+ @raw_connection&.reset
989
+ rescue PG::ConnectionBad
990
+ @raw_connection = nil
991
+ end
992
+
993
+ connect unless @raw_connection
856
994
  end
857
995
 
858
996
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
859
997
  # This is called by #connect and should not be called manually.
860
998
  def configure_connection
861
999
  if @config[:encoding]
862
- @connection.set_client_encoding(@config[:encoding])
1000
+ @raw_connection.set_client_encoding(@config[:encoding])
863
1001
  end
864
1002
  self.client_min_messages = @config[:min_messages] || "warning"
865
1003
  self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
866
1004
 
1005
+ unless ActiveRecord.db_warnings_action.nil?
1006
+ @raw_connection.set_notice_receiver do |result|
1007
+ message = result.error_field(PG::Result::PG_DIAG_MESSAGE_PRIMARY)
1008
+ code = result.error_field(PG::Result::PG_DIAG_SQLSTATE)
1009
+ level = result.error_field(PG::Result::PG_DIAG_SEVERITY)
1010
+ @notice_receiver_sql_warnings << SQLWarning.new(message, code, level, nil, @pool)
1011
+ end
1012
+ end
1013
+
867
1014
  # Use standard-conforming strings so we don't have to do the E'...' dance.
868
1015
  set_standard_conforming_strings
869
1016
 
870
1017
  variables = @config.fetch(:variables, {}).stringify_keys
871
1018
 
872
- # If using Active Record's time zone support configure the connection to return
873
- # TIMESTAMP WITH ZONE types in UTC.
874
- unless variables["timezone"]
875
- if ActiveRecord.default_timezone == :utc
876
- variables["timezone"] = "UTC"
877
- elsif @local_tz
878
- variables["timezone"] = @local_tz
879
- end
880
- end
881
-
882
1019
  # Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
883
- execute("SET intervalstyle = iso_8601", "SCHEMA")
1020
+ internal_execute("SET intervalstyle = iso_8601")
884
1021
 
885
1022
  # SET statements from :variables config hash
886
1023
  # https://www.postgresql.org/docs/current/static/sql-set.html
887
1024
  variables.map do |k, v|
888
1025
  if v == ":default" || v == :default
889
1026
  # Sets the value to the global or compile default
890
- execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
1027
+ internal_execute("SET SESSION #{k} TO DEFAULT")
891
1028
  elsif !v.nil?
892
- execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
1029
+ internal_execute("SET SESSION #{k} TO #{quote(v)}")
893
1030
  end
894
1031
  end
1032
+
1033
+ add_pg_encoders
1034
+ add_pg_decoders
1035
+
1036
+ reload_type_map
1037
+ end
1038
+
1039
+ def reconfigure_connection_timezone
1040
+ variables = @config.fetch(:variables, {}).stringify_keys
1041
+
1042
+ # If it's been directly configured as a connection variable, we don't
1043
+ # need to do anything here; it will be set up by configure_connection
1044
+ # and then never changed.
1045
+ return if variables["timezone"]
1046
+
1047
+ # If using Active Record's time zone support configure the connection
1048
+ # to return TIMESTAMP WITH ZONE types in UTC.
1049
+ if default_timezone == :utc
1050
+ internal_execute("SET SESSION timezone TO 'UTC'")
1051
+ else
1052
+ internal_execute("SET SESSION timezone TO DEFAULT")
1053
+ end
895
1054
  end
896
1055
 
897
1056
  # Returns the list of a table's column names, data types, and default values.
@@ -928,37 +1087,37 @@ module ActiveRecord
928
1087
  SQL
929
1088
  end
930
1089
 
931
- def extract_table_ref_from_insert_sql(sql)
932
- sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
933
- $1.strip if $1
934
- end
935
-
936
1090
  def arel_visitor
937
1091
  Arel::Visitors::PostgreSQL.new(self)
938
1092
  end
939
1093
 
940
1094
  def build_statement_pool
941
- StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
1095
+ StatementPool.new(self, self.class.type_cast_config_to_integer(@config[:statement_limit]))
942
1096
  end
943
1097
 
944
1098
  def can_perform_case_insensitive_comparison_for?(column)
945
- @case_insensitive_cache ||= {}
946
- @case_insensitive_cache[column.sql_type] ||= begin
947
- sql = <<~SQL
948
- SELECT exists(
949
- SELECT * FROM pg_proc
950
- WHERE proname = 'lower'
951
- AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
952
- ) OR exists(
953
- SELECT * FROM pg_proc
954
- INNER JOIN pg_cast
955
- ON ARRAY[casttarget]::oidvector = proargtypes
956
- WHERE proname = 'lower'
957
- AND castsource = #{quote column.sql_type}::regtype
958
- )
959
- SQL
960
- execute_and_clear(sql, "SCHEMA", []) do |result|
961
- result.getvalue(0, 0)
1099
+ # NOTE: citext is an exception. It is possible to perform a
1100
+ # case-insensitive comparison using `LOWER()`, but it is
1101
+ # unnecessary, as `citext` is case-insensitive by definition.
1102
+ @case_insensitive_cache ||= { "citext" => false }
1103
+ @case_insensitive_cache.fetch(column.sql_type) do
1104
+ @case_insensitive_cache[column.sql_type] = begin
1105
+ sql = <<~SQL
1106
+ SELECT exists(
1107
+ SELECT * FROM pg_proc
1108
+ WHERE proname = 'lower'
1109
+ AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
1110
+ ) OR exists(
1111
+ SELECT * FROM pg_proc
1112
+ INNER JOIN pg_cast
1113
+ ON ARRAY[casttarget]::oidvector = proargtypes
1114
+ WHERE proname = 'lower'
1115
+ AND castsource = #{quote column.sql_type}::regtype
1116
+ )
1117
+ SQL
1118
+ execute_and_clear(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
1119
+ result.getvalue(0, 0)
1120
+ end
962
1121
  end
963
1122
  end
964
1123
  end
@@ -968,28 +1127,30 @@ module ActiveRecord
968
1127
  map[Integer] = PG::TextEncoder::Integer.new
969
1128
  map[TrueClass] = PG::TextEncoder::Boolean.new
970
1129
  map[FalseClass] = PG::TextEncoder::Boolean.new
971
- @connection.type_map_for_queries = map
1130
+ @raw_connection.type_map_for_queries = map
972
1131
  end
973
1132
 
974
1133
  def update_typemap_for_default_timezone
975
- if @default_timezone != ActiveRecord.default_timezone && @timestamp_decoder
976
- decoder_class = ActiveRecord.default_timezone == :utc ?
1134
+ if @raw_connection && @mapped_default_timezone != default_timezone && @timestamp_decoder
1135
+ decoder_class = default_timezone == :utc ?
977
1136
  PG::TextDecoder::TimestampUtc :
978
1137
  PG::TextDecoder::TimestampWithoutTimeZone
979
1138
 
980
- @timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
981
- @connection.type_map_for_results.add_coder(@timestamp_decoder)
1139
+ @timestamp_decoder = decoder_class.new(**@timestamp_decoder.to_h)
1140
+ @raw_connection.type_map_for_results.add_coder(@timestamp_decoder)
982
1141
 
983
- @default_timezone = ActiveRecord.default_timezone
1142
+ @mapped_default_timezone = default_timezone
984
1143
 
985
1144
  # if default timezone has changed, we need to reconfigure the connection
986
1145
  # (specifically, the session time zone)
987
- configure_connection
1146
+ reconfigure_connection_timezone
1147
+
1148
+ true
988
1149
  end
989
1150
  end
990
1151
 
991
1152
  def add_pg_decoders
992
- @default_timezone = nil
1153
+ @mapped_default_timezone = nil
993
1154
  @timestamp_decoder = nil
994
1155
 
995
1156
  coders_by_name = {
@@ -1011,13 +1172,13 @@ module ActiveRecord
1011
1172
  FROM pg_type as t
1012
1173
  WHERE t.typname IN (%s)
1013
1174
  SQL
1014
- coders = execute_and_clear(query, "SCHEMA", []) do |result|
1175
+ coders = execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
1015
1176
  result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
1016
1177
  end
1017
1178
 
1018
1179
  map = PG::TypeMapByOid.new
1019
1180
  coders.each { |coder| map.add_coder(coder) }
1020
- @connection.type_map_for_results = map
1181
+ @raw_connection.type_map_for_results = map
1021
1182
 
1022
1183
  @type_map_for_results = PG::TypeMapByOid.new
1023
1184
  @type_map_for_results.default_type_map = map
@@ -1063,5 +1224,6 @@ module ActiveRecord
1063
1224
  ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
1064
1225
  ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
1065
1226
  end
1227
+ ActiveSupport.run_load_hooks(:active_record_postgresqladapter, PostgreSQLAdapter)
1066
1228
  end
1067
1229
  end