Header And Logo

PostgreSQL
| The world's most advanced open source database.

Defines | Functions | Variables

pg_backup_db.c File Reference

#include "pg_backup_db.h"
#include "pg_backup_utils.h"
#include "dumputils.h"
#include "parallel.h"
#include <unistd.h>
#include <ctype.h>
Include dependency graph for pg_backup_db.c:

Go to the source code of this file.

Defines

#define DB_MAX_ERR_STMT   128
#define PARAMS_ARRAY_SIZE   7
#define PARAMS_ARRAY_SIZE   7

Functions

static void _check_database_version (ArchiveHandle *AH)
static PGconn_connectDB (ArchiveHandle *AH, const char *newdbname, const char *newUser)
static void notice_processor (void *arg, const char *message)
int ReconnectToServer (ArchiveHandle *AH, const char *dbname, const char *username)
void ConnectDatabase (Archive *AHX, const char *dbname, const char *pghost, const char *pgport, const char *username, enum trivalue prompt_password)
void DisconnectDatabase (Archive *AHX)
PGconnGetConnection (Archive *AHX)
static void die_on_query_failure (ArchiveHandle *AH, const char *modulename, const char *query)
void ExecuteSqlStatement (Archive *AHX, const char *query)
PGresultExecuteSqlQuery (Archive *AHX, const char *query, ExecStatusType status)
static void ExecuteSqlCommand (ArchiveHandle *AH, const char *qry, const char *desc)
static void ExecuteInsertCommands (ArchiveHandle *AH, const char *buf, size_t bufLen)
int ExecuteSqlCommandBuf (ArchiveHandle *AH, const char *buf, size_t bufLen)
void EndDBCopyMode (ArchiveHandle *AH, TocEntry *te)
void StartTransaction (ArchiveHandle *AH)
void CommitTransaction (ArchiveHandle *AH)
void DropBlobIfExists (ArchiveHandle *AH, Oid oid)

Variables

static const char * modulename = gettext_noop("archiver (db)")

Define Documentation

#define DB_MAX_ERR_STMT   128

Definition at line 25 of file pg_backup_db.c.

Referenced by ExecuteSqlCommand().

#define PARAMS_ARRAY_SIZE   7
#define PARAMS_ARRAY_SIZE   7

Function Documentation

static void _check_database_version ( ArchiveHandle AH  )  [static]

Definition at line 35 of file pg_backup_db.c.

References _archiveHandle::archiveRemoteVersion, _archiveHandle::connection, exit_horribly(), Archive::maxRemoteVersion, modulename, NULL, pg_strdup(), PQparameterStatus(), PQserverVersion(), progname, _archiveHandle::public, Archive::remoteVersion, Archive::remoteVersionStr, and write_msg().

Referenced by _connectDB(), and ConnectDatabase().

{
    const char *remoteversion_str;
    int         remoteversion;

    remoteversion_str = PQparameterStatus(AH->connection, "server_version");
    remoteversion = PQserverVersion(AH->connection);
    if (remoteversion == 0 || !remoteversion_str)
        exit_horribly(modulename, "could not get server_version from libpq\n");

    AH->public.remoteVersionStr = pg_strdup(remoteversion_str);
    AH->public.remoteVersion = remoteversion;
    if (!AH->archiveRemoteVersion)
        AH->archiveRemoteVersion = AH->public.remoteVersionStr;

    if (remoteversion != PG_VERSION_NUM
        && (remoteversion < AH->public.minRemoteVersion ||
            remoteversion > AH->public.maxRemoteVersion))
    {
        write_msg(NULL, "server version: %s; %s version: %s\n",
                  remoteversion_str, progname, PG_VERSION);
        exit_horribly(NULL, "aborting because of server version mismatch\n");
    }
}

static PGconn * _connectDB ( ArchiveHandle AH,
const char *  newdbname,
const char *  newUser 
) [static]

Definition at line 109 of file pg_backup_db.c.

