activerecord 7.0.6 → 7.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (231) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1424 -1390
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +15 -16
  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 +16 -10
  15. data/lib/active_record/associations/collection_proxy.rb +20 -10
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +20 -13
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -7
  20. data/lib/active_record/associations/join_dependency.rb +10 -8
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader.rb +13 -10
  23. data/lib/active_record/associations/singular_association.rb +6 -8
  24. data/lib/active_record/associations/through_association.rb +22 -11
  25. data/lib/active_record/associations.rb +295 -199
  26. data/lib/active_record/attribute_assignment.rb +0 -2
  27. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +40 -26
  29. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  30. data/lib/active_record/attribute_methods/query.rb +28 -16
  31. data/lib/active_record/attribute_methods/read.rb +18 -5
  32. data/lib/active_record/attribute_methods/serialization.rb +150 -31
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +105 -21
  35. data/lib/active_record/attributes.rb +3 -3
  36. data/lib/active_record/autosave_association.rb +60 -18
  37. data/lib/active_record/base.rb +7 -2
  38. data/lib/active_record/callbacks.rb +10 -24
  39. data/lib/active_record/coders/column_serializer.rb +61 -0
  40. data/lib/active_record/coders/json.rb +1 -1
  41. data/lib/active_record/coders/yaml_column.rb +70 -42
  42. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  45. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -32
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  49. data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
  50. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  51. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  52. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  53. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -124
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +496 -102
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +214 -113
  57. data/lib/active_record/connection_adapters/column.rb +9 -0
  58. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  59. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
  60. data/lib/active_record/connection_adapters/mysql/quoting.rb +21 -14
  61. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  62. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  63. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +18 -13
  65. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  66. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  67. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  68. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  69. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  70. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +71 -40
  71. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  72. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +15 -8
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  75. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  76. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  77. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  78. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +349 -55
  79. data/lib/active_record/connection_adapters/postgresql_adapter.rb +338 -176
  80. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  81. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  82. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +45 -39
  83. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +9 -5
  84. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  85. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -9
  86. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +210 -83
  87. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  88. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  89. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  90. data/lib/active_record/connection_adapters.rb +3 -1
  91. data/lib/active_record/connection_handling.rb +71 -94
  92. data/lib/active_record/core.rb +136 -148
  93. data/lib/active_record/counter_cache.rb +46 -25
  94. data/lib/active_record/database_configurations/database_config.rb +9 -3
  95. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  96. data/lib/active_record/database_configurations/url_config.rb +17 -11
  97. data/lib/active_record/database_configurations.rb +86 -33
  98. data/lib/active_record/delegated_type.rb +8 -3
  99. data/lib/active_record/deprecator.rb +7 -0
  100. data/lib/active_record/destroy_association_async_job.rb +2 -0
  101. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  102. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  103. data/lib/active_record/encryption/config.rb +25 -1
  104. data/lib/active_record/encryption/configurable.rb +12 -19
  105. data/lib/active_record/encryption/context.rb +10 -3
  106. data/lib/active_record/encryption/contexts.rb +5 -1
  107. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  108. data/lib/active_record/encryption/encryptable_record.rb +36 -18
  109. data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
  110. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
  111. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  112. data/lib/active_record/encryption/key_generator.rb +12 -1
  113. data/lib/active_record/encryption/message_serializer.rb +2 -0
  114. data/lib/active_record/encryption/properties.rb +3 -3
  115. data/lib/active_record/encryption/scheme.rb +19 -22
  116. data/lib/active_record/encryption.rb +1 -0
  117. data/lib/active_record/enum.rb +113 -26
  118. data/lib/active_record/errors.rb +108 -15
  119. data/lib/active_record/explain.rb +23 -3
  120. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  121. data/lib/active_record/fixture_set/render_context.rb +2 -0
  122. data/lib/active_record/fixture_set/table_row.rb +29 -8
  123. data/lib/active_record/fixtures.rb +119 -71
  124. data/lib/active_record/future_result.rb +30 -5
  125. data/lib/active_record/gem_version.rb +3 -3
  126. data/lib/active_record/inheritance.rb +30 -16
  127. data/lib/active_record/insert_all.rb +55 -8
  128. data/lib/active_record/integration.rb +8 -8
  129. data/lib/active_record/internal_metadata.rb +120 -30
  130. data/lib/active_record/locking/pessimistic.rb +5 -2
  131. data/lib/active_record/log_subscriber.rb +29 -12
  132. data/lib/active_record/marshalling.rb +56 -0
  133. data/lib/active_record/message_pack.rb +124 -0
  134. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  135. data/lib/active_record/middleware/database_selector.rb +5 -7
  136. data/lib/active_record/middleware/shard_selector.rb +3 -1
  137. data/lib/active_record/migration/command_recorder.rb +104 -5
  138. data/lib/active_record/migration/compatibility.rb +142 -58
  139. data/lib/active_record/migration/default_strategy.rb +23 -0
  140. data/lib/active_record/migration/execution_strategy.rb +19 -0
  141. data/lib/active_record/migration.rb +265 -112
  142. data/lib/active_record/model_schema.rb +60 -40
  143. data/lib/active_record/nested_attributes.rb +21 -3
  144. data/lib/active_record/normalization.rb +159 -0
  145. data/lib/active_record/persistence.rb +187 -35
  146. data/lib/active_record/promise.rb +84 -0
  147. data/lib/active_record/query_cache.rb +3 -21
  148. data/lib/active_record/query_logs.rb +77 -52
  149. data/lib/active_record/query_logs_formatter.rb +41 -0
  150. data/lib/active_record/querying.rb +15 -2
  151. data/lib/active_record/railtie.rb +109 -47
  152. data/lib/active_record/railties/controller_runtime.rb +12 -8
  153. data/lib/active_record/railties/databases.rake +139 -145
  154. data/lib/active_record/railties/job_runtime.rb +23 -0
  155. data/lib/active_record/readonly_attributes.rb +32 -5
  156. data/lib/active_record/reflection.rb +162 -44
  157. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  158. data/lib/active_record/relation/batches.rb +190 -61
  159. data/lib/active_record/relation/calculations.rb +160 -63
  160. data/lib/active_record/relation/delegation.rb +22 -8
  161. data/lib/active_record/relation/finder_methods.rb +77 -16
  162. data/lib/active_record/relation/merger.rb +2 -0
  163. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  165. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  166. data/lib/active_record/relation/predicate_builder.rb +27 -16
  167. data/lib/active_record/relation/query_attribute.rb +25 -1
  168. data/lib/active_record/relation/query_methods.rb +378 -70
  169. data/lib/active_record/relation/spawn_methods.rb +18 -1
  170. data/lib/active_record/relation.rb +76 -35
  171. data/lib/active_record/result.rb +19 -5
  172. data/lib/active_record/runtime_registry.rb +10 -1
  173. data/lib/active_record/sanitization.rb +51 -11
  174. data/lib/active_record/schema.rb +2 -3
  175. data/lib/active_record/schema_dumper.rb +46 -7
  176. data/lib/active_record/schema_migration.rb +68 -33
  177. data/lib/active_record/scoping/default.rb +15 -5
  178. data/lib/active_record/scoping/named.rb +2 -2
  179. data/lib/active_record/scoping.rb +2 -1
  180. data/lib/active_record/secure_password.rb +60 -0
  181. data/lib/active_record/secure_token.rb +21 -3
  182. data/lib/active_record/signed_id.rb +7 -5
  183. data/lib/active_record/store.rb +8 -8
  184. data/lib/active_record/suppressor.rb +3 -1
  185. data/lib/active_record/table_metadata.rb +11 -2
  186. data/lib/active_record/tasks/database_tasks.rb +127 -105
  187. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  188. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  189. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  190. data/lib/active_record/test_fixtures.rb +113 -96
  191. data/lib/active_record/timestamp.rb +27 -15
  192. data/lib/active_record/token_for.rb +113 -0
  193. data/lib/active_record/touch_later.rb +11 -6
  194. data/lib/active_record/transactions.rb +39 -13
  195. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  196. data/lib/active_record/type/internal/timezone.rb +7 -2
  197. data/lib/active_record/type/serialized.rb +4 -0
  198. data/lib/active_record/type/time.rb +4 -0
  199. data/lib/active_record/validations/absence.rb +1 -1
  200. data/lib/active_record/validations/numericality.rb +5 -4
  201. data/lib/active_record/validations/presence.rb +5 -28
  202. data/lib/active_record/validations/uniqueness.rb +47 -2
  203. data/lib/active_record/validations.rb +8 -4
  204. data/lib/active_record/version.rb +1 -1
  205. data/lib/active_record.rb +121 -16
  206. data/lib/arel/errors.rb +10 -0
  207. data/lib/arel/factory_methods.rb +4 -0
  208. data/lib/arel/nodes/and.rb +4 -0
  209. data/lib/arel/nodes/binary.rb +6 -1
  210. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  211. data/lib/arel/nodes/cte.rb +36 -0
  212. data/lib/arel/nodes/fragments.rb +35 -0
  213. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  214. data/lib/arel/nodes/leading_join.rb +8 -0
  215. data/lib/arel/nodes/node.rb +111 -2
  216. data/lib/arel/nodes/sql_literal.rb +6 -0
  217. data/lib/arel/nodes/table_alias.rb +4 -0
  218. data/lib/arel/nodes.rb +4 -0
  219. data/lib/arel/predications.rb +2 -0
  220. data/lib/arel/table.rb +9 -5
  221. data/lib/arel/visitors/mysql.rb +8 -1
  222. data/lib/arel/visitors/to_sql.rb +81 -17
  223. data/lib/arel/visitors/visitor.rb +2 -2
  224. data/lib/arel.rb +16 -2
  225. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  226. data/lib/rails/generators/active_record/migration.rb +3 -1
  227. data/lib/rails/generators/active_record/model/USAGE +113 -0
  228. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  229. metadata +50 -15
  230. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  231. 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:
