LCOV - code coverage report
Current view: top level - src/interfaces/libpq - pqexpbuffer.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 88 114 77.2 %
Date: 2019-11-13 22:07:24 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-2019, 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             : static bool appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args) pg_attribute_printf(2, 0);
      44             : 
      45             : 
      46             : /*
      47             :  * markPQExpBufferBroken
      48             :  *
      49             :  * Put a PQExpBuffer in "broken" state if it isn't already.
      50             :  */
      51             : static void
      52           0 : markPQExpBufferBroken(PQExpBuffer str)
      53             : {
      54           0 :     if (str->data != oom_buffer)
      55           0 :         free(str->data);
      56             : 
      57             :     /*
      58             :      * Casting away const here is a bit ugly, but it seems preferable to not
      59             :      * marking oom_buffer const.  We want to do that to encourage the compiler
      60             :      * to put oom_buffer in read-only storage, so that anyone who tries to
      61             :      * scribble on a broken PQExpBuffer will get a failure.
      62             :      */
      63           0 :     str->data = unconstify(char *, oom_buffer_ptr);
      64           0 :     str->len = 0;
      65           0 :     str->maxlen = 0;
      66           0 : }
      67             : 
      68             : /*
      69             :  * createPQExpBuffer
      70             :  *
      71             :  * Create an empty 'PQExpBufferData' & return a pointer to it.
      72             :  */
      73             : PQExpBuffer
      74      162374 : createPQExpBuffer(void)
      75             : {
      76             :     PQExpBuffer res;
      77             : 
      78      162374 :     res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
      79      162374 :     if (res != NULL)
      80      162374 :         initPQExpBuffer(res);
      81             : 
      82      162374 :     return res;
      83             : }
      84             : 
      85             : /*
      86             :  * initPQExpBuffer
      87             :  *
      88             :  * Initialize a PQExpBufferData struct (with previously undefined contents)
      89             :  * to describe an empty string.
      90             :  */
      91             : void
      92      386816 : initPQExpBuffer(PQExpBuffer str)
      93             : {
      94      386816 :     str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
      95      386816 :     if (str->data == NULL)
      96             :     {
      97           0 :         str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
      98           0 :         str->maxlen = 0;
      99           0 :         str->len = 0;
     100             :     }
     101             :     else
     102             :     {
     103      386816 :         str->maxlen = INITIAL_EXPBUFFER_SIZE;
     104      386816 :         str->len = 0;
     105      386816 :         str->data[0] = '\0';
     106             :     }
     107      386816 : }
     108             : 
     109             : /*
     110             :  * destroyPQExpBuffer(str);
     111             :  *
     112             :  *      free()s both the data buffer and the PQExpBufferData.
     113             :  *      This is the inverse of createPQExpBuffer().
     114             :  */
     115             : void
     116      158452 : destroyPQExpBuffer(PQExpBuffer str)
     117             : {
     118      158452 :     if (str)
     119             :     {
     120      158372 :         termPQExpBuffer(str);
     121      158372 :         free(str);
     122             :     }
     123      158452 : }
     124             : 
     125             : /*
     126             :  * termPQExpBuffer(str)
     127             :  *      free()s the data buffer but not the PQExpBufferData itself.
     128             :  *      This is the inverse of initPQExpBuffer().
     129             :  */
     130             : void
     131      326236 : termPQExpBuffer(PQExpBuffer str)
     132             : {
     133      326236 :     if (str->data != oom_buffer)
     134      326236 :         free(str->data);
     135             :     /* just for luck, make the buffer validly empty. */
     136      326236 :     str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
     137      326236 :     str->maxlen = 0;
     138      326236 :     str->len = 0;
     139      326236 : }
     140             : 
     141             : /*
     142             :  * resetPQExpBuffer
     143             :  *      Reset a PQExpBuffer to empty
     144             :  *
     145             :  * Note: if possible, a "broken" PQExpBuffer is returned to normal.
     146             :  */
     147             : void
     148     2800128 : resetPQExpBuffer(PQExpBuffer str)
     149             : {
     150     2800128 :     if (str)
     151             :     {
     152     2800142 :         if (str->data != oom_buffer)
     153             :         {
     154     2800142 :             str->len = 0;
     155     2800142 :             str->data[0] = '\0';
     156             :         }
     157             :         else
     158             :         {
     159             :             /* try to reinitialize to valid state */
     160           0 :             initPQExpBuffer(str);
     161             :         }
     162             :     }
     163     2800128 : }
     164             : 
     165             : /*
     166             :  * enlargePQExpBuffer
     167             :  * Make sure there is enough space for 'needed' more bytes in the buffer
     168             :  * ('needed' does not include the terminating null).
     169             :  *
     170             :  * Returns 1 if OK, 0 if failed to enlarge buffer.  (In the latter case
     171             :  * the buffer is left in "broken" state.)
     172             :  */
     173             : int
     174     6492988 : enlargePQExpBuffer(PQExpBuffer str, size_t needed)
     175             : {
     176             :     size_t      newlen;
     177             :     char       *newdata;
     178             : 
     179     6492988 :     if (PQExpBufferBroken(str))
     180           0 :         return 0;               /* already failed */
     181             : 
     182             :     /*
     183             :      * Guard against ridiculous "needed" values, which can occur if we're fed
     184             :      * bogus data.  Without this, we can get an overflow or infinite loop in
     185             :      * the following.
     186             :      */
     187     6492988 :     if (needed >= ((size_t) INT_MAX - str->len))
     188             :     {
     189           0 :         markPQExpBufferBroken(str);
     190           0 :         return 0;
     191             :     }
     192             : 
     193     6492988 :     needed += str->len + 1;      /* total space required now */
     194             : 
     195             :     /* Because of the above test, we now have needed <= INT_MAX */
     196             : 
     197     6492988 :     if (needed <= str->maxlen)
     198     6468652 :         return 1;               /* got enough space already */
     199             : 
     200             :     /*
     201             :      * We don't want to allocate just a little more space with each append;
     202             :      * for efficiency, double the buffer size each time it overflows.
     203             :      * Actually, we might need to more than double it if 'needed' is big...
     204             :      */
     205       24336 :     newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64;
     206       55338 :     while (needed > newlen)
     207        6666 :         newlen = 2 * newlen;
     208             : 
     209             :     /*
     210             :      * Clamp to INT_MAX in case we went past it.  Note we are assuming here
     211             :      * that INT_MAX <= UINT_MAX/2, else the above loop could overflow.  We
     212             :      * will still have newlen >= needed.
     213             :      */
     214       24336 :     if (newlen > (size_t) INT_MAX)
     215           0 :         newlen = (size_t) INT_MAX;
     216             : 
     217       24336 :     newdata = (char *) realloc(str->data, newlen);
     218       24336 :     if (newdata != NULL)
     219             :     {
     220       24336 :         str->data = newdata;
     221       24336 :         str->maxlen = newlen;
     222       24336 :         return 1;
     223             :     }
     224             : 
     225           0 :     markPQExpBufferBroken(str);
     226           0 :     return 0;
     227             : }
     228             : 
     229             : /*
     230             :  * printfPQExpBuffer
     231             :  * Format text data under the control of fmt (an sprintf-like format string)
     232             :  * and insert it into str.  More space is allocated to str if necessary.
     233             :  * This is a convenience routine that does the same thing as
     234             :  * resetPQExpBuffer() followed by appendPQExpBuffer().
     235             :  */
     236             : void
     237       29968 : printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
     238             : {
     239       29968 :     int         save_errno = errno;
     240             :     va_list     args;
     241             :     bool        done;
     242             : 
     243       29968 :     resetPQExpBuffer(str);
     244             : 
     245       29968 :     if (PQExpBufferBroken(str))
     246           0 :         return;                 /* already failed */
     247             : 
     248             :     /* Loop in case we have to retry after enlarging the buffer. */
     249             :     do
     250             :     {
     251       36996 :         errno = save_errno;
     252       36996 :         va_start(args, fmt);
     253       36996 :         done = appendPQExpBufferVA(str, fmt, args);
     254       36996 :         va_end(args);
     255       36996 :     } while (!done);
     256             : }
     257             : 
     258             : /*
     259             :  * appendPQExpBuffer
     260             :  *
     261             :  * Format text data under the control of fmt (an sprintf-like format string)
     262             :  * and append it to whatever is already in str.  More space is allocated
     263             :  * to str if necessary.  This is sort of like a combination of sprintf and
     264             :  * strcat.
     265             :  */
     266             : void
     267      317520 : appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
     268             : {
     269      317520 :     int         save_errno = errno;
     270             :     va_list     args;
     271             :     bool        done;
     272             : 
     273      317520 :     if (PQExpBufferBroken(str))
     274           0 :         return;                 /* already failed */
     275             : 
     276             :     /* Loop in case we have to retry after enlarging the buffer. */
     277             :     do
     278             :     {
     279      329872 :         errno = save_errno;
     280      329872 :         va_start(args, fmt);
     281      329872 :         done = appendPQExpBufferVA(str, fmt, args);
     282      329872 :         va_end(args);
     283      329872 :     } while (!done);
     284             : }
     285             : 
     286             : /*
     287             :  * appendPQExpBufferVA
     288             :  * Shared guts of printfPQExpBuffer/appendPQExpBuffer.
     289             :  * Attempt to format data and append it to str.  Returns true if done
     290             :  * (either successful or hard failure), false if need to retry.
     291             :  *
     292             :  * Caution: callers must be sure to preserve their entry-time errno
     293             :  * when looping, in case the fmt contains "%m".
     294             :  */
     295             : static bool
     296      366868 : appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args)
     297             : {
     298             :     size_t      avail;
     299             :     size_t      needed;
     300             :     int         nprinted;
     301             : 
     302             :     /*
     303             :      * Try to format the given string into the available space; but if there's
     304             :      * hardly any space, don't bother trying, just enlarge the buffer first.
     305             :      */
     306      366868 :     if (str->maxlen > str->len + 16)
     307             :     {
     308      366306 :         avail = str->maxlen - str->len;
     309             : 
     310      366306 :         nprinted = vsnprintf(str->data + str->len, avail, fmt, args);
     311             : 
     312             :         /*
     313             :          * If vsnprintf reports an error, fail (we assume this means there's
     314             :          * something wrong with the format string).
     315             :          */
     316      366306 :         if (unlikely(nprinted < 0))
     317             :         {
     318           0 :             markPQExpBufferBroken(str);
     319           0 :             return true;
     320             :         }
     321             : 
     322      366306 :         if ((size_t) nprinted < avail)
     323             :         {
     324             :             /* Success.  Note nprinted does not include trailing null. */
     325      347488 :             str->len += nprinted;
     326      347488 :             return true;
     327             :         }
     328             : 
     329             :         /*
     330             :          * We assume a C99-compliant vsnprintf, so believe its estimate of the
     331             :          * required space, and add one for the trailing null.  (If it's wrong,
     332             :          * the logic will still work, but we may loop multiple times.)
     333             :          *
     334             :          * Choke if the required space would exceed INT_MAX, since str->maxlen
     335             :          * can't represent more than that.
     336             :          */
     337       18818 :         if (unlikely(nprinted > INT_MAX - 1))
     338             :         {
     339           0 :             markPQExpBufferBroken(str);
     340           0 :             return true;
     341             :         }
     342       18818 :         needed = nprinted + 1;
     343             :     }
     344             :     else
     345             :     {
     346             :         /*
     347             :          * We have to guess at how much to enlarge, since we're skipping the
     348             :          * formatting work.  Fortunately, because of enlargePQExpBuffer's
     349             :          * preference for power-of-2 sizes, this number isn't very sensitive;
     350             :          * the net effect is that we'll double the buffer size before trying
     351             :          * to run vsnprintf, which seems sensible.
     352             :          */
     353         562 :         needed = 32;
     354             :     }
     355             : 
     356             :     /* Increase the buffer size and try again. */
     357       19380 :     if (!enlargePQExpBuffer(str, needed))
     358           0 :         return true;            /* oops, out of memory */
     359             : 
     360       19380 :     return false;
     361             : }
     362             : 
     363             : /*
     364             :  * appendPQExpBufferStr
     365             :  * Append the given string to a PQExpBuffer, allocating more space
     366             :  * if necessary.
     367             :  */
     368             : void
     369     1214104 : appendPQExpBufferStr(PQExpBuffer str, const char *data)
     370             : {
     371     1214104 :     appendBinaryPQExpBuffer(str, data, strlen(data));
     372     1214116 : }
     373             : 
     374             : /*
     375             :  * appendPQExpBufferChar
     376             :  * Append a single byte to str.
     377             :  * Like appendPQExpBuffer(str, "%c", ch) but much faster.
     378             :  */
     379             : void
     380      628782 : appendPQExpBufferChar(PQExpBuffer str, char ch)
     381             : {
     382             :     /* Make more room if needed */
     383      628782 :     if (!enlargePQExpBuffer(str, 1))
     384           0 :         return;
     385             : 
     386             :     /* OK, append the character */
     387      628782 :     str->data[str->len] = ch;
     388      628782 :     str->len++;
     389      628782 :     str->data[str->len] = '\0';
     390             : }
     391             : 
     392             : /*
     393             :  * appendBinaryPQExpBuffer
     394             :  *
     395             :  * Append arbitrary binary data to a PQExpBuffer, allocating more space
     396             :  * if necessary.
     397             :  */
     398             : void
     399     5835636 : appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
     400             : {
     401             :     /* Make more room if needed */
     402     5835636 :     if (!enlargePQExpBuffer(str, datalen))
     403           0 :         return;
     404             : 
     405             :     /* OK, append the data */
     406     5835650 :     memcpy(str->data + str->len, data, datalen);
     407     5835650 :     str->len += datalen;
     408             : 
     409             :     /*
     410             :      * Keep a trailing null in place, even though it's probably useless for
     411             :      * binary data...
     412             :      */
     413     5835650 :     str->data[str->len] = '\0';
     414             : }

Generated by: LCOV version 1.13