Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

SSL pg_backup_db.c   Interaktion und
Portierbarkeitunbekannt

 
/*-------------------------------------------------------------------------
 *
 * pg_backup_db.c
 *
 * Implements the basic DB functions used by the archiver.
 *
 * IDENTIFICATION
 *   src/bin/pg_dump/pg_backup_db.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres_fe.h"

#include <unistd.h>
#include <ctype.h>
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif

#include "common/connect.h"
#include "common/string.h"
#include "connectdb.h"
#include "parallel.h"
#include "pg_backup_archiver.h"
#include "pg_backup_db.h"
#include "pg_backup_utils.h"

static void _check_database_version(ArchiveHandle *AH);
static void notice_processor(void *arg, const char *message);

static void
_check_database_version(ArchiveHandle *AH)
{
 const char *remoteversion_str;
 int   remoteversion;
 PGresult   *res;

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

 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))
 {
  pg_log_error("aborting because of server version mismatch");
  pg_log_error_detail("server version: %s; %s version: %s",
       remoteversion_str, progname, PG_VERSION);
  exit(1);
 }

 /*
  * Check if server is in recovery mode, which means we are on a hot
  * standby.
 */

 res = ExecuteSqlQueryForSingleRow((Archive *) AH,
           "SELECT pg_catalog.pg_is_in_recovery()");
 AH->public.isStandby = (strcmp(PQgetvalue(res, 00), "t") == 0);
 PQclear(res);
}

/*
 * Reconnect to the server.  If dbname is not NULL, use that database,
 * else the one associated with the archive handle.
 */

void
ReconnectToServer(ArchiveHandle *AH, const char *dbname)
{
 PGconn    *oldConn = AH->connection;
 RestoreOptions *ropt = AH->public.ropt;

 /*
  * Save the dbname, if given, in override_dbname so that it will also
  * affect any later reconnection attempt.
 */

 if (dbname)
  ropt->cparams.override_dbname = pg_strdup(dbname);

 /*
  * Note: we want to establish the new connection, and in particular update
  * ArchiveHandle's connCancel, before closing old connection.  Otherwise
  * an ill-timed SIGINT could try to access a dead connection.
 */

 AH->connection = NULL;  /* dodge error check in ConnectDatabaseAhx */

 ConnectDatabaseAhx((Archive *) AH, &ropt->cparams, true);

 PQfinish(oldConn);
}

/*
 * Make, or remake, a database connection with the given parameters.
 *
 * The resulting connection handle is stored in AHX->connection.
 *
 * An interactive password prompt is automatically issued if required.
 * We store the results of that in AHX->savedPassword.
 * Note: it's not really all that sensible to use a single-entry password
 * cache if the username keeps changing.  In current usage, however, the
 * username never does change, so one savedPassword is sufficient.
 */

void
ConnectDatabaseAhx(Archive *AHX,
       const ConnParams *cparams,
       bool isReconnect)
{
 ArchiveHandle *AH = (ArchiveHandle *) AHX;
 trivalue prompt_password;
 char    *password;

 if (AH->connection)
  pg_fatal("already connected to a database");

 /* Never prompt for a password during a reconnection */
 prompt_password = isReconnect ? TRI_NO : cparams->promptPassword;

 password = AH->savedPassword;

 if (prompt_password == TRI_YES && password == NULL)
  password = simple_prompt("Password: "false);

 AH->connection = ConnectDatabase(cparams->dbname, NULL, cparams->pghost,
          cparams->pgport, cparams->username,
          prompt_password, true,
          progname, NULL, NULL, password, cparams->override_dbname);

 /* Start strict; later phases may override this. */
 PQclear(ExecuteSqlQueryForSingleRow((Archive *) AH,
          ALWAYS_SECURE_SEARCH_PATH_SQL));

 if (password && password != AH->savedPassword)
  free(password);

 /*
  * We want to remember connection's actual password, whether or not we got
  * it by prompting.  So we don't just store the password variable.
 */

 if (PQconnectionUsedPassword(AH->connection))
 {
  free(AH->savedPassword);
  AH->savedPassword = pg_strdup(PQpass(AH->connection));
 }

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

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

 /* arrange for SIGINT to issue a query cancel on this connection */
 set_archive_cancel_info(AH, AH->connection);
}

/*
 * Close the connection to the database and also cancel off the query if we
 * have one running.
 */

