PostgreSQL Source Code git master
pqexpbuffer.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pqexpbuffer.c
4 *
5 * PQExpBuffer provides an indefinitely-extensible string data type.
6 * It can be used to buffer either ordinary C strings (null-terminated text)
7 * or arbitrary binary data. All storage is allocated with malloc().
8 *
9 * This module is essentially the same as the backend's StringInfo data type,
10 * but it is intended for use in frontend libpq and client applications.
11 * Thus, it does not rely on palloc() nor elog(), nor psprintf.c which
12 * will exit() on error.
13 *
14 * It does rely on vsnprintf(); if configure finds that libc doesn't provide
15 * a usable vsnprintf(), then a copy of our own implementation of it will
16 * be linked into libpq.
17 *
18 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
19 * Portions Copyright (c) 1994, Regents of the University of California
20 *
21 * src/interfaces/libpq/pqexpbuffer.c
22 *
23 *-------------------------------------------------------------------------
24 */
25
26#include "postgres_fe.h"
27
28#include <limits.h>
29
30#include "pqexpbuffer.h"
31
32#ifdef WIN32
33#include "win32.h"
34#endif
35
36
37/* All "broken" PQExpBuffers point to this string. */
38static const char oom_buffer[1] = "";
39
40/* Need a char * for unconstify() compatibility */
41static const char *const oom_buffer_ptr = oom_buffer;
42
43
44/*
45 * markPQExpBufferBroken
46 *
47 * Put a PQExpBuffer in "broken" state if it isn't already.
48 */
49static void
51{
52 if (str->data != oom_buffer)
53 free(str->data);
54
55 /*
56 * Casting away const here is a bit ugly, but it seems preferable to not
57 * marking oom_buffer const. We want to do that to encourage the compiler
58 * to put oom_buffer in read-only storage, so that anyone who tries to
59 * scribble on a broken PQExpBuffer will get a failure.
60 */
61 str->data = unconstify(char *, oom_buffer_ptr);
62 str->len = 0;
63 str->maxlen = 0;
64}
65
66/*
67 * createPQExpBuffer
68 *
69 * Create an empty 'PQExpBufferData' & return a pointer to it.
70 */
73{
74 PQExpBuffer res;
75
76 res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
77 if (res != NULL)
78 initPQExpBuffer(res);
79
80 return res;
81}
82
83/*
84 * initPQExpBuffer
85 *
86 * Initialize a PQExpBufferData struct (with previously undefined contents)
87 * to describe an empty string.
88 */
89void
91{
92 str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
93 if (str->data == NULL)
94 {
95 str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
96 str->maxlen = 0;
97 str->len = 0;
98 }
99 else
100 {
101 str->maxlen = INITIAL_EXPBUFFER_SIZE;
102 str->len = 0;
103 str->data[0] = '\0';
104 }
105}
106
107/*
108 * destroyPQExpBuffer(str);
109 *
110 * free()s both the data buffer and the PQExpBufferData.
111 * This is the inverse of createPQExpBuffer().
112 */
113void
115{
116 if (str)
117 {
119 free(str);
120 }
121}
122
123/*
124 * termPQExpBuffer(str)
125 * free()s the data buffer but not the PQExpBufferData itself.
126 * This is the inverse of initPQExpBuffer().
127 */
128void
130{
131 if (str->data != oom_buffer)
132 free(str->data);
133 /* just for luck, make the buffer validly empty. */
134 str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
135 str->maxlen = 0;
136 str->len = 0;
137}
138
139/*
140 * resetPQExpBuffer
141 * Reset a PQExpBuffer to empty
142 *
143 * Note: if possible, a "broken" PQExpBuffer is returned to normal.
144 */
145void
147{
148 if (str)
149 {
150 if (str->data != oom_buffer)
151 {
152 str->len = 0;
153 str->data[0] = '\0';
154 }
155 else
156 {
157 /* try to reinitialize to valid state */
159 }
160 }
161}
162
163/*
164 * enlargePQExpBuffer
165 * Make sure there is enough space for 'needed' more bytes in the buffer
166 * ('needed' does not include the terminating null).
167 *
168 * Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case
169 * the buffer is left in "broken" state.)
170 */
171int
173{
174 size_t newlen;
175 char *newdata;
176
178 return 0; /* already failed */
179
180 /*
181 * Guard against ridiculous "needed" values, which can occur if we're fed
182 * bogus data. Without this, we can get an overflow or infinite loop in
183 * the following.
184 */
185 if (needed >= ((size_t) INT_MAX - str->len))
186 {
188 return 0;
189 }
190
191 needed += str->len + 1; /* total space required now */
192
193 /* Because of the above test, we now have needed <= INT_MAX */
194
195 if (needed <= str->maxlen)
196 return 1; /* got enough space already */
197
198 /*
199 * We don't want to allocate just a little more space with each append;
200 * for efficiency, double the buffer size each time it overflows.
201 * Actually, we might need to more than double it if 'needed' is big...
202 */
203 newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64;
204 while (needed > newlen)
205 newlen = 2 * newlen;
206
207 /*
208 * Clamp to INT_MAX in case we went past it. Note we are assuming here
209 * that INT_MAX <= UINT_MAX/2, else the above loop could overflow. We
210 * will still have newlen >= needed.
211 */
212 if (newlen > (size_t) INT_MAX)
213 newlen = (size_t) INT_MAX;
214
215 newdata = (char *) realloc(str->data, newlen);
216 if (newdata != NULL)
217 {
218 str->data = newdata;
219 str->maxlen = newlen;
220 return 1;
221 }
222
224 return 0;
225}
226
227/*
228 * printfPQExpBuffer
229 * Format text data under the control of fmt (an sprintf-like format string)
230 * and insert it into str. More space is allocated to str if necessary.
231 * This is a convenience routine that does the same thing as
232 * resetPQExpBuffer() followed by appendPQExpBuffer().
233 */
234void
235printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
236{
237 int save_errno = errno;
238 va_list args;
239 bool done;
240
242
244 return; /* already failed */
245
246 /* Loop in case we have to retry after enlarging the buffer. */
247 do
248 {
249 errno = save_errno;
250 va_start(args, fmt);
251 done = appendPQExpBufferVA(str, fmt, args);
252 va_end(args);
253 } while (!done);
254}
255
256/*
257 * appendPQExpBuffer
258 *
259 * Format text data under the control of fmt (an sprintf-like format string)
260 * and append it to whatever is already in str. More space is allocated
261 * to str if necessary. This is sort of like a combination of sprintf and
262 * strcat.
263 */
264void
265appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
266{
267 int save_errno = errno;
268 va_list args;
269 bool done;
270
272 return; /* already failed */
273
274 /* Loop in case we have to retry after enlarging the buffer. */
275 do
276 {
277 errno = save_errno;
278 va_start(args, fmt);
279 done = appendPQExpBufferVA(str, fmt, args);
280 va_end(args);
281 } while (!done);
282}
283
284/*
285 * appendPQExpBufferVA
286 * Shared guts of printfPQExpBuffer/appendPQExpBuffer.
287 * Attempt to format data and append it to str. Returns true if done
288 * (either successful or hard failure), false if need to retry.
289 *
290 * Caution: callers must be sure to preserve their entry-time errno
291 * when looping, in case the fmt contains "%m".
292 */
293bool
294appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args)
295{
296 size_t avail;
297 size_t needed;
298 int nprinted;
299
300 /*
301 * Try to format the given string into the available space; but if there's
302 * hardly any space, don't bother trying, just enlarge the buffer first.
303 */
304 if (str->maxlen > str->len + 16)
305 {
306 avail = str->maxlen - str->len;
307
308 nprinted = vsnprintf(str->data + str->len, avail, fmt, args);
309
310 /*
311 * If vsnprintf reports an error, fail (we assume this means there's
312 * something wrong with the format string).
313 */
314 if (unlikely(nprinted < 0))
315 {
317 return true;
318 }
319
320 if ((size_t) nprinted < avail)
321 {
322 /* Success. Note nprinted does not include trailing null. */
323 str->len += nprinted;
324 return true;
325 }
326
327 /*
328 * We assume a C99-compliant vsnprintf, so believe its estimate of the
329 * required space, and add one for the trailing null. (If it's wrong,
330 * the logic will still work, but we may loop multiple times.)
331 *
332 * Choke if the required space would exceed INT_MAX, since str->maxlen
333 * can't represent more than that.
334 */
335 if (unlikely(nprinted > INT_MAX - 1))
336 {
338 return true;
339 }
340 needed = nprinted + 1;
341 }
342 else
343 {
344 /*
345 * We have to guess at how much to enlarge, since we're skipping the
346 * formatting work. Fortunately, because of enlargePQExpBuffer's
347 * preference for power-of-2 sizes, this number isn't very sensitive;
348 * the net effect is that we'll double the buffer size before trying
349 * to run vsnprintf, which seems sensible.
350 */
351 needed = 32;
352 }
353
354 /* Increase the buffer size and try again. */
355 if (!enlargePQExpBuffer(str, needed))
356 return true; /* oops, out of memory */
357
358 return false;
359}
360
361/*
362 * appendPQExpBufferStr
363 * Append the given string to a PQExpBuffer, allocating more space
364 * if necessary.
365 */
366void
368{
370}
371
372/*
373 * appendPQExpBufferChar
374 * Append a single byte to str.
375 * Like appendPQExpBuffer(str, "%c", ch) but much faster.
376 */
377void
379{
380 /* Make more room if needed */
381 if (!enlargePQExpBuffer(str, 1))
382 return;
383
384 /* OK, append the character */
385 str->data[str->len] = ch;
386 str->len++;
387 str->data[str->len] = '\0';
388}
389
390/*
391 * appendBinaryPQExpBuffer
392 *
393 * Append arbitrary binary data to a PQExpBuffer, allocating more space
394 * if necessary.
395 */
396void
397appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
398{
399 /* Make more room if needed */
400 if (!enlargePQExpBuffer(str, datalen))
401 return;
402
403 /* OK, append the data */
404 memcpy(str->data + str->len, data, datalen);
405 str->len += datalen;
406
407 /*
408 * Keep a trailing null in place, even though it's probably useless for
409 * binary data...
410 */
411 str->data[str->len] = '\0';
412}
#define unconstify(underlying_type, expr)
Definition: c.h:1216
#define unlikely(x)
Definition: c.h:347
const char * str
#define realloc(a, b)
Definition: header.h:60
#define free(a)
Definition: header.h:65
#define malloc(a)
Definition: header.h:50
const void * data
#define vsnprintf
Definition: port.h:238
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:235
PQExpBuffer createPQExpBuffer(void)
Definition: pqexpbuffer.c:72
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:90
int enlargePQExpBuffer(PQExpBuffer str, size_t needed)
Definition: pqexpbuffer.c:172
void resetPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:146
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:265
void appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
Definition: pqexpbuffer.c:397
void destroyPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:114
static const char *const oom_buffer_ptr
Definition: pqexpbuffer.c:41
static const char oom_buffer[1]
Definition: pqexpbuffer.c:38
bool appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args)
Definition: pqexpbuffer.c:294
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:378
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:367
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:129
static void markPQExpBufferBroken(PQExpBuffer str)
Definition: pqexpbuffer.c:50
#define PQExpBufferBroken(str)
Definition: pqexpbuffer.h:59
PQExpBufferData * PQExpBuffer
Definition: pqexpbuffer.h:51
#define INITIAL_EXPBUFFER_SIZE
Definition: pqexpbuffer.h:76