LCOV - differential code coverage report
Current view: top level - src/interfaces/libpq - pqexpbuffer.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC CBC EUB ECB
Current: Differential Code Coverage HEAD vs 15 Lines: 77.2 % 114 88 3 19 4 4 53 31 18 54
Current Date: 2023-04-08 15:15:32 Functions: 92.3 % 13 12 1 12 1 12
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  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-2023, 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 *oom_buffer_ptr = oom_buffer;
      42                 : 
      43                 : 
      44                 : /*
      45                 :  * markPQExpBufferBroken
      46                 :  *
      47                 :  * Put a PQExpBuffer in "broken" state if it isn't already.
      48 EUB             :  */
      49                 : static void
      50 UBC           0 : markPQExpBufferBroken(PQExpBuffer str)
      51 EUB             : {
      52 UIC           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 EUB             :      * scribble on a broken PQExpBuffer will get a failure.
      60                 :      */
      61 UBC           0 :     str->data = unconstify(char *, oom_buffer_ptr);
      62               0 :     str->len = 0;
      63 UIC           0 :     str->maxlen = 0;
      64               0 : }
      65                 : 
      66                 : /*
      67                 :  * createPQExpBuffer
      68                 :  *
      69                 :  * Create an empty 'PQExpBufferData' & return a pointer to it.
      70 ECB             :  */
      71                 : PQExpBuffer
      72 GIC      288432 : createPQExpBuffer(void)
      73                 : {
      74 ECB             :     PQExpBuffer res;
      75                 : 
      76 CBC      288432 :     res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
      77 GIC      288432 :     if (res != NULL)
      78 CBC      288432 :         initPQExpBuffer(res);
      79                 : 
      80 GIC      288432 :     return res;
      81                 : }
      82                 : 
      83                 : /*
      84                 :  * initPQExpBuffer
      85                 :  *
      86                 :  * Initialize a PQExpBufferData struct (with previously undefined contents)
      87                 :  * to describe an empty string.
      88 ECB             :  */
      89                 : void
      90 CBC      571531 : initPQExpBuffer(PQExpBuffer str)
      91 ECB             : {
      92 GIC      571531 :     str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
      93 GBC      571531 :     if (str->data == NULL)
      94 EUB             :     {
      95 UBC           0 :         str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
      96 UIC           0 :         str->maxlen = 0;
      97               0 :         str->len = 0;
      98                 :     }
      99 ECB             :     else
     100                 :     {
     101 CBC      571531 :         str->maxlen = INITIAL_EXPBUFFER_SIZE;
     102 GIC      571531 :         str->len = 0;
     103 CBC      571531 :         str->data[0] = '\0';
     104                 :     }
     105 GIC      571531 : }
     106                 : 
     107                 : /*
     108                 :  * destroyPQExpBuffer(str);
     109                 :  *
     110                 :  *      free()s both the data buffer and the PQExpBufferData.
     111                 :  *      This is the inverse of createPQExpBuffer().
     112 ECB             :  */
     113                 : void
     114 CBC      277228 : destroyPQExpBuffer(PQExpBuffer str)
     115                 : {
     116          277228 :     if (str)
     117 ECB             :     {
     118 GIC      277113 :         termPQExpBuffer(str);
     119 CBC      277113 :         free(str);
     120                 :     }
     121 GIC      277228 : }
     122                 : 
     123                 : /*
     124                 :  * termPQExpBuffer(str)
     125                 :  *      free()s the data buffer but not the PQExpBufferData itself.
     126                 :  *      This is the inverse of initPQExpBuffer().
     127 ECB             :  */
     128                 : void
     129 CBC      508544 : termPQExpBuffer(PQExpBuffer str)
     130 ECB             : {
     131 GIC      508544 :     if (str->data != oom_buffer)
     132 CBC      508544 :         free(str->data);
     133 ECB             :     /* just for luck, make the buffer validly empty. */
     134 CBC      508544 :     str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
     135          508544 :     str->maxlen = 0;
     136 GIC      508544 :     str->len = 0;
     137          508544 : }
     138                 : 
     139                 : /*
     140                 :  * resetPQExpBuffer
     141                 :  *      Reset a PQExpBuffer to empty
     142                 :  *
     143                 :  * Note: if possible, a "broken" PQExpBuffer is returned to normal.
     144 ECB             :  */
     145                 : void
     146 CBC     2415258 : resetPQExpBuffer(PQExpBuffer str)
     147                 : {
     148         2415258 :     if (str)
     149                 :     {
     150         2414498 :         if (str->data != oom_buffer)
     151 ECB             :         {
     152 GIC     2414498 :             str->len = 0;
     153         2414498 :             str->data[0] = '\0';
     154                 :         }
     155                 :         else
     156 EUB             :         {
     157                 :             /* try to reinitialize to valid state */
     158 UIC           0 :             initPQExpBuffer(str);
     159 ECB             :         }
     160                 :     }
     161 GIC     2415258 : }
     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 ECB             :  */
     171                 : int
     172 GIC     6033140 : enlargePQExpBuffer(PQExpBuffer str, size_t needed)
     173                 : {
     174                 :     size_t      newlen;
     175 ECB             :     char       *newdata;
     176 EUB             : 
     177 GIC     6033140 :     if (PQExpBufferBroken(str))
     178 UIC           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 ECB             :      * the following.
     184                 :      */
     185 GBC     6033140 :     if (needed >= ((size_t) INT_MAX - str->len))
     186 EUB             :     {
     187 UIC           0 :         markPQExpBufferBroken(str);
     188               0 :         return 0;
     189 ECB             :     }
     190                 : 
     191 GIC     6033140 :     needed += str->len + 1;      /* total space required now */
     192                 : 
     193 ECB             :     /* Because of the above test, we now have needed <= INT_MAX */
     194                 : 
     195 GIC     6033140 :     if (needed <= str->maxlen)
     196         6014982 :         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 ECB             :      * Actually, we might need to more than double it if 'needed' is big...
     202                 :      */
     203 CBC       18158 :     newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64;
     204 GIC       20425 :     while (needed > newlen)
     205            2267 :         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 ECB             :      * will still have newlen >= needed.
     211 EUB             :      */
     212 GIC       18158 :     if (newlen > (size_t) INT_MAX)
     213 LBC           0 :         newlen = (size_t) INT_MAX;
     214 ECB             : 
     215 GIC       18158 :     newdata = (char *) realloc(str->data, newlen);
     216 CBC       18158 :     if (newdata != NULL)
     217 ECB             :     {
     218 CBC       18158 :         str->data = newdata;
     219 GIC       18158 :         str->maxlen = newlen;
     220           18158 :         return 1;
     221 EUB             :     }
     222                 : 
     223 UIC           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 ECB             :  */
     234                 : void
     235 CBC      231196 : printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
     236                 : {
     237 GIC      231196 :     int         save_errno = errno;
     238                 :     va_list     args;
     239 ECB             :     bool        done;
     240                 : 
     241 CBC      231196 :     resetPQExpBuffer(str);
     242 EUB             : 
     243 GIC      231196 :     if (PQExpBufferBroken(str))
     244 UIC           0 :         return;                 /* already failed */
     245                 : 
     246                 :     /* Loop in case we have to retry after enlarging the buffer. */
     247 ECB             :     do
     248                 :     {
     249 CBC      233332 :         errno = save_errno;
     250          233332 :         va_start(args, fmt);
     251          233332 :         done = appendPQExpBufferVA(str, fmt, args);
     252 GIC      233332 :         va_end(args);
     253          233332 :     } 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 ECB             :  */
     264                 : void
     265 CBC      325952 : appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
     266                 : {
     267 GIC      325952 :     int         save_errno = errno;
     268                 :     va_list     args;
     269 ECB             :     bool        done;
     270 EUB             : 
     271 GIC      325952 :     if (PQExpBufferBroken(str))
     272 UIC           0 :         return;                 /* already failed */
     273                 : 
     274                 :     /* Loop in case we have to retry after enlarging the buffer. */
     275 ECB             :     do
     276                 :     {
     277 CBC      335842 :         errno = save_errno;
     278          335842 :         va_start(args, fmt);
     279          335842 :         done = appendPQExpBufferVA(str, fmt, args);
     280 GIC      335842 :         va_end(args);
     281          335842 :     } 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 ECB             :  */
     293                 : bool
     294 GIC      569624 : 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 ECB             :      * hardly any space, don't bother trying, just enlarge the buffer first.
     303                 :      */
     304 CBC      569624 :     if (str->maxlen > str->len + 16)
     305                 :     {
     306          567697 :         avail = str->maxlen - str->len;
     307                 : 
     308 GIC      567697 :         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 ECB             :          * something wrong with the format string).
     313                 :          */
     314 GBC      567697 :         if (unlikely(nprinted < 0))
     315 EUB             :         {
     316 UIC           0 :             markPQExpBufferBroken(str);
     317               0 :             return true;
     318 ECB             :         }
     319                 : 
     320 GIC      567697 :         if ((size_t) nprinted < avail)
     321 ECB             :         {
     322                 :             /* Success.  Note nprinted does not include trailing null. */
     323 GIC      557594 :             str->len += nprinted;
     324          557594 :             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 ECB             :          * can't represent more than that.
     334                 :          */
     335 GBC       10103 :         if (unlikely(nprinted > INT_MAX - 1))
     336 EUB             :         {
     337 UIC           0 :             markPQExpBufferBroken(str);
     338 LBC           0 :             return true;
     339                 :         }
     340 GIC       10103 :         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 ECB             :          * to run vsnprintf, which seems sensible.
     350                 :          */
     351 GIC        1927 :         needed = 32;
     352                 :     }
     353 ECB             : 
     354 EUB             :     /* Increase the buffer size and try again. */
     355 GIC       12030 :     if (!enlargePQExpBuffer(str, needed))
     356 LBC           0 :         return true;            /* oops, out of memory */
     357                 : 
     358 GIC       12030 :     return false;
     359                 : }
     360                 : 
     361                 : /*
     362                 :  * appendPQExpBufferStr
     363                 :  * Append the given string to a PQExpBuffer, allocating more space
     364                 :  * if necessary.
     365 ECB             :  */
     366                 : void
     367 CBC      862218 : appendPQExpBufferStr(PQExpBuffer str, const char *data)
     368 ECB             : {
     369 GIC      862218 :     appendBinaryPQExpBuffer(str, data, strlen(data));
     370          862218 : }
     371                 : 
     372                 : /*
     373                 :  * appendPQExpBufferChar
     374                 :  * Append a single byte to str.
     375                 :  * Like appendPQExpBuffer(str, "%c", ch) but much faster.
     376 ECB             :  */
     377                 : void
     378 GIC      780211 : appendPQExpBufferChar(PQExpBuffer str, char ch)
     379 ECB             : {
     380 EUB             :     /* Make more room if needed */
     381 GIC      780211 :     if (!enlargePQExpBuffer(str, 1))
     382 UIC           0 :         return;
     383 ECB             : 
     384                 :     /* OK, append the character */
     385 CBC      780211 :     str->data[str->len] = ch;
     386 GIC      780211 :     str->len++;
     387          780211 :     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 ECB             :  */
     396                 : void
     397 GIC     5220049 : appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
     398 ECB             : {
     399 EUB             :     /* Make more room if needed */
     400 GIC     5220049 :     if (!enlargePQExpBuffer(str, datalen))
     401 UIC           0 :         return;
     402 ECB             : 
     403                 :     /* OK, append the data */
     404 GIC     5220049 :     memcpy(str->data + str->len, data, datalen);
     405         5220049 :     str->len += datalen;
     406                 : 
     407                 :     /*
     408                 :      * Keep a trailing null in place, even though it's probably useless for
     409 ECB             :      * binary data...
     410                 :      */
     411 GIC     5220049 :     str->data[str->len] = '\0';
     412                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a