void
DisconnectDatabase(Archive *AHX)
{
 ArchiveHandle *AH = (ArchiveHandle *) AHX;
 char  errbuf[1];

 if (!AH->connection)
  return;

 if (AH->connCancel)
 {
  /*
   * If we have an active query, send a cancel before closing, ignoring
   * any errors.  This is of no use for a normal exit, but might be
   * helpful during pg_fatal().
 */

  if (PQtransactionStatus(AH->connection) == PQTRANS_ACTIVE)
   (void) PQcancel(AH->connCancel, errbuf, sizeof(errbuf));

  /*
   * Prevent signal handler from sending a cancel after this.
 */

  set_archive_cancel_info(AH, NULL);
 }

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

PGconn *
GetConnection(Archive *AHX)
{
 ArchiveHandle *AH = (ArchiveHandle *) AHX;

 return AH->connection;
}

static void
notice_processor(void *arg, const char *message)
{
 pg_log_info("%s", message);
}

/* Like pg_fatal(), but with a complaint about a particular query. */
static void
die_on_query_failure(ArchiveHandle *AH, const char *query)
{
 pg_log_error("query failed: %s",
     PQerrorMessage(AH->connection));
 pg_log_error_detail("Query was: %s", query);
 exit(1);
}

void
ExecuteSqlStatement(Archive *AHX, const char *query)
{
 ArchiveHandle *AH = (ArchiveHandle *) AHX;
 PGresult   *res;

 res = PQexec(AH->connection, query);
 if (PQresultStatus(res) != PGRES_COMMAND_OK)
  die_on_query_failure(AH, query);
 PQclear(res);
}

PGresult *
ExecuteSqlQuery(Archive *AHX, const char *query, ExecStatusType status)
{
 ArchiveHandle *AH = (ArchiveHandle *) AHX;
 PGresult   *res;

 res = PQexec(AH->connection, query);
 if (PQresultStatus(res) != status)
  die_on_query_failure(AH, query);
 return res;
}

/*
 * Execute an SQL query and verify that we got exactly one row back.
 */

PGresult *
ExecuteSqlQueryForSingleRow(Archive *fout, const char *query)
{
 PGresult   *res;
 int   ntups;

 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);

 /* Expecting a single result only */
 ntups = PQntuples(res);
 if (ntups != 1)
  pg_fatal(ngettext("query returned %d row instead of one: %s",
        "query returned %d rows instead of one: %s",
        ntups),
     ntups, query);

 return res;
}

/*
 * Convenience function to send a query.
 * Monitors result to detect COPY statements
 */

static void
ExecuteSqlCommand(ArchiveHandle *AH, const char *qry, const char *desc)
{
 PGconn    *conn = AH->connection;
 PGresult   *res;

#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 */
   warn_or_exit_horribly(AH, "%s: %sCommand was: %s",
          desc, PQerrorMessage(conn), qry);
   break;
 }

 PQclear(res);
}


/*
 * 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;
  }
 }
}


/*
 * Implement ahwrite() for direct-to-DB restore
 */

int
ExecuteSqlCommandBuf(Archive *AHX, const char *buf, size_t bufLen)
{
 ArchiveHandle *AH = (ArchiveHandle *) AHX;

 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)
   pg_fatal("error returned by PQputCopyData: %s",
      PQerrorMessage(AH->connection));
 }
 else if (AH->outputKind == OUTPUT_OTHERDATA)
 {
  /*
   * Table data expressed as INSERT commands; or, in old dump files,
   * BLOB COMMENTS data (which is expressed as COMMENT ON commands).
 */

  ExecuteSimpleCommands(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 bufLen;
}

/*
 * Terminate a COPY operation during direct-to-DB restore
 */

void
EndDBCopyMode(Archive *AHX, const char *tocEntryTag)
{
 ArchiveHandle *AH = (ArchiveHandle *) AHX;

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

  if (PQputCopyEnd(AH->connection, NULL) <= 0)
   pg_fatal("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, "COPY failed for table \"%s\": %s",
          tocEntryTag, PQerrorMessage(AH->connection));
  PQclear(res);

  /* Do this to ensure we've pumped libpq back to idle state */
  if (PQgetResult(AH->connection) != NULL)
   pg_log_warning("unexpected extra results during COPY of table \"%s\"",
         tocEntryTag);

  AH->pgCopyIn = false;
 }
}

