pg_backup_db.c 19.8 KB
Newer Older
P
Philip Warner 已提交
1 2
/*-------------------------------------------------------------------------
 *
3 4
 * pg_backup_db.c
 *
B
Bruce Momjian 已提交
5
 *	Implements the basic DB functions used by the archiver.
6 7
 *
 * IDENTIFICATION
8
 *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.23 2001/08/12 19:02:39 petere Exp $
P
Philip Warner 已提交
9 10
 *
 * NOTES
11 12 13
 *
 * Modifications - 04-Jan-2001 - pjw@rhyme.com.au
 *
B
Bruce Momjian 已提交
14
 *	  - Check results of PQ routines more carefully.
P
Philip Warner 已提交
15
 *
P
Philip Warner 已提交
16 17 18 19
 * Modifications - 19-Mar-2001 - pjw@rhyme.com.au
 *
 *	  - Avoid forcing table name to lower case in FixupBlobXrefs!
 *
20
 *-------------------------------------------------------------------------
P
Philip Warner 已提交
21 22
 */

23 24 25 26
#include "pg_backup.h"
#include "pg_backup_archiver.h"
#include "pg_backup_db.h"

P
Philip Warner 已提交
27 28 29 30 31 32 33 34
#include <unistd.h>				/* for getopt() */
#include <ctype.h>

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

#include "libpq-fe.h"
35
#include "libpq/libpq-fs.h"
P
Philip Warner 已提交
36 37 38 39
#ifndef HAVE_STRDUP
#include "strdup.h"
#endif

40
static const char *modulename = gettext_noop("archiver (db)");
P
Philip Warner 已提交
41

B
Bruce Momjian 已提交
42 43 44
static void _check_database_version(ArchiveHandle *AH, bool ignoreVersion);
static PGconn *_connectDB(ArchiveHandle *AH, const char *newdbname, char *newUser);
static int	_executeSqlCommand(ArchiveHandle *AH, PGconn *conn, PQExpBuffer qry, char *desc);
45
static void notice_processor(void *arg, const char *message);
P
Philip Warner 已提交
46 47


48 49 50 51 52 53 54 55 56 57 58 59 60 61
/*
 * simple_prompt
 *
 * Generalized function especially intended for reading in usernames and
 * password interactively. Reads from stdin.
 *
 * prompt:		The prompt to print
 * maxlen:		How many characters to accept
 * echo:		Set to false if you want to hide what is entered (for passwords)
 *
 * Returns a malloc()'ed string with the input (w/o trailing newline).
 */
char *
simple_prompt(const char *prompt, int maxlen, bool echo)
P
Philip Warner 已提交
62 63
{
	int			length;
64
	char	   *destination;
P
Philip Warner 已提交
65 66 67

#ifdef HAVE_TERMIOS_H
	struct termios t_orig,
B
Bruce Momjian 已提交
68 69
				t;

P
Philip Warner 已提交
70 71
#endif

72 73 74 75
	destination = (char *) malloc(maxlen + 2);
	if (!destination)
		return NULL;
	if (prompt)
76
		fputs(gettext(prompt), stderr);
P
Philip Warner 已提交
77 78

#ifdef HAVE_TERMIOS_H
79 80 81 82 83 84 85
	if (!echo)
	{
		tcgetattr(0, &t);
		t_orig = t;
		t.c_lflag &= ~ECHO;
		tcsetattr(0, TCSADRAIN, &t);
	}
P
Philip Warner 已提交
86
#endif
87 88 89 90

	if (fgets(destination, maxlen, stdin) == NULL)
		destination[0] = '\0';

P
Philip Warner 已提交
91
#ifdef HAVE_TERMIOS_H
92 93 94 95 96
	if (!echo)
	{
		tcsetattr(0, TCSADRAIN, &t_orig);
		fputs("\n", stderr);
	}
P
Philip Warner 已提交
97 98
#endif

99 100
	length = strlen(destination);
	if (length > 0 && destination[length - 1] != '\n')
P
Philip Warner 已提交
101
	{
102
		/* eat rest of the line */
103 104 105
		char		buf[128];
		int			buflen;

P
Philip Warner 已提交
106 107
		do
		{
108 109 110 111
			if (fgets(buf, sizeof(buf), stdin) == NULL)
				break;
			buflen = strlen(buf);
		} while (buflen > 0 && buf[buflen - 1] != '\n');
P
Philip Warner 已提交
112
	}
113 114 115
	if (length > 0 && destination[length - 1] == '\n')
		/* remove trailing newline */
		destination[length - 1] = '\0';
P
Philip Warner 已提交
116

117
	return destination;
P
Philip Warner 已提交
118 119
}