References _check_database_version(), ahlog(), _archiveHandle::connection, CONNECTION_BAD, exit_horribly(), free, modulename, notice_processor(), NULL, PARAMS_ARRAY_SIZE, pg_malloc(), PQconnectdbParams(), PQconnectionNeedsPassword(), PQdb(), PQerrorMessage(), PQfinish(), PQhost(), PQport(), PQsetNoticeProcessor(), PQstatus(), PQuser(), progname, _archiveHandle::promptPassword, _archiveHandle::savedPassword, simple_prompt(), TRI_NO, TRI_YES, and values.

Referenced by ReconnectToServer().

{
    PGconn     *newConn;
    const char *newdb;
    const char *newuser;
    char       *password = AH->savedPassword;
    bool        new_pass;

    if (!reqdb)
        newdb = PQdb(AH->connection);
    else
        newdb = reqdb;

    if (!requser || strlen(requser) == 0)
        newuser = PQuser(AH->connection);
    else
        newuser = requser;

    ahlog(AH, 1, "connecting to database \"%s\" as user \"%s\"\n",
          newdb, newuser);

    if (AH->promptPassword == TRI_YES && password == NULL)
    {
        password = simple_prompt("Password: ", 100, false);
        if (password == NULL)
            exit_horribly(modulename, "out of memory\n");
    }

    do
    {
#define PARAMS_ARRAY_SIZE   7
        const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
        const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));

        keywords[0] = "host";
        values[0] = PQhost(AH->connection);
        keywords[1] = "port";
        values[1] = PQport(AH->connection);
        keywords[2] = "user";
        values[2] = newuser;
        keywords[3] = "password";
        values[3] = password;
        keywords[4] = "dbname";
        values[4] = newdb;
        keywords[5] = "fallback_application_name";
        values[5] = progname;
        keywords[6] = NULL;
        values[6] = NULL;

        new_pass = false;
        newConn = PQconnectdbParams(keywords, values, true);

        free(keywords);
        free(values);

        if (!newConn)
            exit_horribly(modulename, "failed to reconnect to database\n");

        if (PQstatus(newConn) == CONNECTION_BAD)
        {
            if (!PQconnectionNeedsPassword(newConn))
                exit_horribly(modulename, "could not reconnect to database: %s",
                              PQerrorMessage(newConn));
            PQfinish(newConn);

            if (password)
                fprintf(stderr, "Password incorrect\n");

            fprintf(stderr, "Connecting to %s as %s\n",
                    newdb, newuser);

            if (password)
                free(password);

            if (AH->promptPassword != TRI_NO)
                password = simple_prompt("Password: ", 100, false);
            else
                exit_horribly(modulename, "connection needs password\n");

            if (password == NULL)
                exit_horribly(modulename, "out of memory\n");
            new_pass = true;
        }
    } while (new_pass);

    AH->savedPassword = password;

    /* check for version mismatch */
    _check_database_version(AH);

    PQsetNoticeProcessor(newConn, notice_processor, NULL);

    return newConn;
}

void CommitTransaction ( ArchiveHandle AH  ) 

Definition at line 578 of file pg_backup_db.c.

References ExecuteSqlCommand().

{
    ExecuteSqlCommand(AH, "COMMIT", "could not commit database transaction");
}

void ConnectDatabase ( Archive AHX,
const char *  dbname,
const char *  pghost,
const char *  pgport,
const char *  username,
enum trivalue  prompt_password 
)

Definition at line 215 of file pg_backup_db.c.

References _check_database_version(), _archiveHandle::connection, CONNECTION_BAD, exit_horribly(), free, modulename, notice_processor(), NULL, PARAMS_ARRAY_SIZE, pg_malloc(), PQconnectdbParams(), PQconnectionNeedsPassword(), PQdb(), PQerrorMessage(), PQfinish(), PQsetNoticeProcessor(), PQstatus(), progname, _archiveHandle::promptPassword, _archiveHandle::savedPassword, simple_prompt(), TRI_NO, TRI_YES, and values.

Referenced by CloneArchive(), main(), restore_toc_entries_postfork(), and RestoreArchive().

