activerecord 7.0.8 → 7.1.3.2

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 +1546 -1454
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +16 -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 +20 -4
  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 +15 -9
  15. data/lib/active_record/associations/collection_proxy.rb +15 -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 -3
  20. data/lib/active_record/associations/join_dependency.rb +10 -8
  21. data/lib/active_record/associations/preloader/association.rb +31 -7
  22. data/lib/active_record/associations/preloader.rb +13 -10
  23. data/lib/active_record/associations/singular_association.rb +1 -1
  24. data/lib/active_record/associations/through_association.rb +22 -11
  25. data/lib/active_record/associations.rb +313 -217
  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 +52 -34
  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 +55 -9
  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 +74 -51
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
  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 +511 -91
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -108
  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 +22 -143
  60. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
  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 +151 -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 +74 -40
  71. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  72. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  73. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -6
  75. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  76. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  77. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  78. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  79. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +361 -60
  80. data/lib/active_record/connection_adapters/postgresql_adapter.rb +353 -192
  81. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  82. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  83. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
  84. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
  85. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
  86. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
  87. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +209 -79
  88. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  89. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  90. data/lib/active_record/connection_adapters/trilogy_adapter.rb +262 -0
  91. data/lib/active_record/connection_adapters.rb +3 -1
  92. data/lib/active_record/connection_handling.rb +72 -95
  93. data/lib/active_record/core.rb +175 -153
  94. data/lib/active_record/counter_cache.rb +46 -25
  95. data/lib/active_record/database_configurations/database_config.rb +9 -3
  96. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  97. data/lib/active_record/database_configurations/url_config.rb +17 -11
  98. data/lib/active_record/database_configurations.rb +86 -33
  99. data/lib/active_record/delegated_type.rb +9 -4
  100. data/lib/active_record/deprecator.rb +7 -0
  101. data/lib/active_record/destroy_association_async_job.rb +2 -0
  102. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  103. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  104. data/lib/active_record/encryption/config.rb +25 -1
  105. data/lib/active_record/encryption/configurable.rb +12 -19
  106. data/lib/active_record/encryption/context.rb +10 -3
  107. data/lib/active_record/encryption/contexts.rb +5 -1
  108. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  109. data/lib/active_record/encryption/encryptable_record.rb +42 -18
  110. data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
  111. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  112. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  113. data/lib/active_record/encryption/key_generator.rb +12 -1
  114. data/lib/active_record/encryption/message_serializer.rb +2 -0
  115. data/lib/active_record/encryption/properties.rb +3 -3
  116. data/lib/active_record/encryption/scheme.rb +19 -22
  117. data/lib/active_record/encryption.rb +1 -0
  118. data/lib/active_record/enum.rb +112 -28
  119. data/lib/active_record/errors.rb +112 -18
  120. data/lib/active_record/explain.rb +23 -3
  121. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  122. data/lib/active_record/fixture_set/render_context.rb +2 -0
  123. data/lib/active_record/fixture_set/table_row.rb +29 -8
  124. data/lib/active_record/fixtures.rb +135 -71
  125. data/lib/active_record/future_result.rb +31 -5
  126. data/lib/active_record/gem_version.rb +4 -4
  127. data/lib/active_record/inheritance.rb +30 -16
  128. data/lib/active_record/insert_all.rb +57 -10
  129. data/lib/active_record/integration.rb +8 -8
  130. data/lib/active_record/internal_metadata.rb +120 -30
  131. data/lib/active_record/locking/pessimistic.rb +5 -2
  132. data/lib/active_record/log_subscriber.rb +29 -12
  133. data/lib/active_record/marshalling.rb +56 -0
  134. data/lib/active_record/message_pack.rb +124 -0
  135. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  136. data/lib/active_record/middleware/database_selector.rb +6 -8
  137. data/lib/active_record/middleware/shard_selector.rb +3 -1
  138. data/lib/active_record/migration/command_recorder.rb +104 -5
  139. data/lib/active_record/migration/compatibility.rb +139 -5
  140. data/lib/active_record/migration/default_strategy.rb +23 -0
  141. data/lib/active_record/migration/execution_strategy.rb +19 -0
  142. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  143. data/lib/active_record/migration.rb +219 -111
  144. data/lib/active_record/model_schema.rb +64 -44
  145. data/lib/active_record/nested_attributes.rb +24 -6
  146. data/lib/active_record/normalization.rb +167 -0
  147. data/lib/active_record/persistence.rb +188 -37
  148. data/lib/active_record/promise.rb +84 -0
  149. data/lib/active_record/query_cache.rb +3 -21
  150. data/lib/active_record/query_logs.rb +77 -52
  151. data/lib/active_record/query_logs_formatter.rb +41 -0
  152. data/lib/active_record/querying.rb +15 -2
  153. data/lib/active_record/railtie.rb +109 -47
  154. data/lib/active_record/railties/controller_runtime.rb +12 -6
  155. data/lib/active_record/railties/databases.rake +142 -148
  156. data/lib/active_record/railties/job_runtime.rb +23 -0
  157. data/lib/active_record/readonly_attributes.rb +32 -5
  158. data/lib/active_record/reflection.rb +174 -44
  159. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  160. data/lib/active_record/relation/batches.rb +190 -61
  161. data/lib/active_record/relation/calculations.rb +187 -63
  162. data/lib/active_record/relation/delegation.rb +23 -9
  163. data/lib/active_record/relation/finder_methods.rb +77 -16
  164. data/lib/active_record/relation/merger.rb +2 -0
  165. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  166. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  167. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  168. data/lib/active_record/relation/predicate_builder.rb +26 -14
  169. data/lib/active_record/relation/query_attribute.rb +2 -1
  170. data/lib/active_record/relation/query_methods.rb +352 -63
  171. data/lib/active_record/relation/spawn_methods.rb +18 -1
  172. data/lib/active_record/relation.rb +91 -35
  173. data/lib/active_record/result.rb +19 -5
  174. data/lib/active_record/runtime_registry.rb +24 -1
  175. data/lib/active_record/sanitization.rb +51 -11
  176. data/lib/active_record/schema.rb +2 -3
  177. data/lib/active_record/schema_dumper.rb +46 -7
  178. data/lib/active_record/schema_migration.rb +68 -33
  179. data/lib/active_record/scoping/default.rb +15 -5
  180. data/lib/active_record/scoping/named.rb +2 -2
  181. data/lib/active_record/scoping.rb +2 -1
  182. data/lib/active_record/secure_password.rb +60 -0
  183. data/lib/active_record/secure_token.rb +21 -3
  184. data/lib/active_record/signed_id.rb +7 -5
  185. data/lib/active_record/store.rb +8 -8
  186. data/lib/active_record/suppressor.rb +3 -1
  187. data/lib/active_record/table_metadata.rb +10 -1
  188. data/lib/active_record/tasks/database_tasks.rb +127 -105
  189. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  190. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  191. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  192. data/lib/active_record/test_fixtures.rb +113 -96
  193. data/lib/active_record/timestamp.rb +27 -15
  194. data/lib/active_record/token_for.rb +113 -0
  195. data/lib/active_record/touch_later.rb +11 -6
  196. data/lib/active_record/transactions.rb +36 -10
  197. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  198. data/lib/active_record/type/internal/timezone.rb +7 -2
  199. data/lib/active_record/type/time.rb +4 -0
  200. data/lib/active_record/validations/absence.rb +1 -1
  201. data/lib/active_record/validations/numericality.rb +5 -4
  202. data/lib/active_record/validations/presence.rb +5 -28
  203. data/lib/active_record/validations/uniqueness.rb +47 -2
  204. data/lib/active_record/validations.rb +8 -4
  205. data/lib/active_record/version.rb +1 -1
  206. data/lib/active_record.rb +121 -16
  207. data/lib/arel/errors.rb +10 -0
  208. data/lib/arel/factory_methods.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 +1 -9
  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 +48 -12
  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
  ##
