LCOV - code coverage report
Current view: top level - src/interfaces/libpq - pqexpbuffer.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 77.2 % 114 88
Test Date: 2026-02-17 17:20:33 Functions: 92.3 % 13 12
Legend: Lines:     hit not hit

            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       456043 : createPQExpBuffer(void)
      73              : {
      74              :     PQExpBuffer res;
      75              : 
      76       456043 :     res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
      77       456043 :     if (res != NULL)
      78       456043 :         initPQExpBuffer(res);
      79              : 
      80       456043 :     return res;
      81              : }
      82              : 
      83              : /*
      84              :  * initPQExpBuffer
      85              :  *
      86              :  * Initialize a PQExpBufferData struct (with previously undefined contents)
      87              :  * to describe an empty string.
      88              :  */
      89              : void
      90      1052159 : initPQExpBuffer(PQExpBuffer str)
      91              : {
      92      1052159 :     str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
      93      1052159 :     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              :     }
      99              :     else
     100              :     {
     101      1052159 :         str->maxlen = INITIAL_EXPBUFFER_SIZE;
     102      1052159 :         str->len = 0;
     103      1052159 :         str->data[0] = '\0';
     104              :     }
     105      1052159 : }
     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       457822 : destroyPQExpBuffer(PQExpBuffer str)
     115              : {
     116       457822 :     if (str)
     117              :     {
     118       444910 :         termPQExpBuffer(str);
     119       444910 :         free(str);
     120              :     }
     121       457822 : }
     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       923666 : termPQExpBuffer(PQExpBuffer str)
     130              : {
     131       923666 :     if (str->data != oom_buffer)
     132       923666 :         free(str->data);
     133              :     /* just for luck, make the buffer validly empty. */
     134       923666 :     str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
     135       923666 :     str->maxlen = 0;
     136       923666 :     str->len = 0;
     137       923666 : }
     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      3994020 : resetPQExpBuffer(PQExpBuffer str)
     147              : {
     148      3994020 :     if (str)
     149              :     {
     150      3993343 :         if (str->data != oom_buffer)
     151              :         {
     152      3993343 :             str->len = 0;
     153      3993343 :             str->data[0] = '\0';
     154              :         }
     155              :         else
     156              :         {
     157              :             /* try to reinitialize to valid state */
     158            0 :             initPQExpBuffer(str);
     159              :         }
     160              :     }
     161      3994020 : }
     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     10229482 : enlargePQExpBuffer(PQExpBuffer str, size_t needed)
     173              : {
     174              :     size_t      newlen;
     175              :     char       *newdata;
     176              : 
     177     10229482 :     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     10229482 :     if (needed >= ((size_t) INT_MAX - str->len))
     186              :     {
     187            0 :         markPQExpBufferBroken(str);
     188            0 :         return 0;
     189              :     }
     190              : 
     191     10229482 :     needed += str->len + 1;      /* total space required now */
     192              : 
     193              :     /* Because of the above test, we now have needed <= INT_MAX */
     194              : 
     195     10229482 :     if (needed <= str->maxlen)
     196     10135821 :         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        93661 :     newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64;
     204        97934 :     while (needed > newlen)
     205         4273 :         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        93661 :     if (newlen > (size_t) INT_MAX)
     213            0 :         newlen = (size_t) INT_MAX;
     214              : 
     215        93661 :     newdata = (char *) realloc(str->data, newlen);
     216        93661 :     if (newdata != NULL)
     217              :     {
     218        93661 :         str->data = newdata;
     219        93661 :         str->maxlen = newlen;
     220        93661 :         return 1;
     221              :     }
     222              : 
     223            0 :     markPQExpBufferBroken(str);
     224            0 :     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              :  */
     234              : void
     235       244581 : printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
     236              : {
     237       244581 :     int         save_errno = errno;
     238              :     va_list     args;
     239              :     bool        done;
     240              : 
     241       244581 :     resetPQExpBuffer(str);
     242              : 
     243       244581 :     if (PQExpBufferBroken(str))
     244            0 :         return;                 /* already failed */
     245              : 
     246              :     /* Loop in case we have to retry after enlarging the buffer. */
     247              :     do
     248              :     {
     249       247276 :         errno = save_errno;
     250       247276 :         va_start(args, fmt);
     251       247276 :         done = appendPQExpBufferVA(str, fmt, args);
     252       247276 :         va_end(args);
     253       247276 :     } 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              :  */
     264              : void
     265       662892 : appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
     266              : {
     267       662892 :     int         save_errno = errno;
     268              :     va_list     args;
     269              :     bool        done;
     270              : 
     271       662892 :     if (PQExpBufferBroken(str))
     272            0 :         return;                 /* already failed */
     273              : 
     274              :     /* Loop in case we have to retry after enlarging the buffer. */
     275              :     do
     276              :     {
     277       743619 :         errno = save_errno;
     278       743619 :         va_start(args, fmt);
     279       743619 :         done = appendPQExpBufferVA(str, fmt, args);
     280       743619 :         va_end(args);
     281       743619 :     } 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              :  */
     293              : bool
     294       991636 : appendPQExpBufferVA(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       991636 :     if (str->maxlen > str->len + 16)
     305              :     {
     306       988526 :         avail = str->maxlen - str->len;
     307              : 
     308       988526 :         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       988526 :         if (unlikely(nprinted < 0))
     315              :         {
     316            0 :             markPQExpBufferBroken(str);
     317            0 :             return true;
     318              :         }
     319              : 
     320       988526 :         if ((size_t) nprinted < avail)
     321              :         {
     322              :             /* Success.  Note nprinted does not include trailing null. */
     323       908208 :             str->len += nprinted;
     324       908208 :             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        80318 :         if (unlikely(nprinted > INT_MAX - 1))
     336              :         {
     337            0 :             markPQExpBufferBroken(str);
     338            0 :             return true;
     339              :         }
     340        80318 :         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         3110 :         needed = 32;
     352              :     }
     353              : 
     354              :     /* Increase the buffer size and try again. */
     355        83428 :     if (!enlargePQExpBuffer(str, needed))
     356            0 :         return true;            /* oops, out of memory */
     357              : 
     358        83428 :     return false;
     359              : }
     360              : 
     361              : /*
     362              :  * appendPQExpBufferStr
     363              :  * Append the given string to a PQExpBuffer, allocating more space
     364              :  * if necessary.
     365              :  */
     366              : void
     367      1349356 : appendPQExpBufferStr(PQExpBuffer str, const char *data)
     368              : {
     369      1349356 :     appendBinaryPQExpBuffer(str, data, strlen(data));
     370      1349356 : }
     371              : 
     372              : /*
     373              :  * appendPQExpBufferChar
     374              :  * Append a single byte to str.
     375              :  * Like appendPQExpBuffer(str, "%c", ch) but much faster.
     376              :  */
     377              : void
     378      1562782 : appendPQExpBufferChar(PQExpBuffer str, char ch)
     379              : {
     380              :     /* Make more room if needed */
     381      1562782 :     if (!enlargePQExpBuffer(str, 1))
     382            0 :         return;
     383              : 
     384              :     /* OK, append the character */
     385      1562782 :     str->data[str->len] = ch;
     386      1562782 :     str->len++;
     387      1562782 :     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              :  */
     396              : void
     397      8540230 : appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
     398              : {
     399              :     /* Make more room if needed */
     400      8540230 :     if (!enlargePQExpBuffer(str, datalen))
     401            0 :         return;
     402              : 
     403              :     /* OK, append the data */
     404      8540230 :     memcpy(str->data + str->len, data, datalen);
     405      8540230 :     str->len += datalen;
     406              : 
     407              :     /*
     408              :      * Keep a trailing null in place, even though it's probably useless for
     409              :      * binary data...
     410              :      */
     411      8540230 :     str->data[str->len] = '\0';
     412              : }
        

Generated by: LCOV version 2.0-1