2000-07-21 13:43:26 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
2001-01-12 05:32:07 +01:00
|
|
|
* pg_backup_db.c
|
|
|
|
*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Implements the basic DB functions used by the archiver.
|
2001-01-12 05:32:07 +01:00
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2004-04-22 04:39:10 +02:00
|
|
|
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.53 2004/04/22 02:39:10 momjian Exp $
|
2002-01-18 18:13:51 +01:00
|
|
|
*
|
UUNET is looking into offering PostgreSQL as a part of a managed web
hosting product, on both shared and dedicated machines. We currently
offer Oracle and MySQL, and it would be a nice middle-ground.
However, as shipped, PostgreSQL lacks the following features we need
that MySQL has:
1. The ability to listen only on a particular IP address. Each
hosting customer has their own IP address, on which all of their
servers (http, ftp, real media, etc.) run.
2. The ability to place the Unix-domain socket in a mode 700 directory.
This allows us to automatically create an empty database, with an
empty DBA password, for new or upgrading customers without having
to interactively set a DBA password and communicate it to (or from)
the customer. This in turn cuts down our install and upgrade times.
3. The ability to connect to the Unix-domain socket from within a
change-rooted environment. We run CGI programs chrooted to the
user's home directory, which is another reason why we need to be
able to specify where the Unix-domain socket is, instead of /tmp.
4. The ability to, if run as root, open a pid file in /var/run as
root, and then setuid to the desired user. (mysqld -u can almost
do this; I had to patch it, too).
The patch below fixes problem 1-3. I plan to address #4, also, but
haven't done so yet. These diffs are big enough that they should give
the PG development team something to think about in the meantime :-)
Also, I'm about to leave for 2 weeks' vacation, so I thought I'd get
out what I have, which works (for the problems it tackles), now.
With these changes, we can set up and run PostgreSQL with scripts the
same way we can with apache or proftpd or mysql.
In summary, this patch makes the following enhancements:
1. Adds an environment variable PGUNIXSOCKET, analogous to MYSQL_UNIX_PORT,
and command line options -k --unix-socket to the relevant programs.
2. Adds a -h option to postmaster to set the hostname or IP address to
listen on instead of the default INADDR_ANY.
3. Extends some library interfaces to support the above.
4. Fixes a few memory leaks in PQconnectdb().
The default behavior is unchanged from stock 7.0.2; if you don't use
any of these new features, they don't change the operation.
David J. MacKenzie
2000-11-13 16:18:15 +01:00
|
|
|
*-------------------------------------------------------------------------
|
2000-07-21 13:43:26 +02:00
|
|
|
*/
|
|
|
|
|
2001-02-10 03:31:31 +01:00
|
|
|
#include "pg_backup.h"
|
|
|
|
#include "pg_backup_archiver.h"
|
|
|
|
#include "pg_backup_db.h"
|
2002-08-27 20:57:26 +02:00
|
|
|
#include "dumputils.h"
|
2001-02-10 03:31:31 +01:00
|
|
|
|
2002-08-20 19:54:45 +02:00
|
|
|
#include <unistd.h>
|
2000-07-21 13:43:26 +02:00
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#ifdef HAVE_TERMIOS_H
|
|
|
|
#include <termios.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "libpq-fe.h"
|
2001-02-10 03:31:31 +01:00
|
|
|
#include "libpq/libpq-fs.h"
|
2000-07-21 13:43:26 +02:00
|
|
|
#ifndef HAVE_STRDUP
|
|
|
|
#include "strdup.h"
|
|
|
|
#endif
|
|
|
|
|
2001-07-03 22:21:50 +02:00
|
|
|
static const char *modulename = gettext_noop("archiver (db)");
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
static void _check_database_version(ArchiveHandle *AH, bool ignoreVersion);
|
2001-08-22 22:23:24 +02:00
|
|
|
static PGconn *_connectDB(ArchiveHandle *AH, const char *newdbname, const char *newUser);
|
2001-03-22 05:01:46 +01:00
|
|
|
static int _executeSqlCommand(ArchiveHandle *AH, PGconn *conn, PQExpBuffer qry, char *desc);
|
2001-08-12 21:02:39 +02:00
|
|
|
static void notice_processor(void *arg, const char *message);
|
2002-01-18 20:17:05 +01:00
|
|
|
static char *_sendSQLLine(ArchiveHandle *AH, char *qry, char *eos);
|
|
|
|
static char *_sendCopyLine(ArchiveHandle *AH, char *qry, char *eos);
|
2000-07-21 13:43:26 +02:00
|
|
|
|
|
|
|
|
2001-04-25 09:03:20 +02:00
|
|
|
static int
|
2001-10-25 07:50:21 +02:00
|
|
|
_parse_version(ArchiveHandle *AH, const char *versionString)
|
2001-04-25 09:03:20 +02:00
|
|
|
{
|
2002-09-07 18:14:33 +02:00
|
|
|
int v;
|
2001-04-25 09:03:20 +02:00
|
|
|
|
2002-09-07 18:14:33 +02:00
|
|
|
v = parse_version(versionString);
|
|
|
|
if (v < 0)
|
2003-07-23 10:47:41 +02:00
|
|
|
die_horribly(AH, modulename, "could not parse version string \"%s\"\n", versionString);
|
2001-04-25 09:03:20 +02:00
|
|
|
|
2002-09-07 18:14:33 +02:00
|
|
|
return v;
|
2001-04-25 09:03:20 +02:00
|
|
|
}
|
2000-07-21 13:43:26 +02:00
|
|
|
|
|
|
|
static void
|
|
|
|
_check_database_version(ArchiveHandle *AH, bool ignoreVersion)
|
|
|
|
{
|
2001-04-25 09:03:20 +02:00
|
|
|
int myversion;
|
2000-07-21 13:43:26 +02:00
|
|
|
const char *remoteversion_str;
|
2001-04-25 09:03:20 +02:00
|
|
|
int remoteversion;
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2001-04-25 09:03:20 +02:00
|
|
|
myversion = _parse_version(AH, PG_VERSION);
|
|
|
|
|
2003-06-22 02:56:58 +02:00
|
|
|
remoteversion_str = PQparameterStatus(AH->connection, "server_version");
|
|
|
|
if (!remoteversion_str)
|
|
|
|
die_horribly(AH, modulename, "could not get server_version from libpq\n");
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2003-06-22 02:56:58 +02:00
|
|
|
remoteversion = _parse_version(AH, remoteversion_str);
|
2001-04-25 09:03:20 +02:00
|
|
|
|
|
|
|
AH->public.remoteVersion = remoteversion;
|
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
if (myversion != remoteversion
|
2003-06-22 02:56:58 +02:00
|
|
|
&& (remoteversion < AH->public.minRemoteVersion ||
|
|
|
|
remoteversion > AH->public.maxRemoteVersion))
|
2000-07-21 13:43:26 +02:00
|
|
|
{
|
2001-08-12 21:02:39 +02:00
|
|
|
write_msg(NULL, "server version: %s; %s version: %s\n",
|
2001-06-27 23:21:37 +02:00
|
|
|
remoteversion_str, progname, PG_VERSION);
|
2000-07-21 13:43:26 +02:00
|
|
|
if (ignoreVersion)
|
2001-06-27 23:21:37 +02:00
|
|
|
write_msg(NULL, "proceeding despite version mismatch\n");
|
2000-07-21 13:43:26 +02:00
|
|
|
else
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, NULL, "aborting because of version mismatch (Use the -i option to proceed anyway.)\n");
|
2000-07-21 13:43:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-08-01 17:51:45 +02:00
|
|
|
/*
|
2001-08-22 22:23:24 +02:00
|
|
|
* Reconnect to the server. If dbname is not NULL, use that database,
|
|
|
|
* else the one associated with the archive handle. If username is
|
2001-10-25 07:50:21 +02:00
|
|
|
* not NULL, use that user name, else the one from the handle. If
|
2001-08-22 22:23:24 +02:00
|
|
|
* both the database and the user and match the existing connection
|
|
|
|
* already, nothing will be done.
|
|
|
|
*
|
|
|
|
* Returns 1 in any case.
|
2000-08-01 17:51:45 +02:00
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
int
|
2001-08-22 22:23:24 +02:00
|
|
|
ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *username)
|
2000-08-01 17:51:45 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
PGconn *newConn;
|
2001-08-22 22:23:24 +02:00
|
|
|
const char *newdbname;
|
|
|
|
const char *newusername;
|
|
|
|
|
|
|
|
if (!dbname)
|
|
|
|
newdbname = PQdb(AH->connection);
|
|
|
|
else
|
|
|
|
newdbname = dbname;
|
2000-08-01 17:51:45 +02:00
|
|
|
|
2001-08-22 22:23:24 +02:00
|
|
|
if (!username)
|
|
|
|
newusername = PQuser(AH->connection);
|
2000-08-01 17:51:45 +02:00
|
|
|
else
|
2001-08-22 22:23:24 +02:00
|
|
|
newusername = username;
|
2000-08-01 17:51:45 +02:00
|
|
|
|
|
|
|
/* Let's see if the request is already satisfied */
|
2001-08-22 22:23:24 +02:00
|
|
|
if (strcmp(newusername, PQuser(AH->connection)) == 0
|
|
|
|
&& strcmp(newdbname, PQdb(AH->connection)) == 0)
|
2000-08-01 17:51:45 +02:00
|
|
|
return 1;
|
|
|
|
|
2001-08-22 22:23:24 +02:00
|
|
|
newConn = _connectDB(AH, newdbname, newusername);
|
2000-08-01 17:51:45 +02:00
|
|
|
|
|
|
|
PQfinish(AH->connection);
|
|
|
|
AH->connection = newConn;
|
2001-08-22 22:23:24 +02:00
|
|
|
|
2000-08-01 17:51:45 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Connect to the db again.
|
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
static PGconn *
|
2001-08-22 22:23:24 +02:00
|
|
|
_connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
|
2000-07-24 08:24:26 +02:00
|
|
|
{
|
|
|
|
int need_pass;
|
2001-03-22 05:01:46 +01:00
|
|
|
PGconn *newConn;
|
2001-05-17 23:12:49 +02:00
|
|
|
char *password = NULL;
|
2000-07-24 08:24:26 +02:00
|
|
|
int badPwd = 0;
|
|
|
|
int noPwd = 0;
|
2001-03-22 05:01:46 +01:00
|
|
|
char *newdb;
|
|
|
|
char *newuser;
|
2000-07-24 08:24:26 +02:00
|
|
|
|
2001-08-22 22:23:24 +02:00
|
|
|
if (!reqdb)
|
2000-08-01 17:51:45 +02:00
|
|
|
newdb = PQdb(AH->connection);
|
|
|
|
else
|
2001-03-22 05:01:46 +01:00
|
|
|
newdb = (char *) reqdb;
|
2000-08-01 17:51:45 +02:00
|
|
|
|
|
|
|
if (!requser || (strlen(requser) == 0))
|
|
|
|
newuser = PQuser(AH->connection);
|
|
|
|
else
|
2001-03-22 05:01:46 +01:00
|
|
|
newuser = (char *) requser;
|
2000-08-01 17:51:45 +02:00
|
|
|
|
2003-07-23 10:47:41 +02:00
|
|
|
ahlog(AH, 1, "connecting to database \"%s\" as user \"%s\"\n", newdb, newuser);
|
2000-07-24 08:24:26 +02:00
|
|
|
|
2001-05-17 23:12:49 +02:00
|
|
|
if (AH->requirePassword)
|
|
|
|
{
|
|
|
|
password = simple_prompt("Password: ", 100, false);
|
|
|
|
if (password == NULL)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "out of memory\n");
|
2001-05-17 23:12:49 +02:00
|
|
|
}
|
|
|
|
|
2000-07-24 08:24:26 +02:00
|
|
|
do
|
|
|
|
{
|
2000-08-01 17:51:45 +02:00
|
|
|
need_pass = false;
|
|
|
|
newConn = PQsetdbLogin(PQhost(AH->connection), PQport(AH->connection),
|
2001-03-22 05:01:46 +01:00
|
|
|
NULL, NULL, newdb,
|
2001-05-17 23:12:49 +02:00
|
|
|
newuser, password);
|
2000-08-01 17:51:45 +02:00
|
|
|
if (!newConn)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "failed to reconnect to database\n");
|
2000-08-01 17:51:45 +02:00
|
|
|
|
|
|
|
if (PQstatus(newConn) == CONNECTION_BAD)
|
|
|
|
{
|
2001-05-17 23:12:49 +02:00
|
|
|
noPwd = (strcmp(PQerrorMessage(newConn),
|
|
|
|
"fe_sendauth: no password supplied\n") == 0);
|
|
|
|
badPwd = (strncmp(PQerrorMessage(newConn),
|
2001-10-25 07:50:21 +02:00
|
|
|
"Password authentication failed for user", 39) == 0);
|
2000-08-01 17:51:45 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (noPwd || badPwd)
|
2000-08-01 17:51:45 +02:00
|
|
|
{
|
2000-07-24 08:24:26 +02:00
|
|
|
|
2000-08-01 17:51:45 +02:00
|
|
|
if (badPwd)
|
|
|
|
fprintf(stderr, "Password incorrect\n");
|
2000-07-24 08:24:26 +02:00
|
|
|
|
2001-05-17 23:12:49 +02:00
|
|
|
fprintf(stderr, "Connecting to %s as %s\n",
|
|
|
|
PQdb(AH->connection), newuser);
|
2000-07-24 08:24:26 +02:00
|
|
|
|
2000-08-01 17:51:45 +02:00
|
|
|
need_pass = true;
|
2001-05-17 23:12:49 +02:00
|
|
|
if (password)
|
|
|
|
free(password);
|
|
|
|
password = simple_prompt("Password: ", 100, false);
|
2000-07-24 08:24:26 +02:00
|
|
|
}
|
2000-08-01 17:51:45 +02:00
|
|
|
else
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "could not reconnect to database: %s",
|
|
|
|
PQerrorMessage(newConn));
|
2000-08-01 17:51:45 +02:00
|
|
|
}
|
2000-07-24 08:24:26 +02:00
|
|
|
} while (need_pass);
|
|
|
|
|
2001-05-17 23:12:49 +02:00
|
|
|
if (password)
|
|
|
|
free(password);
|
|
|
|
|
2003-02-14 20:40:42 +01:00
|
|
|
/* check for version mismatch */
|
|
|
|
_check_database_version(AH, true);
|
|
|
|
|
2001-08-12 21:02:39 +02:00
|
|
|
PQsetNoticeProcessor(newConn, notice_processor, NULL);
|
|
|
|
|
2000-08-01 17:51:45 +02:00
|
|
|
return newConn;
|
2000-07-24 08:24:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-05-17 23:12:49 +02:00
|
|
|
/*
|
|
|
|
* Make a database connection with the given parameters. The
|
|
|
|
* connection handle is returned, the parameters are stored in AHX.
|
|
|
|
* An interactive password prompt is automatically issued if required.
|
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
PGconn *
|
|
|
|
ConnectDatabase(Archive *AHX,
|
|
|
|
const char *dbname,
|
|
|
|
const char *pghost,
|
|
|
|
const char *pgport,
|
2001-05-17 23:12:49 +02:00
|
|
|
const char *username,
|
2001-03-22 05:01:46 +01:00
|
|
|
const int reqPwd,
|
|
|
|
const int ignoreVersion)
|
2000-07-21 13:43:26 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
ArchiveHandle *AH = (ArchiveHandle *) AHX;
|
2001-05-17 23:12:49 +02:00
|
|
|
char *password = NULL;
|
|
|
|
bool need_pass = false;
|
2000-07-21 13:43:26 +02:00
|
|
|
|
|
|
|
if (AH->connection)
|
2001-07-03 22:21:50 +02:00
|
|
|
die_horribly(AH, modulename, "already connected to a database\n");
|
2000-07-21 13:43:26 +02:00
|
|
|
|
|
|
|
if (reqPwd)
|
|
|
|
{
|
2001-05-17 23:12:49 +02:00
|
|
|
password = simple_prompt("Password: ", 100, false);
|
|
|
|
if (password == NULL)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "out of memory\n");
|
2001-05-17 23:12:49 +02:00
|
|
|
AH->requirePassword = true;
|
2000-07-21 13:43:26 +02:00
|
|
|
}
|
2001-05-17 23:12:49 +02:00
|
|
|
else
|
|
|
|
AH->requirePassword = false;
|
|
|
|
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Start the connection. Loop until we have a password if requested
|
|
|
|
* by backend.
|
2001-05-17 23:12:49 +02:00
|
|
|
*/
|
|
|
|
do
|
|
|
|
{
|
|
|
|
need_pass = false;
|
2002-08-10 18:57:32 +02:00
|
|
|
AH->connection = PQsetdbLogin(pghost, pgport, NULL, NULL,
|
|
|
|
dbname, username, password);
|
2001-05-17 23:12:49 +02:00
|
|
|
|
|
|
|
if (!AH->connection)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "failed to connect to database\n");
|
2001-05-17 23:12:49 +02:00
|
|
|
|
|
|
|
if (PQstatus(AH->connection) == CONNECTION_BAD &&
|
|
|
|
strcmp(PQerrorMessage(AH->connection), "fe_sendauth: no password supplied\n") == 0 &&
|
|
|
|
!feof(stdin))
|
|
|
|
{
|
|
|
|
PQfinish(AH->connection);
|
|
|
|
need_pass = true;
|
|
|
|
free(password);
|
|
|
|
password = NULL;
|
|
|
|
password = simple_prompt("Password: ", 100, false);
|
|
|
|
}
|
|
|
|
} while (need_pass);
|
|
|
|
|
|
|
|
if (password)
|
|
|
|
free(password);
|
2000-07-21 13:43:26 +02:00
|
|
|
|
|
|
|
/* check to see that the backend connection was successfully made */
|
|
|
|
if (PQstatus(AH->connection) == CONNECTION_BAD)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "connection to database \"%s\" failed: %s",
|
2002-09-04 22:31:48 +02:00
|
|
|
PQdb(AH->connection), PQerrorMessage(AH->connection));
|
2000-07-21 13:43:26 +02:00
|
|
|
|
|
|
|
/* check for version mismatch */
|
|
|
|
_check_database_version(AH, ignoreVersion);
|
|
|
|
|
2001-08-12 21:02:39 +02:00
|
|
|
PQsetNoticeProcessor(AH->connection, notice_processor, NULL);
|
|
|
|
|
2000-07-21 13:43:26 +02:00
|
|
|
return AH->connection;
|
|
|
|
}
|
|
|
|
|
2001-08-12 21:02:39 +02:00
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
static void
|
|
|
|
notice_processor(void *arg, const char *message)
|
2001-08-12 21:02:39 +02:00
|
|
|
{
|
|
|
|
write_msg(NULL, "%s", message);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-08-01 17:51:45 +02:00
|
|
|
/* Public interface */
|
2000-07-21 13:43:26 +02:00
|
|
|
/* Convenience function to send a query. Monitors result to handle COPY statements */
|
2001-03-22 05:01:46 +01:00
|
|
|
int
|
2001-06-27 23:21:37 +02:00
|
|
|
ExecuteSqlCommand(ArchiveHandle *AH, PQExpBuffer qry, char *desc, bool use_blob)
|
2000-08-01 17:51:45 +02:00
|
|
|
{
|
2001-06-27 23:21:37 +02:00
|
|
|
if (use_blob)
|
|
|
|
return _executeSqlCommand(AH, AH->blobConnection, qry, desc);
|
|
|
|
else
|
|
|
|
return _executeSqlCommand(AH, AH->connection, qry, desc);
|
2000-08-01 17:51:45 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/*
|
2000-08-01 17:51:45 +02:00
|
|
|
* Handle command execution. This is used to execute a command on more than one connection,
|
|
|
|
* but the 'pgCopyIn' setting assumes the COPY commands are ONLY executed on the primary
|
|
|
|
* setting...an error will be raised otherwise.
|
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
static int
|
|
|
|
_executeSqlCommand(ArchiveHandle *AH, PGconn *conn, PQExpBuffer qry, char *desc)
|
2000-07-21 13:43:26 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
PGresult *res;
|
2000-07-21 13:43:26 +02:00
|
|
|
|
|
|
|
/* fprintf(stderr, "Executing: '%s'\n\n", qry->data); */
|
2000-08-01 17:51:45 +02:00
|
|
|
res = PQexec(conn, qry->data);
|
2000-07-21 13:43:26 +02:00
|
|
|
if (!res)
|
2001-07-03 22:21:50 +02:00
|
|
|
die_horribly(AH, modulename, "%s: no result from server\n", desc);
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)
|
2000-07-21 13:43:26 +02:00
|
|
|
{
|
|
|
|
if (PQresultStatus(res) == PGRES_COPY_IN)
|
2000-08-01 17:51:45 +02:00
|
|
|
{
|
|
|
|
if (conn != AH->connection)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "COPY command executed in non-primary connection\n");
|
2000-08-01 17:51:45 +02:00
|
|
|
|
2000-07-21 13:43:26 +02:00
|
|
|
AH->pgCopyIn = 1;
|
2000-08-01 17:51:45 +02:00
|
|
|
}
|
2001-03-22 05:01:46 +01:00
|
|
|
else
|
2004-04-22 04:39:10 +02:00
|
|
|
warn_or_die_horribly(AH, modulename, "%s: %s",
|
|
|
|
desc, PQerrorMessage(AH->connection));
|
2000-07-21 13:43:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
PQclear(res);
|
|
|
|
|
|
|
|
return strlen(qry->data);
|
|
|
|
}
|
|
|
|
|
2002-01-18 20:17:05 +01:00
|
|
|
/*
|
2002-01-18 18:13:51 +01:00
|
|
|
* Used by ExecuteSqlCommandBuf to send one buffered line when running a COPY command.
|
|
|
|
*/
|
2002-01-18 20:17:05 +01:00
|
|
|
static char *
|
|
|
|
_sendCopyLine(ArchiveHandle *AH, char *qry, char *eos)
|
2000-07-21 13:43:26 +02:00
|
|
|
{
|
2002-08-20 19:54:45 +02:00
|
|
|
size_t loc; /* Location of next newline */
|
2002-01-18 20:17:05 +01:00
|
|
|
int pos = 0; /* Current position */
|
|
|
|
int sPos = 0; /* Last pos of a slash char */
|
|
|
|
int isEnd = 0;
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
/* loop to find unquoted newline ending the line of COPY data */
|
2002-01-18 20:17:05 +01:00
|
|
|
for (;;)
|
|
|
|
{
|
2002-01-18 18:13:51 +01:00
|
|
|
loc = strcspn(&qry[pos], "\n") + pos;
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
/* If no match, then wait */
|
2002-01-18 20:17:05 +01:00
|
|
|
if (loc >= (eos - qry)) /* None found */
|
2001-03-22 05:01:46 +01:00
|
|
|
{
|
2002-01-18 18:13:51 +01:00
|
|
|
appendBinaryPQExpBuffer(AH->pgCopyBuf, qry, (eos - qry));
|
|
|
|
return eos;
|
|
|
|
}
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
/*
|
|
|
|
* fprintf(stderr, "Found cr at %d, prev char was %c, next was
|
|
|
|
* %c\n", loc, qry[loc-1], qry[loc+1]);
|
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
/* Count the number of preceding slashes */
|
|
|
|
sPos = loc;
|
|
|
|
while (sPos > 0 && qry[sPos - 1] == '\\')
|
|
|
|
sPos--;
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
sPos = loc - sPos;
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
/*
|
2002-01-18 20:17:05 +01:00
|
|
|
* If an odd number of preceding slashes, then \n was escaped so
|
|
|
|
* set the next search pos, and loop (if any left).
|
2002-01-18 18:13:51 +01:00
|
|
|
*/
|
|
|
|
if ((sPos & 1) == 1)
|
|
|
|
{
|
|
|
|
/* fprintf(stderr, "cr was escaped\n"); */
|
|
|
|
pos = loc + 1;
|
|
|
|
if (pos >= (eos - qry))
|
2000-07-21 13:43:26 +02:00
|
|
|
{
|
2002-01-18 18:13:51 +01:00
|
|
|
appendBinaryPQExpBuffer(AH->pgCopyBuf, qry, (eos - qry));
|
|
|
|
return eos;
|
2000-07-21 13:43:26 +02:00
|
|
|
}
|
2002-01-18 18:13:51 +01:00
|
|
|
}
|
2002-01-18 20:17:05 +01:00
|
|
|
else
|
|
|
|
break;
|
2002-01-18 18:13:51 +01:00
|
|
|
}
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
/* We found an unquoted newline */
|
|
|
|
qry[loc] = '\0';
|
|
|
|
appendPQExpBuffer(AH->pgCopyBuf, "%s\n", qry);
|
|
|
|
isEnd = (strcmp(AH->pgCopyBuf->data, "\\.\n") == 0);
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
/*---------
|
|
|
|
* fprintf(stderr, "Sending '%s' via
|
|
|
|
* COPY (at end = %d)\n\n", AH->pgCopyBuf->data, isEnd);
|
|
|
|
*---------
|
|
|
|
*/
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
if (PQputline(AH->connection, AH->pgCopyBuf->data) != 0)
|
|
|
|
die_horribly(AH, modulename, "error returned by PQputline\n");
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
resetPQExpBuffer(AH->pgCopyBuf);
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
/*
|
2002-01-18 20:17:05 +01:00
|
|
|
* fprintf(stderr, "Buffer is '%s'\n", AH->pgCopyBuf->data);
|
2002-01-18 18:13:51 +01:00
|
|
|
*/
|
2001-01-12 05:32:07 +01:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
if (isEnd)
|
|
|
|
{
|
|
|
|
if (PQendcopy(AH->connection) != 0)
|
|
|
|
die_horribly(AH, modulename, "error returned by PQendcopy\n");
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
AH->pgCopyIn = 0;
|
|
|
|
}
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
return qry + loc + 1;
|
|
|
|
}
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 20:17:05 +01:00
|
|
|
/*
|
2002-01-18 18:13:51 +01:00
|
|
|
* Used by ExecuteSqlCommandBuf to send one buffered line of SQL (not data for the copy command).
|
|
|
|
*/
|
2002-01-18 20:17:05 +01:00
|
|
|
static char *
|
|
|
|
_sendSQLLine(ArchiveHandle *AH, char *qry, char *eos)
|
2002-01-18 18:13:51 +01:00
|
|
|
{
|
2002-01-18 20:17:05 +01:00
|
|
|
int pos = 0; /* Current position */
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
/*
|
2002-01-18 20:17:05 +01:00
|
|
|
* The following is a mini state machine to assess the end 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.
|
2002-01-18 18:13:51 +01:00
|
|
|
*/
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
/*
|
2002-01-18 20:17:05 +01:00
|
|
|
* fprintf(stderr, "Buffer at start is: '%s'\n\n", AH->sqlBuf->data);
|
2002-01-18 18:13:51 +01:00
|
|
|
*/
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
for (pos = 0; pos < (eos - qry); pos++)
|
|
|
|
{
|
|
|
|
appendPQExpBufferChar(AH->sqlBuf, qry[pos]);
|
|
|
|
/* fprintf(stderr, " %c",qry[pos]); */
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
switch (AH->sqlparse.state)
|
|
|
|
{
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 20:17:05 +01:00
|
|
|
case SQL_SCAN: /* Default state == 0, set in _allocAH */
|
2002-01-18 18:13:51 +01:00
|
|
|
if (qry[pos] == ';' && AH->sqlparse.braceDepth == 0)
|
|
|
|
{
|
|
|
|
/* Send It & reset the buffer */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fprintf(stderr, " sending: '%s'\n\n",
|
|
|
|
* AH->sqlBuf->data);
|
|
|
|
*/
|
|
|
|
ExecuteSqlCommand(AH, AH->sqlBuf, "could not execute query", false);
|
|
|
|
resetPQExpBuffer(AH->sqlBuf);
|
|
|
|
AH->sqlparse.lastChar = '\0';
|
|
|
|
|
2002-01-18 20:17:05 +01:00
|
|
|
/*
|
|
|
|
* Remove any following newlines - so that embedded
|
|
|
|
* COPY commands don't get a starting newline.
|
2002-01-18 18:13:51 +01:00
|
|
|
*/
|
|
|
|
pos++;
|
2002-01-18 20:17:05 +01:00
|
|
|
for (; pos < (eos - qry) && qry[pos] == '\n'; pos++);
|
2002-01-18 18:13:51 +01:00
|
|
|
|
|
|
|
/* We've got our line, so exit */
|
|
|
|
return qry + pos;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (qry[pos] == '"' || qry[pos] == '\'')
|
2000-07-21 13:43:26 +02:00
|
|
|
{
|
2002-01-18 18:13:51 +01:00
|
|
|
/* fprintf(stderr,"[startquote]\n"); */
|
|
|
|
AH->sqlparse.state = SQL_IN_QUOTE;
|
|
|
|
AH->sqlparse.quoteChar = qry[pos];
|
|
|
|
AH->sqlparse.backSlash = 0;
|
2000-07-21 13:43:26 +02:00
|
|
|
}
|
2002-01-18 18:13:51 +01:00
|
|
|
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;
|
|
|
|
else if (qry[pos] == '(')
|
|
|
|
AH->sqlparse.braceDepth++;
|
|
|
|
else if (qry[pos] == ')')
|
|
|
|
AH->sqlparse.braceDepth--;
|
|
|
|
|
|
|
|
AH->sqlparse.lastChar = qry[pos];
|
|
|
|
}
|
|
|
|
break;
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
case SQL_IN_SQL_COMMENT:
|
|
|
|
if (qry[pos] == '\n')
|
|
|
|
AH->sqlparse.state = SQL_SCAN;
|
|
|
|
break;
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
case SQL_IN_EXT_COMMENT:
|
|
|
|
if (AH->sqlparse.lastChar == '*' && qry[pos] == '/')
|
|
|
|
AH->sqlparse.state = SQL_SCAN;
|
|
|
|
break;
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
case SQL_IN_QUOTE:
|
|
|
|
if (!AH->sqlparse.backSlash && AH->sqlparse.quoteChar == qry[pos])
|
|
|
|
{
|
|
|
|
/* fprintf(stderr,"[endquote]\n"); */
|
|
|
|
AH->sqlparse.state = SQL_SCAN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 18:13:51 +01:00
|
|
|
if (qry[pos] == '\\')
|
|
|
|
{
|
|
|
|
if (AH->sqlparse.lastChar == '\\')
|
|
|
|
AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
|
2001-03-22 05:01:46 +01:00
|
|
|
else
|
2002-01-18 18:13:51 +01:00
|
|
|
AH->sqlparse.backSlash = 1;
|
2000-07-21 13:43:26 +02:00
|
|
|
}
|
2002-01-18 18:13:51 +01:00
|
|
|
else
|
|
|
|
AH->sqlparse.backSlash = 0;
|
|
|
|
}
|
|
|
|
break;
|
2000-07-21 13:43:26 +02:00
|
|
|
|
|
|
|
}
|
2002-01-18 18:13:51 +01:00
|
|
|
AH->sqlparse.lastChar = qry[pos];
|
|
|
|
/* fprintf(stderr, "\n"); */
|
|
|
|
}
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-01-18 20:17:05 +01:00
|
|
|
/*
|
|
|
|
* If we get here, we've processed entire string with no complete SQL
|
|
|
|
* stmt
|
|
|
|
*/
|
|
|
|
return eos;
|
2002-01-18 18:13:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Convenience function to send one or more queries. Monitors result to handle COPY statements */
|
|
|
|
int
|
2002-08-20 19:54:45 +02:00
|
|
|
ExecuteSqlCommandBuf(ArchiveHandle *AH, void *qryv, size_t bufLen)
|
2002-01-18 18:13:51 +01:00
|
|
|
{
|
|
|
|
char *qry = (char *) qryv;
|
|
|
|
char *eos = qry + bufLen;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fprintf(stderr, "\n\n*****\n
|
|
|
|
* Buffer:\n\n%s\n*******************\n\n", qry);
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Could switch between command and COPY IN mode at each line */
|
|
|
|
while (qry < eos)
|
|
|
|
{
|
2002-01-18 20:17:05 +01:00
|
|
|
if (AH->pgCopyIn)
|
2002-01-18 18:13:51 +01:00
|
|
|
qry = _sendCopyLine(AH, qry, eos);
|
2002-01-18 20:17:05 +01:00
|
|
|
else
|
2002-01-18 18:13:51 +01:00
|
|
|
qry = _sendSQLLine(AH, qry, eos);
|
2000-07-21 13:43:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
2002-05-11 00:36:27 +02:00
|
|
|
FixupBlobRefs(ArchiveHandle *AH, TocEntry *te)
|
2000-07-21 13:43:26 +02:00
|
|
|
{
|
2002-05-11 00:36:27 +02:00
|
|
|
PQExpBuffer tblName;
|
2001-08-03 21:43:05 +02:00
|
|
|
PQExpBuffer tblQry;
|
2001-03-22 05:01:46 +01:00
|
|
|
PGresult *res,
|
|
|
|
*uRes;
|
|
|
|
int i,
|
|
|
|
n;
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-07-04 17:35:07 +02:00
|
|
|
if (strcmp(te->tag, BLOB_XREF_TABLE) == 0)
|
2000-07-21 13:43:26 +02:00
|
|
|
return;
|
|
|
|
|
2002-05-11 00:36:27 +02:00
|
|
|
tblName = createPQExpBuffer();
|
2001-08-03 21:43:05 +02:00
|
|
|
tblQry = createPQExpBuffer();
|
|
|
|
|
2002-05-11 00:36:27 +02:00
|
|
|
if (te->namespace && strlen(te->namespace) > 0)
|
|
|
|
appendPQExpBuffer(tblName, "%s.",
|
2002-08-18 11:36:26 +02:00
|
|
|
fmtId(te->namespace));
|
2002-05-11 00:36:27 +02:00
|
|
|
appendPQExpBuffer(tblName, "%s",
|
2002-08-18 11:36:26 +02:00
|
|
|
fmtId(te->tag));
|
2002-05-11 00:36:27 +02:00
|
|
|
|
|
|
|
appendPQExpBuffer(tblQry,
|
2003-02-01 23:06:59 +01:00
|
|
|
"SELECT a.attname, t.typname FROM "
|
2002-05-11 00:36:27 +02:00
|
|
|
"pg_catalog.pg_attribute a, pg_catalog.pg_type t "
|
2002-09-04 22:31:48 +02:00
|
|
|
"WHERE a.attnum > 0 AND a.attrelid = '%s'::pg_catalog.regclass "
|
|
|
|
"AND a.atttypid = t.oid AND t.typname in ('oid', 'lo')",
|
2002-05-11 00:36:27 +02:00
|
|
|
tblName->data);
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2000-08-01 17:51:45 +02:00
|
|
|
res = PQexec(AH->blobConnection, tblQry->data);
|
2000-07-21 13:43:26 +02:00
|
|
|
if (!res)
|
2003-07-23 10:47:41 +02:00
|
|
|
die_horribly(AH, modulename, "could not find OID columns of table \"%s\": %s",
|
2002-07-04 17:35:07 +02:00
|
|
|
te->tag, PQerrorMessage(AH->connection));
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if ((n = PQntuples(res)) == 0)
|
|
|
|
{
|
2001-08-03 21:43:05 +02:00
|
|
|
/* nothing to do */
|
2002-07-04 17:35:07 +02:00
|
|
|
ahlog(AH, 1, "no OID type columns in table %s\n", te->tag);
|
2000-07-21 13:43:26 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
for (i = 0; i < n; i++)
|
2000-07-21 13:43:26 +02:00
|
|
|
{
|
2003-02-01 23:06:59 +01:00
|
|
|
char *attr;
|
|
|
|
char *typname;
|
|
|
|
bool typeisoid;
|
|
|
|
|
2000-07-21 13:43:26 +02:00
|
|
|
attr = PQgetvalue(res, i, 0);
|
2003-02-01 23:06:59 +01:00
|
|
|
typname = PQgetvalue(res, i, 1);
|
|
|
|
|
|
|
|
typeisoid = (strcmp(typname, "oid") == 0);
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-05-11 00:36:27 +02:00
|
|
|
ahlog(AH, 1, "fixing large object cross-references for %s.%s\n",
|
2002-07-04 17:35:07 +02:00
|
|
|
te->tag, attr);
|
2000-07-21 13:43:26 +02:00
|
|
|
|
|
|
|
resetPQExpBuffer(tblQry);
|
|
|
|
|
2003-02-01 23:06:59 +01:00
|
|
|
/*
|
|
|
|
* Note: we use explicit typename() cast style here because if we
|
|
|
|
* are dealing with a dump from a pre-7.3 database containing LO
|
|
|
|
* columns, the dump probably will not have CREATE CAST commands
|
|
|
|
* for lo<->oid conversions. What it will have is functions,
|
|
|
|
* which we will invoke as functions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Can't use fmtId more than once per call... */
|
2002-05-11 00:36:27 +02:00
|
|
|
appendPQExpBuffer(tblQry,
|
2003-02-01 23:06:59 +01:00
|
|
|
"UPDATE %s SET %s = ",
|
|
|
|
tblName->data, fmtId(attr));
|
|
|
|
if (typeisoid)
|
|
|
|
appendPQExpBuffer(tblQry,
|
|
|
|
"%s.newOid",
|
|
|
|
BLOB_XREF_TABLE);
|
|
|
|
else
|
|
|
|
appendPQExpBuffer(tblQry,
|
|
|
|
"%s(%s.newOid)",
|
|
|
|
fmtId(typname),
|
|
|
|
BLOB_XREF_TABLE);
|
2002-05-11 00:36:27 +02:00
|
|
|
appendPQExpBuffer(tblQry,
|
2003-02-01 23:06:59 +01:00
|
|
|
" FROM %s WHERE %s.oldOid = ",
|
2002-05-11 00:36:27 +02:00
|
|
|
BLOB_XREF_TABLE,
|
2003-02-01 23:06:59 +01:00
|
|
|
BLOB_XREF_TABLE);
|
|
|
|
if (typeisoid)
|
|
|
|
appendPQExpBuffer(tblQry,
|
|
|
|
"%s.%s",
|
|
|
|
tblName->data, fmtId(attr));
|
|
|
|
else
|
|
|
|
appendPQExpBuffer(tblQry,
|
|
|
|
"oid(%s.%s)",
|
|
|
|
tblName->data, fmtId(attr));
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2001-07-03 22:21:50 +02:00
|
|
|
ahlog(AH, 10, "SQL: %s\n", tblQry->data);
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2000-08-01 17:51:45 +02:00
|
|
|
uRes = PQexec(AH->blobConnection, tblQry->data);
|
2000-07-21 13:43:26 +02:00
|
|
|
if (!uRes)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename,
|
2001-10-25 07:50:21 +02:00
|
|
|
"could not update column \"%s\" of table \"%s\": %s",
|
2002-09-04 22:31:48 +02:00
|
|
|
attr, te->tag, PQerrorMessage(AH->blobConnection));
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (PQresultStatus(uRes) != PGRES_COMMAND_OK)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename,
|
2001-10-25 07:50:21 +02:00
|
|
|
"error while updating column \"%s\" of table \"%s\": %s",
|
2002-09-04 22:31:48 +02:00
|
|
|
attr, te->tag, PQerrorMessage(AH->blobConnection));
|
2000-07-21 13:43:26 +02:00
|
|
|
|
|
|
|
PQclear(uRes);
|
|
|
|
}
|
|
|
|
|
|
|
|
PQclear(res);
|
2002-05-11 00:36:27 +02:00
|
|
|
destroyPQExpBuffer(tblName);
|
2001-08-03 21:43:05 +02:00
|
|
|
destroyPQExpBuffer(tblQry);
|
2000-07-21 13:43:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**********
|
|
|
|
* Convenient SQL calls
|
|
|
|
**********/
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
CreateBlobXrefTable(ArchiveHandle *AH)
|
2000-07-21 13:43:26 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
PQExpBuffer qry = createPQExpBuffer();
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2000-08-01 17:51:45 +02:00
|
|
|
/* IF we don't have a BLOB connection, then create one */
|
|
|
|
if (!AH->blobConnection)
|
|
|
|
AH->blobConnection = _connectDB(AH, NULL, NULL);
|
|
|
|
|
2001-09-21 23:58:30 +02:00
|
|
|
ahlog(AH, 1, "creating table for large object cross-references\n");
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2004-03-03 22:28:55 +01:00
|
|
|
appendPQExpBuffer(qry, "CREATE TEMPORARY TABLE %s(oldOid pg_catalog.oid, newOid pg_catalog.oid) WITHOUT OIDS", BLOB_XREF_TABLE);
|
2001-09-21 23:58:30 +02:00
|
|
|
ExecuteSqlCommand(AH, qry, "could not create large object cross-reference table", true);
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2004-03-03 22:28:55 +01:00
|
|
|
destroyPQExpBuffer(qry);
|
|
|
|
}
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2004-03-03 22:28:55 +01:00
|
|
|
void
|
|
|
|
CreateBlobXrefIndex(ArchiveHandle *AH)
|
|
|
|
{
|
|
|
|
PQExpBuffer qry = createPQExpBuffer();
|
|
|
|
|
|
|
|
ahlog(AH, 1, "creating index for large object cross-references\n");
|
|
|
|
|
|
|
|
appendPQExpBuffer(qry, "CREATE UNIQUE INDEX %s_ix ON %s(oldOid)",
|
|
|
|
BLOB_XREF_TABLE, BLOB_XREF_TABLE);
|
2001-09-21 23:58:30 +02:00
|
|
|
ExecuteSqlCommand(AH, qry, "could not create index on large object cross-reference table", true);
|
2001-08-03 21:43:05 +02:00
|
|
|
|
|
|
|
destroyPQExpBuffer(qry);
|
2000-07-21 13:43:26 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
2002-05-29 03:38:56 +02:00
|
|
|
InsertBlobXref(ArchiveHandle *AH, Oid old, Oid new)
|
2000-07-21 13:43:26 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
PQExpBuffer qry = createPQExpBuffer();
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2002-05-29 03:38:56 +02:00
|
|
|
appendPQExpBuffer(qry,
|
2004-03-03 22:28:55 +01:00
|
|
|
"INSERT INTO %s(oldOid, newOid) VALUES ('%u', '%u')",
|
2002-05-29 03:38:56 +02:00
|
|
|
BLOB_XREF_TABLE, old, new);
|
2001-09-21 23:58:30 +02:00
|
|
|
ExecuteSqlCommand(AH, qry, "could not create large object cross-reference entry", true);
|
2001-08-03 21:43:05 +02:00
|
|
|
|
|
|
|
destroyPQExpBuffer(qry);
|
2000-07-21 13:43:26 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
StartTransaction(ArchiveHandle *AH)
|
2000-07-21 13:43:26 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
PQExpBuffer qry = createPQExpBuffer();
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2004-03-03 22:28:55 +01:00
|
|
|
appendPQExpBuffer(qry, "BEGIN");
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2001-06-27 23:21:37 +02:00
|
|
|
ExecuteSqlCommand(AH, qry, "could not start database transaction", false);
|
2000-10-31 15:20:30 +01:00
|
|
|
AH->txActive = true;
|
2001-08-03 21:43:05 +02:00
|
|
|
|
|
|
|
destroyPQExpBuffer(qry);
|
2000-10-31 15:20:30 +01:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
StartTransactionXref(ArchiveHandle *AH)
|
2000-10-31 15:20:30 +01:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
PQExpBuffer qry = createPQExpBuffer();
|
2000-10-31 15:20:30 +01:00
|
|
|
|
2004-03-03 22:28:55 +01:00
|
|
|
appendPQExpBuffer(qry, "BEGIN");
|
2000-10-31 15:20:30 +01:00
|
|
|
|
2001-06-27 23:21:37 +02:00
|
|
|
ExecuteSqlCommand(AH, qry,
|
2001-09-21 23:58:30 +02:00
|
|
|
"could not start transaction for large object cross-references", true);
|
2000-10-31 15:20:30 +01:00
|
|
|
AH->blobTxActive = true;
|
2001-08-03 21:43:05 +02:00
|
|
|
|
|
|
|
destroyPQExpBuffer(qry);
|
2000-07-21 13:43:26 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
CommitTransaction(ArchiveHandle *AH)
|
2000-07-21 13:43:26 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
PQExpBuffer qry = createPQExpBuffer();
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2004-03-03 22:28:55 +01:00
|
|
|
appendPQExpBuffer(qry, "COMMIT");
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2001-06-27 23:21:37 +02:00
|
|
|
ExecuteSqlCommand(AH, qry, "could not commit database transaction", false);
|
2000-10-31 15:20:30 +01:00
|
|
|
AH->txActive = false;
|
2001-08-03 21:43:05 +02:00
|
|
|
|
|
|
|
destroyPQExpBuffer(qry);
|
2000-07-21 13:43:26 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
CommitTransactionXref(ArchiveHandle *AH)
|
2000-10-31 15:20:30 +01:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
PQExpBuffer qry = createPQExpBuffer();
|
2000-07-21 13:43:26 +02:00
|
|
|
|
2004-03-03 22:28:55 +01:00
|
|
|
appendPQExpBuffer(qry, "COMMIT");
|
2000-10-31 15:20:30 +01:00
|
|
|
|
2001-09-21 23:58:30 +02:00
|
|
|
ExecuteSqlCommand(AH, qry, "could not commit transaction for large object cross-references", true);
|
2000-10-31 15:20:30 +01:00
|
|
|
AH->blobTxActive = false;
|
2001-08-03 21:43:05 +02:00
|
|
|
|
|
|
|
destroyPQExpBuffer(qry);
|
2000-10-31 15:20:30 +01:00
|
|
|
}
|