120

121 122 123 124 125 126 127 128 129 130
static int
_parse_version(ArchiveHandle *AH, const char* versionString)
{
	int			cnt;
	int			vmaj, vmin, vrev;

	cnt = sscanf(versionString, "%d.%d.%d", &vmaj, &vmin, &vrev);

	if (cnt < 2)
	{
131
		die_horribly(AH, modulename, "unable to parse version string \"%s\"\n", versionString);
132 133 134 135 136 137 138
	}

	if (cnt == 2)
		vrev = 0;

	return (100 * vmaj + vmin) * 100 + vrev;
}
P
Philip Warner 已提交
139 140 141 142 143

static void
_check_database_version(ArchiveHandle *AH, bool ignoreVersion)
{
	PGresult   *res;
144
	int			myversion;
P
Philip Warner 已提交
145
	const char *remoteversion_str;
146
	int			remoteversion;
B
Bruce Momjian 已提交
147
	PGconn	   *conn = AH->connection;
P
Philip Warner 已提交
148

149 150
	myversion = _parse_version(AH, PG_VERSION);

151
	res = PQexec(conn, "SELECT version();");
P
Philip Warner 已提交
152 153 154 155
	if (!res ||
		PQresultStatus(res) != PGRES_TUPLES_OK ||
		PQntuples(res) != 1)

156
		die_horribly(AH, modulename, "could not get version from server: %s", PQerrorMessage(conn));
P
Philip Warner 已提交
157 158

	remoteversion_str = PQgetvalue(res, 0, 0);
159 160 161 162 163 164 165 166
	remoteversion = _parse_version(AH, remoteversion_str + 11);

	PQclear(res);

	AH->public.remoteVersion = remoteversion;

	if (myversion != remoteversion 
		&& (remoteversion < AH->public.minRemoteVersion || remoteversion > AH->public.maxRemoteVersion) )
P
Philip Warner 已提交
167
	{
168
		write_msg(NULL, "server version: %s; %s version: %s\n",
169
				  remoteversion_str, progname, PG_VERSION);
P
Philip Warner 已提交
170
		if (ignoreVersion)
171
			write_msg(NULL, "proceeding despite version mismatch\n");
P
Philip Warner 已提交
172
		else
173
			die_horribly(AH, NULL, "aborting because of version mismatch  (Use the -i option to proceed anyway.)\n");
P
Philip Warner 已提交
174 175 176
	}
}

B
Bruce Momjian 已提交
177
/*
178 179
 * Check if a given user is a superuser.
 */
B
Bruce Momjian 已提交
180 181
int
UserIsSuperuser(ArchiveHandle *AH, char *user)
182
{
B
Bruce Momjian 已提交
183 184 185 186 187
	PQExpBuffer qry = createPQExpBuffer();
	PGresult   *res;
	int			i_usesuper;
	int			ntups;
	int			isSuper;
188 189 190 191 192 193

	/* Get the superuser setting */
	appendPQExpBuffer(qry, "select usesuper from pg_user where usename = '%s'", user);
	res = PQexec(AH->connection, qry->data);

	if (!res)
194
		die_horribly(AH, modulename, "null result checking superuser status of %s\n", user);
195 196

	if (PQresultStatus(res) != PGRES_TUPLES_OK)
197 198
		die_horribly(AH, modulename, "could not check superuser status of %s: %s",
					 user, PQerrorMessage(AH->connection));
199 200 201 202 203 204 205 206 207 208 209 210

	ntups = PQntuples(res);

	if (ntups == 0)
		isSuper = 0;
	else
	{
		i_usesuper = PQfnumber(res, "usesuper");
		isSuper = (strcmp(PQgetvalue(res, 0, i_usesuper), "t") == 0);
	}
	PQclear(res);

211 212
	destroyPQExpBuffer(qry);

213 214 215
	return isSuper;
}

