aboutsummaryrefslogtreecommitdiff
path: root/src/bin/pg_dump/pg_dump.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/pg_dump/pg_dump.c')
-rw-r--r--src/bin/pg_dump/pg_dump.c374
1 files changed, 197 insertions, 177 deletions
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 8e8e81feb49..e4a47b9ef10 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -22,7 +22,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.159 2000/07/17 03:05:20 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.160 2000/07/21 11:40:08 pjw Exp $
*
* Modifications - 6/10/96 - dave@bensoft.com - version 1.13.dhb
*
@@ -61,12 +61,19 @@
* - Added a -Z option for compression level on compressed formats
* - Restored '-f' in usage output
*
-*-------------------------------------------------------------------------
+ *
+ * Modifications - 17-Jul-2000 - Philip Warner pjw@rhyme.com.au
+ * - Support for BLOB output.
+ * - Sort archive by OID, put some items at end (out of OID order)
+ *
+ *-------------------------------------------------------------------------
*/
#include <unistd.h> /* for getopt() */
#include <ctype.h>
+#include "pg_backup.h"
+
#include "postgres.h"
#ifdef HAVE_GETOPT_H
@@ -84,6 +91,7 @@
#include "catalog/pg_type.h"
#include "libpq-fe.h"
+#include <libpq/libpq-fs.h>
#ifndef HAVE_STRDUP
#include "strdup.h"
#endif
@@ -109,6 +117,9 @@ static void setMaxOid(Archive *fout);
static void AddAcl(char *aclbuf, const char *keyword);
static char *GetPrivileges(const char *s);
+static int dumpBlobs(Archive *AH, char*, void*);
+
+
extern char *optarg;
extern int optind,
opterr;
@@ -268,14 +279,26 @@ dumpClasses_nodumpData(Archive *fout, char* oid, void *dctxv)
if (oids == true)
{
- archprintf(fout, "COPY %s WITH OIDS FROM stdin;\n",
- fmtId(classname, force_quotes));
+ /*
+ * archprintf(fout, "COPY %s WITH OIDS FROM stdin;\n",
+ * fmtId(classname, force_quotes));
+ *
+ * - Not used as of V1.3 (needs to be in ArchiveEntry call)
+ *
+ */
+
sprintf(query, "COPY %s WITH OIDS TO stdout;\n",
fmtId(classname, force_quotes));
}
else
{
- archprintf(fout, "COPY %s FROM stdin;\n", fmtId(classname, force_quotes));
+ /*
+ *archprintf(fout, "COPY %s FROM stdin;\n", fmtId(classname, force_quotes));
+ *
+ * - Not used as of V1.3 (needs to be in ArchiveEntry call)
+ *
+ */
+
sprintf(query, "COPY %s TO stdout;\n", fmtId(classname, force_quotes));
}
res = PQexec(g_conn, query);
@@ -452,19 +475,28 @@ dumpClasses_dumpData(Archive *fout, char* oid, void *dctxv)
*/
static void
dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
- const char *onlytable, const bool oids)
+ const char *onlytable, const bool oids, const bool force_quotes)
{
int i;
char *all_only;
DataDumperPtr dumpFn;
DumpContext *dumpCtx;
+ char *oidsPart;
+ char copyBuf[512];
+ char *copyStmt;
if (onlytable == NULL)
all_only = "all";
else
all_only = "only";
+ if (oids == true)
+ oidsPart = "WITH OIDS ";
+ else
+ oidsPart = "";
+
+
if (g_verbose)
fprintf(stderr, "%s dumping out the contents of %s %d table%s/sequence%s %s\n",
g_comment_start, all_only,
@@ -514,112 +546,28 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
dumpCtx->tblidx = i;
dumpCtx->oids = oids;
- if (!dumpData)
+ if (!dumpData) /* Dump/restore using COPY */
+ {
dumpFn = dumpClasses_nodumpData;
/* dumpClasses_nodumpData(fout, classname, oids); */
- else
+ sprintf(copyBuf, "COPY %s %s FROM stdin;\n", fmtId(tblinfo[i].relname, force_quotes),
+ oidsPart);
+ copyStmt = copyBuf;
+ }
+ else /* Restore using INSERT */
+ {
dumpFn = dumpClasses_dumpData;
/* dumpClasses_dumpData(fout, classname); */
+ copyStmt = NULL;
+ }
ArchiveEntry(fout, tblinfo[i].oid, fmtId(tblinfo[i].relname, false),
- "TABLE DATA", NULL, "", "", tblinfo[i].usename,
+ "TABLE DATA", NULL, "", "", copyStmt, tblinfo[i].usename,
dumpFn, dumpCtx);
}
}
}
-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(bool ignoreVersion)
-{
- PGresult *res;
- double myversion;
- const char *remoteversion_str;
- double remoteversion;
-
- myversion = strtod(PG_VERSION, NULL);
- res = PQexec(g_conn, "SELECT version()");
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK ||
- PQntuples(res) != 1)
- {
- fprintf(stderr, "check_database_version(): command failed. Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
- exit_nicely(g_conn);
- }
-
- remoteversion_str = PQgetvalue(res, 0, 0);
- remoteversion = strtod(remoteversion_str + 11, NULL);
- if (myversion != remoteversion)
- {
- fprintf(stderr, "Database version: %s\npg_dump version: %s\n",
- remoteversion_str, PG_VERSION);
- if (ignoreVersion)
- fprintf(stderr, "Proceeding despite version mismatch.\n");
- else
- {
- fprintf(stderr, "Aborting because of version mismatch.\n"
- "Use --ignore-version if you think it's safe to proceed anyway.\n");
- exit_nicely(g_conn);
- }
- }
- PQclear(res);
-}
-
-
int
main(int argc, char **argv)
{
@@ -634,20 +582,19 @@ main(int argc, char **argv)
bool oids = false;
TableInfo *tblinfo;
int numTables;
- char connect_string[512] = "";
- char tmp_string[128];
- char username[100];
- char password[100];
bool use_password = false;
int compressLevel = -1;
bool ignore_version = false;
- int plainText = 0;
- int outputClean = 0;
+ int plainText = 0;
+ int outputClean = 0;
+ int outputBlobs = 0;
+
RestoreOptions *ropt;
#ifdef HAVE_GETOPT_LONG
static struct option long_options[] = {
{"data-only", no_argument, NULL, 'a'},
+ {"blobs", no_argument, NULL, 'b' },
{"clean", no_argument, NULL, 'c'},
{"file", required_argument, NULL, 'f'},
{"format", required_argument, NULL, 'F'},
@@ -686,6 +633,12 @@ main(int argc, char **argv)
else
progname = strrchr(argv[0], SEP_CHAR) + 1;
+ /* Set defaulty options based on progname */
+ if (strcmp(progname, "pg_backup") == 0)
+ {
+ format = "c";
+ outputBlobs = 1;
+ }
#ifdef HAVE_GETOPT_LONG
while ((c = getopt_long(argc, argv, "acdDf:F:h:inNop:st:uvxzZ:V?", long_options, &optindex)) != -1)
@@ -698,6 +651,10 @@ main(int argc, char **argv)
case 'a': /* Dump data only */
dataOnly = true;
break;
+ case 'b': /* Dump blobs */
+ outputBlobs = true;
+ break;
+
case 'c': /* clean (i.e., drop) schema prior to
* create */
outputClean = 1;
@@ -843,7 +800,12 @@ main(int argc, char **argv)
case 'p':
case 'P':
plainText = 1;
- g_fout = CreateArchive(filename, archPlainText, 0);
+ g_fout = CreateArchive(filename, archNull, 0);
+ break;
+
+ case 't':
+ case 'T':
+ g_fout = CreateArchive(filename, archTar, compressLevel);
break;
default:
@@ -860,53 +822,13 @@ main(int argc, char **argv)
exit(1);
}
- /* find database */
- if (!(dbname = argv[optind]) &&
- !(dbname = getenv("PGDATABASE")))
- {
- fprintf(stderr, "%s: no database name specified\n", progname);
- exit(1);
- }
+ /* Let the archiver know how noisy to be */
+ g_fout->verbose = g_verbose;
- /* g_conn = PQsetdb(pghost, pgport, NULL, NULL, dbname); */
- if (pghost != NULL)
- {
- sprintf(tmp_string, "host=%s ", pghost);
- strcat(connect_string, tmp_string);
- }
- if (pgport != NULL)
- {
- sprintf(tmp_string, "port=%s ", pgport);
- strcat(connect_string, tmp_string);
- }
- if (dbname != NULL)
- {
- sprintf(tmp_string, "dbname=%s ", dbname);
- strcat(connect_string, tmp_string);
- }
- if (use_password)
- {
- prompt_for_password(username, password);
- strcat(connect_string, "authtype=password ");
- sprintf(tmp_string, "user=%s ", 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));
- }
- g_conn = PQconnectdb(connect_string);
- MemSet(connect_string, 0, sizeof(connect_string));
- /* check to see that the backend connection was successfully made */
- if (PQstatus(g_conn) == CONNECTION_BAD)
- {
- fprintf(stderr, "Connection to database '%s' failed.\n", dbname);
- fprintf(stderr, "%s\n", PQerrorMessage(g_conn));
- exit_nicely(g_conn);
- }
+ dbname = argv[optind];
- /* check for version mismatch */
- check_database_version(ignore_version);
+ /* Open the database using the Archiver, so it knows about it. Errors mean death */
+ g_conn = ConnectDatabase(g_fout, dbname, pghost, pgport, use_password, ignore_version);
/*
* Start serializable transaction to dump consistent data
@@ -916,17 +838,15 @@ main(int argc, char **argv)
res = PQexec(g_conn, "begin");
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- fprintf(stderr, "BEGIN command failed. Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
- exit_nicely(g_conn);
- }
+ exit_horribly(g_fout, "BEGIN command failed. Explanation from backend: '%s'.\n",
+ PQerrorMessage(g_conn));
+
PQclear(res);
res = PQexec(g_conn, "set transaction isolation level serializable");
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- fprintf(stderr, "SET TRANSACTION command failed. Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
- exit_nicely(g_conn);
- }
+ exit_horribly(g_fout, "SET TRANSACTION command failed. Explanation from backend: '%s'.\n",
+ PQerrorMessage(g_conn));
+
PQclear(res);
}
@@ -941,7 +861,12 @@ main(int argc, char **argv)
tblinfo = dumpSchema(g_fout, &numTables, tablename, aclsSkip, oids, schemaOnly, dataOnly);
if (!schemaOnly)
- dumpClasses(tblinfo, numTables, g_fout, tablename, oids);
+ {
+ dumpClasses(tblinfo, numTables, g_fout, tablename, oids, force_quotes);
+ }
+
+ if (outputBlobs)
+ ArchiveEntry(g_fout, "0", "BLOBS", "BLOBS", NULL, "", "", "", "", dumpBlobs, 0);
if (!dataOnly) /* dump indexes and triggers at the end
* for performance */
@@ -951,6 +876,15 @@ main(int argc, char **argv)
dumpRules(g_fout, tablename, tblinfo, numTables);
}
+ /* Now sort the output nicely */
+ SortTocByOID(g_fout);
+ MoveToEnd(g_fout, "TABLE DATA");
+ MoveToEnd(g_fout, "BLOBS");
+ MoveToEnd(g_fout, "INDEX");
+ MoveToEnd(g_fout, "TRIGGER");
+ MoveToEnd(g_fout, "RULE");
+ MoveToEnd(g_fout, "ACL");
+
if (plainText)
{
ropt = NewRestoreOptions();
@@ -974,6 +908,92 @@ main(int argc, char **argv)
}
/*
+ * dumpBlobs:
+ * dump all blobs
+ *
+ */
+
+#define loBufSize 16384
+#define loFetchSize 1000
+
+static int
+dumpBlobs(Archive *AH, char* junkOid, void *junkVal)
+{
+ PQExpBuffer oidQry = createPQExpBuffer();
+ PQExpBuffer oidFetchQry = createPQExpBuffer();
+ PGresult *res;
+ int i;
+ int loFd;
+ char buf[loBufSize];
+ int cnt;
+ int blobOid;
+
+ if (g_verbose)
+ fprintf(stderr, "%s saving BLOBs\n", g_comment_start);
+
+ /* Cursor to get all BLOB tables */
+ appendPQExpBuffer(oidQry, "Declare blobOid Cursor for SELECT oid from pg_class where relkind = 'l'");
+
+ res = PQexec(g_conn, oidQry->data);
+ if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
+ {
+ fprintf(stderr, "dumpBlobs(): Declare Cursor failed. Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
+ exit_nicely(g_conn);
+ }
+
+ /* Fetch for cursor */
+ appendPQExpBuffer(oidFetchQry, "Fetch %d in blobOid", loFetchSize);
+
+ do {
+ /* Do a fetch */
+ PQclear(res);
+ res = PQexec(g_conn, oidFetchQry->data);
+
+ if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
+ {
+ fprintf(stderr, "dumpBlobs(): Fetch Cursor failed. Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
+ exit_nicely(g_conn);
+ }
+
+ /* Process the tuples, if any */
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ blobOid = atoi(PQgetvalue(res, i, 0));
+ /* Open the BLOB */
+ loFd = lo_open(g_conn, blobOid, INV_READ);
+ if (loFd == -1)
+ {
+ fprintf(stderr, "dumpBlobs(): Could not open large object. "
+ "Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
+ exit_nicely(g_conn);
+ }
+
+ StartBlob(AH, blobOid);
+
+ /* Now read it in chunks, sending data to archive */
+ do {
+ cnt = lo_read(g_conn, loFd, buf, loBufSize);
+ if (cnt < 0) {
+ fprintf(stderr, "dumpBlobs(): Error reading large object. "
+ " Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
+ exit_nicely(g_conn);
+ }
+
+ WriteData(AH, buf, cnt);
+
+ } while (cnt > 0);
+
+ lo_close(g_conn, loFd);
+
+ EndBlob(AH, blobOid);
+
+ }
+ } while (PQntuples(res) > 0);
+
+ return 1;
+}
+
+/*
* getTypes:
* read all base types in the system catalogs and return them in the
* TypeInfo* structure
@@ -2409,7 +2429,7 @@ dumpComment(Archive *fout, const char *target, const char *oid)
target, checkForQuote(PQgetvalue(res, 0, i_description)));
ArchiveEntry(fout, oid, target, "COMMENT", NULL, query->data, "" /*Del*/,
- "" /*Owner*/, NULL, NULL);
+ "" /* Copy */, "" /*Owner*/, NULL, NULL);
}
@@ -2542,7 +2562,7 @@ dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs,
appendPQExpBuffer(q, ");\n");
ArchiveEntry(fout, tinfo[i].oid, fmtId(tinfo[i].typname, force_quotes), "TYPE", NULL,
- q->data, delq->data, tinfo[i].usename, NULL, NULL);
+ q->data, delq->data, "", tinfo[i].usename, NULL, NULL);
/*** Dump Type Comments ***/
@@ -2629,7 +2649,7 @@ dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs,
lancompiler);
ArchiveEntry(fout, PQgetvalue(res, i, i_oid), lanname, "PROCEDURAL LANGUAGE",
- NULL, defqry->data, delqry->data, "", NULL, NULL);
+ NULL, defqry->data, delqry->data, "", "", NULL, NULL);
free(lanname);
free(lancompiler);
@@ -2669,8 +2689,8 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
PQExpBuffer fn = createPQExpBuffer();
PQExpBuffer delqry = createPQExpBuffer();
PQExpBuffer fnlist = createPQExpBuffer();
- PQExpBuffer asPart = createPQExpBuffer();
int j;
+ PQExpBuffer asPart = createPQExpBuffer();
char func_lang[NAMEDATALEN + 1];
PGresult *res;
int nlangs;
@@ -2703,7 +2723,7 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
i_lanname = PQfnumber(res, "lanname");
- /*
+ /*
* See backend/commands/define.c for details of how the 'AS' clause
* is used.
*/
@@ -2751,7 +2771,7 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
asPart->data, func_lang);
ArchiveEntry(fout, finfo[i].oid, fn->data, "FUNCTION", NULL, q->data, delqry->data,
- finfo[i].usename, NULL, NULL);
+ "", finfo[i].usename, NULL, NULL);
/*** Dump Function Comments ***/
@@ -2870,7 +2890,7 @@ dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators,
sort2->data);
ArchiveEntry(fout, oprinfo[i].oid, oprinfo[i].oprname, "OPERATOR", NULL,
- q->data, delq->data, oprinfo[i].usename, NULL, NULL);
+ q->data, delq->data, "", oprinfo[i].usename, NULL, NULL);
}
}
@@ -2927,7 +2947,7 @@ dumpAggs(Archive *fout, AggInfo *agginfo, int numAggs,
details->data);
ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "AGGREGATE", NULL,
- q->data, delq->data, agginfo[i].usename, NULL, NULL);
+ q->data, delq->data, "", agginfo[i].usename, NULL, NULL);
/*** Dump Aggregate Comments ***/
@@ -3096,7 +3116,7 @@ dumpACL(Archive *fout, TableInfo tbinfo)
free(aclbuf);
- ArchiveEntry(fout, tbinfo.oid, tbinfo.relname, "ACL", NULL, sql, "", "", NULL, NULL);
+ ArchiveEntry(fout, tbinfo.oid, tbinfo.relname, "ACL", NULL, sql, "", "", "", NULL, NULL);
}
@@ -3274,7 +3294,7 @@ dumpTables(Archive *fout, TableInfo *tblinfo, int numTables,
if (!dataOnly) {
ArchiveEntry(fout, tblinfo[i].oid, fmtId(tblinfo[i].relname, false),
- "TABLE", NULL, q->data, delq->data, tblinfo[i].usename,
+ "TABLE", NULL, q->data, delq->data, "", tblinfo[i].usename,
NULL, NULL);
}
@@ -3468,7 +3488,7 @@ dumpIndices(Archive *fout, IndInfo *indinfo, int numIndices,
/* Dump Index Comments */
ArchiveEntry(fout, tblinfo[tableInd].oid, id1->data, "INDEX", NULL, q->data, delq->data,
- tblinfo[tableInd].usename, NULL, NULL);
+ "", tblinfo[tableInd].usename, NULL, NULL);
resetPQExpBuffer(q);
appendPQExpBuffer(q, "INDEX %s", id1->data);
@@ -3599,7 +3619,7 @@ setMaxOid(Archive *fout)
pos = pos + snprintf(sql+pos, 1024-pos, "\\.\n");
pos = pos + snprintf(sql+pos, 1024-pos, "DROP TABLE pg_dump_oid;\n");
- ArchiveEntry(fout, "0", "Max OID", "<Init>", NULL, sql, "","", NULL, NULL);
+ ArchiveEntry(fout, "0", "Max OID", "<Init>", NULL, sql, "", "", "", NULL, NULL);
}
/*
@@ -3750,7 +3770,7 @@ dumpSequence(Archive *fout, TableInfo tbinfo)
}
ArchiveEntry(fout, tbinfo.oid, fmtId(tbinfo.relname, force_quotes), "SEQUENCE", NULL,
- query->data, delqry->data, tbinfo.usename, NULL, NULL);
+ query->data, delqry->data, "", tbinfo.usename, NULL, NULL);
/* Dump Sequence Comments */
@@ -3779,7 +3799,7 @@ dumpTriggers(Archive *fout, const char *tablename,
for (j = 0; j < tblinfo[i].ntrig; j++)
{
ArchiveEntry(fout, tblinfo[i].triggers[j].oid, tblinfo[i].triggers[j].tgname,
- "TRIGGER", NULL, tblinfo[i].triggers[j].tgsrc, "",
+ "TRIGGER", NULL, tblinfo[i].triggers[j].tgsrc, "", "",
tblinfo[i].usename, NULL, NULL);
dumpComment(fout, tblinfo[i].triggers[j].tgcomment, tblinfo[i].triggers[j].oid);
}
@@ -3846,7 +3866,7 @@ dumpRules(Archive *fout, const char *tablename,
ArchiveEntry(fout, PQgetvalue(res, i, i_oid), PQgetvalue(res, i, i_rulename),
"RULE", NULL, PQgetvalue(res, i, i_definition),
- "", "", NULL, NULL);
+ "", "", "", NULL, NULL);
/* Dump rule comments */