PostgreSQL Source Code  git master
libpq-be-fe-helpers.h
Go to the documentation of this file.
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 #ifndef LIBPQ_BE_FE_HELPERS_H
31 #define LIBPQ_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 #ifdef BUILDING_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 "storage/fd.h"
46 #include "storage/latch.h"
47 #include "utils/wait_event.h"
48 
49 
50 static inline void libpqsrv_connect_prepare(void);
51 static inline void libpqsrv_connect_internal(PGconn *conn, uint32 wait_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 static inline PGconn *
63 libpqsrv_connect(const char *conninfo, uint32 wait_event_info)
64 {
65  PGconn *conn = NULL;
66 
68 
69  conn = PQconnectStart(conninfo);
70 
71  libpqsrv_connect_internal(conn, wait_event_info);
72 
73  return conn;
74 }
75 
76 /*
77  * Like libpqsrv_connect(), except that this is a wrapper for
78  * PQconnectdbParams().
79  */
80 static inline PGconn *
81 libpqsrv_connect_params(const char *const *keywords,
82  const char *const *values,
83  int expand_dbname,
84  uint32 wait_event_info)
85 {
86  PGconn *conn = NULL;
87 
89 
90  conn = PQconnectStartParams(keywords, values, expand_dbname);
91 
92  libpqsrv_connect_internal(conn, wait_event_info);
93 
94  return conn;
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 static inline void
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 
117  PQfinish(conn);
118 }
119 
120 
121 /* internal helper functions follow */
122 
123 
124 /*
125  * Helper function for all connection establishment functions.
126  */
127 static inline 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 #ifndef WIN32 /* 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 static inline void
158 {
159  /*
160  * With conn == NULL libpqsrv_disconnect() wouldn't release the FD. So do
161  * that here.
162  */
163  if (conn == NULL)
164  {
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  {
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  int io_flag;
193  int rc;
194 
195  if (status == PGRES_POLLING_READING)
196  io_flag = WL_SOCKET_READABLE;
197 #ifdef WIN32
198 
199  /*
200  * Windows needs a different test while waiting for
201  * connection-made
202  */
203  else if (PQstatus(conn) == CONNECTION_STARTED)
204  io_flag = WL_SOCKET_CONNECTED;
205 #endif
206  else
207  io_flag = WL_SOCKET_WRITEABLE;
208 
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  {
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  */
235  PQfinish(conn);
236 
237  PG_RE_THROW();
238  }
239  PG_END_TRY();
240 }
241 
242 #endif /* LIBPQ_BE_FE_HELPERS_H */
static Datum values[MAXATTR]
Definition: bootstrap.c:156
unsigned int uint32
Definition: c.h:490
int errdetail(const char *fmt,...)
Definition: elog.c:1202
int errhint(const char *fmt,...)
Definition: elog.c:1316
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define PG_RE_THROW()
Definition: elog.h:411
#define PG_TRY(...)
Definition: elog.h:370
#define PG_END_TRY(...)
Definition: elog.h:395
#define ERROR
Definition: elog.h:39
#define PG_CATCH(...)
Definition: elog.h:380
#define ereport(elevel,...)
Definition: elog.h:149
void ReleaseExternalFD(void)
Definition: fd.c:1150
bool AcquireExternalFD(void)
Definition: fd.c:1097
PGconn * PQconnectStartParams(const char *const *keywords, const char *const *values, int expand_dbname)
Definition: fe-connect.c:780
PGconn * PQconnectStart(const char *conninfo)
Definition: fe-connect.c:861
PostgresPollingStatusType PQconnectPoll(PGconn *conn)
Definition: fe-connect.c:2543
ConnStatusType PQstatus(const PGconn *conn)
Definition: fe-connect.c:7192
void PQfinish(PGconn *conn)
Definition: fe-connect.c:4599
int PQsocket(const PGconn *conn)
Definition: fe-connect.c:7271
struct Latch * MyLatch
Definition: globals.c:58
int WaitLatchOrSocket(Latch *latch, int wakeEvents, pgsocket sock, long timeout, uint32 wait_event_info)
Definition: latch.c:540
void ResetLatch(Latch *latch)
Definition: latch.c:699
#define WL_SOCKET_READABLE
Definition: latch.h:126
#define WL_EXIT_ON_PM_DEATH
Definition: latch.h:130
#define WL_LATCH_SET
Definition: latch.h:125
#define WL_SOCKET_CONNECTED
Definition: latch.h:135
#define WL_SOCKET_WRITEABLE
Definition: latch.h:127
static void libpqsrv_connect_prepare(void)
static PGconn * libpqsrv_connect(const char *conninfo, uint32 wait_event_info)
static void libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info)
static void libpqsrv_disconnect(PGconn *conn)
static PGconn * libpqsrv_connect_params(const char *const *keywords, const char *const *values, int expand_dbname, uint32 wait_event_info)
@ CONNECTION_STARTED
Definition: libpq-fe.h:68
@ CONNECTION_BAD
Definition: libpq-fe.h:61
PostgresPollingStatusType
Definition: libpq-fe.h:85
@ PGRES_POLLING_OK
Definition: libpq-fe.h:89
@ PGRES_POLLING_READING
Definition: libpq-fe.h:87
@ PGRES_POLLING_WRITING
Definition: libpq-fe.h:88
@ PGRES_POLLING_FAILED
Definition: libpq-fe.h:86
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
PGconn * conn
Definition: streamutil.c:54