B
Bruce Momjian 已提交
216 217
int
ConnectedUserIsSuperuser(ArchiveHandle *AH)
218 219 220 221
{
	return UserIsSuperuser(AH, PQuser(AH->connection));
}

B
Bruce Momjian 已提交
222 223
char *
ConnectedUser(ArchiveHandle *AH)
224 225 226 227 228
{
	return PQuser(AH->connection);
}

/*
B
Bruce Momjian 已提交
229
 * Reconnect the DB associated with the archive handle
230
 */
B
Bruce Momjian 已提交
231 232
int
ReconnectDatabase(ArchiveHandle *AH, const char *newdbname, char *newUser)
233
{
B
Bruce Momjian 已提交
234 235
	PGconn	   *newConn;
	char	   *dbname;
236

B
Bruce Momjian 已提交
237
	if (!newdbname || (strcmp(newdbname, "-") == 0))
238 239
		dbname = PQdb(AH->connection);
	else
B
Bruce Momjian 已提交
240
		dbname = (char *) newdbname;
241 242 243 244 245 246 247 248 249

	/* Let's see if the request is already satisfied */
	if (strcmp(PQuser(AH->connection), newUser) == 0 && strcmp(newdbname, PQdb(AH->connection)) == 0)
		return 1;

	newConn = _connectDB(AH, dbname, newUser);

	PQfinish(AH->connection);
	AH->connection = newConn;
250 251
	free(AH->username);
	AH->username = strdup(newUser);
252 253 254 255 256 257 258

	return 1;
}

/*
 * Connect to the db again.
 */
B
Bruce Momjian 已提交
259 260
static PGconn *
_connectDB(ArchiveHandle *AH, const char *reqdb, char *requser)
261 262
{
	int			need_pass;
B
Bruce Momjian 已提交
263
	PGconn	   *newConn;
264
	char	   *password = NULL;
265 266
	int			badPwd = 0;
	int			noPwd = 0;
B
Bruce Momjian 已提交
267 268
	char	   *newdb;
	char	   *newuser;
269

B
Bruce Momjian 已提交
270
	if (!reqdb || (strcmp(reqdb, "-") == 0))
271 272
		newdb = PQdb(AH->connection);
	else
B
Bruce Momjian 已提交
273
		newdb = (char *) reqdb;
274 275 276 277

	if (!requser || (strlen(requser) == 0))
		newuser = PQuser(AH->connection);
	else
B
Bruce Momjian 已提交
278
		newuser = (char *) requser;
279

280
	ahlog(AH, 1, "connecting to database %s as user %s\n", newdb, newuser);
281

282 283 284 285
	if (AH->requirePassword)
	{
		password = simple_prompt("Password: ", 100, false);
		if (password == NULL)
286
			die_horribly(AH, modulename, "out of memory\n");
287 288
	}

289 290
	do
	{
291 292
		need_pass = false;
		newConn = PQsetdbLogin(PQhost(AH->connection), PQport(AH->connection),
B
Bruce Momjian 已提交
293
							   NULL, NULL, newdb,
294
							   newuser, password);
295
		if (!newConn)
296
			die_horribly(AH, modulename, "failed to reconnect to database\n");
297 298 299

		if (PQstatus(newConn) == CONNECTION_BAD)
		{
300 301 302 303
			noPwd = (strcmp(PQerrorMessage(newConn),
							"fe_sendauth: no password supplied\n") == 0);
			badPwd = (strncmp(PQerrorMessage(newConn),
							  "Password authentication failed for user", 39) == 0);
304

B
Bruce Momjian 已提交
305
			if (noPwd || badPwd)
306
			{
307

308 309
				if (badPwd)
					fprintf(stderr, "Password incorrect\n");
310

311 312
				fprintf(stderr, "Connecting to %s as %s\n",
						PQdb(AH->connection), newuser);
313

314
				need_pass = true;
315 316 317
				if (password)
					free(password);
				password = simple_prompt("Password: ", 100, false);
318
			}
319
			else
320 321
				die_horribly(AH, modulename, "could not reconnect to database: %s",
							 PQerrorMessage(newConn));
322
		}
323 324 325

	} while (need_pass);

326 327 328
	if (password)
		free(password);

329 330
	PQsetNoticeProcessor(newConn, notice_processor, NULL);

331
	return newConn;
332 333 334
}


