Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit5a3d5c0

Browse files
anarazelnmisch
authored andcommitted
Add helper library for use of libpq inside the server environment
Currently dblink and postgres_fdw don't process interrupts during connectionestablishment. Besides preventing query cancellations etc, this can lead toundetected deadlocks, as global barriers are not processed.Libpqwalreceiver in contrast, processes interrupts during connectionestablishment. The required code is not trivial, so duplicating it intoadditional places does not seem like a good option.These aforementioned undetected deadlocks are the reason for the spate of CItest failures in the FreeBSD 'test_running' step.For now the helper library is just a header, as it needs to be linked intoeach extension using libpq, and it seems too small to be worth adding adedicated static library for.The conversion to the helper are done in subsequent commits.Reviewed-by: Thomas Munro <thomas.munro@gmail.com>Discussion:https://postgr.es/m/20220925232237.p6uskba2dw6fnwj2@awork3.anarazel.de
1 parent2c6a4f2 commit5a3d5c0

File tree

1 file changed

+242
-0
lines changed

1 file changed

+242
-0
lines changed
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/*-------------------------------------------------------------------------
2+
*
3+
* libpq-be-fe-helpers.h
4+
* Helper functions for using libpq in extensions
5+
*
6+
* Code built directly into the backend is not allowed to link to libpq
7+
* directly. Extension code is allowed to use libpq however. However, libpq
8+
* used in extensions has to be careful to block inside libpq, otherwise
9+
* interrupts will not be processed, leading to issues like unresolvable
10+
* deadlocks. Backend code also needs to take care to acquire/release an
11+
* external fd for the connection, otherwise fd.c's accounting of fd's is
12+
* broken.
13+
*
14+
* This file provides helper functions to make it easier to comply with these
15+
* rules. It is a header only library as it needs to be linked into each
16+
* extension using libpq, and it seems too small to be worth adding a
17+
* dedicated static library for.
18+
*
19+
* TODO: For historical reasons the connections established here are not put
20+
* into non-blocking mode. That can lead to blocking even when only the async
21+
* libpq functions are used. This should be fixed.
22+
*
23+
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
24+
* Portions Copyright (c) 1994, Regents of the University of California
25+
*
26+
* src/include/libpq/libpq-be-fe-helpers.h
27+
*
28+
*-------------------------------------------------------------------------
29+
*/
30+
#ifndefLIBPQ_BE_FE_HELPERS_H
31+
#defineLIBPQ_BE_FE_HELPERS_H
32+
33+
/*
34+
* Despite the name, BUILDING_DLL is set only when building code directly part
35+
* of the backend. Which also is where libpq isn't allowed to be
36+
* used. Obviously this doesn't protect against libpq-fe.h getting included
37+
* otherwise, but perhaps still protects against a few mistakes...
38+
*/
39+
#ifdefBUILDING_DLL
40+
#error "libpq may not be used code directly built into the backend"
41+
#endif
42+
43+
#include"libpq-fe.h"
44+
#include"miscadmin.h"
45+
#include"pgstat.h"
46+
#include"storage/fd.h"
47+
#include"storage/latch.h"
48+
49+
50+
staticinlinevoidlibpqsrv_connect_prepare(void);
51+
staticinlinevoidlibpqsrv_connect_internal(PGconn*conn,uint32wait_event_info);
52+
53+
54+
/*
55+
* PQconnectdb() wrapper that reserves a file descriptor and processes
56+
* interrupts during connection establishment.
57+
*
58+
* Throws an error if AcquireExternalFD() fails, but does not throw if
59+
* connection establishment itself fails. Callers need to use PQstatus() to
60+
* check if connection establishment succeeded.
61+
*/
62+
staticinlinePGconn*
63+
libpqsrv_connect(constchar*conninfo,uint32wait_event_info)
64+
{
65+
PGconn*conn=NULL;
66+
67+
libpqsrv_connect_prepare();
68+
69+
conn=PQconnectStart(conninfo);
70+
71+
libpqsrv_connect_internal(conn,wait_event_info);
72+
73+
returnconn;
74+
}
75+
76+
/*
77+
* Like libpqsrv_connect(), except that this is a wrapper for
78+
* PQconnectdbParams().
79+
*/
80+
staticinlinePGconn*
81+
libpqsrv_connect_params(constchar*const*keywords,
82+
constchar*const*values,
83+
intexpand_dbname,
84+
uint32wait_event_info)
85+
{
86+
PGconn*conn=NULL;
87+
88+
libpqsrv_connect_prepare();
89+
90+
conn=PQconnectStartParams(keywords,values,expand_dbname);
91+
92+
libpqsrv_connect_internal(conn,wait_event_info);
93+
94+
returnconn;
95+
}
96+
97+
/*
98+
* PQfinish() wrapper that additionally releases the reserved file descriptor.
99+
*
100+
* It is allowed to call this with a NULL pgconn iff NULL was returned by
101+
* libpqsrv_connect*.
102+
*/
103+
staticinlinevoid
104+
libpqsrv_disconnect(PGconn*conn)
105+
{
106+
/*
107+
* If no connection was established, we haven't reserved an FD for it (or
108+
* already released it). This rule makes it easier to write PG_CATCH()
109+
* handlers for this facility's users.
110+
*
111+
* See also libpqsrv_connect_internal().
112+
*/
113+
if (conn==NULL)
114+
return;
115+
116+
ReleaseExternalFD();
117+
PQfinish(conn);
118+
}
119+
120+
121+
/* internal helper functions follow */
122+
123+
124+
/*
125+
* Helper function for all connection establishment functions.
126+
*/
127+
staticinlinevoid
128+
libpqsrv_connect_prepare(void)
129+
{
130+
/*
131+
* We must obey fd.c's limit on non-virtual file descriptors. Assume that
132+
* a PGconn represents one long-lived FD. (Doing this here also ensures
133+
* that VFDs are closed if needed to make room.)
134+
*/
135+
if (!AcquireExternalFD())
136+
{
137+
#ifndefWIN32/* can't write #if within ereport() macro */
138+
ereport(ERROR,
139+
(errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION),
140+
errmsg("could not establish connection"),
141+
errdetail("There are too many open files on the local server."),
142+
errhint("Raise the server's max_files_per_process and/or \"ulimit -n\" limits.")));
143+
#else
144+
ereport(ERROR,
145+
(errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION),
146+
errmsg("could not establish connection"),
147+
errdetail("There are too many open files on the local server."),
148+
errhint("Raise the server's max_files_per_process setting.")));
149+
#endif
150+
}
151+
}
152+
153+
/*
154+
* Helper function for all connection establishment functions.
155+
*/
156+
staticinlinevoid
157+
libpqsrv_connect_internal(PGconn*conn,uint32wait_event_info)
158+
{
159+
/*
160+
* With conn == NULL libpqsrv_disconnect() wouldn't release the FD. So do
161+
* that here.
162+
*/
163+
if (conn==NULL)
164+
{
165+
ReleaseExternalFD();
166+
return;
167+
}
168+
169+
/*
170+
* Can't wait without a socket. Note that we don't want to close the libpq
171+
* connection yet, so callers can emit a useful error.
172+
*/
173+
if (PQstatus(conn)==CONNECTION_BAD)
174+
return;
175+
176+
/*
177+
* WaitLatchOrSocket() can conceivably fail, handle that case here instead
178+
* of requiring all callers to do so.
179+
*/
180+
PG_TRY();
181+
{
182+
PostgresPollingStatusTypestatus;
183+
184+
/*
185+
* Poll connection until we have OK or FAILED status.
186+
*
187+
* Per spec for PQconnectPoll, first wait till socket is write-ready.
188+
*/
189+
status=PGRES_POLLING_WRITING;
190+
while (status!=PGRES_POLLING_OK&&status!=PGRES_POLLING_FAILED)
191+
{
192+
intio_flag;
193+
intrc;
194+
195+
if (status==PGRES_POLLING_READING)
196+
io_flag=WL_SOCKET_READABLE;
197+
#ifdefWIN32
198+
199+
/*
200+
* Windows needs a different test while waiting for
201+
* connection-made
202+
*/
203+
elseif (PQstatus(conn)==CONNECTION_STARTED)
204+
io_flag=WL_SOCKET_CONNECTED;
205+
#endif
206+
else
207+
io_flag=WL_SOCKET_WRITEABLE;
208+
209+
rc=WaitLatchOrSocket(MyLatch,
210+
WL_EXIT_ON_PM_DEATH |WL_LATCH_SET |io_flag,
211+
PQsocket(conn),
212+
0,
213+
wait_event_info);
214+
215+
/* Interrupted? */
216+
if (rc&WL_LATCH_SET)
217+
{
218+
ResetLatch(MyLatch);
219+
CHECK_FOR_INTERRUPTS();
220+
}
221+
222+
/* If socket is ready, advance the libpq state machine */
223+
if (rc&io_flag)
224+
status=PQconnectPoll(conn);
225+
}
226+
}
227+
PG_CATCH();
228+
{
229+
/*
230+
* If an error is thrown here, the callers won't call
231+
* libpqsrv_disconnect() with a conn, so release resources
232+
* immediately.
233+
*/
234+
ReleaseExternalFD();
235+
PQfinish(conn);
236+
237+
PG_RE_THROW();
238+
}
239+
PG_END_TRY();
240+
}
241+
242+
#endif/* LIBPQ_BE_FE_HELPERS_H */

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp