Header And Logo

PostgreSQL
| The world's most advanced open source database.

dumputils.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * Utility routines for SQL dumping
00004  *  Basically this is stuff that is useful in both pg_dump and pg_dumpall.
00005  *  Lately it's also being used by psql and bin/scripts/ ...
00006  *
00007  *
00008  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00009  * Portions Copyright (c) 1994, Regents of the University of California
00010  *
00011  * src/bin/pg_dump/dumputils.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 #include "postgres_fe.h"
00016 
00017 #include <ctype.h>
00018 
00019 #include "dumputils.h"
00020 
00021 #include "parser/keywords.h"
00022 
00023 
00024 /* Globals from keywords.c */
00025 extern const ScanKeyword FEScanKeywords[];
00026 extern const int NumFEScanKeywords;
00027 
00028 #define supports_grant_options(version) ((version) >= 70400)
00029 
00030 static bool parseAclItem(const char *item, const char *type,
00031              const char *name, const char *subname, int remoteVersion,
00032              PQExpBuffer grantee, PQExpBuffer grantor,
00033              PQExpBuffer privs, PQExpBuffer privswgo);
00034 static char *copyAclUserName(PQExpBuffer output, char *input);
00035 static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
00036        const char *subname);
00037 static PQExpBuffer defaultGetLocalPQExpBuffer(void);
00038 
00039 /* Globals exported by this file */
00040 int         quote_all_identifiers = 0;
00041 PQExpBuffer (*getLocalPQExpBuffer) (void) = defaultGetLocalPQExpBuffer;
00042 
00043 /*
00044  * Returns a temporary PQExpBuffer, valid until the next call to the function.
00045  * This is used by fmtId and fmtQualifiedId.
00046  *
00047  * Non-reentrant and non-thread-safe but reduces memory leakage. You can
00048  * replace this with a custom version by setting the getLocalPQExpBuffer
00049  * function pointer.
00050  */
00051 static PQExpBuffer
00052 defaultGetLocalPQExpBuffer(void)
00053 {
00054     static PQExpBuffer id_return = NULL;
00055 
00056     if (id_return)              /* first time through? */
00057     {
00058         /* same buffer, just wipe contents */
00059         resetPQExpBuffer(id_return);
00060     }
00061     else
00062     {
00063         /* new buffer */
00064         id_return = createPQExpBuffer();
00065     }
00066 
00067     return id_return;
00068 }
00069 
00070 /*
00071  *  Quotes input string if it's not a legitimate SQL identifier as-is.
00072  *
00073  *  Note that the returned string must be used before calling fmtId again,
00074  *  since we re-use the same return buffer each time.
00075  */
00076 const char *
00077 fmtId(const char *rawid)
00078 {
00079     PQExpBuffer id_return = getLocalPQExpBuffer();
00080 
00081     const char *cp;
00082     bool        need_quotes = false;
00083 
00084     /*
00085      * These checks need to match the identifier production in scan.l. Don't
00086      * use islower() etc.
00087      */
00088     if (quote_all_identifiers)
00089         need_quotes = true;
00090     /* slightly different rules for first character */
00091     else if (!((rawid[0] >= 'a' && rawid[0] <= 'z') || rawid[0] == '_'))
00092         need_quotes = true;
00093     else
00094     {
00095         /* otherwise check the entire string */
00096         for (cp = rawid; *cp; cp++)
00097         {
00098             if (!((*cp >= 'a' && *cp <= 'z')
00099                   || (*cp >= '0' && *cp <= '9')
00100                   || (*cp == '_')))
00101             {
00102                 need_quotes = true;
00103                 break;
00104             }
00105         }
00106     }
00107 
00108     if (!need_quotes)
00109     {
00110         /*
00111          * Check for keyword.  We quote keywords except for unreserved ones.
00112          * (In some cases we could avoid quoting a col_name or type_func_name
00113          * keyword, but it seems much harder than it's worth to tell that.)
00114          *
00115          * Note: ScanKeywordLookup() does case-insensitive comparison, but
00116          * that's fine, since we already know we have all-lower-case.
00117          */
00118         const ScanKeyword *keyword = ScanKeywordLookup(rawid,
00119                                                        FEScanKeywords,
00120                                                        NumFEScanKeywords);
00121 
00122         if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD)
00123             need_quotes = true;
00124     }
00125 
00126     if (!need_quotes)
00127     {
00128         /* no quoting needed */
00129         appendPQExpBufferStr(id_return, rawid);
00130     }
00131     else
00132     {
00133         appendPQExpBufferChar(id_return, '\"');
00134         for (cp = rawid; *cp; cp++)
00135         {
00136             /*
00137              * Did we find a double-quote in the string? Then make this a
00138              * double double-quote per SQL99. Before, we put in a
00139              * backslash/double-quote pair. - thomas 2000-08-05
00140              */
00141             if (*cp == '\"')
00142                 appendPQExpBufferChar(id_return, '\"');
00143             appendPQExpBufferChar(id_return, *cp);
00144         }
00145         appendPQExpBufferChar(id_return, '\"');
00146     }
00147 
00148     return id_return->data;
00149 }
00150 
00151 /*
00152  * fmtQualifiedId - convert a qualified name to the proper format for
00153  * the source database.
00154  *
00155  * Like fmtId, use the result before calling again.
00156  *
00157  * Since we call fmtId and it also uses getThreadLocalPQExpBuffer() we cannot
00158  * use it until we're finished with calling fmtId().
00159  */
00160 const char *
00161 fmtQualifiedId(int remoteVersion, const char *schema, const char *id)
00162 {
00163     PQExpBuffer id_return;
00164     PQExpBuffer lcl_pqexp = createPQExpBuffer();
00165 
00166     /* Suppress schema name if fetching from pre-7.3 DB */
00167     if (remoteVersion >= 70300 && schema && *schema)
00168     {
00169         appendPQExpBuffer(lcl_pqexp, "%s.", fmtId(schema));
00170     }
00171     appendPQExpBuffer(lcl_pqexp, "%s", fmtId(id));
00172 
00173     id_return = getLocalPQExpBuffer();
00174 
00175     appendPQExpBuffer(id_return, "%s", lcl_pqexp->data);
00176     destroyPQExpBuffer(lcl_pqexp);
00177 
00178     return id_return->data;
00179 }
00180 
00181 /*
00182  * Convert a string value to an SQL string literal and append it to
00183  * the given buffer.  We assume the specified client_encoding and
00184  * standard_conforming_strings settings.
00185  *
00186  * This is essentially equivalent to libpq's PQescapeStringInternal,
00187  * except for the output buffer structure.  We need it in situations
00188  * where we do not have a PGconn available.  Where we do,
00189  * appendStringLiteralConn is a better choice.
00190  */
00191 void
00192 appendStringLiteral(PQExpBuffer buf, const char *str,
00193                     int encoding, bool std_strings)
00194 {
00195     size_t      length = strlen(str);
00196     const char *source = str;
00197     char       *target;
00198 
00199     if (!enlargePQExpBuffer(buf, 2 * length + 2))
00200         return;
00201 
00202     target = buf->data + buf->len;
00203     *target++ = '\'';
00204 
00205     while (*source != '\0')
00206     {
00207         char        c = *source;
00208         int         len;
00209         int         i;
00210 
00211         /* Fast path for plain ASCII */
00212         if (!IS_HIGHBIT_SET(c))
00213         {
00214             /* Apply quoting if needed */
00215             if (SQL_STR_DOUBLE(c, !std_strings))
00216                 *target++ = c;
00217             /* Copy the character */
00218             *target++ = c;
00219             source++;
00220             continue;
00221         }
00222 
00223         /* Slow path for possible multibyte characters */
00224         len = PQmblen(source, encoding);
00225 
00226         /* Copy the character */
00227         for (i = 0; i < len; i++)
00228         {
00229             if (*source == '\0')
00230                 break;
00231             *target++ = *source++;
00232         }
00233 
00234         /*
00235          * If we hit premature end of string (ie, incomplete multibyte
00236          * character), try to pad out to the correct length with spaces. We
00237          * may not be able to pad completely, but we will always be able to
00238          * insert at least one pad space (since we'd not have quoted a
00239          * multibyte character).  This should be enough to make a string that
00240          * the server will error out on.
00241          */
00242         if (i < len)
00243         {
00244             char       *stop = buf->data + buf->maxlen - 2;
00245 
00246             for (; i < len; i++)
00247             {
00248                 if (target >= stop)
00249                     break;
00250                 *target++ = ' ';
00251             }
00252             break;
00253         }
00254     }
00255 
00256     /* Write the terminating quote and NUL character. */
00257     *target++ = '\'';
00258     *target = '\0';
00259 
00260     buf->len = target - buf->data;
00261 }
00262 
00263 
00264 /*
00265  * Convert a string value to an SQL string literal and append it to
00266  * the given buffer.  Encoding and string syntax rules are as indicated
00267  * by current settings of the PGconn.
00268  */
00269 void
00270 appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
00271 {
00272     size_t      length = strlen(str);
00273 
00274     /*
00275      * XXX This is a kluge to silence escape_string_warning in our utility
00276      * programs.  It should go away someday.
00277      */
00278     if (strchr(str, '\\') != NULL && PQserverVersion(conn) >= 80100)
00279     {
00280         /* ensure we are not adjacent to an identifier */
00281         if (buf->len > 0 && buf->data[buf->len - 1] != ' ')
00282             appendPQExpBufferChar(buf, ' ');
00283         appendPQExpBufferChar(buf, ESCAPE_STRING_SYNTAX);
00284         appendStringLiteral(buf, str, PQclientEncoding(conn), false);
00285         return;
00286     }
00287     /* XXX end kluge */
00288 
00289     if (!enlargePQExpBuffer(buf, 2 * length + 2))
00290         return;
00291     appendPQExpBufferChar(buf, '\'');
00292     buf->len += PQescapeStringConn(conn, buf->data + buf->len,
00293                                    str, length, NULL);
00294     appendPQExpBufferChar(buf, '\'');
00295 }
00296 
00297 
00298 /*
00299  * Convert a string value to a dollar quoted literal and append it to
00300  * the given buffer. If the dqprefix parameter is not NULL then the
00301  * dollar quote delimiter will begin with that (after the opening $).
00302  *
00303  * No escaping is done at all on str, in compliance with the rules
00304  * for parsing dollar quoted strings.  Also, we need not worry about
00305  * encoding issues.
00306  */
00307 void
00308 appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix)
00309 {
00310     static const char suffixes[] = "_XXXXXXX";
00311     int         nextchar = 0;
00312     PQExpBuffer delimBuf = createPQExpBuffer();
00313 
00314     /* start with $ + dqprefix if not NULL */
00315     appendPQExpBufferChar(delimBuf, '$');
00316     if (dqprefix)
00317         appendPQExpBufferStr(delimBuf, dqprefix);
00318 
00319     /*
00320      * Make sure we choose a delimiter which (without the trailing $) is not
00321      * present in the string being quoted. We don't check with the trailing $
00322      * because a string ending in $foo must not be quoted with $foo$.
00323      */
00324     while (strstr(str, delimBuf->data) != NULL)
00325     {
00326         appendPQExpBufferChar(delimBuf, suffixes[nextchar++]);
00327         nextchar %= sizeof(suffixes) - 1;
00328     }
00329 
00330     /* add trailing $ */
00331     appendPQExpBufferChar(delimBuf, '$');
00332 
00333     /* quote it and we are all done */
00334     appendPQExpBufferStr(buf, delimBuf->data);
00335     appendPQExpBufferStr(buf, str);
00336     appendPQExpBufferStr(buf, delimBuf->data);
00337 
00338     destroyPQExpBuffer(delimBuf);
00339 }
00340 
00341 
00342 /*
00343  * Convert a bytea value (presented as raw bytes) to an SQL string literal
00344  * and append it to the given buffer.  We assume the specified
00345  * standard_conforming_strings setting.
00346  *
00347  * This is needed in situations where we do not have a PGconn available.
00348  * Where we do, PQescapeByteaConn is a better choice.
00349  */
00350 void
00351 appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length,
00352                    bool std_strings)
00353 {
00354     const unsigned char *source = str;
00355     char       *target;
00356 
00357     static const char hextbl[] = "0123456789abcdef";
00358 
00359     /*
00360      * This implementation is hard-wired to produce hex-format output. We do
00361      * not know the server version the output will be loaded into, so making
00362      * an intelligent format choice is impossible.  It might be better to
00363      * always use the old escaped format.
00364      */
00365     if (!enlargePQExpBuffer(buf, 2 * length + 5))
00366         return;
00367 
00368     target = buf->data + buf->len;
00369     *target++ = '\'';
00370     if (!std_strings)
00371         *target++ = '\\';
00372     *target++ = '\\';
00373     *target++ = 'x';
00374 
00375     while (length-- > 0)
00376     {
00377         unsigned char c = *source++;
00378 
00379         *target++ = hextbl[(c >> 4) & 0xF];
00380         *target++ = hextbl[c & 0xF];
00381     }
00382 
00383     /* Write the terminating quote and NUL character. */
00384     *target++ = '\'';
00385     *target = '\0';
00386 
00387     buf->len = target - buf->data;
00388 }
00389 
00390 
00391 /*
00392  * Deconstruct the text representation of a 1-dimensional Postgres array
00393  * into individual items.
00394  *
00395  * On success, returns true and sets *itemarray and *nitems to describe
00396  * an array of individual strings.  On parse failure, returns false;
00397  * *itemarray may exist or be NULL.
00398  *
00399  * NOTE: free'ing itemarray is sufficient to deallocate the working storage.
00400  */
00401 bool
00402 parsePGArray(const char *atext, char ***itemarray, int *nitems)
00403 {
00404     int         inputlen;
00405     char      **items;
00406     char       *strings;
00407     int         curitem;
00408 
00409     /*
00410      * We expect input in the form of "{item,item,item}" where any item is
00411      * either raw data, or surrounded by double quotes (in which case embedded
00412      * characters including backslashes and quotes are backslashed).
00413      *
00414      * We build the result as an array of pointers followed by the actual
00415      * string data, all in one malloc block for convenience of deallocation.
00416      * The worst-case storage need is not more than one pointer and one
00417      * character for each input character (consider "{,,,,,,,,,,}").
00418      */
00419     *itemarray = NULL;
00420     *nitems = 0;
00421     inputlen = strlen(atext);
00422     if (inputlen < 2 || atext[0] != '{' || atext[inputlen - 1] != '}')
00423         return false;           /* bad input */
00424     items = (char **) malloc(inputlen * (sizeof(char *) + sizeof(char)));
00425     if (items == NULL)
00426         return false;           /* out of memory */
00427     *itemarray = items;
00428     strings = (char *) (items + inputlen);
00429 
00430     atext++;                    /* advance over initial '{' */
00431     curitem = 0;
00432     while (*atext != '}')
00433     {
00434         if (*atext == '\0')
00435             return false;       /* premature end of string */
00436         items[curitem] = strings;
00437         while (*atext != '}' && *atext != ',')
00438         {
00439             if (*atext == '\0')
00440                 return false;   /* premature end of string */
00441             if (*atext != '"')
00442                 *strings++ = *atext++;  /* copy unquoted data */
00443             else
00444             {
00445                 /* process quoted substring */
00446                 atext++;
00447                 while (*atext != '"')
00448                 {
00449                     if (*atext == '\0')
00450                         return false;   /* premature end of string */
00451                     if (*atext == '\\')
00452                     {
00453                         atext++;
00454                         if (*atext == '\0')
00455                             return false;       /* premature end of string */
00456                     }
00457                     *strings++ = *atext++;      /* copy quoted data */
00458                 }
00459                 atext++;
00460             }
00461         }
00462         *strings++ = '\0';
00463         if (*atext == ',')
00464             atext++;
00465         curitem++;
00466     }
00467     if (atext[1] != '\0')
00468         return false;           /* bogus syntax (embedded '}') */
00469     *nitems = curitem;
00470     return true;
00471 }
00472 
00473 
00474 /*
00475  * Build GRANT/REVOKE command(s) for an object.
00476  *
00477  *  name: the object name, in the form to use in the commands (already quoted)
00478  *  subname: the sub-object name, if any (already quoted); NULL if none
00479  *  type: the object type (as seen in GRANT command: must be one of
00480  *      TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
00481  *      FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT)
00482  *  acls: the ACL string fetched from the database
00483  *  owner: username of object owner (will be passed through fmtId); can be
00484  *      NULL or empty string to indicate "no owner known"
00485  *  prefix: string to prefix to each generated command; typically empty
00486  *  remoteVersion: version of database
00487  *
00488  * Returns TRUE if okay, FALSE if could not parse the acl string.
00489  * The resulting commands (if any) are appended to the contents of 'sql'.
00490  *
00491  * Note: when processing a default ACL, prefix is "ALTER DEFAULT PRIVILEGES "
00492  * or something similar, and name is an empty string.
00493  *
00494  * Note: beware of passing a fmtId() result directly as 'name' or 'subname',
00495  * since this routine uses fmtId() internally.
00496  */
00497 bool
00498 buildACLCommands(const char *name, const char *subname,
00499                  const char *type, const char *acls, const char *owner,
00500                  const char *prefix, int remoteVersion,
00501                  PQExpBuffer sql)
00502 {
00503     char      **aclitems;
00504     int         naclitems;
00505     int         i;
00506     PQExpBuffer grantee,
00507                 grantor,
00508                 privs,
00509                 privswgo;
00510     PQExpBuffer firstsql,
00511                 secondsql;
00512     bool        found_owner_privs = false;
00513 
00514     if (strlen(acls) == 0)
00515         return true;            /* object has default permissions */
00516 
00517     /* treat empty-string owner same as NULL */
00518     if (owner && *owner == '\0')
00519         owner = NULL;
00520 
00521     if (!parsePGArray(acls, &aclitems, &naclitems))
00522     {
00523         if (aclitems)
00524             free(aclitems);
00525         return false;
00526     }
00527 
00528     grantee = createPQExpBuffer();
00529     grantor = createPQExpBuffer();
00530     privs = createPQExpBuffer();
00531     privswgo = createPQExpBuffer();
00532 
00533     /*
00534      * At the end, these two will be pasted together to form the result. But
00535      * the owner privileges need to go before the other ones to keep the
00536      * dependencies valid.  In recent versions this is normally the case, but
00537      * in old versions they come after the PUBLIC privileges and that results
00538      * in problems if we need to run REVOKE on the owner privileges.
00539      */
00540     firstsql = createPQExpBuffer();
00541     secondsql = createPQExpBuffer();
00542 
00543     /*
00544      * Always start with REVOKE ALL FROM PUBLIC, so that we don't have to
00545      * wire-in knowledge about the default public privileges for different
00546      * kinds of objects.
00547      */
00548     appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
00549     if (subname)
00550         appendPQExpBuffer(firstsql, "(%s)", subname);
00551     appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name);
00552 
00553     /*
00554      * We still need some hacking though to cover the case where new default
00555      * public privileges are added in new versions: the REVOKE ALL will revoke
00556      * them, leading to behavior different from what the old version had,
00557      * which is generally not what's wanted.  So add back default privs if the
00558      * source database is too old to have had that particular priv.
00559      */
00560     if (remoteVersion < 80200 && strcmp(type, "DATABASE") == 0)
00561     {
00562         /* database CONNECT priv didn't exist before 8.2 */
00563         appendPQExpBuffer(firstsql, "%sGRANT CONNECT ON %s %s TO PUBLIC;\n",
00564                           prefix, type, name);
00565     }
00566 
00567     /* Scan individual ACL items */
00568     for (i = 0; i < naclitems; i++)
00569     {
00570         if (!parseAclItem(aclitems[i], type, name, subname, remoteVersion,
00571                           grantee, grantor, privs, privswgo))
00572         {
00573             free(aclitems);
00574             return false;
00575         }
00576 
00577         if (grantor->len == 0 && owner)
00578             printfPQExpBuffer(grantor, "%s", owner);
00579 
00580         if (privs->len > 0 || privswgo->len > 0)
00581         {
00582             if (owner
00583                 && strcmp(grantee->data, owner) == 0
00584                 && strcmp(grantor->data, owner) == 0)
00585             {
00586                 found_owner_privs = true;
00587 
00588                 /*
00589                  * For the owner, the default privilege level is ALL WITH
00590                  * GRANT OPTION (only ALL prior to 7.4).
00591                  */
00592                 if (supports_grant_options(remoteVersion)
00593                     ? strcmp(privswgo->data, "ALL") != 0
00594                     : strcmp(privs->data, "ALL") != 0)
00595                 {
00596                     appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
00597                     if (subname)
00598                         appendPQExpBuffer(firstsql, "(%s)", subname);
00599                     appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
00600                                       type, name, fmtId(grantee->data));
00601                     if (privs->len > 0)
00602                         appendPQExpBuffer(firstsql,
00603                                           "%sGRANT %s ON %s %s TO %s;\n",
00604                                           prefix, privs->data, type, name,
00605                                           fmtId(grantee->data));
00606                     if (privswgo->len > 0)
00607                         appendPQExpBuffer(firstsql,
00608                             "%sGRANT %s ON %s %s TO %s WITH GRANT OPTION;\n",
00609                                           prefix, privswgo->data, type, name,
00610                                           fmtId(grantee->data));
00611                 }
00612             }
00613             else
00614             {
00615                 /*
00616                  * Otherwise can assume we are starting from no privs.
00617                  */
00618                 if (grantor->len > 0
00619                     && (!owner || strcmp(owner, grantor->data) != 0))
00620                     appendPQExpBuffer(secondsql, "SET SESSION AUTHORIZATION %s;\n",
00621                                       fmtId(grantor->data));
00622 
00623                 if (privs->len > 0)
00624                 {
00625                     appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
00626                                       prefix, privs->data, type, name);
00627                     if (grantee->len == 0)
00628                         appendPQExpBuffer(secondsql, "PUBLIC;\n");
00629                     else if (strncmp(grantee->data, "group ",
00630                                      strlen("group ")) == 0)
00631                         appendPQExpBuffer(secondsql, "GROUP %s;\n",
00632                                     fmtId(grantee->data + strlen("group ")));
00633                     else
00634                         appendPQExpBuffer(secondsql, "%s;\n", fmtId(grantee->data));
00635                 }
00636                 if (privswgo->len > 0)
00637                 {
00638                     appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
00639                                       prefix, privswgo->data, type, name);
00640                     if (grantee->len == 0)
00641                         appendPQExpBuffer(secondsql, "PUBLIC");
00642                     else if (strncmp(grantee->data, "group ",
00643                                      strlen("group ")) == 0)
00644                         appendPQExpBuffer(secondsql, "GROUP %s",
00645                                     fmtId(grantee->data + strlen("group ")));
00646                     else
00647                         appendPQExpBuffer(secondsql, "%s", fmtId(grantee->data));
00648                     appendPQExpBuffer(secondsql, " WITH GRANT OPTION;\n");
00649                 }
00650 
00651                 if (grantor->len > 0
00652                     && (!owner || strcmp(owner, grantor->data) != 0))
00653                     appendPQExpBuffer(secondsql, "RESET SESSION AUTHORIZATION;\n");
00654             }
00655         }
00656     }
00657 
00658     /*
00659      * If we didn't find any owner privs, the owner must have revoked 'em all
00660      */
00661     if (!found_owner_privs && owner)
00662     {
00663         appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
00664         if (subname)
00665             appendPQExpBuffer(firstsql, "(%s)", subname);
00666         appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
00667                           type, name, fmtId(owner));
00668     }
00669 
00670     destroyPQExpBuffer(grantee);
00671     destroyPQExpBuffer(grantor);
00672     destroyPQExpBuffer(privs);
00673     destroyPQExpBuffer(privswgo);
00674 
00675     appendPQExpBuffer(sql, "%s%s", firstsql->data, secondsql->data);
00676     destroyPQExpBuffer(firstsql);
00677     destroyPQExpBuffer(secondsql);
00678 
00679     free(aclitems);
00680 
00681     return true;
00682 }
00683 
00684 /*
00685  * Build ALTER DEFAULT PRIVILEGES command(s) for single pg_default_acl entry.
00686  *
00687  *  type: the object type (TABLES, FUNCTIONS, etc)
00688  *  nspname: schema name, or NULL for global default privileges
00689  *  acls: the ACL string fetched from the database
00690  *  owner: username of privileges owner (will be passed through fmtId)
00691  *  remoteVersion: version of database
00692  *
00693  * Returns TRUE if okay, FALSE if could not parse the acl string.
00694  * The resulting commands (if any) are appended to the contents of 'sql'.
00695  */
00696 bool
00697 buildDefaultACLCommands(const char *type, const char *nspname,
00698                         const char *acls, const char *owner,
00699                         int remoteVersion,
00700                         PQExpBuffer sql)
00701 {
00702     bool        result;
00703     PQExpBuffer prefix;
00704 
00705     prefix = createPQExpBuffer();
00706 
00707     /*
00708      * We incorporate the target role directly into the command, rather than
00709      * playing around with SET ROLE or anything like that.  This is so that a
00710      * permissions error leads to nothing happening, rather than changing
00711      * default privileges for the wrong user.
00712      */
00713     appendPQExpBuffer(prefix, "ALTER DEFAULT PRIVILEGES FOR ROLE %s ",
00714                       fmtId(owner));
00715     if (nspname)
00716         appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname));
00717 
00718     result = buildACLCommands("", NULL,
00719                               type, acls, owner,
00720                               prefix->data, remoteVersion,
00721                               sql);
00722 
00723     destroyPQExpBuffer(prefix);
00724 
00725     return result;
00726 }
00727 
00728 /*
00729  * This will parse an aclitem string, having the general form
00730  *      username=privilegecodes/grantor
00731  * or
00732  *      group groupname=privilegecodes/grantor
00733  * (the /grantor part will not be present if pre-7.4 database).
00734  *
00735  * The returned grantee string will be the dequoted username or groupname
00736  * (preceded with "group " in the latter case).  The returned grantor is
00737  * the dequoted grantor name or empty.  Privilege characters are decoded
00738  * and split between privileges with grant option (privswgo) and without
00739  * (privs).
00740  *
00741  * Note: for cross-version compatibility, it's important to use ALL when
00742  * appropriate.
00743  */
00744 static bool
00745 parseAclItem(const char *item, const char *type,
00746              const char *name, const char *subname, int remoteVersion,
00747              PQExpBuffer grantee, PQExpBuffer grantor,
00748              PQExpBuffer privs, PQExpBuffer privswgo)
00749 {
00750     char       *buf;
00751     bool        all_with_go = true;
00752     bool        all_without_go = true;
00753     char       *eqpos;
00754     char       *slpos;
00755     char       *pos;
00756 
00757     buf = strdup(item);
00758     if (!buf)
00759         return false;
00760 
00761     /* user or group name is string up to = */
00762     eqpos = copyAclUserName(grantee, buf);
00763     if (*eqpos != '=')
00764     {
00765         free(buf);
00766         return false;
00767     }
00768 
00769     /* grantor may be listed after / */
00770     slpos = strchr(eqpos + 1, '/');
00771     if (slpos)
00772     {
00773         *slpos++ = '\0';
00774         slpos = copyAclUserName(grantor, slpos);
00775         if (*slpos != '\0')
00776         {
00777             free(buf);
00778             return false;
00779         }
00780     }
00781     else
00782         resetPQExpBuffer(grantor);
00783 
00784     /* privilege codes */
00785 #define CONVERT_PRIV(code, keywd) \
00786 do { \
00787     if ((pos = strchr(eqpos + 1, code))) \
00788     { \
00789         if (*(pos + 1) == '*') \
00790         { \
00791             AddAcl(privswgo, keywd, subname); \
00792             all_without_go = false; \
00793         } \
00794         else \
00795         { \
00796             AddAcl(privs, keywd, subname); \
00797             all_with_go = false; \
00798         } \
00799     } \
00800     else \
00801         all_with_go = all_without_go = false; \
00802 } while (0)
00803 
00804     resetPQExpBuffer(privs);
00805     resetPQExpBuffer(privswgo);
00806 
00807     if (strcmp(type, "TABLE") == 0 || strcmp(type, "SEQUENCE") == 0 ||
00808         strcmp(type, "TABLES") == 0 || strcmp(type, "SEQUENCES") == 0)
00809     {
00810         CONVERT_PRIV('r', "SELECT");
00811 
00812         if (strcmp(type, "SEQUENCE") == 0 ||
00813             strcmp(type, "SEQUENCES") == 0)
00814             /* sequence only */
00815             CONVERT_PRIV('U', "USAGE");
00816         else
00817         {
00818             /* table only */
00819             CONVERT_PRIV('a', "INSERT");
00820             if (remoteVersion >= 70200)
00821                 CONVERT_PRIV('x', "REFERENCES");
00822             /* rest are not applicable to columns */
00823             if (subname == NULL)
00824             {
00825                 if (remoteVersion >= 70200)
00826                 {
00827                     CONVERT_PRIV('d', "DELETE");
00828                     CONVERT_PRIV('t', "TRIGGER");
00829                 }
00830                 if (remoteVersion >= 80400)
00831                     CONVERT_PRIV('D', "TRUNCATE");
00832             }
00833         }
00834 
00835         /* UPDATE */
00836         if (remoteVersion >= 70200 ||
00837             strcmp(type, "SEQUENCE") == 0 ||
00838             strcmp(type, "SEQUENCES") == 0)
00839             CONVERT_PRIV('w', "UPDATE");
00840         else
00841             /* 7.0 and 7.1 have a simpler worldview */
00842             CONVERT_PRIV('w', "UPDATE,DELETE");
00843     }
00844     else if (strcmp(type, "FUNCTION") == 0 ||
00845              strcmp(type, "FUNCTIONS") == 0)
00846         CONVERT_PRIV('X', "EXECUTE");
00847     else if (strcmp(type, "LANGUAGE") == 0)
00848         CONVERT_PRIV('U', "USAGE");
00849     else if (strcmp(type, "SCHEMA") == 0)
00850     {
00851         CONVERT_PRIV('C', "CREATE");
00852         CONVERT_PRIV('U', "USAGE");
00853     }
00854     else if (strcmp(type, "DATABASE") == 0)
00855     {
00856         CONVERT_PRIV('C', "CREATE");
00857         CONVERT_PRIV('c', "CONNECT");
00858         CONVERT_PRIV('T', "TEMPORARY");
00859     }
00860     else if (strcmp(type, "TABLESPACE") == 0)
00861         CONVERT_PRIV('C', "CREATE");
00862     else if (strcmp(type, "TYPE") == 0 ||
00863              strcmp(type, "TYPES") == 0)
00864         CONVERT_PRIV('U', "USAGE");
00865     else if (strcmp(type, "FOREIGN DATA WRAPPER") == 0)
00866         CONVERT_PRIV('U', "USAGE");
00867     else if (strcmp(type, "FOREIGN SERVER") == 0)
00868         CONVERT_PRIV('U', "USAGE");
00869     else if (strcmp(type, "FOREIGN TABLE") == 0)
00870         CONVERT_PRIV('r', "SELECT");
00871     else if (strcmp(type, "LARGE OBJECT") == 0)
00872     {
00873         CONVERT_PRIV('r', "SELECT");
00874         CONVERT_PRIV('w', "UPDATE");
00875     }
00876     else
00877         abort();
00878 
00879 #undef CONVERT_PRIV
00880 
00881     if (all_with_go)
00882     {
00883         resetPQExpBuffer(privs);
00884         printfPQExpBuffer(privswgo, "ALL");
00885         if (subname)
00886             appendPQExpBuffer(privswgo, "(%s)", subname);
00887     }
00888     else if (all_without_go)
00889     {
00890         resetPQExpBuffer(privswgo);
00891         printfPQExpBuffer(privs, "ALL");
00892         if (subname)
00893             appendPQExpBuffer(privs, "(%s)", subname);
00894     }
00895 
00896     free(buf);
00897 
00898     return true;
00899 }
00900 
00901 /*
00902  * Transfer a user or group name starting at *input into the output buffer,
00903  * dequoting if needed.  Returns a pointer to just past the input name.
00904  * The name is taken to end at an unquoted '=' or end of string.
00905  */
00906 static char *
00907 copyAclUserName(PQExpBuffer output, char *input)
00908 {
00909     resetPQExpBuffer(output);
00910 
00911     while (*input && *input != '=')
00912     {
00913         /*
00914          * If user name isn't quoted, then just add it to the output buffer
00915          */
00916         if (*input != '"')
00917             appendPQExpBufferChar(output, *input++);
00918         else
00919         {
00920             /* Otherwise, it's a quoted username */
00921             input++;
00922             /* Loop until we come across an unescaped quote */
00923             while (!(*input == '"' && *(input + 1) != '"'))
00924             {
00925                 if (*input == '\0')
00926                     return input;       /* really a syntax error... */
00927 
00928                 /*
00929                  * Quoting convention is to escape " as "".  Keep this code in
00930                  * sync with putid() in backend's acl.c.
00931                  */
00932                 if (*input == '"' && *(input + 1) == '"')
00933                     input++;
00934                 appendPQExpBufferChar(output, *input++);
00935             }
00936             input++;
00937         }
00938     }
00939     return input;
00940 }
00941 
00942 /*
00943  * Append a privilege keyword to a keyword list, inserting comma if needed.
00944  */
00945 static void
00946 AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname)
00947 {
00948     if (aclbuf->len > 0)
00949         appendPQExpBufferChar(aclbuf, ',');
00950     appendPQExpBuffer(aclbuf, "%s", keyword);
00951     if (subname)
00952         appendPQExpBuffer(aclbuf, "(%s)", subname);
00953 }
00954 
00955 
00956 /*
00957  * processSQLNamePattern
00958  *
00959  * Scan a wildcard-pattern string and generate appropriate WHERE clauses
00960  * to limit the set of objects returned.  The WHERE clauses are appended
00961  * to the already-partially-constructed query in buf.  Returns whether
00962  * any clause was added.
00963  *
00964  * conn: connection query will be sent to (consulted for escaping rules).
00965  * buf: output parameter.
00966  * pattern: user-specified pattern option, or NULL if none ("*" is implied).
00967  * have_where: true if caller already emitted "WHERE" (clauses will be ANDed
00968  * onto the existing WHERE clause).
00969  * force_escape: always quote regexp special characters, even outside
00970  * double quotes (else they are quoted only between double quotes).
00971  * schemavar: name of query variable to match against a schema-name pattern.
00972  * Can be NULL if no schema.
00973  * namevar: name of query variable to match against an object-name pattern.
00974  * altnamevar: NULL, or name of an alternative variable to match against name.
00975  * visibilityrule: clause to use if we want to restrict to visible objects
00976  * (for example, "pg_catalog.pg_table_is_visible(p.oid)").  Can be NULL.
00977  *
00978  * Formatting note: the text already present in buf should end with a newline.
00979  * The appended text, if any, will end with one too.
00980  */
00981 bool
00982 processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
00983                       bool have_where, bool force_escape,
00984                       const char *schemavar, const char *namevar,
00985                       const char *altnamevar, const char *visibilityrule)
00986 {
00987     PQExpBufferData schemabuf;
00988     PQExpBufferData namebuf;
00989     int         encoding = PQclientEncoding(conn);
00990     bool        inquotes;
00991     const char *cp;
00992     int         i;
00993     bool        added_clause = false;
00994 
00995 #define WHEREAND() \
00996     (appendPQExpBufferStr(buf, have_where ? "  AND " : "WHERE "), \
00997      have_where = true, added_clause = true)
00998 
00999     if (pattern == NULL)
01000     {
01001         /* Default: select all visible objects */
01002         if (visibilityrule)
01003         {
01004             WHEREAND();
01005             appendPQExpBuffer(buf, "%s\n", visibilityrule);
01006         }
01007         return added_clause;
01008     }
01009 
01010     initPQExpBuffer(&schemabuf);
01011     initPQExpBuffer(&namebuf);
01012 
01013     /*
01014      * Parse the pattern, converting quotes and lower-casing unquoted letters.
01015      * Also, adjust shell-style wildcard characters into regexp notation.
01016      *
01017      * We surround the pattern with "^(...)$" to force it to match the whole
01018      * string, as per SQL practice.  We have to have parens in case the string
01019      * contains "|", else the "^" and "$" will be bound into the first and
01020      * last alternatives which is not what we want.
01021      *
01022      * Note: the result of this pass is the actual regexp pattern(s) we want
01023      * to execute.  Quoting/escaping into SQL literal format will be done
01024      * below using appendStringLiteralConn().
01025      */
01026     appendPQExpBufferStr(&namebuf, "^(");
01027 
01028     inquotes = false;
01029     cp = pattern;
01030 
01031     while (*cp)
01032     {
01033         char        ch = *cp;
01034 
01035         if (ch == '"')
01036         {
01037             if (inquotes && cp[1] == '"')
01038             {
01039                 /* emit one quote, stay in inquotes mode */
01040                 appendPQExpBufferChar(&namebuf, '"');
01041                 cp++;
01042             }
01043             else
01044                 inquotes = !inquotes;
01045             cp++;
01046         }
01047         else if (!inquotes && isupper((unsigned char) ch))
01048         {
01049             appendPQExpBufferChar(&namebuf,
01050                                   pg_tolower((unsigned char) ch));
01051             cp++;
01052         }
01053         else if (!inquotes && ch == '*')
01054         {
01055             appendPQExpBufferStr(&namebuf, ".*");
01056             cp++;
01057         }
01058         else if (!inquotes && ch == '?')
01059         {
01060             appendPQExpBufferChar(&namebuf, '.');
01061             cp++;
01062         }
01063         else if (!inquotes && ch == '.')
01064         {
01065             /* Found schema/name separator, move current pattern to schema */
01066             resetPQExpBuffer(&schemabuf);
01067             appendPQExpBufferStr(&schemabuf, namebuf.data);
01068             resetPQExpBuffer(&namebuf);
01069             appendPQExpBufferStr(&namebuf, "^(");
01070             cp++;
01071         }
01072         else if (ch == '$')
01073         {
01074             /*
01075              * Dollar is always quoted, whether inside quotes or not. The
01076              * reason is that it's allowed in SQL identifiers, so there's a
01077              * significant use-case for treating it literally, while because
01078              * we anchor the pattern automatically there is no use-case for
01079              * having it possess its regexp meaning.
01080              */
01081             appendPQExpBufferStr(&namebuf, "\\$");
01082             cp++;
01083         }
01084         else
01085         {
01086             /*
01087              * Ordinary data character, transfer to pattern
01088              *
01089              * Inside double quotes, or at all times if force_escape is true,
01090              * quote regexp special characters with a backslash to avoid
01091              * regexp errors.  Outside quotes, however, let them pass through
01092              * as-is; this lets knowledgeable users build regexp expressions
01093              * that are more powerful than shell-style patterns.
01094              */
01095             if ((inquotes || force_escape) &&
01096                 strchr("|*+?()[]{}.^$\\", ch))
01097                 appendPQExpBufferChar(&namebuf, '\\');
01098             i = PQmblen(cp, encoding);
01099             while (i-- && *cp)
01100             {
01101                 appendPQExpBufferChar(&namebuf, *cp);
01102                 cp++;
01103             }
01104         }
01105     }
01106 
01107     /*
01108      * Now decide what we need to emit.  Note there will be a leading "^(" in
01109      * the patterns in any case.
01110      */
01111     if (namebuf.len > 2)
01112     {
01113         /* We have a name pattern, so constrain the namevar(s) */
01114 
01115         appendPQExpBufferStr(&namebuf, ")$");
01116         /* Optimize away a "*" pattern */
01117         if (strcmp(namebuf.data, "^(.*)$") != 0)
01118         {
01119             WHEREAND();
01120             if (altnamevar)
01121             {
01122                 appendPQExpBuffer(buf, "(%s ~ ", namevar);
01123                 appendStringLiteralConn(buf, namebuf.data, conn);
01124                 appendPQExpBuffer(buf, "\n        OR %s ~ ", altnamevar);
01125                 appendStringLiteralConn(buf, namebuf.data, conn);
01126                 appendPQExpBufferStr(buf, ")\n");
01127             }
01128             else
01129             {
01130                 appendPQExpBuffer(buf, "%s ~ ", namevar);
01131                 appendStringLiteralConn(buf, namebuf.data, conn);
01132                 appendPQExpBufferChar(buf, '\n');
01133             }
01134         }
01135     }
01136 
01137     if (schemabuf.len > 2)
01138     {
01139         /* We have a schema pattern, so constrain the schemavar */
01140 
01141         appendPQExpBufferStr(&schemabuf, ")$");
01142         /* Optimize away a "*" pattern */
01143         if (strcmp(schemabuf.data, "^(.*)$") != 0 && schemavar)
01144         {
01145             WHEREAND();
01146             appendPQExpBuffer(buf, "%s ~ ", schemavar);
01147             appendStringLiteralConn(buf, schemabuf.data, conn);
01148             appendPQExpBufferChar(buf, '\n');
01149         }
01150     }
01151     else
01152     {
01153         /* No schema pattern given, so select only visible objects */
01154         if (visibilityrule)
01155         {
01156             WHEREAND();
01157             appendPQExpBuffer(buf, "%s\n", visibilityrule);
01158         }
01159     }
01160 
01161     termPQExpBuffer(&schemabuf);
01162     termPQExpBuffer(&namebuf);
01163 
01164     return added_clause;
01165 #undef WHEREAND
01166 }
01167 
01168 /*
01169  * buildShSecLabelQuery
01170  *
01171  * Build a query to retrieve security labels for a shared object.
01172  */
01173 void
01174 buildShSecLabelQuery(PGconn *conn, const char *catalog_name, uint32 objectId,
01175                      PQExpBuffer sql)
01176 {
01177     appendPQExpBuffer(sql,
01178                       "SELECT provider, label FROM pg_catalog.pg_shseclabel "
01179                       "WHERE classoid = '%s'::pg_catalog.regclass AND "
01180                       "objoid = %u", catalog_name, objectId);
01181 }
01182 
01183 /*
01184  * emitShSecLabels
01185  *
01186  * Format security label data retrieved by the query generated in
01187  * buildShSecLabelQuery.
01188  */
01189 void
01190 emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer,
01191                 const char *target, const char *objname)
01192 {
01193     int         i;
01194 
01195     for (i = 0; i < PQntuples(res); i++)
01196     {
01197         char       *provider = PQgetvalue(res, i, 0);
01198         char       *label = PQgetvalue(res, i, 1);
01199 
01200         /* must use fmtId result before calling it again */
01201         appendPQExpBuffer(buffer,
01202                           "SECURITY LABEL FOR %s ON %s",
01203                           fmtId(provider), target);
01204         appendPQExpBuffer(buffer,
01205                           " %s IS ",
01206                           fmtId(objname));
01207         appendStringLiteralConn(buffer, label, conn);
01208         appendPQExpBuffer(buffer, ";\n");
01209     }
01210 }
01211 
01212 
01213 void
01214 simple_string_list_append(SimpleStringList *list, const char *val)
01215 {
01216     SimpleStringListCell *cell;
01217 
01218     /* this calculation correctly accounts for the null trailing byte */
01219     cell = (SimpleStringListCell *)
01220         pg_malloc(sizeof(SimpleStringListCell) + strlen(val));
01221 
01222     cell->next = NULL;
01223     strcpy(cell->val, val);
01224 
01225     if (list->tail)
01226         list->tail->next = cell;
01227     else
01228         list->head = cell;
01229     list->tail = cell;
01230 }
01231 
01232 bool
01233 simple_string_list_member(SimpleStringList *list, const char *val)
01234 {
01235     SimpleStringListCell *cell;
01236 
01237     for (cell = list->head; cell; cell = cell->next)
01238     {
01239         if (strcmp(cell->val, val) == 0)
01240             return true;
01241     }
01242     return false;
01243 }