- Support for TAR output
- 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 order
This commit is contained in:
parent
e8f69be054
commit
c3e18804ff
497
src/bin/pg_dump/pg_backup_db.c
Normal file
497
src/bin/pg_dump/pg_backup_db.c
Normal file
@ -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");
|
||||
}
|
||||
|
||||
|
16
src/bin/pg_dump/pg_backup_db.h
Normal file
16
src/bin/pg_dump/pg_backup_db.h
Normal file
@ -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);
|
||||
|
114
src/bin/pg_dump/pg_backup_null.c
Normal file
114
src/bin/pg_dump/pg_backup_null.c
Normal file
@ -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 */
|
||||
}
|
||||
|
1132
src/bin/pg_dump/pg_backup_tar.c
Normal file
1132
src/bin/pg_dump/pg_backup_tar.c
Normal file
File diff suppressed because it is too large
Load Diff
35
src/bin/pg_dump/pg_backup_tar.h
Normal file
35
src/bin/pg_dump/pg_backup_tar.h
Normal file
@ -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…
x
Reference in New Issue
Block a user