Branch data Line data Source code
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-2026, 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. */
38 : : static const char oom_buffer[1] = "";
39 : :
40 : : /* Need a char * for unconstify() compatibility */
41 : : static 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 : : */
49 : : static void
50 : 0 : markPQExpBufferBroken(PQExpBuffer str)
51 : : {
52 [ # # ]: 0 : if (str->data != oom_buffer)
53 : 0 : 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 : 0 : str->data = unconstify(char *, oom_buffer_ptr);
62 : 0 : str->len = 0;
63 : 0 : str->maxlen = 0;
64 : 0 : }
65 : :
66 : : /*
67 : : * createPQExpBuffer
68 : : *
69 : : * Create an empty 'PQExpBufferData' & return a pointer to it.
70 : : */
71 : : PQExpBuffer
72 : 1220 : createPQExpBuffer(void)
73 : : {
74 : 1220 : PQExpBuffer res;
75 : :
76 : 1220 : res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
77 [ - + ]: 1220 : if (res != NULL)
78 : 1220 : initPQExpBuffer(res);
79 : :
80 : 2440 : return res;
81 : 1220 : }
82 : :
83 : : /*
84 : : * initPQExpBuffer
85 : : *
86 : : * Initialize a PQExpBufferData struct (with previously undefined contents)
87 : : * to describe an empty string.
88 : : */
89 : : void
90 : 36496 : initPQExpBuffer(PQExpBuffer str)
91 : : {
92 : 36496 : str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
93 [ + - ]: 36496 : if (str->data == NULL)
94 : : {
95 : 0 : str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
96 : 0 : str->maxlen = 0;
97 : 0 : str->len = 0;
98 : 0 : }
99 : : else
100 : : {
101 : 36496 : str->maxlen = INITIAL_EXPBUFFER_SIZE;
102 : 36496 : str->len = 0;
103 : 36496 : str->data[0] = '\0';
104 : : }
105 : 36496 : }
106 : :
107 : : /*
108 : : * destroyPQExpBuffer(str);
109 : : *
110 : : * free()s both the data buffer and the PQExpBufferData.
111 : : * This is the inverse of createPQExpBuffer().
112 : : */
113 : : void
114 : 931 : destroyPQExpBuffer(PQExpBuffer str)
115 : : {
116 [ - + ]: 931 : if (str)
117 : : {
118 : 931 : termPQExpBuffer(str);
119 : 931 : free(str);
120 : 931 : }
121 : 931 : }
122 : :
123 : : /*
124 : : * termPQExpBuffer(str)
125 : : * free()s the data buffer but not the PQExpBufferData itself.
126 : : * This is the inverse of initPQExpBuffer().
127 : : */
128 : : void
129 : 30798 : termPQExpBuffer(PQExpBuffer str)
130 : : {
131 [ - + ]: 30798 : if (str->data != oom_buffer)
132 : 30798 : free(str->data);
133 : : /* just for luck, make the buffer validly empty. */
134 : 30798 : str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
135 : 30798 : str->maxlen = 0;
136 : 30798 : str->len = 0;
137 : 30798 : }
138 : :
139 : : /*
140 : : * resetPQExpBuffer
141 : : * Reset a PQExpBuffer to empty
142 : : *
143 : : * Note: if possible, a "broken" PQExpBuffer is returned to normal.
144 : : */
145 : : void
146 : 472463 : resetPQExpBuffer(PQExpBuffer str)
147 : : {
148 [ - + ]: 472463 : if (str)
149 : : {
150 [ + - ]: 472463 : if (str->data != oom_buffer)
151 : : {
152 : 472463 : str->len = 0;
153 : 472463 : str->data[0] = '\0';
154 : 472463 : }
155 : : else
156 : : {
157 : : /* try to reinitialize to valid state */
158 : 0 : initPQExpBuffer(str);
159 : : }
160 : 472463 : }
161 : 472463 : }
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 : : */
171 : : int
172 : 1709386 : enlargePQExpBuffer(PQExpBuffer str, size_t needed)
173 : : {
174 : 1709386 : size_t newlen;
175 : 1709386 : char *newdata;
176 : :
177 [ + - - + ]: 1709386 : if (PQExpBufferBroken(str))
178 : 0 : 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 [ - + ]: 1709386 : if (needed >= ((size_t) INT_MAX - str->len))
186 : : {
187 : 0 : markPQExpBufferBroken(str);
188 : 0 : return 0;
189 : : }
190 : :
191 : 1709386 : needed += str->len + 1; /* total space required now */
192 : :
193 : : /* Because of the above test, we now have needed <= INT_MAX */
194 : :
195 [ + + ]: 1709386 : if (needed <= str->maxlen)
196 : 1706147 : 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 [ + - ]: 3239 : newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64;
204 [ + + ]: 3629 : while (needed > newlen)
205 : 390 : 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 [ + - ]: 3239 : if (newlen > (size_t) INT_MAX)
213 : 0 : newlen = (size_t) INT_MAX;
214 : :
215 : 3239 : newdata = (char *) realloc(str->data, newlen);
216 [ + - ]: 3239 : if (newdata != NULL)
217 : : {
218 : 3239 : str->data = newdata;
219 : 3239 : str->maxlen = newlen;
220 : 3239 : return 1;
221 : : }
222 : :
223 : 0 : markPQExpBufferBroken(str);
224 : 0 : return 0;
225 : 1709386 : }
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 : : */
234 : : void
235 : 9367 : printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
236 : : {
237 : 9367 : int save_errno = errno;
238 : 9367 : va_list args;
239 : 9367 : bool done;
240 : :
241 : 9367 : resetPQExpBuffer(str);
242 : :
243 [ + - + - ]: 9367 : if (PQExpBufferBroken(str))
244 : 0 : return; /* already failed */
245 : :
246 : : /* Loop in case we have to retry after enlarging the buffer. */
247 : 9367 : do
248 : : {
249 : 10229 : errno = save_errno;
250 : 10229 : va_start(args, fmt);
251 : 10229 : done = appendPQExpBufferVA(str, fmt, args);
252 : 10229 : va_end(args);
253 [ + + ]: 10229 : } while (!done);
254 [ - + ]: 9367 : }
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 : : */
264 : : void
265 : 19540 : appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
266 : : {
267 : 19540 : int save_errno = errno;
268 : 19540 : va_list args;
269 : 19540 : bool done;
270 : :
271 [ + - + - ]: 19540 : if (PQExpBufferBroken(str))
272 : 0 : return; /* already failed */
273 : :
274 : : /* Loop in case we have to retry after enlarging the buffer. */
275 : 19540 : do
276 : : {
277 : 21117 : errno = save_errno;
278 : 21117 : va_start(args, fmt);
279 : 21117 : done = appendPQExpBufferVA(str, fmt, args);
280 : 21117 : va_end(args);
281 [ + + ]: 21117 : } while (!done);
282 [ - + ]: 19540 : }
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 : : */
293 : : bool
294 : 31355 : appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args)
295 : : {
296 : 31355 : size_t avail;
297 : 31355 : size_t needed;
298 : 31355 : 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 [ + + ]: 31355 : if (str->maxlen > str->len + 16)
305 : : {
306 : 30930 : avail = str->maxlen - str->len;
307 : :
308 : 30930 : 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 [ - + ]: 30930 : if (unlikely(nprinted < 0))
315 : : {
316 : 0 : markPQExpBufferBroken(str);
317 : 0 : return true;
318 : : }
319 : :
320 [ + + ]: 30930 : if ((size_t) nprinted < avail)
321 : : {
322 : : /* Success. Note nprinted does not include trailing null. */
323 : 28916 : str->len += nprinted;
324 : 28916 : 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 [ - + ]: 2014 : if (unlikely(nprinted > INT_MAX - 1))
336 : : {
337 : 0 : markPQExpBufferBroken(str);
338 : 0 : return true;
339 : : }
340 : 2014 : needed = nprinted + 1;
341 : 2014 : }
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 : 425 : needed = 32;
352 : : }
353 : :
354 : : /* Increase the buffer size and try again. */
355 [ + - ]: 2439 : if (!enlargePQExpBuffer(str, needed))
356 : 0 : return true; /* oops, out of memory */
357 : :
358 : 2439 : return false;
359 : 31355 : }
360 : :
361 : : /*
362 : : * appendPQExpBufferStr
363 : : * Append the given string to a PQExpBuffer, allocating more space
364 : : * if necessary.
365 : : */
366 : : void
367 : 157370 : appendPQExpBufferStr(PQExpBuffer str, const char *data)
368 : : {
369 : 157370 : appendBinaryPQExpBuffer(str, data, strlen(data));
370 : 157370 : }
371 : :
372 : : /*
373 : : * appendPQExpBufferChar
374 : : * Append a single byte to str.
375 : : * Like appendPQExpBuffer(str, "%c", ch) but much faster.
376 : : */
377 : : void
378 : 136216 : appendPQExpBufferChar(PQExpBuffer str, char ch)
379 : : {
380 : : /* Make more room if needed */
381 [ + - ]: 136216 : if (!enlargePQExpBuffer(str, 1))
382 : 0 : return;
383 : :
384 : : /* OK, append the character */
385 : 136216 : str->data[str->len] = ch;
386 : 136216 : str->len++;
387 : 136216 : str->data[str->len] = '\0';
388 : 136216 : }
389 : :
390 : : /*
391 : : * appendBinaryPQExpBuffer
392 : : *
393 : : * Append arbitrary binary data to a PQExpBuffer, allocating more space
394 : : * if necessary.
395 : : */
396 : : void
397 : 1569350 : appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
398 : : {
399 : : /* Make more room if needed */
400 [ + - ]: 1569350 : if (!enlargePQExpBuffer(str, datalen))
401 : 0 : return;
402 : :
403 : : /* OK, append the data */
404 : 1569350 : memcpy(str->data + str->len, data, datalen);
405 : 1569350 : str->len += datalen;
406 : :
407 : : /*
408 : : * Keep a trailing null in place, even though it's probably useless for
409 : : * binary data...
410 : : */
411 : 1569350 : str->data[str->len] = '\0';
412 : 1569350 : }
|