@@ -96,16 +108,17 @@ module ActiveRecord
96
108
  # but significantly increases the risk of data loss if the database
97
109
  # crashes. As a result, this should not be used in production
98
110
  # environments. If you would like all created tables to be unlogged in
99
- # the test environment you can add the following line to your test.rb
100
- # file:
111
+ # the test environment you can add the following to your test.rb file:
101
112
  #
102
- # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
113
+ # ActiveSupport.on_load(:active_record_postgresqladapter) do
114
+ # self.create_unlogged_tables = true
115
+ # end
103
116
  class_attribute :create_unlogged_tables, default: false
104
117
 
105
118
  ##
106
119
  # :singleton-method:
107
120
  # 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".
121
+ # in migrations, \Rails will translate this to a PostgreSQL "timestamp without time zone".
109
122
  # Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
110
123
  # store DateTimes as "timestamp with time zone":
111
124
  #
@@ -183,13 +196,17 @@ module ActiveRecord
183
196
  end
184
197
 
185
198
  def supports_partitioned_indexes?
186
- database_version >= 110_000 # >= 11.0
199
+ database_version >= 11_00_00 # >= 11.0
187
200
  end
188
201
 
189
202
  def supports_partial_index?
190
203
  true
191
204
  end
192
205
 
206
+ def supports_index_include?
207
+ database_version >= 11_00_00 # >= 11.0
208
+ end
209
+
193
210
  def supports_expression_index?
