LCOV - code coverage report
Current view: top level - src/fe_utils - string_utils.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 97 320 30.3 %
Date: 2017-09-29 15:12:54 Functions: 7 15 46.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * String-processing utility routines for frontend code
       4             :  *
       5             :  * Assorted utility functions that are useful in constructing SQL queries
       6             :  * and interpreting backend output.
       7             :  *
       8             :  *
       9             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
      10             :  * Portions Copyright (c) 1994, Regents of the University of California
      11             :  *
      12             :  * src/fe_utils/string_utils.c
      13             :  *
      14             :  *-------------------------------------------------------------------------
      15             :  */
      16             : #include "postgres_fe.h"
      17             : 
      18             : #include <ctype.h>
      19             : 
      20             : #include "fe_utils/string_utils.h"
      21             : 
      22             : #include "common/keywords.h"
      23             : 
      24             : 
      25             : static PQExpBuffer defaultGetLocalPQExpBuffer(void);
      26             : 
      27             : /* Globals exported by this file */
      28             : int         quote_all_identifiers = 0;
      29             : PQExpBuffer (*getLocalPQExpBuffer) (void) = defaultGetLocalPQExpBuffer;
      30             : 
      31             : 
      32             : /*
      33             :  * Returns a temporary PQExpBuffer, valid until the next call to the function.
      34             :  * This is used by fmtId and fmtQualifiedId.
      35             :  *
      36             :  * Non-reentrant and non-thread-safe but reduces memory leakage. You can
      37             :  * replace this with a custom version by setting the getLocalPQExpBuffer
      38             :  * function pointer.
      39             :  */
      40             : static PQExpBuffer
      41           2 : defaultGetLocalPQExpBuffer(void)
      42             : {
      43             :     static PQExpBuffer id_return = NULL;
      44             : 
      45           2 :     if (id_return)              /* first time through? */
      46             :     {
      47             :         /* same buffer, just wipe contents */
      48           1 :         resetPQExpBuffer(id_return);
      49             :     }
      50             :     else
      51             :     {
      52             :         /* new buffer */
      53           1 :         id_return = createPQExpBuffer();
      54             :     }
      55             : 
      56           2 :     return id_return;
      57             : }
      58             : 
      59             : /*
      60             :  *  Quotes input string if it's not a legitimate SQL identifier as-is.
      61             :  *
      62             :  *  Note that the returned string must be used before calling fmtId again,
      63             :  *  since we re-use the same return buffer each time.
      64             :  */
      65             : const char *
      66           2 : fmtId(const char *rawid)
      67             : {
      68           2 :     PQExpBuffer id_return = getLocalPQExpBuffer();
      69             : 
      70             :     const char *cp;
      71           2 :     bool        need_quotes = false;
      72             : 
      73             :     /*
      74             :      * These checks need to match the identifier production in scan.l. Don't
      75             :      * use islower() etc.
      76             :      */
      77           2 :     if (quote_all_identifiers)
      78           0 :         need_quotes = true;
      79             :     /* slightly different rules for first character */
      80           2 :     else if (!((rawid[0] >= 'a' && rawid[0] <= 'z') || rawid[0] == '_'))
      81           0 :         need_quotes = true;
      82             :     else
      83             :     {
      84             :         /* otherwise check the entire string */
      85          21 :         for (cp = rawid; *cp; cp++)
      86             :         {
      87          19 :             if (!((*cp >= 'a' && *cp <= 'z')
      88           1 :                   || (*cp >= '0' && *cp <= '9')
      89           0 :                   || (*cp == '_')))
      90             :             {
      91           0 :                 need_quotes = true;
      92           0 :                 break;
      93             :             }
      94             :         }
      95             :     }
      96             : 
      97           2 :     if (!need_quotes)
      98             :     {
      99             :         /*
     100             :          * Check for keyword.  We quote keywords except for unreserved ones.
     101             :          * (In some cases we could avoid quoting a col_name or type_func_name
     102             :          * keyword, but it seems much harder than it's worth to tell that.)
     103             :          *
     104             :          * Note: ScanKeywordLookup() does case-insensitive comparison, but
     105             :          * that's fine, since we already know we have all-lower-case.
     106             :          */
     107           2 :         const ScanKeyword *keyword = ScanKeywordLookup(rawid,
     108             :                                                        ScanKeywords,
     109             :                                                        NumScanKeywords);
     110             : 
     111           2 :         if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD)
     112           0 :             need_quotes = true;
     113             :     }
     114             : 
     115           2 :     if (!need_quotes)
     116             :     {
     117             :         /* no quoting needed */
     118           2 :         appendPQExpBufferStr(id_return, rawid);
     119             :     }
     120             :     else
     121             :     {
     122           0 :         appendPQExpBufferChar(id_return, '"');
     123           0 :         for (cp = rawid; *cp; cp++)
     124             :         {
     125             :             /*
     126             :              * Did we find a double-quote in the string? Then make this a
     127             :              * double double-quote per SQL99. Before, we put in a
     128             :              * backslash/double-quote pair. - thomas 2000-08-05
     129             :              */
     130           0 :             if (*cp == '"')
     131           0 :                 appendPQExpBufferChar(id_return, '"');
     132           0 :             appendPQExpBufferChar(id_return, *cp);
     133             :         }
     134           0 :         appendPQExpBufferChar(id_return, '"');
     135             :     }
     136             : 
     137           2 :     return id_return->data;
     138             : }
     139             : 
     140             : /*
     141             :  * fmtQualifiedId - convert a qualified name to the proper format for
     142             :  * the source database.
     143             :  *
     144             :  * Like fmtId, use the result before calling again.
     145             :  *
     146             :  * Since we call fmtId and it also uses getLocalPQExpBuffer() we cannot
     147             :  * use that buffer until we're finished with calling fmtId().
     148             :  */
     149             : const char *
     150           0 : fmtQualifiedId(int remoteVersion, const char *schema, const char *id)
     151             : {
     152             :     PQExpBuffer id_return;
     153           0 :     PQExpBuffer lcl_pqexp = createPQExpBuffer();
     154             : 
     155             :     /* Suppress schema name if fetching from pre-7.3 DB */
     156           0 :     if (remoteVersion >= 70300 && schema && *schema)
     157             :     {
     158           0 :         appendPQExpBuffer(lcl_pqexp, "%s.", fmtId(schema));
     159             :     }
     160           0 :     appendPQExpBufferStr(lcl_pqexp, fmtId(id));
     161             : 
     162           0 :     id_return = getLocalPQExpBuffer();
     163             : 
     164           0 :     appendPQExpBufferStr(id_return, lcl_pqexp->data);
     165           0 :     destroyPQExpBuffer(lcl_pqexp);
     166             : 
     167           0 :     return id_return->data;
     168             : }
     169             : 
     170             : 
     171             : /*
     172             :  * Format a Postgres version number (in the PG_VERSION_NUM integer format
     173             :  * returned by PQserverVersion()) as a string.  This exists mainly to
     174             :  * encapsulate knowledge about two-part vs. three-part version numbers.
     175             :  *
     176             :  * For reentrancy, caller must supply the buffer the string is put in.
     177             :  * Recommended size of the buffer is 32 bytes.
     178             :  *
     179             :  * Returns address of 'buf', as a notational convenience.
     180             :  */
     181             : char *
     182           0 : formatPGVersionNumber(int version_number, bool include_minor,
     183             :                       char *buf, size_t buflen)
     184             : {
     185           0 :     if (version_number >= 100000)
     186             :     {
     187             :         /* New two-part style */
     188           0 :         if (include_minor)
     189           0 :             snprintf(buf, buflen, "%d.%d", version_number / 10000,
     190             :                      version_number % 10000);
     191             :         else
     192           0 :             snprintf(buf, buflen, "%d", version_number / 10000);
     193             :     }
     194             :     else
     195             :     {
     196             :         /* Old three-part style */
     197           0 :         if (include_minor)
     198           0 :             snprintf(buf, buflen, "%d.%d.%d", version_number / 10000,
     199           0 :                      (version_number / 100) % 100,
     200             :                      version_number % 100);
     201             :         else
     202           0 :             snprintf(buf, buflen, "%d.%d", version_number / 10000,
     203           0 :                      (version_number / 100) % 100);
     204             :     }
     205           0 :     return buf;
     206             : }
     207             : 
     208             : 
     209             : /*
     210             :  * Convert a string value to an SQL string literal and append it to
     211             :  * the given buffer.  We assume the specified client_encoding and
     212             :  * standard_conforming_strings settings.
     213             :  *
     214             :  * This is essentially equivalent to libpq's PQescapeStringInternal,
     215             :  * except for the output buffer structure.  We need it in situations
     216             :  * where we do not have a PGconn available.  Where we do,
     217             :  * appendStringLiteralConn is a better choice.
     218             :  */
     219             : void
     220           0 : appendStringLiteral(PQExpBuffer buf, const char *str,
     221             :                     int encoding, bool std_strings)
     222             : {
     223           0 :     size_t      length = strlen(str);
     224           0 :     const char *source = str;
     225             :     char       *target;
     226             : 
     227           0 :     if (!enlargePQExpBuffer(buf, 2 * length + 2))
     228           0 :         return;
     229             : 
     230           0 :     target = buf->data + buf->len;
     231           0 :     *target++ = '\'';
     232             : 
     233           0 :     while (*source != '\0')
     234             :     {
     235           0 :         char        c = *source;
     236             :         int         len;
     237             :         int         i;
     238             : 
     239             :         /* Fast path for plain ASCII */
     240           0 :         if (!IS_HIGHBIT_SET(c))
     241             :         {
     242             :             /* Apply quoting if needed */
     243           0 :             if (SQL_STR_DOUBLE(c, !std_strings))
     244           0 :                 *target++ = c;
     245             :             /* Copy the character */
     246           0 :             *target++ = c;
     247           0 :             source++;
     248           0 :             continue;
     249             :         }
     250             : 
     251             :         /* Slow path for possible multibyte characters */
     252           0 :         len = PQmblen(source, encoding);
     253             : 
     254             :         /* Copy the character */
     255           0 :         for (i = 0; i < len; i++)
     256             :         {
     257           0 :             if (*source == '\0')
     258           0 :                 break;
     259           0 :             *target++ = *source++;
     260             :         }
     261             : 
     262             :         /*
     263             :          * If we hit premature end of string (ie, incomplete multibyte
     264             :          * character), try to pad out to the correct length with spaces. We
     265             :          * may not be able to pad completely, but we will always be able to
     266             :          * insert at least one pad space (since we'd not have quoted a
     267             :          * multibyte character).  This should be enough to make a string that
     268             :          * the server will error out on.
     269             :          */
     270           0 :         if (i < len)
     271             :         {
     272           0 :             char       *stop = buf->data + buf->maxlen - 2;
     273             : 
     274           0 :             for (; i < len; i++)
     275             :             {
     276           0 :                 if (target >= stop)
     277           0 :                     break;
     278           0 :                 *target++ = ' ';
     279             :             }
     280           0 :             break;
     281             :         }
     282             :     }
     283             : 
     284             :     /* Write the terminating quote and NUL character. */
     285           0 :     *target++ = '\'';
     286           0 :     *target = '\0';
     287             : 
     288           0 :     buf->len = target - buf->data;
     289             : }
     290             : 
     291             : 
     292             : /*
     293             :  * Convert a string value to an SQL string literal and append it to
     294             :  * the given buffer.  Encoding and string syntax rules are as indicated
     295             :  * by current settings of the PGconn.
     296             :  */
     297             : void
     298         273 : appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
     299             : {
     300         273 :     size_t      length = strlen(str);
     301             : 
     302             :     /*
     303             :      * XXX This is a kluge to silence escape_string_warning in our utility
     304             :      * programs.  It should go away someday.
     305             :      */
     306         273 :     if (strchr(str, '\\') != NULL && PQserverVersion(conn) >= 80100)
     307             :     {
     308             :         /* ensure we are not adjacent to an identifier */
     309           0 :         if (buf->len > 0 && buf->data[buf->len - 1] != ' ')
     310           0 :             appendPQExpBufferChar(buf, ' ');
     311           0 :         appendPQExpBufferChar(buf, ESCAPE_STRING_SYNTAX);
     312           0 :         appendStringLiteral(buf, str, PQclientEncoding(conn), false);
     313           0 :         return;
     314             :     }
     315             :     /* XXX end kluge */
     316             : 
     317         273 :     if (!enlargePQExpBuffer(buf, 2 * length + 2))
     318           0 :         return;
     319         273 :     appendPQExpBufferChar(buf, '\'');
     320         273 :     buf->len += PQescapeStringConn(conn, buf->data + buf->len,
     321             :                                    str, length, NULL);
     322         273 :     appendPQExpBufferChar(buf, '\'');
     323             : }
     324             : 
     325             : 
     326             : /*
     327             :  * Convert a string value to a dollar quoted literal and append it to
     328             :  * the given buffer. If the dqprefix parameter is not NULL then the
     329             :  * dollar quote delimiter will begin with that (after the opening $).
     330             :  *
     331             :  * No escaping is done at all on str, in compliance with the rules
     332             :  * for parsing dollar quoted strings.  Also, we need not worry about
     333             :  * encoding issues.
     334             :  */
     335             : void
     336           0 : appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix)
     337             : {
     338             :     static const char suffixes[] = "_XXXXXXX";
     339           0 :     int         nextchar = 0;
     340           0 :     PQExpBuffer delimBuf = createPQExpBuffer();
     341             : 
     342             :     /* start with $ + dqprefix if not NULL */
     343           0 :     appendPQExpBufferChar(delimBuf, '$');
     344           0 :     if (dqprefix)
     345           0 :         appendPQExpBufferStr(delimBuf, dqprefix);
     346             : 
     347             :     /*
     348             :      * Make sure we choose a delimiter which (without the trailing $) is not
     349             :      * present in the string being quoted. We don't check with the trailing $
     350             :      * because a string ending in $foo must not be quoted with $foo$.
     351             :      */
     352           0 :     while (strstr(str, delimBuf->data) != NULL)
     353             :     {
     354           0 :         appendPQExpBufferChar(delimBuf, suffixes[nextchar++]);
     355           0 :         nextchar %= sizeof(suffixes) - 1;
     356             :     }
     357             : 
     358             :     /* add trailing $ */
     359           0 :     appendPQExpBufferChar(delimBuf, '$');
     360             : 
     361             :     /* quote it and we are all done */
     362           0 :     appendPQExpBufferStr(buf, delimBuf->data);
     363           0 :     appendPQExpBufferStr(buf, str);
     364           0 :     appendPQExpBufferStr(buf, delimBuf->data);
     365             : 
     366           0 :     destroyPQExpBuffer(delimBuf);
     367           0 : }
     368             : 
     369             : 
     370             : /*
     371             :  * Convert a bytea value (presented as raw bytes) to an SQL string literal
     372             :  * and append it to the given buffer.  We assume the specified
     373             :  * standard_conforming_strings setting.
     374             :  *
     375             :  * This is needed in situations where we do not have a PGconn available.
     376             :  * Where we do, PQescapeByteaConn is a better choice.
     377             :  */
     378             : void
     379           0 : appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length,
     380             :                    bool std_strings)
     381             : {
     382           0 :     const unsigned char *source = str;
     383             :     char       *target;
     384             : 
     385             :     static const char hextbl[] = "0123456789abcdef";
     386             : 
     387             :     /*
     388             :      * This implementation is hard-wired to produce hex-format output. We do
     389             :      * not know the server version the output will be loaded into, so making
     390             :      * an intelligent format choice is impossible.  It might be better to
     391             :      * always use the old escaped format.
     392             :      */
     393           0 :     if (!enlargePQExpBuffer(buf, 2 * length + 5))
     394           0 :         return;
     395             : 
     396           0 :     target = buf->data + buf->len;
     397           0 :     *target++ = '\'';
     398           0 :     if (!std_strings)
     399           0 :         *target++ = '\\';
     400           0 :     *target++ = '\\';
     401           0 :     *target++ = 'x';
     402             : 
     403           0 :     while (length-- > 0)
     404             :     {
     405           0 :         unsigned char c = *source++;
     406             : 
     407           0 :         *target++ = hextbl[(c >> 4) & 0xF];
     408           0 :         *target++ = hextbl[c & 0xF];
     409             :     }
     410             : 
     411             :     /* Write the terminating quote and NUL character. */
     412           0 :     *target++ = '\'';
     413           0 :     *target = '\0';
     414             : 
     415           0 :     buf->len = target - buf->data;
     416             : }
     417             : 
     418             : 
     419             : /*
     420             :  * Append the given string to the shell command being built in the buffer,
     421             :  * with shell-style quoting as needed to create exactly one argument.
     422             :  *
     423             :  * Forbid LF or CR characters, which have scant practical use beyond designing
     424             :  * security breaches.  The Windows command shell is unusable as a conduit for
     425             :  * arguments containing LF or CR characters.  A future major release should
     426             :  * reject those characters in CREATE ROLE and CREATE DATABASE, because use
     427             :  * there eventually leads to errors here.
     428             :  *
     429             :  * appendShellString() simply prints an error and dies if LF or CR appears.
     430             :  * appendShellStringNoError() omits those characters from the result, and
     431             :  * returns false if there were any.
     432             :  */
     433             : void
     434           2 : appendShellString(PQExpBuffer buf, const char *str)
     435             : {
     436           2 :     if (!appendShellStringNoError(buf, str))
     437             :     {
     438           0 :         fprintf(stderr,
     439             :                 _("shell command argument contains a newline or carriage return: \"%s\"\n"),
     440             :                 str);
     441           0 :         exit(EXIT_FAILURE);
     442             :     }
     443           2 : }
     444             : 
     445             : bool
     446           2 : appendShellStringNoError(PQExpBuffer buf, const char *str)
     447             : {
     448             : #ifdef WIN32
     449             :     int         backslash_run_length = 0;
     450             : #endif
     451           2 :     bool        ok = true;
     452             :     const char *p;
     453             : 
     454             :     /*
     455             :      * Don't bother with adding quotes if the string is nonempty and clearly
     456             :      * contains only safe characters.
     457             :      */
     458           4 :     if (*str != '\0' &&
     459           2 :         strspn(str, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_./:") == strlen(str))
     460             :     {
     461           2 :         appendPQExpBufferStr(buf, str);
     462           2 :         return ok;
     463             :     }
     464             : 
     465             : #ifndef WIN32
     466           0 :     appendPQExpBufferChar(buf, '\'');
     467           0 :     for (p = str; *p; p++)
     468             :     {
     469           0 :         if (*p == '\n' || *p == '\r')
     470             :         {
     471           0 :             ok = false;
     472           0 :             continue;
     473             :         }
     474             : 
     475           0 :         if (*p == '\'')
     476           0 :             appendPQExpBufferStr(buf, "'\"'\"'");
     477             :         else
     478           0 :             appendPQExpBufferChar(buf, *p);
     479             :     }
     480           0 :     appendPQExpBufferChar(buf, '\'');
     481             : #else                           /* WIN32 */
     482             : 
     483             :     /*
     484             :      * A Windows system() argument experiences two layers of interpretation.
     485             :      * First, cmd.exe interprets the string.  Its behavior is undocumented,
     486             :      * but a caret escapes any byte except LF or CR that would otherwise have
     487             :      * special meaning.  Handling of a caret before LF or CR differs between
     488             :      * "cmd.exe /c" and other modes, and it is unusable here.
     489             :      *
     490             :      * Second, the new process parses its command line to construct argv (see
     491             :      * https://msdn.microsoft.com/en-us/library/17w5ykft.aspx).  This treats
     492             :      * backslash-double quote sequences specially.
     493             :      */
     494             :     appendPQExpBufferStr(buf, "^\"");
     495             :     for (p = str; *p; p++)
     496             :     {
     497             :         if (*p == '\n' || *p == '\r')
     498             :         {
     499             :             ok = false;
     500             :             continue;
     501             :         }
     502             : 
     503             :         /* Change N backslashes before a double quote to 2N+1 backslashes. */
     504             :         if (*p == '"')
     505             :         {
     506             :             while (backslash_run_length)
     507             :             {
     508             :                 appendPQExpBufferStr(buf, "^\\");
     509             :                 backslash_run_length--;
     510             :             }
     511             :             appendPQExpBufferStr(buf, "^\\");
     512             :         }
     513             :         else if (*p == '\\')
     514             :             backslash_run_length++;
     515             :         else
     516             :             backslash_run_length = 0;
     517             : 
     518             :         /*
     519             :          * Decline to caret-escape the most mundane characters, to ease
     520             :          * debugging and lest we approach the command length limit.
     521             :          */
     522             :         if (!((*p >= 'a' && *p <= 'z') ||
     523             :               (*p >= 'A' && *p <= 'Z') ||
     524             :               (*p >= '0' && *p <= '9')))
     525             :             appendPQExpBufferChar(buf, '^');
     526             :         appendPQExpBufferChar(buf, *p);
     527             :     }
     528             : 
     529             :     /*
     530             :      * Change N backslashes at end of argument to 2N backslashes, because they
     531             :      * precede the double quote that terminates the argument.
     532             :      */
     533             :     while (backslash_run_length)
     534             :     {
     535             :         appendPQExpBufferStr(buf, "^\\");
     536             :         backslash_run_length--;
     537             :     }
     538             :     appendPQExpBufferStr(buf, "^\"");
     539             : #endif                          /* WIN32 */
     540             : 
     541           0 :     return ok;
     542             : }
     543             : 
     544             : 
     545             : /*
     546             :  * Append the given string to the buffer, with suitable quoting for passing
     547             :  * the string as a value, in a keyword/pair value in a libpq connection
     548             :  * string
     549             :  */
     550             : void
     551          33 : appendConnStrVal(PQExpBuffer buf, const char *str)
     552             : {
     553             :     const char *s;
     554             :     bool        needquotes;
     555             : 
     556             :     /*
     557             :      * If the string is one or more plain ASCII characters, no need to quote
     558             :      * it. This is quite conservative, but better safe than sorry.
     559             :      */
     560          33 :     needquotes = true;
     561         363 :     for (s = str; *s; s++)
     562             :     {
     563         330 :         if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
     564           0 :               (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
     565             :         {
     566           0 :             needquotes = true;
     567           0 :             break;
     568             :         }
     569         330 :         needquotes = false;
     570             :     }
     571             : 
     572          33 :     if (needquotes)
     573             :     {
     574           0 :         appendPQExpBufferChar(buf, '\'');
     575           0 :         while (*str)
     576             :         {
     577             :             /* ' and \ must be escaped by to \' and \\ */
     578           0 :             if (*str == '\'' || *str == '\\')
     579           0 :                 appendPQExpBufferChar(buf, '\\');
     580             : 
     581           0 :             appendPQExpBufferChar(buf, *str);
     582           0 :             str++;
     583             :         }
     584           0 :         appendPQExpBufferChar(buf, '\'');
     585             :     }
     586             :     else
     587          33 :         appendPQExpBufferStr(buf, str);
     588          33 : }
     589             : 
     590             : 
     591             : /*
     592             :  * Append a psql meta-command that connects to the given database with the
     593             :  * then-current connection's user, host and port.
     594             :  */
     595             : void
     596           0 : appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname)
     597             : {
     598             :     const char *s;
     599             :     bool complex;
     600             : 
     601             :     /*
     602             :      * If the name is plain ASCII characters, emit a trivial "\connect "foo"".
     603             :      * For other names, even many not technically requiring it, skip to the
     604             :      * general case.  No database has a zero-length name.
     605             :      */
     606           0 :     complex = false;
     607             : 
     608           0 :     for (s = dbname; *s; s++)
     609             :     {
     610           0 :         if (*s == '\n' || *s == '\r')
     611             :         {
     612           0 :             fprintf(stderr,
     613             :                     _("database name contains a newline or carriage return: \"%s\"\n"),
     614             :                     dbname);
     615           0 :             exit(EXIT_FAILURE);
     616             :         }
     617             : 
     618           0 :         if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
     619           0 :               (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
     620             :         {
     621           0 :             complex = true;
     622             :         }
     623             :     }
     624             : 
     625           0 :     appendPQExpBufferStr(buf, "\\connect ");
     626           0 :     if (complex)
     627             :     {
     628             :         PQExpBufferData connstr;
     629             : 
     630           0 :         initPQExpBuffer(&connstr);
     631           0 :         appendPQExpBuffer(&connstr, "dbname=");
     632           0 :         appendConnStrVal(&connstr, dbname);
     633             : 
     634           0 :         appendPQExpBuffer(buf, "-reuse-previous=on ");
     635             : 
     636             :         /*
     637             :          * As long as the name does not contain a newline, SQL identifier
     638             :          * quoting satisfies the psql meta-command parser.  Prefer not to
     639             :          * involve psql-interpreted single quotes, which behaved differently
     640             :          * before PostgreSQL 9.2.
     641             :          */
     642           0 :         appendPQExpBufferStr(buf, fmtId(connstr.data));
     643             : 
     644           0 :         termPQExpBuffer(&connstr);
     645             :     }
     646             :     else
     647           0 :         appendPQExpBufferStr(buf, fmtId(dbname));
     648           0 :     appendPQExpBufferChar(buf, '\n');
     649           0 : }
     650             : 
     651             : 
     652             : /*
     653             :  * Deconstruct the text representation of a 1-dimensional Postgres array
     654             :  * into individual items.
     655             :  *
     656             :  * On success, returns true and sets *itemarray and *nitems to describe
     657             :  * an array of individual strings.  On parse failure, returns false;
     658             :  * *itemarray may exist or be NULL.
     659             :  *
     660             :  * NOTE: free'ing itemarray is sufficient to deallocate the working storage.
     661             :  */
     662             : bool
     663           0 : parsePGArray(const char *atext, char ***itemarray, int *nitems)
     664             : {
     665             :     int         inputlen;
     666             :     char      **items;
     667             :     char       *strings;
     668             :     int         curitem;
     669             : 
     670             :     /*
     671             :      * We expect input in the form of "{item,item,item}" where any item is
     672             :      * either raw data, or surrounded by double quotes (in which case embedded
     673             :      * characters including backslashes and quotes are backslashed).
     674             :      *
     675             :      * We build the result as an array of pointers followed by the actual
     676             :      * string data, all in one malloc block for convenience of deallocation.
     677             :      * The worst-case storage need is not more than one pointer and one
     678             :      * character for each input character (consider "{,,,,,,,,,,}").
     679             :      */
     680           0 :     *itemarray = NULL;
     681           0 :     *nitems = 0;
     682           0 :     inputlen = strlen(atext);
     683           0 :     if (inputlen < 2 || atext[0] != '{' || atext[inputlen - 1] != '}')
     684           0 :         return false;           /* bad input */
     685           0 :     items = (char **) malloc(inputlen * (sizeof(char *) + sizeof(char)));
     686           0 :     if (items == NULL)
     687           0 :         return false;           /* out of memory */
     688           0 :     *itemarray = items;
     689           0 :     strings = (char *) (items + inputlen);
     690             : 
     691           0 :     atext++;                    /* advance over initial '{' */
     692           0 :     curitem = 0;
     693           0 :     while (*atext != '}')
     694             :     {
     695           0 :         if (*atext == '\0')
     696           0 :             return false;       /* premature end of string */
     697           0 :         items[curitem] = strings;
     698           0 :         while (*atext != '}' && *atext != ',')
     699             :         {
     700           0 :             if (*atext == '\0')
     701           0 :                 return false;   /* premature end of string */
     702           0 :             if (*atext != '"')
     703           0 :                 *strings++ = *atext++;  /* copy unquoted data */
     704             :             else
     705             :             {
     706             :                 /* process quoted substring */
     707           0 :                 atext++;
     708           0 :                 while (*atext != '"')
     709             :                 {
     710           0 :                     if (*atext == '\0')
     711           0 :                         return false;   /* premature end of string */
     712           0 :                     if (*atext == '\\')
     713             :                     {
     714           0 :                         atext++;
     715           0 :                         if (*atext == '\0')
     716           0 :                             return false;   /* premature end of string */
     717             :                     }
     718           0 :                     *strings++ = *atext++;  /* copy quoted data */
     719             :                 }
     720           0 :                 atext++;
     721             :             }
     722             :         }
     723           0 :         *strings++ = '\0';
     724           0 :         if (*atext == ',')
     725           0 :             atext++;
     726           0 :         curitem++;
     727             :     }
     728           0 :     if (atext[1] != '\0')
     729           0 :         return false;           /* bogus syntax (embedded '}') */
     730           0 :     *nitems = curitem;
     731           0 :     return true;
     732             : }
     733             : 
     734             : 
     735             : /*
     736             :  * Format a reloptions array and append it to the given buffer.
     737             :  *
     738             :  * "prefix" is prepended to the option names; typically it's "" or "toast.".
     739             :  *
     740             :  * Returns false if the reloptions array could not be parsed (in which case
     741             :  * nothing will have been appended to the buffer), or true on success.
     742             :  *
     743             :  * Note: this logic should generally match the backend's flatten_reloptions()
     744             :  * (in adt/ruleutils.c).
     745             :  */
     746             : bool
     747           0 : appendReloptionsArray(PQExpBuffer buffer, const char *reloptions,
     748             :                       const char *prefix, int encoding, bool std_strings)
     749             : {
     750             :     char      **options;
     751             :     int         noptions;
     752             :     int         i;
     753             : 
     754           0 :     if (!parsePGArray(reloptions, &options, &noptions))
     755             :     {
     756           0 :         if (options)
     757           0 :             free(options);
     758           0 :         return false;
     759             :     }
     760             : 
     761           0 :     for (i = 0; i < noptions; i++)
     762             :     {
     763           0 :         char       *option = options[i];
     764             :         char       *name;
     765             :         char       *separator;
     766             :         char       *value;
     767             : 
     768             :         /*
     769             :          * Each array element should have the form name=value.  If the "=" is
     770             :          * missing for some reason, treat it like an empty value.
     771             :          */
     772           0 :         name = option;
     773           0 :         separator = strchr(option, '=');
     774           0 :         if (separator)
     775             :         {
     776           0 :             *separator = '\0';
     777           0 :             value = separator + 1;
     778             :         }
     779             :         else
     780           0 :             value = "";
     781             : 
     782           0 :         if (i > 0)
     783           0 :             appendPQExpBufferStr(buffer, ", ");
     784           0 :         appendPQExpBuffer(buffer, "%s%s=", prefix, fmtId(name));
     785             : 
     786             :         /*
     787             :          * In general we need to quote the value; but to avoid unnecessary
     788             :          * clutter, do not quote if it is an identifier that would not need
     789             :          * quoting.  (We could also allow numbers, but that is a bit trickier
     790             :          * than it looks --- for example, are leading zeroes significant?  We
     791             :          * don't want to assume very much here about what custom reloptions
     792             :          * might mean.)
     793             :          */
     794           0 :         if (strcmp(fmtId(value), value) == 0)
     795           0 :             appendPQExpBufferStr(buffer, value);
     796             :         else
     797           0 :             appendStringLiteral(buffer, value, encoding, std_strings);
     798             :     }
     799             : 
     800           0 :     if (options)
     801           0 :         free(options);
     802             : 
     803           0 :     return true;
     804             : }
     805             : 
     806             : 
     807             : /*
     808             :  * processSQLNamePattern
     809             :  *
     810             :  * Scan a wildcard-pattern string and generate appropriate WHERE clauses
     811             :  * to limit the set of objects returned.  The WHERE clauses are appended
     812             :  * to the already-partially-constructed query in buf.  Returns whether
     813             :  * any clause was added.
     814             :  *
     815             :  * conn: connection query will be sent to (consulted for escaping rules).
     816             :  * buf: output parameter.
     817             :  * pattern: user-specified pattern option, or NULL if none ("*" is implied).
     818             :  * have_where: true if caller already emitted "WHERE" (clauses will be ANDed
     819             :  * onto the existing WHERE clause).
     820             :  * force_escape: always quote regexp special characters, even outside
     821             :  * double quotes (else they are quoted only between double quotes).
     822             :  * schemavar: name of query variable to match against a schema-name pattern.
     823             :  * Can be NULL if no schema.
     824             :  * namevar: name of query variable to match against an object-name pattern.
     825             :  * altnamevar: NULL, or name of an alternative variable to match against name.
     826             :  * visibilityrule: clause to use if we want to restrict to visible objects
     827             :  * (for example, "pg_catalog.pg_table_is_visible(p.oid)").  Can be NULL.
     828             :  *
     829             :  * Formatting note: the text already present in buf should end with a newline.
     830             :  * The appended text, if any, will end with one too.
     831             :  */
     832             : bool
     833         303 : processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
     834             :                       bool have_where, bool force_escape,
     835             :                       const char *schemavar, const char *namevar,
     836             :                       const char *altnamevar, const char *visibilityrule)
     837             : {
     838             :     PQExpBufferData schemabuf;
     839             :     PQExpBufferData namebuf;
     840         303 :     int         encoding = PQclientEncoding(conn);
     841             :     bool        inquotes;
     842             :     const char *cp;
     843             :     int         i;
     844         303 :     bool        added_clause = false;
     845             : 
     846             : #define WHEREAND() \
     847             :     (appendPQExpBufferStr(buf, have_where ? "  AND " : "WHERE "), \
     848             :      have_where = true, added_clause = true)
     849             : 
     850         303 :     if (pattern == NULL)
     851             :     {
     852             :         /* Default: select all visible objects */
     853          46 :         if (visibilityrule)
     854             :         {
     855           2 :             WHEREAND();
     856           2 :             appendPQExpBuffer(buf, "%s\n", visibilityrule);
     857             :         }
     858          46 :         return added_clause;
     859             :     }
     860             : 
     861         257 :     initPQExpBuffer(&schemabuf);
     862         257 :     initPQExpBuffer(&namebuf);
     863             : 
     864             :     /*
     865             :      * Parse the pattern, converting quotes and lower-casing unquoted letters.
     866             :      * Also, adjust shell-style wildcard characters into regexp notation.
     867             :      *
     868             :      * We surround the pattern with "^(...)$" to force it to match the whole
     869             :      * string, as per SQL practice.  We have to have parens in case the string
     870             :      * contains "|", else the "^" and "$" will be bound into the first and
     871             :      * last alternatives which is not what we want.
     872             :      *
     873             :      * Note: the result of this pass is the actual regexp pattern(s) we want
     874             :      * to execute.  Quoting/escaping into SQL literal format will be done
     875             :      * below using appendStringLiteralConn().
     876             :      */
     877         257 :     appendPQExpBufferStr(&namebuf, "^(");
     878             : 
     879         257 :     inquotes = false;
     880         257 :     cp = pattern;
     881             : 
     882        3472 :     while (*cp)
     883             :     {
     884        2958 :         char        ch = *cp;
     885             : 
     886        2958 :         if (ch == '"')
     887             :         {
     888           0 :             if (inquotes && cp[1] == '"')
     889             :             {
     890             :                 /* emit one quote, stay in inquotes mode */
     891           0 :                 appendPQExpBufferChar(&namebuf, '"');
     892           0 :                 cp++;
     893             :             }
     894             :             else
     895           0 :                 inquotes = !inquotes;
     896           0 :             cp++;
     897             :         }
     898        2958 :         else if (!inquotes && isupper((unsigned char) ch))
     899             :         {
     900           0 :             appendPQExpBufferChar(&namebuf,
     901           0 :                                   pg_tolower((unsigned char) ch));
     902           0 :             cp++;
     903             :         }
     904        2958 :         else if (!inquotes && ch == '*')
     905             :         {
     906           1 :             appendPQExpBufferStr(&namebuf, ".*");
     907           1 :             cp++;
     908             :         }
     909        2957 :         else if (!inquotes && ch == '?')
     910             :         {
     911           0 :             appendPQExpBufferChar(&namebuf, '.');
     912           0 :             cp++;
     913             :         }
     914        2957 :         else if (!inquotes && ch == '.')
     915             :         {
     916             :             /* Found schema/name separator, move current pattern to schema */
     917          15 :             resetPQExpBuffer(&schemabuf);
     918          15 :             appendPQExpBufferStr(&schemabuf, namebuf.data);
     919          15 :             resetPQExpBuffer(&namebuf);
     920          15 :             appendPQExpBufferStr(&namebuf, "^(");
     921          15 :             cp++;
     922             :         }
     923        2942 :         else if (ch == '$')
     924             :         {
     925             :             /*
     926             :              * Dollar is always quoted, whether inside quotes or not. The
     927             :              * reason is that it's allowed in SQL identifiers, so there's a
     928             :              * significant use-case for treating it literally, while because
     929             :              * we anchor the pattern automatically there is no use-case for
     930             :              * having it possess its regexp meaning.
     931             :              */
     932           0 :             appendPQExpBufferStr(&namebuf, "\\$");
     933           0 :             cp++;
     934             :         }
     935             :         else
     936             :         {
     937             :             /*
     938             :              * Ordinary data character, transfer to pattern
     939             :              *
     940             :              * Inside double quotes, or at all times if force_escape is true,
     941             :              * quote regexp special characters with a backslash to avoid
     942             :              * regexp errors.  Outside quotes, however, let them pass through
     943             :              * as-is; this lets knowledgeable users build regexp expressions
     944             :              * that are more powerful than shell-style patterns.
     945             :              */
     946        2942 :             if ((inquotes || force_escape) &&
     947           0 :                 strchr("|*+?()[]{}.^$\\", ch))
     948           0 :                 appendPQExpBufferChar(&namebuf, '\\');
     949        2942 :             i = PQmblen(cp, encoding);
     950        8826 :             while (i-- && *cp)
     951             :             {
     952        2942 :                 appendPQExpBufferChar(&namebuf, *cp);
     953        2942 :                 cp++;
     954             :             }
     955             :         }
     956             :     }
     957             : 
     958             :     /*
     959             :      * Now decide what we need to emit.  Note there will be a leading "^(" in
     960             :      * the patterns in any case.
     961             :      */
     962         257 :     if (namebuf.len > 2)
     963             :     {
     964             :         /* We have a name pattern, so constrain the namevar(s) */
     965             : 
     966         257 :         appendPQExpBufferStr(&namebuf, ")$");
     967             :         /* Optimize away a "*" pattern */
     968         257 :         if (strcmp(namebuf.data, "^(.*)$") != 0)
     969             :         {
     970         257 :             WHEREAND();
     971         257 :             if (altnamevar)
     972             :             {
     973           0 :                 appendPQExpBuffer(buf, "(%s ~ ", namevar);
     974           0 :                 appendStringLiteralConn(buf, namebuf.data, conn);
     975           0 :                 appendPQExpBuffer(buf, "\n        OR %s ~ ", altnamevar);
     976           0 :                 appendStringLiteralConn(buf, namebuf.data, conn);
     977           0 :                 appendPQExpBufferStr(buf, ")\n");
     978             :             }
     979             :             else
     980             :             {
     981         257 :                 appendPQExpBuffer(buf, "%s ~ ", namevar);
     982         257 :                 appendStringLiteralConn(buf, namebuf.data, conn);
     983         257 :                 appendPQExpBufferChar(buf, '\n');
     984             :             }
     985             :         }
     986             :     }
     987             : 
     988         257 :     if (schemabuf.len > 2)
     989             :     {
     990             :         /* We have a schema pattern, so constrain the schemavar */
     991             : 
     992          15 :         appendPQExpBufferStr(&schemabuf, ")$");
     993             :         /* Optimize away a "*" pattern */
     994          15 :         if (strcmp(schemabuf.data, "^(.*)$") != 0 && schemavar)
     995             :         {
     996          15 :             WHEREAND();
     997          15 :             appendPQExpBuffer(buf, "%s ~ ", schemavar);
     998          15 :             appendStringLiteralConn(buf, schemabuf.data, conn);
     999          15 :             appendPQExpBufferChar(buf, '\n');
    1000             :         }
    1001             :     }
    1002             :     else
    1003             :     {
    1004             :         /* No schema pattern given, so select only visible objects */
    1005         242 :         if (visibilityrule)
    1006             :         {
    1007         234 :             WHEREAND();
    1008         234 :             appendPQExpBuffer(buf, "%s\n", visibilityrule);
    1009             :         }
    1010             :     }
    1011             : 
    1012         257 :     termPQExpBuffer(&schemabuf);
    1013         257 :     termPQExpBuffer(&namebuf);
    1014             : 
    1015         257 :     return added_clause;
    1016             : #undef WHEREAND
    1017             : }

Generated by: LCOV version 1.11