1 /*-------------------------------------------------------------------------
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().
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.
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.
18 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
19 * Portions Copyright (c) 1994, Regents of the University of California
21 * src/interfaces/libpq/pqexpbuffer.c
23 *-------------------------------------------------------------------------
26 #include "postgres_fe.h"
30 #include "pqexpbuffer.h"
37 /* All "broken" PQExpBuffers point to this string. */
38 static const char oom_buffer
[1] = "";
40 /* Need a char * for unconstify() compatibility */
41 static const char *const oom_buffer_ptr
= oom_buffer
;
45 * markPQExpBufferBroken
47 * Put a PQExpBuffer in "broken" state if it isn't already.
50 markPQExpBufferBroken(PQExpBuffer str
)
52 if (str
->data
!= oom_buffer
)
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.
61 str
->data
= unconstify(char *, oom_buffer_ptr
);
69 * Create an empty 'PQExpBufferData' & return a pointer to it.
72 createPQExpBuffer(void)
76 res
= (PQExpBuffer
) malloc(sizeof(PQExpBufferData
));
86 * Initialize a PQExpBufferData struct (with previously undefined contents)
87 * to describe an empty string.
90 initPQExpBuffer(PQExpBuffer str
)
92 str
->data
= (char *) malloc(INITIAL_EXPBUFFER_SIZE
);
93 if (str
->data
== NULL
)
95 str
->data
= unconstify(char *, oom_buffer_ptr
); /* see comment above */
101 str
->maxlen
= INITIAL_EXPBUFFER_SIZE
;
108 * destroyPQExpBuffer(str);
110 * free()s both the data buffer and the PQExpBufferData.
111 * This is the inverse of createPQExpBuffer().
114 destroyPQExpBuffer(PQExpBuffer str
)
118 termPQExpBuffer(str
);
124 * termPQExpBuffer(str)
125 * free()s the data buffer but not the PQExpBufferData itself.
126 * This is the inverse of initPQExpBuffer().
129 termPQExpBuffer(PQExpBuffer str
)
131 if (str
->data
!= oom_buffer
)
133 /* just for luck, make the buffer validly empty. */
134 str
->data
= unconstify(char *, oom_buffer_ptr
); /* see comment above */
141 * Reset a PQExpBuffer to empty
143 * Note: if possible, a "broken" PQExpBuffer is returned to normal.
146 resetPQExpBuffer(PQExpBuffer str
)
150 if (str
->data
!= oom_buffer
)
157 /* try to reinitialize to valid state */
158 initPQExpBuffer(str
);
165 * Make sure there is enough space for 'needed' more bytes in the buffer
166 * ('needed' does not include the terminating null).
168 * Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case
169 * the buffer is left in "broken" state.)
172 enlargePQExpBuffer(PQExpBuffer str
, size_t needed
)
177 if (PQExpBufferBroken(str
))
178 return 0; /* already failed */
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
185 if (needed
>= ((size_t) INT_MAX
- str
->len
))
187 markPQExpBufferBroken(str
);
191 needed
+= str
->len
+ 1; /* total space required now */
193 /* Because of the above test, we now have needed <= INT_MAX */
195 if (needed
<= str
->maxlen
)
196 return 1; /* got enough space already */
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...
203 newlen
= (str
->maxlen
> 0) ? (2 * str
->maxlen
) : 64;
204 while (needed
> newlen
)
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.
212 if (newlen
> (size_t) INT_MAX
)
213 newlen
= (size_t) INT_MAX
;
215 newdata
= (char *) realloc(str
->data
, newlen
);
219 str
->maxlen
= newlen
;
223 markPQExpBufferBroken(str
);
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().
235 printfPQExpBuffer(PQExpBuffer str
, const char *fmt
,...)
237 int save_errno
= errno
;
241 resetPQExpBuffer(str
);
243 if (PQExpBufferBroken(str
))
244 return; /* already failed */
246 /* Loop in case we have to retry after enlarging the buffer. */
251 done
= appendPQExpBufferVA(str
, fmt
, args
);
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
265 appendPQExpBuffer(PQExpBuffer str
, const char *fmt
,...)
267 int save_errno
= errno
;
271 if (PQExpBufferBroken(str
))
272 return; /* already failed */
274 /* Loop in case we have to retry after enlarging the buffer. */
279 done
= appendPQExpBufferVA(str
, fmt
, args
);
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.
290 * Caution: callers must be sure to preserve their entry-time errno
291 * when looping, in case the fmt contains "%m".
294 appendPQExpBufferVA(PQExpBuffer str
, const char *fmt
, va_list args
)
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.
304 if (str
->maxlen
> str
->len
+ 16)
306 avail
= str
->maxlen
- str
->len
;
308 nprinted
= vsnprintf(str
->data
+ str
->len
, avail
, fmt
, args
);
311 * If vsnprintf reports an error, fail (we assume this means there's
312 * something wrong with the format string).
314 if (unlikely(nprinted
< 0))
316 markPQExpBufferBroken(str
);
320 if ((size_t) nprinted
< avail
)
322 /* Success. Note nprinted does not include trailing null. */
323 str
->len
+= nprinted
;
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.)
332 * Choke if the required space would exceed INT_MAX, since str->maxlen
333 * can't represent more than that.
335 if (unlikely(nprinted
> INT_MAX
- 1))
337 markPQExpBufferBroken(str
);
340 needed
= nprinted
+ 1;
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.
354 /* Increase the buffer size and try again. */
355 if (!enlargePQExpBuffer(str
, needed
))
356 return true; /* oops, out of memory */
362 * appendPQExpBufferStr
363 * Append the given string to a PQExpBuffer, allocating more space
367 appendPQExpBufferStr(PQExpBuffer str
, const char *data
)
369 appendBinaryPQExpBuffer(str
, data
, strlen(data
));
373 * appendPQExpBufferChar
374 * Append a single byte to str.
375 * Like appendPQExpBuffer(str, "%c", ch) but much faster.
378 appendPQExpBufferChar(PQExpBuffer str
, char ch
)
380 /* Make more room if needed */
381 if (!enlargePQExpBuffer(str
, 1))
384 /* OK, append the character */
385 str
->data
[str
->len
] = ch
;
387 str
->data
[str
->len
] = '\0';
391 * appendBinaryPQExpBuffer
393 * Append arbitrary binary data to a PQExpBuffer, allocating more space
397 appendBinaryPQExpBuffer(PQExpBuffer str
, const char *data
, size_t datalen
)
399 /* Make more room if needed */
400 if (!enlargePQExpBuffer(str
, datalen
))
403 /* OK, append the data */
404 memcpy(str
->data
+ str
->len
, data
, datalen
);
408 * Keep a trailing null in place, even though it's probably useless for
411 str
->data
[str
->len
] = '\0';