194
211
  true
195
212
  end
@@ -206,6 +223,14 @@ module ActiveRecord
206
223
  true
207
224
  end
208
225
 
226
+ def supports_exclusion_constraints?
227
+ true
228
+ end
229
+
230
+ def supports_unique_constraints?
231
+ true
232
+ end
233
+
209
234
  def supports_validate_constraints?
210
235
  true
211
236
  end
@@ -234,25 +259,41 @@ module ActiveRecord
234
259
  true
235
260
  end
236
261
 
262
+ def supports_restart_db_transaction?
263
+ database_version >= 12_00_00 # >= 12.0
264
+ end
265
+
237
266
  def supports_insert_returning?
238
267
  true
239
268
  end
240
269
 
241
270
  def supports_insert_on_conflict?
242
- database_version >= 90500 # >= 9.5
271
+ database_version >= 9_05_00 # >= 9.5
243
272
  end
244
273
  alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
245
274
  alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
246
275
  alias supports_insert_conflict_target? supports_insert_on_conflict?
247
276
 
248
277
  def supports_virtual_columns?
249
- database_version >= 120_000 # >= 12.0
278
+ database_version >= 12_00_00 # >= 12.0
279
+ end
280
+
281
+ def supports_identity_columns? # :nodoc:
282
+ database_version >= 10_00_00 # >= 10.0
283
+ end
284
+
285
+ def supports_nulls_not_distinct?
286
+ database_version >= 15_00_00 # >= 15.0
250
287
  end
251
288
 
252
289
  def index_algorithms
253
290
  { concurrently: "CONCURRENTLY" }
254
291
  end
255
292
 
293
+ def return_value_after_insert?(column) # :nodoc:
294
+ column.auto_populated?
295
+ end
296
+
256
297
  class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
257
298
  def initialize(connection, max)
258
299
  super(max)
@@ -266,47 +307,46 @@ module ActiveRecord
266
307
 
267
308
  private
268
309
  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
310
+ # This is ugly, but safe: the statement pool is only
311
+ # accessed while holding the connection's lock. (And we
312
+ # don't need the complication of with_raw_connection because
313
+ # a reconnect would invalidate the entire statement pool.)
314
+ if conn = @connection.instance_variable_get(:@raw_connection)
315
+ conn.query "DEALLOCATE #{key}" if conn.status == PG::CONNECTION_OK
316
+ end
275
317
  rescue PG::Error
276
- false
277
318
  end
278
319
  end
279
320
 
280
321
  # Initializes and connects a PostgreSQL adapter.
281
- def initialize(connection, logger, connection_parameters, config)
282
- super(connection, logger, config)
322
+ def initialize(...)
323
+ super
283
324
 
284
- @connection_parameters = connection_parameters || {}
325
+ conn_params = @config.compact
285
326
 
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
327
+ # Map ActiveRecords param names to PGs.
328
+ conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
329
+ conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
289
330
 
290
- configure_connection
291
- add_pg_encoders
292
- add_pg_decoders
331
+ # Forward only valid config params to PG::Connection.connect.
332
+ valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
333
+ conn_params.slice!(*valid_conn_param_keys)
293
334
 
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
335
+ @connection_parameters = conn_params
299
336
 
300
- def self.database_exists?(config)
301
- !!ActiveRecord::Base.postgresql_connection(config)
302
- rescue ActiveRecord::NoDatabaseError
303
- false
337
+ @max_identifier_length = nil
338
+ @type_map = nil
339
+ @raw_connection = nil
340
+ @notice_receiver_sql_warnings = []
341
+
342
+ @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
304
343
  end