335 336 337 338 339
/*
 * 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.
 */
B
Bruce Momjian 已提交
340 341 342 343 344
PGconn *
ConnectDatabase(Archive *AHX,
				const char *dbname,
				const char *pghost,
				const char *pgport,
345
				const char *username,
B
Bruce Momjian 已提交
346 347
				const int reqPwd,
				const int ignoreVersion)
P
Philip Warner 已提交
348
{
B
Bruce Momjian 已提交
349
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
350 351
	char	   *password = NULL;
	bool		need_pass = false;
P
Philip Warner 已提交
352 353

	if (AH->connection)
354
		die_horribly(AH, modulename, "already connected to a database\n");
P
Philip Warner 已提交
355

B
Bruce Momjian 已提交
356
	if (!dbname && !(dbname = getenv("PGDATABASE")))
357
		die_horribly(AH, modulename, "no database name specified\n");
P
Philip Warner 已提交
358 359 360 361 362 363

	AH->dbname = strdup(dbname);

	if (pghost != NULL)
		AH->pghost = strdup(pghost);
	else
B
Bruce Momjian 已提交
364
		AH->pghost = NULL;
P
Philip Warner 已提交
365 366 367 368

	if (pgport != NULL)
		AH->pgport = strdup(pgport);
	else
B
Bruce Momjian 已提交
369
		AH->pgport = NULL;
P
Philip Warner 已提交
370

371 372 373 374
	if (username != NULL)
		AH->username = strdup(username);
	else
		AH->username = NULL;
P
Philip Warner 已提交
375 376 377

	if (reqPwd)
	{
378 379
		password = simple_prompt("Password: ", 100, false);
		if (password == NULL)
380
			die_horribly(AH, modulename, "out of memory\n");
381
		AH->requirePassword = true;
P
Philip Warner 已提交
382
	}
383 384 385 386 387 388 389 390 391 392 393 394 395 396
	else
		AH->requirePassword = false;

	/*
	 * Start the connection.  Loop until we have a password if
	 * requested by backend.
	 */
	do
	{
		need_pass = false;
		AH->connection = PQsetdbLogin(AH->pghost, AH->pgport, NULL, NULL,
									  AH->dbname, AH->username, password);

		if (!AH->connection)
397
			die_horribly(AH, modulename, "failed to connect to database\n");
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412

		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);
P
Philip Warner 已提交
413 414 415

	/* check to see that the backend connection was successfully made */
	if (PQstatus(AH->connection) == CONNECTION_BAD)
416
		die_horribly(AH, modulename, "connection to database \"%s\" failed: %s",
B
Bruce Momjian 已提交
417
					 AH->dbname, PQerrorMessage(AH->connection));
P
Philip Warner 已提交
418 419 420 421

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

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

424
	/*
B
Bruce Momjian 已提交
425 426 427 428
	 * AH->currUser = PQuser(AH->connection);
	 *
	 * Removed because it prevented an initial \connect when dumping to SQL
	 * in pg_dump.
429
	 */
430

P
Philip Warner 已提交
431 432 433
	return AH->connection;
}

434 435 436 437 438 439 440

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


441
/* Public interface */
P
Philip Warner 已提交
442
/* Convenience function to send a query. Monitors result to handle COPY statements */
B
Bruce Momjian 已提交
443
int
444
ExecuteSqlCommand(ArchiveHandle *AH, PQExpBuffer qry, char *desc, bool use_blob)
445
{
446 447 448 449
	if (use_blob)
		return _executeSqlCommand(AH, AH->blobConnection, qry, desc);
	else
		return _executeSqlCommand(AH, AH->connection, qry, desc);
450 451
}

