/* * Dump user-specific configuration */ static void dumpUserConfig(PGconn *conn, const char *username) { PQExpBuffer buf = createPQExpBuffer(); int count = 1; for (;;) { PGresult *res; if (server_version >= 80100) printfPQExpBuffer(buf, "SELECT rolconfig[%d] FROM pg_authid WHERE rolname = ", count); else printfPQExpBuffer(buf, "SELECT useconfig[%d] FROM pg_shadow WHERE usename = ", count); appendStringLiteral(buf, username, true); res = executeQuery(conn, buf->data); if (PQntuples(res) == 1 && !PQgetisnull(res, 0, 0)) { makeAlterConfigCommand(PQgetvalue(res, 0, 0), "ROLE", username); PQclear(res); count++; } else { PQclear(res); break; } } destroyPQExpBuffer(buf); }
/* * Run pg_dump on dbname. */ static int runPgDump(const char *dbname) { PQExpBuffer cmd = createPQExpBuffer(); int ret; appendPQExpBuffer(cmd, SYSTEMQUOTE "\"%s\" %s", pg_dump_bin, pgdumpopts->data); /* * If we have a filename, use the undocumented plain-append pg_dump * format. */ if (filename) appendPQExpBuffer(cmd, " -Fa "); else appendPQExpBuffer(cmd, " -Fp "); doShellQuoting(cmd, dbname); appendPQExpBuffer(cmd, "%s", SYSTEMQUOTE); if (verbose) fprintf(stderr, _("%s: running \"%s\"\n"), progname, cmd->data); fflush(stdout); fflush(stderr); ret = system(cmd->data); destroyPQExpBuffer(cmd); return ret; }
/* * Dump database-specific configuration */ static void dumpDatabaseConfig(PGconn *conn, const char *dbname) { PQExpBuffer buf = createPQExpBuffer(); int count = 1; for (;;) { PGresult *res; printfPQExpBuffer(buf, "SELECT datconfig[%d] FROM pg_database WHERE datname = ", count); appendStringLiteral(buf, dbname, true); appendPQExpBuffer(buf, ";"); res = executeQuery(conn, buf->data); if (!PQgetisnull(res, 0, 0)) { makeAlterConfigCommand(PQgetvalue(res, 0, 0), "DATABASE", dbname); PQclear(res); count++; } else { PQclear(res); break; } } destroyPQExpBuffer(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); }
/* * Helper function for dumpXXXConfig(). */ static void makeAlterConfigCommand(const char *arrayitem, const char *type, const char *name) { char *pos; char *mine; PQExpBuffer buf = createPQExpBuffer(); mine = strdup(arrayitem); pos = strchr(mine, '='); if (pos == NULL) return; *pos = 0; appendPQExpBuffer(buf, "ALTER %s %s ", type, fmtId(name)); appendPQExpBuffer(buf, "SET %s TO ", fmtId(mine)); /* * Some GUC variable names are 'LIST' type and hence must not be quoted. */ if (pg_strcasecmp(mine, "DateStyle") == 0 || pg_strcasecmp(mine, "search_path") == 0) appendPQExpBuffer(buf, "%s", pos + 1); else appendStringLiteral(buf, pos + 1, false); appendPQExpBuffer(buf, ";\n"); printf("%s", buf->data); destroyPQExpBuffer(buf); free(mine); }
/* * Dump group memberships from a pre-8.1 server. It's annoying that we * can't share any useful amount of code with the post-8.1 case, but * the catalog representations are too different. * * Note: we expect dumpRoles already created all the roles, but there is * no membership yet. */ static void dumpGroups(PGconn *conn) { PQExpBuffer buf = createPQExpBuffer(); PGresult *res; int i; res = executeQuery(conn, "SELECT groname, grolist FROM pg_group ORDER BY 1"); if (PQntuples(res) > 0) printf("--\n-- Role memberships\n--\n\n"); for (i = 0; i < PQntuples(res); i++) { char *groname = PQgetvalue(res, i, 0); char *grolist = PQgetvalue(res, i, 1); PGresult *res2; int j; /* * Array representation is {1,2,3} ... convert to (1,2,3) */ if (strlen(grolist) < 3) continue; grolist = strdup(grolist); grolist[0] = '('; grolist[strlen(grolist) - 1] = ')'; printfPQExpBuffer(buf, "SELECT usename FROM pg_shadow " "WHERE usesysid IN %s ORDER BY 1", grolist); free(grolist); res2 = executeQuery(conn, buf->data); for (j = 0; j < PQntuples(res2); j++) { char *usename = PQgetvalue(res2, j, 0); /* * Don't try to grant a role to itself; can happen if old * installation has identically named user and group. */ if (strcmp(groname, usename) == 0) continue; printf("GRANT %s", fmtId(groname)); printf(" TO %s;\n", fmtId(usename)); } PQclear(res2); } PQclear(res); destroyPQExpBuffer(buf); printf("\n\n"); }
/* * Dump user-and-database-specific configuration */ static void dumpDbRoleConfig(PGconn *conn) { PQExpBuffer buf = createPQExpBuffer(); PGresult *res; int i; printfPQExpBuffer(buf, "SELECT rolname, datname, unnest(setconfig) " "FROM pg_db_role_setting, pg_authid, pg_database " "WHERE setrole = pg_authid.oid AND setdatabase = pg_database.oid"); res = executeQuery(conn, buf->data); if (PQntuples(res) > 0) { fprintf(OPF, "--\n-- Per-Database Role Settings \n--\n\n"); for (i = 0; i < PQntuples(res); i++) { makeAlterConfigCommand(conn, PQgetvalue(res, i, 2), "ROLE", PQgetvalue(res, i, 0), "DATABASE", PQgetvalue(res, i, 1)); } fprintf(OPF, "\n\n"); } PQclear(res); destroyPQExpBuffer(buf); }
/* * This function takes a function description, e.g. "x" or "x(int)", and * issues a query on the given connection to retrieve the function's OID * using a cast to regproc or regprocedure (as appropriate). The result, * if there is one, is returned at *foid. Note that we'll fail if the * function doesn't exist OR if there are multiple matching candidates * OR if there's something syntactically wrong with the function description; * unfortunately it can be hard to tell the difference. */ static bool lookup_function_oid(PGconn *conn, const char *desc, Oid *foid) { bool result = true; PQExpBuffer query; PGresult *res; query = createPQExpBuffer(); printfPQExpBuffer(query, "SELECT "); appendStringLiteralConn(query, desc, conn); appendPQExpBuffer(query, "::pg_catalog.%s::pg_catalog.oid", strchr(desc, '(') ? "regprocedure" : "regproc"); res = PQexec(conn, query->data); if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) *foid = atooid(PQgetvalue(res, 0, 0)); else { minimal_error_message(res); result = false; } PQclear(res); destroyPQExpBuffer(query); return result; }
/* * Fetches the "CREATE OR REPLACE FUNCTION ..." command that describes the * function with the given OID. If successful, the result is stored in buf. */ static bool get_create_function_cmd(PGconn *conn, Oid oid, PQExpBuffer buf) { bool result = true; PQExpBuffer query; PGresult *res; query = createPQExpBuffer(); printfPQExpBuffer(query, "SELECT pg_catalog.pg_get_functiondef(%u)", oid); res = PQexec(conn, query->data); if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) { resetPQExpBuffer(buf); appendPQExpBufferStr(buf, PQgetvalue(res, 0, 0)); } else { minimal_error_message(res); result = false; } PQclear(res); destroyPQExpBuffer(query); return result; }
/* * Build ALTER DEFAULT PRIVILEGES command(s) for single pg_default_acl entry. * * type: the object type (TABLES, FUNCTIONS, etc) * 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 * 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. */ 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; }
/* * DoCancelNotifyListen: This function executes a LISTEN or a NOTIFY command, with * name in the format N%s_%d_%d, where the %s is replaced by the CDBDumpKey, * and the 2 integers are the contentid and dbid. */ void DoCancelNotifyListen(PGconn *pConn, bool bListen, const char *pszCDBDumpKey, int role_id, int db_id, int target_db_id, const char *pszSuffix) { PGresult *pRes; PQExpBuffer q = createPQExpBuffer(); char *pszCmd = bListen ? "LISTEN" : "NOTIFY"; appendPQExpBuffer(q, "%s N%s_%d_%d", pszCmd, pszCDBDumpKey, role_id, db_id); /* this is valid only for restore operations */ if (target_db_id != -1) appendPQExpBuffer(q, "_T%d", target_db_id); if (pszSuffix != NULL) appendPQExpBuffer(q, "_%s", pszSuffix); pRes = PQexec(pConn, q->data); if (pRes == NULL || PQresultStatus(pRes) != PGRES_COMMAND_OK) { mpp_err_msg_cache("%s command failed for for backup key %s, instid %d, segid %d failed : %s", pszCmd, pszCDBDumpKey, role_id, db_id, PQerrorMessage(pConn)); } PQclear(pRes); destroyPQExpBuffer(q); }
/* * ReadBackendBackupFile: This function calls the backend function gp_read_backup_file * which reads the contents out of the appropriate file on the database server. * If the call succeeds/fails, it returns status code 0/-1, an appropriate error string * is inserted into the buffer of pszRtn. */ int ReadBackendBackupFileError(PGconn *pConn, const char *pszBackupDirectory, const char *pszKey, BackupFileType fileType, const char *progName, PQExpBuffer pszRtn) { char *pszFileType; PQExpBuffer Qry; PGresult *pRes; int status = 0; switch (fileType) { case BFT_BACKUP: pszFileType = "0"; break; case BFT_BACKUP_STATUS: pszFileType = "1"; break; case BFT_RESTORE_STATUS: pszFileType = "2"; break; default: appendPQExpBuffer(pszRtn, "Unknown file type passed to ReadBackendBackupFile"); mpp_err_msg("ERROR", progName, " %s: %d\n", pszRtn->data, fileType); return -1; } Qry = createPQExpBuffer(); appendPQExpBuffer(Qry, "SELECT * FROM gp_read_backup_file('%s', '%s', %s)", StringNotNull(pszBackupDirectory, ""), StringNotNull(pszKey, ""), pszFileType); pRes = PQexec(pConn, Qry->data); if (!pRes || PQresultStatus(pRes) != PGRES_TUPLES_OK || PQntuples(pRes) == 0) { appendPQExpBuffer(pszRtn, "Error executing query %s : %s\n", Qry->data, PQerrorMessage(pConn)); mpp_err_msg_cache("ERROR", progName, pszRtn->data); status = -1; } else { char *res = PQgetvalue(pRes, 0, 0); appendPQExpBufferStr(pszRtn, res); if (strstr(res, "ERROR:") || strstr(res, "[ERROR]")) { status = -1; } } PQclear(pRes); destroyPQExpBuffer(Qry); return status; }
/* * Run pg_dump on dbname. */ static int runPgDump(const char *dbname) { PQExpBuffer cmd = createPQExpBuffer(); const char *p; int ret; /* * Win32 has to use double-quotes for args, rather than single quotes. * Strangely enough, this is the only place we pass a database name on the * command line, except "postgres" which doesn't need quoting. */ #ifndef WIN32 appendPQExpBuffer(cmd, "%s\"%s\" %s -Fp '", SYSTEMQUOTE, pg_dump_bin, #else appendPQExpBuffer(cmd, "%s\"%s\" %s -Fp \"", SYSTEMQUOTE, pg_dump_bin, #endif pgdumpopts->data); /* Shell quoting is not quite like SQL quoting, so can't use fmtId */ for (p = dbname; *p; p++) { #ifndef WIN32 if (*p == '\'') appendPQExpBuffer(cmd, "'\"'\"'"); #else if (*p == '"') appendPQExpBuffer(cmd, "\\\""); #endif else appendPQExpBufferChar(cmd, *p); } #ifndef WIN32 appendPQExpBufferChar(cmd, '\''); #else appendPQExpBufferChar(cmd, '"'); #endif appendPQExpBuffer(cmd, "%s", SYSTEMQUOTE); if (verbose) fprintf(stderr, _("%s: running \"%s\"\n"), progname, cmd->data); fflush(stdout); fflush(stderr); ret = system(cmd->data); destroyPQExpBuffer(cmd); return ret; }
/* * Exit closing active database connections */ static void exit_gracefuly(int status) { destroyPQExpBuffer(dbQry); if(lastDbConn) PQfinish(lastDbConn); if(conn) PQfinish(conn); close(logFd); exit(status); }
/* * Issue a query on a catalog table, and produce calls to a preassign support * function from the result set. * * The output is a string, containing SQL calls like: * * SELECT binary_upgrade.preassign_*_oid(<oid>, <other args); * * 'funcname' is the "preassign_*_oid" function to use. * 'sql' is the query to issue. The columns of the result set are passed as * arguments to the preassign-support function. * */ static void dump_rows(migratorContext *ctx, PQExpBuffer buf, FILE *file, PGconn *conn, const char *sql, const char *funcname) { int ntups; int ncols; int row; int col; PGresult *res; if (file != NULL) buf = createPQExpBuffer(); /* * Add a WHERE or AND clause to filter out built-in objects. * * If the query contains "UNION ALL", then it's the caller's * responsibility to do the filtering. This special case is for the * one more complicated query in get_old_oids() function; all the * other queries are very simple ones. */ if (strstr(sql, "WHERE ") == NULL) res = executeQueryOrDie(ctx, conn, "%s WHERE oid >= %u", sql, FirstNormalObjectId); else if (strstr(sql, "UNION ALL") == NULL) res = executeQueryOrDie(ctx, conn, "%s AND oid >= %u", sql, FirstNormalObjectId); else res = executeQueryOrDie(ctx, conn, "%s", sql); ntups = PQntuples(res); ncols = PQnfields(res); for (row = 0; row < ntups; row++) { appendPQExpBuffer(buf, "SELECT binary_upgrade.%s('%s'", funcname, simple_escape_literal(ctx, conn, PQgetvalue(res, row, 0))); for (col = 1; col < ncols; col++) appendPQExpBuffer(buf, ", '%s'", simple_escape_literal(ctx, conn, PQgetvalue(res, row, col))); appendPQExpBuffer(buf, ");\n"); if (file) { fwrite(buf->data, buf->len, 1, file); resetPQExpBuffer(buf); } } PQclear(res); if (file != NULL) destroyPQExpBuffer(buf); }
/* * Destroy a CdbDispatchResult object. */ void cdbdisp_termResult(CdbDispatchResult *dispatchResult) { PQExpBuffer trash; dispatchResult->segdbDesc = NULL; /* * Free the PGresult objects. */ cdbdisp_resetResult(dispatchResult); /* * Free the error message buffer and result buffer. */ trash = dispatchResult->resultbuf; dispatchResult->resultbuf = NULL; destroyPQExpBuffer(trash); trash = dispatchResult->error_message; dispatchResult->error_message = NULL; destroyPQExpBuffer(trash); }
/* * ReadBackendBackupFile: This function calls the backend function gp_read_backup_file * which reads the contents out of the appropriate file on the database server. * If the call fails, it returns NULL. The returned pointer must be freed by the caller. */ char * ReadBackendBackupFile(PGconn *pConn, const char *pszBackupDirectory, const char *pszKey, BackupFileType fileType, const char *progName) { char *pszRtn = NULL; char *pszFileType; PQExpBuffer Qry; PGresult *pRes; switch (fileType) { case BFT_BACKUP: pszFileType = "0"; break; case BFT_BACKUP_STATUS: pszFileType = "1"; break; case BFT_RESTORE_STATUS: pszFileType = "2"; break; default: mpp_err_msg("ERROR", progName, "Unknown file type passed to ReadBackendBackupFile : %d\n", fileType); return NULL; } Qry = createPQExpBuffer(); appendPQExpBuffer(Qry, "SELECT * FROM gp_read_backup_file('%s', '%s', %s)", StringNotNull(pszBackupDirectory, ""), StringNotNull(pszKey, ""), pszFileType); pRes = PQexec(pConn, Qry->data); if (!pRes || PQresultStatus(pRes) != PGRES_TUPLES_OK || PQntuples(pRes) == 0) { mpp_err_msg_cache("ERROR", progName, "Error executing query %s : %s\n", Qry->data, PQerrorMessage(pConn)); } else { pszRtn = strdup(PQgetvalue(pRes, 0, 0)); } PQclear(pRes); destroyPQExpBuffer(Qry); return pszRtn; }
/* * Called by dumper via archiver from within a data dump routine * We substitute this for _WriteData while emitting a BLOB */ static size_t _WriteBlobData(ArchiveHandle *AH, const void *data, size_t dLen) { if (dLen > 0) { PQExpBuffer buf = createPQExpBuffer(); appendByteaLiteralAHX(buf, (const unsigned char *) data, dLen, AH); ahprintf(AH, "SELECT pg_catalog.lowrite(0, %s);\n", buf->data); destroyPQExpBuffer(buf); } return dLen; }
/* * 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; }
/* * Build ALTER DEFAULT PRIVILEGES command(s) for single pg_default_acl entry. * * type: the object type (TABLES, FUNCTIONS, etc) * 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 *racls, const char *initacls, const char *initracls, const char *owner, int remoteVersion, PQExpBuffer sql) { PQExpBuffer prefix; prefix = createPQExpBuffer(); /* * We incorporate the target role directly into the command, rather than * 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. */ appendPQExpBuffer(prefix, "ALTER DEFAULT PRIVILEGES FOR ROLE %s ", fmtId(owner)); if (nspname) appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname)); if (strlen(initacls) != 0 || strlen(initracls) != 0) { appendPQExpBuffer(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n"); if (!buildACLCommands("", NULL, type, initacls, initracls, owner, prefix->data, remoteVersion, sql)) return false; appendPQExpBuffer(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n"); } if (!buildACLCommands("", NULL, type, acls, racls, owner, prefix->data, remoteVersion, sql)) return false; destroyPQExpBuffer(prefix); return true; }
/* * Dump database-specific configuration */ static void dumpDatabaseConfig(PGconn *conn, const char *dbname) { PQExpBuffer buf = createPQExpBuffer(); int count = 1; for (;;) { PGresult *res; if (server_version >= 90000) printfPQExpBuffer(buf, "SELECT setconfig[%d] FROM pg_db_role_setting WHERE " "setrole = 0 AND setdatabase = (SELECT oid FROM pg_database WHERE datname = ", count); else printfPQExpBuffer(buf, "SELECT datconfig[%d] FROM pg_database WHERE datname = ", count); appendStringLiteralConn(buf, dbname, conn); if (server_version >= 90000) appendPQExpBuffer(buf, ")"); appendPQExpBuffer(buf, ";"); res = executeQuery(conn, buf->data); if (PQntuples(res) == 1 && !PQgetisnull(res, 0, 0)) { makeAlterConfigCommand(conn, PQgetvalue(res, 0, 0), "DATABASE", dbname, NULL, NULL); PQclear(res); count++; } else { PQclear(res); break; } } destroyPQExpBuffer(buf); }
/* Initiates the non-blocking capture of a consistent snapshot of the database, * using the exported snapshot context->repl.snapshot_name. */ int snapshot_start(client_context_t context) { if (!context->repl.snapshot_name || context->repl.snapshot_name[0] == '\0') { client_error(context, "snapshot_name must be set in client context"); return EINVAL; } int err = 0; check(err, exec_sql(context, "BEGIN")); check(err, exec_sql(context, "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ")); PQExpBuffer query = createPQExpBuffer(); appendPQExpBuffer(query, "SET TRANSACTION SNAPSHOT '%s'", context->repl.snapshot_name); check(err, exec_sql(context, query->data)); destroyPQExpBuffer(query); Oid argtypes[] = { 25, 16 }; // 25 == TEXTOID, 16 == BOOLOID const char *args[] = { "%", context->allow_unkeyed ? "t" : "f" }; if (!PQsendQueryParams(context->sql_conn, "SELECT bottledwater_export(table_pattern := $1, allow_unkeyed := $2)", 2, argtypes, args, NULL, NULL, 1)) { // The final 1 requests results in binary format client_error(context, "Could not dispatch snapshot fetch: %s", PQerrorMessage(context->sql_conn)); return EIO; } if (!PQsetSingleRowMode(context->sql_conn)) { client_error(context, "Could not activate single-row mode"); return EIO; } // Invoke the begin-transaction callback with xid==0 to indicate start of snapshot begin_txn_cb begin_txn = context->repl.frame_reader->on_begin_txn; void *cb_context = context->repl.frame_reader->cb_context; if (begin_txn) { check(err, begin_txn(cb_context, context->repl.start_lsn, 0)); } return 0; }
/* * Report just the primary error; this is to avoid cluttering the output * with, for instance, a redisplay of the internally generated query */ static void minimal_error_message(PGresult *res) { PQExpBuffer msg; const char *fld; msg = createPQExpBuffer(); fld = PQresultErrorField(res, PG_DIAG_SEVERITY); if (fld) printfPQExpBuffer(msg, "%s: ", fld); else printfPQExpBuffer(msg, "ERROR: "); fld = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); if (fld) appendPQExpBufferStr(msg, fld); else appendPQExpBufferStr(msg, "(not available)"); appendPQExpBufferStr(msg, "\n"); psql_error("%s", msg->data); destroyPQExpBuffer(msg); }
/* * Dump commands to create each database. * * To minimize the number of reconnections (and possibly ensuing * password prompts) required by the output script, we emit all CREATE * DATABASE commands during the initial phase of the script, and then * run pg_dump for each database to dump the contents of that * database. We skip databases marked not datallowconn, since we'd be * unable to connect to them anyway (and besides, we don't want to * dump template0). */ static void dumpCreateDB(PGconn *conn) { PQExpBuffer buf = createPQExpBuffer(); PGresult *res; int i; printf("--\n-- Database creation\n--\n\n"); if (server_version >= 80100) res = executeQuery(conn, "SELECT datname, " "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "datistemplate, datacl, datconnlimit, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " "WHERE datallowconn ORDER BY 1"); else if (server_version >= 80000) 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, datacl, -1 as datconnlimit, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " "WHERE datallowconn ORDER BY 1"); else 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, datacl, -1 as datconnlimit, " "'pg_default' AS dattablespace " "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, '' as datacl, -1 as datconnlimit, " "'pg_default' AS dattablespace " "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, " "'' as datacl, -1 as datconnlimit, " "'pg_default' AS dattablespace " "FROM pg_database d " "ORDER BY 1"); } for (i = 0; i < PQntuples(res); i++) { char *dbname = PQgetvalue(res, i, 0); char *dbowner = PQgetvalue(res, i, 1); char *dbencoding = PQgetvalue(res, i, 2); char *dbistemplate = PQgetvalue(res, i, 3); char *dbacl = PQgetvalue(res, i, 4); char *dbconnlimit = PQgetvalue(res, i, 5); char *dbtablespace = PQgetvalue(res, i, 6); char *fdbname; fdbname = strdup(fmtId(dbname)); resetPQExpBuffer(buf); /* * Skip the CREATE DATABASE commands for "template1" and "postgres", * since they are presumably already there in the destination cluster. * We do want to emit their ACLs and config options if any, however. */ if (strcmp(dbname, "template1") != 0 && strcmp(dbname, "postgres") != 0) { if (output_clean) appendPQExpBuffer(buf, "DROP DATABASE %s;\n", fdbname); appendPQExpBuffer(buf, "CREATE DATABASE %s", fdbname); appendPQExpBuffer(buf, " WITH TEMPLATE = template0"); if (strlen(dbowner) != 0) appendPQExpBuffer(buf, " OWNER = %s", fmtId(dbowner)); appendPQExpBuffer(buf, " ENCODING = "); appendStringLiteral(buf, dbencoding, true); /* Output tablespace if it isn't default */ if (strcmp(dbtablespace, "pg_default") != 0) appendPQExpBuffer(buf, " TABLESPACE = %s", fmtId(dbtablespace)); if (strcmp(dbconnlimit, "-1") != 0) appendPQExpBuffer(buf, " CONNECTION LIMIT = %s", dbconnlimit); appendPQExpBuffer(buf, ";\n"); if (strcmp(dbistemplate, "t") == 0) { appendPQExpBuffer(buf, "UPDATE pg_database SET datistemplate = 't' WHERE datname = "); appendStringLiteral(buf, dbname, true); appendPQExpBuffer(buf, ";\n"); } } if (!skip_acls && !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); if (server_version >= 70300) dumpDatabaseConfig(conn, dbname); free(fdbname); } PQclear(res); destroyPQExpBuffer(buf); printf("\n\n"); }
int main(int argc, char **argv) { PQExpBuffer valueBuf = NULL; PQExpBuffer escapeBuf = createPQExpBuffer(); RestoreOptions *opts; int c; int exit_code = 0; Archive *AH; char *inputFileSpec = NULL; extern int optind; extern char *optarg; static int use_setsessauth = 0; static int disable_triggers = 0; SegmentDatabase SegDB; StatusOp *pOp; struct sigaction act; pid_t newpid; /* int i; */ PQExpBuffer pszCmdLine; int status; int rc; char *pszErrorMsg; ArchiveHandle *pAH; int postDataSchemaOnly = 0; #ifdef USE_DDBOOST char *ddp_file_name = NULL; char *dd_boost_dir = NULL; #endif struct option cmdopts[] = { {"clean", 0, NULL, 'c'}, {"create", 0, NULL, 'C'}, {"data-only", 0, NULL, 'a'}, {"dbname", 1, NULL, 'd'}, {"exit-on-error", 0, NULL, 'e'}, {"file", 1, NULL, 'f'}, {"format", 1, NULL, 'F'}, {"function", 1, NULL, 'P'}, {"host", 1, NULL, 'h'}, {"ignore-version", 0, NULL, 'i'}, {"index", 1, NULL, 'I'}, {"list", 0, NULL, 'l'}, {"no-privileges", 0, NULL, 'x'}, {"no-acl", 0, NULL, 'x'}, {"no-owner", 0, NULL, 'O'}, {"no-reconnect", 0, NULL, 'R'}, {"port", 1, NULL, 'p'}, {"password", 0, NULL, 'W'}, {"schema-only", 0, NULL, 's'}, {"superuser", 1, NULL, 'S'}, {"table", 1, NULL, 't'}, {"trigger", 1, NULL, 'T'}, {"use-list", 1, NULL, 'L'}, {"username", 1, NULL, 'U'}, {"verbose", 0, NULL, 'v'}, /* * the following options don't have an equivalent short option letter, * but are available as '-X long-name' */ {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, {"disable-triggers", no_argument, &disable_triggers, 1}, /* * the following are cdb specific, and don't have an equivalent short * option */ {"gp-d", required_argument, NULL, 1}, {"gp-e", no_argument, NULL, 2}, {"gp-k", required_argument, NULL, 3}, {"gp-c", required_argument, NULL, 4}, {"target-dbid", required_argument, NULL, 5}, {"target-host", required_argument, NULL, 6}, {"target-port", required_argument, NULL, 7}, {"post-data-schema-only", no_argument, &postDataSchemaOnly, 1}, {"dd_boost_file", required_argument, NULL, 8}, {"dd_boost_enabled", no_argument, NULL, 9}, {"dd_boost_dir", required_argument, NULL, 10}, {"dd_boost_buf_size", required_argument, NULL, 11}, {"gp-f", required_argument, NULL, 12}, {"prefix", required_argument, NULL, 13}, {"status", required_argument, NULL, 14}, {"netbackup-service-host", required_argument, NULL, 15}, {"netbackup-block-size", required_argument, NULL, 16}, {"change-schema-file", required_argument, NULL, 17}, {"schema-level-file", required_argument, NULL, 18}, {"ddboost-storage-unit",required_argument, NULL, 19}, {NULL, 0, NULL, 0} }; set_pglocale_pgservice(argv[0], "pg_dump"); opts = NewRestoreOptions(); /* set format default */ opts->formatName = "p"; progname = get_progname(argv[0]); if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { usage(progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { puts("pg_restore (Greenplum Database) " PG_VERSION); exit(0); } } while ((c = getopt_long(argc, argv, "acCd:ef:F:h:iI:lL:Op:P:RsS:t:T:uU:vwWxX:", cmdopts, NULL)) != -1) { switch (c) { case 'a': /* Dump data only */ opts->dataOnly = 1; break; case 'c': /* clean (i.e., drop) schema prior to create */ opts->dropSchema = 1; break; case 'C': opts->createDB = 1; break; case 'd': opts->dbname = strdup(optarg); break; case 'e': opts->exit_on_error = true; break; case 'f': /* output file name */ opts->filename = strdup(optarg); break; case 'F': if (strlen(optarg) != 0) opts->formatName = strdup(optarg); break; case 'h': if (strlen(optarg) != 0) opts->pghost = strdup(optarg); break; case 'i': /* obsolete option */ break; case 'l': /* Dump the TOC summary */ opts->tocSummary = 1; break; case 'L': /* input TOC summary file name */ opts->tocFile = strdup(optarg); break; case 'O': opts->noOwner = 1; break; case 'p': if (strlen(optarg) != 0) opts->pgport = strdup(optarg); break; case 'R': /* no-op, still accepted for backwards compatibility */ break; case 'P': /* Function */ opts->selTypes = 1; opts->selFunction = 1; opts->functionNames = strdup(optarg); break; case 'I': /* Index */ opts->selTypes = 1; opts->selIndex = 1; opts->indexNames = strdup(optarg); break; case 'T': /* Trigger */ opts->selTypes = 1; opts->selTrigger = 1; opts->triggerNames = strdup(optarg); break; case 's': /* dump schema only */ opts->schemaOnly = 1; break; case 'S': /* Superuser username */ if (strlen(optarg) != 0) opts->superuser = strdup(optarg); break; case 't': /* Dump data for this table only */ opts->selTypes = 1; opts->selTable = 1; opts->tableNames = strdup(optarg); break; case 'u': opts->promptPassword = TRI_YES; opts->username = simple_prompt("User name: ", 100, true); break; case 'U': opts->username = optarg; break; case 'v': /* verbose */ opts->verbose = 1; break; case 'w': opts->promptPassword = TRI_NO; break; case 'W': opts->promptPassword = TRI_YES; break; case 'x': /* skip ACL dump */ opts->aclsSkip = 1; break; case 'X': /* -X is a deprecated alternative to long options */ if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; else if (strcmp(optarg, "disable-triggers") == 0) disable_triggers = 1; else { fprintf(stderr, _("%s: invalid -X option -- %s\n"), progname, optarg); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } break; case 0: /* This covers the long options equivalent to -X xxx. */ break; case 1: /* --gp-d MPP Output Directory */ g_pszMPPOutputDirectory = strdup(optarg); break; case 2: /* --gp-e On Error Stop for psql */ g_bOnErrorStop = opts->exit_on_error = true; break; case 3: /* --gp-k MPP Dump Info Format is * Key_s-dbid_s-role_t-dbid */ g_gpdumpInfo = strdup(optarg); if (!ParseCDBDumpInfo(progname, g_gpdumpInfo, &g_gpdumpKey, &g_role, &g_sourceDBID, &g_MPPPassThroughCredentials)) { exit(1); } break; case 4: /* gp-c */ g_compPg = strdup(optarg); break; case 5: /* target-dbid */ g_targetDBID = atoi(strdup(optarg)); break; case 6: /* target-host */ g_targetHost = strdup(optarg); break; case 7: /* target-port */ g_targetPort = strdup(optarg); break; #ifdef USE_DDBOOST case 9: dd_boost_enabled = 1; break; case 10: dd_boost_dir = strdup(optarg); break; case 11: dd_boost_buf_size = strdup(optarg); break; #endif case 12: table_filter_file = strdup(optarg); break; case 13: dump_prefix = strdup(optarg); break; /* Hack to pass in the status_file name to cdbbackup.c (gp_restore_launch) */ case 14: break; case 15: netbackup_service_host = strdup(optarg); break; case 16: netbackup_block_size = strdup(optarg); break; case 17: change_schema_file = strdup(optarg); break; case 18: schema_level_file = strdup(optarg); break; #ifdef USE_DDBOOST case 19: ddboost_storage_unit = strdup(optarg); break; #endif default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } } /* Should get at most one of -d and -f, else user is confused */ if (opts->dbname) { if (opts->filename) { fprintf(stderr, _("%s: cannot specify both -d and -f output\n"), progname); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } opts->useDB = 1; } opts->disable_triggers = disable_triggers; opts->use_setsessauth = use_setsessauth; if (opts->formatName) { switch (opts->formatName[0]) { case 'c': case 'C': opts->format = archCustom; break; case 'f': case 'F': opts->format = archFiles; break; case 't': case 'T': opts->format = archTar; break; case 'p': case 'P': bUsePSQL = true; break; default: mpp_err_msg(logInfo, progname, "unrecognized archive format '%s'; please specify 't' or 'c'\n", opts->formatName); exit(1); } } if (g_gpdumpInfo == NULL) { mpp_err_msg(logInfo, progname, "missing required parameter gp-k (backup key)\n"); exit(1); } #ifdef USE_DDBOOST if (dd_boost_enabled) { /* remote is always false when restoring from primary DDR */ int err = DD_ERR_NONE; err = ddp_init("gp_dump"); if (err != DD_ERR_NONE) { mpp_err_msg("ERROR", "ddboost", "ddboost init failed. Err = %d\n", err); exit(1); } if (initDDSystem(&ddp_inst, &ddp_conn, &dd_client_info, &ddboost_storage_unit, false, &DEFAULT_BACKUP_DIRECTORY, false)) { mpp_err_msg(logInfo, progname, "Initializing DD system failed\n"); exit(1); } mpp_err_msg(logInfo, progname, "ddboost is initialized\n"); ddp_file_name = formDDBoostFileName(g_gpdumpKey, postDataSchemaOnly, dd_boost_dir); if (ddp_file_name == NULL) { mpp_err_msg(logInfo, progname, "Error in opening ddboost file\n"); exit(1); } } #endif SegDB.dbid = g_sourceDBID; SegDB.role = g_role; SegDB.port = opts->pgport ? atoi(opts->pgport) : 5432; SegDB.pszHost = opts->pghost ? strdup(opts->pghost) : NULL; SegDB.pszDBName = opts->dbname ? strdup(opts->dbname) : NULL; SegDB.pszDBUser = opts->username ? strdup(opts->username) : NULL; SegDB.pszDBPswd = NULL; if (g_MPPPassThroughCredentials != NULL && *g_MPPPassThroughCredentials != '\0') { unsigned int nBytes; char *pszDBPswd = Base64ToData(g_MPPPassThroughCredentials, &nBytes); if (pszDBPswd == NULL) { mpp_err_msg(logError, progname, "Invalid Greenplum DB Credentials: %s\n", g_MPPPassThroughCredentials); exit(1); } if (nBytes > 0) { SegDB.pszDBPswd = malloc(nBytes + 1); if (SegDB.pszDBPswd == NULL) { mpp_err_msg(logInfo, progname, "Cannot allocate memory for Greenplum Database Credentials\n"); exit(1); } memcpy(SegDB.pszDBPswd, pszDBPswd, nBytes); SegDB.pszDBPswd[nBytes] = '\0'; } } if (g_role == ROLE_MASTER) g_conn = MakeDBConnection(&SegDB, true); else g_conn = MakeDBConnection(&SegDB, false); if (PQstatus(g_conn) == CONNECTION_BAD) { exit_horribly(NULL, NULL, "connection to database \"%s\" failed: %s", PQdb(g_conn), PQerrorMessage(g_conn)); } if (g_gpdumpKey != NULL) { /* * Open the database again, for writing status info */ g_conn_status = MakeDBConnection(&SegDB, false); if (PQstatus(g_conn_status) == CONNECTION_BAD) { exit_horribly(NULL, NULL, "Connection on host %s failed: %s", StringNotNull(SegDB.pszHost, "localhost"), PQerrorMessage(g_conn_status)); } g_main_tid = pthread_self(); g_pStatusOpList = CreateStatusOpList(); if (g_pStatusOpList == NULL) { exit_horribly(NULL, NULL, "cannot allocate memory for gp_backup_status operation\n"); } /* * Create thread for monitoring for cancel requests. If we're running * using PSQL, the monitor is not allowed to start until the worker * process is forked. This is done to prevent the forked process from * being blocked by locks held by library routines (__tz_convert, for * example). */ if (bUsePSQL) { pthread_mutex_lock(&g_threadSyncPoint); } pthread_create(&g_monitor_tid, NULL, monitorThreadProc, NULL); /* Install Ctrl-C interrupt handler, now that we have a connection */ if (!bUsePSQL) { act.sa_handler = myHandler; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_flags |= SA_RESTART; if (sigaction(SIGINT, &act, NULL) < 0) { mpp_err_msg(logInfo, progname, "Error trying to set SIGINT interrupt handler\n"); } act.sa_handler = myHandler; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_flags |= SA_RESTART; if (sigaction(SIGTERM, &act, NULL) < 0) { mpp_err_msg(logInfo, progname, "Error trying to set SIGTERM interrupt handler\n"); } } pOp = CreateStatusOp(TASK_START, TASK_RC_SUCCESS, SUFFIX_START, TASK_MSG_SUCCESS); if (pOp == NULL) { exit_horribly(NULL, NULL, "cannot allocate memory for gp_backup_status operation\n"); } AddToStatusOpList(g_pStatusOpList, pOp); } /* end cdb additions */ if (bUsePSQL) { /* Install Ctrl-C interrupt handler, now that we have a connection */ act.sa_handler = psqlHandler; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_flags |= SA_RESTART; if (sigaction(SIGINT, &act, NULL) < 0) { mpp_err_msg(logInfo, progname, "Error trying to set SIGINT interrupt handler\n"); } act.sa_handler = psqlHandler; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_flags |= SA_RESTART; if (sigaction(SIGTERM, &act, NULL) < 0) { mpp_err_msg(logInfo, progname, "Error trying to set SIGTERM interrupt handler\n"); } /* Establish a SIGCHLD handler to catch termination the psql process */ act.sa_handler = myChildHandler; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_flags |= SA_RESTART; if (sigaction(SIGCHLD, &act, NULL) < 0) { mpp_err_msg(logInfo, progname, "Error trying to set SIGCHLD interrupt handler\n"); exit(1); } mpp_err_msg(logInfo, progname, "Before fork of gp_restore_agent\n"); newpid = fork(); if (newpid < 0) { mpp_err_msg(logError, progname, "Failed to fork\n"); } else if (newpid == 0) { /* TODO: use findAcceptableBackupFilePathName(...) to look for the file name * if user invoked gp_restore_agent directly without supplying a file name. * If the agent is invoked from gp_restore_launch, then we are ok. */ if (optind < argc) { char *rawInputFile = argv[optind]; valueBuf = createPQExpBuffer(); inputFileSpec = shellEscape(rawInputFile, valueBuf, false, false); } if (inputFileSpec == NULL || inputFileSpec[0] == '\0') { mpp_err_msg(logError, progname, "dump file path is empty"); exit(1); } if (postDataSchemaOnly) { if (strstr(inputFileSpec,"_post_data") == NULL) { fprintf(stderr,"Adding _post_data to the end of the file name?\n"); char * newFS = malloc(strlen(inputFileSpec) + strlen("_post_data") + 1); strcpy(newFS, inputFileSpec); strcat(newFS, "_post_data"); inputFileSpec = newFS; } } pszCmdLine = createPQExpBuffer(); #ifdef USE_DDBOOST if (dd_boost_enabled) { formDDBoostPsqlCommandLine(pszCmdLine, argv[0], (postDataSchemaOnly == 1 ? true : false), g_role, g_compPg, ddp_file_name, dd_boost_buf_size, table_filter_file, change_schema_file, schema_level_file, ddboost_storage_unit); } else { #endif formPsqlCommandLine(pszCmdLine, argv[0], (postDataSchemaOnly == 1), g_role, inputFileSpec, g_compPg, table_filter_file, netbackup_service_host, netbackup_block_size, change_schema_file, schema_level_file); #ifdef USE_DDBOOST } #endif appendPQExpBuffer(pszCmdLine, " -h %s -p %s -U %s -d ", g_targetHost, g_targetPort, SegDB.pszDBUser); shellEscape(SegDB.pszDBName, pszCmdLine, true /* quote */, false /* reset */); appendPQExpBuffer(pszCmdLine, " -a "); if (g_bOnErrorStop) appendPQExpBuffer(pszCmdLine, " -v ON_ERROR_STOP="); if (g_role == ROLE_SEGDB) putenv("PGOPTIONS=-c gp_session_role=UTILITY"); if (g_role == ROLE_MASTER) putenv("PGOPTIONS=-c gp_session_role=DISPATCH"); mpp_err_msg(logInfo, progname, "Command Line: %s\n", pszCmdLine->data); /* * Make this new process the process group leader of the children * being launched. This allows a signal to be sent to all * processes in the group simultaneously. */ setpgid(newpid, newpid); execl("/bin/sh", "sh", "-c", pszCmdLine->data, NULL); mpp_err_msg(logInfo, progname, "Error in gp_restore_agent - execl of %s with Command Line %s failed", "/bin/sh", pszCmdLine->data); _exit(127); } else { /* * Make the new child process the process group leader of the * children being launched. This allows a signal to be sent to * all processes in the group simultaneously. * * This is a redundant call to avoid a race condition suggested by * Stevens. */ setpgid(newpid, newpid); /* Allow the monitor thread to begin execution. */ pthread_mutex_unlock(&g_threadSyncPoint); /* Parent . Lets sleep and wake up until we see it's done */ while (!bPSQLDone) { sleep(5); } /* * If this process has been sent a SIGINT or SIGTERM, we need to * send a SIGINT to the psql process GROUP. */ if (bKillPsql) { mpp_err_msg(logInfo, progname, "Terminating psql due to signal.\n"); kill(-newpid, SIGINT); } waitpid(newpid, &status, 0); if (WIFEXITED(status)) { rc = WEXITSTATUS(status); if (rc == 0) { mpp_err_msg(logInfo, progname, "psql finished with rc %d.\n", rc); /* Normal completion falls to end of routine. */ } else { if (rc >= 128) { /* * If the exit code has the 128-bit set, the exit code * represents a shell exited by signal where the * signal number is exitCode - 128. */ rc -= 128; pszErrorMsg = MakeString("psql finished abnormally with signal number %d.\n", rc); } else { pszErrorMsg = MakeString("psql finished abnormally with return code %d.\n", rc); } makeSureMonitorThreadEnds(TASK_RC_FAILURE, pszErrorMsg); free(pszErrorMsg); exit_code = 2; } } else if (WIFSIGNALED(status)) { pszErrorMsg = MakeString("psql finished abnormally with signal number %d.\n", WTERMSIG(status)); mpp_err_msg(logError, progname, pszErrorMsg); makeSureMonitorThreadEnds(TASK_RC_FAILURE, pszErrorMsg); free(pszErrorMsg); exit_code = 2; } else { pszErrorMsg = MakeString("psql crashed or finished badly; status=%#x.\n", status); mpp_err_msg(logError, progname, pszErrorMsg); makeSureMonitorThreadEnds(TASK_RC_FAILURE, pszErrorMsg); free(pszErrorMsg); exit_code = 2; } } } else { AH = OpenArchive(inputFileSpec, opts->format); /* Let the archiver know how noisy to be */ AH->verbose = opts->verbose; /* * Whether to keep submitting sql commands as "pg_restore ... | psql ... " */ AH->exit_on_error = opts->exit_on_error; if (opts->tocFile) SortTocFromFile(AH, opts); if (opts->tocSummary) PrintTOCSummary(AH, opts); else { pAH = (ArchiveHandle *) AH; if (opts->useDB) { /* check for version mismatch */ if (pAH->version < K_VERS_1_3) die_horribly(NULL, NULL, "direct database connections are not supported in pre-1.3 archives\n"); pAH->connection = g_conn; /* XXX Should get this from the archive */ AH->minRemoteVersion = 070100; AH->maxRemoteVersion = 999999; _check_database_version(pAH); } RestoreArchive(AH, opts); /* * The following is necessary when the -C option is used. A new * connection is gotten to the database within RestoreArchive */ if (pAH->connection != g_conn) g_conn = pAH->connection; } /* done, print a summary of ignored errors */ if (AH->n_errors) fprintf(stderr, _("WARNING: errors ignored on restore: %d\n"), AH->n_errors); /* AH may be freed in CloseArchive? */ exit_code = AH->n_errors ? 1 : 0; CloseArchive(AH); } #ifdef USE_DDBOOST if(dd_boost_enabled) cleanupDDSystem(); free(ddboost_storage_unit); #endif makeSureMonitorThreadEnds(TASK_RC_SUCCESS, TASK_MSG_SUCCESS); DestroyStatusOpList(g_pStatusOpList); if (change_schema_file) free(change_schema_file); if (schema_level_file) free(schema_level_file); if (SegDB.pszHost) free(SegDB.pszHost); if (SegDB.pszDBName) free(SegDB.pszDBName); if (SegDB.pszDBUser) free(SegDB.pszDBUser); if (SegDB.pszDBPswd) free(SegDB.pszDBPswd); if (valueBuf) destroyPQExpBuffer(valueBuf); if (escapeBuf) destroyPQExpBuffer(escapeBuf); PQfinish(g_conn); if (exit_code == 0) mpp_err_msg(logInfo, progname, "Finished successfully\n"); else mpp_err_msg(logError, progname, "Finished with errors\n"); return exit_code; }
/* * Dump tablespaces. */ static void dumpTablespaces(PGconn *conn) { PGresult *res; int i; /* * Get all tablespaces except built-in ones (which we assume are named * pg_xxx) */ res = executeQuery(conn, "SELECT spcname, " "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " "spclocation, spcacl " "FROM pg_catalog.pg_tablespace " "WHERE spcname !~ '^pg_' " "ORDER BY 1"); if (PQntuples(res) > 0) printf("--\n-- Tablespaces\n--\n\n"); for (i = 0; i < PQntuples(res); i++) { PQExpBuffer buf = createPQExpBuffer(); char *spcname = PQgetvalue(res, i, 0); char *spcowner = PQgetvalue(res, i, 1); char *spclocation = PQgetvalue(res, i, 2); char *spcacl = PQgetvalue(res, i, 3); char *fspcname; /* needed for buildACLCommands() */ fspcname = strdup(fmtId(spcname)); if (output_clean) appendPQExpBuffer(buf, "DROP TABLESPACE %s;\n", fspcname); appendPQExpBuffer(buf, "CREATE TABLESPACE %s", fspcname); appendPQExpBuffer(buf, " OWNER %s", fmtId(spcowner)); appendPQExpBuffer(buf, " LOCATION "); appendStringLiteral(buf, spclocation, true); appendPQExpBuffer(buf, ";\n"); if (!skip_acls && !buildACLCommands(fspcname, "TABLESPACE", spcacl, spcowner, server_version, buf)) { fprintf(stderr, _("%s: could not parse ACL list (%s) for tablespace \"%s\"\n"), progname, spcacl, fspcname); PQfinish(conn); exit(1); } printf("%s", buf->data); free(fspcname); destroyPQExpBuffer(buf); } PQclear(res); printf("\n\n"); }
bool GetRestoreSegmentDatabaseArray(PGconn *pConn, RestorePairArray * restorePairAr, BackupLoc backupLocation, char *restore_set_str, bool dataOnly) { bool bRtn = true; PQExpBuffer pQry = NULL; PGresult *pRes = NULL; int ntups; int count; int i_dbid; int i_content; int i_host; int i_port; int i; int j; int x; int dbidset_count = 0; int *dbidset = NULL; SegmentDatabase *sourceSegDB; SegmentDatabase *targetSegDB; pQry = createPQExpBuffer(); appendPQExpBuffer(pQry, "SELECT" " dbid," " content," " hostname," " port " "FROM " " gp_segment_configuration " "WHERE role='p' " "ORDER BY content DESC"); pRes = PQexec(pConn, pQry->data); if (!pRes || PQresultStatus(pRes) != PGRES_TUPLES_OK) { mpp_err_msg("ERROR", "gp_restore", "query to obtain list of Greenplum segment databases failed: %s", PQerrorMessage(pConn)); bRtn = false; goto cleanup; } ntups = PQntuples(pRes); if (ntups <= 0) { mpp_err_msg("ERROR", "gp_restore", "no Greenplum segment databases found on master segment schema"); bRtn = false; goto cleanup; } count = ntups + 1; /* * Allocate enough memory for all of them, even though some may be * filtered out. */ restorePairAr->count = 0; restorePairAr->pData = (RestorePair *) calloc(count, sizeof(RestorePair)); if (restorePairAr->pData == NULL) { mpp_err_msg("ERROR", "gp_restore", "Unable to allocate memory for Greenplum segment database information\n"); bRtn = false; goto cleanup; } /* get the column numbers */ i_dbid = PQfnumber(pRes, "dbid"); i_content = PQfnumber(pRes, "content"); i_host = PQfnumber(pRes, "hostname"); i_port = PQfnumber(pRes, "port"); /* * if the dump file is on individual databases, parse the list of dbid's * where those files exist. */ if (backupLocation == FILE_ON_INDIVIDUAL) { /* allocate dbidset. len is more than we need but it's a safe bet */ dbidset = (int *) calloc(strlen(restore_set_str), sizeof(int)); MemSet(dbidset, 0, strlen(restore_set_str) * sizeof(int)); if (dbidset == NULL) { mpp_err_msg("ERROR", "gp_restore", "Unable to allocate memory for Greenplum dbidset information\n"); bRtn = false; goto cleanup; } /* parse the user specified dbid list, return dbid count */ dbidset_count = parseDbidSet(dbidset, restore_set_str); if (dbidset_count < 1) { bRtn = false; goto cleanup; } for (i = 0; i < dbidset_count; i++) { bool match = false; for (j = 0; j < ntups; j++) { int dbid = atoi(PQgetvalue(pRes, j, i_dbid)); if (dbid == dbidset[i]) { match = true; break; } } if (!match) { mpp_err_msg("ERROR", "gp_restore", "dbid %d is not a primary Greenplum segment databases entry\n", dbidset[i]); bRtn = false; goto cleanup; } } } mpp_err_msg("INFO", "gp_restore", "Preparing to restore the following segments:\n"); /* Read through the results set. */ x = 0; for (i = 0; i < ntups; i++) { int dbid = atoi(PQgetvalue(pRes, i, i_dbid)); int contentid = atoi(PQgetvalue(pRes, i, i_content)); bool should_restore = false; /* if dataOnly don't restore the master (table definitions) */ if (dataOnly && contentid == -1) continue; if (backupLocation == FILE_ON_INDIVIDUAL) { should_restore = false; for (j = 0; j < dbidset_count; j++) { if (dbid == dbidset[j]) should_restore = true; } if (!should_restore) continue; } targetSegDB = &restorePairAr->pData[x].segdb_target; targetSegDB->dbid = dbid; targetSegDB->role = (contentid == -1 ? ROLE_MASTER : ROLE_SEGDB); targetSegDB->port = atoi(PQgetvalue(pRes, i, i_port)); targetSegDB->pszHost = strdup(PQgetvalue(pRes, i, i_host)); targetSegDB->pszDBName = PQdb(pConn); targetSegDB->pszDBUser = PQuser(pConn); targetSegDB->pszDBPswd = PQpass(pConn); targetSegDB->content = contentid; sourceSegDB = &restorePairAr->pData[x].segdb_source; sourceSegDB->dbid = targetSegDB->dbid; sourceSegDB->role = targetSegDB->role; sourceSegDB->port = targetSegDB->port; sourceSegDB->pszHost = targetSegDB->pszHost; sourceSegDB->pszDBName = targetSegDB->pszDBName; sourceSegDB->pszDBUser = targetSegDB->pszDBUser; if (targetSegDB->pszDBPswd) sourceSegDB->pszDBPswd = targetSegDB->pszDBPswd; if (targetSegDB->role == ROLE_MASTER) mpp_err_msg("INFO", "gp_restore", "Master (dbid 1)\n"); else mpp_err_msg("INFO", "gp_restore", "Segment %d (dbid %d)\n", contentid, targetSegDB->dbid); x++; } /* set the count to be the number that passed the set inclusion test */ restorePairAr->count = x; cleanup: if (pQry != NULL) destroyPQExpBuffer(pQry); if (pRes != NULL) PQclear(pRes); return bRtn; }
/* * Dump roles */ static void dumpRoles(PGconn *conn) { PQExpBuffer buf = createPQExpBuffer(); PGresult *res; int i_rolname, i_rolsuper, i_rolinherit, i_rolcreaterole, i_rolcreatedb, i_rolcatupdate, i_rolcanlogin, i_rolconnlimit, i_rolpassword, i_rolvaliduntil; int i; /* note: rolconfig is dumped later */ if (server_version >= 80100) printfPQExpBuffer(buf, "SELECT rolname, rolsuper, rolinherit, " "rolcreaterole, rolcreatedb, rolcatupdate, " "rolcanlogin, rolconnlimit, rolpassword, " "rolvaliduntil " "FROM pg_authid " "ORDER BY 1"); else printfPQExpBuffer(buf, "SELECT usename as rolname, " "usesuper as rolsuper, " "true as rolinherit, " "usesuper as rolcreaterole, " "usecreatedb as rolcreatedb, " "usecatupd as rolcatupdate, " "true as rolcanlogin, " "-1 as rolconnlimit, " "passwd as rolpassword, " "valuntil as rolvaliduntil " "FROM pg_shadow " "UNION ALL " "SELECT groname as rolname, " "false as rolsuper, " "true as rolinherit, " "false as rolcreaterole, " "false as rolcreatedb, " "false as rolcatupdate, " "false as rolcanlogin, " "-1 as rolconnlimit, " "null::text as rolpassword, " "null::abstime as rolvaliduntil " "FROM pg_group " "WHERE NOT EXISTS (SELECT 1 FROM pg_shadow " " WHERE usename = groname) " "ORDER BY 1"); res = executeQuery(conn, buf->data); i_rolname = PQfnumber(res, "rolname"); i_rolsuper = PQfnumber(res, "rolsuper"); i_rolinherit = PQfnumber(res, "rolinherit"); i_rolcreaterole = PQfnumber(res, "rolcreaterole"); i_rolcreatedb = PQfnumber(res, "rolcreatedb"); i_rolcatupdate = PQfnumber(res, "rolcatupdate"); i_rolcanlogin = PQfnumber(res, "rolcanlogin"); i_rolconnlimit = PQfnumber(res, "rolconnlimit"); i_rolpassword = PQfnumber(res, "rolpassword"); i_rolvaliduntil = PQfnumber(res, "rolvaliduntil"); if (PQntuples(res) > 0) printf("--\n-- Roles\n--\n\n"); for (i = 0; i < PQntuples(res); i++) { const char *rolename; rolename = PQgetvalue(res, i, i_rolname); resetPQExpBuffer(buf); if (output_clean) appendPQExpBuffer(buf, "DROP ROLE %s;\n", fmtId(rolename)); /* * We dump CREATE ROLE followed by ALTER ROLE to ensure that the role * will acquire the right properties even if it already exists. (The * above DROP may therefore seem redundant, but it isn't really, * because this technique doesn't get rid of role memberships.) */ appendPQExpBuffer(buf, "CREATE ROLE %s;\n", fmtId(rolename)); appendPQExpBuffer(buf, "ALTER ROLE %s WITH", fmtId(rolename)); if (strcmp(PQgetvalue(res, i, i_rolsuper), "t") == 0) appendPQExpBuffer(buf, " SUPERUSER"); else appendPQExpBuffer(buf, " NOSUPERUSER"); if (strcmp(PQgetvalue(res, i, i_rolinherit), "t") == 0) appendPQExpBuffer(buf, " INHERIT"); else appendPQExpBuffer(buf, " NOINHERIT"); if (strcmp(PQgetvalue(res, i, i_rolcreaterole), "t") == 0) appendPQExpBuffer(buf, " CREATEROLE"); else appendPQExpBuffer(buf, " NOCREATEROLE"); if (strcmp(PQgetvalue(res, i, i_rolcreatedb), "t") == 0) appendPQExpBuffer(buf, " CREATEDB"); else appendPQExpBuffer(buf, " NOCREATEDB"); if (strcmp(PQgetvalue(res, i, i_rolcanlogin), "t") == 0) appendPQExpBuffer(buf, " LOGIN"); else appendPQExpBuffer(buf, " NOLOGIN"); if (strcmp(PQgetvalue(res, i, i_rolconnlimit), "-1") != 0) appendPQExpBuffer(buf, " CONNECTION LIMIT %s", PQgetvalue(res, i, i_rolconnlimit)); if (!PQgetisnull(res, i, i_rolpassword)) { appendPQExpBuffer(buf, " PASSWORD "); appendStringLiteral(buf, PQgetvalue(res, i, i_rolpassword), true); } if (!PQgetisnull(res, i, i_rolvaliduntil)) appendPQExpBuffer(buf, " VALID UNTIL '%s'", PQgetvalue(res, i, i_rolvaliduntil)); appendPQExpBuffer(buf, ";\n"); printf("%s", buf->data); if (server_version >= 70300) dumpUserConfig(conn, rolename); } PQclear(res); printf("\n\n"); destroyPQExpBuffer(buf); }
/* GetSegmentDatabaseArray: This function reads all active instance segment pairs * from the function gp_segment_instance_map(). * It then uses the set specification fo filter out any instid, segid combinations * that don't match the set. bExcludeHead = true is used for restoring, because * the head is restored first seperately from the rest of the databases. */ bool GetDumpSegmentDatabaseArray(PGconn *pConn, int remote_version, SegmentDatabaseArray *pSegDBAr, ActorSet actors, char *dump_set_str, char *pszDBName, char *pszUserName, bool dataOnly, bool schemaOnly) { bool bRtn = true; PQExpBuffer pQry = NULL; PGresult *pRes = NULL; int ntups; int count; int i_dbid; int i_content; int i_host; int i_port; int i; int j; int x; int dbidset_count = 0; int *dbidset = NULL; SegmentDatabase *pSegDB; pQry = createPQExpBuffer(); if (remote_version >= 80214) /* 4.0 and beyond */ appendPQExpBuffer(pQry, "SELECT" " dbid," " content," " hostname," " port " "FROM " " gp_segment_configuration " "WHERE role='p' " "ORDER BY content DESC"); else /* pre 4.0 */ appendPQExpBuffer(pQry, "SELECT" " dbid," " content," " hostname," " port " "FROM" " gp_configuration " "WHERE valid = 't' " "AND isPrimary = 't' " "ORDER BY content DESC"); pRes = PQexec(pConn, pQry->data); if (!pRes || PQresultStatus(pRes) != PGRES_TUPLES_OK) { mpp_err_msg("ERROR", "gp_dump", "query to obtain list of Greenplum segment databases failed: %s", PQerrorMessage(pConn)); bRtn = false; goto cleanup; } ntups = PQntuples(pRes); if (ntups <= 0) { mpp_err_msg("ERROR", "gp_dump", "no Greenplum segment databases found on master segment schema"); bRtn = false; goto cleanup; } count = ntups + 1; /* * See how many set elements there are that were specified by HostIP or * name. */ /* * Allocate enough memory for all of them, even though some may be * filtered out. */ pSegDBAr->count = 0; pSegDBAr->pData = (SegmentDatabase *) calloc(count, sizeof(SegmentDatabase)); if (pSegDBAr->pData == NULL) { mpp_err_msg("ERROR", "gp_dump", "Unable to allocate memory for Greenplum segment/instances information\n"); bRtn = false; goto cleanup; } /* get the column numbers */ i_dbid = PQfnumber(pRes, "dbid"); i_content = PQfnumber(pRes, "content"); i_host = PQfnumber(pRes, "hostname"); i_port = PQfnumber(pRes, "port"); /* * if an individual set of dbid's was requested to be dumped, parse it and * check that it really exists and is a primary segment. */ if (actors == SET_INDIVIDUAL) { /* allocate dbidset. len is more than we need but it's a safe bet */ dbidset = (int *) calloc(strlen(dump_set_str), sizeof(int)); MemSet(dbidset, 0, strlen(dump_set_str) * sizeof(int)); if (dbidset == NULL) { mpp_err_msg("ERROR", "gp_dump", "Unable to allocate memory for Greenplum dbid set information\n"); bRtn = false; goto cleanup; } /* parse the user specified dbid list, return dbid count */ dbidset_count = parseDbidSet(dbidset, dump_set_str); if (dbidset_count < 1) { bRtn = false; goto cleanup; } for (i = 0; i < dbidset_count; i++) { bool match = false; for (j = 0; j < ntups; j++) { int dbid = atoi(PQgetvalue(pRes, j, i_dbid)); if (dbid == dbidset[i]) { match = true; break; } } if (!match) { mpp_err_msg("ERROR", "gp_dump", "dbid %d is not a correct Greenplum segment databases " "entry, or is not a primary segment\n ", dbidset[i]); bRtn = false; goto cleanup; } } } mpp_err_msg("INFO", "gp_dump", "Preparing to dump the following segments:\n"); /* Read through the results set (all primary segments) */ x = 0; for (i = 0; i < ntups; i++) { int dbid = atoi(PQgetvalue(pRes, i, i_dbid)); int content = atoi(PQgetvalue(pRes, i, i_content)); bool should_dump = false; /* in dataOnly we don't dump master information */ if (dataOnly && content == -1 && actors != SET_INDIVIDUAL) continue; /* in schemaOnly we skip all but the master */ if (schemaOnly && content != -1 && actors != SET_INDIVIDUAL) continue; if (actors == SET_INDIVIDUAL) { should_dump = false; for (j = 0; j < dbidset_count; j++) { if (dbid == dbidset[j]) should_dump = true; } if (!should_dump) continue; } pSegDB = &pSegDBAr->pData[x++]; pSegDB->dbid = dbid; pSegDB->content = content; pSegDB->role = (content == -1 ? ROLE_MASTER : ROLE_SEGDB); pSegDB->port = atoi(PQgetvalue(pRes, i, i_port)); pSegDB->pszHost = strdup(PQgetvalue(pRes, i, i_host)); pSegDB->pszDBName = pszDBName; pSegDB->pszDBUser = pszUserName; pSegDB->pszDBPswd = PQpass(pConn); if (pSegDB->role == ROLE_MASTER) mpp_err_msg("INFO", "gp_dump", "Master (dbid 1)\n"); else mpp_err_msg("INFO", "gp_dump", "Segment %d (dbid %d)\n", pSegDB->content, pSegDB->dbid); } /* set the count to be the number that passed the set inclusion test */ pSegDBAr->count = x; cleanup: if (pQry != NULL) destroyPQExpBuffer(pQry); if (pRes != NULL) PQclear(pRes); return bRtn; }
/* * Build GRANT/REVOKE command(s) for an object. * * name: the object name, in the form to use in the commands (already quoted) * subname: the sub-object name, if any (already quoted); NULL if none * type: the object type (as seen in GRANT command: must be one of * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE, * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT) * acls: the ACL string fetched from the database * owner: username of object owner (will be passed through fmtId); can be * NULL or empty string to indicate "no owner known" * prefix: string to prefix to each generated command; typically empty * 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: when processing a default ACL, prefix is "ALTER DEFAULT PRIVILEGES " * or something similar, and name is an empty string. * * Note: beware of passing a fmtId() result directly as 'name' or 'subname', * since this routine uses fmtId() internally. */ bool buildACLCommands(const char *name, const char *subname, const char *type, const char *acls, const char *owner, const char *prefix, int remoteVersion, PQExpBuffer sql) { char **aclitems; int naclitems; int i; PQExpBuffer grantee, grantor, privs, privswgo; PQExpBuffer firstsql, secondsql; bool found_owner_privs = false; if (strlen(acls) == 0) return true; /* object has default permissions */ /* treat empty-string owner same as NULL */ if (owner && *owner == '\0') owner = NULL; if (!parsePGArray(acls, &aclitems, &naclitems)) { if (aclitems) free(aclitems); return false; } grantee = createPQExpBuffer(); grantor = createPQExpBuffer(); privs = createPQExpBuffer(); privswgo = createPQExpBuffer(); /* * 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. */ firstsql = createPQExpBuffer(); secondsql = 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(firstsql, "%sREVOKE ALL", prefix); if (subname) appendPQExpBuffer(firstsql, "(%s)", subname); appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name); /* * 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, * 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. */ if (remoteVersion < 80200 && strcmp(type, "DATABASE") == 0) { /* database CONNECT priv didn't exist before 8.2 */ appendPQExpBuffer(firstsql, "%sGRANT CONNECT ON %s %s TO PUBLIC;\n", prefix, type, name); } /* Scan individual ACL items */ for (i = 0; i < naclitems; i++) { if (!parseAclItem(aclitems[i], type, name, subname, 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 && strcmp(grantor->data, owner) == 0) { found_owner_privs = true; /* * For the owner, the default privilege level is ALL WITH * GRANT OPTION (only ALL prior to 7.4). */ if (supports_grant_options(remoteVersion) ? strcmp(privswgo->data, "ALL") != 0 : strcmp(privs->data, "ALL") != 0) { appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix); if (subname) appendPQExpBuffer(firstsql, "(%s)", subname); appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n", type, name, fmtId(grantee->data)); if (privs->len > 0) appendPQExpBuffer(firstsql, "%sGRANT %s ON %s %s TO %s;\n", prefix, privs->data, type, name, fmtId(grantee->data)); if (privswgo->len > 0) appendPQExpBuffer(firstsql, "%sGRANT %s ON %s %s TO %s WITH GRANT OPTION;\n", prefix, privswgo->data, type, name, fmtId(grantee->data)); } } else { /* * Otherwise can assume we are starting from no privs. */ if (grantor->len > 0 && (!owner || strcmp(owner, grantor->data) != 0)) appendPQExpBuffer(secondsql, "SET SESSION AUTHORIZATION %s;\n", fmtId(grantor->data)); if (privs->len > 0) { appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ", prefix, privs->data, type, name); if (grantee->len == 0) appendPQExpBuffer(secondsql, "PUBLIC;\n"); else if (strncmp(grantee->data, "group ", strlen("group ")) == 0) appendPQExpBuffer(secondsql, "GROUP %s;\n", fmtId(grantee->data + strlen("group "))); else appendPQExpBuffer(secondsql, "%s;\n", fmtId(grantee->data)); } if (privswgo->len > 0) { appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ", prefix, privswgo->data, type, name); if (grantee->len == 0) appendPQExpBuffer(secondsql, "PUBLIC"); else if (strncmp(grantee->data, "group ", strlen("group ")) == 0) appendPQExpBuffer(secondsql, "GROUP %s", fmtId(grantee->data + strlen("group "))); else appendPQExpBuffer(secondsql, "%s", fmtId(grantee->data)); appendPQExpBuffer(secondsql, " WITH GRANT OPTION;\n"); } if (grantor->len > 0 && (!owner || strcmp(owner, grantor->data) != 0)) appendPQExpBuffer(secondsql, "RESET SESSION AUTHORIZATION;\n"); } } } /* * If we didn't find any owner privs, the owner must have revoked 'em all */ if (!found_owner_privs && owner) { appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix); if (subname) appendPQExpBuffer(firstsql, "(%s)", subname); appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n", type, name, fmtId(owner)); } destroyPQExpBuffer(grantee); destroyPQExpBuffer(grantor); destroyPQExpBuffer(privs); destroyPQExpBuffer(privswgo); appendPQExpBuffer(sql, "%s%s", firstsql->data, secondsql->data); destroyPQExpBuffer(firstsql); destroyPQExpBuffer(secondsql); free(aclitems); return true; }