305
344
 
306
345
  # Is this connection alive and ready for queries?
307
346
  def active?
308
347
  @lock.synchronize do
309
- @connection.query ";"
348
+ return false unless @raw_connection
349
+ @raw_connection.query ";"
310
350
  end
311
351
  true
312
352
  rescue PG::Error
@@ -314,31 +354,27 @@ module ActiveRecord
314
354
  end
315
355
 
316
356
  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
357
  @lock.synchronize do
324
- super
325
- @connection.reset
326
- configure_connection
327
- reload_type_map
328
- rescue PG::ConnectionBad
329
- connect
358
+ if @type_map
359
+ type_map.clear
360
+ else
361
+ @type_map = Type::HashLookupTypeMap.new
362
+ end
363
+
364
+ initialize_type_map
330
365
  end
331
366
  end
332
367
 
333
368
  def reset!
334
369
  @lock.synchronize do
335
- clear_cache!
336
- reset_transaction
337
- unless @connection.transaction_status == ::PG::PQTRANS_IDLE
338
- @connection.query "ROLLBACK"
370
+ return connect! unless @raw_connection
371
+
372
+ unless @raw_connection.transaction_status == ::PG::PQTRANS_IDLE
373
+ @raw_connection.query "ROLLBACK"
339
374
  end
340
- @connection.query "DISCARD ALL"
341
- configure_connection
375
+ @raw_connection.query "DISCARD ALL"
376
+
377
+ super
342
378
  end
343
379
  end
344
380
 
@@ -347,14 +383,15 @@ module ActiveRecord
347
383
  def disconnect!
348
384
  @lock.synchronize do
349
385
  super
350
- @connection.close rescue nil
386
+ @raw_connection&.close rescue nil
387
+ @raw_connection = nil
351
388
  end
352
389
  end
353
390
 
354
391
  def discard! # :nodoc:
355
392
  super
356
- @connection.socket_io.reopen(IO::NULL) rescue nil
357
- @connection = nil
393
+ @raw_connection&.socket_io&.reopen(IO::NULL) rescue nil
394
+ @raw_connection = nil
358
395
  end
359
396
 
360
397
  def native_database_types # :nodoc:
@@ -370,7 +407,7 @@ module ActiveRecord
370
407
  end
371
408
 
372
409
  def set_standard_conforming_strings
373
- execute("SET standard_conforming_strings = on", "SCHEMA")
410
+ internal_execute("SET standard_conforming_strings = on")
374
411
  end
375
412
 
376
413
  def supports_ddl_transactions?
@@ -398,7 +435,7 @@ module ActiveRecord
398
435
  end
399
436
 
400
437
  def supports_pgcrypto_uuid?
401
- database_version >= 90400 # >= 9.4
438
+ database_version >= 9_04_00 # >= 9.4
402
439
  end
403
440
 
404
441
  def supports_optimizer_hints?
@@ -430,14 +467,21 @@ module ActiveRecord
430
467
  query_value("SELECT pg_advisory_unlock(#{lock_id})")
431
468
  end
432
469
 
433
- def enable_extension(name)
434
- exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
435
- reload_type_map
436
- }
470
+ def enable_extension(name, **)
471
+ schema, name = name.to_s.split(".").values_at(-2, -1)
472
+ sql = +"CREATE EXTENSION IF NOT EXISTS \"#{name}\""
473
+ sql << " SCHEMA #{schema}" if schema
474
+
475
+ internal_exec_query(sql).tap { reload_type_map }
437
476
  end
438
477
 
439
- def disable_extension(name)
440
- exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
478
+ # Removes an extension from the database.
479
+ #
480
+ # [<tt>:force</tt>]
481
+ # Set to +:cascade+ to drop dependent objects as well.
482
+ # Defaults to false.
483
+ def disable_extension(name, force: false)
484
+ internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
441
485
  reload_type_map
442
486
  }
443
487
  end
@@ -451,7 +495,7 @@ module ActiveRecord
451
495
  end
452
496
 
453
497
  def extensions
454
- exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
498
+ internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
455
499
  end
456
500
 
457
501
  # Returns a list of defined enum types, and their values.
@@ -459,31 +503,97 @@ module ActiveRecord
459
503
  query = <<~SQL
460
504
  SELECT
461
505
  type.typname AS name,
