diff options
Diffstat (limited to 'src/bin/pg_dump/pg_backup_db.c')
-rw-r--r-- | src/bin/pg_dump/pg_backup_db.c | 879 |
1 files changed, 0 insertions, 879 deletions
diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c deleted file mode 100644 index fd0c8bd920a..00000000000 --- a/src/bin/pg_dump/pg_backup_db.c +++ /dev/null @@ -1,879 +0,0 @@ -/*------------------------------------------------------------------------- - * - * pg_backup_db.c - * - * Implements the basic DB functions used by the archiver. - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.33 2002/05/29 01:38:56 tgl Exp $ - * - *------------------------------------------------------------------------- - */ - -#include "pg_backup.h" -#include "pg_backup_archiver.h" -#include "pg_backup_db.h" - -#include <unistd.h> /* for getopt() */ -#include <ctype.h> - -#ifdef HAVE_TERMIOS_H -#include <termios.h> -#endif - -#include "libpq-fe.h" -#include "libpq/libpq-fs.h" -#ifndef HAVE_STRDUP -#include "strdup.h" -#endif - -static const char *modulename = gettext_noop("archiver (db)"); - -static void _check_database_version(ArchiveHandle *AH, bool ignoreVersion); -static PGconn *_connectDB(ArchiveHandle *AH, const char *newdbname, const char *newUser); -static int _executeSqlCommand(ArchiveHandle *AH, PGconn *conn, PQExpBuffer qry, char *desc); -static void notice_processor(void *arg, const char *message); -static char *_sendSQLLine(ArchiveHandle *AH, char *qry, char *eos); -static char *_sendCopyLine(ArchiveHandle *AH, char *qry, char *eos); - - -/* - * simple_prompt --- borrowed from psql - * - * Generalized function especially intended for reading in usernames and - * password interactively. Reads from /dev/tty or stdin/stderr. - * - * prompt: The prompt to print - * maxlen: How many characters to accept - * echo: Set to false if you want to hide what is entered (for passwords) - * - * Returns a malloc()'ed string with the input (w/o trailing newline). - */ -static bool prompt_state = false; - -char * -simple_prompt(const char *prompt, int maxlen, bool echo) -{ - int length; - char *destination; - FILE *termin, - *termout; - -#ifdef HAVE_TERMIOS_H - struct termios t_orig, - t; -#endif - - destination = (char *) malloc(maxlen + 2); - if (!destination) - return NULL; - - prompt_state = true; /* disable SIGINT */ - - /* - * Do not try to collapse these into one "w+" mode file. Doesn't work - * on some platforms (eg, HPUX 10.20). - */ - termin = fopen("/dev/tty", "r"); - termout = fopen("/dev/tty", "w"); - if (!termin || !termout) - { - if (termin) - fclose(termin); - if (termout) - fclose(termout); - termin = stdin; - termout = stderr; - } - -#ifdef HAVE_TERMIOS_H - if (!echo) - { - tcgetattr(fileno(termin), &t); - t_orig = t; - t.c_lflag &= ~ECHO; - tcsetattr(fileno(termin), TCSAFLUSH, &t); - } -#endif - - if (prompt) - { - fputs(gettext(prompt), termout); - fflush(termout); - } - - if (fgets(destination, maxlen, termin) == NULL) - destination[0] = '\0'; - - length = strlen(destination); - if (length > 0 && destination[length - 1] != '\n') - { - /* eat rest of the line */ - char buf[128]; - int buflen; - - do - { - if (fgets(buf, sizeof(buf), termin) == NULL) - break; - buflen = strlen(buf); - } while (buflen > 0 && buf[buflen - 1] != '\n'); - } - - if (length > 0 && destination[length - 1] == '\n') - /* remove trailing newline */ - destination[length - 1] = '\0'; - -#ifdef HAVE_TERMIOS_H - if (!echo) - { - tcsetattr(fileno(termin), TCSAFLUSH, &t_orig); - fputs("\n", termout); - fflush(termout); - } -#endif - - if (termin != stdin) - { - fclose(termin); - fclose(termout); - } - - prompt_state = false; /* SIGINT okay again */ - - return destination; -} - - -static int -_parse_version(ArchiveHandle *AH, const char *versionString) -{ - int cnt; - int vmaj, - vmin, - vrev; - - cnt = sscanf(versionString, "%d.%d.%d", &vmaj, &vmin, &vrev); - - if (cnt < 2) - die_horribly(AH, modulename, "unable to parse version string \"%s\"\n", versionString); - - if (cnt == 2) - vrev = 0; - - return (100 * vmaj + vmin) * 100 + vrev; -} - -static void -_check_database_version(ArchiveHandle *AH, bool ignoreVersion) -{ - PGresult *res; - int myversion; - const char *remoteversion_str; - int remoteversion; - PGconn *conn = AH->connection; - - myversion = _parse_version(AH, PG_VERSION); - - res = PQexec(conn, "SELECT version();"); - if (!res || - PQresultStatus(res) != PGRES_TUPLES_OK || - PQntuples(res) != 1) - - die_horribly(AH, modulename, "could not get version from server: %s", PQerrorMessage(conn)); - - remoteversion_str = PQgetvalue(res, 0, 0); - remoteversion = _parse_version(AH, remoteversion_str + 11); - - PQclear(res); - - AH->public.remoteVersion = remoteversion; - - if (myversion != remoteversion - && (remoteversion < AH->public.minRemoteVersion || remoteversion > AH->public.maxRemoteVersion)) - { - write_msg(NULL, "server version: %s; %s version: %s\n", - remoteversion_str, progname, PG_VERSION); - if (ignoreVersion) - write_msg(NULL, "proceeding despite version mismatch\n"); - else - die_horribly(AH, NULL, "aborting because of version mismatch (Use the -i option to proceed anyway.)\n"); - } -} - -/* - * Reconnect to the server. If dbname is not NULL, use that database, - * else the one associated with the archive handle. If username is - * not NULL, use that user name, else the one from the handle. If - * both the database and the user and match the existing connection - * already, nothing will be done. - * - * Returns 1 in any case. - */ -int -ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *username) -{ - PGconn *newConn; - const char *newdbname; - const char *newusername; - - if (!dbname) - newdbname = PQdb(AH->connection); - else - newdbname = dbname; - - if (!username) - newusername = PQuser(AH->connection); - else - newusername = username; - - /* Let's see if the request is already satisfied */ - if (strcmp(newusername, PQuser(AH->connection)) == 0 - && strcmp(newdbname, PQdb(AH->connection)) == 0) - return 1; - - newConn = _connectDB(AH, newdbname, newusername); - - PQfinish(AH->connection); - AH->connection = newConn; - - free(AH->username); - AH->username = strdup(newusername); - /* XXX Why don't we update AH->dbname? */ - - /* don't assume we still know the output schema */ - if (AH->currSchema) - free(AH->currSchema); - AH->currSchema = strdup(""); - - return 1; -} - -/* - * Connect to the db again. - */ -static PGconn * -_connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser) -{ - int need_pass; - PGconn *newConn; - char *password = NULL; - int badPwd = 0; - int noPwd = 0; - char *newdb; - char *newuser; - - if (!reqdb) - newdb = PQdb(AH->connection); - else - newdb = (char *) reqdb; - - if (!requser || (strlen(requser) == 0)) - newuser = PQuser(AH->connection); - else - newuser = (char *) requser; - - ahlog(AH, 1, "connecting to database %s as user %s\n", newdb, newuser); - - if (AH->requirePassword) - { - password = simple_prompt("Password: ", 100, false); - if (password == NULL) - die_horribly(AH, modulename, "out of memory\n"); - } - - do - { - need_pass = false; - newConn = PQsetdbLogin(PQhost(AH->connection), PQport(AH->connection), - NULL, NULL, newdb, - newuser, password); - if (!newConn) - die_horribly(AH, modulename, "failed to reconnect to database\n"); - - if (PQstatus(newConn) == CONNECTION_BAD) - { - noPwd = (strcmp(PQerrorMessage(newConn), - "fe_sendauth: no password supplied\n") == 0); - badPwd = (strncmp(PQerrorMessage(newConn), - "Password authentication failed for user", 39) == 0); - - if (noPwd || badPwd) - { - - if (badPwd) - fprintf(stderr, "Password incorrect\n"); - - fprintf(stderr, "Connecting to %s as %s\n", - PQdb(AH->connection), newuser); - - need_pass = true; - if (password) - free(password); - password = simple_prompt("Password: ", 100, false); - } - else - die_horribly(AH, modulename, "could not reconnect to database: %s", - PQerrorMessage(newConn)); - } - - } while (need_pass); - - if (password) - free(password); - - PQsetNoticeProcessor(newConn, notice_processor, NULL); - - return newConn; -} - - -/* - * Make a database connection with the given parameters. The - * connection handle is returned, the parameters are stored in AHX. - * An interactive password prompt is automatically issued if required. - */ -PGconn * -ConnectDatabase(Archive *AHX, - const char *dbname, - const char *pghost, - const char *pgport, - const char *username, - const int reqPwd, - const int ignoreVersion) -{ - ArchiveHandle *AH = (ArchiveHandle *) AHX; - char *password = NULL; - bool need_pass = false; - - if (AH->connection) - die_horribly(AH, modulename, "already connected to a database\n"); - - if (!dbname && !(dbname = getenv("PGDATABASE"))) - die_horribly(AH, modulename, "no database name specified\n"); - - AH->dbname = strdup(dbname); - - if (pghost != NULL) - AH->pghost = strdup(pghost); - else - AH->pghost = NULL; - - if (pgport != NULL) - AH->pgport = strdup(pgport); - else - AH->pgport = NULL; - - if (username != NULL) - AH->username = strdup(username); - else - AH->username = NULL; - - if (reqPwd) - { - password = simple_prompt("Password: ", 100, false); - if (password == NULL) - die_horribly(AH, modulename, "out of memory\n"); - AH->requirePassword = true; - } - else - AH->requirePassword = false; - - /* - * Start the connection. Loop until we have a password if requested - * by backend. - */ - do - { - need_pass = false; - AH->connection = PQsetdbLogin(AH->pghost, AH->pgport, NULL, NULL, - AH->dbname, AH->username, password); - - if (!AH->connection) - die_horribly(AH, modulename, "failed to connect to database\n"); - - if (PQstatus(AH->connection) == CONNECTION_BAD && - strcmp(PQerrorMessage(AH->connection), "fe_sendauth: no password supplied\n") == 0 && - !feof(stdin)) - { - PQfinish(AH->connection); - need_pass = true; - free(password); - password = NULL; - password = simple_prompt("Password: ", 100, false); - } - } while (need_pass); - - if (password) - free(password); - - /* check to see that the backend connection was successfully made */ - if (PQstatus(AH->connection) == CONNECTION_BAD) - die_horribly(AH, modulename, "connection to database \"%s\" failed: %s", - AH->dbname, PQerrorMessage(AH->connection)); - - /* check for version mismatch */ - _check_database_version(AH, ignoreVersion); - - PQsetNoticeProcessor(AH->connection, notice_processor, NULL); - - return AH->connection; -} - - -static void -notice_processor(void *arg, const char *message) -{ - write_msg(NULL, "%s", message); -} - - -/* Public interface */ -/* Convenience function to send a query. Monitors result to handle COPY statements */ -int -ExecuteSqlCommand(ArchiveHandle *AH, PQExpBuffer qry, char *desc, bool use_blob) -{ - if (use_blob) - return _executeSqlCommand(AH, AH->blobConnection, qry, desc); - else - return _executeSqlCommand(AH, AH->connection, qry, desc); -} - -/* - * Handle command execution. This is used to execute a command on more than one connection, - * but the 'pgCopyIn' setting assumes the COPY commands are ONLY executed on the primary - * setting...an error will be raised otherwise. - */ -static int -_executeSqlCommand(ArchiveHandle *AH, PGconn *conn, PQExpBuffer qry, char *desc) -{ - PGresult *res; - - /* fprintf(stderr, "Executing: '%s'\n\n", qry->data); */ - res = PQexec(conn, qry->data); - if (!res) - die_horribly(AH, modulename, "%s: no result from server\n", desc); - - if (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK) - { - if (PQresultStatus(res) == PGRES_COPY_IN) - { - if (conn != AH->connection) - die_horribly(AH, modulename, "COPY command executed in non-primary connection\n"); - - AH->pgCopyIn = 1; - } - else - die_horribly(AH, modulename, "%s: %s", - desc, PQerrorMessage(AH->connection)); - } - - PQclear(res); - - return strlen(qry->data); -} - -/* - * Used by ExecuteSqlCommandBuf to send one buffered line when running a COPY command. - */ -static char * -_sendCopyLine(ArchiveHandle *AH, char *qry, char *eos) -{ - int loc; /* Location of next newline */ - int pos = 0; /* Current position */ - int sPos = 0; /* Last pos of a slash char */ - int isEnd = 0; - - /* loop to find unquoted newline ending the line of COPY data */ - for (;;) - { - loc = strcspn(&qry[pos], "\n") + pos; - - /* If no match, then wait */ - if (loc >= (eos - qry)) /* None found */ - { - appendBinaryPQExpBuffer(AH->pgCopyBuf, qry, (eos - qry)); - return eos; - } - - /* - * fprintf(stderr, "Found cr at %d, prev char was %c, next was - * %c\n", loc, qry[loc-1], qry[loc+1]); - */ - - /* Count the number of preceding slashes */ - sPos = loc; - while (sPos > 0 && qry[sPos - 1] == '\\') - sPos--; - - sPos = loc - sPos; - - /* - * If an odd number of preceding slashes, then \n was escaped so - * set the next search pos, and loop (if any left). - */ - if ((sPos & 1) == 1) - { - /* fprintf(stderr, "cr was escaped\n"); */ - pos = loc + 1; - if (pos >= (eos - qry)) - { - appendBinaryPQExpBuffer(AH->pgCopyBuf, qry, (eos - qry)); - return eos; - } - } - else - break; - } - - /* We found an unquoted newline */ - qry[loc] = '\0'; - appendPQExpBuffer(AH->pgCopyBuf, "%s\n", qry); - isEnd = (strcmp(AH->pgCopyBuf->data, "\\.\n") == 0); - - /*--------- - * fprintf(stderr, "Sending '%s' via - * COPY (at end = %d)\n\n", AH->pgCopyBuf->data, isEnd); - *--------- - */ - - if (PQputline(AH->connection, AH->pgCopyBuf->data) != 0) - die_horribly(AH, modulename, "error returned by PQputline\n"); - - resetPQExpBuffer(AH->pgCopyBuf); - - /* - * fprintf(stderr, "Buffer is '%s'\n", AH->pgCopyBuf->data); - */ - - if (isEnd) - { - if (PQendcopy(AH->connection) != 0) - die_horribly(AH, modulename, "error returned by PQendcopy\n"); - - AH->pgCopyIn = 0; - } - - return qry + loc + 1; -} - -/* - * Used by ExecuteSqlCommandBuf to send one buffered line of SQL (not data for the copy command). - */ -static char * -_sendSQLLine(ArchiveHandle *AH, char *qry, char *eos) -{ - int pos = 0; /* Current position */ - - /* - * The following is a mini state machine to assess the end of an SQL - * statement. It really only needs to parse good SQL, or at least - * that's the theory... End-of-statement is assumed to be an unquoted, - * un commented semi-colon. - */ - - /* - * fprintf(stderr, "Buffer at start is: '%s'\n\n", AH->sqlBuf->data); - */ - - for (pos = 0; pos < (eos - qry); pos++) - { - appendPQExpBufferChar(AH->sqlBuf, qry[pos]); - /* fprintf(stderr, " %c",qry[pos]); */ - - switch (AH->sqlparse.state) - { - - case SQL_SCAN: /* Default state == 0, set in _allocAH */ - - if (qry[pos] == ';' && AH->sqlparse.braceDepth == 0) - { - /* Send It & reset the buffer */ - - /* - * fprintf(stderr, " sending: '%s'\n\n", - * AH->sqlBuf->data); - */ - ExecuteSqlCommand(AH, AH->sqlBuf, "could not execute query", false); - resetPQExpBuffer(AH->sqlBuf); - AH->sqlparse.lastChar = '\0'; - - /* - * Remove any following newlines - so that embedded - * COPY commands don't get a starting newline. - */ - pos++; - for (; pos < (eos - qry) && qry[pos] == '\n'; pos++); - - /* We've got our line, so exit */ - return qry + pos; - } - else - { - if (qry[pos] == '"' || qry[pos] == '\'') - { - /* fprintf(stderr,"[startquote]\n"); */ - AH->sqlparse.state = SQL_IN_QUOTE; - AH->sqlparse.quoteChar = qry[pos]; - AH->sqlparse.backSlash = 0; - } - else if (qry[pos] == '-' && AH->sqlparse.lastChar == '-') - AH->sqlparse.state = SQL_IN_SQL_COMMENT; - else if (qry[pos] == '*' && AH->sqlparse.lastChar == '/') - AH->sqlparse.state = SQL_IN_EXT_COMMENT; - else if (qry[pos] == '(') - AH->sqlparse.braceDepth++; - else if (qry[pos] == ')') - AH->sqlparse.braceDepth--; - - AH->sqlparse.lastChar = qry[pos]; - } - - break; - - case SQL_IN_SQL_COMMENT: - - if (qry[pos] == '\n') - AH->sqlparse.state = SQL_SCAN; - break; - - case SQL_IN_EXT_COMMENT: - - if (AH->sqlparse.lastChar == '*' && qry[pos] == '/') - AH->sqlparse.state = SQL_SCAN; - break; - - case SQL_IN_QUOTE: - - if (!AH->sqlparse.backSlash && AH->sqlparse.quoteChar == qry[pos]) - { - /* fprintf(stderr,"[endquote]\n"); */ - AH->sqlparse.state = SQL_SCAN; - } - else - { - - if (qry[pos] == '\\') - { - if (AH->sqlparse.lastChar == '\\') - AH->sqlparse.backSlash = !AH->sqlparse.backSlash; - else - AH->sqlparse.backSlash = 1; - } - else - AH->sqlparse.backSlash = 0; - } - break; - - } - AH->sqlparse.lastChar = qry[pos]; - /* fprintf(stderr, "\n"); */ - } - - /* - * If we get here, we've processed entire string with no complete SQL - * stmt - */ - return eos; - -} - - -/* Convenience function to send one or more queries. Monitors result to handle COPY statements */ -int -ExecuteSqlCommandBuf(ArchiveHandle *AH, void *qryv, int bufLen) -{ - char *qry = (char *) qryv; - char *eos = qry + bufLen; - - /* - * fprintf(stderr, "\n\n*****\n - * Buffer:\n\n%s\n*******************\n\n", qry); - */ - - /* Could switch between command and COPY IN mode at each line */ - while (qry < eos) - { - if (AH->pgCopyIn) - qry = _sendCopyLine(AH, qry, eos); - else - qry = _sendSQLLine(AH, qry, eos); - } - - return 1; -} - -void -FixupBlobRefs(ArchiveHandle *AH, TocEntry *te) -{ - PQExpBuffer tblName; - PQExpBuffer tblQry; - PGresult *res, - *uRes; - int i, - n; - char *attr; - - if (strcmp(te->name, BLOB_XREF_TABLE) == 0) - return; - - tblName = createPQExpBuffer(); - tblQry = createPQExpBuffer(); - - if (te->namespace && strlen(te->namespace) > 0) - appendPQExpBuffer(tblName, "%s.", - fmtId(te->namespace, false)); - appendPQExpBuffer(tblName, "%s", - fmtId(te->name, false)); - - appendPQExpBuffer(tblQry, - "SELECT a.attname FROM " - "pg_catalog.pg_attribute a, pg_catalog.pg_type t " - "WHERE a.attnum > 0 AND a.attrelid = '%s'::pg_catalog.regclass " - "AND a.atttypid = t.oid AND t.typname in ('oid', 'lo')", - tblName->data); - - res = PQexec(AH->blobConnection, tblQry->data); - if (!res) - die_horribly(AH, modulename, "could not find oid columns of table \"%s\": %s", - te->name, PQerrorMessage(AH->connection)); - - if ((n = PQntuples(res)) == 0) - { - /* nothing to do */ - ahlog(AH, 1, "no OID type columns in table %s\n", te->name); - } - - for (i = 0; i < n; i++) - { - attr = PQgetvalue(res, i, 0); - - ahlog(AH, 1, "fixing large object cross-references for %s.%s\n", - te->name, attr); - - resetPQExpBuffer(tblQry); - - /* Can't use fmtId twice in one call... */ - appendPQExpBuffer(tblQry, - "UPDATE %s SET %s = %s.newOid", - tblName->data, fmtId(attr, false), - BLOB_XREF_TABLE); - appendPQExpBuffer(tblQry, - " FROM %s WHERE %s.oldOid = %s.%s", - BLOB_XREF_TABLE, - BLOB_XREF_TABLE, - tblName->data, fmtId(attr, false)); - - ahlog(AH, 10, "SQL: %s\n", tblQry->data); - - uRes = PQexec(AH->blobConnection, tblQry->data); - if (!uRes) - die_horribly(AH, modulename, - "could not update column \"%s\" of table \"%s\": %s", - attr, te->name, PQerrorMessage(AH->blobConnection)); - - if (PQresultStatus(uRes) != PGRES_COMMAND_OK) - die_horribly(AH, modulename, - "error while updating column \"%s\" of table \"%s\": %s", - attr, te->name, PQerrorMessage(AH->blobConnection)); - - PQclear(uRes); - } - - PQclear(res); - destroyPQExpBuffer(tblName); - destroyPQExpBuffer(tblQry); -} - -/********** - * Convenient SQL calls - **********/ -void -CreateBlobXrefTable(ArchiveHandle *AH) -{ - PQExpBuffer qry = createPQExpBuffer(); - - /* IF we don't have a BLOB connection, then create one */ - if (!AH->blobConnection) - AH->blobConnection = _connectDB(AH, NULL, NULL); - - ahlog(AH, 1, "creating table for large object cross-references\n"); - - appendPQExpBuffer(qry, "Create Temporary Table %s(oldOid pg_catalog.oid, newOid pg_catalog.oid);", BLOB_XREF_TABLE); - - ExecuteSqlCommand(AH, qry, "could not create large object cross-reference table", true); - - resetPQExpBuffer(qry); - - appendPQExpBuffer(qry, "Create Unique Index %s_ix on %s(oldOid)", BLOB_XREF_TABLE, BLOB_XREF_TABLE); - ExecuteSqlCommand(AH, qry, "could not create index on large object cross-reference table", true); - - destroyPQExpBuffer(qry); -} - -void -InsertBlobXref(ArchiveHandle *AH, Oid old, Oid new) -{ - PQExpBuffer qry = createPQExpBuffer(); - - appendPQExpBuffer(qry, - "Insert Into %s(oldOid, newOid) Values ('%u', '%u');", - BLOB_XREF_TABLE, old, new); - - ExecuteSqlCommand(AH, qry, "could not create large object cross-reference entry", true); - - destroyPQExpBuffer(qry); -} - -void -StartTransaction(ArchiveHandle *AH) -{ - PQExpBuffer qry = createPQExpBuffer(); - - appendPQExpBuffer(qry, "Begin;"); - - ExecuteSqlCommand(AH, qry, "could not start database transaction", false); - AH->txActive = true; - - destroyPQExpBuffer(qry); -} - -void -StartTransactionXref(ArchiveHandle *AH) -{ - PQExpBuffer qry = createPQExpBuffer(); - - appendPQExpBuffer(qry, "Begin;"); - - ExecuteSqlCommand(AH, qry, - "could not start transaction for large object cross-references", true); - AH->blobTxActive = true; - - destroyPQExpBuffer(qry); -} - -void -CommitTransaction(ArchiveHandle *AH) -{ - PQExpBuffer qry = createPQExpBuffer(); - - appendPQExpBuffer(qry, "Commit;"); - - ExecuteSqlCommand(AH, qry, "could not commit database transaction", false); - AH->txActive = false; - - destroyPQExpBuffer(qry); -} - -void -CommitTransactionXref(ArchiveHandle *AH) -{ - PQExpBuffer qry = createPQExpBuffer(); - - appendPQExpBuffer(qry, "Commit;"); - - ExecuteSqlCommand(AH, qry, "could not commit transaction for large object cross-references", true); - AH->blobTxActive = false; - - destroyPQExpBuffer(qry); -} |