void
StartTransaction(Archive *AHX)
{
 ArchiveHandle *AH = (ArchiveHandle *) AHX;

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

void
CommitTransaction(Archive *AHX)
{
 ArchiveHandle *AH = (ArchiveHandle *) AHX;

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

/*
 * Issue per-blob commands for the large object(s) listed in the TocEntry
 *
 * The TocEntry's defn string is assumed to consist of large object OIDs,
 * one per line.  Wrap these in the given SQL command fragments and issue
 * the commands.  (cmdEnd need not include a semicolon.)
 */

void
IssueCommandPerBlob(ArchiveHandle *AH, TocEntry *te,
     const char *cmdBegin, const char *cmdEnd)
{
 /* Make a writable copy of the command string */
 char    *buf = pg_strdup(te->defn);
 RestoreOptions *ropt = AH->public.ropt;
 char    *st;
 char    *en;

 st = buf;
 while ((en = strchr(st, '\n')) != NULL)
 {
  *en++ = '\0';
  ahprintf(AH, "%s%s%s;\n", cmdBegin, st, cmdEnd);

  /* In --transaction-size mode, count each command as an action */
  if (ropt && ropt->txn_size > 0)
  {
   if (++AH->txnCount >= ropt->txn_size)
   {
    if (AH->connection)
    {
     CommitTransaction(&AH->public);
     StartTransaction(&AH->public);
    }
    else
     ahprintf(AH, "COMMIT;\nBEGIN;\n\n");
    AH->txnCount = 0;
   }
  }

  st = en;
 }
 ahprintf(AH, "\n");
 pg_free(buf);
}

/*
 * Process a "LARGE OBJECTS" ACL TocEntry.
 *
 * To save space in the dump file, the TocEntry contains only one copy
 * of the required GRANT/REVOKE commands, written to apply to the first
 * blob in the group (although we do not depend on that detail here).
 * We must expand the text to generate commands for all the blobs listed
 * in the associated BLOB METADATA entry.
 */

void
IssueACLPerBlob(ArchiveHandle *AH, TocEntry *te)
{
 TocEntry   *blobte = getTocEntryByDumpId(AH, te->dependencies[0]);
 char    *buf;
 char    *st;
 char    *st2;
 char    *en;
 bool  inquotes;

 if (!blobte)
  pg_fatal("could not find entry for ID %d", te->dependencies[0]);
 Assert(strcmp(blobte->desc, "BLOB METADATA") == 0);

 /* Make a writable copy of the ACL commands string */
 buf = pg_strdup(te->defn);

 /*
  * We have to parse out the commands sufficiently to locate the blob OIDs
  * and find the command-ending semicolons.  The commands should not
  * contain anything hard to parse except for double-quoted role names,
  * which are easy to ignore.  Once we've split apart the first and second
  * halves of a command, apply IssueCommandPerBlob.  (This means the
  * updates on the blobs are interleaved if there's multiple commands, but
  * that should cause no trouble.)
 */

 inquotes = false;
 st = en = buf;
 st2 = NULL;
 while (*en)
 {
  /* Ignore double-quoted material */
  if (*en == '"')
   inquotes = !inquotes;
  if (inquotes)
  {
   en++;
   continue;
  }
  /* If we found "LARGE OBJECT", that's the end of the first half */
  if (strncmp(en, "LARGE OBJECT "13) == 0)
  {
   /* Terminate the first-half string */
   en += 13;
   Assert(isdigit((unsigned char) *en));
   *en++ = '\0';
   /* Skip the rest of the blob OID */
   while (isdigit((unsigned char) *en))
    en++;
   /* Second half starts here */
   Assert(st2 == NULL);
   st2 = en;
  }
  /* If we found semicolon, that's the end of the second half */
  else if (*en == ';')
  {
   /* Terminate the second-half string */
   *en++ = '\0';
   Assert(st2 != NULL);
   /* Issue this command for each blob */
   IssueCommandPerBlob(AH, blobte, st, st2);
   /* For neatness, skip whitespace before the next command */
   while (isspace((unsigned char) *en))
    en++;
   /* Reset for new command */
   st = en;
   st2 = NULL;
  }
  else
   en++;
 }
 pg_free(buf);
}

void
DropLOIfExists(ArchiveHandle *AH, Oid oid)
{
 ahprintf(AH,
    "SELECT pg_catalog.lo_unlink(oid) "
    "FROM pg_catalog.pg_largeobject_metadata "
    "WHERE oid = '%u';\n",
    oid);
}

Messung V0.5 in Prozent
C=92 H=95 G=93

[Verzeichnis aufwärts0.18unsichere VerbindungÜbersetzung europäischer Sprachen durch Browser2026-08-07]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002