public inbox for pgsql-hackers@postgresql.org
help / color / mirror / Atom feedSet notice receiver before libpq connection startup
10+ messages / 4 participants
[nested] [flat]
* Set notice receiver before libpq connection startup
@ 2026-05-20 07:21 Chao Li <li.evan.chao@gmail.com>
0 siblings, 1 reply; 10+ messages in thread
From: Chao Li @ 2026-05-20 07:21 UTC (permalink / raw)
To: pgsql-hackers; +Cc: Fujii Masao <masao.fujii@gmail.com>; vignesh C <vignesh21@gmail.com>
Hi,
While testing “Log remote NOTICE, WARNING, and similar messages using ereport()”, I noticed that libpqsrv_notice_receiver is only installed after libpqsrv_connect() finishes. As a result, NOTICE messages generated during connection establishment are missed by ereport() and are still printed to stderr.
To reproduce the issue, I created a separate database called remotedb and defined a login trigger that emits a NOTICE message:
```
CREATE DATABASE remotedb;
\c remotedb
CREATE OR REPLACE FUNCTION repro_login_notice()
RETURNS event_trigger
LANGUAGE plpgsql AS $$
BEGIN
RAISE NOTICE 'startup notice from remotedb login trigger';
END;
$$;
CREATE EVENT TRIGGER repro_login_notice_trg
ON login
EXECUTE FUNCTION repro_login_notice();
ALTER EVENT TRIGGER repro_login_notice_trg ENABLE ALWAYS;
```
Then, from another database:
```
evantest=# create extension dblink;
CREATE EXTENSION
evantest=# SELECT dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
dblink_connect
----------------
OK
(1 row)
```
In the system log, the NOTICE message is printed directly:
```
2026-05-20 13:02:19.350 CST [24909] STATEMENT: SELECT dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
NOTICE: startup notice from remotedb login trigger
```
To fix that, I think we should install libpqsrv_notice_receiver before libpqsrv_connect_internal(). In the attached patch, I added two helpers: libpqsrv_connect_with_notice_receiver() and libpqsrv_connect_params_with_notice_receiver().
With the fix, the NOTICE message now looks like this:
```
2026-05-20 14:44:49.296 CST [45567] LOG: received message via remote connection: NOTICE: startup notice from remotedb login trigger
2026-05-20 14:44:49.296 CST [45567] STATEMENT: SELECT dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
```
Please see the attached patch for details.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Attachments:
[application/octet-stream] v1-0001-Set-notice-receiver-before-libpq-connection-start.patch (7.6K, 2-v1-0001-Set-notice-receiver-before-libpq-connection-start.patch)
download | inline diff:
From db52f5949884e57769b7ba7fc56be64c47ef1fa1 Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <lic@highgo.com>
Date: Wed, 20 May 2026 14:57:25 +0800
Subject: [PATCH v1] Set notice receiver before libpq connection startup
Some callers of libpqsrv_connect() and libpqsrv_connect_params()
install libpqsrv_notice_receiver after the connection has been
established. However, notices may also be received while
libpqsrv_connect_internal() is still processing the asynchronous
connection startup.
Add variants of these helpers that install a notice receiver immediately
after PQconnectStart() or PQconnectStartParams(), before entering
libpqsrv_connect_internal(). Use them in dblink, postgres_fdw, and
libpqwalreceiver, so notices emitted during connection startup are
handled in the same way as notices received after the connection is
established.
Author: Chao Li <lic@highgo.com>
Reviewed-by:
Discussion: https://postgr.es/m/
---
contrib/dblink/dblink.c | 15 ++++---
contrib/postgres_fdw/connection.c | 11 +++--
.../libpqwalreceiver/libpqwalreceiver.c | 11 +++--
src/include/libpq/libpq-be-fe-helpers.h | 42 ++++++++++++++-----
4 files changed, 49 insertions(+), 30 deletions(-)
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index d843eee7e97..2093aef990f 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -222,7 +222,10 @@ dblink_get_conn(char *conname_or_str,
dblink_we_get_conn = WaitEventExtensionNew("DblinkGetConnect");
/* OK to make connection */
- conn = libpqsrv_connect(connstr, dblink_we_get_conn);
+ conn = libpqsrv_connect_with_notice_receiver(connstr,
+ dblink_we_get_conn,
+ libpqsrv_notice_receiver,
+ "received message via remote connection");
if (PQstatus(conn) == CONNECTION_BAD)
{
@@ -235,9 +238,6 @@ dblink_get_conn(char *conname_or_str,
errdetail_internal("%s", msg)));
}
- PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
- "received message via remote connection");
-
dblink_security_check(conn, NULL, connstr);
if (PQclientEncoding(conn) != GetDatabaseEncoding())
PQsetClientEncoding(conn, GetDatabaseEncodingName());
@@ -321,7 +321,9 @@ dblink_connect(PG_FUNCTION_ARGS)
}
/* OK to make connection */
- conn = libpqsrv_connect(connstr, dblink_we_connect);
+ conn = libpqsrv_connect_with_notice_receiver(connstr, dblink_we_connect,
+ libpqsrv_notice_receiver,
+ "received message via remote connection");
if (PQstatus(conn) == CONNECTION_BAD)
{
@@ -336,9 +338,6 @@ dblink_connect(PG_FUNCTION_ARGS)
errdetail_internal("%s", msg)));
}
- PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
- "received message via remote connection");
-
/* check password actually used if not superuser */
dblink_security_check(conn, connname, connstr);
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 3d2a8d0519d..0278f4a7cea 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -646,9 +646,11 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
pgfdw_we_connect = WaitEventExtensionNew("PostgresFdwConnect");
/* OK to make connection */
- conn = libpqsrv_connect_params(keywords, values,
- false, /* expand_dbname */
- pgfdw_we_connect);
+ conn = libpqsrv_connect_params_with_notice_receiver(keywords, values,
+ false, /* expand_dbname */
+ pgfdw_we_connect,
+ libpqsrv_notice_receiver,
+ "received message via remote connection");
if (!conn || PQstatus(conn) != CONNECTION_OK)
ereport(ERROR,
@@ -657,9 +659,6 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
server->servername),
errdetail_internal("%s", pchomp(PQerrorMessage(conn)))));
- PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
- "received message via remote connection");
-
/* Perform post-connection security checks. */
pgfdw_security_check(keywords, values, user, conn);
diff --git a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
index 9f04c9ed25d..cc5f77d53f7 100644
--- a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
+++ b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
@@ -223,9 +223,11 @@ libpqrcv_connect(const char *conninfo, bool replication, bool logical,
conn = palloc0_object(WalReceiverConn);
conn->streamConn =
- libpqsrv_connect_params(keys, vals,
- /* expand_dbname = */ true,
- WAIT_EVENT_LIBPQWALRECEIVER_CONNECT);
+ libpqsrv_connect_params_with_notice_receiver(keys, vals,
+ /* expand_dbname = */ true,
+ WAIT_EVENT_LIBPQWALRECEIVER_CONNECT,
+ libpqsrv_notice_receiver,
+ "received message via replication");
if (options_val != NULL)
pfree(options_val);
@@ -245,9 +247,6 @@ libpqrcv_connect(const char *conninfo, bool replication, bool logical,
errhint("Target server's authentication method must be changed, or set password_required=false in the subscription parameters.")));
}
- PQsetNoticeReceiver(conn->streamConn, libpqsrv_notice_receiver,
- "received message via replication");
-
/*
* Set always-secure search path for the cases where the connection is
* used to run SQL queries, so malicious users can't get control.
diff --git a/src/include/libpq/libpq-be-fe-helpers.h b/src/include/libpq/libpq-be-fe-helpers.h
index 85d8b63f019..0571bb1fe50 100644
--- a/src/include/libpq/libpq-be-fe-helpers.h
+++ b/src/include/libpq/libpq-be-fe-helpers.h
@@ -43,6 +43,23 @@ static inline void libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_inf
static inline PGresult *libpqsrv_get_result_last(PGconn *conn, uint32 wait_event_info);
static inline PGresult *libpqsrv_get_result(PGconn *conn, uint32 wait_event_info);
+static inline PGconn *
+libpqsrv_connect_with_notice_receiver(const char *conninfo,
+ uint32 wait_event_info,
+ PQnoticeReceiver proc, void *arg)
+{
+ PGconn *conn = NULL;
+
+ libpqsrv_connect_prepare();
+
+ conn = PQconnectStart(conninfo);
+ if (conn != NULL && proc != NULL)
+ PQsetNoticeReceiver(conn, proc, arg);
+
+ libpqsrv_connect_internal(conn, wait_event_info);
+
+ return conn;
+}
/*
* PQconnectdb() wrapper that reserves a file descriptor and processes
@@ -54,12 +71,24 @@ static inline PGresult *libpqsrv_get_result(PGconn *conn, uint32 wait_event_info
*/
static inline PGconn *
libpqsrv_connect(const char *conninfo, uint32 wait_event_info)
+{
+ return libpqsrv_connect_with_notice_receiver(conninfo, wait_event_info, NULL, NULL);
+}
+
+static inline PGconn *
+libpqsrv_connect_params_with_notice_receiver(const char *const *keywords,
+ const char *const *values,
+ int expand_dbname,
+ uint32 wait_event_info,
+ PQnoticeReceiver proc, void *arg)
{
PGconn *conn = NULL;
libpqsrv_connect_prepare();
- conn = PQconnectStart(conninfo);
+ conn = PQconnectStartParams(keywords, values, expand_dbname);
+ if (conn != NULL && proc != NULL)
+ PQsetNoticeReceiver(conn, proc, arg);
libpqsrv_connect_internal(conn, wait_event_info);
@@ -76,15 +105,8 @@ libpqsrv_connect_params(const char *const *keywords,
int expand_dbname,
uint32 wait_event_info)
{
- PGconn *conn = NULL;
-
- libpqsrv_connect_prepare();
-
- conn = PQconnectStartParams(keywords, values, expand_dbname);
-
- libpqsrv_connect_internal(conn, wait_event_info);
-
- return conn;
+ return libpqsrv_connect_params_with_notice_receiver(keywords, values, expand_dbname,
+ wait_event_info, NULL, NULL);
}
/*
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 10+ messages in thread
* Re: Set notice receiver before libpq connection startup
@ 2026-05-20 09:19 Fujii Masao <masao.fujii@gmail.com>
parent: Chao Li <li.evan.chao@gmail.com>
0 siblings, 1 reply; 10+ messages in thread
From: Fujii Masao @ 2026-05-20 09:19 UTC (permalink / raw)
To: Chao Li <li.evan.chao@gmail.com>; +Cc: pgsql-hackers; vignesh C <vignesh21@gmail.com>
On Wed, May 20, 2026 at 4:21 PM Chao Li <li.evan.chao@gmail.com> wrote:
>
> Hi,
>
> While testing “Log remote NOTICE, WARNING, and similar messages using ereport()”, I noticed that libpqsrv_notice_receiver is only installed after libpqsrv_connect() finishes. As a result, NOTICE messages generated during connection establishment are missed by ereport() and are still printed to stderr.
>
> To reproduce the issue, I created a separate database called remotedb and defined a login trigger that emits a NOTICE message:
> ```
> CREATE DATABASE remotedb;
>
> \c remotedb
>
> CREATE OR REPLACE FUNCTION repro_login_notice()
> RETURNS event_trigger
> LANGUAGE plpgsql AS $$
> BEGIN
> RAISE NOTICE 'startup notice from remotedb login trigger';
> END;
> $$;
>
> CREATE EVENT TRIGGER repro_login_notice_trg
> ON login
> EXECUTE FUNCTION repro_login_notice();
>
> ALTER EVENT TRIGGER repro_login_notice_trg ENABLE ALWAYS;
> ```
>
> Then, from another database:
> ```
> evantest=# create extension dblink;
> CREATE EXTENSION
> evantest=# SELECT dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
> dblink_connect
> ----------------
> OK
> (1 row)
> ```
>
> In the system log, the NOTICE message is printed directly:
> ```
> 2026-05-20 13:02:19.350 CST [24909] STATEMENT: SELECT dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
> NOTICE: startup notice from remotedb login trigger
> ```
>
> To fix that, I think we should install libpqsrv_notice_receiver before libpqsrv_connect_internal(). In the attached patch, I added two helpers: libpqsrv_connect_with_notice_receiver() and libpqsrv_connect_params_with_notice_receiver().
>
> With the fix, the NOTICE message now looks like this:
> ```
> 2026-05-20 14:44:49.296 CST [45567] LOG: received message via remote connection: NOTICE: startup notice from remotedb login trigger
> 2026-05-20 14:44:49.296 CST [45567] STATEMENT: SELECT dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
> ```
>
> Please see the attached patch for details.
Thanks for the report and patch!
I'd prefer to avoid adding notice-receiver-specific wrappers such as
libpqsrv_connect_with_notice_receiver(). Instead, how about splitting
libpqsrv_connect() into two steps: libpqsrv_connect_start(), which performs
libpqsrv_connect_prepare() and PQconnectStart(), and
libpqsrv_connect_complete(), which runs libpqsrv_connect_internal()?
With this approach, callers could invoke PQsetNoticeReceiver() after
libpqsrv_connect_start() returns but before libpqsrv_connect_complete() is
called. This would allow startup-time notices to be handled correctly without
introducing a dedicated wrapper function.
Compared to the *_with_notice_receiver() approach, this design is more
general because it exposes the phase between PQconnectStart() and connection
completion. It could also support other kinds of per-connection setup in the
future, not just notice receiver installation. Thought?
Regards,
--
Fujii Masao
^ permalink raw reply [nested|flat] 10+ messages in thread
* Re: Set notice receiver before libpq connection startup
@ 2026-05-21 02:09 Chao Li <li.evan.chao@gmail.com>
parent: Fujii Masao <masao.fujii@gmail.com>
0 siblings, 1 reply; 10+ messages in thread
From: Chao Li @ 2026-05-21 02:09 UTC (permalink / raw)
To: Fujii Masao <masao.fujii@gmail.com>; +Cc: pgsql-hackers; vignesh C <vignesh21@gmail.com>
> On May 20, 2026, at 17:19, Fujii Masao <masao.fujii@gmail.com> wrote:
>
> On Wed, May 20, 2026 at 4:21 PM Chao Li <li.evan.chao@gmail.com> wrote:
>>
>> Hi,
>>
>> While testing “Log remote NOTICE, WARNING, and similar messages using ereport()”, I noticed that libpqsrv_notice_receiver is only installed after libpqsrv_connect() finishes. As a result, NOTICE messages generated during connection establishment are missed by ereport() and are still printed to stderr.
>>
>> To reproduce the issue, I created a separate database called remotedb and defined a login trigger that emits a NOTICE message:
>> ```
>> CREATE DATABASE remotedb;
>>
>> \c remotedb
>>
>> CREATE OR REPLACE FUNCTION repro_login_notice()
>> RETURNS event_trigger
>> LANGUAGE plpgsql AS $$
>> BEGIN
>> RAISE NOTICE 'startup notice from remotedb login trigger';
>> END;
>> $$;
>>
>> CREATE EVENT TRIGGER repro_login_notice_trg
>> ON login
>> EXECUTE FUNCTION repro_login_notice();
>>
>> ALTER EVENT TRIGGER repro_login_notice_trg ENABLE ALWAYS;
>> ```
>>
>> Then, from another database:
>> ```
>> evantest=# create extension dblink;
>> CREATE EXTENSION
>> evantest=# SELECT dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
>> dblink_connect
>> ----------------
>> OK
>> (1 row)
>> ```
>>
>> In the system log, the NOTICE message is printed directly:
>> ```
>> 2026-05-20 13:02:19.350 CST [24909] STATEMENT: SELECT dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
>> NOTICE: startup notice from remotedb login trigger
>> ```
>>
>> To fix that, I think we should install libpqsrv_notice_receiver before libpqsrv_connect_internal(). In the attached patch, I added two helpers: libpqsrv_connect_with_notice_receiver() and libpqsrv_connect_params_with_notice_receiver().
>>
>> With the fix, the NOTICE message now looks like this:
>> ```
>> 2026-05-20 14:44:49.296 CST [45567] LOG: received message via remote connection: NOTICE: startup notice from remotedb login trigger
>> 2026-05-20 14:44:49.296 CST [45567] STATEMENT: SELECT dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
>> ```
>>
>> Please see the attached patch for details.
>
> Thanks for the report and patch!
>
> I'd prefer to avoid adding notice-receiver-specific wrappers such as
> libpqsrv_connect_with_notice_receiver(). Instead, how about splitting
> libpqsrv_connect() into two steps: libpqsrv_connect_start(), which performs
> libpqsrv_connect_prepare() and PQconnectStart(), and
> libpqsrv_connect_complete(), which runs libpqsrv_connect_internal()?
>
> With this approach, callers could invoke PQsetNoticeReceiver() after
> libpqsrv_connect_start() returns but before libpqsrv_connect_complete() is
> called. This would allow startup-time notices to be handled correctly without
> introducing a dedicated wrapper function.
>
> Compared to the *_with_notice_receiver() approach, this design is more
> general because it exposes the phase between PQconnectStart() and connection
> completion. It could also support other kinds of per-connection setup in the
> future, not just notice receiver installation. Thought?
>
> Regards,
>
> --
> Fujii Masao
The idea sounds good to me, so v2 is implemented following that idea.
A few things I want to point out abut v2:
* Since libpqsrv_connect_complete() would only wrap libpqsrv_connect_internal(), I just renamed libpqsrv_connect_internal() to libpqsrv_connect_complete().
* Since libpqsrv_connect() is now split into two phases, libpqsrv_connect_complete() must be called even if conn is NULL, because it may need to call ReleaseExternalFD(). I mentioned this in the header comment of libpqsrv_connect_start().
* In the postgres_fdw/connection.c change, I introduced a new local variable, start_conn, to keep the original logic unchanged. Because there is a PG_TRY/PG_CATCH block, conn is assigned only after libpqsrv_connect_complete() finishes successfully.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Attachments:
[application/octet-stream] v2-0001-Set-notice-receiver-before-libpq-connection-start.patch (10.2K, 2-v2-0001-Set-notice-receiver-before-libpq-connection-start.patch)
download | inline diff:
From f90baaca93f34cefbe829396fc532d7b04d35729 Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <lic@highgo.com>
Date: Wed, 20 May 2026 14:57:25 +0800
Subject: [PATCH v2] Set notice receiver before libpq connection startup
libpqsrv_connect() and libpqsrv_connect_params() complete the libpq
connection startup before returning the PGconn. As a result, callers that
install a notice receiver after these functions return can miss NOTICE,
WARNING, and similar messages emitted during connection establishment.
Split the connection helper API into start and complete steps, so callers
can install per-connection setup such as notice receivers after
PQconnectStart() returns but before the startup sequence is completed.
Document the resource-lifetime rule for the split API: callers must either
call libpqsrv_connect_complete(), even when the start function returns NULL,
or otherwise clean up the partially-started connection and release the
reserved external FD if an error is thrown before completion.
Author: Chao Li <lic@highgo.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/A2B8B7DE-C119-492F-A9FA-14CF86849777@gmail.com
---
contrib/dblink/dblink.c | 18 ++---
contrib/postgres_fdw/connection.c | 14 ++--
.../libpqwalreceiver/libpqwalreceiver.c | 13 ++--
src/include/libpq/libpq-be-fe-helpers.h | 69 +++++++++++++++----
4 files changed, 79 insertions(+), 35 deletions(-)
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index d843eee7e97..0751df9e444 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -222,7 +222,11 @@ dblink_get_conn(char *conname_or_str,
dblink_we_get_conn = WaitEventExtensionNew("DblinkGetConnect");
/* OK to make connection */
- conn = libpqsrv_connect(connstr, dblink_we_get_conn);
+ conn = libpqsrv_connect_start(connstr);
+ if (conn != NULL)
+ PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
+ "received message via remote connection");
+ libpqsrv_connect_complete(conn, dblink_we_get_conn);
if (PQstatus(conn) == CONNECTION_BAD)
{
@@ -235,9 +239,6 @@ dblink_get_conn(char *conname_or_str,
errdetail_internal("%s", msg)));
}
- PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
- "received message via remote connection");
-
dblink_security_check(conn, NULL, connstr);
if (PQclientEncoding(conn) != GetDatabaseEncoding())
PQsetClientEncoding(conn, GetDatabaseEncodingName());
@@ -321,7 +322,11 @@ dblink_connect(PG_FUNCTION_ARGS)
}
/* OK to make connection */
- conn = libpqsrv_connect(connstr, dblink_we_connect);
+ conn = libpqsrv_connect_start(connstr);
+ if (conn != NULL)
+ PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
+ "received message via remote connection");
+ libpqsrv_connect_complete(conn, dblink_we_connect);
if (PQstatus(conn) == CONNECTION_BAD)
{
@@ -336,9 +341,6 @@ dblink_connect(PG_FUNCTION_ARGS)
errdetail_internal("%s", msg)));
}
- PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
- "received message via remote connection");
-
/* check password actually used if not superuser */
dblink_security_check(conn, connname, connstr);
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 3d2a8d0519d..86178ec5fb2 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -638,6 +638,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
const char **keywords;
const char **values;
char *appname;
+ PGconn *start_conn;
construct_connection_params(server, user, &keywords, &values, &appname);
@@ -646,9 +647,13 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
pgfdw_we_connect = WaitEventExtensionNew("PostgresFdwConnect");
/* OK to make connection */
- conn = libpqsrv_connect_params(keywords, values,
- false, /* expand_dbname */
- pgfdw_we_connect);
+ start_conn = libpqsrv_connect_params_start(keywords, values,
+ /* expand_dbname = */ false);
+ if (start_conn != NULL)
+ PQsetNoticeReceiver(start_conn, libpqsrv_notice_receiver,
+ "received message via remote connection");
+ libpqsrv_connect_complete(start_conn, pgfdw_we_connect);
+ conn = start_conn;
if (!conn || PQstatus(conn) != CONNECTION_OK)
ereport(ERROR,
@@ -657,9 +662,6 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
server->servername),
errdetail_internal("%s", pchomp(PQerrorMessage(conn)))));
- PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
- "received message via remote connection");
-
/* Perform post-connection security checks. */
pgfdw_security_check(keywords, values, user, conn);
diff --git a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
index 9f04c9ed25d..af2734cb328 100644
--- a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
+++ b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
@@ -223,9 +223,13 @@ libpqrcv_connect(const char *conninfo, bool replication, bool logical,
conn = palloc0_object(WalReceiverConn);
conn->streamConn =
- libpqsrv_connect_params(keys, vals,
- /* expand_dbname = */ true,
- WAIT_EVENT_LIBPQWALRECEIVER_CONNECT);
+ libpqsrv_connect_params_start(keys, vals,
+ /* expand_dbname = */ true);
+ if (conn->streamConn != NULL)
+ PQsetNoticeReceiver(conn->streamConn, libpqsrv_notice_receiver,
+ "received message via replication");
+ libpqsrv_connect_complete(conn->streamConn,
+ WAIT_EVENT_LIBPQWALRECEIVER_CONNECT);
if (options_val != NULL)
pfree(options_val);
@@ -245,9 +249,6 @@ libpqrcv_connect(const char *conninfo, bool replication, bool logical,
errhint("Target server's authentication method must be changed, or set password_required=false in the subscription parameters.")));
}
- PQsetNoticeReceiver(conn->streamConn, libpqsrv_notice_receiver,
- "received message via replication");
-
/*
* Set always-secure search path for the cases where the connection is
* used to run SQL queries, so malicious users can't get control.
diff --git a/src/include/libpq/libpq-be-fe-helpers.h b/src/include/libpq/libpq-be-fe-helpers.h
index 85d8b63f019..cd540f82106 100644
--- a/src/include/libpq/libpq-be-fe-helpers.h
+++ b/src/include/libpq/libpq-be-fe-helpers.h
@@ -39,10 +39,32 @@
static inline void libpqsrv_connect_prepare(void);
-static inline void libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info);
+static inline void libpqsrv_connect_complete(PGconn *conn, uint32 wait_event_info);
static inline PGresult *libpqsrv_get_result_last(PGconn *conn, uint32 wait_event_info);
static inline PGresult *libpqsrv_get_result(PGconn *conn, uint32 wait_event_info);
+/*
+ * Start a connection using PQconnectStart().
+ *
+ * The returned connection has not yet completed its startup sequence. Callers
+ * may perform per-connection setup, such as installing a notice receiver,
+ * before calling libpqsrv_connect_complete().
+ *
+ * Callers must call libpqsrv_connect_complete(), even if this function returns
+ * NULL, because libpqsrv_connect_prepare() may already have reserved an
+ * external FD that must be released.
+ */
+static inline PGconn *
+libpqsrv_connect_start(const char *conninfo)
+{
+ PGconn *conn = NULL;
+
+ libpqsrv_connect_prepare();
+
+ conn = PQconnectStart(conninfo);
+
+ return conn;
+}
/*
* PQconnectdb() wrapper that reserves a file descriptor and processes
@@ -54,14 +76,31 @@ static inline PGresult *libpqsrv_get_result(PGconn *conn, uint32 wait_event_info
*/
static inline PGconn *
libpqsrv_connect(const char *conninfo, uint32 wait_event_info)
+{
+ PGconn *conn;
+
+ conn = libpqsrv_connect_start(conninfo);
+
+ libpqsrv_connect_complete(conn, wait_event_info);
+
+ return conn;
+}
+
+/*
+ * Start a connection using PQconnectStartParams().
+ *
+ * See libpqsrv_connect_start() for the resource-lifetime rules.
+ */
+static inline PGconn *
+libpqsrv_connect_params_start(const char *const *keywords,
+ const char *const *values,
+ int expand_dbname)
{
PGconn *conn = NULL;
libpqsrv_connect_prepare();
- conn = PQconnectStart(conninfo);
-
- libpqsrv_connect_internal(conn, wait_event_info);
+ conn = PQconnectStartParams(keywords, values, expand_dbname);
return conn;
}
@@ -76,13 +115,11 @@ libpqsrv_connect_params(const char *const *keywords,
int expand_dbname,
uint32 wait_event_info)
{
- PGconn *conn = NULL;
+ PGconn *conn;
- libpqsrv_connect_prepare();
-
- conn = PQconnectStartParams(keywords, values, expand_dbname);
+ conn = libpqsrv_connect_params_start(keywords, values, expand_dbname);
- libpqsrv_connect_internal(conn, wait_event_info);
+ libpqsrv_connect_complete(conn, wait_event_info);
return conn;
}
@@ -90,8 +127,9 @@ libpqsrv_connect_params(const char *const *keywords,
/*
* PQfinish() wrapper that additionally releases the reserved file descriptor.
*
- * It is allowed to call this with a NULL pgconn iff NULL was returned by
- * libpqsrv_connect*.
+ * It is allowed to call this with NULL only when the external FD reservation
+ * has already been released, for example after calling
+ * libpqsrv_connect_complete() with a NULL connection.
*/
static inline void
libpqsrv_disconnect(PGconn *conn)
@@ -101,7 +139,7 @@ libpqsrv_disconnect(PGconn *conn)
* already released it). This rule makes it easier to write PG_CATCH()
* handlers for this facility's users.
*
- * See also libpqsrv_connect_internal().
+ * See also libpqsrv_connect_complete().
*/
if (conn == NULL)
return;
@@ -111,7 +149,7 @@ libpqsrv_disconnect(PGconn *conn)
}
-/* internal helper functions follow */
+/* lower-level connection helper functions follow */
/*
@@ -144,10 +182,11 @@ libpqsrv_connect_prepare(void)
}
/*
- * Helper function for all connection establishment functions.
+ * Complete a connection started by libpqsrv_connect_start() or
+ * libpqsrv_connect_params_start().
*/
static inline void
-libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info)
+libpqsrv_connect_complete(PGconn *conn, uint32 wait_event_info)
{
/*
* With conn == NULL libpqsrv_disconnect() wouldn't release the FD. So do
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 10+ messages in thread
* Re: Set notice receiver before libpq connection startup
@ 2026-05-21 05:03 vignesh C <vignesh21@gmail.com>
parent: Chao Li <li.evan.chao@gmail.com>
0 siblings, 1 reply; 10+ messages in thread
From: vignesh C @ 2026-05-21 05:03 UTC (permalink / raw)
To: Chao Li <li.evan.chao@gmail.com>; +Cc: Fujii Masao <masao.fujii@gmail.com>; pgsql-hackers
On Thu, 21 May 2026 at 07:40, Chao Li <li.evan.chao@gmail.com> wrote:
>
>
>
> > On May 20, 2026, at 17:19, Fujii Masao <masao.fujii@gmail.com> wrote:
> >
> > On Wed, May 20, 2026 at 4:21 PM Chao Li <li.evan.chao@gmail.com> wrote:
> >>
> >> Hi,
> >>
> >> While testing “Log remote NOTICE, WARNING, and similar messages using ereport()”, I noticed that libpqsrv_notice_receiver is only installed after libpqsrv_connect() finishes. As a result, NOTICE messages generated during connection establishment are missed by ereport() and are still printed to stderr.
> >>
> >> To reproduce the issue, I created a separate database called remotedb and defined a login trigger that emits a NOTICE message:
> >> ```
> >> CREATE DATABASE remotedb;
> >>
> >> \c remotedb
> >>
> >> CREATE OR REPLACE FUNCTION repro_login_notice()
> >> RETURNS event_trigger
> >> LANGUAGE plpgsql AS $$
> >> BEGIN
> >> RAISE NOTICE 'startup notice from remotedb login trigger';
> >> END;
> >> $$;
> >>
> >> CREATE EVENT TRIGGER repro_login_notice_trg
> >> ON login
> >> EXECUTE FUNCTION repro_login_notice();
> >>
> >> ALTER EVENT TRIGGER repro_login_notice_trg ENABLE ALWAYS;
> >> ```
> >>
> >> Then, from another database:
> >> ```
> >> evantest=# create extension dblink;
> >> CREATE EXTENSION
> >> evantest=# SELECT dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
> >> dblink_connect
> >> ----------------
> >> OK
> >> (1 row)
> >> ```
> >>
> >> In the system log, the NOTICE message is printed directly:
> >> ```
> >> 2026-05-20 13:02:19.350 CST [24909] STATEMENT: SELECT dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
> >> NOTICE: startup notice from remotedb login trigger
> >> ```
> >>
> >> To fix that, I think we should install libpqsrv_notice_receiver before libpqsrv_connect_internal(). In the attached patch, I added two helpers: libpqsrv_connect_with_notice_receiver() and libpqsrv_connect_params_with_notice_receiver().
> >>
> >> With the fix, the NOTICE message now looks like this:
> >> ```
> >> 2026-05-20 14:44:49.296 CST [45567] LOG: received message via remote connection: NOTICE: startup notice from remotedb login trigger
> >> 2026-05-20 14:44:49.296 CST [45567] STATEMENT: SELECT dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
> >> ```
> >>
> >> Please see the attached patch for details.
> >
> > Thanks for the report and patch!
> >
> > I'd prefer to avoid adding notice-receiver-specific wrappers such as
> > libpqsrv_connect_with_notice_receiver(). Instead, how about splitting
> > libpqsrv_connect() into two steps: libpqsrv_connect_start(), which performs
> > libpqsrv_connect_prepare() and PQconnectStart(), and
> > libpqsrv_connect_complete(), which runs libpqsrv_connect_internal()?
> >
> > With this approach, callers could invoke PQsetNoticeReceiver() after
> > libpqsrv_connect_start() returns but before libpqsrv_connect_complete() is
> > called. This would allow startup-time notices to be handled correctly without
> > introducing a dedicated wrapper function.
> >
> > Compared to the *_with_notice_receiver() approach, this design is more
> > general because it exposes the phase between PQconnectStart() and connection
> > completion. It could also support other kinds of per-connection setup in the
> > future, not just notice receiver installation. Thought?
> >
> > Regards,
> >
> > --
> > Fujii Masao
>
> The idea sounds good to me, so v2 is implemented following that idea.
>
> A few things I want to point out abut v2:
>
> * Since libpqsrv_connect_complete() would only wrap libpqsrv_connect_internal(), I just renamed libpqsrv_connect_internal() to libpqsrv_connect_complete().
> * Since libpqsrv_connect() is now split into two phases, libpqsrv_connect_complete() must be called even if conn is NULL, because it may need to call ReleaseExternalFD(). I mentioned this in the header comment of libpqsrv_connect_start().
> * In the postgres_fdw/connection.c change, I introduced a new local variable, start_conn, to keep the original logic unchanged. Because there is a PG_TRY/PG_CATCH block, conn is assigned only after libpqsrv_connect_complete() finishes successfully.
Thanks for reporting this issue. I was able to reproduce the issue
with the steps provided and your patch fixes the issue.
Few comments:
1) No need of conn variable here, we can directly return
PQconnectStart(conninfo) in this function:
+static inline PGconn *
+libpqsrv_connect_start(const char *conninfo)
+{
+ PGconn *conn = NULL;
+
+ libpqsrv_connect_prepare();
+
+ conn = PQconnectStart(conninfo);
+
+ return conn;
+}
2) Similarly here too:
+static inline PGconn *
+libpqsrv_connect_params_start(const char *const *keywords,
+ const char *const *values,
+ int expand_dbname)
{
PGconn *conn = NULL;
libpqsrv_connect_prepare();
- conn = PQconnectStart(conninfo);
-
- libpqsrv_connect_internal(conn, wait_event_info);
+ conn = PQconnectStartParams(keywords, values, expand_dbname);
return conn;
}
Regards,
Vignesh
^ permalink raw reply [nested|flat] 10+ messages in thread
* Re: Set notice receiver before libpq connection startup
@ 2026-05-21 07:26 Chao Li <li.evan.chao@gmail.com>
parent: vignesh C <vignesh21@gmail.com>
0 siblings, 1 reply; 10+ messages in thread
From: Chao Li @ 2026-05-21 07:26 UTC (permalink / raw)
To: vignesh C <vignesh21@gmail.com>; +Cc: Fujii Masao <masao.fujii@gmail.com>; pgsql-hackers
> On May 21, 2026, at 13:03, vignesh C <vignesh21@gmail.com> wrote:
>
> On Thu, 21 May 2026 at 07:40, Chao Li <li.evan.chao@gmail.com> wrote:
>>
>>
>>
>>> On May 20, 2026, at 17:19, Fujii Masao <masao.fujii@gmail.com> wrote:
>>>
>>> On Wed, May 20, 2026 at 4:21 PM Chao Li <li.evan.chao@gmail.com> wrote:
>>>>
>>>> Hi,
>>>>
>>>> While testing “Log remote NOTICE, WARNING, and similar messages using ereport()”, I noticed that libpqsrv_notice_receiver is only installed after libpqsrv_connect() finishes. As a result, NOTICE messages generated during connection establishment are missed by ereport() and are still printed to stderr.
>>>>
>>>> To reproduce the issue, I created a separate database called remotedb and defined a login trigger that emits a NOTICE message:
>>>> ```
>>>> CREATE DATABASE remotedb;
>>>>
>>>> \c remotedb
>>>>
>>>> CREATE OR REPLACE FUNCTION repro_login_notice()
>>>> RETURNS event_trigger
>>>> LANGUAGE plpgsql AS $$
>>>> BEGIN
>>>> RAISE NOTICE 'startup notice from remotedb login trigger';
>>>> END;
>>>> $$;
>>>>
>>>> CREATE EVENT TRIGGER repro_login_notice_trg
>>>> ON login
>>>> EXECUTE FUNCTION repro_login_notice();
>>>>
>>>> ALTER EVENT TRIGGER repro_login_notice_trg ENABLE ALWAYS;
>>>> ```
>>>>
>>>> Then, from another database:
>>>> ```
>>>> evantest=# create extension dblink;
>>>> CREATE EXTENSION
>>>> evantest=# SELECT dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
>>>> dblink_connect
>>>> ----------------
>>>> OK
>>>> (1 row)
>>>> ```
>>>>
>>>> In the system log, the NOTICE message is printed directly:
>>>> ```
>>>> 2026-05-20 13:02:19.350 CST [24909] STATEMENT: SELECT dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
>>>> NOTICE: startup notice from remotedb login trigger
>>>> ```
>>>>
>>>> To fix that, I think we should install libpqsrv_notice_receiver before libpqsrv_connect_internal(). In the attached patch, I added two helpers: libpqsrv_connect_with_notice_receiver() and libpqsrv_connect_params_with_notice_receiver().
>>>>
>>>> With the fix, the NOTICE message now looks like this:
>>>> ```
>>>> 2026-05-20 14:44:49.296 CST [45567] LOG: received message via remote connection: NOTICE: startup notice from remotedb login trigger
>>>> 2026-05-20 14:44:49.296 CST [45567] STATEMENT: SELECT dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
>>>> ```
>>>>
>>>> Please see the attached patch for details.
>>>
>>> Thanks for the report and patch!
>>>
>>> I'd prefer to avoid adding notice-receiver-specific wrappers such as
>>> libpqsrv_connect_with_notice_receiver(). Instead, how about splitting
>>> libpqsrv_connect() into two steps: libpqsrv_connect_start(), which performs
>>> libpqsrv_connect_prepare() and PQconnectStart(), and
>>> libpqsrv_connect_complete(), which runs libpqsrv_connect_internal()?
>>>
>>> With this approach, callers could invoke PQsetNoticeReceiver() after
>>> libpqsrv_connect_start() returns but before libpqsrv_connect_complete() is
>>> called. This would allow startup-time notices to be handled correctly without
>>> introducing a dedicated wrapper function.
>>>
>>> Compared to the *_with_notice_receiver() approach, this design is more
>>> general because it exposes the phase between PQconnectStart() and connection
>>> completion. It could also support other kinds of per-connection setup in the
>>> future, not just notice receiver installation. Thought?
>>>
>>> Regards,
>>>
>>> --
>>> Fujii Masao
>>
>> The idea sounds good to me, so v2 is implemented following that idea.
>>
>> A few things I want to point out abut v2:
>>
>> * Since libpqsrv_connect_complete() would only wrap libpqsrv_connect_internal(), I just renamed libpqsrv_connect_internal() to libpqsrv_connect_complete().
>> * Since libpqsrv_connect() is now split into two phases, libpqsrv_connect_complete() must be called even if conn is NULL, because it may need to call ReleaseExternalFD(). I mentioned this in the header comment of libpqsrv_connect_start().
>> * In the postgres_fdw/connection.c change, I introduced a new local variable, start_conn, to keep the original logic unchanged. Because there is a PG_TRY/PG_CATCH block, conn is assigned only after libpqsrv_connect_complete() finishes successfully.
>
> Thanks for reporting this issue. I was able to reproduce the issue
> with the steps provided and your patch fixes the issue.
> Few comments:
> 1) No need of conn variable here, we can directly return
> PQconnectStart(conninfo) in this function:
> +static inline PGconn *
> +libpqsrv_connect_start(const char *conninfo)
> +{
> + PGconn *conn = NULL;
> +
> + libpqsrv_connect_prepare();
> +
> + conn = PQconnectStart(conninfo);
> +
> + return conn;
> +}
>
> 2) Similarly here too:
> +static inline PGconn *
> +libpqsrv_connect_params_start(const char *const *keywords,
> + const char *const *values,
> + int expand_dbname)
> {
> PGconn *conn = NULL;
>
> libpqsrv_connect_prepare();
>
> - conn = PQconnectStart(conninfo);
> -
> - libpqsrv_connect_internal(conn, wait_event_info);
> + conn = PQconnectStartParams(keywords, values, expand_dbname);
>
> return conn;
> }
>
> Regards,
> Vignesh
Thanks for your comment. Addressed in v3.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Attachments:
[application/octet-stream] v3-0001-Set-notice-receiver-before-libpq-connection-start.patch (10.3K, 2-v3-0001-Set-notice-receiver-before-libpq-connection-start.patch)
download | inline diff:
From 8549b8bfbaa48a954687c12d732f4bb4443a4d85 Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <lic@highgo.com>
Date: Wed, 20 May 2026 14:57:25 +0800
Subject: [PATCH v3] Set notice receiver before libpq connection startup
libpqsrv_connect() and libpqsrv_connect_params() complete the libpq
connection startup before returning the PGconn. As a result, callers that
install a notice receiver after these functions return can miss NOTICE,
WARNING, and similar messages emitted during connection establishment.
Split the connection helper API into start and complete steps, so callers
can install per-connection setup such as notice receivers after
PQconnectStart() returns but before the startup sequence is completed.
Document the resource-lifetime rule for the split API: callers must either
call libpqsrv_connect_complete(), even when the start function returns NULL,
or otherwise clean up the partially-started connection and release the
reserved external FD if an error is thrown before completion.
Author: Chao Li <lic@highgo.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Vignesh C <vignesh21@gmail.com>
Discussion: https://postgr.es/m/A2B8B7DE-C119-492F-A9FA-14CF86849777@gmail.com
---
contrib/dblink/dblink.c | 18 ++---
contrib/postgres_fdw/connection.c | 14 ++--
.../libpqwalreceiver/libpqwalreceiver.c | 13 ++--
src/include/libpq/libpq-be-fe-helpers.h | 65 ++++++++++++++-----
4 files changed, 73 insertions(+), 37 deletions(-)
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index d843eee7e97..0751df9e444 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -222,7 +222,11 @@ dblink_get_conn(char *conname_or_str,
dblink_we_get_conn = WaitEventExtensionNew("DblinkGetConnect");
/* OK to make connection */
- conn = libpqsrv_connect(connstr, dblink_we_get_conn);
+ conn = libpqsrv_connect_start(connstr);
+ if (conn != NULL)
+ PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
+ "received message via remote connection");
+ libpqsrv_connect_complete(conn, dblink_we_get_conn);
if (PQstatus(conn) == CONNECTION_BAD)
{
@@ -235,9 +239,6 @@ dblink_get_conn(char *conname_or_str,
errdetail_internal("%s", msg)));
}
- PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
- "received message via remote connection");
-
dblink_security_check(conn, NULL, connstr);
if (PQclientEncoding(conn) != GetDatabaseEncoding())
PQsetClientEncoding(conn, GetDatabaseEncodingName());
@@ -321,7 +322,11 @@ dblink_connect(PG_FUNCTION_ARGS)
}
/* OK to make connection */
- conn = libpqsrv_connect(connstr, dblink_we_connect);
+ conn = libpqsrv_connect_start(connstr);
+ if (conn != NULL)
+ PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
+ "received message via remote connection");
+ libpqsrv_connect_complete(conn, dblink_we_connect);
if (PQstatus(conn) == CONNECTION_BAD)
{
@@ -336,9 +341,6 @@ dblink_connect(PG_FUNCTION_ARGS)
errdetail_internal("%s", msg)));
}
- PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
- "received message via remote connection");
-
/* check password actually used if not superuser */
dblink_security_check(conn, connname, connstr);
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 3d2a8d0519d..86178ec5fb2 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -638,6 +638,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
const char **keywords;
const char **values;
char *appname;
+ PGconn *start_conn;
construct_connection_params(server, user, &keywords, &values, &appname);
@@ -646,9 +647,13 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
pgfdw_we_connect = WaitEventExtensionNew("PostgresFdwConnect");
/* OK to make connection */
- conn = libpqsrv_connect_params(keywords, values,
- false, /* expand_dbname */
- pgfdw_we_connect);
+ start_conn = libpqsrv_connect_params_start(keywords, values,
+ /* expand_dbname = */ false);
+ if (start_conn != NULL)
+ PQsetNoticeReceiver(start_conn, libpqsrv_notice_receiver,
+ "received message via remote connection");
+ libpqsrv_connect_complete(start_conn, pgfdw_we_connect);
+ conn = start_conn;
if (!conn || PQstatus(conn) != CONNECTION_OK)
ereport(ERROR,
@@ -657,9 +662,6 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
server->servername),
errdetail_internal("%s", pchomp(PQerrorMessage(conn)))));
- PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
- "received message via remote connection");
-
/* Perform post-connection security checks. */
pgfdw_security_check(keywords, values, user, conn);
diff --git a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
index 9f04c9ed25d..af2734cb328 100644
--- a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
+++ b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
@@ -223,9 +223,13 @@ libpqrcv_connect(const char *conninfo, bool replication, bool logical,
conn = palloc0_object(WalReceiverConn);
conn->streamConn =
- libpqsrv_connect_params(keys, vals,
- /* expand_dbname = */ true,
- WAIT_EVENT_LIBPQWALRECEIVER_CONNECT);
+ libpqsrv_connect_params_start(keys, vals,
+ /* expand_dbname = */ true);
+ if (conn->streamConn != NULL)
+ PQsetNoticeReceiver(conn->streamConn, libpqsrv_notice_receiver,
+ "received message via replication");
+ libpqsrv_connect_complete(conn->streamConn,
+ WAIT_EVENT_LIBPQWALRECEIVER_CONNECT);
if (options_val != NULL)
pfree(options_val);
@@ -245,9 +249,6 @@ libpqrcv_connect(const char *conninfo, bool replication, bool logical,
errhint("Target server's authentication method must be changed, or set password_required=false in the subscription parameters.")));
}
- PQsetNoticeReceiver(conn->streamConn, libpqsrv_notice_receiver,
- "received message via replication");
-
/*
* Set always-secure search path for the cases where the connection is
* used to run SQL queries, so malicious users can't get control.
diff --git a/src/include/libpq/libpq-be-fe-helpers.h b/src/include/libpq/libpq-be-fe-helpers.h
index 85d8b63f019..cff68cd1c37 100644
--- a/src/include/libpq/libpq-be-fe-helpers.h
+++ b/src/include/libpq/libpq-be-fe-helpers.h
@@ -39,10 +39,28 @@
static inline void libpqsrv_connect_prepare(void);
-static inline void libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info);
+static inline void libpqsrv_connect_complete(PGconn *conn, uint32 wait_event_info);
static inline PGresult *libpqsrv_get_result_last(PGconn *conn, uint32 wait_event_info);
static inline PGresult *libpqsrv_get_result(PGconn *conn, uint32 wait_event_info);
+/*
+ * Start a connection using PQconnectStart().
+ *
+ * The returned connection has not yet completed its startup sequence. Callers
+ * may perform per-connection setup, such as installing a notice receiver,
+ * before calling libpqsrv_connect_complete().
+ *
+ * Callers must call libpqsrv_connect_complete(), even if this function returns
+ * NULL, because libpqsrv_connect_prepare() may already have reserved an
+ * external FD that must be released.
+ */
+static inline PGconn *
+libpqsrv_connect_start(const char *conninfo)
+{
+ libpqsrv_connect_prepare();
+
+ return PQconnectStart(conninfo);
+}
/*
* PQconnectdb() wrapper that reserves a file descriptor and processes
@@ -55,17 +73,30 @@ static inline PGresult *libpqsrv_get_result(PGconn *conn, uint32 wait_event_info
static inline PGconn *
libpqsrv_connect(const char *conninfo, uint32 wait_event_info)
{
- PGconn *conn = NULL;
+ PGconn *conn;
- libpqsrv_connect_prepare();
-
- conn = PQconnectStart(conninfo);
+ conn = libpqsrv_connect_start(conninfo);
- libpqsrv_connect_internal(conn, wait_event_info);
+ libpqsrv_connect_complete(conn, wait_event_info);
return conn;
}
+/*
+ * Start a connection using PQconnectStartParams().
+ *
+ * See libpqsrv_connect_start() for the resource-lifetime rules.
+ */
+static inline PGconn *
+libpqsrv_connect_params_start(const char *const *keywords,
+ const char *const *values,
+ int expand_dbname)
+{
+ libpqsrv_connect_prepare();
+
+ return PQconnectStartParams(keywords, values, expand_dbname);
+}
+
/*
* Like libpqsrv_connect(), except that this is a wrapper for
* PQconnectdbParams().
@@ -76,13 +107,11 @@ libpqsrv_connect_params(const char *const *keywords,
int expand_dbname,
uint32 wait_event_info)
{
- PGconn *conn = NULL;
+ PGconn *conn;
- libpqsrv_connect_prepare();
+ conn = libpqsrv_connect_params_start(keywords, values, expand_dbname);
- conn = PQconnectStartParams(keywords, values, expand_dbname);
-
- libpqsrv_connect_internal(conn, wait_event_info);
+ libpqsrv_connect_complete(conn, wait_event_info);
return conn;
}
@@ -90,8 +119,9 @@ libpqsrv_connect_params(const char *const *keywords,
/*
* PQfinish() wrapper that additionally releases the reserved file descriptor.
*
- * It is allowed to call this with a NULL pgconn iff NULL was returned by
- * libpqsrv_connect*.
+ * It is allowed to call this with NULL only when the external FD reservation
+ * has already been released, for example after calling
+ * libpqsrv_connect_complete() with a NULL connection.
*/
static inline void
libpqsrv_disconnect(PGconn *conn)
@@ -101,7 +131,7 @@ libpqsrv_disconnect(PGconn *conn)
* already released it). This rule makes it easier to write PG_CATCH()
* handlers for this facility's users.
*
- * See also libpqsrv_connect_internal().
+ * See also libpqsrv_connect_complete().
*/
if (conn == NULL)
return;
@@ -111,7 +141,7 @@ libpqsrv_disconnect(PGconn *conn)
}
-/* internal helper functions follow */
+/* lower-level connection helper functions follow */
/*
@@ -144,10 +174,11 @@ libpqsrv_connect_prepare(void)
}
/*
- * Helper function for all connection establishment functions.
+ * Complete a connection started by libpqsrv_connect_start() or
+ * libpqsrv_connect_params_start().
*/
static inline void
-libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info)
+libpqsrv_connect_complete(PGconn *conn, uint32 wait_event_info)
{
/*
* With conn == NULL libpqsrv_disconnect() wouldn't release the FD. So do
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 10+ messages in thread
* Re: Set notice receiver before libpq connection startup
@ 2026-05-22 11:33 Rafia Sabih <rafia.pghackers@gmail.com>
parent: Chao Li <li.evan.chao@gmail.com>
0 siblings, 1 reply; 10+ messages in thread
From: Rafia Sabih @ 2026-05-22 11:33 UTC (permalink / raw)
To: Chao Li <li.evan.chao@gmail.com>; +Cc: vignesh C <vignesh21@gmail.com>; Fujii Masao <masao.fujii@gmail.com>; pgsql-hackers
On Thu, 21 May 2026 at 09:27, Chao Li <li.evan.chao@gmail.com> wrote:
>
>
> > On May 21, 2026, at 13:03, vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Thu, 21 May 2026 at 07:40, Chao Li <li.evan.chao@gmail.com> wrote:
> >>
> >>
> >>
> >>> On May 20, 2026, at 17:19, Fujii Masao <masao.fujii@gmail.com> wrote:
> >>>
> >>> On Wed, May 20, 2026 at 4:21 PM Chao Li <li.evan.chao@gmail.com>
> wrote:
> >>>>
> >>>> Hi,
> >>>>
> >>>> While testing “Log remote NOTICE, WARNING, and similar messages using
> ereport()”, I noticed that libpqsrv_notice_receiver is only installed after
> libpqsrv_connect() finishes. As a result, NOTICE messages generated during
> connection establishment are missed by ereport() and are still printed to
> stderr.
> >>>>
> >>>> To reproduce the issue, I created a separate database called remotedb
> and defined a login trigger that emits a NOTICE message:
> >>>> ```
> >>>> CREATE DATABASE remotedb;
> >>>>
> >>>> \c remotedb
> >>>>
> >>>> CREATE OR REPLACE FUNCTION repro_login_notice()
> >>>> RETURNS event_trigger
> >>>> LANGUAGE plpgsql AS $$
> >>>> BEGIN
> >>>> RAISE NOTICE 'startup notice from remotedb login trigger';
> >>>> END;
> >>>> $$;
> >>>>
> >>>> CREATE EVENT TRIGGER repro_login_notice_trg
> >>>> ON login
> >>>> EXECUTE FUNCTION repro_login_notice();
> >>>>
> >>>> ALTER EVENT TRIGGER repro_login_notice_trg ENABLE ALWAYS;
> >>>> ```
> >>>>
> >>>> Then, from another database:
> >>>> ```
> >>>> evantest=# create extension dblink;
> >>>> CREATE EXTENSION
> >>>> evantest=# SELECT dblink_connect('host=127.0.0.1 port=5432
> dbname=remotedb user=chaol sslmode=disable gssencmode=disable');
> >>>> dblink_connect
> >>>> ----------------
> >>>> OK
> >>>> (1 row)
> >>>> ```
> >>>>
> >>>> In the system log, the NOTICE message is printed directly:
> >>>> ```
> >>>> 2026-05-20 13:02:19.350 CST [24909] STATEMENT: SELECT
> dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol
> sslmode=disable gssencmode=disable');
> >>>> NOTICE: startup notice from remotedb login trigger
> >>>> ```
> >>>>
> >>>> To fix that, I think we should install libpqsrv_notice_receiver
> before libpqsrv_connect_internal(). In the attached patch, I added two
> helpers: libpqsrv_connect_with_notice_receiver() and
> libpqsrv_connect_params_with_notice_receiver().
> >>>>
> >>>> With the fix, the NOTICE message now looks like this:
> >>>> ```
> >>>> 2026-05-20 14:44:49.296 CST [45567] LOG: received message via remote
> connection: NOTICE: startup notice from remotedb login trigger
> >>>> 2026-05-20 14:44:49.296 CST [45567] STATEMENT: SELECT
> dblink_connect('host=127.0.0.1 port=5432 dbname=remotedb user=chaol
> sslmode=disable gssencmode=disable');
> >>>> ```
> >>>>
> >>>> Please see the attached patch for details.
> >>>
> >>> Thanks for the report and patch!
> >>>
> >>> I'd prefer to avoid adding notice-receiver-specific wrappers such as
> >>> libpqsrv_connect_with_notice_receiver(). Instead, how about splitting
> >>> libpqsrv_connect() into two steps: libpqsrv_connect_start(), which
> performs
> >>> libpqsrv_connect_prepare() and PQconnectStart(), and
> >>> libpqsrv_connect_complete(), which runs libpqsrv_connect_internal()?
> >>>
> >>> With this approach, callers could invoke PQsetNoticeReceiver() after
> >>> libpqsrv_connect_start() returns but before
> libpqsrv_connect_complete() is
> >>> called. This would allow startup-time notices to be handled correctly
> without
> >>> introducing a dedicated wrapper function.
> >>>
> >>> Compared to the *_with_notice_receiver() approach, this design is more
> >>> general because it exposes the phase between PQconnectStart() and
> connection
> >>> completion. It could also support other kinds of per-connection setup
> in the
> >>> future, not just notice receiver installation. Thought?
> >>>
> >>> Regards,
> >>>
> >>> --
> >>> Fujii Masao
> >>
> >> The idea sounds good to me, so v2 is implemented following that idea.
> >>
> >> A few things I want to point out abut v2:
> >>
> >> * Since libpqsrv_connect_complete() would only wrap
> libpqsrv_connect_internal(), I just renamed libpqsrv_connect_internal() to
> libpqsrv_connect_complete().
> >> * Since libpqsrv_connect() is now split into two phases,
> libpqsrv_connect_complete() must be called even if conn is NULL, because it
> may need to call ReleaseExternalFD(). I mentioned this in the header
> comment of libpqsrv_connect_start().
> >> * In the postgres_fdw/connection.c change, I introduced a new local
> variable, start_conn, to keep the original logic unchanged. Because there
> is a PG_TRY/PG_CATCH block, conn is assigned only after
> libpqsrv_connect_complete() finishes successfully.
> >
> > Thanks for reporting this issue. I was able to reproduce the issue
> > with the steps provided and your patch fixes the issue.
> > Few comments:
> > 1) No need of conn variable here, we can directly return
> > PQconnectStart(conninfo) in this function:
> > +static inline PGconn *
> > +libpqsrv_connect_start(const char *conninfo)
> > +{
> > + PGconn *conn = NULL;
> > +
> > + libpqsrv_connect_prepare();
> > +
> > + conn = PQconnectStart(conninfo);
> > +
> > + return conn;
> > +}
> >
> > 2) Similarly here too:
> > +static inline PGconn *
> > +libpqsrv_connect_params_start(const char *const *keywords,
> > + const char *const *values,
> > + int expand_dbname)
> > {
> > PGconn *conn = NULL;
> >
> > libpqsrv_connect_prepare();
> >
> > - conn = PQconnectStart(conninfo);
> > -
> > - libpqsrv_connect_internal(conn, wait_event_info);
> > + conn = PQconnectStartParams(keywords, values, expand_dbname);
> >
> > return conn;
> > }
> >
> > Regards,
> > Vignesh
>
> Thanks for your comment. Addressed in v3.
>
> Here are my two cents,
we need not to check conn is null here
+ conn = libpqsrv_connect_start(connstr);
+ if (conn != NULL)
+ PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
+ "received message via remote connection");
because it is done so in PQsetNoticeReceiver anyway. Also, since there is
no else here so it doesn't make sense more, because if it is null then also
we will just continue with the next function call.
Another point is, in pg_connect_server I don't get the value of adding
another PGConn variable start_conn, can't we use conn itself...?
I hope this helps.
--
Regards,
Rafia Sabih
CYBERTEC PostgreSQL International GmbH
^ permalink raw reply [nested|flat] 10+ messages in thread
* Re: Set notice receiver before libpq connection startup
@ 2026-05-22 13:42 Fujii Masao <masao.fujii@gmail.com>
parent: Rafia Sabih <rafia.pghackers@gmail.com>
0 siblings, 1 reply; 10+ messages in thread
From: Fujii Masao @ 2026-05-22 13:42 UTC (permalink / raw)
To: Rafia Sabih <rafia.pghackers@gmail.com>; +Cc: Chao Li <li.evan.chao@gmail.com>; vignesh C <vignesh21@gmail.com>; pgsql-hackers
On Fri, May 22, 2026 at 8:33 PM Rafia Sabih <rafia.pghackers@gmail.com> wrote:
> Here are my two cents,
> we need not to check conn is null here
> + conn = libpqsrv_connect_start(connstr);
> + if (conn != NULL)
> + PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
> + "received message via remote connection");
> because it is done so in PQsetNoticeReceiver anyway. Also, since there is no else here so it doesn't make sense more, because if it is null then also we will just continue with the next function call.
Yes, but I'm fine with the current code in the patch. That code makes
the intent explicit, i.e., install the notice receiver only when a connection
object actually exists. That said, I'm also OK with simply calling
PQsetNoticeReceiver() without that check.
> Another point is, in pg_connect_server I don't get the value of adding another PGConn variable start_conn, can't we use conn itself...?
> I hope this helps.
Not only connect_pg_server() but libpqsrv_connect_complete() has
a PG_TRY/PG_CATCH block. So if start_conn were not used, an error thrown
in libpqsrv_connect_complete() could cause the current connection (conn) to
be cleaned up twice unexpectedly: once in libpqsrv_connect_complete() and
again in connect_pg_server(). I guess that's why Chao introduced start_conn.
Regards,
--
Fujii Masao
^ permalink raw reply [nested|flat] 10+ messages in thread
* Re: Set notice receiver before libpq connection startup
@ 2026-05-22 14:55 Chao Li <li.evan.chao@gmail.com>
parent: Fujii Masao <masao.fujii@gmail.com>
0 siblings, 1 reply; 10+ messages in thread
From: Chao Li @ 2026-05-22 14:55 UTC (permalink / raw)
To: Fujii Masao <masao.fujii@gmail.com>; +Cc: Rafia Sabih <rafia.pghackers@gmail.com>; vignesh C <vignesh21@gmail.com>; pgsql-hackers
> On May 22, 2026, at 21:42, Fujii Masao <masao.fujii@gmail.com> wrote:
>
> On Fri, May 22, 2026 at 8:33 PM Rafia Sabih <rafia.pghackers@gmail.com> wrote:
>> Here are my two cents,
>> we need not to check conn is null here
>> + conn = libpqsrv_connect_start(connstr);
>> + if (conn != NULL)
>> + PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
>> + "received message via remote connection");
>> because it is done so in PQsetNoticeReceiver anyway. Also, since there is no else here so it doesn't make sense more, because if it is null then also we will just continue with the next function call.
>
> Yes, but I'm fine with the current code in the patch. That code makes
> the intent explicit, i.e., install the notice receiver only when a connection
> object actually exists. That said, I'm also OK with simply calling
> PQsetNoticeReceiver() without that check.
>
Every PG**() function checks if conn is NULL, so I am okay to remove the check.
>
>> Another point is, in pg_connect_server I don't get the value of adding another PGConn variable start_conn, can't we use conn itself...?
>> I hope this helps.
>
> Not only connect_pg_server() but libpqsrv_connect_complete() has
> a PG_TRY/PG_CATCH block. So if start_conn were not used, an error thrown
> in libpqsrv_connect_complete() could cause the current connection (conn) to
> be cleaned up twice unexpectedly: once in libpqsrv_connect_complete() and
> again in connect_pg_server(). I guess that's why Chao introduced start_conn.
>
Exactly. With introducing start_conn, when libpqsrv_connect_complete() raises an error, conn is still NULL, so that PG_CATCH clause won’t cleanup conn, which keeps the same behavior as the old code.
PFA v4, just removed conn NULL check.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Attachments:
[application/octet-stream] v4-0001-Set-notice-receiver-before-libpq-connection-start.patch (10.2K, 2-v4-0001-Set-notice-receiver-before-libpq-connection-start.patch)
download | inline diff:
From 61d2a96f062473be47bd267f50ba71d5b85d1a71 Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <lic@highgo.com>
Date: Wed, 20 May 2026 14:57:25 +0800
Subject: [PATCH v4] Set notice receiver before libpq connection startup
libpqsrv_connect() and libpqsrv_connect_params() complete the libpq
connection startup before returning the PGconn. As a result, callers that
install a notice receiver after these functions return can miss NOTICE,
WARNING, and similar messages emitted during connection establishment.
Split the connection helper API into start and complete steps, so callers
can install per-connection setup such as notice receivers after
PQconnectStart() returns but before the startup sequence is completed.
Document the resource-lifetime rule for the split API: callers must either
call libpqsrv_connect_complete(), even when the start function returns NULL,
or otherwise clean up the partially-started connection and release the
reserved external FD if an error is thrown before completion.
Author: Chao Li <lic@highgo.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Vignesh C <vignesh21@gmail.com>
Reviewed-by: Rafia Sabih <rafia.pghackers@gmail.com>
Discussion: https://postgr.es/m/A2B8B7DE-C119-492F-A9FA-14CF86849777@gmail.com
---
contrib/dblink/dblink.c | 17 ++---
contrib/postgres_fdw/connection.c | 13 ++--
.../libpqwalreceiver/libpqwalreceiver.c | 12 ++--
src/include/libpq/libpq-be-fe-helpers.h | 65 ++++++++++++++-----
4 files changed, 70 insertions(+), 37 deletions(-)
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index d843eee7e97..3ac47ce3e0e 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -222,7 +222,10 @@ dblink_get_conn(char *conname_or_str,
dblink_we_get_conn = WaitEventExtensionNew("DblinkGetConnect");
/* OK to make connection */
- conn = libpqsrv_connect(connstr, dblink_we_get_conn);
+ conn = libpqsrv_connect_start(connstr);
+ PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
+ "received message via remote connection");
+ libpqsrv_connect_complete(conn, dblink_we_get_conn);
if (PQstatus(conn) == CONNECTION_BAD)
{
@@ -235,9 +238,6 @@ dblink_get_conn(char *conname_or_str,
errdetail_internal("%s", msg)));
}
- PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
- "received message via remote connection");
-
dblink_security_check(conn, NULL, connstr);
if (PQclientEncoding(conn) != GetDatabaseEncoding())
PQsetClientEncoding(conn, GetDatabaseEncodingName());
@@ -321,7 +321,11 @@ dblink_connect(PG_FUNCTION_ARGS)
}
/* OK to make connection */
- conn = libpqsrv_connect(connstr, dblink_we_connect);
+ conn = libpqsrv_connect_start(connstr);
+ if (conn != NULL)
+ PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
+ "received message via remote connection");
+ libpqsrv_connect_complete(conn, dblink_we_connect);
if (PQstatus(conn) == CONNECTION_BAD)
{
@@ -336,9 +340,6 @@ dblink_connect(PG_FUNCTION_ARGS)
errdetail_internal("%s", msg)));
}
- PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
- "received message via remote connection");
-
/* check password actually used if not superuser */
dblink_security_check(conn, connname, connstr);
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 3d2a8d0519d..c3a1c5f46ca 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -638,6 +638,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
const char **keywords;
const char **values;
char *appname;
+ PGconn *start_conn;
construct_connection_params(server, user, &keywords, &values, &appname);
@@ -646,9 +647,12 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
pgfdw_we_connect = WaitEventExtensionNew("PostgresFdwConnect");
/* OK to make connection */
- conn = libpqsrv_connect_params(keywords, values,
- false, /* expand_dbname */
- pgfdw_we_connect);
+ start_conn = libpqsrv_connect_params_start(keywords, values,
+ /* expand_dbname = */ false);
+ PQsetNoticeReceiver(start_conn, libpqsrv_notice_receiver,
+ "received message via remote connection");
+ libpqsrv_connect_complete(start_conn, pgfdw_we_connect);
+ conn = start_conn;
if (!conn || PQstatus(conn) != CONNECTION_OK)
ereport(ERROR,
@@ -657,9 +661,6 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
server->servername),
errdetail_internal("%s", pchomp(PQerrorMessage(conn)))));
- PQsetNoticeReceiver(conn, libpqsrv_notice_receiver,
- "received message via remote connection");
-
/* Perform post-connection security checks. */
pgfdw_security_check(keywords, values, user, conn);
diff --git a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
index 9f04c9ed25d..ebfd64bdf05 100644
--- a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
+++ b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
@@ -223,9 +223,12 @@ libpqrcv_connect(const char *conninfo, bool replication, bool logical,
conn = palloc0_object(WalReceiverConn);
conn->streamConn =
- libpqsrv_connect_params(keys, vals,
- /* expand_dbname = */ true,
- WAIT_EVENT_LIBPQWALRECEIVER_CONNECT);
+ libpqsrv_connect_params_start(keys, vals,
+ /* expand_dbname = */ true);
+ PQsetNoticeReceiver(conn->streamConn, libpqsrv_notice_receiver,
+ "received message via replication");
+ libpqsrv_connect_complete(conn->streamConn,
+ WAIT_EVENT_LIBPQWALRECEIVER_CONNECT);
if (options_val != NULL)
pfree(options_val);
@@ -245,9 +248,6 @@ libpqrcv_connect(const char *conninfo, bool replication, bool logical,
errhint("Target server's authentication method must be changed, or set password_required=false in the subscription parameters.")));
}
- PQsetNoticeReceiver(conn->streamConn, libpqsrv_notice_receiver,
- "received message via replication");
-
/*
* Set always-secure search path for the cases where the connection is
* used to run SQL queries, so malicious users can't get control.
diff --git a/src/include/libpq/libpq-be-fe-helpers.h b/src/include/libpq/libpq-be-fe-helpers.h
index 85d8b63f019..cff68cd1c37 100644
--- a/src/include/libpq/libpq-be-fe-helpers.h
+++ b/src/include/libpq/libpq-be-fe-helpers.h
@@ -39,10 +39,28 @@
static inline void libpqsrv_connect_prepare(void);
-static inline void libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info);
+static inline void libpqsrv_connect_complete(PGconn *conn, uint32 wait_event_info);
static inline PGresult *libpqsrv_get_result_last(PGconn *conn, uint32 wait_event_info);
static inline PGresult *libpqsrv_get_result(PGconn *conn, uint32 wait_event_info);
+/*
+ * Start a connection using PQconnectStart().
+ *
+ * The returned connection has not yet completed its startup sequence. Callers
+ * may perform per-connection setup, such as installing a notice receiver,
+ * before calling libpqsrv_connect_complete().
+ *
+ * Callers must call libpqsrv_connect_complete(), even if this function returns
+ * NULL, because libpqsrv_connect_prepare() may already have reserved an
+ * external FD that must be released.
+ */
+static inline PGconn *
+libpqsrv_connect_start(const char *conninfo)
+{
+ libpqsrv_connect_prepare();
+
+ return PQconnectStart(conninfo);
+}
/*
* PQconnectdb() wrapper that reserves a file descriptor and processes
@@ -55,17 +73,30 @@ static inline PGresult *libpqsrv_get_result(PGconn *conn, uint32 wait_event_info
static inline PGconn *
libpqsrv_connect(const char *conninfo, uint32 wait_event_info)
{
- PGconn *conn = NULL;
+ PGconn *conn;
- libpqsrv_connect_prepare();
-
- conn = PQconnectStart(conninfo);
+ conn = libpqsrv_connect_start(conninfo);
- libpqsrv_connect_internal(conn, wait_event_info);
+ libpqsrv_connect_complete(conn, wait_event_info);
return conn;
}
+/*
+ * Start a connection using PQconnectStartParams().
+ *
+ * See libpqsrv_connect_start() for the resource-lifetime rules.
+ */
+static inline PGconn *
+libpqsrv_connect_params_start(const char *const *keywords,
+ const char *const *values,
+ int expand_dbname)
+{
+ libpqsrv_connect_prepare();
+
+ return PQconnectStartParams(keywords, values, expand_dbname);
+}
+
/*
* Like libpqsrv_connect(), except that this is a wrapper for
* PQconnectdbParams().
@@ -76,13 +107,11 @@ libpqsrv_connect_params(const char *const *keywords,
int expand_dbname,
uint32 wait_event_info)
{
- PGconn *conn = NULL;
+ PGconn *conn;
- libpqsrv_connect_prepare();
+ conn = libpqsrv_connect_params_start(keywords, values, expand_dbname);
- conn = PQconnectStartParams(keywords, values, expand_dbname);
-
- libpqsrv_connect_internal(conn, wait_event_info);
+ libpqsrv_connect_complete(conn, wait_event_info);
return conn;
}
@@ -90,8 +119,9 @@ libpqsrv_connect_params(const char *const *keywords,
/*
* PQfinish() wrapper that additionally releases the reserved file descriptor.
*
- * It is allowed to call this with a NULL pgconn iff NULL was returned by
- * libpqsrv_connect*.
+ * It is allowed to call this with NULL only when the external FD reservation
+ * has already been released, for example after calling
+ * libpqsrv_connect_complete() with a NULL connection.
*/
static inline void
libpqsrv_disconnect(PGconn *conn)
@@ -101,7 +131,7 @@ libpqsrv_disconnect(PGconn *conn)
* already released it). This rule makes it easier to write PG_CATCH()
* handlers for this facility's users.
*
- * See also libpqsrv_connect_internal().
+ * See also libpqsrv_connect_complete().
*/
if (conn == NULL)
return;
@@ -111,7 +141,7 @@ libpqsrv_disconnect(PGconn *conn)
}
-/* internal helper functions follow */
+/* lower-level connection helper functions follow */
/*
@@ -144,10 +174,11 @@ libpqsrv_connect_prepare(void)
}
/*
- * Helper function for all connection establishment functions.
+ * Complete a connection started by libpqsrv_connect_start() or
+ * libpqsrv_connect_params_start().
*/
static inline void
-libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info)
+libpqsrv_connect_complete(PGconn *conn, uint32 wait_event_info)
{
/*
* With conn == NULL libpqsrv_disconnect() wouldn't release the FD. So do
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 10+ messages in thread
* Re: Set notice receiver before libpq connection startup
@ 2026-05-22 15:28 Fujii Masao <masao.fujii@gmail.com>
parent: Chao Li <li.evan.chao@gmail.com>
0 siblings, 1 reply; 10+ messages in thread
From: Fujii Masao @ 2026-05-22 15:28 UTC (permalink / raw)
To: Chao Li <li.evan.chao@gmail.com>; +Cc: Rafia Sabih <rafia.pghackers@gmail.com>; vignesh C <vignesh21@gmail.com>; pgsql-hackers
On Fri, May 22, 2026 at 11:56 PM Chao Li <li.evan.chao@gmail.com> wrote:
> PFA v4, just removed conn NULL check.
Thanks for updating the patch! I've pushed it.
Regards,
--
Fujii Masao
^ permalink raw reply [nested|flat] 10+ messages in thread
* Re: Set notice receiver before libpq connection startup
@ 2026-05-22 23:56 Chao Li <li.evan.chao@gmail.com>
parent: Fujii Masao <masao.fujii@gmail.com>
0 siblings, 0 replies; 10+ messages in thread
From: Chao Li @ 2026-05-22 23:56 UTC (permalink / raw)
To: Fujii Masao <masao.fujii@gmail.com>; +Cc: Rafia Sabih <rafia.pghackers@gmail.com>; vignesh C <vignesh21@gmail.com>; pgsql-hackers
> On May 22, 2026, at 23:28, Fujii Masao <masao.fujii@gmail.com> wrote:
>
> On Fri, May 22, 2026 at 11:56 PM Chao Li <li.evan.chao@gmail.com> wrote:
>> PFA v4, just removed conn NULL check.
>
> Thanks for updating the patch! I've pushed it.
>
> Regards,
>
> --
> Fujii Masao
Thanks for pushing and still working hard during the PGConf.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
^ permalink raw reply [nested|flat] 10+ messages in thread
end of thread, other threads:[~2026-05-22 23:56 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-05-20 07:21 Set notice receiver before libpq connection startup Chao Li <li.evan.chao@gmail.com>
2026-05-20 09:19 ` Fujii Masao <masao.fujii@gmail.com>
2026-05-21 02:09 ` Chao Li <li.evan.chao@gmail.com>
2026-05-21 05:03 ` vignesh C <vignesh21@gmail.com>
2026-05-21 07:26 ` Chao Li <li.evan.chao@gmail.com>
2026-05-22 11:33 ` Rafia Sabih <rafia.pghackers@gmail.com>
2026-05-22 13:42 ` Fujii Masao <masao.fujii@gmail.com>
2026-05-22 14:55 ` Chao Li <li.evan.chao@gmail.com>
2026-05-22 15:28 ` Fujii Masao <masao.fujii@gmail.com>
2026-05-22 23:56 ` Chao Li <li.evan.chao@gmail.com>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox