discourse/config/initializers/000-pg_connection_patch.rb
Alan Guo Xiang Tan a406316dde
DEV: Patch ActiveRecord's postgresql adapter .new_client (#33820)
This commit updates our monkey patch of
`ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.new_client` to
improve two things:

1. Improve the conditional used to determine if a database does not
   exist. Just checking that the error message contains the `database`
name is not sufficient as the value of the database name may be part of
the `host` connection param which can appear in PG error messages as
well.

2. When the PG error messages includes the `user` or `host` connection
   param. We append the original PG error message on to the original
error messge so that we don't just end up with a generic error message
that doesn't help us to figure out the actual error.
2025-09-18 11:34:37 +08:00

39 lines
2.1 KiB
Ruby

# frozen_string_literal: true
class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
def self.new_client(conn_params)
start = Time.now()
PG.connect(**conn_params)
rescue ::PG::Error => error
if conn_params && conn_params[:dbname] == "postgres"
raise ActiveRecord::ConnectionNotEstablished, error.message
# === PATCH START ===
# PostgreSQL's error message for a missing database looks something like this `FATAL: database "<databasename>" does not exist`.
# This patch updates the following conditional to be stricter and not match on other error messages that may contain the database name.
# For example, `<database>-some-host" (<some IP>), port 5432 failed: timeout expired after 11.30376464s` should not be considered
# a missing database error.
#
# Ideally, we want to include more of the missing database error message but we are unsure if the error message
# will change based on a user's locale and whether the locale is even selectable. The workaround now is already much better
# than the existing conditional.
elsif conn_params && conn_params[:dbname] &&
error.message.include?("\"#{conn_params[:dbname]}\"")
raise ActiveRecord::NoDatabaseError.db_error(conn_params[:dbname])
# === PATCH END ===
elsif conn_params && conn_params[:user] && error.message.include?(conn_params[:user])
# === PATCH START ===
error_message =
ActiveRecord::DatabaseConnectionError.username_error(conn_params[:user]).message
raise ActiveRecord::DatabaseConnectionError.new("#{error_message}\n#{error.message}")
# === PATCH END ===
elsif conn_params && conn_params[:host] && error.message.include?(conn_params[:host])
# === PATCH START ===
error_message =
ActiveRecord::DatabaseConnectionError.hostname_error(conn_params[:host]).message
raise ActiveRecord::DatabaseConnectionError.new("#{error_message}\n#{error.message}")
# === PATCH END ===
else
raise ActiveRecord::ConnectionNotEstablished, error.message
end
end
end