{
    ArchiveHandle *AH = (ArchiveHandle *) AHX;
    char       *password = AH->savedPassword;
    bool        new_pass;

    if (AH->connection)
        exit_horribly(modulename, "already connected to a database\n");

    if (prompt_password == TRI_YES && password == NULL)
    {
        password = simple_prompt("Password: ", 100, false);
        if (password == NULL)
            exit_horribly(modulename, "out of memory\n");
    }
    AH->promptPassword = prompt_password;

    /*
     * Start the connection.  Loop until we have a password if requested by
     * backend.
     */
    do
    {
#define PARAMS_ARRAY_SIZE   7
        const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
        const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));

        keywords[0] = "host";
        values[0] = pghost;
        keywords[1] = "port";
        values[1] = pgport;
        keywords[2] = "user";
        values[2] = username;
        keywords[3] = "password";
        values[3] = password;
        keywords[4] = "dbname";
        values[4] = dbname;
        keywords[5] = "fallback_application_name";
        values[5] = progname;
        keywords[6] = NULL;
        values[6] = NULL;

        new_pass = false;
        AH->connection = PQconnectdbParams(keywords, values, true);

        free(keywords);
        free(values);

        if (!AH->connection)
            exit_horribly(modulename, "failed to connect to database\n");

        if (PQstatus(AH->connection) == CONNECTION_BAD &&
            PQconnectionNeedsPassword(AH->connection) &&
            password == NULL &&
            prompt_password != TRI_NO)
        {
            PQfinish(AH->connection);
            password = simple_prompt("Password: ", 100, false);
            if (password == NULL)
                exit_horribly(modulename, "out of memory\n");
            new_pass = true;
        }
    } while (new_pass);

    AH->savedPassword = password;

    /* check to see that the backend connection was successfully made */
    if (PQstatus(AH->connection) == CONNECTION_BAD)
        exit_horribly(modulename, "connection to database \"%s\" failed: %s",
                      PQdb(AH->connection) ? PQdb(AH->connection) : "",
                      PQerrorMessage(AH->connection));

    /* check for version mismatch */
    _check_database_version(AH);

    PQsetNoticeProcessor(AH->connection, notice_processor, NULL);
}

static void die_on_query_failure ( ArchiveHandle AH,
const char *  modulename,
const char *  query 
) [static]

Definition at line 341 of file pg_backup_db.c.

References _archiveHandle::connection, exit_horribly(), PQerrorMessage(), and write_msg().

Referenced by ExecuteSqlQuery(), and ExecuteSqlStatement().

{
    write_msg(modulename, "query failed: %s",
              PQerrorMessage(AH->connection));
    exit_horribly(modulename, "query was: %s\n", query);
}

void DisconnectDatabase ( Archive AHX  ) 

Definition at line 303 of file pg_backup_db.c.

References _archiveHandle::connection, PQcancel(), PQfinish(), PQfreeCancel(), PQgetCancel(), and PQtransactionStatus().

Referenced by archive_close_connection(), restore_toc_entries_prefork(), and RestoreArchive().

{
    ArchiveHandle *AH = (ArchiveHandle *) AHX;
    PGcancel   *cancel;
    char        errbuf[1];

    if (!AH->connection)
        return;

    if (PQtransactionStatus(AH->connection) == PQTRANS_ACTIVE)
    {
        if ((cancel = PQgetCancel(AH->connection)))
        {
            PQcancel(cancel, errbuf, sizeof(errbuf));
            PQfreeCancel(cancel);
        }
    }

    PQfinish(AH->connection);
    AH->connection = NULL;
}

void DropBlobIfExists ( ArchiveHandle AH,
Oid  oid 
)

Definition at line 584 of file pg_backup_db.c.

References ahprintf(), _archiveHandle::connection, NULL, and PQserverVersion().

Referenced by _StartBlob(), and StartRestoreBlob().