506
+ type.OID AS oid,
507
+ n.nspname AS schema,
462
508
  string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
463
509
  FROM pg_enum AS enum
464
- JOIN pg_type AS type
465
- ON (type.oid = enum.enumtypid)
466
- GROUP BY type.typname;
510
+ JOIN pg_type AS type ON (type.oid = enum.enumtypid)
511
+ JOIN pg_namespace n ON type.typnamespace = n.oid
512
+ WHERE n.nspname = ANY (current_schemas(false))
513
+ GROUP BY type.OID, n.nspname, type.typname;
467
514
  SQL
468
- exec_query(query, "SCHEMA").cast_values
515
+
516
+ internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.each_with_object({}) do |row, memo|
517
+ name, schema = row[0], row[2]
518
+ schema = nil if schema == current_schema
519
+ full_name = [schema, name].compact.join(".")
520
+ memo[full_name] = row.last
521
+ end.to_a
469
522
  end
470
523
 
471
524
  # 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(", ")
525
+ def create_enum(name, values, **options)
526
+ sql_values = values.map { |s| quote(s) }.join(", ")
527
+ scope = quoted_scope(name)
474
528
  query = <<~SQL
475
529
  DO $$
476
530
  BEGIN
477
531
  IF NOT EXISTS (
478
- SELECT 1 FROM pg_type t
479
- WHERE t.typname = '#{name}'
532
+ SELECT 1
533
+ FROM pg_type t
534
+ JOIN pg_namespace n ON t.typnamespace = n.oid
535
+ WHERE t.typname = #{scope[:name]}
536
+ AND n.nspname = #{scope[:schema]}
480
537
  ) THEN
481
- CREATE TYPE \"#{name}\" AS ENUM (#{sql_values});
538
+ CREATE TYPE #{quote_table_name(name)} AS ENUM (#{sql_values});
482
539
  END IF;
483
540
  END
484
541
  $$;
485
542
  SQL
486
- exec_query(query)
543
+ internal_exec_query(query).tap { reload_type_map }
544
+ end
545
+
546
+ # Drops an enum type.
547
+ #
548
+ # If the <tt>if_exists: true</tt> option is provided, the enum is dropped
549
+ # only if it exists. Otherwise, if the enum doesn't exist, an error is
550
+ # raised.
551
+ #
552
+ # The +values+ parameter will be ignored if present. It can be helpful
553
+ # to provide this in a migration's +change+ method so it can be reverted.
554
+ # In that case, +values+ will be used by #create_enum.
555
+ def drop_enum(name, values = nil, **options)
556
+ query = <<~SQL
557
+ DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
558
+ SQL
559
+ internal_exec_query(query).tap { reload_type_map }
560
+ end
561
+
562
+ # Rename an existing enum type to something else.
563
+ def rename_enum(name, options = {})
564
+ to = options.fetch(:to) { raise ArgumentError, ":to is required" }
565
+
566
+ exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
567
+ end
568
+
569
+ # Add enum value to an existing enum type.
570
+ def add_enum_value(type_name, value, options = {})
571
+ before, after = options.values_at(:before, :after)
572
+ sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE '#{value}'"
573
+
574
+ if before && after
575
+ raise ArgumentError, "Cannot have both :before and :after at the same time"
576
+ elsif before
577
+ sql << " BEFORE '#{before}'"
578
+ elsif after
579
+ sql << " AFTER '#{after}'"
580
+ end
581
+
582
+ execute(sql).tap { reload_type_map }
583
+ end
584
+
585
+ # Rename enum value on an existing enum type.
586
+ def rename_enum_value(type_name, options = {})
587
+ unless database_version >= 10_00_00 # >= 10.0
588
+ raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
589
+ end
590
+
591
+ from = options.fetch(:from) { raise ArgumentError, ":from is required" }
592
+ to = options.fetch(:to) { raise ArgumentError, ":to is required" }
593
+
594
+ execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE '#{from}' TO '#{to}'").tap {
595
+ reload_type_map
596
+ }
487
597
  end
488
598
 
489
599
  # Returns the configured supported identifier length supported by PostgreSQL
@@ -494,7 +604,7 @@ module ActiveRecord
494
604
  # Set the authorized user for this session
495
605
  def session_auth=(user)
496
606
  clear_cache!
