mirror of https://github.com/postgres/postgres
- Support for BLOB output from pg_dump and input via pg_restore - Support for direct DB connection in pg_restore - Fixes in support for --insert flag - pg_dump now outputs in modified OID orderREL7_1_STABLE
parent
e8f69be054
commit
c3e18804ff
@ -0,0 +1,497 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <unistd.h> /* for getopt() */ |
||||||
|
#include <ctype.h> |
||||||
|
|
||||||
|
#include "postgres.h" |
||||||
|
|
||||||
|
#ifdef HAVE_TERMIOS_H |
||||||
|
#include <termios.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "access/attnum.h" |
||||||
|
#include "access/htup.h" |
||||||
|
#include "catalog/pg_index.h" |
||||||
|
#include "catalog/pg_language.h" |
||||||
|
#include "catalog/pg_trigger.h" |
||||||
|
#include "catalog/pg_type.h" |
||||||
|
|
||||||
|
#include "libpq-fe.h" |
||||||
|
#include <libpq/libpq-fs.h> |
||||||
|
#ifndef HAVE_STRDUP |
||||||
|
#include "strdup.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "pg_dump.h" |
||||||
|
#include "pg_backup.h" |
||||||
|
#include "pg_backup_archiver.h" |
||||||
|
#include "pg_backup_db.h" |
||||||
|
|
||||||
|
static const char *progname = "Archiver(db)"; |
||||||
|
|
||||||
|
static void _prompt_for_password(char *username, char *password); |
||||||
|
static void _check_database_version(ArchiveHandle *AH, bool ignoreVersion); |
||||||
|
|
||||||
|
|
||||||
|
static void |
||||||
|
_prompt_for_password(char *username, char *password) |
||||||
|
{ |
||||||
|
char buf[512]; |
||||||
|
int length; |
||||||
|
|
||||||
|
#ifdef HAVE_TERMIOS_H |
||||||
|
struct termios t_orig, |
||||||
|
t; |
||||||
|
#endif |
||||||
|
|
||||||
|
fprintf(stderr, "Username: "); |
||||||
|
fflush(stderr); |
||||||
|
fgets(username, 100, stdin); |
||||||
|
length = strlen(username); |
||||||
|
/* skip rest of the line */ |
||||||
|
if (length > 0 && username[length - 1] != '\n') |
||||||
|
{ |
||||||
|
do |
||||||
|
{ |
||||||
|
fgets(buf, 512, stdin); |
||||||
|
} while (buf[strlen(buf) - 1] != '\n'); |
||||||
|
} |
||||||
|
if (length > 0 && username[length - 1] == '\n') |
||||||
|
username[length - 1] = '\0'; |
||||||
|
|
||||||
|
#ifdef HAVE_TERMIOS_H |
||||||
|
tcgetattr(0, &t); |
||||||
|
t_orig = t; |
||||||
|
t.c_lflag &= ~ECHO; |
||||||
|
tcsetattr(0, TCSADRAIN, &t); |
||||||
|
#endif |
||||||
|
fprintf(stderr, "Password: "); |
||||||
|
fflush(stderr); |
||||||
|
fgets(password, 100, stdin); |
||||||
|
#ifdef HAVE_TERMIOS_H |
||||||
|
tcsetattr(0, TCSADRAIN, &t_orig); |
||||||
|
#endif |
||||||
|
|
||||||
|
length = strlen(password); |
||||||
|
/* skip rest of the line */ |
||||||
|
if (length > 0 && password[length - 1] != '\n') |
||||||
|
{ |
||||||
|
do |
||||||
|
{ |
||||||
|
fgets(buf, 512, stdin); |
||||||
|
} while (buf[strlen(buf) - 1] != '\n'); |
||||||
|
} |
||||||
|
if (length > 0 && password[length - 1] == '\n') |
||||||
|
password[length - 1] = '\0'; |
||||||
|
|
||||||
|
fprintf(stderr, "\n\n"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static void |
||||||
|
_check_database_version(ArchiveHandle *AH, bool ignoreVersion) |
||||||
|
{ |
||||||
|
PGresult *res; |
||||||
|
double myversion; |
||||||
|
const char *remoteversion_str; |
||||||
|
double remoteversion; |
||||||
|
PGconn *conn = AH->connection; |
||||||
|
|
||||||
|
myversion = strtod(PG_VERSION, NULL); |
||||||
|
res = PQexec(conn, "SELECT version()"); |
||||||
|
if (!res || |
||||||
|
PQresultStatus(res) != PGRES_TUPLES_OK || |
||||||
|
PQntuples(res) != 1) |
||||||
|
|
||||||
|
die_horribly(AH, "check_database_version(): command failed. " |
||||||
|
"Explanation from backend: '%s'.\n", PQerrorMessage(conn)); |
||||||
|
|
||||||
|
remoteversion_str = PQgetvalue(res, 0, 0); |
||||||
|
remoteversion = strtod(remoteversion_str + 11, NULL); |
||||||
|
if (myversion != remoteversion) |
||||||
|
{ |
||||||
|
fprintf(stderr, "Database version: %s\n%s version: %s\n", |
||||||
|
progname, remoteversion_str, PG_VERSION); |
||||||
|
if (ignoreVersion) |
||||||
|
fprintf(stderr, "Proceeding despite version mismatch.\n"); |
||||||
|
else |
||||||
|
die_horribly(AH, "Aborting because of version mismatch.\n" |
||||||
|
"Use --ignore-version if you think it's safe to proceed anyway.\n");
|
||||||
|
} |
||||||
|
PQclear(res); |
||||||
|
} |
||||||
|
|
||||||
|
PGconn* ConnectDatabase(Archive *AHX,
|
||||||
|
const char* dbname, |
||||||
|
const char* pghost, |
||||||
|
const char* pgport, |
||||||
|
const int reqPwd, |
||||||
|
const int ignoreVersion) |
||||||
|
{ |
||||||
|
ArchiveHandle *AH = (ArchiveHandle*)AHX; |
||||||
|
char connect_string[512] = ""; |
||||||
|
char tmp_string[128]; |
||||||
|
char password[100]; |
||||||
|
|
||||||
|
if (AH->connection) |
||||||
|
die_horribly(AH, "%s: already connected to database\n", progname); |
||||||
|
|
||||||
|
if (!dbname && !(dbname = getenv("PGDATABASE")) )
|
||||||
|
die_horribly(AH, "%s: no database name specified\n", progname); |
||||||
|
|
||||||
|
AH->dbname = strdup(dbname); |
||||||
|
|
||||||
|
if (pghost != NULL) |
||||||
|
{ |
||||||
|
AH->pghost = strdup(pghost); |
||||||
|
sprintf(tmp_string, "host=%s ", AH->pghost); |
||||||
|
strcat(connect_string, tmp_string); |
||||||
|
} |
||||||
|
else |
||||||
|
AH->pghost = NULL; |
||||||
|
|
||||||
|
if (pgport != NULL) |
||||||
|
{ |
||||||
|
AH->pgport = strdup(pgport); |
||||||
|
sprintf(tmp_string, "port=%s ", AH->pgport); |
||||||
|
strcat(connect_string, tmp_string); |
||||||
|
} |
||||||
|
else |
||||||
|
AH->pgport = NULL; |
||||||
|
|
||||||
|
sprintf(tmp_string, "dbname=%s ", AH->dbname); |
||||||
|
strcat(connect_string, tmp_string); |
||||||
|
|
||||||
|
if (reqPwd) |
||||||
|
{ |
||||||
|
_prompt_for_password(AH->username, password); |
||||||
|
strcat(connect_string, "authtype=password "); |
||||||
|
sprintf(tmp_string, "user=%s ", AH->username); |
||||||
|
strcat(connect_string, tmp_string); |
||||||
|
sprintf(tmp_string, "password=%s ", password); |
||||||
|
strcat(connect_string, tmp_string); |
||||||
|
MemSet(tmp_string, 0, sizeof(tmp_string)); |
||||||
|
MemSet(password, 0, sizeof(password)); |
||||||
|
} |
||||||
|
AH->connection = PQconnectdb(connect_string); |
||||||
|
MemSet(connect_string, 0, sizeof(connect_string)); |
||||||
|
|
||||||
|
/* check to see that the backend connection was successfully made */ |
||||||
|
if (PQstatus(AH->connection) == CONNECTION_BAD) |
||||||
|
die_horribly(AH, "Connection to database '%s' failed.\n%s\n", |
||||||
|
AH->dbname, PQerrorMessage(AH->connection)); |
||||||
|
|
||||||
|
/* check for version mismatch */ |
||||||
|
_check_database_version(AH, ignoreVersion); |
||||||
|
|
||||||
|
return AH->connection; |
||||||
|
} |
||||||
|
|
||||||
|
/* Convenience function to send a query. Monitors result to handle COPY statements */ |
||||||
|
int ExecuteSqlCommand(ArchiveHandle* AH, PQExpBuffer qry, char *desc) |
||||||
|
{ |
||||||
|
PGresult *res; |
||||||
|
|
||||||
|
/* fprintf(stderr, "Executing: '%s'\n\n", qry->data); */ |
||||||
|
res = PQexec(AH->connection, qry->data); |
||||||
|
if (!res) |
||||||
|
die_horribly(AH, "%s: %s. No result from backend.\n", progname, desc); |
||||||
|
|
||||||
|
if (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK) |
||||||
|
{ |
||||||
|
if (PQresultStatus(res) == PGRES_COPY_IN) |
||||||
|
AH->pgCopyIn = 1; |
||||||
|
else
|
||||||
|
die_horribly(AH, "%s: %s. Code = %d. Explanation from backend: '%s'.\n", |
||||||
|
progname, desc, PQresultStatus(res), PQerrorMessage(AH->connection)); |
||||||
|
} |
||||||
|
|
||||||
|
PQclear(res); |
||||||
|
|
||||||
|
return strlen(qry->data); |
||||||
|
} |
||||||
|
|
||||||
|
/* Convenience function to send one or more queries. Monitors result to handle COPY statements */ |
||||||
|
int ExecuteSqlCommandBuf(ArchiveHandle* AH, void *qryv, int bufLen) |
||||||
|
{ |
||||||
|
int loc; |
||||||
|
int pos = 0; |
||||||
|
int sPos = 0; |
||||||
|
char *qry = (char*)qryv; |
||||||
|
int isEnd = 0; |
||||||
|
char *eos = qry + bufLen; |
||||||
|
|
||||||
|
/* fprintf(stderr, "\n\n*****\n Buffer:\n\n%s\n*******************\n\n", qry); */ |
||||||
|
|
||||||
|
/* If we're in COPY IN mode, then just break it into lines and send... */ |
||||||
|
if (AH->pgCopyIn) { |
||||||
|
for(;;) { |
||||||
|
|
||||||
|
/* Find a lf */ |
||||||
|
loc = strcspn(&qry[pos], "\n") + pos; |
||||||
|
pos = 0; |
||||||
|
|
||||||
|
/* If no match, then wait */ |
||||||
|
if (loc >= (eos - qry)) /* None found */ |
||||||
|
{ |
||||||
|
appendBinaryPQExpBuffer(AH->pgCopyBuf, qry, (eos - qry)); |
||||||
|
break; |
||||||
|
}; |
||||||
|
|
||||||
|
/* 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 restart (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)); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
/* We got a good cr */ |
||||||
|
qry[loc] = '\0'; |
||||||
|
appendPQExpBuffer(AH->pgCopyBuf, "%s\n", qry); |
||||||
|
qry += loc + 1;
|
||||||
|
isEnd = (strcmp(AH->pgCopyBuf->data, "\\.\n") == 0); |
||||||
|
|
||||||
|
/* fprintf(stderr, "Sending '%s' via COPY (at end = %d)\n\n", AH->pgCopyBuf->data, isEnd); */
|
||||||
|
|
||||||
|
PQputline(AH->connection, AH->pgCopyBuf->data); |
||||||
|
|
||||||
|
resetPQExpBuffer(AH->pgCopyBuf); |
||||||
|
|
||||||
|
/* fprintf(stderr, "Buffer is '%s'\n", AH->pgCopyBuf->data); */ |
||||||
|
|
||||||
|
if(isEnd) { |
||||||
|
PQendcopy(AH->connection); |
||||||
|
AH->pgCopyIn = 0; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/* Make sure we're not past the original buffer end */ |
||||||
|
if (qry >= eos) |
||||||
|
break; |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* We may have finished Copy In, and have a non-empty buffer */ |
||||||
|
if (!AH->pgCopyIn) { |
||||||
|
|
||||||
|
/*
|
||||||
|
* The following is a mini state machine to assess then of 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] == ';') |
||||||
|
{ |
||||||
|
/* Send It & reset the buffer */ |
||||||
|
/* fprintf(stderr, " sending: '%s'\n\n", AH->sqlBuf->data); */ |
||||||
|
ExecuteSqlCommand(AH, AH->sqlBuf, "Could not execute query"); |
||||||
|
resetPQExpBuffer(AH->sqlBuf); |
||||||
|
AH->sqlparse.lastChar = '\0'; |
||||||
|
}
|
||||||
|
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; |
||||||
|
} |
||||||
|
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"); */ |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
void FixupBlobRefs(ArchiveHandle *AH, char *tablename) |
||||||
|
{ |
||||||
|
PQExpBuffer tblQry = createPQExpBuffer(); |
||||||
|
PGresult *res, *uRes; |
||||||
|
int i, n; |
||||||
|
char *attr; |
||||||
|
|
||||||
|
for(i=0 ; i < strlen(tablename) ; i++) |
||||||
|
tablename[i] = tolower(tablename[i]); |
||||||
|
|
||||||
|
if (strcmp(tablename, BLOB_XREF_TABLE) == 0) |
||||||
|
return; |
||||||
|
|
||||||
|
appendPQExpBuffer(tblQry, "SELECT a.attname FROM pg_class c, pg_attribute a, pg_type t " |
||||||
|
" WHERE a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid " |
||||||
|
" AND t.typname = 'oid' AND c.relname = '%s';", tablename); |
||||||
|
|
||||||
|
res = PQexec(AH->connection, tblQry->data); |
||||||
|
if (!res) |
||||||
|
die_horribly(AH, "%s: could not find OID attrs of %s. Explanation from backend '%s'\n", |
||||||
|
progname, tablename, PQerrorMessage(AH->connection)); |
||||||
|
|
||||||
|
if ((n = PQntuples(res)) == 0) { |
||||||
|
/* We're done */ |
||||||
|
ahlog(AH, 1, "No OID attributes in table %s\n", tablename); |
||||||
|
PQclear(res); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
for (i = 0 ; i < n ; i++) |
||||||
|
{ |
||||||
|
attr = PQgetvalue(res, i, 0); |
||||||
|
|
||||||
|
ahlog(AH, 1, " - %s.%s\n", tablename, attr); |
||||||
|
|
||||||
|
resetPQExpBuffer(tblQry); |
||||||
|
appendPQExpBuffer(tblQry, "Update \"%s\" Set \"%s\" = x.newOid From %s x " |
||||||
|
"Where x.oldOid = \"%s\".\"%s\";", |
||||||
|
|
||||||
|
tablename, attr, BLOB_XREF_TABLE, tablename, attr); |
||||||
|
|
||||||
|
ahlog(AH, 10, " - sql = %s\n", tblQry->data); |
||||||
|
|
||||||
|
uRes = PQexec(AH->connection, tblQry->data); |
||||||
|
if (!uRes) |
||||||
|
die_horribly(AH, "%s: could not update attr %s of table %s. Explanation from backend '%s'\n", |
||||||
|
progname, attr, tablename, PQerrorMessage(AH->connection)); |
||||||
|
|
||||||
|
if ( PQresultStatus(uRes) != PGRES_COMMAND_OK ) |
||||||
|
die_horribly(AH, "%s: error while updating attr %s of table %s. Explanation from backend '%s'\n", |
||||||
|
progname, attr, tablename, PQerrorMessage(AH->connection)); |
||||||
|
|
||||||
|
PQclear(uRes); |
||||||
|
} |
||||||
|
|
||||||
|
PQclear(res); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/**********
|
||||||
|
* Convenient SQL calls |
||||||
|
**********/ |
||||||
|
void CreateBlobXrefTable(ArchiveHandle* AH) |
||||||
|
{ |
||||||
|
PQExpBuffer qry = createPQExpBuffer(); |
||||||
|
|
||||||
|
ahlog(AH, 1, "Creating table for BLOBS xrefs\n"); |
||||||
|
|
||||||
|
appendPQExpBuffer(qry, "Create Temporary Table %s(oldOid oid, newOid oid);", BLOB_XREF_TABLE); |
||||||
|
|
||||||
|
ExecuteSqlCommand(AH, qry, "can not create BLOB xref table '" BLOB_XREF_TABLE "'"); |
||||||
|
|
||||||
|
resetPQExpBuffer(qry); |
||||||
|
|
||||||
|
appendPQExpBuffer(qry, "Create Unique Index %s_ix on %s(oldOid)", BLOB_XREF_TABLE, BLOB_XREF_TABLE); |
||||||
|
ExecuteSqlCommand(AH, qry, "can not create index on BLOB xref table '" BLOB_XREF_TABLE "'"); |
||||||
|
} |
||||||
|
|
||||||
|
void InsertBlobXref(ArchiveHandle* AH, int old, int new) |
||||||
|
{ |
||||||
|
PQExpBuffer qry = createPQExpBuffer(); |
||||||
|
|
||||||
|
appendPQExpBuffer(qry, "Insert Into %s(oldOid, newOid) Values (%d, %d);", BLOB_XREF_TABLE, old, new); |
||||||
|
|
||||||
|
ExecuteSqlCommand(AH, qry, "can not create BLOB xref entry"); |
||||||
|
} |
||||||
|
|
||||||
|
void StartTransaction(ArchiveHandle* AH) |
||||||
|
{ |
||||||
|
PQExpBuffer qry = createPQExpBuffer(); |
||||||
|
|
||||||
|
appendPQExpBuffer(qry, "Begin;"); |
||||||
|
|
||||||
|
ExecuteSqlCommand(AH, qry, "can not start database transaction"); |
||||||
|
} |
||||||
|
|
||||||
|
void CommitTransaction(ArchiveHandle* AH) |
||||||
|
{ |
||||||
|
PQExpBuffer qry = createPQExpBuffer(); |
||||||
|
|
||||||
|
appendPQExpBuffer(qry, "Commit;"); |
||||||
|
|
||||||
|
ExecuteSqlCommand(AH, qry, "can not commit database transaction"); |
||||||
|
} |
||||||
|
|
||||||
|
|
@ -0,0 +1,16 @@ |
|||||||
|
/*
|
||||||
|
* Definitions for pg_backup_db.c |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#define BLOB_XREF_TABLE "dump_blob_xref" /* MUST be lower case */ |
||||||
|
|
||||||
|
extern void FixupBlobRefs(ArchiveHandle *AH, char *tablename); |
||||||
|
extern int ExecuteSqlCommand(ArchiveHandle* AH, PQExpBuffer qry, char *desc); |
||||||
|
extern int ExecuteSqlCommandBuf(ArchiveHandle* AH, void *qry, int bufLen); |
||||||
|
|
||||||
|
extern void CreateBlobXrefTable(ArchiveHandle* AH); |
||||||
|
extern void InsertBlobXref(ArchiveHandle* AH, int old, int new); |
||||||
|
extern void StartTransaction(ArchiveHandle* AH); |
||||||
|
extern void CommitTransaction(ArchiveHandle* AH); |
||||||
|
|
@ -0,0 +1,114 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* pg_backup_null.c |
||||||
|
* |
||||||
|
* Implementation of an archive that is never saved; it is used by
|
||||||
|
* pg_dump to output output a plain text SQL script instead of save |
||||||
|
* a real archive. |
||||||
|
* |
||||||
|
* See the headers to pg_restore for more details. |
||||||
|
* |
||||||
|
* Copyright (c) 2000, Philip Warner |
||||||
|
* Rights are granted to use this software in any way so long |
||||||
|
* as this notice is not removed. |
||||||
|
* |
||||||
|
* The author is not responsible for loss or damages that may |
||||||
|
* result from it's use. |
||||||
|
* |
||||||
|
* |
||||||
|
* IDENTIFICATION |
||||||
|
* |
||||||
|
* Modifications - 09-Jul-2000 - pjw@rhyme.com.au |
||||||
|
* |
||||||
|
* Initial version.
|
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <unistd.h> /* for dup */ |
||||||
|
#include "pg_backup.h" |
||||||
|
#include "pg_backup_archiver.h" |
||||||
|
|
||||||
|
static int _WriteData(ArchiveHandle* AH, const void* data, int dLen); |
||||||
|
static void _EndData(ArchiveHandle* AH, TocEntry* te); |
||||||
|
static int _WriteByte(ArchiveHandle* AH, const int i); |
||||||
|
static int _WriteBuf(ArchiveHandle* AH, const void* buf, int len); |
||||||
|
static void _CloseArchive(ArchiveHandle* AH); |
||||||
|
static void _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Initializer |
||||||
|
*/ |
||||||
|
void InitArchiveFmt_Null(ArchiveHandle* AH)
|
||||||
|
{ |
||||||
|
/* Assuming static functions, this can be copied for each format. */ |
||||||
|
AH->WriteDataPtr = _WriteData; |
||||||
|
AH->EndDataPtr = _EndData; |
||||||
|
AH->WriteBytePtr = _WriteByte; |
||||||
|
AH->WriteBufPtr = _WriteBuf; |
||||||
|
AH->ClosePtr = _CloseArchive; |
||||||
|
AH->PrintTocDataPtr = _PrintTocData; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Now prevent reading... |
||||||
|
*/ |
||||||
|
if (AH->mode == archModeRead) |
||||||
|
die_horribly(AH, "%s: This format can not be read\n"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* - Start a new TOC entry |
||||||
|
*/ |
||||||
|
|
||||||
|
/*------
|
||||||
|
* Called by dumper via archiver from within a data dump routine
|
||||||
|
* As at V1.3, this is only called for COPY FROM dfata, and BLOB data |
||||||
|
*------ |
||||||
|
*/ |
||||||
|
static int _WriteData(ArchiveHandle* AH, const void* data, int dLen) |
||||||
|
{ |
||||||
|
/* Just send it to output */ |
||||||
|
ahwrite(data, 1, dLen, AH); |
||||||
|
return dLen; |
||||||
|
} |
||||||
|
|
||||||
|
static void _EndData(ArchiveHandle* AH, TocEntry* te) |
||||||
|
{ |
||||||
|
ahprintf(AH, "\n\n"); |
||||||
|
} |
||||||
|
|
||||||
|
/*------
|
||||||
|
* Called as part of a RestoreArchive call; for the NULL archive, this |
||||||
|
* just sends the data for a given TOC entry to the output. |
||||||
|
*------ |
||||||
|
*/ |
||||||
|
static void _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt) |
||||||
|
{ |
||||||
|
if (*te->dataDumper) |
||||||
|
{ |
||||||
|
AH->currToc = te; |
||||||
|
(*te->dataDumper)((Archive*)AH, te->oid, te->dataDumperArg); |
||||||
|
AH->currToc = NULL; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static int _WriteByte(ArchiveHandle* AH, const int i) |
||||||
|
{ |
||||||
|
/* Don't do anything */ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int _WriteBuf(ArchiveHandle* AH, const void* buf, int len) |
||||||
|
{ |
||||||
|
/* Don't do anything */ |
||||||
|
return len; |
||||||
|
} |
||||||
|
|
||||||
|
static void _CloseArchive(ArchiveHandle* AH) |
||||||
|
{ |
||||||
|
/* Nothing to do */ |
||||||
|
} |
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,35 @@ |
|||||||
|
/* Header
|
||||||
|
Offset Length Contents |
||||||
|
0 100 bytes File name ('\0' terminated, 99 maxmum length) |
||||||
|
100 8 bytes File mode (in octal ascii) |
||||||
|
108 8 bytes User ID (in octal ascii) |
||||||
|
116 8 bytes Group ID (in octal ascii) |
||||||
|
124 12 bytes File size (s) (in octal ascii) |
||||||
|
136 12 bytes Modify time (in octal ascii) |
||||||
|
148 8 bytes Header checksum (in octal ascii) |
||||||
|
156 1 bytes Link flag |
||||||
|
157 100 bytes Linkname ('\0' terminated, 99 maxmum length) |
||||||
|
257 8 bytes Magic ("ustar \0") |
||||||
|
265 32 bytes User name ('\0' terminated, 31 maxmum length) |
||||||
|
297 32 bytes Group name ('\0' terminated, 31 maxmum length) |
||||||
|
329 8 bytes Major device ID (in octal ascii) |
||||||
|
337 8 bytes Minor device ID (in octal ascii) |
||||||
|
345 167 bytes Padding |
||||||
|
512 (s+p)bytes File contents (s+p) := (((s) + 511) & ~511), round up to 512 bytes |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* The linkflag defines the type of file */ |
||||||
|
#define LF_OLDNORMAL '\0' /* Normal disk file, Unix compatible */ |
||||||
|
#define LF_NORMAL '0' /* Normal disk file */ |
||||||
|
#define LF_LINK '1' /* Link to previously dumped file */ |
||||||
|
#define LF_SYMLINK '2' /* Symbolic link */ |
||||||
|
#define LF_CHR '3' /* Character special file */ |
||||||
|
#define LF_BLK '4' /* Block special file */ |
||||||
|
#define LF_DIR '5' /* Directory */ |
||||||
|
#define LF_FIFO '6' /* FIFO special file */ |
||||||
|
#define LF_CONTIG '7' /* Contiguous file */ |
||||||
|
|
||||||
|
|
Loading…
Reference in new issue