Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * libpq-be-fe.h
4 : : * Wrapper 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. One of the
8 : : * main risks in doing so is leaking the malloc-allocated structures
9 : : * returned by libpq, causing a process-lifespan memory leak.
10 : : *
11 : : * This file provides wrapper objects to help in building memory-safe code.
12 : : * A PGresult object wrapped this way acts much as if it were palloc'd:
13 : : * it will go away when the specified context is reset or deleted.
14 : : * We might later extend the concept to other objects such as PGconns.
15 : : *
16 : : * See also the libpq-be-fe-helpers.h file, which provides additional
17 : : * facilities built on top of this one.
18 : : *
19 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
20 : : * Portions Copyright (c) 1994, Regents of the University of California
21 : : *
22 : : * src/include/libpq/libpq-be-fe.h
23 : : *
24 : : *-------------------------------------------------------------------------
25 : : */
26 : : #ifndef LIBPQ_BE_FE_H
27 : : #define LIBPQ_BE_FE_H
28 : :
29 : : /*
30 : : * Despite the name, BUILDING_DLL is set only when building code directly part
31 : : * of the backend. Which also is where libpq isn't allowed to be
32 : : * used. Obviously this doesn't protect against libpq-fe.h getting included
33 : : * otherwise, but perhaps still protects against a few mistakes...
34 : : */
35 : : #ifdef BUILDING_DLL
36 : : #error "libpq may not be used in code directly built into the backend"
37 : : #endif
38 : :
39 : : #include "libpq-fe.h"
40 : :
41 : : /*
42 : : * Memory-context-safe wrapper object for a PGresult.
43 : : */
44 : : typedef struct libpqsrv_PGresult
45 : : {
46 : : PGresult *res; /* the wrapped PGresult */
47 : : MemoryContext ctx; /* the MemoryContext it's attached to */
48 : : MemoryContextCallback cb; /* the callback that implements freeing */
49 : : } libpqsrv_PGresult;
50 : :
51 : :
52 : : /*
53 : : * Wrap the given PGresult in a libpqsrv_PGresult object, so that it will
54 : : * go away automatically if the current memory context is reset or deleted.
55 : : *
56 : : * To avoid potential memory leaks, backend code must always apply this
57 : : * immediately to the output of any PGresult-yielding libpq function.
58 : : */
59 : : static inline libpqsrv_PGresult *
43 tgl@sss.pgh.pa.us 60 :GNC 24878 : libpqsrv_PQwrap(PGresult *res)
61 : : {
62 : : libpqsrv_PGresult *bres;
63 : 24878 : MemoryContext ctx = CurrentMemoryContext;
64 : :
65 : : /* We pass through a NULL result as-is, since there's nothing to free */
66 [ + + ]: 24878 : if (res == NULL)
67 : 11860 : return NULL;
68 : : /* Attempt to allocate the wrapper ... this had better not throw error */
69 : : bres = (libpqsrv_PGresult *)
70 : 13018 : MemoryContextAllocExtended(ctx,
71 : : sizeof(libpqsrv_PGresult),
72 : : MCXT_ALLOC_NO_OOM);
73 : : /* If we failed to allocate a wrapper, free the PGresult before failing */
74 [ - + ]: 13018 : if (bres == NULL)
75 : : {
43 tgl@sss.pgh.pa.us 76 :UNC 0 : PQclear(res);
77 [ # # ]: 0 : ereport(ERROR,
78 : : (errcode(ERRCODE_OUT_OF_MEMORY),
79 : : errmsg("out of memory")));
80 : : }
81 : : /* Okay, set up the wrapper */
43 tgl@sss.pgh.pa.us 82 :GNC 13018 : bres->res = res;
83 : 13018 : bres->ctx = ctx;
84 : 13018 : bres->cb.func = (MemoryContextCallbackFunction) PQclear;
85 : 13018 : bres->cb.arg = res;
86 : 13018 : MemoryContextRegisterResetCallback(ctx, &bres->cb);
87 : 13018 : return bres;
88 : : }
89 : :
90 : : /*
91 : : * Free a wrapped PGresult, after detaching it from the memory context.
92 : : * Like PQclear(), allow the argument to be NULL.
93 : : */
94 : : static inline void
95 : 25227 : libpqsrv_PQclear(libpqsrv_PGresult *bres)
96 : : {
97 [ + + ]: 25227 : if (bres)
98 : : {
99 : 12985 : MemoryContextUnregisterResetCallback(bres->ctx, &bres->cb);
100 : 12985 : PQclear(bres->res);
101 : 12985 : pfree(bres);
102 : : }
103 : 25227 : }
104 : :
105 : : /*
106 : : * Move a wrapped PGresult to have a different parent context.
107 : : */
108 : : static inline libpqsrv_PGresult *
109 : 67 : libpqsrv_PGresultSetParent(libpqsrv_PGresult *bres, MemoryContext ctx)
110 : : {
111 : : libpqsrv_PGresult *newres;
112 : :
113 : : /* We pass through a NULL result as-is */
114 [ - + ]: 67 : if (bres == NULL)
43 tgl@sss.pgh.pa.us 115 :UNC 0 : return NULL;
116 : : /* Make a new wrapper in the target context, raising error on OOM */
117 : : newres = (libpqsrv_PGresult *)
43 tgl@sss.pgh.pa.us 118 :GNC 67 : MemoryContextAlloc(ctx, sizeof(libpqsrv_PGresult));
119 : : /* Okay, set up the new wrapper */
120 : 67 : newres->res = bres->res;
121 : 67 : newres->ctx = ctx;
122 : 67 : newres->cb.func = (MemoryContextCallbackFunction) PQclear;
123 : 67 : newres->cb.arg = bres->res;
124 : 67 : MemoryContextRegisterResetCallback(ctx, &newres->cb);
125 : : /* Disarm and delete the old wrapper */
126 : 67 : MemoryContextUnregisterResetCallback(bres->ctx, &bres->cb);
127 : 67 : pfree(bres);
128 : 67 : return newres;
129 : : }
130 : :
131 : : /*
132 : : * Convenience wrapper for PQgetResult.
133 : : *
134 : : * We could supply wrappers for other PGresult-returning functions too,
135 : : * but at present there's no need.
136 : : */
137 : : static inline libpqsrv_PGresult *
138 : 24878 : libpqsrv_PQgetResult(PGconn *conn)
139 : : {
140 : 24878 : return libpqsrv_PQwrap(PQgetResult(conn));
141 : : }
142 : :
143 : : /*
144 : : * Accessor functions for libpqsrv_PGresult. While it's not necessary to use
145 : : * these, they emulate the behavior of the underlying libpq functions when
146 : : * passed a NULL pointer. This is particularly important for PQresultStatus,
147 : : * which is often the first check on a result.
148 : : */
149 : :
150 : : static inline ExecStatusType
151 : 49922 : libpqsrv_PQresultStatus(const libpqsrv_PGresult *res)
152 : : {
153 [ - + ]: 49922 : if (!res)
43 tgl@sss.pgh.pa.us 154 :UNC 0 : return PGRES_FATAL_ERROR;
43 tgl@sss.pgh.pa.us 155 :GNC 49922 : return PQresultStatus(res->res);
156 : : }
157 : :
158 : : static inline const char *
159 : : libpqsrv_PQresultErrorMessage(const libpqsrv_PGresult *res)
160 : : {
161 : : if (!res)
162 : : return "";
163 : : return PQresultErrorMessage(res->res);
164 : : }
165 : :
166 : : static inline char *
167 : 143 : libpqsrv_PQresultErrorField(const libpqsrv_PGresult *res, int fieldcode)
168 : : {
169 [ - + ]: 143 : if (!res)
43 tgl@sss.pgh.pa.us 170 :UNC 0 : return NULL;
43 tgl@sss.pgh.pa.us 171 :GNC 143 : return PQresultErrorField(res->res, fieldcode);
172 : : }
173 : :
174 : : static inline char *
175 : 24 : libpqsrv_PQcmdStatus(const libpqsrv_PGresult *res)
176 : : {
177 [ - + ]: 24 : if (!res)
43 tgl@sss.pgh.pa.us 178 :UNC 0 : return NULL;
43 tgl@sss.pgh.pa.us 179 :GNC 24 : return PQcmdStatus(res->res);
180 : : }
181 : :
182 : : static inline int
183 : 100591 : libpqsrv_PQntuples(const libpqsrv_PGresult *res)
184 : : {
185 [ - + ]: 100591 : if (!res)
43 tgl@sss.pgh.pa.us 186 :UNC 0 : return 0;
43 tgl@sss.pgh.pa.us 187 :GNC 100591 : return PQntuples(res->res);
188 : : }
189 : :
190 : : static inline int
191 : 95176 : libpqsrv_PQnfields(const libpqsrv_PGresult *res)
192 : : {
193 [ - + ]: 95176 : if (!res)
43 tgl@sss.pgh.pa.us 194 :UNC 0 : return 0;
43 tgl@sss.pgh.pa.us 195 :GNC 95176 : return PQnfields(res->res);
196 : : }
197 : :
198 : : static inline char *
199 : 255757 : libpqsrv_PQgetvalue(const libpqsrv_PGresult *res, int tup_num, int field_num)
200 : : {
201 [ - + ]: 255757 : if (!res)
43 tgl@sss.pgh.pa.us 202 :UNC 0 : return NULL;
43 tgl@sss.pgh.pa.us 203 :GNC 255757 : return PQgetvalue(res->res, tup_num, field_num);
204 : : }
205 : :
206 : : static inline int
207 : 10 : libpqsrv_PQgetlength(const libpqsrv_PGresult *res, int tup_num, int field_num)
208 : : {
209 [ - + ]: 10 : if (!res)
43 tgl@sss.pgh.pa.us 210 :UNC 0 : return 0;
43 tgl@sss.pgh.pa.us 211 :GNC 10 : return PQgetlength(res->res, tup_num, field_num);
212 : : }
213 : :
214 : : static inline int
215 : 264983 : libpqsrv_PQgetisnull(const libpqsrv_PGresult *res, int tup_num, int field_num)
216 : : {
217 [ - + ]: 264983 : if (!res)
43 tgl@sss.pgh.pa.us 218 :UNC 0 : return 1; /* pretend it is null */
43 tgl@sss.pgh.pa.us 219 :GNC 264983 : return PQgetisnull(res->res, tup_num, field_num);
220 : : }
221 : :
222 : : static inline char *
223 : 2791 : libpqsrv_PQfname(const libpqsrv_PGresult *res, int field_num)
224 : : {
225 [ - + ]: 2791 : if (!res)
43 tgl@sss.pgh.pa.us 226 :UNC 0 : return NULL;
43 tgl@sss.pgh.pa.us 227 :GNC 2791 : return PQfname(res->res, field_num);
228 : : }
229 : :
230 : : static inline const char *
231 : 992 : libpqsrv_PQcmdTuples(const libpqsrv_PGresult *res)
232 : : {
233 [ - + ]: 992 : if (!res)
43 tgl@sss.pgh.pa.us 234 :UNC 0 : return "";
43 tgl@sss.pgh.pa.us 235 :GNC 992 : return PQcmdTuples(res->res);
236 : : }
237 : :
238 : : /*
239 : : * Redefine these libpq entry point names concerned with PGresults so that
240 : : * they will operate on libpqsrv_PGresults instead. This avoids needing to
241 : : * convert a lot of pre-existing code, and reduces the notational differences
242 : : * between frontend and backend libpq-using code.
243 : : */
244 : : #define PGresult libpqsrv_PGresult
245 : : #define PQclear libpqsrv_PQclear
246 : : #define PQgetResult libpqsrv_PQgetResult
247 : : #define PQresultStatus libpqsrv_PQresultStatus
248 : : #define PQresultErrorMessage libpqsrv_PQresultErrorMessage
249 : : #define PQresultErrorField libpqsrv_PQresultErrorField
250 : : #define PQcmdStatus libpqsrv_PQcmdStatus
251 : : #define PQntuples libpqsrv_PQntuples
252 : : #define PQnfields libpqsrv_PQnfields
253 : : #define PQgetvalue libpqsrv_PQgetvalue
254 : : #define PQgetlength libpqsrv_PQgetlength
255 : : #define PQgetisnull libpqsrv_PQgetisnull
256 : : #define PQfname libpqsrv_PQfname
257 : : #define PQcmdTuples libpqsrv_PQcmdTuples
258 : :
259 : : #endif /* LIBPQ_BE_FE_H */
|