2002-08-27 20:57:26 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Utility routines for SQL dumping
|
2003-05-31 00:55:16 +02:00
|
|
|
* Basically this is stuff that is useful in both pg_dump and pg_dumpall.
|
2006-10-10 01:30:33 +02:00
|
|
|
* Lately it's also being used by psql and bin/scripts/ ...
|
2003-05-31 00:55:16 +02:00
|
|
|
*
|
2002-08-27 20:57:26 +02:00
|
|
|
*
|
2013-01-01 23:15:01 +01:00
|
|
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
2002-08-27 20:57:26 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/bin/pg_dump/dumputils.c
|
2002-08-27 20:57:26 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres_fe.h"
|
|
|
|
|
2006-10-10 01:30:33 +02:00
|
|
|
#include <ctype.h>
|
|
|
|
|
2002-08-27 20:57:26 +02:00
|
|
|
#include "dumputils.h"
|
|
|
|
|
|
|
|
#include "parser/keywords.h"
|
|
|
|
|
|
|
|
|
2012-03-31 19:15:53 +02:00
|
|
|
/* Globals from keywords.c */
|
|
|
|
extern const ScanKeyword FEScanKeywords[];
|
|
|
|
extern const int NumFEScanKeywords;
|
|
|
|
|
2011-11-30 02:41:06 +01:00
|
|
|
/* Globals exported by this file */
|
2011-04-10 17:42:00 +02:00
|
|
|
int quote_all_identifiers = 0;
|
2011-11-30 02:41:06 +01:00
|
|
|
const char *progname = NULL;
|
2010-08-03 21:24:05 +02:00
|
|
|
|
2012-02-16 17:49:20 +01:00
|
|
|
#define MAX_ON_EXIT_NICELY 20
|
|
|
|
|
|
|
|
static struct
|
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
on_exit_nicely_callback function;
|
2012-02-16 17:49:20 +01:00
|
|
|
void *arg;
|
2012-06-10 21:20:04 +02:00
|
|
|
} on_exit_nicely_list[MAX_ON_EXIT_NICELY];
|
2012-02-16 17:49:20 +01:00
|
|
|
|
2012-06-10 21:20:04 +02:00
|
|
|
static int on_exit_nicely_index;
|
2010-08-03 21:24:05 +02:00
|
|
|
|
2003-07-31 19:21:57 +02:00
|
|
|
#define supports_grant_options(version) ((version) >= 70400)
|
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
static bool parseAclItem(const char *item, const char *type,
|
|
|
|
const char *name, const char *subname, int remoteVersion,
|
|
|
|
PQExpBuffer grantee, PQExpBuffer grantor,
|
2003-08-04 02:43:34 +02:00
|
|
|
PQExpBuffer privs, PQExpBuffer privswgo);
|
2003-07-31 19:21:57 +02:00
|
|
|
static char *copyAclUserName(PQExpBuffer output, char *input);
|
2009-01-22 21:16:10 +01:00
|
|
|
static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
|
2009-06-11 16:49:15 +02:00
|
|
|
const char *subname);
|
2003-05-31 00:55:16 +02:00
|
|
|
|
2009-03-11 04:33:29 +01:00
|
|
|
#ifdef WIN32
|
|
|
|
static bool parallel_init_done = false;
|
|
|
|
static DWORD tls_index;
|
2012-03-20 22:38:11 +01:00
|
|
|
static DWORD mainThreadId;
|
2009-03-11 04:33:29 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
void
|
|
|
|
init_parallel_dump_utils(void)
|
|
|
|
{
|
|
|
|
#ifdef WIN32
|
2009-06-11 16:49:15 +02:00
|
|
|
if (!parallel_init_done)
|
2009-03-11 04:33:29 +01:00
|
|
|
{
|
|
|
|
tls_index = TlsAlloc();
|
|
|
|
parallel_init_done = true;
|
2012-03-20 22:38:11 +01:00
|
|
|
mainThreadId = GetCurrentThreadId();
|
2009-03-11 04:33:29 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2002-08-27 20:57:26 +02:00
|
|
|
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Quotes input string if it's not a legitimate SQL identifier as-is.
|
2002-08-27 20:57:26 +02:00
|
|
|
*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Note that the returned string must be used before calling fmtId again,
|
|
|
|
* since we re-use the same return buffer each time. Non-reentrant but
|
|
|
|
* reduces memory leakage. (On Windows the memory leakage will be one buffer
|
|
|
|
* per thread, which is at least better than one per call).
|
2002-08-27 20:57:26 +02:00
|
|
|
*/
|
|
|
|
const char *
|
|
|
|
fmtId(const char *rawid)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
/*
|
2009-03-11 04:33:29 +01:00
|
|
|
* The Tls code goes awry if we use a static var, so we provide for both
|
|
|
|
* static and auto, and omit any use of the static var when using Tls.
|
|
|
|
*/
|
|
|
|
static PQExpBuffer s_id_return = NULL;
|
|
|
|
PQExpBuffer id_return;
|
|
|
|
|
2002-08-27 20:57:26 +02:00
|
|
|
const char *cp;
|
2002-09-04 22:31:48 +02:00
|
|
|
bool need_quotes = false;
|
2002-08-27 20:57:26 +02:00
|
|
|
|
2009-03-11 04:33:29 +01:00
|
|
|
#ifdef WIN32
|
|
|
|
if (parallel_init_done)
|
2009-06-11 16:49:15 +02:00
|
|
|
id_return = (PQExpBuffer) TlsGetValue(tls_index); /* 0 when not set */
|
2009-03-11 04:33:29 +01:00
|
|
|
else
|
|
|
|
id_return = s_id_return;
|
|
|
|
#else
|
|
|
|
id_return = s_id_return;
|
|
|
|
#endif
|
|
|
|
|
2002-08-27 20:57:26 +02:00
|
|
|
if (id_return) /* first time through? */
|
2009-03-11 04:33:29 +01:00
|
|
|
{
|
|
|
|
/* same buffer, just wipe contents */
|
2002-08-27 20:57:26 +02:00
|
|
|
resetPQExpBuffer(id_return);
|
2009-03-11 04:33:29 +01:00
|
|
|
}
|
2002-08-27 20:57:26 +02:00
|
|
|
else
|
2009-03-11 04:33:29 +01:00
|
|
|
{
|
|
|
|
/* new buffer */
|
2002-08-27 20:57:26 +02:00
|
|
|
id_return = createPQExpBuffer();
|
2009-06-11 16:49:15 +02:00
|
|
|
#ifdef WIN32
|
2009-03-11 04:33:29 +01:00
|
|
|
if (parallel_init_done)
|
2009-06-11 16:49:15 +02:00
|
|
|
TlsSetValue(tls_index, id_return);
|
2009-03-11 04:33:29 +01:00
|
|
|
else
|
|
|
|
s_id_return = id_return;
|
|
|
|
#else
|
|
|
|
s_id_return = id_return;
|
|
|
|
#endif
|
2009-06-11 16:49:15 +02:00
|
|
|
|
2009-03-11 04:33:29 +01:00
|
|
|
}
|
2002-08-27 20:57:26 +02:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* These checks need to match the identifier production in scan.l. Don't
|
|
|
|
* use islower() etc.
|
2002-09-04 22:31:48 +02:00
|
|
|
*/
|
2010-07-22 03:22:35 +02:00
|
|
|
if (quote_all_identifiers)
|
|
|
|
need_quotes = true;
|
2002-08-27 20:57:26 +02:00
|
|
|
/* slightly different rules for first character */
|
2010-07-22 03:22:35 +02:00
|
|
|
else if (!((rawid[0] >= 'a' && rawid[0] <= 'z') || rawid[0] == '_'))
|
2002-08-27 20:57:26 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-18 23:40:58 +02:00
|
|
|
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.
|
|
|
|
*/
|
2009-07-14 22:24:10 +02:00
|
|
|
const ScanKeyword *keyword = ScanKeywordLookup(rawid,
|
2012-03-31 19:15:53 +02:00
|
|
|
FEScanKeywords,
|
|
|
|
NumFEScanKeywords);
|
2007-06-18 23:40:58 +02:00
|
|
|
|
|
|
|
if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD)
|
|
|
|
need_quotes = true;
|
|
|
|
}
|
|
|
|
|
2002-08-27 20:57:26 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert a string value to an SQL string literal and append it to
|
2006-05-28 23:13:54 +02:00
|
|
|
* the given buffer. We assume the specified client_encoding and
|
|
|
|
* standard_conforming_strings settings.
|
2002-08-27 20:57:26 +02:00
|
|
|
*
|
2006-05-28 23:13:54 +02:00
|
|
|
* This is essentially equivalent to libpq's PQescapeStringInternal,
|
2006-10-04 02:30:14 +02:00
|
|
|
* except for the output buffer structure. We need it in situations
|
2006-05-28 23:13:54 +02:00
|
|
|
* where we do not have a PGconn available. Where we do,
|
|
|
|
* appendStringLiteralConn is a better choice.
|
2002-08-27 20:57:26 +02:00
|
|
|
*/
|
|
|
|
void
|
2006-05-28 23:13:54 +02:00
|
|
|
appendStringLiteral(PQExpBuffer buf, const char *str,
|
|
|
|
int encoding, bool std_strings)
|
2002-08-27 20:57:26 +02:00
|
|
|
{
|
2006-05-28 23:13:54 +02:00
|
|
|
size_t length = strlen(str);
|
|
|
|
const char *source = str;
|
|
|
|
char *target;
|
2005-07-01 23:03:25 +02:00
|
|
|
|
2006-05-28 23:13:54 +02:00
|
|
|
if (!enlargePQExpBuffer(buf, 2 * length + 2))
|
|
|
|
return;
|
|
|
|
|
|
|
|
target = buf->data + buf->len;
|
|
|
|
*target++ = '\'';
|
|
|
|
|
|
|
|
while (*source != '\0')
|
2005-07-01 23:03:25 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char c = *source;
|
|
|
|
int len;
|
|
|
|
int i;
|
2006-05-27 01:48:54 +02:00
|
|
|
|
2006-05-28 23:13:54 +02:00
|
|
|
/* Fast path for plain ASCII */
|
|
|
|
if (!IS_HIGHBIT_SET(c))
|
2005-07-01 23:03:25 +02:00
|
|
|
{
|
2006-05-28 23:13:54 +02:00
|
|
|
/* Apply quoting if needed */
|
|
|
|
if (SQL_STR_DOUBLE(c, !std_strings))
|
|
|
|
*target++ = c;
|
|
|
|
/* Copy the character */
|
|
|
|
*target++ = c;
|
|
|
|
source++;
|
|
|
|
continue;
|
2005-07-01 23:03:25 +02:00
|
|
|
}
|
2005-10-15 04:49:52 +02:00
|
|
|
|
2006-05-28 23:13:54 +02:00
|
|
|
/* Slow path for possible multibyte characters */
|
|
|
|
len = PQmblen(source, encoding);
|
|
|
|
|
|
|
|
/* Copy the character */
|
|
|
|
for (i = 0; i < len; i++)
|
2002-08-27 20:57:26 +02:00
|
|
|
{
|
2006-05-28 23:13:54 +02:00
|
|
|
if (*source == '\0')
|
|
|
|
break;
|
|
|
|
*target++ = *source++;
|
2002-08-27 20:57:26 +02:00
|
|
|
}
|
2006-05-28 23:13:54 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we hit premature end of string (ie, incomplete multibyte
|
2006-10-04 02:30:14 +02:00
|
|
|
* 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
|
2006-05-28 23:13:54 +02:00
|
|
|
* multibyte character). This should be enough to make a string that
|
|
|
|
* the server will error out on.
|
|
|
|
*/
|
|
|
|
if (i < len)
|
2002-08-27 20:57:26 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char *stop = buf->data + buf->maxlen - 2;
|
2006-05-28 23:13:54 +02:00
|
|
|
|
|
|
|
for (; i < len; i++)
|
|
|
|
{
|
|
|
|
if (target >= stop)
|
|
|
|
break;
|
|
|
|
*target++ = ' ';
|
|
|
|
}
|
|
|
|
break;
|
2002-08-27 20:57:26 +02:00
|
|
|
}
|
|
|
|
}
|
2006-05-28 23:13:54 +02:00
|
|
|
|
|
|
|
/* 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)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
size_t length = strlen(str);
|
2006-05-28 23:13:54 +02:00
|
|
|
|
2006-06-01 02:15:36 +02:00
|
|
|
/*
|
|
|
|
* 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 */
|
2006-10-04 02:30:14 +02:00
|
|
|
if (buf->len > 0 && buf->data[buf->len - 1] != ' ')
|
2006-06-01 02:15:36 +02:00
|
|
|
appendPQExpBufferChar(buf, ' ');
|
|
|
|
appendPQExpBufferChar(buf, ESCAPE_STRING_SYNTAX);
|
|
|
|
appendStringLiteral(buf, str, PQclientEncoding(conn), false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* XXX end kluge */
|
|
|
|
|
2006-05-28 23:13:54 +02:00
|
|
|
if (!enlargePQExpBuffer(buf, 2 * length + 2))
|
|
|
|
return;
|
|
|
|
appendPQExpBufferChar(buf, '\'');
|
|
|
|
buf->len += PQescapeStringConn(conn, buf->data + buf->len,
|
|
|
|
str, length, NULL);
|
2002-08-27 20:57:26 +02:00
|
|
|
appendPQExpBufferChar(buf, '\'');
|
|
|
|
}
|
2002-09-07 18:14:33 +02:00
|
|
|
|
|
|
|
|
2004-03-23 23:06:08 +01:00
|
|
|
/*
|
|
|
|
* Convert a string value to a dollar quoted literal and append it to
|
2004-06-18 08:14:31 +02:00
|
|
|
* the given buffer. If the dqprefix parameter is not NULL then the
|
2004-03-23 23:06:08 +01:00
|
|
|
* dollar quote delimiter will begin with that (after the opening $).
|
|
|
|
*
|
|
|
|
* No escaping is done at all on str, in compliance with the rules
|
2006-05-28 23:13:54 +02:00
|
|
|
* for parsing dollar quoted strings. Also, we need not worry about
|
|
|
|
* encoding issues.
|
2004-03-23 23:06:08 +01:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix)
|
|
|
|
{
|
|
|
|
static const char suffixes[] = "_XXXXXXX";
|
2004-08-29 07:07:03 +02:00
|
|
|
int nextchar = 0;
|
2004-03-23 23:06:08 +01:00
|
|
|
PQExpBuffer delimBuf = createPQExpBuffer();
|
|
|
|
|
|
|
|
/* start with $ + dqprefix if not NULL */
|
|
|
|
appendPQExpBufferChar(delimBuf, '$');
|
|
|
|
if (dqprefix)
|
2005-04-30 10:08:51 +02:00
|
|
|
appendPQExpBufferStr(delimBuf, dqprefix);
|
2004-03-23 23:06:08 +01:00
|
|
|
|
2004-06-18 08:14:31 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* 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$.
|
2004-03-23 23:06:08 +01:00
|
|
|
*/
|
|
|
|
while (strstr(str, delimBuf->data) != NULL)
|
|
|
|
{
|
|
|
|
appendPQExpBufferChar(delimBuf, suffixes[nextchar++]);
|
2004-08-29 07:07:03 +02:00
|
|
|
nextchar %= sizeof(suffixes) - 1;
|
2004-03-23 23:06:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* add trailing $ */
|
|
|
|
appendPQExpBufferChar(delimBuf, '$');
|
|
|
|
|
|
|
|
/* quote it and we are all done */
|
|
|
|
appendPQExpBufferStr(buf, delimBuf->data);
|
|
|
|
appendPQExpBufferStr(buf, str);
|
|
|
|
appendPQExpBufferStr(buf, delimBuf->data);
|
|
|
|
|
|
|
|
destroyPQExpBuffer(delimBuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-04 23:56:09 +02:00
|
|
|
/*
|
|
|
|
* 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";
|
|
|
|
|
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* 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.
|
2009-08-04 23:56:09 +02:00
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-05-31 00:55:16 +02:00
|
|
|
/*
|
|
|
|
* Convert backend's version string into a number.
|
|
|
|
*/
|
2002-09-07 18:14:33 +02:00
|
|
|
int
|
|
|
|
parse_version(const char *versionString)
|
|
|
|
{
|
|
|
|
int cnt;
|
|
|
|
int vmaj,
|
|
|
|
vmin,
|
|
|
|
vrev;
|
|
|
|
|
|
|
|
cnt = sscanf(versionString, "%d.%d.%d", &vmaj, &vmin, &vrev);
|
|
|
|
|
|
|
|
if (cnt < 2)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (cnt == 2)
|
|
|
|
vrev = 0;
|
|
|
|
|
|
|
|
return (100 * vmaj + vmin) * 100 + vrev;
|
|
|
|
}
|
2003-05-31 00:55:16 +02:00
|
|
|
|
|
|
|
|
2004-01-07 01:44:21 +01:00
|
|
|
/*
|
|
|
|
* 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
|
2005-10-15 04:49:52 +02:00
|
|
|
* either raw data, or surrounded by double quotes (in which case embedded
|
|
|
|
* characters including backslashes and quotes are backslashed).
|
2004-01-07 01:44:21 +01:00
|
|
|
*
|
2005-11-22 19:17:34 +01:00
|
|
|
* 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 "{,,,,,,,,,,}").
|
2004-01-07 01:44:21 +01:00
|
|
|
*/
|
|
|
|
*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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-05-31 00:55:16 +02:00
|
|
|
/*
|
|
|
|
* Build GRANT/REVOKE command(s) for an object.
|
|
|
|
*
|
|
|
|
* name: the object name, in the form to use in the commands (already quoted)
|
2009-01-22 21:16:10 +01:00
|
|
|
* subname: the sub-object name, if any (already quoted); NULL if none
|
2003-05-31 00:55:16 +02:00
|
|
|
* type: the object type (as seen in GRANT command: must be one of
|
2010-02-18 02:29:10 +01:00
|
|
|
* TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
|
|
|
|
* FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT)
|
2003-05-31 00:55:16 +02:00
|
|
|
* acls: the ACL string fetched from the database
|
2005-12-03 22:06:18 +01:00
|
|
|
* owner: username of object owner (will be passed through fmtId); can be
|
|
|
|
* NULL or empty string to indicate "no owner known"
|
2009-10-05 21:24:49 +02:00
|
|
|
* prefix: string to prefix to each generated command; typically empty
|
2003-05-31 00:55:16 +02:00
|
|
|
* remoteVersion: version of database
|
|
|
|
*
|
|
|
|
* Returns TRUE if okay, FALSE if could not parse the acl string.
|
|
|
|
* The resulting commands (if any) are appended to the contents of 'sql'.
|
|
|
|
*
|
2009-10-05 21:24:49 +02:00
|
|
|
* Note: when processing a default ACL, prefix is "ALTER DEFAULT PRIVILEGES "
|
|
|
|
* or something similar, and name is an empty string.
|
|
|
|
*
|
2009-01-22 21:16:10 +01:00
|
|
|
* Note: beware of passing a fmtId() result directly as 'name' or 'subname',
|
|
|
|
* since this routine uses fmtId() internally.
|
2003-05-31 00:55:16 +02:00
|
|
|
*/
|
|
|
|
bool
|
2009-01-22 21:16:10 +01:00
|
|
|
buildACLCommands(const char *name, const char *subname,
|
|
|
|
const char *type, const char *acls, const char *owner,
|
2009-10-05 21:24:49 +02:00
|
|
|
const char *prefix, int remoteVersion,
|
2003-05-31 00:55:16 +02:00
|
|
|
PQExpBuffer sql)
|
|
|
|
{
|
2003-07-31 19:21:57 +02:00
|
|
|
char **aclitems;
|
|
|
|
int naclitems;
|
|
|
|
int i;
|
2003-08-04 02:43:34 +02:00
|
|
|
PQExpBuffer grantee,
|
|
|
|
grantor,
|
|
|
|
privs,
|
|
|
|
privswgo;
|
|
|
|
PQExpBuffer firstsql,
|
|
|
|
secondsql;
|
2003-05-31 00:55:16 +02:00
|
|
|
bool found_owner_privs = false;
|
|
|
|
|
|
|
|
if (strlen(acls) == 0)
|
|
|
|
return true; /* object has default permissions */
|
|
|
|
|
2005-12-03 22:06:18 +01:00
|
|
|
/* treat empty-string owner same as NULL */
|
|
|
|
if (owner && *owner == '\0')
|
|
|
|
owner = NULL;
|
|
|
|
|
2004-01-07 01:44:21 +01:00
|
|
|
if (!parsePGArray(acls, &aclitems, &naclitems))
|
2003-07-31 19:21:57 +02:00
|
|
|
{
|
|
|
|
if (aclitems)
|
|
|
|
free(aclitems);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2003-05-31 00:55:16 +02:00
|
|
|
grantee = createPQExpBuffer();
|
|
|
|
grantor = createPQExpBuffer();
|
|
|
|
privs = createPQExpBuffer();
|
|
|
|
privswgo = createPQExpBuffer();
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-07-24 17:52:53 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* At the end, these two will be pasted together to form the result. But
|
|
|
|
* the owner privileges need to go before the other ones to keep the
|
|
|
|
* dependencies valid. In recent versions this is normally the case, but
|
|
|
|
* in old versions they come after the PUBLIC privileges and that results
|
|
|
|
* in problems if we need to run REVOKE on the owner privileges.
|
2003-07-24 17:52:53 +02:00
|
|
|
*/
|
|
|
|
firstsql = createPQExpBuffer();
|
|
|
|
secondsql = createPQExpBuffer();
|
2003-05-31 00:55:16 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Always start with REVOKE ALL FROM PUBLIC, so that we don't have to
|
|
|
|
* wire-in knowledge about the default public privileges for different
|
|
|
|
* kinds of objects.
|
|
|
|
*/
|
2009-10-05 21:24:49 +02:00
|
|
|
appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
|
2009-01-22 21:16:10 +01:00
|
|
|
if (subname)
|
|
|
|
appendPQExpBuffer(firstsql, "(%s)", subname);
|
|
|
|
appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name);
|
2003-05-31 00:55:16 +02:00
|
|
|
|
2007-01-04 18:49:37 +01:00
|
|
|
/*
|
|
|
|
* We still need some hacking though to cover the case where new default
|
|
|
|
* public privileges are added in new versions: the REVOKE ALL will revoke
|
|
|
|
* them, leading to behavior different from what the old version had,
|
2007-11-15 22:14:46 +01:00
|
|
|
* which is generally not what's wanted. So add back default privs if the
|
|
|
|
* source database is too old to have had that particular priv.
|
2007-01-04 18:49:37 +01:00
|
|
|
*/
|
|
|
|
if (remoteVersion < 80200 && strcmp(type, "DATABASE") == 0)
|
|
|
|
{
|
|
|
|
/* database CONNECT priv didn't exist before 8.2 */
|
2009-10-05 21:24:49 +02:00
|
|
|
appendPQExpBuffer(firstsql, "%sGRANT CONNECT ON %s %s TO PUBLIC;\n",
|
|
|
|
prefix, type, name);
|
2007-01-04 18:49:37 +01:00
|
|
|
}
|
|
|
|
|
2003-07-31 19:21:57 +02:00
|
|
|
/* Scan individual ACL items */
|
|
|
|
for (i = 0; i < naclitems; i++)
|
2003-05-31 00:55:16 +02:00
|
|
|
{
|
2009-01-22 21:16:10 +01:00
|
|
|
if (!parseAclItem(aclitems[i], type, name, subname, remoteVersion,
|
2003-05-31 00:55:16 +02:00
|
|
|
grantee, grantor, privs, privswgo))
|
2012-06-03 17:52:52 +02:00
|
|
|
{
|
|
|
|
free(aclitems);
|
2003-05-31 00:55:16 +02:00
|
|
|
return false;
|
2012-06-03 17:52:52 +02:00
|
|
|
}
|
2003-05-31 00:55:16 +02:00
|
|
|
|
|
|
|
if (grantor->len == 0 && owner)
|
|
|
|
printfPQExpBuffer(grantor, "%s", owner);
|
|
|
|
|
|
|
|
if (privs->len > 0 || privswgo->len > 0)
|
|
|
|
{
|
2003-07-24 17:52:53 +02:00
|
|
|
if (owner
|
|
|
|
&& strcmp(grantee->data, owner) == 0
|
|
|
|
&& strcmp(grantor->data, owner) == 0)
|
2003-05-31 00:55:16 +02:00
|
|
|
{
|
2003-07-24 17:52:53 +02:00
|
|
|
found_owner_privs = true;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-05-31 00:55:16 +02:00
|
|
|
/*
|
2003-08-04 02:43:34 +02:00
|
|
|
* For the owner, the default privilege level is ALL WITH
|
|
|
|
* GRANT OPTION (only ALL prior to 7.4).
|
2003-05-31 00:55:16 +02:00
|
|
|
*/
|
2003-07-24 17:52:53 +02:00
|
|
|
if (supports_grant_options(remoteVersion)
|
|
|
|
? strcmp(privswgo->data, "ALL") != 0
|
|
|
|
: strcmp(privs->data, "ALL") != 0)
|
2003-05-31 00:55:16 +02:00
|
|
|
{
|
2009-10-05 21:24:49 +02:00
|
|
|
appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
|
2009-01-22 21:16:10 +01:00
|
|
|
if (subname)
|
|
|
|
appendPQExpBuffer(firstsql, "(%s)", subname);
|
|
|
|
appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
|
|
|
|
type, name, fmtId(grantee->data));
|
2003-05-31 00:55:16 +02:00
|
|
|
if (privs->len > 0)
|
2009-01-22 21:16:10 +01:00
|
|
|
appendPQExpBuffer(firstsql,
|
2009-10-05 21:24:49 +02:00
|
|
|
"%sGRANT %s ON %s %s TO %s;\n",
|
|
|
|
prefix, privs->data, type, name,
|
2003-05-31 00:55:16 +02:00
|
|
|
fmtId(grantee->data));
|
|
|
|
if (privswgo->len > 0)
|
2009-01-22 21:16:10 +01:00
|
|
|
appendPQExpBuffer(firstsql,
|
2010-02-26 03:01:40 +01:00
|
|
|
"%sGRANT %s ON %s %s TO %s WITH GRANT OPTION;\n",
|
2009-10-05 21:24:49 +02:00
|
|
|
prefix, privswgo->data, type, name,
|
2003-05-31 00:55:16 +02:00
|
|
|
fmtId(grantee->data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Otherwise can assume we are starting from no privs.
|
|
|
|
*/
|
2003-07-24 17:52:53 +02:00
|
|
|
if (grantor->len > 0
|
|
|
|
&& (!owner || strcmp(owner, grantor->data) != 0))
|
|
|
|
appendPQExpBuffer(secondsql, "SET SESSION AUTHORIZATION %s;\n",
|
|
|
|
fmtId(grantor->data));
|
|
|
|
|
2003-05-31 00:55:16 +02:00
|
|
|
if (privs->len > 0)
|
|
|
|
{
|
2009-10-05 21:24:49 +02:00
|
|
|
appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
|
|
|
|
prefix, privs->data, type, name);
|
2003-05-31 00:55:16 +02:00
|
|
|
if (grantee->len == 0)
|
2003-07-24 17:52:53 +02:00
|
|
|
appendPQExpBuffer(secondsql, "PUBLIC;\n");
|
2003-05-31 00:55:16 +02:00
|
|
|
else if (strncmp(grantee->data, "group ",
|
|
|
|
strlen("group ")) == 0)
|
2003-07-24 17:52:53 +02:00
|
|
|
appendPQExpBuffer(secondsql, "GROUP %s;\n",
|
2005-10-15 04:49:52 +02:00
|
|
|
fmtId(grantee->data + strlen("group ")));
|
2003-05-31 00:55:16 +02:00
|
|
|
else
|
2003-07-24 17:52:53 +02:00
|
|
|
appendPQExpBuffer(secondsql, "%s;\n", fmtId(grantee->data));
|
2003-05-31 00:55:16 +02:00
|
|
|
}
|
|
|
|
if (privswgo->len > 0)
|
|
|
|
{
|
2009-10-05 21:24:49 +02:00
|
|
|
appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
|
|
|
|
prefix, privswgo->data, type, name);
|
2003-05-31 00:55:16 +02:00
|
|
|
if (grantee->len == 0)
|
2003-07-24 17:52:53 +02:00
|
|
|
appendPQExpBuffer(secondsql, "PUBLIC");
|
2003-05-31 00:55:16 +02:00
|
|
|
else if (strncmp(grantee->data, "group ",
|
|
|
|
strlen("group ")) == 0)
|
2003-07-24 17:52:53 +02:00
|
|
|
appendPQExpBuffer(secondsql, "GROUP %s",
|
2005-10-15 04:49:52 +02:00
|
|
|
fmtId(grantee->data + strlen("group ")));
|
2003-05-31 00:55:16 +02:00
|
|
|
else
|
2003-07-24 17:52:53 +02:00
|
|
|
appendPQExpBuffer(secondsql, "%s", fmtId(grantee->data));
|
|
|
|
appendPQExpBuffer(secondsql, " WITH GRANT OPTION;\n");
|
2003-05-31 00:55:16 +02:00
|
|
|
}
|
2003-07-24 17:52:53 +02:00
|
|
|
|
|
|
|
if (grantor->len > 0
|
|
|
|
&& (!owner || strcmp(owner, grantor->data) != 0))
|
|
|
|
appendPQExpBuffer(secondsql, "RESET SESSION AUTHORIZATION;\n");
|
2003-05-31 00:55:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* If we didn't find any owner privs, the owner must have revoked 'em all
|
2003-05-31 00:55:16 +02:00
|
|
|
*/
|
|
|
|
if (!found_owner_privs && owner)
|
2009-01-22 21:16:10 +01:00
|
|
|
{
|
2009-10-05 21:24:49 +02:00
|
|
|
appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
|
2009-01-22 21:16:10 +01:00
|
|
|
if (subname)
|
|
|
|
appendPQExpBuffer(firstsql, "(%s)", subname);
|
|
|
|
appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
|
2003-05-31 00:55:16 +02:00
|
|
|
type, name, fmtId(owner));
|
2009-01-22 21:16:10 +01:00
|
|
|
}
|
2003-05-31 00:55:16 +02:00
|
|
|
|
|
|
|
destroyPQExpBuffer(grantee);
|
|
|
|
destroyPQExpBuffer(grantor);
|
|
|
|
destroyPQExpBuffer(privs);
|
|
|
|
destroyPQExpBuffer(privswgo);
|
|
|
|
|
2003-07-24 17:52:53 +02:00
|
|
|
appendPQExpBuffer(sql, "%s%s", firstsql->data, secondsql->data);
|
|
|
|
destroyPQExpBuffer(firstsql);
|
|
|
|
destroyPQExpBuffer(secondsql);
|
|
|
|
|
2003-07-31 19:21:57 +02:00
|
|
|
free(aclitems);
|
|
|
|
|
2003-05-31 00:55:16 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-10-05 21:24:49 +02:00
|
|
|
/*
|
|
|
|
* Build ALTER DEFAULT PRIVILEGES command(s) for single pg_default_acl entry.
|
|
|
|
*
|
2009-10-13 01:41:45 +02:00
|
|
|
* type: the object type (TABLES, FUNCTIONS, etc)
|
2009-10-05 21:24:49 +02:00
|
|
|
* nspname: schema name, or NULL for global default privileges
|
|
|
|
* acls: the ACL string fetched from the database
|
|
|
|
* owner: username of privileges owner (will be passed through fmtId)
|
|
|
|
* remoteVersion: version of database
|
|
|
|
*
|
|
|
|
* Returns TRUE if okay, FALSE if could not parse the acl string.
|
|
|
|
* The resulting commands (if any) are appended to the contents of 'sql'.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
buildDefaultACLCommands(const char *type, const char *nspname,
|
|
|
|
const char *acls, const char *owner,
|
|
|
|
int remoteVersion,
|
|
|
|
PQExpBuffer sql)
|
|
|
|
{
|
|
|
|
bool result;
|
|
|
|
PQExpBuffer prefix;
|
|
|
|
|
|
|
|
prefix = createPQExpBuffer();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We incorporate the target role directly into the command, rather than
|
2010-02-26 03:01:40 +01:00
|
|
|
* playing around with SET ROLE or anything like that. This is so that a
|
|
|
|
* permissions error leads to nothing happening, rather than changing
|
|
|
|
* default privileges for the wrong user.
|
2009-10-05 21:24:49 +02:00
|
|
|
*/
|
|
|
|
appendPQExpBuffer(prefix, "ALTER DEFAULT PRIVILEGES FOR ROLE %s ",
|
|
|
|
fmtId(owner));
|
|
|
|
if (nspname)
|
|
|
|
appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname));
|
|
|
|
|
|
|
|
result = buildACLCommands("", NULL,
|
|
|
|
type, acls, owner,
|
|
|
|
prefix->data, remoteVersion,
|
|
|
|
sql);
|
|
|
|
|
|
|
|
destroyPQExpBuffer(prefix);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2003-05-31 00:55:16 +02:00
|
|
|
/*
|
2003-07-31 19:21:57 +02:00
|
|
|
* This will parse an aclitem string, having the general form
|
|
|
|
* username=privilegecodes/grantor
|
|
|
|
* or
|
|
|
|
* group groupname=privilegecodes/grantor
|
|
|
|
* (the /grantor part will not be present if pre-7.4 database).
|
|
|
|
*
|
|
|
|
* The returned grantee string will be the dequoted username or groupname
|
|
|
|
* (preceded with "group " in the latter case). The returned grantor is
|
2003-08-04 02:43:34 +02:00
|
|
|
* the dequoted grantor name or empty. Privilege characters are decoded
|
2003-07-31 19:21:57 +02:00
|
|
|
* and split between privileges with grant option (privswgo) and without
|
|
|
|
* (privs).
|
2003-05-31 00:55:16 +02:00
|
|
|
*
|
|
|
|
* Note: for cross-version compatibility, it's important to use ALL when
|
|
|
|
* appropriate.
|
|
|
|
*/
|
|
|
|
static bool
|
2009-01-22 21:16:10 +01:00
|
|
|
parseAclItem(const char *item, const char *type,
|
|
|
|
const char *name, const char *subname, int remoteVersion,
|
|
|
|
PQExpBuffer grantee, PQExpBuffer grantor,
|
2003-05-31 00:55:16 +02:00
|
|
|
PQExpBuffer privs, PQExpBuffer privswgo)
|
|
|
|
{
|
|
|
|
char *buf;
|
|
|
|
bool all_with_go = true;
|
|
|
|
bool all_without_go = true;
|
|
|
|
char *eqpos;
|
|
|
|
char *slpos;
|
|
|
|
char *pos;
|
|
|
|
|
|
|
|
buf = strdup(item);
|
2006-10-10 01:30:33 +02:00
|
|
|
if (!buf)
|
|
|
|
return false;
|
2003-05-31 00:55:16 +02:00
|
|
|
|
2003-07-31 19:21:57 +02:00
|
|
|
/* user or group name is string up to = */
|
|
|
|
eqpos = copyAclUserName(grantee, buf);
|
|
|
|
if (*eqpos != '=')
|
2012-06-03 17:52:52 +02:00
|
|
|
{
|
|
|
|
free(buf);
|
2003-05-31 00:55:16 +02:00
|
|
|
return false;
|
2012-06-03 17:52:52 +02:00
|
|
|
}
|
2003-05-31 00:55:16 +02:00
|
|
|
|
|
|
|
/* grantor may be listed after / */
|
|
|
|
slpos = strchr(eqpos + 1, '/');
|
|
|
|
if (slpos)
|
|
|
|
{
|
2003-07-31 19:21:57 +02:00
|
|
|
*slpos++ = '\0';
|
|
|
|
slpos = copyAclUserName(grantor, slpos);
|
|
|
|
if (*slpos != '\0')
|
2012-06-03 17:52:52 +02:00
|
|
|
{
|
|
|
|
free(buf);
|
2003-07-31 19:21:57 +02:00
|
|
|
return false;
|
2012-06-03 17:52:52 +02:00
|
|
|
}
|
2003-05-31 00:55:16 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
resetPQExpBuffer(grantor);
|
|
|
|
|
|
|
|
/* privilege codes */
|
|
|
|
#define CONVERT_PRIV(code, keywd) \
|
2006-01-21 03:16:21 +01:00
|
|
|
do { \
|
2003-05-31 00:55:16 +02:00
|
|
|
if ((pos = strchr(eqpos + 1, code))) \
|
|
|
|
{ \
|
|
|
|
if (*(pos + 1) == '*') \
|
|
|
|
{ \
|
2009-01-22 21:16:10 +01:00
|
|
|
AddAcl(privswgo, keywd, subname); \
|
2003-05-31 00:55:16 +02:00
|
|
|
all_without_go = false; \
|
|
|
|
} \
|
|
|
|
else \
|
|
|
|
{ \
|
2009-01-22 21:16:10 +01:00
|
|
|
AddAcl(privs, keywd, subname); \
|
2003-05-31 00:55:16 +02:00
|
|
|
all_with_go = false; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
else \
|
2006-01-21 03:16:21 +01:00
|
|
|
all_with_go = all_without_go = false; \
|
|
|
|
} while (0)
|
2003-05-31 00:55:16 +02:00
|
|
|
|
|
|
|
resetPQExpBuffer(privs);
|
|
|
|
resetPQExpBuffer(privswgo);
|
|
|
|
|
2009-10-13 01:41:45 +02:00
|
|
|
if (strcmp(type, "TABLE") == 0 || strcmp(type, "SEQUENCE") == 0 ||
|
|
|
|
strcmp(type, "TABLES") == 0 || strcmp(type, "SEQUENCES") == 0)
|
2003-05-31 00:55:16 +02:00
|
|
|
{
|
|
|
|
CONVERT_PRIV('r', "SELECT");
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2009-10-13 01:41:45 +02:00
|
|
|
if (strcmp(type, "SEQUENCE") == 0 ||
|
|
|
|
strcmp(type, "SEQUENCES") == 0)
|
2006-01-21 03:16:21 +01:00
|
|
|
/* sequence only */
|
|
|
|
CONVERT_PRIV('U', "USAGE");
|
|
|
|
else
|
2003-05-31 00:55:16 +02:00
|
|
|
{
|
2006-01-21 03:16:21 +01:00
|
|
|
/* table only */
|
|
|
|
CONVERT_PRIV('a', "INSERT");
|
|
|
|
if (remoteVersion >= 70200)
|
|
|
|
CONVERT_PRIV('x', "REFERENCES");
|
2009-01-22 21:16:10 +01:00
|
|
|
/* rest are not applicable to columns */
|
|
|
|
if (subname == NULL)
|
|
|
|
{
|
|
|
|
if (remoteVersion >= 70200)
|
|
|
|
{
|
|
|
|
CONVERT_PRIV('d', "DELETE");
|
|
|
|
CONVERT_PRIV('t', "TRIGGER");
|
|
|
|
}
|
|
|
|
if (remoteVersion >= 80400)
|
|
|
|
CONVERT_PRIV('D', "TRUNCATE");
|
2006-01-21 03:16:21 +01:00
|
|
|
}
|
2003-05-31 00:55:16 +02:00
|
|
|
}
|
2006-01-21 03:16:21 +01:00
|
|
|
|
|
|
|
/* UPDATE */
|
2009-10-13 01:41:45 +02:00
|
|
|
if (remoteVersion >= 70200 ||
|
|
|
|
strcmp(type, "SEQUENCE") == 0 ||
|
|
|
|
strcmp(type, "SEQUENCES") == 0)
|
2006-01-21 03:16:21 +01:00
|
|
|
CONVERT_PRIV('w', "UPDATE");
|
2003-05-31 00:55:16 +02:00
|
|
|
else
|
|
|
|
/* 7.0 and 7.1 have a simpler worldview */
|
|
|
|
CONVERT_PRIV('w', "UPDATE,DELETE");
|
|
|
|
}
|
2009-10-13 01:41:45 +02:00
|
|
|
else if (strcmp(type, "FUNCTION") == 0 ||
|
|
|
|
strcmp(type, "FUNCTIONS") == 0)
|
2003-05-31 00:55:16 +02:00
|
|
|
CONVERT_PRIV('X', "EXECUTE");
|
|
|
|
else if (strcmp(type, "LANGUAGE") == 0)
|
|
|
|
CONVERT_PRIV('U', "USAGE");
|
|
|
|
else if (strcmp(type, "SCHEMA") == 0)
|
|
|
|
{
|
|
|
|
CONVERT_PRIV('C', "CREATE");
|
|
|
|
CONVERT_PRIV('U', "USAGE");
|
|
|
|
}
|
|
|
|
else if (strcmp(type, "DATABASE") == 0)
|
|
|
|
{
|
|
|
|
CONVERT_PRIV('C', "CREATE");
|
2006-04-30 23:15:33 +02:00
|
|
|
CONVERT_PRIV('c', "CONNECT");
|
2003-05-31 00:55:16 +02:00
|
|
|
CONVERT_PRIV('T', "TEMPORARY");
|
|
|
|
}
|
2004-06-18 08:14:31 +02:00
|
|
|
else if (strcmp(type, "TABLESPACE") == 0)
|
|
|
|
CONVERT_PRIV('C', "CREATE");
|
2012-12-09 06:08:23 +01:00
|
|
|
else if (strcmp(type, "TYPE") == 0 ||
|
|
|
|
strcmp(type, "TYPES") == 0)
|
|
|
|
CONVERT_PRIV('U', "USAGE");
|
2008-12-19 17:25:19 +01:00
|
|
|
else if (strcmp(type, "FOREIGN DATA WRAPPER") == 0)
|
|
|
|
CONVERT_PRIV('U', "USAGE");
|
2010-03-03 21:10:48 +01:00
|
|
|
else if (strcmp(type, "FOREIGN SERVER") == 0)
|
2008-12-19 17:25:19 +01:00
|
|
|
CONVERT_PRIV('U', "USAGE");
|
2011-01-02 05:48:11 +01:00
|
|
|
else if (strcmp(type, "FOREIGN TABLE") == 0)
|
|
|
|
CONVERT_PRIV('r', "SELECT");
|
2009-12-11 04:34:57 +01:00
|
|
|
else if (strcmp(type, "LARGE OBJECT") == 0)
|
|
|
|
{
|
|
|
|
CONVERT_PRIV('r', "SELECT");
|
|
|
|
CONVERT_PRIV('w', "UPDATE");
|
|
|
|
}
|
2003-05-31 00:55:16 +02:00
|
|
|
else
|
|
|
|
abort();
|
|
|
|
|
|
|
|
#undef CONVERT_PRIV
|
|
|
|
|
|
|
|
if (all_with_go)
|
|
|
|
{
|
|
|
|
resetPQExpBuffer(privs);
|
|
|
|
printfPQExpBuffer(privswgo, "ALL");
|
2009-01-22 21:16:10 +01:00
|
|
|
if (subname)
|
|
|
|
appendPQExpBuffer(privswgo, "(%s)", subname);
|
2003-05-31 00:55:16 +02:00
|
|
|
}
|
|
|
|
else if (all_without_go)
|
|
|
|
{
|
|
|
|
resetPQExpBuffer(privswgo);
|
|
|
|
printfPQExpBuffer(privs, "ALL");
|
2009-01-22 21:16:10 +01:00
|
|
|
if (subname)
|
|
|
|
appendPQExpBuffer(privs, "(%s)", subname);
|
2003-05-31 00:55:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
free(buf);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2003-07-31 19:21:57 +02:00
|
|
|
/*
|
|
|
|
* Transfer a user or group name starting at *input into the output buffer,
|
|
|
|
* dequoting if needed. Returns a pointer to just past the input name.
|
|
|
|
* The name is taken to end at an unquoted '=' or end of string.
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
copyAclUserName(PQExpBuffer output, char *input)
|
|
|
|
{
|
|
|
|
resetPQExpBuffer(output);
|
2003-08-14 16:19:11 +02:00
|
|
|
|
2003-07-31 19:21:57 +02:00
|
|
|
while (*input && *input != '=')
|
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* If user name isn't quoted, then just add it to the output buffer
|
2004-08-29 07:07:03 +02:00
|
|
|
*/
|
2003-07-31 19:21:57 +02:00
|
|
|
if (*input != '"')
|
|
|
|
appendPQExpBufferChar(output, *input++);
|
|
|
|
else
|
|
|
|
{
|
2004-06-18 08:14:31 +02:00
|
|
|
/* Otherwise, it's a quoted username */
|
2003-07-31 19:21:57 +02:00
|
|
|
input++;
|
2003-08-14 16:19:11 +02:00
|
|
|
/* Loop until we come across an unescaped quote */
|
|
|
|
while (!(*input == '"' && *(input + 1) != '"'))
|
2003-07-31 19:21:57 +02:00
|
|
|
{
|
|
|
|
if (*input == '\0')
|
2003-08-04 02:43:34 +02:00
|
|
|
return input; /* really a syntax error... */
|
|
|
|
|
2003-07-31 19:21:57 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Quoting convention is to escape " as "". Keep this code in
|
|
|
|
* sync with putid() in backend's acl.c.
|
2003-07-31 19:21:57 +02:00
|
|
|
*/
|
2003-08-14 16:19:11 +02:00
|
|
|
if (*input == '"' && *(input + 1) == '"')
|
|
|
|
input++;
|
2003-07-31 19:21:57 +02:00
|
|
|
appendPQExpBufferChar(output, *input++);
|
|
|
|
}
|
|
|
|
input++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return input;
|
|
|
|
}
|
2003-05-31 00:55:16 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Append a privilege keyword to a keyword list, inserting comma if needed.
|
|
|
|
*/
|
|
|
|
static void
|
2009-01-22 21:16:10 +01:00
|
|
|
AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname)
|
2003-05-31 00:55:16 +02:00
|
|
|
{
|
|
|
|
if (aclbuf->len > 0)
|
|
|
|
appendPQExpBufferChar(aclbuf, ',');
|
|
|
|
appendPQExpBuffer(aclbuf, "%s", keyword);
|
2009-01-22 21:16:10 +01:00
|
|
|
if (subname)
|
|
|
|
appendPQExpBuffer(aclbuf, "(%s)", subname);
|
2003-05-31 00:55:16 +02:00
|
|
|
}
|
2006-10-10 01:30:33 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* processSQLNamePattern
|
|
|
|
*
|
|
|
|
* Scan a wildcard-pattern string and generate appropriate WHERE clauses
|
|
|
|
* to limit the set of objects returned. The WHERE clauses are appended
|
2009-10-08 00:14:26 +02:00
|
|
|
* to the already-partially-constructed query in buf. Returns whether
|
|
|
|
* any clause was added.
|
2006-10-10 01:30:33 +02:00
|
|
|
*
|
|
|
|
* 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.
|
2007-11-07 13:24:24 +01:00
|
|
|
* altnamevar: NULL, or name of an alternative variable to match against name.
|
2006-10-10 01:30:33 +02:00
|
|
|
* 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.
|
|
|
|
*/
|
2009-10-08 00:14:26 +02:00
|
|
|
bool
|
2006-10-10 01:30:33 +02:00
|
|
|
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;
|
2009-10-08 00:14:26 +02:00
|
|
|
bool added_clause = false;
|
2006-10-10 01:30:33 +02:00
|
|
|
|
|
|
|
#define WHEREAND() \
|
2009-10-08 00:14:26 +02:00
|
|
|
(appendPQExpBufferStr(buf, have_where ? " AND " : "WHERE "), \
|
|
|
|
have_where = true, added_clause = true)
|
2006-10-10 01:30:33 +02:00
|
|
|
|
|
|
|
if (pattern == NULL)
|
|
|
|
{
|
|
|
|
/* Default: select all visible objects */
|
|
|
|
if (visibilityrule)
|
|
|
|
{
|
|
|
|
WHEREAND();
|
|
|
|
appendPQExpBuffer(buf, "%s\n", visibilityrule);
|
|
|
|
}
|
2009-10-08 00:14:26 +02:00
|
|
|
return added_clause;
|
2006-10-10 01:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
|
|
*
|
2007-11-15 22:14:46 +01:00
|
|
|
* 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().
|
2006-10-10 01:30:33 +02:00
|
|
|
*/
|
|
|
|
appendPQExpBufferStr(&namebuf, "^(");
|
|
|
|
|
|
|
|
inquotes = false;
|
|
|
|
cp = pattern;
|
|
|
|
|
|
|
|
while (*cp)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
char ch = *cp;
|
2006-10-10 01:30:33 +02:00
|
|
|
|
|
|
|
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++;
|
|
|
|
}
|
2007-07-10 02:21:31 +02:00
|
|
|
else if (ch == '$')
|
|
|
|
{
|
|
|
|
/*
|
2007-11-15 22:14:46 +01:00
|
|
|
* 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.
|
2007-07-10 02:21:31 +02:00
|
|
|
*/
|
|
|
|
appendPQExpBufferStr(&namebuf, "\\$");
|
|
|
|
cp++;
|
|
|
|
}
|
2006-10-10 01:30:33 +02:00
|
|
|
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++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2007-11-15 22:14:46 +01:00
|
|
|
* Now decide what we need to emit. Note there will be a leading "^(" in
|
|
|
|
* the patterns in any case.
|
2006-10-10 01:30:33 +02:00
|
|
|
*/
|
|
|
|
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);
|
|
|
|
|
2009-10-08 00:14:26 +02:00
|
|
|
return added_clause;
|
2006-10-10 01:30:33 +02:00
|
|
|
#undef WHEREAND
|
|
|
|
}
|
2011-07-20 19:18:24 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* buildShSecLabelQuery
|
|
|
|
*
|
|
|
|
* Build a query to retrieve security labels for a shared object.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
buildShSecLabelQuery(PGconn *conn, const char *catalog_name, uint32 objectId,
|
|
|
|
PQExpBuffer sql)
|
|
|
|
{
|
|
|
|
appendPQExpBuffer(sql,
|
|
|
|
"SELECT provider, label FROM pg_catalog.pg_shseclabel "
|
|
|
|
"WHERE classoid = '%s'::pg_catalog.regclass AND "
|
|
|
|
"objoid = %u", catalog_name, objectId);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* emitShSecLabels
|
|
|
|
*
|
|
|
|
* Format security label data retrieved by the query generated in
|
|
|
|
* buildShSecLabelQuery.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer,
|
|
|
|
const char *target, const char *objname)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < PQntuples(res); i++)
|
2012-06-10 21:20:04 +02:00
|
|
|
{
|
|
|
|
char *provider = PQgetvalue(res, i, 0);
|
|
|
|
char *label = PQgetvalue(res, i, 1);
|
2011-07-20 19:18:24 +02:00
|
|
|
|
|
|
|
/* must use fmtId result before calling it again */
|
|
|
|
appendPQExpBuffer(buffer,
|
|
|
|
"SECURITY LABEL FOR %s ON %s",
|
|
|
|
fmtId(provider), target);
|
|
|
|
appendPQExpBuffer(buffer,
|
|
|
|
" %s IS ",
|
|
|
|
fmtId(objname));
|
|
|
|
appendStringLiteralConn(buffer, label, conn);
|
2012-06-10 21:20:04 +02:00
|
|
|
appendPQExpBuffer(buffer, ";\n");
|
2011-07-20 19:18:24 +02:00
|
|
|
}
|
|
|
|
}
|
2011-11-29 22:34:45 +01:00
|
|
|
|
|
|
|
|
Rewrite --section option to decouple it from --schema-only/--data-only.
The initial implementation of pg_dump's --section option supposed that the
existing --schema-only and --data-only options could be made equivalent to
--section settings. This is wrong, though, due to dubious but long since
set-in-stone decisions about where to dump SEQUENCE SET items, as seen in
bug report from Martin Pitt. (And I'm not totally convinced there weren't
other bugs, either.) Undo that coupling and instead drive --section
filtering off current-section state tracked as we scan through the TOC
list to call _tocEntryRequired().
To make sure those decisions don't shift around and hopefully save a few
cycles, run _tocEntryRequired() only once per TOC entry and save the result
in a new TOC field. This required minor rejiggering of ACL handling but
also allows a far cleaner implementation of inhibit_data_for_failed_table.
Also, to ensure that pg_dump and pg_restore have the same behavior with
respect to the --section switches, add _tocEntryRequired() filtering to
WriteToc() and WriteDataChunks(), rather than trying to implement section
filtering in an entirely orthogonal way in dumpDumpableObject(). This
required adjusting the handling of the special ENCODING and STDSTRINGS
items, but they were pretty weird before anyway.
Minor other code review for the patch, too.
2012-05-30 05:22:14 +02:00
|
|
|
/*
|
|
|
|
* Parse a --section=foo command line argument.
|
|
|
|
*
|
|
|
|
* Set or update the bitmask in *dumpSections according to arg.
|
|
|
|
* dumpSections is initialised as DUMP_UNSECTIONED by pg_dump and
|
|
|
|
* pg_restore so they can know if this has even been called.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_dump_section(const char *arg, int *dumpSections)
|
|
|
|
{
|
|
|
|
/* if this is the first call, clear all the bits */
|
|
|
|
if (*dumpSections == DUMP_UNSECTIONED)
|
|
|
|
*dumpSections = 0;
|
|
|
|
|
2012-06-10 21:20:04 +02:00
|
|
|
if (strcmp(arg, "pre-data") == 0)
|
Rewrite --section option to decouple it from --schema-only/--data-only.
The initial implementation of pg_dump's --section option supposed that the
existing --schema-only and --data-only options could be made equivalent to
--section settings. This is wrong, though, due to dubious but long since
set-in-stone decisions about where to dump SEQUENCE SET items, as seen in
bug report from Martin Pitt. (And I'm not totally convinced there weren't
other bugs, either.) Undo that coupling and instead drive --section
filtering off current-section state tracked as we scan through the TOC
list to call _tocEntryRequired().
To make sure those decisions don't shift around and hopefully save a few
cycles, run _tocEntryRequired() only once per TOC entry and save the result
in a new TOC field. This required minor rejiggering of ACL handling but
also allows a far cleaner implementation of inhibit_data_for_failed_table.
Also, to ensure that pg_dump and pg_restore have the same behavior with
respect to the --section switches, add _tocEntryRequired() filtering to
WriteToc() and WriteDataChunks(), rather than trying to implement section
filtering in an entirely orthogonal way in dumpDumpableObject(). This
required adjusting the handling of the special ENCODING and STDSTRINGS
items, but they were pretty weird before anyway.
Minor other code review for the patch, too.
2012-05-30 05:22:14 +02:00
|
|
|
*dumpSections |= DUMP_PRE_DATA;
|
2012-06-10 21:20:04 +02:00
|
|
|
else if (strcmp(arg, "data") == 0)
|
Rewrite --section option to decouple it from --schema-only/--data-only.
The initial implementation of pg_dump's --section option supposed that the
existing --schema-only and --data-only options could be made equivalent to
--section settings. This is wrong, though, due to dubious but long since
set-in-stone decisions about where to dump SEQUENCE SET items, as seen in
bug report from Martin Pitt. (And I'm not totally convinced there weren't
other bugs, either.) Undo that coupling and instead drive --section
filtering off current-section state tracked as we scan through the TOC
list to call _tocEntryRequired().
To make sure those decisions don't shift around and hopefully save a few
cycles, run _tocEntryRequired() only once per TOC entry and save the result
in a new TOC field. This required minor rejiggering of ACL handling but
also allows a far cleaner implementation of inhibit_data_for_failed_table.
Also, to ensure that pg_dump and pg_restore have the same behavior with
respect to the --section switches, add _tocEntryRequired() filtering to
WriteToc() and WriteDataChunks(), rather than trying to implement section
filtering in an entirely orthogonal way in dumpDumpableObject(). This
required adjusting the handling of the special ENCODING and STDSTRINGS
items, but they were pretty weird before anyway.
Minor other code review for the patch, too.
2012-05-30 05:22:14 +02:00
|
|
|
*dumpSections |= DUMP_DATA;
|
2012-06-10 21:20:04 +02:00
|
|
|
else if (strcmp(arg, "post-data") == 0)
|
Rewrite --section option to decouple it from --schema-only/--data-only.
The initial implementation of pg_dump's --section option supposed that the
existing --schema-only and --data-only options could be made equivalent to
--section settings. This is wrong, though, due to dubious but long since
set-in-stone decisions about where to dump SEQUENCE SET items, as seen in
bug report from Martin Pitt. (And I'm not totally convinced there weren't
other bugs, either.) Undo that coupling and instead drive --section
filtering off current-section state tracked as we scan through the TOC
list to call _tocEntryRequired().
To make sure those decisions don't shift around and hopefully save a few
cycles, run _tocEntryRequired() only once per TOC entry and save the result
in a new TOC field. This required minor rejiggering of ACL handling but
also allows a far cleaner implementation of inhibit_data_for_failed_table.
Also, to ensure that pg_dump and pg_restore have the same behavior with
respect to the --section switches, add _tocEntryRequired() filtering to
WriteToc() and WriteDataChunks(), rather than trying to implement section
filtering in an entirely orthogonal way in dumpDumpableObject(). This
required adjusting the handling of the special ENCODING and STDSTRINGS
items, but they were pretty weird before anyway.
Minor other code review for the patch, too.
2012-05-30 05:22:14 +02:00
|
|
|
*dumpSections |= DUMP_POST_DATA;
|
|
|
|
else
|
|
|
|
{
|
2012-06-07 22:35:33 +02:00
|
|
|
fprintf(stderr, _("%s: unrecognized section name: \"%s\"\n"),
|
Rewrite --section option to decouple it from --schema-only/--data-only.
The initial implementation of pg_dump's --section option supposed that the
existing --schema-only and --data-only options could be made equivalent to
--section settings. This is wrong, though, due to dubious but long since
set-in-stone decisions about where to dump SEQUENCE SET items, as seen in
bug report from Martin Pitt. (And I'm not totally convinced there weren't
other bugs, either.) Undo that coupling and instead drive --section
filtering off current-section state tracked as we scan through the TOC
list to call _tocEntryRequired().
To make sure those decisions don't shift around and hopefully save a few
cycles, run _tocEntryRequired() only once per TOC entry and save the result
in a new TOC field. This required minor rejiggering of ACL handling but
also allows a far cleaner implementation of inhibit_data_for_failed_table.
Also, to ensure that pg_dump and pg_restore have the same behavior with
respect to the --section switches, add _tocEntryRequired() filtering to
WriteToc() and WriteDataChunks(), rather than trying to implement section
filtering in an entirely orthogonal way in dumpDumpableObject(). This
required adjusting the handling of the special ENCODING and STDSTRINGS
items, but they were pretty weird before anyway.
Minor other code review for the patch, too.
2012-05-30 05:22:14 +02:00
|
|
|
progname, arg);
|
|
|
|
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
|
|
|
|
progname);
|
|
|
|
exit_nicely(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-11-30 02:41:06 +01:00
|
|
|
/*
|
|
|
|
* Write a printf-style message to stderr.
|
|
|
|
*
|
|
|
|
* The program name is prepended, if "progname" has been set.
|
|
|
|
* Also, if modulename isn't NULL, that's included too.
|
|
|
|
* Note that we'll try to translate the modulename and the fmt string.
|
|
|
|
*/
|
2011-11-29 22:34:45 +01:00
|
|
|
void
|
|
|
|
write_msg(const char *modulename, const char *fmt,...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
2011-11-30 02:41:06 +01:00
|
|
|
vwrite_msg(modulename, fmt, ap);
|
2011-11-29 22:34:45 +01:00
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2011-11-30 02:41:06 +01:00
|
|
|
/*
|
|
|
|
* As write_msg, but pass a va_list not variable arguments.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
vwrite_msg(const char *modulename, const char *fmt, va_list ap)
|
|
|
|
{
|
|
|
|
if (progname)
|
|
|
|
{
|
|
|
|
if (modulename)
|
|
|
|
fprintf(stderr, "%s: [%s] ", progname, _(modulename));
|
|
|
|
else
|
|
|
|
fprintf(stderr, "%s: ", progname);
|
|
|
|
}
|
|
|
|
vfprintf(stderr, _(fmt), ap);
|
|
|
|
}
|
|
|
|
|
2011-11-29 22:34:45 +01:00
|
|
|
|
2011-11-30 02:41:06 +01:00
|
|
|
/*
|
2012-06-10 21:20:04 +02:00
|
|
|
* Fail and die, with a message to stderr. Parameters as for write_msg.
|
2011-11-30 02:41:06 +01:00
|
|
|
*/
|
2011-11-29 22:34:45 +01:00
|
|
|
void
|
|
|
|
exit_horribly(const char *modulename, const char *fmt,...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
2011-11-30 02:41:06 +01:00
|
|
|
vwrite_msg(modulename, fmt, ap);
|
2011-11-29 22:34:45 +01:00
|
|
|
va_end(ap);
|
|
|
|
|
2012-02-16 17:49:20 +01:00
|
|
|
exit_nicely(1);
|
2011-11-29 22:34:45 +01:00
|
|
|
}
|
2011-12-17 01:09:38 +01:00
|
|
|
|
2012-02-16 17:49:20 +01:00
|
|
|
/* Register a callback to be run when exit_nicely is invoked. */
|
|
|
|
void
|
|
|
|
on_exit_nicely(on_exit_nicely_callback function, void *arg)
|
|
|
|
{
|
|
|
|
if (on_exit_nicely_index >= MAX_ON_EXIT_NICELY)
|
2012-06-18 22:57:00 +02:00
|
|
|
exit_horribly(NULL, "out of on_exit_nicely slots\n");
|
2012-02-16 17:49:20 +01:00
|
|
|
on_exit_nicely_list[on_exit_nicely_index].function = function;
|
|
|
|
on_exit_nicely_list[on_exit_nicely_index].arg = arg;
|
|
|
|
on_exit_nicely_index++;
|
|
|
|
}
|
|
|
|
|
2012-04-03 14:38:24 +02:00
|
|
|
/*
|
|
|
|
* Run accumulated on_exit_nicely callbacks in reverse order and then exit
|
|
|
|
* quietly. This needs to be thread-safe.
|
|
|
|
*/
|
2012-02-16 17:49:20 +01:00
|
|
|
void
|
|
|
|
exit_nicely(int code)
|
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
int i;
|
2012-04-03 14:38:24 +02:00
|
|
|
|
|
|
|
for (i = on_exit_nicely_index - 1; i >= 0; i--)
|
2012-06-10 21:20:04 +02:00
|
|
|
(*on_exit_nicely_list[i].function) (code,
|
|
|
|
on_exit_nicely_list[i].arg);
|
2012-04-03 14:38:24 +02:00
|
|
|
|
2012-04-06 03:30:19 +02:00
|
|
|
#ifdef WIN32
|
|
|
|
if (parallel_init_done && GetCurrentThreadId() != mainThreadId)
|
|
|
|
ExitThread(code);
|
|
|
|
#endif
|
|
|
|
|
2012-02-16 17:49:20 +01:00
|
|
|
exit(code);
|
|
|
|
}
|