497
- execute("SET SESSION AUTHORIZATION #{user}")
607
+ internal_execute("SET SESSION AUTHORIZATION #{user}", nil, materialize_transactions: true)
498
608
  end
499
609
 
500
610
  def use_insert_returning?
@@ -503,7 +613,7 @@ module ActiveRecord
503
613
 
504
614
  # Returns the version of the connected PostgreSQL server.
505
615
  def get_database_version # :nodoc:
506
- @connection.server_version
616
+ valid_raw_connection.server_version
507
617
  end
508
618
  alias :postgresql_version :database_version
509
619
 
@@ -531,7 +641,7 @@ module ActiveRecord
531
641
  end
532
642
 
533
643
  def check_version # :nodoc:
534
- if database_version < 90300 # < 9.3
644
+ if database_version < 9_03_00 # < 9.3
535
645
  raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
536
646
  end
537
647
  end
@@ -575,10 +685,6 @@ module ActiveRecord
575
685
  m.register_type "polygon", OID::SpecializedString.new(:polygon)
576
686
  m.register_type "circle", OID::SpecializedString.new(:circle)
577
687
 
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
688
  m.register_type "numeric" do |_, fmod, sql_type|
583
689
  precision = extract_precision(sql_type)
584
690
  scale = extract_scale(sql_type)
@@ -607,12 +713,15 @@ module ActiveRecord
607
713
  end
608
714
 
609
715
  private
610
- def type_map
611
- @type_map ||= Type::HashLookupTypeMap.new
612
- end
716
+ attr_reader :type_map
613
717
 
614
718
  def initialize_type_map(m = type_map)
615
719
  self.class.initialize_type_map(m)
720
+
721
+ self.class.register_class_with_precision m, "time", Type::Time, timezone: @default_timezone
722
+ self.class.register_class_with_precision m, "timestamp", OID::Timestamp, timezone: @default_timezone
723
+ self.class.register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone
724
+
616
725
  load_additional_types
617
726
  end
618
727
 
@@ -669,35 +778,53 @@ module ActiveRecord
669
778
  case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
670
779
  when nil
671
780
  if exception.message.match?(/connection is closed/i)
672
- ConnectionNotEstablished.new(exception)
781
+ ConnectionNotEstablished.new(exception, connection_pool: @pool)
782
+ elsif exception.is_a?(PG::ConnectionBad)
783
+ # libpq message style always ends with a newline; the pg gem's internal
784
+ # errors do not. We separate these cases because a pg-internal
785
+ # ConnectionBad means it failed before it managed to send the query,
786
+ # whereas a libpq failure could have occurred at any time (meaning the
787
+ # server may have already executed part or all of the query).
788
+ if exception.message.end_with?("\n")
789
+ ConnectionFailed.new(exception, connection_pool: @pool)
790
+ else
791
+ ConnectionNotEstablished.new(exception, connection_pool: @pool)
792
+ end
673
793
  else
674
794
  super
675
795
  end
676
796
  when UNIQUE_VIOLATION
677
- RecordNotUnique.new(message, sql: sql, binds: binds)
797
+ RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
678
798
  when FOREIGN_KEY_VIOLATION
679
- InvalidForeignKey.new(message, sql: sql, binds: binds)
799
+ InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
680
800
  when VALUE_LIMIT_VIOLATION
681
- ValueTooLong.new(message, sql: sql, binds: binds)
801
+ ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
682
802
  when NUMERIC_VALUE_OUT_OF_RANGE
683
- RangeError.new(message, sql: sql, binds: binds)
803
+ RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
684
804
  when NOT_NULL_VIOLATION
685
- NotNullViolation.new(message, sql: sql, binds: binds)
805
+ NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
686
806
  when SERIALIZATION_FAILURE
687
- SerializationFailure.new(message, sql: sql, binds: binds)
807
+ SerializationFailure.new(message, sql: sql, binds: binds, connection_pool: @pool)
688
808
  when DEADLOCK_DETECTED
689
- Deadlocked.new(message, sql: sql, binds: binds)
809
+ Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
690
810
  when DUPLICATE_DATABASE
691
- DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
811
+ DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
692
812
  when LOCK_NOT_AVAILABLE
693
- LockWaitTimeout.new(message, sql: sql, binds: binds)
813
+ LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
694
814
  when QUERY_CANCELED