B
Bruce Momjian 已提交
452
/*
453 454 455 456
 * 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.
 */
B
Bruce Momjian 已提交
457 458
static int
_executeSqlCommand(ArchiveHandle *AH, PGconn *conn, PQExpBuffer qry, char *desc)
P
Philip Warner 已提交
459
{
B
Bruce Momjian 已提交
460
	PGresult   *res;
P
Philip Warner 已提交
461 462

	/* fprintf(stderr, "Executing: '%s'\n\n", qry->data); */
463
	res = PQexec(conn, qry->data);
P
Philip Warner 已提交
464
	if (!res)
465
		die_horribly(AH, modulename, "%s: no result from server\n", desc);
P
Philip Warner 已提交
466

B
Bruce Momjian 已提交
467
	if (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)
P
Philip Warner 已提交
468 469
	{
		if (PQresultStatus(res) == PGRES_COPY_IN)
470 471
		{
			if (conn != AH->connection)
472
				die_horribly(AH, modulename, "COPY command executed in non-primary connection\n");
473

P
Philip Warner 已提交
474
			AH->pgCopyIn = 1;
475
		}
B
Bruce Momjian 已提交
476
		else
477 478
			die_horribly(AH, modulename, "%s: %s",
						 desc, PQerrorMessage(AH->connection));
P
Philip Warner 已提交
479 480 481 482 483 484 485 486
	}

	PQclear(res);

	return strlen(qry->data);
}