{
    /*
     * If we are not restoring to a direct database connection, we have to
     * guess about how to detect whether the blob exists.  Assume new-style.
     */
    if (AH->connection == NULL ||
        PQserverVersion(AH->connection) >= 90000)
    {
        ahprintf(AH,
                 "SELECT pg_catalog.lo_unlink(oid) "
                 "FROM pg_catalog.pg_largeobject_metadata "
                 "WHERE oid = '%u';\n",
                 oid);
    }
    else
    {
        /* Restoring to pre-9.0 server, so do it the old way */
        ahprintf(AH,
                 "SELECT CASE WHEN EXISTS("
                 "SELECT 1 FROM pg_catalog.pg_largeobject WHERE loid = '%u'"
                 ") THEN pg_catalog.lo_unlink('%u') END;\n",
                 oid, oid);
    }
}

void EndDBCopyMode ( ArchiveHandle AH,
TocEntry te 
)

Definition at line 550 of file pg_backup_db.c.

References _archiveHandle::connection, exit_horribly(), modulename, NULL, _archiveHandle::pgCopyIn, PGRES_COMMAND_OK, PQclear(), PQerrorMessage(), PQgetResult(), PQputCopyEnd(), PQresultStatus(), _tocEntry::tag, and warn_or_exit_horribly().

Referenced by restore_toc_entry().

{
    if (AH->pgCopyIn)
    {
        PGresult   *res;

        if (PQputCopyEnd(AH->connection, NULL) <= 0)
            exit_horribly(modulename, "error returned by PQputCopyEnd: %s",
                          PQerrorMessage(AH->connection));

        /* Check command status and return to normal libpq state */
        res = PQgetResult(AH->connection);
        if (PQresultStatus(res) != PGRES_COMMAND_OK)
            warn_or_exit_horribly(AH, modulename, "COPY failed for table \"%s\": %s",
                                  te->tag, PQerrorMessage(AH->connection));
        PQclear(res);

        AH->pgCopyIn = false;
    }
}

static void ExecuteInsertCommands ( ArchiveHandle AH,
const char *  buf,
size_t  bufLen 
) [static]

Definition at line 433 of file pg_backup_db.c.

References appendPQExpBufferChar(), sqlparseInfo::backSlash, createPQExpBuffer(), sqlparseInfo::curCmd, PQExpBufferData::data, ExecuteSqlCommand(), PQExpBufferData::len, NULL, _archiveHandle::public, resetPQExpBuffer(), SQL_IN_DOUBLE_QUOTE, SQL_IN_SINGLE_QUOTE, SQL_SCAN, _archiveHandle::sqlparse, sqlparseInfo::state, and Archive::std_strings.

Referenced by ExecuteSqlCommandBuf().

{
    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;
        }
    }
}

static void ExecuteSqlCommand ( ArchiveHandle AH,
const char *  qry,
const char *  desc 
) [static]

Definition at line 377 of file pg_backup_db.c.

References conn, _archiveHandle::connection, DB_MAX_ERR_STMT, modulename, _archiveHandle::pgCopyIn, PGRES_COMMAND_OK, PGRES_COPY_IN, PGRES_EMPTY_QUERY, PGRES_TUPLES_OK, PQclear(), PQerrorMessage(), PQexec(), PQresultStatus(), and warn_or_exit_horribly().

Referenced by CommitTransaction(), ExecuteInsertCommands(), ExecuteSqlCommandBuf(), and StartTransaction().

{
    PGconn     *conn = AH->connection;
    PGresult   *res;
    char        errStmt[DB_MAX_ERR_STMT];

#ifdef NOT_USED
    fprintf(stderr, "Executing: '%s'\n\n", qry);
#endif
    res = PQexec(conn, qry);

    switch (PQresultStatus(res))
    {
        case PGRES_COMMAND_OK:
        case PGRES_TUPLES_OK:
        case PGRES_EMPTY_QUERY:
            /* A-OK */
            break;
        case PGRES_COPY_IN:
            /* Assume this is an expected result */
            AH->pgCopyIn = true;
            break;
        default:
            /* trouble */
            strncpy(errStmt, qry, DB_MAX_ERR_STMT);
            if (errStmt[DB_MAX_ERR_STMT - 1] != '\0')
            {
                errStmt[DB_MAX_ERR_STMT - 4] = '.';
                errStmt[DB_MAX_ERR_STMT - 3] = '.';
                errStmt[DB_MAX_ERR_STMT - 2] = '.';
                errStmt[DB_MAX_ERR_STMT - 1] = '\0';
            }
            warn_or_exit_horribly(AH, modulename, "%s: %s    Command was: %s\n",
                                  desc, PQerrorMessage(conn), errStmt);
            break;
    }

    PQclear(res);
}