695
- QueryCanceled.new(message, sql: sql, binds: binds)
815
+ QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
696
816
  else
697
817
  super
698
818
  end
699
819
  end
700
820
 
821
+ def retryable_query_error?(exception)
822
+ # We cannot retry anything if we're inside a broken transaction; we need to at
823
+ # least raise until the innermost savepoint is rolled back
824
+ @raw_connection&.transaction_status != ::PG::PQTRANS_INERROR &&
825
+ super
826
+ end
827
+
701
828
  def get_oid_type(oid, fmod, column_name, sql_type = "")
702
829
  if !type_map.key?(oid)
703
830
  load_additional_types([oid])
@@ -714,7 +841,7 @@ module ActiveRecord
714
841
  def load_additional_types(oids = nil)
715
842
  initializer = OID::TypeMapInitializer.new(type_map)
716
843
  load_types_queries(initializer, oids) do |query|
717
- execute_and_clear(query, "SCHEMA", []) do |records|
844
+ execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |records|
718
845
  initializer.run(records)
719
846
  end
720
847
  end
@@ -737,14 +864,14 @@ module ActiveRecord
737
864
 
738
865
  FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
739
866
 
740
- def execute_and_clear(sql, name, binds, prepare: false, async: false)
867
+ def execute_and_clear(sql, name, binds, prepare: false, async: false, allow_retry: false, materialize_transactions: true)
741
868
  sql = transform_query(sql)
742
869
  check_if_write_query(sql)
743
870
 
744
871
  if !prepare || without_prepared_statement?(binds)
745
- result = exec_no_cache(sql, name, binds, async: async)
872
+ result = exec_no_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
746
873
  else
747
- result = exec_cache(sql, name, binds, async: async)
874
+ result = exec_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
748
875
  end
749
876
  begin
750
877
  ret = yield result
@@ -754,8 +881,7 @@ module ActiveRecord
754
881
  ret
755
882
  end
756
883
 
757
- def exec_no_cache(sql, name, binds, async: false)
758
- materialize_transactions
884
+ def exec_no_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
759
885
  mark_transaction_written_if_write(sql)
760
886
 
761
887
  # make sure we carry over any changes to ActiveRecord.default_timezone that have been
@@ -764,23 +890,27 @@ module ActiveRecord
764
890
 
765
891
  type_casted_binds = type_casted_binds(binds)
766
892
  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)
893
+ with_raw_connection do |conn|
894
+ result = conn.exec_params(sql, type_casted_binds)
895
+ verified!
896
+ result
769
897
  end
770
898
  end
771
899
  end
772
900
 
773
- def exec_cache(sql, name, binds, async: false)
774
- materialize_transactions
901
+ def exec_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
775
902
  mark_transaction_written_if_write(sql)
903
+
776
904
  update_typemap_for_default_timezone
777
905
 
778
- stmt_key = prepare_statement(sql, binds)
779
- type_casted_binds = type_casted_binds(binds)
906
+ with_raw_connection do |conn|
907
+ stmt_key = prepare_statement(sql, binds, conn)
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
+ log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
911
+ result = conn.exec_prepared(stmt_key, type_casted_binds)
912
+ verified!
913
+ result
784
914
  end
785
915
  end
786
916
  rescue ActiveRecord::StatementInvalid => e
@@ -828,70 +958,98 @@ module ActiveRecord
828
958
 
829
959
  # Prepare the statement if it hasn't been prepared, return
830
960
  # the statement key.
831
- def prepare_statement(sql, binds)
832
- @lock.synchronize do
833
- sql_key = sql_key(sql)
834
- unless @statements.key? sql_key
835
- nextkey = @statements.next_key
836
- begin
837
- @connection.prepare nextkey, sql
838
- rescue => e
839
- raise translate_exception_class(e, sql, binds)
840
- end
841
- # Clear the queue
842
- @connection.get_last_result
843
- @statements[sql_key] = nextkey
961
+ def prepare_statement(sql, binds, conn)
962
+ sql_key = sql_key(sql)
963
+ unless @statements.key? sql_key
964
+ nextkey = @statements.next_key
965
+ begin
966
+ conn.prepare nextkey, sql
967
+ rescue => e
968
+ raise translate_exception_class(e, sql, binds)
844
969
  end
845
- @statements[sql_key]
970
+ # Clear the queue
971
+ conn.get_last_result
972
+ @statements[sql_key] = nextkey
846
973
  end
