include \
interfaces \
backend/replication/libpqwalreceiver \
+ fe_utils \
bin \
pl \
makefiles \
$(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
##########################################################################
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 \
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
#include <ctype.h>
#include "catalog/pg_class.h"
+#include "fe_utils/string_utils.h"
/*
}
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;
-}
/*-------------------------------------------------------------------------
*
* 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
*/
#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)
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;
-}
/*
}
-/*
- * 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
*
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;
-}
/*-------------------------------------------------------------------------
*
* 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
#include "libpq-fe.h"
#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.
#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,
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 */
#include "parallel.h"
#include "pg_backup_utils.h"
+#include "fe_utils/string_utils.h"
#ifndef WIN32
#include <sys/types.h>
#ifndef PG_BACKUP_H
#define PG_BACKUP_H
-#include "dumputils.h"
+#include "fe_utils/simple_list.h"
#include "libpq-fe.h"
#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>
#include "pg_backup_archiver.h"
#include "pg_backup_utils.h"
+#include "fe_utils/string_utils.h"
#include "libpq/libpq-fs.h"
#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>
#include "pg_backup_db.h"
#include "pg_backup_utils.h"
#include "pg_dump.h"
+#include "fe_utils/string_utils.h"
typedef struct
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,
#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"
/psqlscanslash.c
/sql_help.h
/sql_help.c
-/dumputils.c
/psql
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) $*
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
#include "libpq-fe.h"
#include "pqexpbuffer.h"
-#include "dumputils.h"
+#include "fe_utils/string_utils.h"
#include "common.h"
#include "copy.h"
#include "libpq-fe.h"
#include "pqexpbuffer.h"
-#include "dumputils.h"
#include "settings.h"
#include "common.h"
#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"
/vacuumdb
/pg_isready
-/dumputils.c
/mbprint.c
/print.c
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) $< .
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:
#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,
#include "postgres_fe.h"
#include "common.h"
-#include "dumputils.h"
+#include "fe_utils/string_utils.h"
static void help(const char *progname);
#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);
#include "postgres_fe.h"
#include "common.h"
-#include "dumputils.h"
+#include "fe_utils/string_utils.h"
static void help(const char *progname);
#include "postgres_fe.h"
#include "common.h"
-#include "dumputils.h"
+#include "fe_utils/string_utils.h"
static void help(const char *progname);
#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,
#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"
--- /dev/null
+#-------------------------------------------------------------------------
+#
+# 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)
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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
+}
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 \
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
my $solution;
my $libpgport;
my $libpgcommon;
+my $libpgfeutils;
my $postgres;
my $libpq;
'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'],
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);
$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');
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);
}
}
}
$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');
}
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');