@@ -77,16 +68,37 @@ module ActiveRecord
77
68
  def new_client(conn_params)
78
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
  ##
@@ -105,7 +117,7 @@ module ActiveRecord
105
117
  ##
106
118
  # :singleton-method:
107
119
  # 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".
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
  #
@@ -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.
@@ -917,6 +1076,7 @@ module ActiveRecord
917
1076
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
918
1077
  pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
919
1078
  c.collname, col_description(a.attrelid, a.attnum) AS comment,
1079
+ a.attidentity AS identity,
920
1080
  #{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
921
1081
  FROM pg_attribute a
922
1082
  LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
@@ -928,37 +1088,37 @@ module ActiveRecord
928
1088
  SQL
929
1089
  end
930
1090
 
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
1091
  def arel_visitor
937
1092
  Arel::Visitors::PostgreSQL.new(self)
938
1093
  end
939
1094
 
940
1095
  def build_statement_pool
941
- StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
1096
+ StatementPool.new(self, self.class.type_cast_config_to_integer(@config[:statement_limit]))
942
1097
  end
943
1098
 
944
1099
  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)
1100
+ # NOTE: citext is an exception. It is possible to perform a
1101
+ # case-insensitive comparison using `LOWER()`, but it is
1102
+ # unnecessary, as `citext` is case-insensitive by definition.
1103
+ @case_insensitive_cache ||= { "citext" => false }
1104
+ @case_insensitive_cache.fetch(column.sql_type) do
1105
+ @case_insensitive_cache[column.sql_type] = begin
1106
+ sql = <<~SQL
1107
+ SELECT exists(
1108
+ SELECT * FROM pg_proc
1109
+ WHERE proname = 'lower'
1110
+ AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
1111
+ ) OR exists(
1112
+ SELECT * FROM pg_proc
1113
+ INNER JOIN pg_cast
1114
+ ON ARRAY[casttarget]::oidvector = proargtypes
1115
+ WHERE proname = 'lower'
1116
+ AND castsource = #{quote column.sql_type}::regtype
1117
+ )
1118
+ SQL
1119
+ execute_and_clear(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
1120
+ result.getvalue(0, 0)
1121
+ end
962
1122
  end
963
1123
  end
964
1124
  end
@@ -968,28 +1128,30 @@ module ActiveRecord
968
1128
  map[Integer] = PG::TextEncoder::Integer.new
969
1129
  map[TrueClass] = PG::TextEncoder::Boolean.new
970
1130
  map[FalseClass] = PG::TextEncoder::Boolean.new
971
- @connection.type_map_for_queries = map
1131
+ @raw_connection.type_map_for_queries = map
972
1132
  end
973
1133
 
974
1134
  def update_typemap_for_default_timezone
975
- if @default_timezone != ActiveRecord.default_timezone && @timestamp_decoder
976
- decoder_class = ActiveRecord.default_timezone == :utc ?
1135
+ if @raw_connection && @mapped_default_timezone != default_timezone && @timestamp_decoder
1136
+ decoder_class = default_timezone == :utc ?
977
1137
  PG::TextDecoder::TimestampUtc :
978
1138
  PG::TextDecoder::TimestampWithoutTimeZone
979
1139
 
980
1140
  @timestamp_decoder = decoder_class.new(**@timestamp_decoder.to_h)
981
- @connection.type_map_for_results.add_coder(@timestamp_decoder)
1141
+ @raw_connection.type_map_for_results.add_coder(@timestamp_decoder)
982
1142
 
983
- @default_timezone = ActiveRecord.default_timezone
1143
+ @mapped_default_timezone = default_timezone
984
1144
 
985
1145
  # if default timezone has changed, we need to reconfigure the connection
986
1146
  # (specifically, the session time zone)
987
- configure_connection
1147
+ reconfigure_connection_timezone
1148
+
1149
+ true
988
1150
  end
989
1151
  end
990
1152
 
991
1153
  def add_pg_decoders
992
- @default_timezone = nil
1154
+ @mapped_default_timezone = nil
993
1155
  @timestamp_decoder = nil
994
1156
 
995
1157
  coders_by_name = {
@@ -1011,13 +1173,13 @@ module ActiveRecord
1011
1173
  FROM pg_type as t
1012
1174
  WHERE t.typname IN (%s)
1013
1175
  SQL
1014
- coders = execute_and_clear(query, "SCHEMA", []) do |result|
1176
+ coders = execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
1015
1177
  result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
1016
1178
  end
1017
1179
 
1018
1180
  map = PG::TypeMapByOid.new
1019
1181
  coders.each { |coder| map.add_coder(coder) }
1020
- @connection.type_map_for_results = map
1182
+ @raw_connection.type_map_for_results = map
1021
1183
 
1022
1184
  @type_map_for_results = PG::TypeMapByOid.new
1023
1185
  @type_map_for_results.default_type_map = map