974
+ @statements[sql_key]
847
975
  end
848
976
 
849
977
  # Connects to a PostgreSQL server and sets up the adapter depending on the
850
978
  # connected server's characteristics.
851
979
  def connect
852
- @connection = self.class.new_client(@connection_parameters)
853
- configure_connection
854
- add_pg_encoders
855
- add_pg_decoders
980
+ @raw_connection = self.class.new_client(@connection_parameters)
981
+ rescue ConnectionNotEstablished => ex
982
+ raise ex.set_pool(@pool)
983
+ end
984
+
985
+ def reconnect
986
+ begin
987
+ @raw_connection&.reset
988
+ rescue PG::ConnectionBad
989
+ @raw_connection = nil
990
+ end
991
+
992
+ connect unless @raw_connection
856
993
  end
857
994
 
858
995
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
859
996
  # This is called by #connect and should not be called manually.
860
997
  def configure_connection
861
998
  if @config[:encoding]
862
- @connection.set_client_encoding(@config[:encoding])
999
+ @raw_connection.set_client_encoding(@config[:encoding])
863
1000
  end
864
1001
  self.client_min_messages = @config[:min_messages] || "warning"
865
1002
  self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
866
1003
 
1004
+ unless ActiveRecord.db_warnings_action.nil?
1005
+ @raw_connection.set_notice_receiver do |result|
1006
+ message = result.error_field(PG::Result::PG_DIAG_MESSAGE_PRIMARY)
1007
+ code = result.error_field(PG::Result::PG_DIAG_SQLSTATE)
1008
+ level = result.error_field(PG::Result::PG_DIAG_SEVERITY)
1009
+ @notice_receiver_sql_warnings << SQLWarning.new(message, code, level, nil, @pool)
1010
+ end
1011
+ end
1012
+
867
1013
  # Use standard-conforming strings so we don't have to do the E'...' dance.
868
1014
  set_standard_conforming_strings
869
1015
 
870
1016
  variables = @config.fetch(:variables, {}).stringify_keys
871
1017
 
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
1018
  # Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
883
- execute("SET intervalstyle = iso_8601", "SCHEMA")
1019
+ internal_execute("SET intervalstyle = iso_8601")
884
1020
 
885
1021
  # SET statements from :variables config hash
886
1022
  # https://www.postgresql.org/docs/current/static/sql-set.html
887
1023
  variables.map do |k, v|
888
1024
  if v == ":default" || v == :default
889
1025
  # Sets the value to the global or compile default
890
- execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
1026
+ internal_execute("SET SESSION #{k} TO DEFAULT")
891
1027
  elsif !v.nil?
892
- execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
1028
+ internal_execute("SET SESSION #{k} TO #{quote(v)}")
893
1029
  end
894
1030
  end
1031
+
1032
+ add_pg_encoders
1033
+ add_pg_decoders
1034
+
1035
+ reload_type_map
1036
+ end
1037
+
1038
+ def reconfigure_connection_timezone
1039
+ variables = @config.fetch(:variables, {}).stringify_keys
1040
+
1041
+ # If it's been directly configured as a connection variable, we don't
1042
+ # need to do anything here; it will be set up by configure_connection
1043
+ # and then never changed.
1044
+ return if variables["timezone"]
1045
+
1046
+ # If using Active Record's time zone support configure the connection
1047
+ # to return TIMESTAMP WITH ZONE types in UTC.
1048
+ if default_timezone == :utc
1049
+ internal_execute("SET SESSION timezone TO 'UTC'")
1050
+ else
1051
+ internal_execute("SET SESSION timezone TO DEFAULT")
1052
+ end
895
1053
  end
896
1054
 
897
1055
  # Returns the list of a table's column names, data types, and default values.
@@ -917,6 +1075,7 @@ module ActiveRecord
917
1075
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
918
1076
  pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
919
1077
  c.collname, col_description(a.attrelid, a.attnum) AS comment,
1078
+ #{supports_identity_columns? ? 'attidentity' : quote('')} AS identity,
920
1079
  #{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
921
1080
  FROM pg_attribute a
922
1081
  LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
@@ -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
1139
  @timestamp_decoder = decoder_class.new(**@timestamp_decoder.to_h)
981
- @connection.type_map_for_results.add_coder(@timestamp_decoder)
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