1 /*-------------------------------------------------------------------------
3 * Utility routines for SQL dumping
4 * Basically this is stuff that is useful in both pg_dump and pg_dumpall.
5 * Lately it's also being used by psql and bin/scripts/ ...
8 * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
9 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/bin/pg_dump/dumputils.c
13 *-------------------------------------------------------------------------
15 #include "postgres_fe.h"
19 #include "dumputils.h"
21 #include "parser/keywords.h"
24 int quote_all_identifiers = 0;
27 #define supports_grant_options(version) ((version) >= 70400)
29 static bool parseAclItem(const char *item, const char *type,
30 const char *name, const char *subname, int remoteVersion,
31 PQExpBuffer grantee, PQExpBuffer grantor,
32 PQExpBuffer privs, PQExpBuffer privswgo);
33 static char *copyAclUserName(PQExpBuffer output, char *input);
34 static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
38 static bool parallel_init_done = false;
39 static DWORD tls_index;
43 init_parallel_dump_utils(void)
46 if (!parallel_init_done)
48 tls_index = TlsAlloc();
49 parallel_init_done = true;
55 * Quotes input string if it's not a legitimate SQL identifier as-is.
57 * Note that the returned string must be used before calling fmtId again,
58 * since we re-use the same return buffer each time. Non-reentrant but
59 * reduces memory leakage. (On Windows the memory leakage will be one buffer
60 * per thread, which is at least better than one per call).
63 fmtId(const char *rawid)
66 * The Tls code goes awry if we use a static var, so we provide for both
67 * static and auto, and omit any use of the static var when using Tls.
69 static PQExpBuffer s_id_return = NULL;
70 PQExpBuffer id_return;
73 bool need_quotes = false;
76 if (parallel_init_done)
77 id_return = (PQExpBuffer) TlsGetValue(tls_index); /* 0 when not set */
79 id_return = s_id_return;
81 id_return = s_id_return;
84 if (id_return) /* first time through? */
86 /* same buffer, just wipe contents */
87 resetPQExpBuffer(id_return);
92 id_return = createPQExpBuffer();
94 if (parallel_init_done)
95 TlsSetValue(tls_index, id_return);
97 s_id_return = id_return;
99 s_id_return = id_return;
105 * These checks need to match the identifier production in scan.l. Don't
108 if (quote_all_identifiers)
110 /* slightly different rules for first character */
111 else if (!((rawid[0] >= 'a' && rawid[0] <= 'z') || rawid[0] == '_'))
115 /* otherwise check the entire string */
116 for (cp = rawid; *cp; cp++)
118 if (!((*cp >= 'a' && *cp <= 'z')
119 || (*cp >= '0' && *cp <= '9')
131 * Check for keyword. We quote keywords except for unreserved ones.
132 * (In some cases we could avoid quoting a col_name or type_func_name
133 * keyword, but it seems much harder than it's worth to tell that.)
135 * Note: ScanKeywordLookup() does case-insensitive comparison, but
136 * that's fine, since we already know we have all-lower-case.
138 const ScanKeyword *keyword = ScanKeywordLookup(rawid,
142 if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD)
148 /* no quoting needed */
149 appendPQExpBufferStr(id_return, rawid);
153 appendPQExpBufferChar(id_return, '\"');
154 for (cp = rawid; *cp; cp++)
157 * Did we find a double-quote in the string? Then make this a
158 * double double-quote per SQL99. Before, we put in a
159 * backslash/double-quote pair. - thomas 2000-08-05
162 appendPQExpBufferChar(id_return, '\"');
163 appendPQExpBufferChar(id_return, *cp);
165 appendPQExpBufferChar(id_return, '\"');
168 return id_return->data;
173 * Convert a string value to an SQL string literal and append it to
174 * the given buffer. We assume the specified client_encoding and
175 * standard_conforming_strings settings.
177 * This is essentially equivalent to libpq's PQescapeStringInternal,
178 * except for the output buffer structure. We need it in situations
179 * where we do not have a PGconn available. Where we do,
180 * appendStringLiteralConn is a better choice.
183 appendStringLiteral(PQExpBuffer buf, const char *str,
184 int encoding, bool std_strings)
186 size_t length = strlen(str);
187 const char *source = str;
190 if (!enlargePQExpBuffer(buf, 2 * length + 2))
193 target = buf->data + buf->len;
196 while (*source != '\0')
202 /* Fast path for plain ASCII */
203 if (!IS_HIGHBIT_SET(c))
205 /* Apply quoting if needed */
206 if (SQL_STR_DOUBLE(c, !std_strings))
208 /* Copy the character */
214 /* Slow path for possible multibyte characters */
215 len = PQmblen(source, encoding);
217 /* Copy the character */
218 for (i = 0; i < len; i++)
222 *target++ = *source++;
226 * If we hit premature end of string (ie, incomplete multibyte
227 * character), try to pad out to the correct length with spaces. We
228 * may not be able to pad completely, but we will always be able to
229 * insert at least one pad space (since we'd not have quoted a
230 * multibyte character). This should be enough to make a string that
231 * the server will error out on.
235 char *stop = buf->data + buf->maxlen - 2;
247 /* Write the terminating quote and NUL character. */
251 buf->len = target - buf->data;
256 * Convert a string value to an SQL string literal and append it to
257 * the given buffer. Encoding and string syntax rules are as indicated
258 * by current settings of the PGconn.
261 appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
263 size_t length = strlen(str);
266 * XXX This is a kluge to silence escape_string_warning in our utility
267 * programs. It should go away someday.
269 if (strchr(str, '\\') != NULL && PQserverVersion(conn) >= 80100)
271 /* ensure we are not adjacent to an identifier */
272 if (buf->len > 0 && buf->data[buf->len - 1] != ' ')
273 appendPQExpBufferChar(buf, ' ');
274 appendPQExpBufferChar(buf, ESCAPE_STRING_SYNTAX);
275 appendStringLiteral(buf, str, PQclientEncoding(conn), false);
280 if (!enlargePQExpBuffer(buf, 2 * length + 2))
282 appendPQExpBufferChar(buf, '\'');
283 buf->len += PQescapeStringConn(conn, buf->data + buf->len,
285 appendPQExpBufferChar(buf, '\'');
290 * Convert a string value to a dollar quoted literal and append it to
291 * the given buffer. If the dqprefix parameter is not NULL then the
292 * dollar quote delimiter will begin with that (after the opening $).
294 * No escaping is done at all on str, in compliance with the rules
295 * for parsing dollar quoted strings. Also, we need not worry about
299 appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix)
301 static const char suffixes[] = "_XXXXXXX";
303 PQExpBuffer delimBuf = createPQExpBuffer();
305 /* start with $ + dqprefix if not NULL */
306 appendPQExpBufferChar(delimBuf, '$');
308 appendPQExpBufferStr(delimBuf, dqprefix);
311 * Make sure we choose a delimiter which (without the trailing $) is not
312 * present in the string being quoted. We don't check with the trailing $
313 * because a string ending in $foo must not be quoted with $foo$.
315 while (strstr(str, delimBuf->data) != NULL)
317 appendPQExpBufferChar(delimBuf, suffixes[nextchar++]);
318 nextchar %= sizeof(suffixes) - 1;
322 appendPQExpBufferChar(delimBuf, '$');
324 /* quote it and we are all done */
325 appendPQExpBufferStr(buf, delimBuf->data);
326 appendPQExpBufferStr(buf, str);
327 appendPQExpBufferStr(buf, delimBuf->data);
329 destroyPQExpBuffer(delimBuf);
334 * Convert a bytea value (presented as raw bytes) to an SQL string literal
335 * and append it to the given buffer. We assume the specified
336 * standard_conforming_strings setting.
338 * This is needed in situations where we do not have a PGconn available.
339 * Where we do, PQescapeByteaConn is a better choice.
342 appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length,
345 const unsigned char *source = str;
348 static const char hextbl[] = "0123456789abcdef";
351 * This implementation is hard-wired to produce hex-format output. We do
352 * not know the server version the output will be loaded into, so making
353 * an intelligent format choice is impossible. It might be better to
354 * always use the old escaped format.
356 if (!enlargePQExpBuffer(buf, 2 * length + 5))
359 target = buf->data + buf->len;
368 unsigned char c = *source++;
370 *target++ = hextbl[(c >> 4) & 0xF];
371 *target++ = hextbl[c & 0xF];
374 /* Write the terminating quote and NUL character. */
378 buf->len = target - buf->data;
383 * Convert backend's version string into a number.
386 parse_version(const char *versionString)
393 cnt = sscanf(versionString, "%d.%d.%d", &vmaj, &vmin, &vrev);
401 return (100 * vmaj + vmin) * 100 + vrev;
406 * Deconstruct the text representation of a 1-dimensional Postgres array
407 * into individual items.
409 * On success, returns true and sets *itemarray and *nitems to describe
410 * an array of individual strings. On parse failure, returns false;
411 * *itemarray may exist or be NULL.
413 * NOTE: free'ing itemarray is sufficient to deallocate the working storage.
416 parsePGArray(const char *atext, char ***itemarray, int *nitems)
424 * We expect input in the form of "{item,item,item}" where any item is
425 * either raw data, or surrounded by double quotes (in which case embedded
426 * characters including backslashes and quotes are backslashed).
428 * We build the result as an array of pointers followed by the actual
429 * string data, all in one malloc block for convenience of deallocation.
430 * The worst-case storage need is not more than one pointer and one
431 * character for each input character (consider "{,,,,,,,,,,}").
435 inputlen = strlen(atext);
436 if (inputlen < 2 || atext[0] != '{' || atext[inputlen - 1] != '}')
437 return false; /* bad input */
438 items = (char **) malloc(inputlen * (sizeof(char *) + sizeof(char)));
440 return false; /* out of memory */
442 strings = (char *) (items + inputlen);
444 atext++; /* advance over initial '{' */
446 while (*atext != '}')
449 return false; /* premature end of string */
450 items[curitem] = strings;
451 while (*atext != '}' && *atext != ',')
454 return false; /* premature end of string */
456 *strings++ = *atext++; /* copy unquoted data */
459 /* process quoted substring */
461 while (*atext != '"')
464 return false; /* premature end of string */
469 return false; /* premature end of string */
471 *strings++ = *atext++; /* copy quoted data */
481 if (atext[1] != '\0')
482 return false; /* bogus syntax (embedded '}') */
489 * Build GRANT/REVOKE command(s) for an object.
491 * name: the object name, in the form to use in the commands (already quoted)
492 * subname: the sub-object name, if any (already quoted); NULL if none
493 * type: the object type (as seen in GRANT command: must be one of
494 * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
495 * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT)
496 * acls: the ACL string fetched from the database
497 * owner: username of object owner (will be passed through fmtId); can be
498 * NULL or empty string to indicate "no owner known"
499 * prefix: string to prefix to each generated command; typically empty
500 * remoteVersion: version of database
502 * Returns TRUE if okay, FALSE if could not parse the acl string.
503 * The resulting commands (if any) are appended to the contents of 'sql'.
505 * Note: when processing a default ACL, prefix is "ALTER DEFAULT PRIVILEGES "
506 * or something similar, and name is an empty string.
508 * Note: beware of passing a fmtId() result directly as 'name' or 'subname',
509 * since this routine uses fmtId() internally.
512 buildACLCommands(const char *name, const char *subname,
513 const char *type, const char *acls, const char *owner,
514 const char *prefix, int remoteVersion,
524 PQExpBuffer firstsql,
526 bool found_owner_privs = false;
528 if (strlen(acls) == 0)
529 return true; /* object has default permissions */
531 /* treat empty-string owner same as NULL */
532 if (owner && *owner == '\0')
535 if (!parsePGArray(acls, &aclitems, &naclitems))
542 grantee = createPQExpBuffer();
543 grantor = createPQExpBuffer();
544 privs = createPQExpBuffer();
545 privswgo = createPQExpBuffer();
548 * At the end, these two will be pasted together to form the result. But
549 * the owner privileges need to go before the other ones to keep the
550 * dependencies valid. In recent versions this is normally the case, but
551 * in old versions they come after the PUBLIC privileges and that results
552 * in problems if we need to run REVOKE on the owner privileges.
554 firstsql = createPQExpBuffer();
555 secondsql = createPQExpBuffer();
558 * Always start with REVOKE ALL FROM PUBLIC, so that we don't have to
559 * wire-in knowledge about the default public privileges for different
562 appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
564 appendPQExpBuffer(firstsql, "(%s)", subname);
565 appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name);
568 * We still need some hacking though to cover the case where new default
569 * public privileges are added in new versions: the REVOKE ALL will revoke
570 * them, leading to behavior different from what the old version had,
571 * which is generally not what's wanted. So add back default privs if the
572 * source database is too old to have had that particular priv.
574 if (remoteVersion < 80200 && strcmp(type, "DATABASE") == 0)
576 /* database CONNECT priv didn't exist before 8.2 */
577 appendPQExpBuffer(firstsql, "%sGRANT CONNECT ON %s %s TO PUBLIC;\n",
581 /* Scan individual ACL items */
582 for (i = 0; i < naclitems; i++)
584 if (!parseAclItem(aclitems[i], type, name, subname, remoteVersion,
585 grantee, grantor, privs, privswgo))
588 if (grantor->len == 0 && owner)
589 printfPQExpBuffer(grantor, "%s", owner);
591 if (privs->len > 0 || privswgo->len > 0)
594 && strcmp(grantee->data, owner) == 0
595 && strcmp(grantor->data, owner) == 0)
597 found_owner_privs = true;
600 * For the owner, the default privilege level is ALL WITH
601 * GRANT OPTION (only ALL prior to 7.4).
603 if (supports_grant_options(remoteVersion)
604 ? strcmp(privswgo->data, "ALL") != 0
605 : strcmp(privs->data, "ALL") != 0)
607 appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
609 appendPQExpBuffer(firstsql, "(%s)", subname);
610 appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
611 type, name, fmtId(grantee->data));
613 appendPQExpBuffer(firstsql,
614 "%sGRANT %s ON %s %s TO %s;\n",
615 prefix, privs->data, type, name,
616 fmtId(grantee->data));
617 if (privswgo->len > 0)
618 appendPQExpBuffer(firstsql,
619 "%sGRANT %s ON %s %s TO %s WITH GRANT OPTION;\n",
620 prefix, privswgo->data, type, name,
621 fmtId(grantee->data));
627 * Otherwise can assume we are starting from no privs.
630 && (!owner || strcmp(owner, grantor->data) != 0))
631 appendPQExpBuffer(secondsql, "SET SESSION AUTHORIZATION %s;\n",
632 fmtId(grantor->data));
636 appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
637 prefix, privs->data, type, name);
638 if (grantee->len == 0)
639 appendPQExpBuffer(secondsql, "PUBLIC;\n");
640 else if (strncmp(grantee->data, "group ",
641 strlen("group ")) == 0)
642 appendPQExpBuffer(secondsql, "GROUP %s;\n",
643 fmtId(grantee->data + strlen("group ")));
645 appendPQExpBuffer(secondsql, "%s;\n", fmtId(grantee->data));
647 if (privswgo->len > 0)
649 appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
650 prefix, privswgo->data, type, name);
651 if (grantee->len == 0)
652 appendPQExpBuffer(secondsql, "PUBLIC");
653 else if (strncmp(grantee->data, "group ",
654 strlen("group ")) == 0)
655 appendPQExpBuffer(secondsql, "GROUP %s",
656 fmtId(grantee->data + strlen("group ")));
658 appendPQExpBuffer(secondsql, "%s", fmtId(grantee->data));
659 appendPQExpBuffer(secondsql, " WITH GRANT OPTION;\n");
663 && (!owner || strcmp(owner, grantor->data) != 0))
664 appendPQExpBuffer(secondsql, "RESET SESSION AUTHORIZATION;\n");
670 * If we didn't find any owner privs, the owner must have revoked 'em all
672 if (!found_owner_privs && owner)
674 appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
676 appendPQExpBuffer(firstsql, "(%s)", subname);
677 appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
678 type, name, fmtId(owner));
681 destroyPQExpBuffer(grantee);
682 destroyPQExpBuffer(grantor);
683 destroyPQExpBuffer(privs);
684 destroyPQExpBuffer(privswgo);
686 appendPQExpBuffer(sql, "%s%s", firstsql->data, secondsql->data);
687 destroyPQExpBuffer(firstsql);
688 destroyPQExpBuffer(secondsql);
696 * Build ALTER DEFAULT PRIVILEGES command(s) for single pg_default_acl entry.
698 * type: the object type (TABLES, FUNCTIONS, etc)
699 * nspname: schema name, or NULL for global default privileges
700 * acls: the ACL string fetched from the database
701 * owner: username of privileges owner (will be passed through fmtId)
702 * remoteVersion: version of database
704 * Returns TRUE if okay, FALSE if could not parse the acl string.
705 * The resulting commands (if any) are appended to the contents of 'sql'.
708 buildDefaultACLCommands(const char *type, const char *nspname,
709 const char *acls, const char *owner,
716 prefix = createPQExpBuffer();
719 * We incorporate the target role directly into the command, rather than
720 * playing around with SET ROLE or anything like that. This is so that a
721 * permissions error leads to nothing happening, rather than changing
722 * default privileges for the wrong user.
724 appendPQExpBuffer(prefix, "ALTER DEFAULT PRIVILEGES FOR ROLE %s ",
727 appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname));
729 result = buildACLCommands("", NULL,
731 prefix->data, remoteVersion,
734 destroyPQExpBuffer(prefix);
740 * This will parse an aclitem string, having the general form
741 * username=privilegecodes/grantor
743 * group groupname=privilegecodes/grantor
744 * (the /grantor part will not be present if pre-7.4 database).
746 * The returned grantee string will be the dequoted username or groupname
747 * (preceded with "group " in the latter case). The returned grantor is
748 * the dequoted grantor name or empty. Privilege characters are decoded
749 * and split between privileges with grant option (privswgo) and without
752 * Note: for cross-version compatibility, it's important to use ALL when
756 parseAclItem(const char *item, const char *type,
757 const char *name, const char *subname, int remoteVersion,
758 PQExpBuffer grantee, PQExpBuffer grantor,
759 PQExpBuffer privs, PQExpBuffer privswgo)
762 bool all_with_go = true;
763 bool all_without_go = true;
772 /* user or group name is string up to = */
773 eqpos = copyAclUserName(grantee, buf);
777 /* grantor may be listed after / */
778 slpos = strchr(eqpos + 1, '/');
782 slpos = copyAclUserName(grantor, slpos);
787 resetPQExpBuffer(grantor);
789 /* privilege codes */
790 #define CONVERT_PRIV(code, keywd) \
792 if ((pos = strchr(eqpos + 1, code))) \
794 if (*(pos + 1) == '*') \
796 AddAcl(privswgo, keywd, subname); \
797 all_without_go = false; \
801 AddAcl(privs, keywd, subname); \
802 all_with_go = false; \
806 all_with_go = all_without_go = false; \
809 resetPQExpBuffer(privs);
810 resetPQExpBuffer(privswgo);
812 if (strcmp(type, "TABLE") == 0 || strcmp(type, "SEQUENCE") == 0 ||
813 strcmp(type, "TABLES") == 0 || strcmp(type, "SEQUENCES") == 0)
815 CONVERT_PRIV('r', "SELECT");
817 if (strcmp(type, "SEQUENCE") == 0 ||
818 strcmp(type, "SEQUENCES") == 0)
820 CONVERT_PRIV('U', "USAGE");
824 CONVERT_PRIV('a', "INSERT");
825 if (remoteVersion >= 70200)
826 CONVERT_PRIV('x', "REFERENCES");
827 /* rest are not applicable to columns */
830 if (remoteVersion >= 70200)
832 CONVERT_PRIV('d', "DELETE");
833 CONVERT_PRIV('t', "TRIGGER");
835 if (remoteVersion >= 80400)
836 CONVERT_PRIV('D', "TRUNCATE");
841 if (remoteVersion >= 70200 ||
842 strcmp(type, "SEQUENCE") == 0 ||
843 strcmp(type, "SEQUENCES") == 0)
844 CONVERT_PRIV('w', "UPDATE");
846 /* 7.0 and 7.1 have a simpler worldview */
847 CONVERT_PRIV('w', "UPDATE,DELETE");
849 else if (strcmp(type, "FUNCTION") == 0 ||
850 strcmp(type, "FUNCTIONS") == 0)
851 CONVERT_PRIV('X', "EXECUTE");
852 else if (strcmp(type, "LANGUAGE") == 0)
853 CONVERT_PRIV('U', "USAGE");
854 else if (strcmp(type, "SCHEMA") == 0)
856 CONVERT_PRIV('C', "CREATE");
857 CONVERT_PRIV('U', "USAGE");
859 else if (strcmp(type, "DATABASE") == 0)
861 CONVERT_PRIV('C', "CREATE");
862 CONVERT_PRIV('c', "CONNECT");
863 CONVERT_PRIV('T', "TEMPORARY");
865 else if (strcmp(type, "TABLESPACE") == 0)
866 CONVERT_PRIV('C', "CREATE");
867 else if (strcmp(type, "FOREIGN DATA WRAPPER") == 0)
868 CONVERT_PRIV('U', "USAGE");
869 else if (strcmp(type, "FOREIGN SERVER") == 0)
870 CONVERT_PRIV('U', "USAGE");
871 else if (strcmp(type, "FOREIGN TABLE") == 0)
872 CONVERT_PRIV('r', "SELECT");
873 else if (strcmp(type, "LARGE OBJECT") == 0)
875 CONVERT_PRIV('r', "SELECT");
876 CONVERT_PRIV('w', "UPDATE");
885 resetPQExpBuffer(privs);
886 printfPQExpBuffer(privswgo, "ALL");
888 appendPQExpBuffer(privswgo, "(%s)", subname);
890 else if (all_without_go)
892 resetPQExpBuffer(privswgo);
893 printfPQExpBuffer(privs, "ALL");
895 appendPQExpBuffer(privs, "(%s)", subname);
904 * Transfer a user or group name starting at *input into the output buffer,
905 * dequoting if needed. Returns a pointer to just past the input name.
906 * The name is taken to end at an unquoted '=' or end of string.
909 copyAclUserName(PQExpBuffer output, char *input)
911 resetPQExpBuffer(output);
913 while (*input && *input != '=')
916 * If user name isn't quoted, then just add it to the output buffer
919 appendPQExpBufferChar(output, *input++);
922 /* Otherwise, it's a quoted username */
924 /* Loop until we come across an unescaped quote */
925 while (!(*input == '"' && *(input + 1) != '"'))
928 return input; /* really a syntax error... */
931 * Quoting convention is to escape " as "". Keep this code in
932 * sync with putid() in backend's acl.c.
934 if (*input == '"' && *(input + 1) == '"')
936 appendPQExpBufferChar(output, *input++);
945 * Append a privilege keyword to a keyword list, inserting comma if needed.
948 AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname)
951 appendPQExpBufferChar(aclbuf, ',');
952 appendPQExpBuffer(aclbuf, "%s", keyword);
954 appendPQExpBuffer(aclbuf, "(%s)", subname);
959 * processSQLNamePattern
961 * Scan a wildcard-pattern string and generate appropriate WHERE clauses
962 * to limit the set of objects returned. The WHERE clauses are appended
963 * to the already-partially-constructed query in buf. Returns whether
964 * any clause was added.
966 * conn: connection query will be sent to (consulted for escaping rules).
967 * buf: output parameter.
968 * pattern: user-specified pattern option, or NULL if none ("*" is implied).
969 * have_where: true if caller already emitted "WHERE" (clauses will be ANDed
970 * onto the existing WHERE clause).
971 * force_escape: always quote regexp special characters, even outside
972 * double quotes (else they are quoted only between double quotes).
973 * schemavar: name of query variable to match against a schema-name pattern.
974 * Can be NULL if no schema.
975 * namevar: name of query variable to match against an object-name pattern.
976 * altnamevar: NULL, or name of an alternative variable to match against name.
977 * visibilityrule: clause to use if we want to restrict to visible objects
978 * (for example, "pg_catalog.pg_table_is_visible(p.oid)"). Can be NULL.
980 * Formatting note: the text already present in buf should end with a newline.
981 * The appended text, if any, will end with one too.
984 processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
985 bool have_where, bool force_escape,
986 const char *schemavar, const char *namevar,
987 const char *altnamevar, const char *visibilityrule)
989 PQExpBufferData schemabuf;
990 PQExpBufferData namebuf;
991 int encoding = PQclientEncoding(conn);
995 bool added_clause = false;
998 (appendPQExpBufferStr(buf, have_where ? " AND " : "WHERE "), \
999 have_where = true, added_clause = true)
1001 if (pattern == NULL)
1003 /* Default: select all visible objects */
1007 appendPQExpBuffer(buf, "%s\n", visibilityrule);
1009 return added_clause;
1012 initPQExpBuffer(&schemabuf);
1013 initPQExpBuffer(&namebuf);
1016 * Parse the pattern, converting quotes and lower-casing unquoted letters.
1017 * Also, adjust shell-style wildcard characters into regexp notation.
1019 * We surround the pattern with "^(...)$" to force it to match the whole
1020 * string, as per SQL practice. We have to have parens in case the string
1021 * contains "|", else the "^" and "$" will be bound into the first and
1022 * last alternatives which is not what we want.
1024 * Note: the result of this pass is the actual regexp pattern(s) we want
1025 * to execute. Quoting/escaping into SQL literal format will be done
1026 * below using appendStringLiteralConn().
1028 appendPQExpBufferStr(&namebuf, "^(");
1039 if (inquotes && cp[1] == '"')
1041 /* emit one quote, stay in inquotes mode */
1042 appendPQExpBufferChar(&namebuf, '"');
1046 inquotes = !inquotes;
1049 else if (!inquotes && isupper((unsigned char) ch))
1051 appendPQExpBufferChar(&namebuf,
1052 pg_tolower((unsigned char) ch));
1055 else if (!inquotes && ch == '*')
1057 appendPQExpBufferStr(&namebuf, ".*");
1060 else if (!inquotes && ch == '?')
1062 appendPQExpBufferChar(&namebuf, '.');
1065 else if (!inquotes && ch == '.')
1067 /* Found schema/name separator, move current pattern to schema */
1068 resetPQExpBuffer(&schemabuf);
1069 appendPQExpBufferStr(&schemabuf, namebuf.data);
1070 resetPQExpBuffer(&namebuf);
1071 appendPQExpBufferStr(&namebuf, "^(");
1077 * Dollar is always quoted, whether inside quotes or not. The
1078 * reason is that it's allowed in SQL identifiers, so there's a
1079 * significant use-case for treating it literally, while because
1080 * we anchor the pattern automatically there is no use-case for
1081 * having it possess its regexp meaning.
1083 appendPQExpBufferStr(&namebuf, "\\$");
1089 * Ordinary data character, transfer to pattern
1091 * Inside double quotes, or at all times if force_escape is true,
1092 * quote regexp special characters with a backslash to avoid
1093 * regexp errors. Outside quotes, however, let them pass through
1094 * as-is; this lets knowledgeable users build regexp expressions
1095 * that are more powerful than shell-style patterns.
1097 if ((inquotes || force_escape) &&
1098 strchr("|*+?()[]{}.^$\\", ch))
1099 appendPQExpBufferChar(&namebuf, '\\');
1100 i = PQmblen(cp, encoding);
1103 appendPQExpBufferChar(&namebuf, *cp);
1110 * Now decide what we need to emit. Note there will be a leading "^(" in
1111 * the patterns in any case.
1113 if (namebuf.len > 2)
1115 /* We have a name pattern, so constrain the namevar(s) */
1117 appendPQExpBufferStr(&namebuf, ")$");
1118 /* Optimize away a "*" pattern */
1119 if (strcmp(namebuf.data, "^(.*)$") != 0)
1124 appendPQExpBuffer(buf, "(%s ~ ", namevar);
1125 appendStringLiteralConn(buf, namebuf.data, conn);
1126 appendPQExpBuffer(buf, "\n OR %s ~ ", altnamevar);
1127 appendStringLiteralConn(buf, namebuf.data, conn);
1128 appendPQExpBufferStr(buf, ")\n");
1132 appendPQExpBuffer(buf, "%s ~ ", namevar);
1133 appendStringLiteralConn(buf, namebuf.data, conn);
1134 appendPQExpBufferChar(buf, '\n');
1139 if (schemabuf.len > 2)
1141 /* We have a schema pattern, so constrain the schemavar */
1143 appendPQExpBufferStr(&schemabuf, ")$");
1144 /* Optimize away a "*" pattern */
1145 if (strcmp(schemabuf.data, "^(.*)$") != 0 && schemavar)
1148 appendPQExpBuffer(buf, "%s ~ ", schemavar);
1149 appendStringLiteralConn(buf, schemabuf.data, conn);
1150 appendPQExpBufferChar(buf, '\n');
1155 /* No schema pattern given, so select only visible objects */
1159 appendPQExpBuffer(buf, "%s\n", visibilityrule);
1163 termPQExpBuffer(&schemabuf);
1164 termPQExpBuffer(&namebuf);
1166 return added_clause;
1171 * buildShSecLabelQuery
1173 * Build a query to retrieve security labels for a shared object.
1176 buildShSecLabelQuery(PGconn *conn, const char *catalog_name, uint32 objectId,
1179 appendPQExpBuffer(sql,
1180 "SELECT provider, label FROM pg_catalog.pg_shseclabel "
1181 "WHERE classoid = '%s'::pg_catalog.regclass AND "
1182 "objoid = %u", catalog_name, objectId);
1188 * Format security label data retrieved by the query generated in
1189 * buildShSecLabelQuery.
1192 emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer,
1193 const char *target, const char *objname)
1197 for (i = 0; i < PQntuples(res); i++)
1199 char *provider = PQgetvalue(res, i, 0);
1200 char *label = PQgetvalue(res, i, 1);
1202 /* must use fmtId result before calling it again */
1203 appendPQExpBuffer(buffer,
1204 "SECURITY LABEL FOR %s ON %s",
1205 fmtId(provider), target);
1206 appendPQExpBuffer(buffer,
1209 appendStringLiteralConn(buffer, label, conn);
1210 appendPQExpBuffer(buffer, ";\n");