int ExecuteSqlCommandBuf ( ArchiveHandle AH,
const char *  buf,
size_t  bufLen 
)

Definition at line 498 of file pg_backup_db.c.

References _archiveHandle::connection, ExecuteInsertCommands(), ExecuteSqlCommand(), exit_horribly(), free, modulename, OUTPUT_COPYDATA, OUTPUT_OTHERDATA, _archiveHandle::outputKind, pg_malloc(), _archiveHandle::pgCopyIn, PQerrorMessage(), and PQputCopyData().

Referenced by ahwrite().

{
    if (AH->outputKind == OUTPUT_COPYDATA)
    {
        /*
         * COPY data.
         *
         * We drop the data on the floor if libpq has failed to enter COPY
         * mode; this allows us to behave reasonably when trying to continue
         * after an error in a COPY command.
         */
        if (AH->pgCopyIn &&
            PQputCopyData(AH->connection, buf, bufLen) <= 0)
            exit_horribly(modulename, "error returned by PQputCopyData: %s",
                          PQerrorMessage(AH->connection));
    }
    else if (AH->outputKind == OUTPUT_OTHERDATA)
    {
        /*
         * Table data expressed as INSERT commands.
         */
        ExecuteInsertCommands(AH, buf, bufLen);
    }
    else
    {
        /*
         * General SQL commands; we assume that commands will not be split
         * across calls.
         *
         * In most cases the data passed to us will be a null-terminated
         * string, but if it's not, we have to add a trailing null.
         */
        if (buf[bufLen] == '\0')
            ExecuteSqlCommand(AH, buf, "could not execute query");
        else
        {
            char       *str = (char *) pg_malloc(bufLen + 1);

            memcpy(str, buf, bufLen);
            str[bufLen] = '\0';
            ExecuteSqlCommand(AH, str, "could not execute query");
            free(str);
        }
    }

    return 1;
}

PGresult* ExecuteSqlQuery ( Archive AHX,
const char *  query,
ExecStatusType  status 
)
void ExecuteSqlStatement ( Archive AHX,
const char *  query 
)
PGconn* GetConnection ( Archive AHX  ) 

Definition at line 326 of file pg_backup_db.c.

References _archiveHandle::connection.

{
    ArchiveHandle *AH = (ArchiveHandle *) AHX;

    return AH->connection;
}

static void notice_processor ( void *  arg,
const char *  message 
) [static]

Definition at line 334 of file pg_backup_db.c.

References NULL, and write_msg().

Referenced by _connectDB(), and ConnectDatabase().

{
    write_msg(NULL, "%s", message);
}

int ReconnectToServer ( ArchiveHandle AH,
const char *  dbname,
const char *  username 
)

Definition at line 70 of file pg_backup_db.c.

References _connectDB(), _archiveHandle::connection, PQdb(), PQfinish(), and PQuser().

Referenced by _reconnectToDB().

{
    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(newdbname, PQdb(AH->connection)) == 0 &&
        strcmp(newusername, PQuser(AH->connection)) == 0)
        return 1;

    newConn = _connectDB(AH, newdbname, newusername);

    PQfinish(AH->connection);
    AH->connection = newConn;

    return 1;
}

void StartTransaction ( ArchiveHandle AH  ) 

Definition at line 572 of file pg_backup_db.c.

References ExecuteSqlCommand().

{
    ExecuteSqlCommand(AH, "BEGIN", "could not start database transaction");
}


Variable Documentation

const char* modulename = gettext_noop("archiver (db)") [static]