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

Generated by: LCOV version 1.11