From df1df6bb90e0dcdea47b45f8292e8fe95843c8de Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 30 May 2003 22:55:16 +0000 Subject: [PATCH] Cause pg_dumpall to include GRANT/REVOKE for database-level permissions in its output. Make it work with server versions back to 7.0, too. --- src/bin/pg_dump/dumputils.c | 308 ++++++++++++++++++++++++++++++++++- src/bin/pg_dump/dumputils.h | 15 +- src/bin/pg_dump/pg_dump.c | 275 ++----------------------------- src/bin/pg_dump/pg_dumpall.c | 96 +++++++++-- 4 files changed, 405 insertions(+), 289 deletions(-) diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c index 259a8c56395..6b8bafdb32c 100644 --- a/src/bin/pg_dump/dumputils.c +++ b/src/bin/pg_dump/dumputils.c @@ -1,12 +1,13 @@ /*------------------------------------------------------------------------- * * Utility routines for SQL dumping + * Basically this is stuff that is useful in both pg_dump and pg_dumpall. + * * * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * - * $Header: /cvsroot/pgsql/src/bin/pg_dump/dumputils.c,v 1.3 2002/09/07 16:14:33 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/dumputils.c,v 1.4 2003/05/30 22:55:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,6 +19,12 @@ #include "parser/keywords.h" +static bool parseAclItem(const char *item, const char *type, const char *name, + int remoteVersion, + PQExpBuffer grantee, PQExpBuffer grantor, + PQExpBuffer privs, PQExpBuffer privswgo); +static void AddAcl(PQExpBuffer aclbuf, const char *keyword); + /* * Quotes input string if it's not a legitimate SQL identifier as-is. @@ -89,7 +96,6 @@ fmtId(const char *rawid) } - /* * Convert a string value to an SQL string literal and append it to * the given buffer. @@ -133,7 +139,9 @@ appendStringLiteral(PQExpBuffer buf, const char *str, bool escapeAll) } - +/* + * Convert backend's version string into a number. + */ int parse_version(const char *versionString) { @@ -152,3 +160,295 @@ parse_version(const char *versionString) return (100 * vmaj + vmin) * 100 + vrev; } + + +/* + * Build GRANT/REVOKE command(s) for an object. + * + * name: the object name, in the form to use in the commands (already quoted) + * type: the object type (as seen in GRANT command: must be one of + * TABLE, FUNCTION, LANGUAGE, or SCHEMA, or DATABASE) + * acls: the ACL string fetched from the database + * owner: username of object owner (will be passed through fmtId), or NULL + * 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'. + * + * Note: beware of passing fmtId() result as 'name', since this routine + * uses fmtId() internally. + */ +bool +buildACLCommands(const char *name, const char *type, + const char *acls, const char *owner, + int remoteVersion, + PQExpBuffer sql) +{ + char *aclbuf, + *tok; + PQExpBuffer grantee, grantor, privs, privswgo; + bool found_owner_privs = false; + + if (strlen(acls) == 0) + return true; /* object has default permissions */ + + grantee = createPQExpBuffer(); + grantor = createPQExpBuffer(); + privs = createPQExpBuffer(); + privswgo = createPQExpBuffer(); + + /* + * 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. + */ + appendPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM PUBLIC;\n", + type, name); + + /* Make a working copy of acls so we can use strtok */ + aclbuf = strdup(acls); + + /* Scan comma-separated ACL items */ + for (tok = strtok(aclbuf, ","); tok != NULL; tok = strtok(NULL, ",")) + { + size_t toklen; + + /* + * Token may start with '{' and/or '"'. Actually only the start + * of the string should have '{', but we don't verify that. + */ + if (*tok == '{') + tok++; + if (*tok == '"') + tok++; + toklen = strlen(tok); + while (toklen >=0 && (tok[toklen-1] == '"' || tok[toklen-1] == '}')) + tok[toklen-- - 1] = '\0'; + + if (!parseAclItem(tok, type, name, remoteVersion, + grantee, grantor, privs, privswgo)) + return false; + + if (grantor->len == 0 && owner) + printfPQExpBuffer(grantor, "%s", owner); + + if (privs->len > 0 || privswgo->len > 0) + { + if (owner && strcmp(grantee->data, owner) == 0) + { + /* + * For the owner, the default privilege level is + * ALL WITH GRANT OPTION. + */ + found_owner_privs = true; + if (strcmp(privswgo->data, "ALL") != 0) + { + appendPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM %s;\n", + type, name, + fmtId(grantee->data)); + if (privs->len > 0) + appendPQExpBuffer(sql, "GRANT %s ON %s %s TO %s;\n", + privs->data, type, name, + fmtId(grantee->data)); + if (privswgo->len > 0) + appendPQExpBuffer(sql, "GRANT %s ON %s %s TO %s WITH GRANT OPTION;\n", + privswgo->data, type, name, + fmtId(grantee->data)); + } + } + else + { + /* + * Otherwise can assume we are starting from no privs. + */ + if (privs->len > 0) + { + appendPQExpBuffer(sql, "GRANT %s ON %s %s TO ", + privs->data, type, name); + if (grantee->len == 0) + appendPQExpBuffer(sql, "PUBLIC;\n"); + else if (strncmp(grantee->data, "group ", + strlen("group ")) == 0) + appendPQExpBuffer(sql, "GROUP %s;\n", + fmtId(grantee->data + strlen("group "))); + else + appendPQExpBuffer(sql, "%s;\n", fmtId(grantee->data)); + } + if (privswgo->len > 0) + { + appendPQExpBuffer(sql, "GRANT %s ON %s %s TO ", + privswgo->data, type, name); + if (grantee->len == 0) + appendPQExpBuffer(sql, "PUBLIC"); + else if (strncmp(grantee->data, "group ", + strlen("group ")) == 0) + appendPQExpBuffer(sql, "GROUP %s", + fmtId(grantee->data + strlen("group "))); + else + appendPQExpBuffer(sql, "%s", fmtId(grantee->data)); + appendPQExpBuffer(sql, " WITH GRANT OPTION;\n"); + } + } + } + else + { + /* No privileges. Issue explicit REVOKE for safety. */ + if (grantee->len == 0) + ; /* Empty left-hand side means "PUBLIC"; already did it */ + else if (strncmp(grantee->data, "group ", strlen("group ")) == 0) + appendPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM GROUP %s;\n", + type, name, + fmtId(grantee->data + strlen("group "))); + else + appendPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM %s;\n", + type, name, fmtId(grantee->data)); + } + } + + /* + * If we didn't find any owner privs, the owner must have revoked 'em + * all + */ + if (!found_owner_privs && owner) + { + appendPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM %s;\n", + type, name, fmtId(owner)); + } + + free(aclbuf); + destroyPQExpBuffer(grantee); + destroyPQExpBuffer(grantor); + destroyPQExpBuffer(privs); + destroyPQExpBuffer(privswgo); + + return true; +} + + +/* + * This will take an aclitem string of privilege code letters and + * parse it into grantee, grantor, and privilege information. The + * privilege information is split between privileges with grant option + * (privswgo) and without (privs). + * + * Note: for cross-version compatibility, it's important to use ALL when + * appropriate. + */ +static bool +parseAclItem(const char *item, const char *type, const char *name, + int remoteVersion, + PQExpBuffer grantee, PQExpBuffer grantor, + 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); + + /* user name is string up to = */ + eqpos = strchr(buf, '='); + if (!eqpos) + return false; + *eqpos = '\0'; + printfPQExpBuffer(grantee, "%s", buf); + + /* grantor may be listed after / */ + slpos = strchr(eqpos + 1, '/'); + if (slpos) + { + *slpos = '\0'; + printfPQExpBuffer(grantor, "%s", slpos + 1); + } + else + resetPQExpBuffer(grantor); + + /* privilege codes */ +#define CONVERT_PRIV(code, keywd) \ + if ((pos = strchr(eqpos + 1, code))) \ + { \ + if (*(pos + 1) == '*') \ + { \ + AddAcl(privswgo, keywd); \ + all_without_go = false; \ + } \ + else \ + { \ + AddAcl(privs, keywd); \ + all_with_go = false; \ + } \ + } \ + else \ + all_with_go = all_without_go = false + + resetPQExpBuffer(privs); + resetPQExpBuffer(privswgo); + + if (strcmp(type, "TABLE") == 0) + { + CONVERT_PRIV('a', "INSERT"); + CONVERT_PRIV('r', "SELECT"); + CONVERT_PRIV('R', "RULE"); + + if (remoteVersion >= 70200) + { + CONVERT_PRIV('w', "UPDATE"); + CONVERT_PRIV('d', "DELETE"); + CONVERT_PRIV('x', "REFERENCES"); + CONVERT_PRIV('t', "TRIGGER"); + } + else + { + /* 7.0 and 7.1 have a simpler worldview */ + CONVERT_PRIV('w', "UPDATE,DELETE"); + } + } + else if (strcmp(type, "FUNCTION") == 0) + 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"); + CONVERT_PRIV('T', "TEMPORARY"); + } + else + abort(); + +#undef CONVERT_PRIV + + if (all_with_go) + { + resetPQExpBuffer(privs); + printfPQExpBuffer(privswgo, "ALL"); + } + else if (all_without_go) + { + resetPQExpBuffer(privswgo); + printfPQExpBuffer(privs, "ALL"); + } + + free(buf); + + return true; +} + + +/* + * Append a privilege keyword to a keyword list, inserting comma if needed. + */ +static void +AddAcl(PQExpBuffer aclbuf, const char *keyword) +{ + if (aclbuf->len > 0) + appendPQExpBufferChar(aclbuf, ','); + appendPQExpBuffer(aclbuf, "%s", keyword); +} diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h index acaff801d88..41946af31af 100644 --- a/src/bin/pg_dump/dumputils.h +++ b/src/bin/pg_dump/dumputils.h @@ -1,12 +1,13 @@ /*------------------------------------------------------------------------- * * Utility routines for SQL dumping + * Basically this is stuff that is useful in both pg_dump and pg_dumpall. + * * * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * - * $Header: /cvsroot/pgsql/src/bin/pg_dump/dumputils.h,v 1.4 2002/09/07 16:14:33 petere Exp $ + * $Id: dumputils.h,v 1.5 2003/05/30 22:55:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -14,14 +15,18 @@ #ifndef DUMPUTILS_H #define DUMPUTILS_H -#include "postgres_fe.h" - #include "pqexpbuffer.h" + extern char *simple_prompt(const char *prompt, int maxlen, bool echo); extern const char *fmtId(const char *identifier); -extern void appendStringLiteral(PQExpBuffer buf, const char *str, bool escapeAll); +extern void appendStringLiteral(PQExpBuffer buf, const char *str, + bool escapeAll); extern int parse_version(const char *versionString); +extern bool buildACLCommands(const char *name, const char *type, + const char *acls, const char *owner, + int remoteVersion, + PQExpBuffer sql); #endif /* DUMPUTILS_H */ diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 9f5f25bfd46..90a533c6185 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12,7 +12,7 @@ * by PostgreSQL * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.330 2003/05/17 15:53:12 tgl Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.331 2003/05/30 22:55:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -116,12 +116,6 @@ static void selectSourceSchema(const char *schemaName); static char *getFormattedTypeName(const char *oid, OidOptions opts); static char *myFormatType(const char *typname, int32 typmod); static const char *fmtQualifiedId(const char *schema, const char *id); - -static void AddAcl(PQExpBuffer aclbuf, const char *keyword); -static void -parseAclItem(const char *item, const char *type, const char *name, int remoteVersion, - PQExpBuffer grantee, PQExpBuffer grantor, PQExpBuffer privs, PQExpBuffer privswgo); - static int dumpBlobs(Archive *AH, char *, void *); static int dumpDatabase(Archive *AH); static const char *getAttrName(int attrnum, TableInfo *tblInfo); @@ -2494,7 +2488,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) "a.attnotnull, a.atthasdef, false as attisdropped, null as attislocal, " "format_type(a.atttypid,a.atttypmod) as atttypname " "from pg_attribute a, pg_type t " - "where a.atttypid = t.oid" + "where a.atttypid = t.oid " "and a.attrelid = '%s'::oid " "and a.attnum > 0::int2 " "order by a.attrelid, a.attnum", @@ -4943,128 +4937,6 @@ dumpOneAgg(Archive *fout, AggInfo *agginfo) } -/* - * Append a privilege keyword to a keyword list, inserting comma if needed. - */ -static void -AddAcl(PQExpBuffer aclbuf, const char *keyword) -{ - if (aclbuf->len > 0) - appendPQExpBufferChar(aclbuf, ','); - appendPQExpBuffer(aclbuf, "%s", keyword); -} - - -/* - * This will take an aclitem string of privilege code letters and - * parse it into grantee, grantor, and privilege information. The - * privilege information is split between privileges with grant option - * (privswgo) and without (privs). - * - * Note: for cross-version compatibility, it's important to use ALL when - * appropriate. - */ -static void -parseAclItem(const char *item, const char *type, const char *name, int remoteVersion, - PQExpBuffer grantee, PQExpBuffer grantor, 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); - - /* user name is string up to = */ - eqpos = strchr(buf, '='); - if (!eqpos) - { - write_msg(NULL, "could not parse ACL list (%s) for object %s (%s)\n", - item, name, type); - exit_nicely(); - } - *eqpos = '\0'; - printfPQExpBuffer(grantee, "%s", buf); - - /* grantor may be listed after / */ - slpos = strchr(eqpos + 1, '/'); - if (slpos) - { - *slpos = '\0'; - printfPQExpBuffer(grantor, "%s", slpos + 1); - } - else - resetPQExpBuffer(grantor); - - /* privilege codes */ -#define CONVERT_PRIV(code, keywd) \ - if ((pos = strchr(eqpos + 1, code))) \ - { \ - if (*(pos + 1) == '*') \ - { \ - AddAcl(privswgo, keywd); \ - all_without_go = false; \ - } \ - else \ - { \ - AddAcl(privs, keywd); \ - all_with_go = false; \ - } \ - } \ - else \ - all_with_go = all_without_go = false - - resetPQExpBuffer(privs); - resetPQExpBuffer(privswgo); - - if (strcmp(type, "TABLE") == 0) - { - CONVERT_PRIV('a', "INSERT"); - CONVERT_PRIV('r', "SELECT"); - CONVERT_PRIV('R', "RULE"); - - if (remoteVersion >= 70200) - { - CONVERT_PRIV('w', "UPDATE"); - CONVERT_PRIV('d', "DELETE"); - CONVERT_PRIV('x', "REFERENCES"); - CONVERT_PRIV('t', "TRIGGER"); - } - else - { - /* 7.0 and 7.1 have a simpler worldview */ - CONVERT_PRIV('w', "UPDATE,DELETE"); - } - } - else if (strcmp(type, "FUNCTION") == 0) - 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 - abort(); - -#undef CONVERT_PRIV - - if (all_with_go) - { - resetPQExpBuffer(privs); - printfPQExpBuffer(privswgo, "ALL"); - } - else if (all_without_go) - { - resetPQExpBuffer(privswgo); - printfPQExpBuffer(privs, "ALL"); - } -} - - /*---------- * Write out grant/revoke information * @@ -5083,10 +4955,7 @@ dumpACL(Archive *fout, const char *type, const char *name, const char *tag, const char *nspname, const char *owner, const char *acls, const char *objoid) { - char *aclbuf, - *tok; - PQExpBuffer sql, grantee, grantor, privs, privswgo; - bool found_owner_privs = false; + PQExpBuffer sql; /* acl_lang is a flag only true if we are dumping language's ACL, * so we can set 'type' to a value that is suitable to build @@ -5098,142 +4967,22 @@ dumpACL(Archive *fout, const char *type, const char *name, acl_lang = true; } - if (strlen(acls) == 0) - return; /* object has default permissions */ - -#define MKENTRY(grantor, command) \ - ArchiveEntry(fout, objoid, tag, nspname, grantor ? grantor : "", acl_lang ? "ACL LANGUAGE" : "ACL" , NULL, command, "", NULL, NULL, NULL) - sql = createPQExpBuffer(); - grantee = createPQExpBuffer(); - grantor = createPQExpBuffer(); - privs = createPQExpBuffer(); - privswgo = createPQExpBuffer(); - /* - * 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. - */ - printfPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM PUBLIC;\n", - type, name); - MKENTRY(owner, sql->data); - - /* Make a working copy of acls so we can use strtok */ - aclbuf = strdup(acls); - - /* Scan comma-separated ACL items */ - for (tok = strtok(aclbuf, ","); tok != NULL; tok = strtok(NULL, ",")) + if (!buildACLCommands(name, type, acls, owner, fout->remoteVersion, sql)) { - size_t toklen; - - resetPQExpBuffer(sql); - - /* - * Token may start with '{' and/or '"'. Actually only the start - * of the string should have '{', but we don't verify that. - */ - if (*tok == '{') - tok++; - if (*tok == '"') - tok++; - toklen = strlen(tok); - while (toklen >=0 && (tok[toklen-1] == '"' || tok[toklen-1] == '}')) - tok[toklen-- - 1] = '\0'; - - parseAclItem(tok, type, name, fout->remoteVersion, - grantee, grantor, privs, privswgo); - if (grantor->len == 0 && owner) - printfPQExpBuffer(grantor, "%s", owner); - - if (privs->len > 0 || privswgo->len > 0) - { - if (owner && strcmp(grantee->data, owner) == 0) - { - /* - * For the owner, the default privilege level is ALL WITH GRANT OPTION. - */ - found_owner_privs = true; - if (strcmp(privswgo->data, "ALL") != 0) - { - appendPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM %s;\n", - type, name, fmtId(grantee->data)); - if (privs->len > 0) - appendPQExpBuffer(sql, "GRANT %s ON %s %s TO %s;\n", - privs->data, type, name, fmtId(grantee->data)); - if (privswgo->len > 0) - appendPQExpBuffer(sql, "GRANT %s ON %s %s TO %s WITH GRANT OPTION;\n", - privswgo->data, type, name, fmtId(grantee->data)); - } - } - else - { - /* - * Otherwise can assume we are starting from no privs. - */ - if (privs->len > 0) - { - appendPQExpBuffer(sql, "GRANT %s ON %s %s TO ", - privs->data, type, name); - if (grantee->len == 0) - appendPQExpBuffer(sql, "PUBLIC;\n"); - else if (strncmp(grantee->data, "group ", strlen("group ")) == 0) - appendPQExpBuffer(sql, "GROUP %s;\n", - fmtId(grantee->data + strlen("group "))); - else - appendPQExpBuffer(sql, "%s;\n", fmtId(grantee->data)); - } - if (privswgo->len > 0) - { - appendPQExpBuffer(sql, "GRANT %s ON %s %s TO ", - privswgo->data, type, name); - if (grantee->len == 0) - appendPQExpBuffer(sql, "PUBLIC"); - else if (strncmp(grantee->data, "group ", strlen("group ")) == 0) - appendPQExpBuffer(sql, "GROUP %s", - fmtId(grantee->data + strlen("group "))); - else - appendPQExpBuffer(sql, "%s", fmtId(grantee->data)); - appendPQExpBuffer(sql, " WITH GRANT OPTION;\n"); - } - } - } - else - { - /* No privileges. Issue explicit REVOKE for safety. */ - if (grantee->len == 0) - ; /* Empty left-hand side means "PUBLIC"; already did it */ - else if (strncmp(grantee->data, "group ", strlen("group ")) == 0) - appendPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM GROUP %s;\n", - type, name, - fmtId(grantee->data + strlen("group "))); - else - appendPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM %s;\n", - type, name, fmtId(grantee->data)); - } - - if (sql->len > 0) - MKENTRY(grantor->data, sql->data); + write_msg(NULL, "could not parse ACL list (%s) for object %s (%s)\n", + acls, name, type); + exit_nicely(); } - /* - * If we didn't find any owner privs, the owner must have revoked 'em - * all - */ - if (!found_owner_privs && owner) - { - printfPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM %s;\n", - type, name, fmtId(owner)); - MKENTRY(owner, sql->data); - } + if (sql->len > 0) + ArchiveEntry(fout, objoid, tag, nspname, + owner ? owner : "", + acl_lang ? "ACL LANGUAGE" : "ACL", + NULL, sql->data, "", NULL, NULL, NULL); - free(aclbuf); destroyPQExpBuffer(sql); - destroyPQExpBuffer(grantee); - destroyPQExpBuffer(grantor); - destroyPQExpBuffer(privs); - destroyPQExpBuffer(privswgo); -#undef MKENTRY } diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index f9db6dfdec1..7737c8fa129 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.18 2003/05/14 03:26:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.19 2003/05/30 22:55:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -248,10 +248,18 @@ dumpUsers(PGconn *conn) printf("--\n-- Users\n--\n\n"); printf("DELETE FROM pg_shadow WHERE usesysid <> (SELECT datdba FROM pg_database WHERE datname = 'template0');\n\n"); - res = executeQuery(conn, - "SELECT usename, usesysid, passwd, usecreatedb, usesuper, CAST(valuntil AS timestamp) " - "FROM pg_shadow " - "WHERE usesysid <> (SELECT datdba FROM pg_database WHERE datname = 'template0');"); + if (server_version >= 70100) + res = executeQuery(conn, + "SELECT usename, usesysid, passwd, usecreatedb, " + "usesuper, CAST(valuntil AS timestamp) " + "FROM pg_shadow " + "WHERE usesysid <> (SELECT datdba FROM pg_database WHERE datname = 'template0')"); + else + res = executeQuery(conn, + "SELECT usename, usesysid, passwd, usecreatedb, " + "usesuper, CAST(valuntil AS timestamp) " + "FROM pg_shadow " + "WHERE usesysid <> (SELECT datdba FROM pg_database WHERE datname = 'template1')"); for (i = 0; i < PQntuples(res); i++) { @@ -280,7 +288,8 @@ dumpUsers(PGconn *conn) appendPQExpBuffer(buf, " NOCREATEUSER"); if (!PQgetisnull(res, i, 5)) - appendPQExpBuffer(buf, " VALID UNTIL '%s'", PQgetvalue(res, i, 5)); + appendPQExpBuffer(buf, " VALID UNTIL '%s'", + PQgetvalue(res, i, 5)); appendPQExpBuffer(buf, ";\n"); @@ -309,7 +318,7 @@ dumpGroups(PGconn *conn) printf("--\n-- Groups\n--\n\n"); printf("DELETE FROM pg_group;\n\n"); - res = executeQuery(conn, "SELECT groname, grosysid, grolist FROM pg_group;"); + res = executeQuery(conn, "SELECT groname, grosysid, grolist FROM pg_group"); for (i = 0; i < PQntuples(res); i++) { @@ -374,11 +383,38 @@ dumpCreateDB(PGconn *conn) printf("--\n-- Database creation\n--\n\n"); - /* - * Basically this query returns: dbname, dbowner, encoding, - * istemplate, dbpath - */ - res = executeQuery(conn, "SELECT datname, coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), pg_encoding_to_char(d.encoding), datistemplate, datpath FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) WHERE datallowconn ORDER BY 1;"); + if (server_version >= 70300) + res = executeQuery(conn, + "SELECT datname, " + "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "datistemplate, datpath, datacl " + "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 70100) + res = executeQuery(conn, + "SELECT datname, " + "coalesce(" + "(select usename from pg_shadow where usesysid=datdba), " + "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "datistemplate, datpath, '' as datacl " + "FROM pg_database d " + "WHERE datallowconn ORDER BY 1"); + else + { + /* + * Note: 7.0 fails to cope with sub-select in COALESCE, so just + * deal with getting a NULL by not printing any OWNER clause. + */ + res = executeQuery(conn, + "SELECT datname, " + "(select usename from pg_shadow where usesysid=datdba), " + "pg_encoding_to_char(d.encoding), " + "'f' as datistemplate, datpath, '' as datacl " + "FROM pg_database d " + "ORDER BY 1"); + } for (i = 0; i < PQntuples(res); i++) { @@ -388,19 +424,27 @@ dumpCreateDB(PGconn *conn) char *dbencoding = PQgetvalue(res, i, 2); char *dbistemplate = PQgetvalue(res, i, 3); char *dbpath = PQgetvalue(res, i, 4); + char *dbacl = PQgetvalue(res, i, 5); + char *fdbname; if (strcmp(dbname, "template1") == 0) continue; buf = createPQExpBuffer(); + /* needed for buildACLCommands() */ + fdbname = strdup(fmtId(dbname)); + if (output_clean) - appendPQExpBuffer(buf, "DROP DATABASE %s;\n", fmtId(dbname)); + appendPQExpBuffer(buf, "DROP DATABASE %s;\n", fdbname); - appendPQExpBuffer(buf, "CREATE DATABASE %s", fmtId(dbname)); - appendPQExpBuffer(buf, " WITH OWNER = %s TEMPLATE = template0", fmtId(dbowner)); + appendPQExpBuffer(buf, "CREATE DATABASE %s", fdbname); + if (strlen(dbowner) != 0) + appendPQExpBuffer(buf, " WITH OWNER = %s", + fmtId(dbowner)); + appendPQExpBuffer(buf, " TEMPLATE = template0"); - if (strcmp(dbpath, "") != 0) + if (strlen(dbpath) != 0) { appendPQExpBuffer(buf, " LOCATION = "); appendStringLiteral(buf, dbpath, true); @@ -417,8 +461,19 @@ dumpCreateDB(PGconn *conn) appendStringLiteral(buf, dbname, true); appendPQExpBuffer(buf, ";\n"); } + + if (!buildACLCommands(fdbname, "DATABASE", dbacl, dbowner, + server_version, buf)) + { + fprintf(stderr, _("%s: could not parse ACL list (%s) for database %s\n"), + progname, dbacl, fdbname); + PQfinish(conn); + exit(1); + } + printf("%s", buf->data); destroyPQExpBuffer(buf); + free(fdbname); if (server_version >= 70300) dumpDatabaseConfig(conn, dbname); @@ -539,7 +594,11 @@ dumpDatabases(PGconn *conn) PGresult *res; int i; - res = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;"); + if (server_version >= 70100) + res = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1"); + else + res = executeQuery(conn, "SELECT datname FROM pg_database ORDER BY 1"); + for (i = 0; i < PQntuples(res); i++) { int ret; @@ -688,6 +747,9 @@ executeQuery(PGconn *conn, const char *query) { PGresult *res; + if (verbose) + fprintf(stderr, _("%s: executing %s\n"), progname, query); + res = PQexec(conn, query); if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)