8
8
*
9
9
* This module is essentially the same as the backend's StringInfo data type,
10
10
* but it is intended for use in frontend libpq and client applications.
11
- * Thus, it does not rely on palloc() nor elog().
11
+ * Thus, it does not rely on palloc() nor elog(), nor psprintf.c which
12
+ * will exit() on error.
12
13
*
13
14
* It does rely on vsnprintf(); if configure finds that libc doesn't provide
14
15
* a usable vsnprintf(), then a copy of our own implementation of it will
36
37
/* All "broken" PQExpBuffers point to this string. */
37
38
static const char oom_buffer [1 ] = "" ;
38
39
40
+ static bool
41
+ appendPQExpBufferVA (PQExpBuffer str , const char * fmt , va_list args )
42
+ __attribute__((format (PG_PRINTF_ATTRIBUTE , 2 , 0 )));
43
+
39
44
40
45
/*
41
46
* markPQExpBufferBroken
@@ -231,45 +236,20 @@ void
231
236
printfPQExpBuffer (PQExpBuffer str , const char * fmt ,...)
232
237
{
233
238
va_list args ;
234
- size_t avail ;
235
- int nprinted ;
239
+ bool done ;
236
240
237
241
resetPQExpBuffer (str );
238
242
239
243
if (PQExpBufferBroken (str ))
240
244
return ; /* already failed */
241
245
242
- for (;;)
246
+ /* Loop in case we have to retry after enlarging the buffer. */
247
+ do
243
248
{
244
- /*
245
- * Try to format the given string into the available space; but if
246
- * there's hardly any space, don't bother trying, just fall through to
247
- * enlarge the buffer first.
248
- */
249
- if (str -> maxlen > str -> len + 16 )
250
- {
251
- avail = str -> maxlen - str -> len - 1 ;
252
- va_start (args , fmt );
253
- nprinted = vsnprintf (str -> data + str -> len , avail ,
254
- fmt , args );
255
- va_end (args );
256
-
257
- /*
258
- * Note: some versions of vsnprintf return the number of chars
259
- * actually stored, but at least one returns -1 on failure. Be
260
- * conservative about believing whether the print worked.
261
- */
262
- if (nprinted >= 0 && nprinted < (int ) avail - 1 )
263
- {
264
- /* Success. Note nprinted does not include trailing null. */
265
- str -> len += nprinted ;
266
- break ;
267
- }
268
- }
269
- /* Double the buffer size and try again. */
270
- if (!enlargePQExpBuffer (str , str -> maxlen ))
271
- return ; /* oops, out of memory */
272
- }
249
+ va_start (args , fmt );
250
+ done = appendPQExpBufferVA (str , fmt , args );
251
+ va_end (args );
252
+ } while (!done );
273
253
}
274
254
275
255
/*
@@ -284,43 +264,118 @@ void
284
264
appendPQExpBuffer (PQExpBuffer str , const char * fmt ,...)
285
265
{
286
266
va_list args ;
287
- size_t avail ;
288
- int nprinted ;
267
+ bool done ;
289
268
290
269
if (PQExpBufferBroken (str ))
291
270
return ; /* already failed */
292
271
293
- for (;;)
272
+ /* Loop in case we have to retry after enlarging the buffer. */
273
+ do
274
+ {
275
+ va_start (args , fmt );
276
+ done = appendPQExpBufferVA (str , fmt , args );
277
+ va_end (args );
278
+ } while (!done );
279
+ }
280
+
281
+ /*
282
+ * appendPQExpBufferVA
283
+ * Shared guts of printfPQExpBuffer/appendPQExpBuffer.
284
+ * Attempt to format data and append it to str. Returns true if done
285
+ * (either successful or hard failure), false if need to retry.
286
+ */
287
+ static bool
288
+ appendPQExpBufferVA (PQExpBuffer str , const char * fmt , va_list args )
289
+ {
290
+ size_t avail ;
291
+ size_t needed ;
292
+ int nprinted ;
293
+
294
+ /*
295
+ * Try to format the given string into the available space; but if there's
296
+ * hardly any space, don't bother trying, just enlarge the buffer first.
297
+ */
298
+ if (str -> maxlen > str -> len + 16 )
294
299
{
295
300
/*
296
- * Try to format the given string into the available space; but if
297
- * there's hardly any space, don't bother trying, just fall through to
298
- * enlarge the buffer first.
301
+ * Note: we intentionally leave one byte unused, as a guard against
302
+ * old broken versions of vsnprintf.
299
303
*/
300
- if (str -> maxlen > str -> len + 16 )
304
+ avail = str -> maxlen - str -> len - 1 ;
305
+
306
+ errno = 0 ;
307
+
308
+ nprinted = vsnprintf (str -> data + str -> len , avail , fmt , args );
309
+
310
+ /*
311
+ * If vsnprintf reports an error other than ENOMEM, fail.
312
+ */
313
+ if (nprinted < 0 && errno != 0 && errno != ENOMEM )
301
314
{
302
- avail = str -> maxlen - str -> len - 1 ;
303
- va_start (args , fmt );
304
- nprinted = vsnprintf (str -> data + str -> len , avail ,
305
- fmt , args );
306
- va_end (args );
315
+ markPQExpBufferBroken (str );
316
+ return true;
317
+ }
307
318
319
+ /*
320
+ * Note: some versions of vsnprintf return the number of chars
321
+ * actually stored, not the total space needed as C99 specifies. And
322
+ * at least one returns -1 on failure. Be conservative about
323
+ * believing whether the print worked.
324
+ */
325
+ if (nprinted >= 0 && (size_t ) nprinted < avail - 1 )
326
+ {
327
+ /* Success. Note nprinted does not include trailing null. */
328
+ str -> len += nprinted ;
329
+ return true;
330
+ }
331
+
332
+ if (nprinted >= 0 && (size_t ) nprinted > avail )
333
+ {
308
334
/*
309
- * Note: some versions of vsnprintf return the number of chars
310
- * actually stored, but at least one returns -1 on failure. Be
311
- * conservative about believing whether the print worked.
335
+ * This appears to be a C99-compliant vsnprintf, so believe its
336
+ * estimate of the required space. (If it's wrong, the logic will
337
+ * still work, but we may loop multiple times.) Note that the
338
+ * space needed should be only nprinted+1 bytes, but we'd better
339
+ * allocate one more than that so that the test above will succeed
340
+ * next time.
341
+ *
342
+ * In the corner case where the required space just barely
343
+ * overflows, fail.
312
344
*/
313
- if (nprinted >= 0 && nprinted < ( int ) avail - 1 )
345
+ if (nprinted > INT_MAX - 2 )
314
346
{
315
- /* Success. Note nprinted does not include trailing null. */
316
- str -> len += nprinted ;
317
- break ;
347
+ markPQExpBufferBroken (str );
348
+ return true;
318
349
}
350
+ needed = nprinted + 2 ;
319
351
}
320
- /* Double the buffer size and try again. */
321
- if (!enlargePQExpBuffer (str , str -> maxlen ))
322
- return ; /* oops, out of memory */
352
+ else
353
+ {
354
+ /*
355
+ * Buffer overrun, and we don't know how much space is needed.
356
+ * Estimate twice the previous buffer size, but not more than
357
+ * INT_MAX.
358
+ */
359
+ if (avail >= INT_MAX / 2 )
360
+ needed = INT_MAX ;
361
+ else
362
+ needed = avail * 2 ;
363
+ }
364
+ }
365
+ else
366
+ {
367
+ /*
368
+ * We have to guess at how much to enlarge, since we're skipping the
369
+ * formatting work.
370
+ */
371
+ needed = 32 ;
323
372
}
373
+
374
+ /* Increase the buffer size and try again. */
375
+ if (!enlargePQExpBuffer (str , needed ))
376
+ return true; /* oops, out of memory */
377
+
378
+ return false;
324
379
}
325
380
326
381
/*
0 commit comments