LCOV - code coverage report
Current view: top level - src/interfaces/libpq - pqexpbuffer.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 88 114 77.2 %
Date: 2024-04-16 18:11:15 Functions: 12 13 92.3 %
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-2024, 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      680822 : createPQExpBuffer(void)
      73             : {
      74             :     PQExpBuffer res;
      75             : 
      76      680822 :     res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
      77      680822 :     if (res != NULL)
      78      680822 :         initPQExpBuffer(res);
      79             : 
      80      680822 :     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     1323858 : initPQExpBuffer(PQExpBuffer str)
      91             : {
      92     1323858 :     str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
      93     1323858 :     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     1323858 :         str->maxlen = INITIAL_EXPBUFFER_SIZE;
     102     1323858 :         str->len = 0;
     103     1323858 :         str->data[0] = '\0';
     104             :     }
     105     1323858 : }
     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      664634 : destroyPQExpBuffer(PQExpBuffer str)
     115             : {
     116      664634 :     if (str)
     117             :     {
     118      664376 :         termPQExpBuffer(str);
     119      664376 :         free(str);
     120             :     }
     121      664634 : }
     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     1187830 : termPQExpBuffer(PQExpBuffer str)
     130             : {
     131     1187830 :     if (str->data != oom_buffer)
     132     1187830 :         free(str->data);
     133             :     /* just for luck, make the buffer validly empty. */
     134     1187830 :     str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
     135     1187830 :     str->maxlen = 0;
     136     1187830 :     str->len = 0;
     137     1187830 : }
     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     5479774 : resetPQExpBuffer(PQExpBuffer str)
     147             : {
     148     5479774 :     if (str)
     149             :     {
     150     5478344 :         if (str->data != oom_buffer)
     151             :         {
     152     5478344 :             str->len = 0;
     153     5478344 :             str->data[0] = '\0';
     154             :         }
     155             :         else
     156             :         {
     157             :             /* try to reinitialize to valid state */
     158           0 :             initPQExpBuffer(str);
     159             :         }
     160             :     }
     161     5479774 : }
     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    13543896 : enlargePQExpBuffer(PQExpBuffer str, size_t needed)
     173             : {
     174             :     size_t      newlen;
     175             :     char       *newdata;
     176             : 
     177    13543896 :     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    13543896 :     if (needed >= ((size_t) INT_MAX - str->len))
     186             :     {
     187           0 :         markPQExpBufferBroken(str);
     188           0 :         return 0;
     189             :     }
     190             : 
     191    13543896 :     needed += str->len + 1;      /* total space required now */
     192             : 
     193             :     /* Because of the above test, we now have needed <= INT_MAX */
     194             : 
     195    13543896 :     if (needed <= str->maxlen)
     196    13501834 :         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       42062 :     newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64;
     204       47606 :     while (needed > newlen)
     205        5544 :         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       42062 :     if (newlen > (size_t) INT_MAX)
     213           0 :         newlen = (size_t) INT_MAX;
     214             : 
     215       42062 :     newdata = (char *) realloc(str->data, newlen);
     216       42062 :     if (newdata != NULL)
     217             :     {
     218       42062 :         str->data = newdata;
     219       42062 :         str->maxlen = newlen;
     220       42062 :         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      470994 : printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
     236             : {
     237      470994 :     int         save_errno = errno;
     238             :     va_list     args;
     239             :     bool        done;
     240             : 
     241      470994 :     resetPQExpBuffer(str);
     242             : 
     243      470994 :     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      475728 :         errno = save_errno;
     250      475728 :         va_start(args, fmt);
     251      475728 :         done = appendPQExpBufferVA(str, fmt, args);
     252      475728 :         va_end(args);
     253      475728 :     } 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      770736 : appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
     266             : {
     267      770736 :     int         save_errno = errno;
     268             :     va_list     args;
     269             :     bool        done;
     270             : 
     271      770736 :     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      794412 :         errno = save_errno;
     278      794412 :         va_start(args, fmt);
     279      794412 :         done = appendPQExpBufferVA(str, fmt, args);
     280      794412 :         va_end(args);
     281      794412 :     } 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     1271050 : 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     1271050 :     if (str->maxlen > str->len + 16)
     305             :     {
     306     1266916 :         avail = str->maxlen - str->len;
     307             : 
     308     1266916 :         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     1266916 :         if (unlikely(nprinted < 0))
     315             :         {
     316           0 :             markPQExpBufferBroken(str);
     317           0 :             return true;
     318             :         }
     319             : 
     320     1266916 :         if ((size_t) nprinted < avail)
     321             :         {
     322             :             /* Success.  Note nprinted does not include trailing null. */
     323     1242632 :             str->len += nprinted;
     324     1242632 :             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       24284 :         if (unlikely(nprinted > INT_MAX - 1))
     336             :         {
     337           0 :             markPQExpBufferBroken(str);
     338           0 :             return true;
     339             :         }
     340       24284 :         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        4134 :         needed = 32;
     352             :     }
     353             : 
     354             :     /* Increase the buffer size and try again. */
     355       28418 :     if (!enlargePQExpBuffer(str, needed))
     356           0 :         return true;            /* oops, out of memory */
     357             : 
     358       28418 :     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     1958976 : appendPQExpBufferStr(PQExpBuffer str, const char *data)
     368             : {
     369     1958976 :     appendBinaryPQExpBuffer(str, data, strlen(data));
     370     1958976 : }
     371             : 
     372             : /*
     373             :  * appendPQExpBufferChar
     374             :  * Append a single byte to str.
     375             :  * Like appendPQExpBuffer(str, "%c", ch) but much faster.
     376             :  */
     377             : void
     378     1793288 : appendPQExpBufferChar(PQExpBuffer str, char ch)
     379             : {
     380             :     /* Make more room if needed */
     381     1793288 :     if (!enlargePQExpBuffer(str, 1))
     382           0 :         return;
     383             : 
     384             :     /* OK, append the character */
     385     1793288 :     str->data[str->len] = ch;
     386     1793288 :     str->len++;
     387     1793288 :     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    11692238 : appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
     398             : {
     399             :     /* Make more room if needed */
     400    11692238 :     if (!enlargePQExpBuffer(str, datalen))
     401           0 :         return;
     402             : 
     403             :     /* OK, append the data */
     404    11692238 :     memcpy(str->data + str->len, data, datalen);
     405    11692238 :     str->len += datalen;
     406             : 
     407             :     /*
     408             :      * Keep a trailing null in place, even though it's probably useless for
     409             :      * binary data...
     410             :      */
     411    11692238 :     str->data[str->len] = '\0';
     412             : }

Generated by: LCOV version 1.14