/* * 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; }
/* * 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; }
/* * 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); }
/* * 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); }
/* * 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); }
/* * 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; }
/* * 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); }
/* * Process non-COPY table data (that is, INSERT commands). * * The commands have been run together as one long string for compressibility, * and we are receiving them in bufferloads with arbitrary boundaries, so we * have to locate command boundaries and save partial commands across calls. * All state must be kept in AH->sqlparse, not in local variables of this * routine. We assume that AH->sqlparse was filled with zeroes when created. * * We have to lex the data to the extent of identifying literals and quoted * identifiers, so that we can recognize statement-terminating semicolons. * We assume that INSERT data will not contain SQL comments, E'' literals, * or dollar-quoted strings, so this is much simpler than a full SQL lexer. * * Note: when restoring from a pre-9.0 dump file, this code is also used to * process BLOB COMMENTS data, which has the same problem of containing * multiple SQL commands that might be split across bufferloads. Fortunately, * that data won't contain anything complicated to lex either. */ static void ExecuteSimpleCommands(ArchiveHandle *AH, const char *buf, size_t bufLen) { const char *qry = buf; const char *eos = buf + bufLen; /* initialize command buffer if first time through */ if (AH->sqlparse.curCmd == NULL) AH->sqlparse.curCmd = createPQExpBuffer(); for (; qry < eos; qry++) { char ch = *qry; /* For neatness, we skip any newlines between commands */ if (!(ch == '\n' && AH->sqlparse.curCmd->len == 0)) appendPQExpBufferChar(AH->sqlparse.curCmd, ch); switch (AH->sqlparse.state) { case SQL_SCAN: /* Default state == 0, set in _allocAH */ if (ch == ';') { /* * We've found the end of a statement. Send it and reset * the buffer. */ ExecuteSqlCommand(AH, AH->sqlparse.curCmd->data, "could not execute query"); resetPQExpBuffer(AH->sqlparse.curCmd); } else if (ch == '\'') { AH->sqlparse.state = SQL_IN_SINGLE_QUOTE; AH->sqlparse.backSlash = false; } else if (ch == '"') { AH->sqlparse.state = SQL_IN_DOUBLE_QUOTE; } break; case SQL_IN_SINGLE_QUOTE: /* We needn't handle '' specially */ if (ch == '\'' && !AH->sqlparse.backSlash) AH->sqlparse.state = SQL_SCAN; else if (ch == '\\' && !AH->public__.std_strings) AH->sqlparse.backSlash = !AH->sqlparse.backSlash; else AH->sqlparse.backSlash = false; break; case SQL_IN_DOUBLE_QUOTE: /* We needn't handle "" specially */ if (ch == '"') AH->sqlparse.state = SQL_SCAN; break; } } }
/* * 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; }
int main(int argc, char** argv) { int c, i, optindex; static struct option long_options[] = { {"host", required_argument, NULL, 'h'}, {"port", required_argument, NULL, 'p'}, {"username", required_argument, NULL, 'U'}, {"help", no_argument, NULL, '?'}, {NULL, 0, NULL, 0} }; if (argc == 1 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-?")) help(); while ((c = getopt_long(argc, argv, "h:p:U:", long_options, &optindex)) != -1) { switch (c) { case 'h': /* host for tranlsting oids */ pghost = optarg; break; case 'p': /* port for translating oids */ pgport = optarg; break; case 'U': /* username for translating oids */ username = optarg; break; default: fprintf(stderr, "Try \"changetrackingdump --help\" for more information.\n"); exit(1); } } if(pghost) { conn = DBConnect(pghost, pgport, "template1", username); } dbQry = createPQExpBuffer(); for (i = optind; i < argc; i++) { char *fname = argv[i]; logFd = open(fname, O_RDONLY | PG_BINARY, 0); if (logFd < 0) { perror(fname); continue; } dumpChangeTracking(fname); } exit_gracefuly(0); /* just to avoid a warning */ return 0; }
/* * 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); }
/* * gets_fromFile * * Gets a line of noninteractive input from a file (which could be stdin). * The result is a malloc'd string, or NULL on EOF or input error. * * Caller *must* have set up sigint_interrupt_jmp before calling. * * Note: we re-use a static PQExpBuffer for each call. This is to avoid * leaking memory if interrupted by SIGINT. */ char * gets_fromFile(FILE *source) { static PQExpBuffer buffer = NULL; char line[1024]; if (buffer == NULL) /* first time through? */ buffer = createPQExpBuffer(); else resetPQExpBuffer(buffer); for (;;) { char *result; /* Enable SIGINT to longjmp to sigint_interrupt_jmp */ sigint_interrupt_enabled = true; /* Get some data */ result = fgets(line, sizeof(line), source); /* Disable SIGINT again */ sigint_interrupt_enabled = false; /* EOF or error? */ if (result == NULL) { if (ferror(source)) { psql_error("could not read from input file: %s\n", strerror(errno)); return NULL; } break; } appendPQExpBufferStr(buffer, line); if (PQExpBufferBroken(buffer)) { psql_error("out of memory\n"); return NULL; } /* EOL? */ if (buffer->data[buffer->len - 1] == '\n') { buffer->data[buffer->len - 1] = '\0'; return pg_strdup(buffer->data); } } if (buffer->len > 0) /* EOF after reading some bufferload(s) */ return pg_strdup(buffer->data); /* EOF, so return null */ return NULL; }
/* * 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; }
/* * 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); }
/* * Read all pg_type and pg_class OIDs from old cluster, into the DbInfo structs. * * The OIDs of types and relations need to be preserved from the old cluster. * In the upstream, we use a different mechanism, integrated into pg_dump, for * this, but things are a bit more complicated in GPDB, because of partitions, * auxiliary AO segment tables, and bitmap index LOV tables. So we use a * different strategy. */ void get_old_oids(migratorContext *ctx) { int dbnum; prep_status(ctx, "Exporting object OIDs from the old cluster"); for (dbnum = 0; dbnum < ctx->old.dbarr.ndbs; dbnum++) { DbInfo *olddb = &ctx->old.dbarr.dbs[dbnum]; PGconn *conn; PQExpBuffer buf = createPQExpBuffer(); conn = connectToServer(ctx, olddb->db_name, CLUSTER_OLD); PQclear(executeQueryOrDie(ctx, conn, "set search_path='pg_catalog';")); dump_rows(ctx, buf, NULL, conn, "SELECT oid, nspname FROM pg_namespace", "preassign_namespace_oid"); dump_rows(ctx, buf, NULL, conn, "SELECT oid, typname, typnamespace FROM pg_type", "preassign_type_oid"); /* * A table's TOAST table might not be named the usual way in the old * cluster, so dump it according to the way it will be named when created * in the new cluster, instead of the current name. * * For example, "alter_distpol_g_char_11_false_false", in the regression * database. FIXME: I don't quite understand how that happens. * * We don't preserve the OIDs of AO segment tables. */ dump_rows(ctx, buf, NULL, conn, "SELECT oid, relname, relnamespace FROM pg_class " " WHERE relnamespace NOT IN " " ( " CppAsString2(PG_TOAST_NAMESPACE) ", " "" CppAsString2(PG_AOSEGMENT_NAMESPACE) ") " " AND oid >= " CppAsString2(FirstNormalObjectId) " " "UNION ALL " "SELECT reltoastrelid, 'pg_toast_' || oid, " CppAsString2(PG_TOAST_NAMESPACE) " FROM pg_class WHERE reltoastrelid <> 0 AND oid >= " CppAsString2(FirstNormalObjectId) " ", "preassign_relation_oid"); PQfinish(conn); olddb->reserved_oids = buf->data; } check_ok(ctx); }
/* * 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; }
/* * gets_fromFile * * Gets a line of noninteractive input from a file (which could be stdin). * The result is a malloc'd string. * * Caller *must* have set up sigint_interrupt_jmp before calling. * * Note: we re-use a static PQExpBuffer for each call. This is to avoid * leaking memory if interrupted by SIGINT. */ char * gets_fromFile(FILE *source) { static PQExpBuffer buffer = NULL; char line[1024]; if (buffer == NULL) /* first time through? */ buffer = createPQExpBuffer(); else resetPQExpBuffer(buffer); for (;;) { char *result; /* Enable SIGINT to longjmp to sigint_interrupt_jmp */ sigint_interrupt_enabled = true; /* Get some data */ result = fgets(line, sizeof(line), source); /* Disable SIGINT again */ sigint_interrupt_enabled = false; /* EOF? */ if (result == NULL) break; appendPQExpBufferStr(buffer, line); /* EOL? */ if (buffer->data[buffer->len - 1] == '\n') { buffer->data[buffer->len - 1] = '\0'; return pg_strdup(buffer->data); } } if (buffer->len > 0) /* EOF after reading some bufferload(s) */ return pg_strdup(buffer->data); /* EOF, so return null */ return NULL; }
/* * 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; }
/* * Returns a temporary PQExpBuffer, valid until the next call to the function. * This is used by fmtId and fmtQualifiedId. * * Non-reentrant and non-thread-safe but reduces memory leakage. You can * replace this with a custom version by setting the getLocalPQExpBuffer * function pointer. */ static PQExpBuffer defaultGetLocalPQExpBuffer(void) { static PQExpBuffer id_return = NULL; if (id_return) /* first time through? */ { /* same buffer, just wipe contents */ resetPQExpBuffer(id_return); } else { /* new buffer */ id_return = createPQExpBuffer(); } return id_return; }
/* * 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; }
/* * cluster_conn_opts() * * Return standard command-line options for connecting to this cluster when * using psql, pg_dump, etc. Ideally this would match what get_db_conn() * sets, but the utilities we need aren't very consistent about the treatment * of database name options, so we leave that out. * * Result is valid until the next call to this function. */ char * cluster_conn_opts(ClusterInfo *cluster) { static PQExpBuffer buf; if (buf == NULL) buf = createPQExpBuffer(); else resetPQExpBuffer(buf); if (cluster->sockdir) { appendPQExpBufferStr(buf, "--host "); appendShellString(buf, cluster->sockdir); appendPQExpBufferChar(buf, ' '); } appendPQExpBuffer(buf, "--port %d --username ", cluster->port); appendShellString(buf, os_info.user); return buf->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[]) { char *pghost = NULL; char *pgport = NULL; char *pguser = NULL; bool force_password = false; bool data_only = false; bool globals_only = false; bool schema_only = false; PGconn *conn; int c, ret; static struct option long_options[] = { {"data-only", no_argument, NULL, 'a'}, {"clean", no_argument, NULL, 'c'}, {"inserts", no_argument, NULL, 'd'}, {"attribute-inserts", no_argument, NULL, 'D'}, {"column-inserts", no_argument, NULL, 'D'}, {"globals-only", no_argument, NULL, 'g'}, {"host", required_argument, NULL, 'h'}, {"ignore-version", no_argument, NULL, 'i'}, {"oids", no_argument, NULL, 'o'}, {"no-owner", no_argument, NULL, 'O'}, {"port", required_argument, NULL, 'p'}, {"password", no_argument, NULL, 'W'}, {"schema-only", no_argument, NULL, 's'}, {"superuser", required_argument, NULL, 'S'}, {"username", required_argument, NULL, 'U'}, {"verbose", no_argument, NULL, 'v'}, {"no-privileges", no_argument, NULL, 'x'}, {"no-acl", no_argument, NULL, 'x'}, /* * the following options don't have an equivalent short option letter, * but are available as '-X long-name' */ {"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1}, {"disable-triggers", no_argument, &disable_triggers, 1}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, {NULL, 0, NULL, 0} }; int optindex; set_pglocale_pgservice(argv[0], "pg_dump"); progname = get_progname(argv[0]); if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { help(); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { puts("pg_dumpall (PostgreSQL) " PG_VERSION); exit(0); } } if ((ret = find_other_exec(argv[0], "pg_dump", PG_VERSIONSTR, pg_dump_bin)) < 0) { char full_path[MAXPGPATH]; if (find_my_exec(argv[0], full_path) < 0) StrNCpy(full_path, progname, MAXPGPATH); if (ret == -1) fprintf(stderr, _("The program \"pg_dump\" is needed by %s " "but was not found in the\n" "same directory as \"%s\".\n" "Check your installation.\n"), progname, full_path); else fprintf(stderr, _("The program \"pg_dump\" was found by \"%s\"\n" "but was not the same version as %s.\n" "Check your installation.\n"), full_path, progname); exit(1); } pgdumpopts = createPQExpBuffer(); while ((c = getopt_long(argc, argv, "acdDgh:ioOp:sS:U:vWxX:", long_options, &optindex)) != -1) { switch (c) { case 'a': data_only = true; appendPQExpBuffer(pgdumpopts, " -a"); break; case 'c': output_clean = true; break; case 'd': case 'D': appendPQExpBuffer(pgdumpopts, " -%c", c); break; case 'g': globals_only = true; break; case 'h': pghost = optarg; #ifndef WIN32 appendPQExpBuffer(pgdumpopts, " -h '%s'", pghost); #else appendPQExpBuffer(pgdumpopts, " -h \"%s\"", pghost); #endif break; case 'i': ignoreVersion = true; appendPQExpBuffer(pgdumpopts, " -i"); break; case 'o': appendPQExpBuffer(pgdumpopts, " -o"); break; case 'O': appendPQExpBuffer(pgdumpopts, " -O"); break; case 'p': pgport = optarg; #ifndef WIN32 appendPQExpBuffer(pgdumpopts, " -p '%s'", pgport); #else appendPQExpBuffer(pgdumpopts, " -p \"%s\"", pgport); #endif break; case 's': schema_only = true; appendPQExpBuffer(pgdumpopts, " -s"); break; case 'S': #ifndef WIN32 appendPQExpBuffer(pgdumpopts, " -S '%s'", optarg); #else appendPQExpBuffer(pgdumpopts, " -S \"%s\"", optarg); #endif break; case 'U': pguser = optarg; #ifndef WIN32 appendPQExpBuffer(pgdumpopts, " -U '%s'", pguser); #else appendPQExpBuffer(pgdumpopts, " -U \"%s\"", pguser); #endif break; case 'v': verbose = true; appendPQExpBuffer(pgdumpopts, " -v"); break; case 'W': force_password = true; appendPQExpBuffer(pgdumpopts, " -W"); break; case 'x': skip_acls = true; appendPQExpBuffer(pgdumpopts, " -x"); break; case 'X': if (strcmp(optarg, "disable-dollar-quoting") == 0) appendPQExpBuffer(pgdumpopts, " -X disable-dollar-quoting"); else if (strcmp(optarg, "disable-triggers") == 0) appendPQExpBuffer(pgdumpopts, " -X disable-triggers"); else if (strcmp(optarg, "use-set-session-authorization") == 0) /* no-op, still allowed for compatibility */ ; 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: break; default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } } /* Add long options to the pg_dump argument list */ if (disable_dollar_quoting) appendPQExpBuffer(pgdumpopts, " -X disable-dollar-quoting"); if (disable_triggers) appendPQExpBuffer(pgdumpopts, " -X disable-triggers"); if (use_setsessauth) appendPQExpBuffer(pgdumpopts, " -X use-set-session-authorization"); if (optind < argc) { fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"), progname, argv[optind]); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } /* * First try to connect to database "postgres", and failing that * "template1". "postgres" is the preferred choice for 8.1 and later * servers, but it usually will not exist on older ones. */ conn = connectDatabase("postgres", pghost, pgport, pguser, force_password, false); if (!conn) conn = connectDatabase("template1", pghost, pgport, pguser, force_password, true); printf("--\n-- PostgreSQL database cluster dump\n--\n\n"); if (verbose) dumpTimestamp("Started on"); printf("\\connect postgres\n\n"); if (!data_only) { /* Dump roles (users) */ dumpRoles(conn); /* Dump role memberships --- need different method for pre-8.1 */ if (server_version >= 80100) dumpRoleMembership(conn); else dumpGroups(conn); /* Dump tablespaces */ if (server_version >= 80000) dumpTablespaces(conn); /* Dump CREATE DATABASE commands */ if (!globals_only) dumpCreateDB(conn); } if (!globals_only) dumpDatabases(conn); PQfinish(conn); if (verbose) dumpTimestamp("Completed on"); printf("--\n-- PostgreSQL database cluster dump complete\n--\n\n"); exit(0); }