LCOV - code coverage report
Current view: top level - src/interfaces/libpq - pqexpbuffer.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 77.2 % 114 88
Test Date: 2026-07-22 18:15:44 Functions: 92.3 % 13 12
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 62.5 % 56 35

             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                 :      494287 : createPQExpBuffer(void)
      73                 :             : {
      74                 :             :     PQExpBuffer res;
      75                 :             : 
      76                 :      494287 :     res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
      77         [ +  - ]:      494287 :     if (res != NULL)
      78                 :      494287 :         initPQExpBuffer(res);
      79                 :             : 
      80                 :      494287 :     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                 :     1258378 : initPQExpBuffer(PQExpBuffer str)
      91                 :             : {
      92                 :     1258378 :     str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
      93         [ -  + ]:     1258378 :     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                 :     1258378 :         str->maxlen = INITIAL_EXPBUFFER_SIZE;
     102                 :     1258378 :         str->len = 0;
     103                 :     1258378 :         str->data[0] = '\0';
     104                 :             :     }
     105                 :     1258378 : }
     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                 :      496341 : destroyPQExpBuffer(PQExpBuffer str)
     115                 :             : {
     116         [ +  + ]:      496341 :     if (str)
     117                 :             :     {
     118                 :      482142 :         termPQExpBuffer(str);
     119                 :      482142 :         free(str);
     120                 :             :     }
     121                 :      496341 : }
     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                 :     1119028 : termPQExpBuffer(PQExpBuffer str)
     130                 :             : {
     131         [ +  - ]:     1119028 :     if (str->data != oom_buffer)
     132                 :     1119028 :         free(str->data);
     133                 :             :     /* just for luck, make the buffer validly empty. */
     134                 :     1119028 :     str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
     135                 :     1119028 :     str->maxlen = 0;
     136                 :     1119028 :     str->len = 0;
     137                 :     1119028 : }
     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                 :     5801039 : resetPQExpBuffer(PQExpBuffer str)
     147                 :             : {
     148         [ +  + ]:     5801039 :     if (str)
     149                 :             :     {
     150         [ +  - ]:     5800335 :         if (str->data != oom_buffer)
     151                 :             :         {
     152                 :     5800335 :             str->len = 0;
     153                 :     5800335 :             str->data[0] = '\0';
     154                 :             :         }
     155                 :             :         else
     156                 :             :         {
     157                 :             :             /* try to reinitialize to valid state */
     158                 :           0 :             initPQExpBuffer(str);
     159                 :             :         }
     160                 :             :     }
     161                 :     5801039 : }
     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                 :    14000393 : enlargePQExpBuffer(PQExpBuffer str, size_t needed)
     173                 :             : {
     174                 :             :     size_t      newlen;
     175                 :             :     char       *newdata;
     176                 :             : 
     177   [ +  -  -  + ]:    14000393 :     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         [ -  + ]:    14000393 :     if (needed >= ((size_t) INT_MAX - str->len))
     186                 :             :     {
     187                 :           0 :         markPQExpBufferBroken(str);
     188                 :           0 :         return 0;
     189                 :             :     }
     190                 :             : 
     191                 :    14000393 :     needed += str->len + 1;      /* total space required now */
     192                 :             : 
     193                 :             :     /* Because of the above test, we now have needed <= INT_MAX */
     194                 :             : 
     195         [ +  + ]:    14000393 :     if (needed <= str->maxlen)
     196                 :    13798449 :         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         [ +  - ]:      201944 :     newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64;
     204         [ +  + ]:      208269 :     while (needed > newlen)
     205                 :        6325 :         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         [ -  + ]:      201944 :     if (newlen > (size_t) INT_MAX)
     213                 :           0 :         newlen = (size_t) INT_MAX;
     214                 :             : 
     215                 :      201944 :     newdata = (char *) realloc(str->data, newlen);
     216         [ +  - ]:      201944 :     if (newdata != NULL)
     217                 :             :     {
     218                 :      201944 :         str->data = newdata;
     219                 :      201944 :         str->maxlen = newlen;
     220                 :      201944 :         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                 :      258454 : printfPQExpBuffer(PQExpBuffer str, const char *fmt, ...)
     236                 :             : {
     237                 :      258454 :     int         save_errno = errno;
     238                 :             :     va_list     args;
     239                 :             :     bool        done;
     240                 :             : 
     241                 :      258454 :     resetPQExpBuffer(str);
     242                 :             : 
     243   [ +  -  -  + ]:      258454 :     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                 :      258522 :         errno = save_errno;
     250                 :      258522 :         va_start(args, fmt);
     251                 :      258522 :         done = appendPQExpBufferVA(str, fmt, args);
     252                 :      258522 :         va_end(args);
     253         [ +  + ]:      258522 :     } 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                 :     1113009 : appendPQExpBuffer(PQExpBuffer str, const char *fmt, ...)
     266                 :             : {
     267                 :     1113009 :     int         save_errno = errno;
     268                 :             :     va_list     args;
     269                 :             :     bool        done;
     270                 :             : 
     271   [ +  -  -  + ]:     1113009 :     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                 :     1300833 :         errno = save_errno;
     278                 :     1300833 :         va_start(args, fmt);
     279                 :     1300833 :         done = appendPQExpBufferVA(str, fmt, args);
     280                 :     1300833 :         va_end(args);
     281         [ +  + ]:     1300833 :     } 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                 :     1560298 : 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         [ +  + ]:     1560298 :     if (str->maxlen > str->len + 16)
     305                 :             :     {
     306                 :     1557953 :         avail = str->maxlen - str->len;
     307                 :             : 
     308                 :     1557953 :         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         [ -  + ]:     1557953 :         if (unlikely(nprinted < 0))
     315                 :             :         {
     316                 :           0 :             markPQExpBufferBroken(str);
     317                 :           0 :             return true;
     318                 :             :         }
     319                 :             : 
     320         [ +  + ]:     1557953 :         if ((size_t) nprinted < avail)
     321                 :             :         {
     322                 :             :             /* Success.  Note nprinted does not include trailing null. */
     323                 :     1372400 :             str->len += nprinted;
     324                 :     1372400 :             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         [ -  + ]:      185553 :         if (unlikely(nprinted > INT_MAX - 1))
     336                 :             :         {
     337                 :           0 :             markPQExpBufferBroken(str);
     338                 :           0 :             return true;
     339                 :             :         }
     340                 :      185553 :         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                 :        2345 :         needed = 32;
     352                 :             :     }
     353                 :             : 
     354                 :             :     /* Increase the buffer size and try again. */
     355         [ -  + ]:      187898 :     if (!enlargePQExpBuffer(str, needed))
     356                 :           0 :         return true;            /* oops, out of memory */
     357                 :             : 
     358                 :      187898 :     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                 :     1715556 : appendPQExpBufferStr(PQExpBuffer str, const char *data)
     368                 :             : {
     369                 :     1715556 :     appendBinaryPQExpBuffer(str, data, strlen(data));
     370                 :     1715556 : }
     371                 :             : 
     372                 :             : /*
     373                 :             :  * appendPQExpBufferChar
     374                 :             :  * Append a single byte to str.
     375                 :             :  * Like appendPQExpBuffer(str, "%c", ch) but much faster.
     376                 :             :  */
     377                 :             : void
     378                 :     1924006 : appendPQExpBufferChar(PQExpBuffer str, char ch)
     379                 :             : {
     380                 :             :     /* Make more room if needed */
     381         [ -  + ]:     1924006 :     if (!enlargePQExpBuffer(str, 1))
     382                 :           0 :         return;
     383                 :             : 
     384                 :             :     /* OK, append the character */
     385                 :     1924006 :     str->data[str->len] = ch;
     386                 :     1924006 :     str->len++;
     387                 :     1924006 :     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                 :    11842553 : appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
     398                 :             : {
     399                 :             :     /* Make more room if needed */
     400         [ -  + ]:    11842553 :     if (!enlargePQExpBuffer(str, datalen))
     401                 :           0 :         return;
     402                 :             : 
     403                 :             :     /* OK, append the data */
     404                 :    11842553 :     memcpy(str->data + str->len, data, datalen);
     405                 :    11842553 :     str->len += datalen;
     406                 :             : 
     407                 :             :     /*
     408                 :             :      * Keep a trailing null in place, even though it's probably useless for
     409                 :             :      * binary data...
     410                 :             :      */
     411                 :    11842553 :     str->data[str->len] = '\0';
     412                 :             : }
        

Generated by: LCOV version 2.0-1