LCOV - code coverage report
Current view: top level - src/interfaces/libpq - pqexpbuffer.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 80.4 % 138 111
Test Date: 2026-01-26 10:56:24 Functions: 92.3 % 13 12
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 58.3 % 60 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                 :        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 : }
        

Generated by: LCOV version 2.3.2-1