/* Convenience function to send one or more queries. Monitors result to handle COPY statements */
B
Bruce Momjian 已提交
487 488
int
ExecuteSqlCommandBuf(ArchiveHandle *AH, void *qryv, int bufLen)
P
Philip Warner 已提交
489
{
B
Bruce Momjian 已提交
490 491 492 493 494 495
	int			loc;
	int			pos = 0;
	int			sPos = 0;
	char	   *qry = (char *) qryv;
	int			isEnd = 0;
	char	   *eos = qry + bufLen;
P
Philip Warner 已提交
496

B
Bruce Momjian 已提交
497 498 499 500
	/*
	 * fprintf(stderr, "\n\n*****\n
	 * Buffer:\n\n%s\n*******************\n\n", qry);
	 */
P
Philip Warner 已提交
501 502

	/* If we're in COPY IN mode, then just break it into lines and send... */
B
Bruce Momjian 已提交
503 504 505 506
	if (AH->pgCopyIn)
	{
		for (;;)
		{
P
Philip Warner 已提交
507 508 509 510 511 512

			/* Find a lf */
			loc = strcspn(&qry[pos], "\n") + pos;
			pos = 0;

			/* If no match, then wait */
B
Bruce Momjian 已提交
513
			if (loc >= (eos - qry))		/* None found */
P
Philip Warner 已提交
514 515 516 517 518
			{
				appendBinaryPQExpBuffer(AH->pgCopyBuf, qry, (eos - qry));
				break;
			};

B
Bruce Momjian 已提交
519 520 521 522 523
			/*
			 * fprintf(stderr, "Found cr at %d, prev char was %c, next was
			 * %c\n", loc, qry[loc-1], qry[loc+1]);
			 */

P
Philip Warner 已提交
524 525
			/* Count the number of preceding slashes */
			sPos = loc;
B
Bruce Momjian 已提交
526
			while (sPos > 0 && qry[sPos - 1] == '\\')
P
Philip Warner 已提交
527 528 529 530
				sPos--;

			sPos = loc - sPos;

B
Bruce Momjian 已提交
531 532
			/*
			 * If an odd number of preceding slashes, then \n was escaped
P
Philip Warner 已提交
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
			 * 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);
B
Bruce Momjian 已提交
550
				qry += loc + 1;
P
Philip Warner 已提交
551 552
				isEnd = (strcmp(AH->pgCopyBuf->data, "\\.\n") == 0);

553 554 555 556
				/*---------
				 * fprintf(stderr, "Sending '%s' via
				 *		COPY (at end = %d)\n\n", AH->pgCopyBuf->data, isEnd);
				 *---------
B
Bruce Momjian 已提交
557 558
				 */

559
				if (PQputline(AH->connection, AH->pgCopyBuf->data) != 0)
560
					die_horribly(AH, modulename, "error returned by PQputline\n");
P
Philip Warner 已提交
561 562 563

				resetPQExpBuffer(AH->pgCopyBuf);

B
Bruce Momjian 已提交
564 565 566 567
				/*
				 * fprintf(stderr, "Buffer is '%s'\n",
				 * AH->pgCopyBuf->data);
				 */
P
Philip Warner 已提交
568

B
Bruce Momjian 已提交
569 570
				if (isEnd)
				{
571
					if (PQendcopy(AH->connection) != 0)
572
						die_horribly(AH, modulename, "error returned by PQendcopy\n");
573

P
Philip Warner 已提交
574 575 576 577 578 579 580 581 582 583 584 585 586 587
					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 */
B
Bruce Momjian 已提交
588 589
	if (!AH->pgCopyIn)
	{
P
Philip Warner 已提交
590

B
Bruce Momjian 已提交
591 592 593 594 595
		/*
		 * 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.
P
Philip Warner 已提交
596 597
		 */

B
Bruce Momjian 已提交
598 599 600 601
		/*
		 * fprintf(stderr, "Buffer at start is: '%s'\n\n",
		 * AH->sqlBuf->data);
		 */
P
Philip Warner 已提交
602

B
Bruce Momjian 已提交
603
		for (pos = 0; pos < (eos - qry); pos++)
P
Philip Warner 已提交
604 605 606 607
		{
			appendPQExpBufferChar(AH->sqlBuf, qry[pos]);
			/* fprintf(stderr, " %c",qry[pos]); */

B
Bruce Momjian 已提交
608 609
			switch (AH->sqlparse.state)
			{
P
Philip Warner 已提交
610

B
Bruce Momjian 已提交
611
				case SQL_SCAN:	/* Default state == 0, set in _allocAH */
P
Philip Warner 已提交
612

613
					if (qry[pos] == ';' && AH->sqlparse.braceDepth == 0)
P
Philip Warner 已提交
614 615
					{
						/* Send It & reset the buffer */
B
Bruce Momjian 已提交
616 617 618 619 620

						/*
						 * fprintf(stderr, "    sending: '%s'\n\n",
						 * AH->sqlBuf->data);
						 */
621
						ExecuteSqlCommand(AH, AH->sqlBuf, "could not execute query", false);
P
Philip Warner 已提交
622 623
						resetPQExpBuffer(AH->sqlBuf);
						AH->sqlparse.lastChar = '\0';
B
Bruce Momjian 已提交
624 625
					}
					else
P
Philip Warner 已提交
626 627
					{
						if (qry[pos] == '"' || qry[pos] == '\'')
B
Bruce Momjian 已提交
628
						{
P
Philip Warner 已提交
629 630 631 632
							/* fprintf(stderr,"[startquote]\n"); */
							AH->sqlparse.state = SQL_IN_QUOTE;
							AH->sqlparse.quoteChar = qry[pos];
							AH->sqlparse.backSlash = 0;
B
Bruce Momjian 已提交
633
						}
P
Philip Warner 已提交
634 635 636 637
						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;
B
Bruce Momjian 已提交
638
						else if (qry[pos] == '(')
639 640 641 642
							AH->sqlparse.braceDepth++;
						else if (qry[pos] == ')')
							AH->sqlparse.braceDepth--;

P
Philip Warner 已提交
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
						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;
B
Bruce Momjian 已提交
666 667
					}
					else
P
Philip Warner 已提交
668 669 670 671 672 673 674 675 676
					{

						if (qry[pos] == '\\')
						{
							if (AH->sqlparse.lastChar == '\\')
								AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
							else
								AH->sqlparse.backSlash = 1;
						}
B
Bruce Momjian 已提交
677 678
						else
							AH->sqlparse.backSlash = 0;
P
Philip Warner 已提交
679 680 681 682 683 684 685 686 687 688 689 690 691
					}
					break;

			}
			AH->sqlparse.lastChar = qry[pos];
			/* fprintf(stderr, "\n"); */
		}

	}

	return 1;
}

B
Bruce Momjian 已提交
692 693
void
FixupBlobRefs(ArchiveHandle *AH, char *tablename)
P
Philip Warner 已提交
694
{
695
	PQExpBuffer tblQry;
B
Bruce Momjian 已提交
696 697 698 699 700
	PGresult   *res,
			   *uRes;
	int			i,
				n;
	char	   *attr;
P
Philip Warner 已提交
701 702 703 704

	if (strcmp(tablename, BLOB_XREF_TABLE) == 0)
		return;

705 706
	tblQry = createPQExpBuffer();

P
Philip Warner 已提交
707
	appendPQExpBuffer(tblQry, "SELECT a.attname FROM pg_class c, pg_attribute a, pg_type t "
B
Bruce Momjian 已提交
708 709
	 " WHERE a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid "
			  " AND t.typname = 'oid' AND c.relname = '%s';", tablename);
P
Philip Warner 已提交
710

711
	res = PQexec(AH->blobConnection, tblQry->data);
P
Philip Warner 已提交
712
	if (!res)
713 714
		die_horribly(AH, modulename, "could not find oid columns of table \"%s\": %s",
					 tablename, PQerrorMessage(AH->connection));
P
Philip Warner 已提交
715

B
Bruce Momjian 已提交
716 717
	if ((n = PQntuples(res)) == 0)
	{
718
		/* nothing to do */
719
		ahlog(AH, 1, "no OID type columns in table %s\n", tablename);
P
Philip Warner 已提交
720 721
	}

B
Bruce Momjian 已提交
722
	for (i = 0; i < n; i++)
P
Philip Warner 已提交
723 724 725
	{
		attr = PQgetvalue(res, i, 0);

726
		ahlog(AH, 1, "fixing BLOB cross-references for %s.%s\n", tablename, attr);
P
Philip Warner 已提交
727 728 729

		resetPQExpBuffer(tblQry);

730
		/*
B
Bruce Momjian 已提交
731 732
		 * We should use coalesce here (rather than 'exists'), but it
		 * seems to be broken in 7.0.2 (weird optimizer strategy)
733
		 */
B
Bruce Momjian 已提交
734
		appendPQExpBuffer(tblQry, "UPDATE \"%s\" SET \"%s\" = ", tablename, attr);
735
		appendPQExpBuffer(tblQry, " (SELECT x.newOid FROM \"%s\" x WHERE x.oldOid = \"%s\".\"%s\")",
B
Bruce Momjian 已提交
736
						  BLOB_XREF_TABLE, tablename, attr);
737
		appendPQExpBuffer(tblQry, " where exists"
B
Bruce Momjian 已提交
738 739
				  "(select * from %s x where x.oldOid = \"%s\".\"%s\");",
						  BLOB_XREF_TABLE, tablename, attr);
P
Philip Warner 已提交
740

741
		ahlog(AH, 10, "SQL: %s\n", tblQry->data);
P
Philip Warner 已提交
742

743
		uRes = PQexec(AH->blobConnection, tblQry->data);
P
Philip Warner 已提交
744
		if (!uRes)
745 746 747
			die_horribly(AH, modulename,
						 "could not update column \"%s\" of table \"%s\": %s",
						 attr, tablename, PQerrorMessage(AH->blobConnection));
P
Philip Warner 已提交
748

B
Bruce Momjian 已提交
749
		if (PQresultStatus(uRes) != PGRES_COMMAND_OK)
750 751 752
			die_horribly(AH, modulename,
						 "error while updating column \"%s\" of table \"%s\": %s",
						 attr, tablename, PQerrorMessage(AH->blobConnection));
P
Philip Warner 已提交
753 754 755 756 757

		PQclear(uRes);
	}

	PQclear(res);
758
	destroyPQExpBuffer(tblQry);
P
Philip Warner 已提交
759 760 761 762 763
}

/**********
 *	Convenient SQL calls
 **********/
B
Bruce Momjian 已提交
764 765
void
CreateBlobXrefTable(ArchiveHandle *AH)
P
Philip Warner 已提交
766
{
B
Bruce Momjian 已提交
767
	PQExpBuffer qry = createPQExpBuffer();
P
Philip Warner 已提交
768

769 770 771 772
	/* IF we don't have a BLOB connection, then create one */
	if (!AH->blobConnection)
		AH->blobConnection = _connectDB(AH, NULL, NULL);

773
	ahlog(AH, 1, "creating table for BLOB cross-references\n");
P
Philip Warner 已提交
774 775 776

	appendPQExpBuffer(qry, "Create Temporary Table %s(oldOid oid, newOid oid);", BLOB_XREF_TABLE);

777
	ExecuteSqlCommand(AH, qry, "could not create BLOB cross reference table", true);
P
Philip Warner 已提交
778 779 780 781

	resetPQExpBuffer(qry);

	appendPQExpBuffer(qry, "Create Unique Index %s_ix on %s(oldOid)", BLOB_XREF_TABLE, BLOB_XREF_TABLE);
782
	ExecuteSqlCommand(AH, qry, "could not create index on BLOB cross reference table", true);
783 784

	destroyPQExpBuffer(qry);
P
Philip Warner 已提交
785 786
}

B
Bruce Momjian 已提交
787 788
void
InsertBlobXref(ArchiveHandle *AH, int old, int new)
P
Philip Warner 已提交
789
{
B
Bruce Momjian 已提交
790
	PQExpBuffer qry = createPQExpBuffer();
P
Philip Warner 已提交
791 792 793

	appendPQExpBuffer(qry, "Insert Into %s(oldOid, newOid) Values (%d, %d);", BLOB_XREF_TABLE, old, new);

794
	ExecuteSqlCommand(AH, qry, "could not create BLOB cross reference entry", true);
795 796

	destroyPQExpBuffer(qry);
P
Philip Warner 已提交
797 798
}

B
Bruce Momjian 已提交
799 800
void
StartTransaction(ArchiveHandle *AH)
P
Philip Warner 已提交
801
{
B
Bruce Momjian 已提交
802
	PQExpBuffer qry = createPQExpBuffer();
P
Philip Warner 已提交
803 804 805

	appendPQExpBuffer(qry, "Begin;");

806
	ExecuteSqlCommand(AH, qry, "could not start database transaction", false);
807
	AH->txActive = true;
808 809

	destroyPQExpBuffer(qry);
810 811
}

B
Bruce Momjian 已提交
812 813
void
StartTransactionXref(ArchiveHandle *AH)
814
{
B
Bruce Momjian 已提交
815
	PQExpBuffer qry = createPQExpBuffer();
816 817 818

	appendPQExpBuffer(qry, "Begin;");

819 820
	ExecuteSqlCommand(AH, qry,
					  "could not start transaction for BLOB cross references", true);
821
	AH->blobTxActive = true;
822 823

	destroyPQExpBuffer(qry);
P
Philip Warner 已提交
824 825
}

B
Bruce Momjian 已提交
826 827
void
CommitTransaction(ArchiveHandle *AH)
P
Philip Warner 已提交
828
{
B
Bruce Momjian 已提交
829
	PQExpBuffer qry = createPQExpBuffer();
P
Philip Warner 已提交
830

B
Bruce Momjian 已提交
831
	appendPQExpBuffer(qry, "Commit;");
P
Philip Warner 已提交
832

833
	ExecuteSqlCommand(AH, qry, "could not commit database transaction", false);
834
	AH->txActive = false;
835 836

	destroyPQExpBuffer(qry);
P
Philip Warner 已提交
837 838
}

B
Bruce Momjian 已提交
839 840
void
CommitTransactionXref(ArchiveHandle *AH)
841
{
B
Bruce Momjian 已提交
842
	PQExpBuffer qry = createPQExpBuffer();
P
Philip Warner 已提交
843

844 845
	appendPQExpBuffer(qry, "Commit;");

846
	ExecuteSqlCommand(AH, qry, "could not commit transaction for BLOB cross references", true);
847
	AH->blobTxActive = false;
848 849

	destroyPQExpBuffer(qry);
850
}