diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2016-03-24 15:55:44 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2016-03-24 15:55:57 -0400 |
commit | 588d963b00e5e4385b6425418e3faa726f63f72e (patch) | |
tree | ea104b491189a036fd36898359b960bb914f6a08 | |
parent | a596db332b8c7f593a82af86f69353ba08f6214c (diff) | |
download | postgresql-588d963b00e5e4385b6425418e3faa726f63f72e.tar.gz postgresql-588d963b00e5e4385b6425418e3faa726f63f72e.zip |
Create src/fe_utils/, and move stuff into there from pg_dump's dumputils.
Per discussion, we want to create a static library and put the stuff into
it that until now has been shared across src/bin/ directories by ad-hoc
methods like symlinking a source file. This commit creates the library and
populates it with a couple of files that contain the widely-useful portions
of pg_dump's dumputils.c file. dumputils.c survives, because it has some
stuff that didn't seem appropriate for fe_utils, but it's significantly
smaller and is no longer referenced from any other directory.
Follow-on patches will move more stuff into fe_utils.
The Mkvcbuild.pm hacking here is just a best guess; we'll see how the
buildfarm likes it.
35 files changed, 1004 insertions, 848 deletions
diff --git a/src/Makefile b/src/Makefile index e859826dc4f..b526be79859 100644 --- a/src/Makefile +++ b/src/Makefile @@ -22,6 +22,7 @@ SUBDIRS = \ include \ interfaces \ backend/replication/libpqwalreceiver \ + fe_utils \ bin \ pl \ makefiles \ diff --git a/src/Makefile.global.in b/src/Makefile.global.in index 47b265ee498..811d05f4608 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -501,7 +501,12 @@ submake-libpgport: $(MAKE) -C $(top_builddir)/src/port all $(MAKE) -C $(top_builddir)/src/common all -.PHONY: submake-libpq submake-libpgport +submake-libpgfeutils: + $(MAKE) -C $(top_builddir)/src/port all + $(MAKE) -C $(top_builddir)/src/common all + $(MAKE) -C $(top_builddir)/src/fe_utils all + +.PHONY: submake-libpq submake-libpgport submake-libpgfeutils ########################################################################## diff --git a/src/bin/pg_dump/Makefile b/src/bin/pg_dump/Makefile index ddf940210ab..ea515fd9de4 100644 --- a/src/bin/pg_dump/Makefile +++ b/src/bin/pg_dump/Makefile @@ -17,6 +17,7 @@ top_builddir = ../../.. include $(top_builddir)/src/Makefile.global override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) +LDFLAGS += -L$(top_builddir)/src/fe_utils -lpgfeutils OBJS= pg_backup_archiver.o pg_backup_db.o pg_backup_custom.o \ pg_backup_null.o pg_backup_tar.o pg_backup_directory.o \ @@ -24,13 +25,13 @@ OBJS= pg_backup_archiver.o pg_backup_db.o pg_backup_custom.o \ all: pg_dump pg_restore pg_dumpall -pg_dump: pg_dump.o common.o pg_dump_sort.o $(OBJS) | submake-libpq submake-libpgport +pg_dump: pg_dump.o common.o pg_dump_sort.o $(OBJS) | submake-libpq submake-libpgport submake-libpgfeutils $(CC) $(CFLAGS) pg_dump.o common.o pg_dump_sort.o $(OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) -pg_restore: pg_restore.o $(OBJS) | submake-libpq submake-libpgport +pg_restore: pg_restore.o $(OBJS) | submake-libpq submake-libpgport submake-libpgfeutils $(CC) $(CFLAGS) pg_restore.o $(OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) -pg_dumpall: pg_dumpall.o dumputils.o | submake-libpq submake-libpgport +pg_dumpall: pg_dumpall.o dumputils.o | submake-libpq submake-libpgport submake-libpgfeutils $(CC) $(CFLAGS) pg_dumpall.o dumputils.o $(WIN32RES) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) install: all installdirs diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 1acd91ab444..373d3bc54b1 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -22,6 +22,7 @@ #include <ctype.h> #include "catalog/pg_class.h" +#include "fe_utils/string_utils.h" /* @@ -992,37 +993,3 @@ strInArray(const char *pattern, char **arr, int arr_size) } return -1; } - - -/* - * Support for simple list operations - */ - -void -simple_oid_list_append(SimpleOidList *list, Oid val) -{ - SimpleOidListCell *cell; - - cell = (SimpleOidListCell *) pg_malloc(sizeof(SimpleOidListCell)); - cell->next = NULL; - cell->val = val; - - if (list->tail) - list->tail->next = cell; - else - list->head = cell; - list->tail = cell; -} - -bool -simple_oid_list_member(SimpleOidList *list, Oid val) -{ - SimpleOidListCell *cell; - - for (cell = list->head; cell; cell = cell->next) - { - if (cell->val == val) - return true; - } - return false; -} diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c index a685d28d600..5301d3fa541 100644 --- a/src/bin/pg_dump/dumputils.c +++ b/src/bin/pg_dump/dumputils.c @@ -1,8 +1,8 @@ /*------------------------------------------------------------------------- * * Utility routines for SQL dumping - * Basically this is stuff that is useful in both pg_dump and pg_dumpall. - * Lately it's also being used by psql and bin/scripts/ ... + * + * Basically this is stuff that is useful in both pg_dump and pg_dumpall. * * * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group @@ -14,11 +14,8 @@ */ #include "postgres_fe.h" -#include <ctype.h> - #include "dumputils.h" - -#include "common/keywords.h" +#include "fe_utils/string_utils.h" #define supports_grant_options(version) ((version) >= 70400) @@ -30,441 +27,6 @@ static bool parseAclItem(const char *item, const char *type, static char *copyAclUserName(PQExpBuffer output, char *input); static void AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname); -static PQExpBuffer defaultGetLocalPQExpBuffer(void); - -/* Globals exported by this file */ -int quote_all_identifiers = 0; -PQExpBuffer (*getLocalPQExpBuffer) (void) = defaultGetLocalPQExpBuffer; - -/* - * Returns a temporary PQExpBuffer, valid until the next call to the function. - * This is used by fmtId and fmtQualifiedId. - * - * Non-reentrant and non-thread-safe but reduces memory leakage. You can - * replace this with a custom version by setting the getLocalPQExpBuffer - * function pointer. - */ -static PQExpBuffer -defaultGetLocalPQExpBuffer(void) -{ - static PQExpBuffer id_return = NULL; - - if (id_return) /* first time through? */ - { - /* same buffer, just wipe contents */ - resetPQExpBuffer(id_return); - } - else - { - /* new buffer */ - id_return = createPQExpBuffer(); - } - - return id_return; -} - -/* - * Quotes input string if it's not a legitimate SQL identifier as-is. - * - * Note that the returned string must be used before calling fmtId again, - * since we re-use the same return buffer each time. - */ -const char * -fmtId(const char *rawid) -{ - PQExpBuffer id_return = getLocalPQExpBuffer(); - - const char *cp; - bool need_quotes = false; - - /* - * These checks need to match the identifier production in scan.l. Don't - * use islower() etc. - */ - if (quote_all_identifiers) - need_quotes = true; - /* slightly different rules for first character */ - else if (!((rawid[0] >= 'a' && rawid[0] <= 'z') || rawid[0] == '_')) - need_quotes = true; - else - { - /* otherwise check the entire string */ - for (cp = rawid; *cp; cp++) - { - if (!((*cp >= 'a' && *cp <= 'z') - || (*cp >= '0' && *cp <= '9') - || (*cp == '_'))) - { - need_quotes = true; - break; - } - } - } - - if (!need_quotes) - { - /* - * Check for keyword. We quote keywords except for unreserved ones. - * (In some cases we could avoid quoting a col_name or type_func_name - * keyword, but it seems much harder than it's worth to tell that.) - * - * Note: ScanKeywordLookup() does case-insensitive comparison, but - * that's fine, since we already know we have all-lower-case. - */ - const ScanKeyword *keyword = ScanKeywordLookup(rawid, - ScanKeywords, - NumScanKeywords); - - if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD) - need_quotes = true; - } - - if (!need_quotes) - { - /* no quoting needed */ - appendPQExpBufferStr(id_return, rawid); - } - else - { - appendPQExpBufferChar(id_return, '"'); - for (cp = rawid; *cp; cp++) - { - /* - * Did we find a double-quote in the string? Then make this a - * double double-quote per SQL99. Before, we put in a - * backslash/double-quote pair. - thomas 2000-08-05 - */ - if (*cp == '"') - appendPQExpBufferChar(id_return, '"'); - appendPQExpBufferChar(id_return, *cp); - } - appendPQExpBufferChar(id_return, '"'); - } - - return id_return->data; -} - -/* - * fmtQualifiedId - convert a qualified name to the proper format for - * the source database. - * - * Like fmtId, use the result before calling again. - * - * Since we call fmtId and it also uses getThreadLocalPQExpBuffer() we cannot - * use it until we're finished with calling fmtId(). - */ -const char * -fmtQualifiedId(int remoteVersion, const char *schema, const char *id) -{ - PQExpBuffer id_return; - PQExpBuffer lcl_pqexp = createPQExpBuffer(); - - /* Suppress schema name if fetching from pre-7.3 DB */ - if (remoteVersion >= 70300 && schema && *schema) - { - appendPQExpBuffer(lcl_pqexp, "%s.", fmtId(schema)); - } - appendPQExpBufferStr(lcl_pqexp, fmtId(id)); - - id_return = getLocalPQExpBuffer(); - - appendPQExpBufferStr(id_return, lcl_pqexp->data); - destroyPQExpBuffer(lcl_pqexp); - - return id_return->data; -} - -/* - * Convert a string value to an SQL string literal and append it to - * the given buffer. We assume the specified client_encoding and - * standard_conforming_strings settings. - * - * This is essentially equivalent to libpq's PQescapeStringInternal, - * except for the output buffer structure. We need it in situations - * where we do not have a PGconn available. Where we do, - * appendStringLiteralConn is a better choice. - */ -void -appendStringLiteral(PQExpBuffer buf, const char *str, - int encoding, bool std_strings) -{ - size_t length = strlen(str); - const char *source = str; - char *target; - - if (!enlargePQExpBuffer(buf, 2 * length + 2)) - return; - - target = buf->data + buf->len; - *target++ = '\''; - - while (*source != '\0') - { - char c = *source; - int len; - int i; - - /* Fast path for plain ASCII */ - if (!IS_HIGHBIT_SET(c)) - { - /* Apply quoting if needed */ - if (SQL_STR_DOUBLE(c, !std_strings)) - *target++ = c; - /* Copy the character */ - *target++ = c; - source++; - continue; - } - - /* Slow path for possible multibyte characters */ - len = PQmblen(source, encoding); - - /* Copy the character */ - for (i = 0; i < len; i++) - { - if (*source == '\0') - break; - *target++ = *source++; - } - - /* - * If we hit premature end of string (ie, incomplete multibyte - * character), try to pad out to the correct length with spaces. We - * may not be able to pad completely, but we will always be able to - * insert at least one pad space (since we'd not have quoted a - * multibyte character). This should be enough to make a string that - * the server will error out on. - */ - if (i < len) - { - char *stop = buf->data + buf->maxlen - 2; - - for (; i < len; i++) - { - if (target >= stop) - break; - *target++ = ' '; - } - break; - } - } - - /* Write the terminating quote and NUL character. */ - *target++ = '\''; - *target = '\0'; - - buf->len = target - buf->data; -} - - -/* - * Convert a string value to an SQL string literal and append it to - * the given buffer. Encoding and string syntax rules are as indicated - * by current settings of the PGconn. - */ -void -appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn) -{ - size_t length = strlen(str); - - /* - * XXX This is a kluge to silence escape_string_warning in our utility - * programs. It should go away someday. - */ - if (strchr(str, '\\') != NULL && PQserverVersion(conn) >= 80100) - { - /* ensure we are not adjacent to an identifier */ - if (buf->len > 0 && buf->data[buf->len - 1] != ' ') - appendPQExpBufferChar(buf, ' '); - appendPQExpBufferChar(buf, ESCAPE_STRING_SYNTAX); - appendStringLiteral(buf, str, PQclientEncoding(conn), false); - return; - } - /* XXX end kluge */ - - if (!enlargePQExpBuffer(buf, 2 * length + 2)) - return; - appendPQExpBufferChar(buf, '\''); - buf->len += PQescapeStringConn(conn, buf->data + buf->len, - str, length, NULL); - appendPQExpBufferChar(buf, '\''); -} - - -/* - * Convert a string value to a dollar quoted literal and append it to - * the given buffer. If the dqprefix parameter is not NULL then the - * dollar quote delimiter will begin with that (after the opening $). - * - * No escaping is done at all on str, in compliance with the rules - * for parsing dollar quoted strings. Also, we need not worry about - * encoding issues. - */ -void -appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix) -{ - static const char suffixes[] = "_XXXXXXX"; - int nextchar = 0; - PQExpBuffer delimBuf = createPQExpBuffer(); - - /* start with $ + dqprefix if not NULL */ - appendPQExpBufferChar(delimBuf, '$'); - if (dqprefix) - appendPQExpBufferStr(delimBuf, dqprefix); - - /* - * Make sure we choose a delimiter which (without the trailing $) is not - * present in the string being quoted. We don't check with the trailing $ - * because a string ending in $foo must not be quoted with $foo$. - */ - while (strstr(str, delimBuf->data) != NULL) - { - appendPQExpBufferChar(delimBuf, suffixes[nextchar++]); - nextchar %= sizeof(suffixes) - 1; - } - - /* add trailing $ */ - appendPQExpBufferChar(delimBuf, '$'); - - /* quote it and we are all done */ - appendPQExpBufferStr(buf, delimBuf->data); - appendPQExpBufferStr(buf, str); - appendPQExpBufferStr(buf, delimBuf->data); - - destroyPQExpBuffer(delimBuf); -} - - -/* - * Convert a bytea value (presented as raw bytes) to an SQL string literal - * and append it to the given buffer. We assume the specified - * standard_conforming_strings setting. - * - * This is needed in situations where we do not have a PGconn available. - * Where we do, PQescapeByteaConn is a better choice. - */ -void -appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length, - bool std_strings) -{ - const unsigned char *source = str; - char *target; - - static const char hextbl[] = "0123456789abcdef"; - - /* - * This implementation is hard-wired to produce hex-format output. We do - * not know the server version the output will be loaded into, so making - * an intelligent format choice is impossible. It might be better to - * always use the old escaped format. - */ - if (!enlargePQExpBuffer(buf, 2 * length + 5)) - return; - - target = buf->data + buf->len; - *target++ = '\''; - if (!std_strings) - *target++ = '\\'; - *target++ = '\\'; - *target++ = 'x'; - - while (length-- > 0) - { - unsigned char c = *source++; - - *target++ = hextbl[(c >> 4) & 0xF]; - *target++ = hextbl[c & 0xF]; - } - - /* Write the terminating quote and NUL character. */ - *target++ = '\''; - *target = '\0'; - - buf->len = target - buf->data; -} - - -/* - * Deconstruct the text representation of a 1-dimensional Postgres array - * into individual items. - * - * On success, returns true and sets *itemarray and *nitems to describe - * an array of individual strings. On parse failure, returns false; - * *itemarray may exist or be NULL. - * - * NOTE: free'ing itemarray is sufficient to deallocate the working storage. - */ -bool -parsePGArray(const char *atext, char ***itemarray, int *nitems) -{ - int inputlen; - char **items; - char *strings; - int curitem; - - /* - * We expect input in the form of "{item,item,item}" where any item is - * either raw data, or surrounded by double quotes (in which case embedded - * characters including backslashes and quotes are backslashed). - * - * We build the result as an array of pointers followed by the actual - * string data, all in one malloc block for convenience of deallocation. - * The worst-case storage need is not more than one pointer and one - * character for each input character (consider "{,,,,,,,,,,}"). - */ - *itemarray = NULL; - *nitems = 0; - inputlen = strlen(atext); - if (inputlen < 2 || atext[0] != '{' || atext[inputlen - 1] != '}') - return false; /* bad input */ - items = (char **) malloc(inputlen * (sizeof(char *) + sizeof(char))); - if (items == NULL) - return false; /* out of memory */ - *itemarray = items; - strings = (char *) (items + inputlen); - - atext++; /* advance over initial '{' */ - curitem = 0; - while (*atext != '}') - { - if (*atext == '\0') - return false; /* premature end of string */ - items[curitem] = strings; - while (*atext != '}' && *atext != ',') - { - if (*atext == '\0') - return false; /* premature end of string */ - if (*atext != '"') - *strings++ = *atext++; /* copy unquoted data */ - else - { - /* process quoted substring */ - atext++; - while (*atext != '"') - { - if (*atext == '\0') - return false; /* premature end of string */ - if (*atext == '\\') - { - atext++; - if (*atext == '\0') - return false; /* premature end of string */ - } - *strings++ = *atext++; /* copy quoted data */ - } - atext++; - } - } - *strings++ = '\0'; - if (*atext == ',') - atext++; - curitem++; - } - if (atext[1] != '\0') - return false; /* bogus syntax (embedded '}') */ - *nitems = curitem; - return true; -} /* @@ -951,218 +513,6 @@ AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname) /* - * processSQLNamePattern - * - * Scan a wildcard-pattern string and generate appropriate WHERE clauses - * to limit the set of objects returned. The WHERE clauses are appended - * to the already-partially-constructed query in buf. Returns whether - * any clause was added. - * - * conn: connection query will be sent to (consulted for escaping rules). - * buf: output parameter. - * pattern: user-specified pattern option, or NULL if none ("*" is implied). - * have_where: true if caller already emitted "WHERE" (clauses will be ANDed - * onto the existing WHERE clause). - * force_escape: always quote regexp special characters, even outside - * double quotes (else they are quoted only between double quotes). - * schemavar: name of query variable to match against a schema-name pattern. - * Can be NULL if no schema. - * namevar: name of query variable to match against an object-name pattern. - * altnamevar: NULL, or name of an alternative variable to match against name. - * visibilityrule: clause to use if we want to restrict to visible objects - * (for example, "pg_catalog.pg_table_is_visible(p.oid)"). Can be NULL. - * - * Formatting note: the text already present in buf should end with a newline. - * The appended text, if any, will end with one too. - */ -bool -processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern, - bool have_where, bool force_escape, - const char *schemavar, const char *namevar, - const char *altnamevar, const char *visibilityrule) -{ - PQExpBufferData schemabuf; - PQExpBufferData namebuf; - int encoding = PQclientEncoding(conn); - bool inquotes; - const char *cp; - int i; - bool added_clause = false; - -#define WHEREAND() \ - (appendPQExpBufferStr(buf, have_where ? " AND " : "WHERE "), \ - have_where = true, added_clause = true) - - if (pattern == NULL) - { - /* Default: select all visible objects */ - if (visibilityrule) - { - WHEREAND(); - appendPQExpBuffer(buf, "%s\n", visibilityrule); - } - return added_clause; - } - - initPQExpBuffer(&schemabuf); - initPQExpBuffer(&namebuf); - - /* - * Parse the pattern, converting quotes and lower-casing unquoted letters. - * Also, adjust shell-style wildcard characters into regexp notation. - * - * We surround the pattern with "^(...)$" to force it to match the whole - * string, as per SQL practice. We have to have parens in case the string - * contains "|", else the "^" and "$" will be bound into the first and - * last alternatives which is not what we want. - * - * Note: the result of this pass is the actual regexp pattern(s) we want - * to execute. Quoting/escaping into SQL literal format will be done - * below using appendStringLiteralConn(). - */ - appendPQExpBufferStr(&namebuf, "^("); - - inquotes = false; - cp = pattern; - - while (*cp) - { - char ch = *cp; - - if (ch == '"') - { - if (inquotes && cp[1] == '"') - { - /* emit one quote, stay in inquotes mode */ - appendPQExpBufferChar(&namebuf, '"'); - cp++; - } - else - inquotes = !inquotes; - cp++; - } - else if (!inquotes && isupper((unsigned char) ch)) - { - appendPQExpBufferChar(&namebuf, - pg_tolower((unsigned char) ch)); - cp++; - } - else if (!inquotes && ch == '*') - { - appendPQExpBufferStr(&namebuf, ".*"); - cp++; - } - else if (!inquotes && ch == '?') - { - appendPQExpBufferChar(&namebuf, '.'); - cp++; - } - else if (!inquotes && ch == '.') - { - /* Found schema/name separator, move current pattern to schema */ - resetPQExpBuffer(&schemabuf); - appendPQExpBufferStr(&schemabuf, namebuf.data); - resetPQExpBuffer(&namebuf); - appendPQExpBufferStr(&namebuf, "^("); - cp++; - } - else if (ch == '$') - { - /* - * Dollar is always quoted, whether inside quotes or not. The - * reason is that it's allowed in SQL identifiers, so there's a - * significant use-case for treating it literally, while because - * we anchor the pattern automatically there is no use-case for - * having it possess its regexp meaning. - */ - appendPQExpBufferStr(&namebuf, "\\$"); - cp++; - } - else - { - /* - * Ordinary data character, transfer to pattern - * - * Inside double quotes, or at all times if force_escape is true, - * quote regexp special characters with a backslash to avoid - * regexp errors. Outside quotes, however, let them pass through - * as-is; this lets knowledgeable users build regexp expressions - * that are more powerful than shell-style patterns. - */ - if ((inquotes || force_escape) && - strchr("|*+?()[]{}.^$\\", ch)) - appendPQExpBufferChar(&namebuf, '\\'); - i = PQmblen(cp, encoding); - while (i-- && *cp) - { - appendPQExpBufferChar(&namebuf, *cp); - cp++; - } - } - } - - /* - * Now decide what we need to emit. Note there will be a leading "^(" in - * the patterns in any case. - */ - if (namebuf.len > 2) - { - /* We have a name pattern, so constrain the namevar(s) */ - - appendPQExpBufferStr(&namebuf, ")$"); - /* Optimize away a "*" pattern */ - if (strcmp(namebuf.data, "^(.*)$") != 0) - { - WHEREAND(); - if (altnamevar) - { - appendPQExpBuffer(buf, "(%s ~ ", namevar); - appendStringLiteralConn(buf, namebuf.data, conn); - appendPQExpBuffer(buf, "\n OR %s ~ ", altnamevar); - appendStringLiteralConn(buf, namebuf.data, conn); - appendPQExpBufferStr(buf, ")\n"); - } - else - { - appendPQExpBuffer(buf, "%s ~ ", namevar); - appendStringLiteralConn(buf, namebuf.data, conn); - appendPQExpBufferChar(buf, '\n'); - } - } - } - - if (schemabuf.len > 2) - { - /* We have a schema pattern, so constrain the schemavar */ - - appendPQExpBufferStr(&schemabuf, ")$"); - /* Optimize away a "*" pattern */ - if (strcmp(schemabuf.data, "^(.*)$") != 0 && schemavar) - { - WHEREAND(); - appendPQExpBuffer(buf, "%s ~ ", schemavar); - appendStringLiteralConn(buf, schemabuf.data, conn); - appendPQExpBufferChar(buf, '\n'); - } - } - else - { - /* No schema pattern given, so select only visible objects */ - if (visibilityrule) - { - WHEREAND(); - appendPQExpBuffer(buf, "%s\n", visibilityrule); - } - } - - termPQExpBuffer(&schemabuf); - termPQExpBuffer(&namebuf); - - return added_clause; -#undef WHEREAND -} - -/* * buildShSecLabelQuery * * Build a query to retrieve security labels for a shared object. @@ -1205,52 +555,3 @@ emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer, appendPQExpBufferStr(buffer, ";\n"); } } - - -void -simple_string_list_append(SimpleStringList *list, const char *val) -{ - SimpleStringListCell *cell; - - cell = (SimpleStringListCell *) - pg_malloc(offsetof(SimpleStringListCell, val) +strlen(val) + 1); - - cell->next = NULL; - cell->touched = false; - strcpy(cell->val, val); - - if (list->tail) - list->tail->next = cell; - else - list->head = cell; - list->tail = cell; -} - -bool -simple_string_list_member(SimpleStringList *list, const char *val) -{ - SimpleStringListCell *cell; - - for (cell = list->head; cell; cell = cell->next) - { - if (strcmp(cell->val, val) == 0) - { - cell->touched = true; - return true; - } - } - return false; -} - -const char * -simple_string_list_not_touched(SimpleStringList *list) -{ - SimpleStringListCell *cell; - - for (cell = list->head; cell; cell = cell->next) - { - if (!cell->touched) - return cell->val; - } - return NULL; -} diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h index 4941ec02e79..4b404be99a9 100644 --- a/src/bin/pg_dump/dumputils.h +++ b/src/bin/pg_dump/dumputils.h @@ -1,8 +1,8 @@ /*------------------------------------------------------------------------- * * Utility routines for SQL dumping - * Basically this is stuff that is useful in both pg_dump and pg_dumpall. - * Lately it's also being used by psql and bin/scripts/ ... + * + * Basically this is stuff that is useful in both pg_dump and pg_dumpall. * * * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group @@ -19,39 +19,6 @@ #include "pqexpbuffer.h" /* - * Data structures for simple lists of OIDs and strings. The support for - * these is very primitive compared to the backend's List facilities, but - * it's all we need in pg_dump. - */ -typedef struct SimpleOidListCell -{ - struct SimpleOidListCell *next; - Oid val; -} SimpleOidListCell; - -typedef struct SimpleOidList -{ - SimpleOidListCell *head; - SimpleOidListCell *tail; -} SimpleOidList; - -typedef struct SimpleStringListCell -{ - struct SimpleStringListCell *next; - bool touched; /* true, when this string was searched and - * touched */ - char val[FLEXIBLE_ARRAY_MEMBER]; /* null-terminated string here */ -} SimpleStringListCell; - -typedef struct SimpleStringList -{ - SimpleStringListCell *head; - SimpleStringListCell *tail; -} SimpleStringList; - -#define atooid(x) ((Oid) strtoul((x), NULL, 10)) - -/* * Preferred strftime(3) format specifier for printing timestamps in pg_dump * and friends. * @@ -68,22 +35,7 @@ typedef struct SimpleStringList #define PGDUMP_STRFTIME_FMT "%Y-%m-%d %H:%M:%S" #endif -extern int quote_all_identifiers; -extern PQExpBuffer (*getLocalPQExpBuffer) (void); -extern const char *fmtId(const char *identifier); -extern const char *fmtQualifiedId(int remoteVersion, - const char *schema, const char *id); -extern void appendStringLiteral(PQExpBuffer buf, const char *str, - int encoding, bool std_strings); -extern void appendStringLiteralConn(PQExpBuffer buf, const char *str, - PGconn *conn); -extern void appendStringLiteralDQ(PQExpBuffer buf, const char *str, - const char *dqprefix); -extern void appendByteaLiteral(PQExpBuffer buf, - const unsigned char *str, size_t length, - bool std_strings); -extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems); extern bool buildACLCommands(const char *name, const char *subname, const char *type, const char *acls, const char *owner, const char *prefix, int remoteVersion, @@ -92,20 +44,9 @@ extern bool buildDefaultACLCommands(const char *type, const char *nspname, const char *acls, const char *owner, int remoteVersion, PQExpBuffer sql); -extern bool processSQLNamePattern(PGconn *conn, PQExpBuffer buf, - const char *pattern, - bool have_where, bool force_escape, - const char *schemavar, const char *namevar, - const char *altnamevar, const char *visibilityrule); extern void buildShSecLabelQuery(PGconn *conn, const char *catalog_name, uint32 objectId, PQExpBuffer sql); extern void emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer, const char *target, const char *objname); -extern void set_dump_section(const char *arg, int *dumpSections); - -extern void simple_string_list_append(SimpleStringList *list, const char *val); -extern bool simple_string_list_member(SimpleStringList *list, const char *val); -extern const char *simple_string_list_not_touched(SimpleStringList *list); - #endif /* DUMPUTILS_H */ diff --git a/src/bin/pg_dump/parallel.c b/src/bin/pg_dump/parallel.c index 9ce7711bf4d..91672949e69 100644 --- a/src/bin/pg_dump/parallel.c +++ b/src/bin/pg_dump/parallel.c @@ -20,6 +20,7 @@ #include "parallel.h" #include "pg_backup_utils.h" +#include "fe_utils/string_utils.h" #ifndef WIN32 #include <sys/types.h> diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index 26061e7a6ec..83f6029c1fe 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -23,7 +23,7 @@ #ifndef PG_BACKUP_H #define PG_BACKUP_H -#include "dumputils.h" +#include "fe_utils/simple_list.h" #include "libpq-fe.h" diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index 0132dad0a21..fdca64f07ef 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -25,6 +25,8 @@ #include "pg_backup_archiver.h" #include "pg_backup_db.h" #include "pg_backup_utils.h" +#include "dumputils.h" +#include "fe_utils/string_utils.h" #include <ctype.h> #include <fcntl.h> diff --git a/src/bin/pg_dump/pg_backup_null.c b/src/bin/pg_dump/pg_backup_null.c index 848eed49d0d..ff419bb82f4 100644 --- a/src/bin/pg_dump/pg_backup_null.c +++ b/src/bin/pg_dump/pg_backup_null.c @@ -25,6 +25,7 @@ #include "pg_backup_archiver.h" #include "pg_backup_utils.h" +#include "fe_utils/string_utils.h" #include "libpq/libpq-fs.h" diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c index eb5bcbb0e31..8dfc6a98de1 100644 --- a/src/bin/pg_dump/pg_backup_tar.c +++ b/src/bin/pg_dump/pg_backup_tar.c @@ -33,6 +33,7 @@ #include "pg_backup_tar.h" #include "pg_backup_utils.h" #include "pgtar.h" +#include "fe_utils/string_utils.h" #include <sys/stat.h> #include <ctype.h> diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index b3ef201a3ae..ad4b4e5135e 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -61,6 +61,7 @@ #include "pg_backup_db.h" #include "pg_backup_utils.h" #include "pg_dump.h" +#include "fe_utils/string_utils.h" typedef struct diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 66e693183ae..c02c536a9c4 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -536,9 +536,6 @@ extern ExtensionInfo *findExtensionByOid(Oid oid); extern void setExtensionMembership(ExtensionMemberId *extmems, int nextmems); extern ExtensionInfo *findOwningExtension(CatalogId catalogId); -extern void simple_oid_list_append(SimpleOidList *list, Oid val); -extern bool simple_oid_list_member(SimpleOidList *list, Oid val); - extern void parseOidArray(const char *str, Oid *array, int arraysize); extern void sortDumpableObjects(DumpableObject **objs, int numObjs, diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index be6b4a898e7..530d3f4d2c0 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -26,6 +26,7 @@ #include "dumputils.h" #include "pg_backup.h" +#include "fe_utils/string_utils.h" /* version string we expect back from pg_dump */ #define PGDUMP_VERSIONSTR "pg_dump (PostgreSQL) " PG_VERSION "\n" diff --git a/src/bin/psql/.gitignore b/src/bin/psql/.gitignore index ce881f45ad2..9de50c0a287 100644 --- a/src/bin/psql/.gitignore +++ b/src/bin/psql/.gitignore @@ -2,6 +2,5 @@ /psqlscanslash.c /sql_help.h /sql_help.c -/dumputils.c /psql diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile index 75a9b395b4a..251d6389900 100644 --- a/src/bin/psql/Makefile +++ b/src/bin/psql/Makefile @@ -18,25 +18,23 @@ include $(top_builddir)/src/Makefile.global REFDOCDIR= $(top_srcdir)/doc/src/sgml/ref -override CPPFLAGS := -I. -I$(srcdir) -I$(libpq_srcdir) -I$(top_srcdir)/src/bin/pg_dump $(CPPFLAGS) +override CPPFLAGS := -I. -I$(srcdir) -I$(libpq_srcdir) $(CPPFLAGS) +LDFLAGS += -L$(top_builddir)/src/fe_utils -lpgfeutils OBJS= command.o common.o help.o input.o stringutils.o mainloop.o copy.o \ startup.o prompt.o variables.o large_obj.o print.o describe.o \ - tab-complete.o mbprint.o dumputils.o \ + tab-complete.o mbprint.o \ sql_help.o psqlscan.o psqlscanslash.o \ $(WIN32RES) all: psql -psql: $(OBJS) | submake-libpq submake-libpgport +psql: $(OBJS) | submake-libpq submake-libpgport submake-libpgfeutils $(CC) $(CFLAGS) $(OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) help.o: sql_help.h -dumputils.c: % : $(top_srcdir)/src/bin/pg_dump/% - rm -f $@ && $(LN_S) $< . - sql_help.c: sql_help.h ; sql_help.h: create_help.pl $(wildcard $(REFDOCDIR)/*.sgml) $(PERL) $< $(REFDOCDIR) $* @@ -67,7 +65,7 @@ uninstall: rm -f '$(DESTDIR)$(bindir)/psql$(X)' '$(DESTDIR)$(datadir)/psqlrc.sample' clean distclean: - rm -f psql$(X) $(OBJS) dumputils.c lex.backup + rm -f psql$(X) $(OBJS) lex.backup # files removed here are supposed to be in the distribution tarball, # so do not clean them in the clean/distclean rules diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 3ea12b8f8f9..e5ec8af11c5 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -35,7 +35,7 @@ #include "libpq-fe.h" #include "pqexpbuffer.h" -#include "dumputils.h" +#include "fe_utils/string_utils.h" #include "common.h" #include "copy.h" diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c index ff1d61b568f..942264fbf97 100644 --- a/src/bin/psql/copy.c +++ b/src/bin/psql/copy.c @@ -18,7 +18,6 @@ #include "libpq-fe.h" #include "pqexpbuffer.h" -#include "dumputils.h" #include "settings.h" #include "common.h" diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index fd8dc9122d4..b824d4e5492 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -15,10 +15,10 @@ #include <ctype.h> #include "catalog/pg_default_acl.h" +#include "fe_utils/string_utils.h" #include "common.h" #include "describe.h" -#include "dumputils.h" #include "mbprint.h" #include "print.h" #include "settings.h" diff --git a/src/bin/scripts/.gitignore b/src/bin/scripts/.gitignore index e12d27a2f1b..784f25b93e7 100644 --- a/src/bin/scripts/.gitignore +++ b/src/bin/scripts/.gitignore @@ -9,7 +9,6 @@ /vacuumdb /pg_isready -/dumputils.c /mbprint.c /print.c diff --git a/src/bin/scripts/Makefile b/src/bin/scripts/Makefile index ad34d42d682..5e47e13a78a 100644 --- a/src/bin/scripts/Makefile +++ b/src/bin/scripts/Makefile @@ -18,27 +18,25 @@ include $(top_builddir)/src/Makefile.global PROGRAMS = createdb createlang createuser dropdb droplang dropuser clusterdb vacuumdb reindexdb pg_isready -override CPPFLAGS := -I$(top_srcdir)/src/bin/pg_dump -I$(top_srcdir)/src/bin/psql -I$(libpq_srcdir) $(CPPFLAGS) +override CPPFLAGS := -I$(top_srcdir)/src/bin/psql -I$(libpq_srcdir) $(CPPFLAGS) +LDFLAGS += -L$(top_builddir)/src/fe_utils -lpgfeutils all: $(PROGRAMS) %: %.o $(WIN32RES) $(CC) $(CFLAGS) $^ $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) -createdb: createdb.o common.o dumputils.o | submake-libpq submake-libpgport +createdb: createdb.o common.o | submake-libpq submake-libpgport submake-libpgfeutils createlang: createlang.o common.o print.o mbprint.o | submake-libpq submake-libpgport -createuser: createuser.o common.o dumputils.o | submake-libpq submake-libpgport -dropdb: dropdb.o common.o dumputils.o | submake-libpq submake-libpgport +createuser: createuser.o common.o | submake-libpq submake-libpgport submake-libpgfeutils +dropdb: dropdb.o common.o | submake-libpq submake-libpgport submake-libpgfeutils droplang: droplang.o common.o print.o mbprint.o | submake-libpq submake-libpgport -dropuser: dropuser.o common.o dumputils.o | submake-libpq submake-libpgport -clusterdb: clusterdb.o common.o dumputils.o | submake-libpq submake-libpgport -vacuumdb: vacuumdb.o common.o dumputils.o | submake-libpq submake-libpgport -reindexdb: reindexdb.o common.o dumputils.o | submake-libpq submake-libpgport +dropuser: dropuser.o common.o | submake-libpq submake-libpgport submake-libpgfeutils +clusterdb: clusterdb.o common.o | submake-libpq submake-libpgport submake-libpgfeutils +vacuumdb: vacuumdb.o common.o | submake-libpq submake-libpgport submake-libpgfeutils +reindexdb: reindexdb.o common.o | submake-libpq submake-libpgport submake-libpgfeutils pg_isready: pg_isready.o common.o | submake-libpq submake-libpgport -dumputils.c: % : $(top_srcdir)/src/bin/pg_dump/% - rm -f $@ && $(LN_S) $< . - print.c mbprint.c : % : $(top_srcdir)/src/bin/psql/% rm -f $@ && $(LN_S) $< . @@ -62,8 +60,8 @@ uninstall: clean distclean maintainer-clean: rm -f $(addsuffix $(X), $(PROGRAMS)) $(addsuffix .o, $(PROGRAMS)) - rm -f common.o dumputils.o print.o mbprint.o $(WIN32RES) - rm -f dumputils.c print.c mbprint.c + rm -f common.o print.o mbprint.o $(WIN32RES) + rm -f print.c mbprint.c rm -rf tmp_check check: diff --git a/src/bin/scripts/clusterdb.c b/src/bin/scripts/clusterdb.c index 9e8d9580a66..be34ba11220 100644 --- a/src/bin/scripts/clusterdb.c +++ b/src/bin/scripts/clusterdb.c @@ -11,7 +11,7 @@ #include "postgres_fe.h" #include "common.h" -#include "dumputils.h" +#include "fe_utils/simple_list.h" static void cluster_one_database(const char *dbname, bool verbose, const char *table, diff --git a/src/bin/scripts/createdb.c b/src/bin/scripts/createdb.c index d08b1a77f1e..fddfde76e23 100644 --- a/src/bin/scripts/createdb.c +++ b/src/bin/scripts/createdb.c @@ -12,7 +12,7 @@ #include "postgres_fe.h" #include "common.h" -#include "dumputils.h" +#include "fe_utils/string_utils.h" static void help(const char *progname); diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c index 35e48e334f7..e88879dc19e 100644 --- a/src/bin/scripts/createuser.c +++ b/src/bin/scripts/createuser.c @@ -12,7 +12,8 @@ #include "postgres_fe.h" #include "common.h" -#include "dumputils.h" +#include "fe_utils/simple_list.h" +#include "fe_utils/string_utils.h" static void help(const char *progname); diff --git a/src/bin/scripts/dropdb.c b/src/bin/scripts/dropdb.c index 08b72a77ba3..145beb02217 100644 --- a/src/bin/scripts/dropdb.c +++ b/src/bin/scripts/dropdb.c @@ -12,7 +12,7 @@ #include "postgres_fe.h" #include "common.h" -#include "dumputils.h" +#include "fe_utils/string_utils.h" static void help(const char *progname); diff --git a/src/bin/scripts/dropuser.c b/src/bin/scripts/dropuser.c index df91eec22c9..31fa28f7cdc 100644 --- a/src/bin/scripts/dropuser.c +++ b/src/bin/scripts/dropuser.c @@ -12,7 +12,7 @@ #include "postgres_fe.h" #include "common.h" -#include "dumputils.h" +#include "fe_utils/string_utils.h" static void help(const char *progname); diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c index c25bd26675d..0c8c90c22a4 100644 --- a/src/bin/scripts/reindexdb.c +++ b/src/bin/scripts/reindexdb.c @@ -11,7 +11,8 @@ #include "postgres_fe.h" #include "common.h" -#include "dumputils.h" +#include "fe_utils/simple_list.h" +#include "fe_utils/string_utils.h" static void reindex_one_database(const char *name, const char *dbname, diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c index b673be83ff1..dbaae1288b8 100644 --- a/src/bin/scripts/vacuumdb.c +++ b/src/bin/scripts/vacuumdb.c @@ -13,7 +13,8 @@ #include "postgres_fe.h" #include "common.h" -#include "dumputils.h" +#include "fe_utils/simple_list.h" +#include "fe_utils/string_utils.h" #define ERRCODE_UNDEFINED_TABLE "42P01" diff --git a/src/fe_utils/Makefile b/src/fe_utils/Makefile new file mode 100644 index 00000000000..f6a52dfdb41 --- /dev/null +++ b/src/fe_utils/Makefile @@ -0,0 +1,39 @@ +#------------------------------------------------------------------------- +# +# Makefile +# Makefile for src/fe_utils +# +# This makefile generates a static library, libpgfeutils.a, +# for use by client applications +# +# IDENTIFICATION +# src/fe_utils/Makefile +# +#------------------------------------------------------------------------- + +subdir = src/fe_utils +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global + +override CPPFLAGS := -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS) + +OBJS = simple_list.o string_utils.o + +all: libpgfeutils.a + +libpgfeutils.a: $(OBJS) + rm -f $@ + $(AR) $(AROPT) $@ $^ + +# libpgfeutils could be useful to contrib, so install it +install: all installdirs + $(INSTALL_STLIB) libpgfeutils.a '$(DESTDIR)$(libdir)/libpgfeutils.a' + +installdirs: + $(MKDIR_P) '$(DESTDIR)$(libdir)' + +uninstall: + rm -f '$(DESTDIR)$(libdir)/libpgfeutils.a' + +clean distclean maintainer-clean: + rm -f libpgfeutils.a $(OBJS) diff --git a/src/fe_utils/simple_list.c b/src/fe_utils/simple_list.c new file mode 100644 index 00000000000..ed4d1885161 --- /dev/null +++ b/src/fe_utils/simple_list.c @@ -0,0 +1,116 @@ +/*------------------------------------------------------------------------- + * + * Simple list facilities for frontend code + * + * Data structures for simple lists of OIDs and strings. The support for + * these is very primitive compared to the backend's List facilities, but + * it's all we need in, eg, pg_dump. + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/fe_utils/simple_list.c + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include "fe_utils/simple_list.h" + + +/* + * Append an OID to the list. + */ +void +simple_oid_list_append(SimpleOidList *list, Oid val) +{ + SimpleOidListCell *cell; + + cell = (SimpleOidListCell *) pg_malloc(sizeof(SimpleOidListCell)); + cell->next = NULL; + cell->val = val; + + if (list->tail) + list->tail->next = cell; + else + list->head = cell; + list->tail = cell; +} + +/* + * Is OID present in the list? + */ +bool +simple_oid_list_member(SimpleOidList *list, Oid val) +{ + SimpleOidListCell *cell; + + for (cell = list->head; cell; cell = cell->next) + { + if (cell->val == val) + return true; + } + return false; +} + +/* + * Append a string to the list. + * + * The given string is copied, so it need not survive past the call. + */ +void +simple_string_list_append(SimpleStringList *list, const char *val) +{ + SimpleStringListCell *cell; + + cell = (SimpleStringListCell *) + pg_malloc(offsetof(SimpleStringListCell, val) +strlen(val) + 1); + + cell->next = NULL; + cell->touched = false; + strcpy(cell->val, val); + + if (list->tail) + list->tail->next = cell; + else + list->head = cell; + list->tail = cell; +} + +/* + * Is string present in the list? + * + * If found, the "touched" field of the first match is set true. + */ +bool +simple_string_list_member(SimpleStringList *list, const char *val) +{ + SimpleStringListCell *cell; + + for (cell = list->head; cell; cell = cell->next) + { + if (strcmp(cell->val, val) == 0) + { + cell->touched = true; + return true; + } + } + return false; +} + +/* + * Find first not-touched list entry, if there is one. + */ +const char * +simple_string_list_not_touched(SimpleStringList *list) +{ + SimpleStringListCell *cell; + + for (cell = list->head; cell; cell = cell->next) + { + if (!cell->touched) + return cell->val; + } + return NULL; +} diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c new file mode 100644 index 00000000000..c57d8366379 --- /dev/null +++ b/src/fe_utils/string_utils.c @@ -0,0 +1,674 @@ +/*------------------------------------------------------------------------- + * + * String-processing utility routines for frontend code + * + * Assorted utility functions that are useful in constructing SQL queries + * and interpreting backend output. + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/fe_utils/string_utils.c + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include <ctype.h> + +#include "fe_utils/string_utils.h" + +#include "common/keywords.h" + + +static PQExpBuffer defaultGetLocalPQExpBuffer(void); + +/* Globals exported by this file */ +int quote_all_identifiers = 0; +PQExpBuffer (*getLocalPQExpBuffer) (void) = defaultGetLocalPQExpBuffer; + + +/* + * Returns a temporary PQExpBuffer, valid until the next call to the function. + * This is used by fmtId and fmtQualifiedId. + * + * Non-reentrant and non-thread-safe but reduces memory leakage. You can + * replace this with a custom version by setting the getLocalPQExpBuffer + * function pointer. + */ +static PQExpBuffer +defaultGetLocalPQExpBuffer(void) +{ + static PQExpBuffer id_return = NULL; + + if (id_return) /* first time through? */ + { + /* same buffer, just wipe contents */ + resetPQExpBuffer(id_return); + } + else + { + /* new buffer */ + id_return = createPQExpBuffer(); + } + + return id_return; +} + +/* + * Quotes input string if it's not a legitimate SQL identifier as-is. + * + * Note that the returned string must be used before calling fmtId again, + * since we re-use the same return buffer each time. + */ +const char * +fmtId(const char *rawid) +{ + PQExpBuffer id_return = getLocalPQExpBuffer(); + + const char *cp; + bool need_quotes = false; + + /* + * These checks need to match the identifier production in scan.l. Don't + * use islower() etc. + */ + if (quote_all_identifiers) + need_quotes = true; + /* slightly different rules for first character */ + else if (!((rawid[0] >= 'a' && rawid[0] <= 'z') || rawid[0] == '_')) + need_quotes = true; + else + { + /* otherwise check the entire string */ + for (cp = rawid; *cp; cp++) + { + if (!((*cp >= 'a' && *cp <= 'z') + || (*cp >= '0' && *cp <= '9') + || (*cp == '_'))) + { + need_quotes = true; + break; + } + } + } + + if (!need_quotes) + { + /* + * Check for keyword. We quote keywords except for unreserved ones. + * (In some cases we could avoid quoting a col_name or type_func_name + * keyword, but it seems much harder than it's worth to tell that.) + * + * Note: ScanKeywordLookup() does case-insensitive comparison, but + * that's fine, since we already know we have all-lower-case. + */ + const ScanKeyword *keyword = ScanKeywordLookup(rawid, + ScanKeywords, + NumScanKeywords); + + if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD) + need_quotes = true; + } + + if (!need_quotes) + { + /* no quoting needed */ + appendPQExpBufferStr(id_return, rawid); + } + else + { + appendPQExpBufferChar(id_return, '"'); + for (cp = rawid; *cp; cp++) + { + /* + * Did we find a double-quote in the string? Then make this a + * double double-quote per SQL99. Before, we put in a + * backslash/double-quote pair. - thomas 2000-08-05 + */ + if (*cp == '"') + appendPQExpBufferChar(id_return, '"'); + appendPQExpBufferChar(id_return, *cp); + } + appendPQExpBufferChar(id_return, '"'); + } + + return id_return->data; +} + +/* + * fmtQualifiedId - convert a qualified name to the proper format for + * the source database. + * + * Like fmtId, use the result before calling again. + * + * Since we call fmtId and it also uses getThreadLocalPQExpBuffer() we cannot + * use it until we're finished with calling fmtId(). + */ +const char * +fmtQualifiedId(int remoteVersion, const char *schema, const char *id) +{ + PQExpBuffer id_return; + PQExpBuffer lcl_pqexp = createPQExpBuffer(); + + /* Suppress schema name if fetching from pre-7.3 DB */ + if (remoteVersion >= 70300 && schema && *schema) + { + appendPQExpBuffer(lcl_pqexp, "%s.", fmtId(schema)); + } + appendPQExpBufferStr(lcl_pqexp, fmtId(id)); + + id_return = getLocalPQExpBuffer(); + + appendPQExpBufferStr(id_return, lcl_pqexp->data); + destroyPQExpBuffer(lcl_pqexp); + + return id_return->data; +} + + +/* + * Convert a string value to an SQL string literal and append it to + * the given buffer. We assume the specified client_encoding and + * standard_conforming_strings settings. + * + * This is essentially equivalent to libpq's PQescapeStringInternal, + * except for the output buffer structure. We need it in situations + * where we do not have a PGconn available. Where we do, + * appendStringLiteralConn is a better choice. + */ +void +appendStringLiteral(PQExpBuffer buf, const char *str, + int encoding, bool std_strings) +{ + size_t length = strlen(str); + const char *source = str; + char *target; + + if (!enlargePQExpBuffer(buf, 2 * length + 2)) + return; + + target = buf->data + buf->len; + *target++ = '\''; + + while (*source != '\0') + { + char c = *source; + int len; + int i; + + /* Fast path for plain ASCII */ + if (!IS_HIGHBIT_SET(c)) + { + /* Apply quoting if needed */ + if (SQL_STR_DOUBLE(c, !std_strings)) + *target++ = c; + /* Copy the character */ + *target++ = c; + source++; + continue; + } + + /* Slow path for possible multibyte characters */ + len = PQmblen(source, encoding); + + /* Copy the character */ + for (i = 0; i < len; i++) + { + if (*source == '\0') + break; + *target++ = *source++; + } + + /* + * If we hit premature end of string (ie, incomplete multibyte + * character), try to pad out to the correct length with spaces. We + * may not be able to pad completely, but we will always be able to + * insert at least one pad space (since we'd not have quoted a + * multibyte character). This should be enough to make a string that + * the server will error out on. + */ + if (i < len) + { + char *stop = buf->data + buf->maxlen - 2; + + for (; i < len; i++) + { + if (target >= stop) + break; + *target++ = ' '; + } + break; + } + } + + /* Write the terminating quote and NUL character. */ + *target++ = '\''; + *target = '\0'; + + buf->len = target - buf->data; +} + + +/* + * Convert a string value to an SQL string literal and append it to + * the given buffer. Encoding and string syntax rules are as indicated + * by current settings of the PGconn. + */ +void +appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn) +{ + size_t length = strlen(str); + + /* + * XXX This is a kluge to silence escape_string_warning in our utility + * programs. It should go away someday. + */ + if (strchr(str, '\\') != NULL && PQserverVersion(conn) >= 80100) + { + /* ensure we are not adjacent to an identifier */ + if (buf->len > 0 && buf->data[buf->len - 1] != ' ') + appendPQExpBufferChar(buf, ' '); + appendPQExpBufferChar(buf, ESCAPE_STRING_SYNTAX); + appendStringLiteral(buf, str, PQclientEncoding(conn), false); + return; + } + /* XXX end kluge */ + + if (!enlargePQExpBuffer(buf, 2 * length + 2)) + return; + appendPQExpBufferChar(buf, '\''); + buf->len += PQescapeStringConn(conn, buf->data + buf->len, + str, length, NULL); + appendPQExpBufferChar(buf, '\''); +} + + +/* + * Convert a string value to a dollar quoted literal and append it to + * the given buffer. If the dqprefix parameter is not NULL then the + * dollar quote delimiter will begin with that (after the opening $). + * + * No escaping is done at all on str, in compliance with the rules + * for parsing dollar quoted strings. Also, we need not worry about + * encoding issues. + */ +void +appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix) +{ + static const char suffixes[] = "_XXXXXXX"; + int nextchar = 0; + PQExpBuffer delimBuf = createPQExpBuffer(); + + /* start with $ + dqprefix if not NULL */ + appendPQExpBufferChar(delimBuf, '$'); + if (dqprefix) + appendPQExpBufferStr(delimBuf, dqprefix); + + /* + * Make sure we choose a delimiter which (without the trailing $) is not + * present in the string being quoted. We don't check with the trailing $ + * because a string ending in $foo must not be quoted with $foo$. + */ + while (strstr(str, delimBuf->data) != NULL) + { + appendPQExpBufferChar(delimBuf, suffixes[nextchar++]); + nextchar %= sizeof(suffixes) - 1; + } + + /* add trailing $ */ + appendPQExpBufferChar(delimBuf, '$'); + + /* quote it and we are all done */ + appendPQExpBufferStr(buf, delimBuf->data); + appendPQExpBufferStr(buf, str); + appendPQExpBufferStr(buf, delimBuf->data); + + destroyPQExpBuffer(delimBuf); +} + + +/* + * Convert a bytea value (presented as raw bytes) to an SQL string literal + * and append it to the given buffer. We assume the specified + * standard_conforming_strings setting. + * + * This is needed in situations where we do not have a PGconn available. + * Where we do, PQescapeByteaConn is a better choice. + */ +void +appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length, + bool std_strings) +{ + const unsigned char *source = str; + char *target; + + static const char hextbl[] = "0123456789abcdef"; + + /* + * This implementation is hard-wired to produce hex-format output. We do + * not know the server version the output will be loaded into, so making + * an intelligent format choice is impossible. It might be better to + * always use the old escaped format. + */ + if (!enlargePQExpBuffer(buf, 2 * length + 5)) + return; + + target = buf->data + buf->len; + *target++ = '\''; + if (!std_strings) + *target++ = '\\'; + *target++ = '\\'; + *target++ = 'x'; + + while (length-- > 0) + { + unsigned char c = *source++; + + *target++ = hextbl[(c >> 4) & 0xF]; + *target++ = hextbl[c & 0xF]; + } + + /* Write the terminating quote and NUL character. */ + *target++ = '\''; + *target = '\0'; + + buf->len = target - buf->data; +} + + +/* + * Deconstruct the text representation of a 1-dimensional Postgres array + * into individual items. + * + * On success, returns true and sets *itemarray and *nitems to describe + * an array of individual strings. On parse failure, returns false; + * *itemarray may exist or be NULL. + * + * NOTE: free'ing itemarray is sufficient to deallocate the working storage. + */ +bool +parsePGArray(const char *atext, char ***itemarray, int *nitems) +{ + int inputlen; + char **items; + char *strings; + int curitem; + + /* + * We expect input in the form of "{item,item,item}" where any item is + * either raw data, or surrounded by double quotes (in which case embedded + * characters including backslashes and quotes are backslashed). + * + * We build the result as an array of pointers followed by the actual + * string data, all in one malloc block for convenience of deallocation. + * The worst-case storage need is not more than one pointer and one + * character for each input character (consider "{,,,,,,,,,,}"). + */ + *itemarray = NULL; + *nitems = 0; + inputlen = strlen(atext); + if (inputlen < 2 || atext[0] != '{' || atext[inputlen - 1] != '}') + return false; /* bad input */ + items = (char **) malloc(inputlen * (sizeof(char *) + sizeof(char))); + if (items == NULL) + return false; /* out of memory */ + *itemarray = items; + strings = (char *) (items + inputlen); + + atext++; /* advance over initial '{' */ + curitem = 0; + while (*atext != '}') + { + if (*atext == '\0') + return false; /* premature end of string */ + items[curitem] = strings; + while (*atext != '}' && *atext != ',') + { + if (*atext == '\0') + return false; /* premature end of string */ + if (*atext != '"') + *strings++ = *atext++; /* copy unquoted data */ + else + { + /* process quoted substring */ + atext++; + while (*atext != '"') + { + if (*atext == '\0') + return false; /* premature end of string */ + if (*atext == '\\') + { + atext++; + if (*atext == '\0') + return false; /* premature end of string */ + } + *strings++ = *atext++; /* copy quoted data */ + } + atext++; + } + } + *strings++ = '\0'; + if (*atext == ',') + atext++; + curitem++; + } + if (atext[1] != '\0') + return false; /* bogus syntax (embedded '}') */ + *nitems = curitem; + return true; +} + + +/* + * processSQLNamePattern + * + * Scan a wildcard-pattern string and generate appropriate WHERE clauses + * to limit the set of objects returned. The WHERE clauses are appended + * to the already-partially-constructed query in buf. Returns whether + * any clause was added. + * + * conn: connection query will be sent to (consulted for escaping rules). + * buf: output parameter. + * pattern: user-specified pattern option, or NULL if none ("*" is implied). + * have_where: true if caller already emitted "WHERE" (clauses will be ANDed + * onto the existing WHERE clause). + * force_escape: always quote regexp special characters, even outside + * double quotes (else they are quoted only between double quotes). + * schemavar: name of query variable to match against a schema-name pattern. + * Can be NULL if no schema. + * namevar: name of query variable to match against an object-name pattern. + * altnamevar: NULL, or name of an alternative variable to match against name. + * visibilityrule: clause to use if we want to restrict to visible objects + * (for example, "pg_catalog.pg_table_is_visible(p.oid)"). Can be NULL. + * + * Formatting note: the text already present in buf should end with a newline. + * The appended text, if any, will end with one too. + */ +bool +processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern, + bool have_where, bool force_escape, + const char *schemavar, const char *namevar, + const char *altnamevar, const char *visibilityrule) +{ + PQExpBufferData schemabuf; + PQExpBufferData namebuf; + int encoding = PQclientEncoding(conn); + bool inquotes; + const char *cp; + int i; + bool added_clause = false; + +#define WHEREAND() \ + (appendPQExpBufferStr(buf, have_where ? " AND " : "WHERE "), \ + have_where = true, added_clause = true) + + if (pattern == NULL) + { + /* Default: select all visible objects */ + if (visibilityrule) + { + WHEREAND(); + appendPQExpBuffer(buf, "%s\n", visibilityrule); + } + return added_clause; + } + + initPQExpBuffer(&schemabuf); + initPQExpBuffer(&namebuf); + + /* + * Parse the pattern, converting quotes and lower-casing unquoted letters. + * Also, adjust shell-style wildcard characters into regexp notation. + * + * We surround the pattern with "^(...)$" to force it to match the whole + * string, as per SQL practice. We have to have parens in case the string + * contains "|", else the "^" and "$" will be bound into the first and + * last alternatives which is not what we want. + * + * Note: the result of this pass is the actual regexp pattern(s) we want + * to execute. Quoting/escaping into SQL literal format will be done + * below using appendStringLiteralConn(). + */ + appendPQExpBufferStr(&namebuf, "^("); + + inquotes = false; + cp = pattern; + + while (*cp) + { + char ch = *cp; + + if (ch == '"') + { + if (inquotes && cp[1] == '"') + { + /* emit one quote, stay in inquotes mode */ + appendPQExpBufferChar(&namebuf, '"'); + cp++; + } + else + inquotes = !inquotes; + cp++; + } + else if (!inquotes && isupper((unsigned char) ch)) + { + appendPQExpBufferChar(&namebuf, + pg_tolower((unsigned char) ch)); + cp++; + } + else if (!inquotes && ch == '*') + { + appendPQExpBufferStr(&namebuf, ".*"); + cp++; + } + else if (!inquotes && ch == '?') + { + appendPQExpBufferChar(&namebuf, '.'); + cp++; + } + else if (!inquotes && ch == '.') + { + /* Found schema/name separator, move current pattern to schema */ + resetPQExpBuffer(&schemabuf); + appendPQExpBufferStr(&schemabuf, namebuf.data); + resetPQExpBuffer(&namebuf); + appendPQExpBufferStr(&namebuf, "^("); + cp++; + } + else if (ch == '$') + { + /* + * Dollar is always quoted, whether inside quotes or not. The + * reason is that it's allowed in SQL identifiers, so there's a + * significant use-case for treating it literally, while because + * we anchor the pattern automatically there is no use-case for + * having it possess its regexp meaning. + */ + appendPQExpBufferStr(&namebuf, "\\$"); + cp++; + } + else + { + /* + * Ordinary data character, transfer to pattern + * + * Inside double quotes, or at all times if force_escape is true, + * quote regexp special characters with a backslash to avoid + * regexp errors. Outside quotes, however, let them pass through + * as-is; this lets knowledgeable users build regexp expressions + * that are more powerful than shell-style patterns. + */ + if ((inquotes || force_escape) && + strchr("|*+?()[]{}.^$\\", ch)) + appendPQExpBufferChar(&namebuf, '\\'); + i = PQmblen(cp, encoding); + while (i-- && *cp) + { + appendPQExpBufferChar(&namebuf, *cp); + cp++; + } + } + } + + /* + * Now decide what we need to emit. Note there will be a leading "^(" in + * the patterns in any case. + */ + if (namebuf.len > 2) + { + /* We have a name pattern, so constrain the namevar(s) */ + + appendPQExpBufferStr(&namebuf, ")$"); + /* Optimize away a "*" pattern */ + if (strcmp(namebuf.data, "^(.*)$") != 0) + { + WHEREAND(); + if (altnamevar) + { + appendPQExpBuffer(buf, "(%s ~ ", namevar); + appendStringLiteralConn(buf, namebuf.data, conn); + appendPQExpBuffer(buf, "\n OR %s ~ ", altnamevar); + appendStringLiteralConn(buf, namebuf.data, conn); + appendPQExpBufferStr(buf, ")\n"); + } + else + { + appendPQExpBuffer(buf, "%s ~ ", namevar); + appendStringLiteralConn(buf, namebuf.data, conn); + appendPQExpBufferChar(buf, '\n'); + } + } + } + + if (schemabuf.len > 2) + { + /* We have a schema pattern, so constrain the schemavar */ + + appendPQExpBufferStr(&schemabuf, ")$"); + /* Optimize away a "*" pattern */ + if (strcmp(schemabuf.data, "^(.*)$") != 0 && schemavar) + { + WHEREAND(); + appendPQExpBuffer(buf, "%s ~ ", schemavar); + appendStringLiteralConn(buf, schemabuf.data, conn); + appendPQExpBufferChar(buf, '\n'); + } + } + else + { + /* No schema pattern given, so select only visible objects */ + if (visibilityrule) + { + WHEREAND(); + appendPQExpBuffer(buf, "%s\n", visibilityrule); + } + } + + termPQExpBuffer(&schemabuf); + termPQExpBuffer(&namebuf); + + return added_clause; +#undef WHEREAND +} diff --git a/src/include/Makefile b/src/include/Makefile index 314d3b03f5e..cad8951f97d 100644 --- a/src/include/Makefile +++ b/src/include/Makefile @@ -16,8 +16,9 @@ include $(top_builddir)/src/Makefile.global all: pg_config.h pg_config_ext.h pg_config_os.h -# Subdirectories containing headers for server-side dev -SUBDIRS = access bootstrap catalog commands common datatype executor foreign \ +# Subdirectories containing installable headers +SUBDIRS = access bootstrap catalog commands common datatype \ + executor fe_utils foreign \ lib libpq mb nodes optimizer parser postmaster regex replication \ rewrite storage tcop snowball snowball/libstemmer tsearch \ tsearch/dicts utils port port/atomics port/win32 port/win32_msvc \ diff --git a/src/include/fe_utils/simple_list.h b/src/include/fe_utils/simple_list.h new file mode 100644 index 00000000000..87d32fbac9f --- /dev/null +++ b/src/include/fe_utils/simple_list.h @@ -0,0 +1,55 @@ +/*------------------------------------------------------------------------- + * + * Simple list facilities for frontend code + * + * Data structures for simple lists of OIDs and strings. The support for + * these is very primitive compared to the backend's List facilities, but + * it's all we need in, eg, pg_dump. + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/fe_utils/simple_list.h + * + *------------------------------------------------------------------------- + */ +#ifndef SIMPLE_LIST_H +#define SIMPLE_LIST_H + +typedef struct SimpleOidListCell +{ + struct SimpleOidListCell *next; + Oid val; +} SimpleOidListCell; + +typedef struct SimpleOidList +{ + SimpleOidListCell *head; + SimpleOidListCell *tail; +} SimpleOidList; + +typedef struct SimpleStringListCell +{ + struct SimpleStringListCell *next; + bool touched; /* true, when this string was searched and + * touched */ + char val[FLEXIBLE_ARRAY_MEMBER]; /* null-terminated string here */ +} SimpleStringListCell; + +typedef struct SimpleStringList +{ + SimpleStringListCell *head; + SimpleStringListCell *tail; +} SimpleStringList; + + +extern void simple_oid_list_append(SimpleOidList *list, Oid val); +extern bool simple_oid_list_member(SimpleOidList *list, Oid val); + +extern void simple_string_list_append(SimpleStringList *list, const char *val); +extern bool simple_string_list_member(SimpleStringList *list, const char *val); + +extern const char *simple_string_list_not_touched(SimpleStringList *list); + +#endif /* SIMPLE_LIST_H */ diff --git a/src/include/fe_utils/string_utils.h b/src/include/fe_utils/string_utils.h new file mode 100644 index 00000000000..5d3fcc24271 --- /dev/null +++ b/src/include/fe_utils/string_utils.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------- + * + * String-processing utility routines for frontend code + * + * Assorted utility functions that are useful in constructing SQL queries + * and interpreting backend output. + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/fe_utils/string_utils.h + * + *------------------------------------------------------------------------- + */ +#ifndef STRING_UTILS_H +#define STRING_UTILS_H + +#include "libpq-fe.h" +#include "pqexpbuffer.h" + +#define atooid(x) ((Oid) strtoul((x), NULL, 10)) + +/* Global variables controlling behavior of fmtId() and fmtQualifiedId() */ +extern int quote_all_identifiers; +extern PQExpBuffer (*getLocalPQExpBuffer) (void); + +/* Functions */ +extern const char *fmtId(const char *identifier); +extern const char *fmtQualifiedId(int remoteVersion, + const char *schema, const char *id); + +extern void appendStringLiteral(PQExpBuffer buf, const char *str, + int encoding, bool std_strings); +extern void appendStringLiteralConn(PQExpBuffer buf, const char *str, + PGconn *conn); +extern void appendStringLiteralDQ(PQExpBuffer buf, const char *str, + const char *dqprefix); +extern void appendByteaLiteral(PQExpBuffer buf, + const unsigned char *str, size_t length, + bool std_strings); + +extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems); + +extern bool processSQLNamePattern(PGconn *conn, PQExpBuffer buf, + const char *pattern, + bool have_where, bool force_escape, + const char *schemavar, const char *namevar, + const char *altnamevar, const char *visibilityrule); + +#endif /* STRING_UTILS_H */ diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 8716642847a..f12addba02f 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -25,6 +25,7 @@ our (@ISA, @EXPORT_OK); my $solution; my $libpgport; my $libpgcommon; +my $libpgfeutils; my $postgres; my $libpq; @@ -62,7 +63,7 @@ my $frontend_extralibs = { 'psql' => ['ws2_32.lib'] }; my $frontend_extraincludes = { 'initdb' => ['src/timezone'], - 'psql' => [ 'src/bin/pg_dump', 'src/backend' ], + 'psql' => [ 'src/backend' ], 'pgbench' => [ 'src/bin/psql' ] }; my $frontend_extrasource = { 'psql' => ['src/bin/psql/psqlscan.l', 'src/bin/psql/psqlscanslash.l'], @@ -118,6 +119,9 @@ sub mkvcbuild our @pgcommonbkndfiles = @pgcommonallfiles; + our @pgfeutilsfiles = qw( + simple_list.c string_utils.c); + $libpgport = $solution->AddProject('libpgport', 'lib', 'misc'); $libpgport->AddDefine('FRONTEND'); $libpgport->AddFiles('src/port', @pgportfiles); @@ -126,6 +130,10 @@ sub mkvcbuild $libpgcommon->AddDefine('FRONTEND'); $libpgcommon->AddFiles('src/common', @pgcommonfrontendfiles); + $libpgfeutils = $solution->AddProject('libpgfeutils', 'lib', 'misc'); + $libpgfeutils->AddDefine('FRONTEND'); + $libpgfeutils->AddFiles('src/fe_utils', @pgfeutilsfiles); + $postgres = $solution->AddProject('postgres', 'exe', '', 'src/backend'); $postgres->AddIncludeDir('src/backend'); $postgres->AddDir('src/backend/port/win32'); @@ -613,11 +621,7 @@ sub mkvcbuild foreach my $f (@files) { $f =~ s/\.o$/\.c/; - if ($f eq 'dumputils.c') - { - $proj->AddFile('src/bin/pg_dump/dumputils.c'); - } - elsif ($f =~ /print\.c$/) + if ($f =~ /print\.c$/) { # Also catches mbprint.c $proj->AddFile('src/bin/psql/' . $f); } @@ -627,9 +631,9 @@ sub mkvcbuild } } $proj->AddIncludeDir('src/interfaces/libpq'); - $proj->AddIncludeDir('src/bin/pg_dump'); $proj->AddIncludeDir('src/bin/psql'); - $proj->AddReference($libpq, $libpgcommon, $libpgport); + $proj->AddReference($libpq, $libpgfeutils, $libpgcommon, + $libpgport); $proj->AddDirResourceFile('src/bin/scripts'); $proj->AddLibrary('ws2_32.lib'); } @@ -680,7 +684,7 @@ sub AddSimpleFrontend my $p = $solution->AddProject($n, 'exe', 'bin'); $p->AddDir('src/bin/' . $n); - $p->AddReference($libpgcommon, $libpgport); + $p->AddReference($libpgfeutils, $libpgcommon, $libpgport); if ($uselibpq) { $p->AddIncludeDir('src/interfaces/libpq'); |