pg_backup_db.c 17.0 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
 *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.65 2005/09/11 04:10:25 tgl Exp $
9
 *
10
 *-------------------------------------------------------------------------
P
Philip Warner 已提交
11 12
 */

13 14 15
#include "pg_backup.h"
#include "pg_backup_archiver.h"
#include "pg_backup_db.h"
16
#include "dumputils.h"
17

P
Peter Eisentraut 已提交
18
#include <unistd.h>
P
Philip Warner 已提交
19 20 21 22 23 24 25
#include <ctype.h>

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

#include "libpq-fe.h"
26
#include "libpq/libpq-fs.h"
P
Philip Warner 已提交
27 28 29 30
#ifndef HAVE_STRDUP
#include "strdup.h"
#endif

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

B
Bruce Momjian 已提交
33
static void _check_database_version(ArchiveHandle *AH, bool ignoreVersion);
34
static PGconn *_connectDB(ArchiveHandle *AH, const char *newdbname, const char *newUser);
35
static void notice_processor(void *arg, const char *message);
36 37
static char *_sendSQLLine(ArchiveHandle *AH, char *qry, char *eos);
static char *_sendCopyLine(ArchiveHandle *AH, char *qry, char *eos);
P
Philip Warner 已提交
38

39 40
static bool _isIdentChar(unsigned char c);
static bool _isDQChar(unsigned char c, bool atStart);
P
Philip Warner 已提交
41

42 43
#define DB_MAX_ERR_STMT 128

44
static int
45
_parse_version(ArchiveHandle *AH, const char *versionString)
46
{
47
	int			v;
48

49 50
	v = parse_version(versionString);
	if (v < 0)
51
		die_horribly(AH, modulename, "could not parse version string \"%s\"\n", versionString);
52

53
	return v;
54
}
P
Philip Warner 已提交
55 56 57 58

static void
_check_database_version(ArchiveHandle *AH, bool ignoreVersion)
{
59
	int			myversion;
P
Philip Warner 已提交
60
	const char *remoteversion_str;
61
	int			remoteversion;
P
Philip Warner 已提交
62

63 64
	myversion = _parse_version(AH, PG_VERSION);

65 66 67
	remoteversion_str = PQparameterStatus(AH->connection, "server_version");
	if (!remoteversion_str)
		die_horribly(AH, modulename, "could not get server_version from libpq\n");
P
Philip Warner 已提交
68

69
	remoteversion = _parse_version(AH, remoteversion_str);
70

71
	AH->public.remoteVersionStr = strdup(remoteversion_str);
72 73
	AH->public.remoteVersion = remoteversion;

74
	if (myversion != remoteversion
75 76
		&& (remoteversion < AH->public.minRemoteVersion ||
			remoteversion > AH->public.maxRemoteVersion))
P
Philip Warner 已提交
77
	{
78
		write_msg(NULL, "server version: %s; %s version: %s\n",
79
				  remoteversion_str, progname, PG_VERSION);
P
Philip Warner 已提交
80
		if (ignoreVersion)
81
			write_msg(NULL, "proceeding despite version mismatch\n");
P
Philip Warner 已提交
82
		else
83
			die_horribly(AH, NULL, "aborting because of version mismatch  (Use the -i option to proceed anyway.)\n");
P
Philip Warner 已提交
84 85 86
	}
}

87
/*
88 89
 * Reconnect to the server.  If dbname is not NULL, use that database,
 * else the one associated with the archive handle.  If username is
90
 * not NULL, use that user name, else the one from the handle.	If
91 92
 * both the database and the user match the existing connection already,
 * nothing will be done.
93 94
 *
 * Returns 1 in any case.
95
 */
B
Bruce Momjian 已提交
96
int
97
ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *username)
98
{
B
Bruce Momjian 已提交
99
	PGconn	   *newConn;
100 101 102 103 104 105 106
	const char *newdbname;
	const char *newusername;

	if (!dbname)
		newdbname = PQdb(AH->connection);
	else
		newdbname = dbname;
107

108 109
	if (!username)
		newusername = PQuser(AH->connection);
110
	else
111
		newusername = username;
112 113

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

118
	newConn = _connectDB(AH, newdbname, newusername);
119 120 121

	PQfinish(AH->connection);
	AH->connection = newConn;
122

123 124 125 126 127 128
	return 1;
}

/*
 * Connect to the db again.
 */
B
Bruce Momjian 已提交
129
static PGconn *
130
_connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
131 132
{
	int			need_pass;
B
Bruce Momjian 已提交
133
	PGconn	   *newConn;
134
	char	   *password = NULL;
135 136
	int			badPwd = 0;
	int			noPwd = 0;
B
Bruce Momjian 已提交
137 138
	char	   *newdb;
	char	   *newuser;
139

140
	if (!reqdb)
141 142
		newdb = PQdb(AH->connection);
	else
B
Bruce Momjian 已提交
143
		newdb = (char *) reqdb;
144 145 146 147

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

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

152 153 154 155
	if (AH->requirePassword)
	{
		password = simple_prompt("Password: ", 100, false);
		if (password == NULL)
156
			die_horribly(AH, modulename, "out of memory\n");
157 158
	}

159 160
	do
	{
161 162
		need_pass = false;
		newConn = PQsetdbLogin(PQhost(AH->connection), PQport(AH->connection),
B
Bruce Momjian 已提交
163
							   NULL, NULL, newdb,
164
							   newuser, password);
165
		if (!newConn)
166
			die_horribly(AH, modulename, "failed to reconnect to database\n");
167 168 169

		if (PQstatus(newConn) == CONNECTION_BAD)
		{
170
			noPwd = (strcmp(PQerrorMessage(newConn),
171
							PQnoPasswordSupplied) == 0);
172
			badPwd = (strncmp(PQerrorMessage(newConn),
173
					"Password authentication failed for user", 39) == 0);
174

B
Bruce Momjian 已提交
175
			if (noPwd || badPwd)
176 177 178
			{
				if (badPwd)
					fprintf(stderr, "Password incorrect\n");
179

180
				fprintf(stderr, "Connecting to %s as %s\n",
181
						newdb, newuser);
182

183
				need_pass = true;
184 185 186
				if (password)
					free(password);
				password = simple_prompt("Password: ", 100, false);
187
			}
188
			else
189 190
				die_horribly(AH, modulename, "could not reconnect to database: %s",
							 PQerrorMessage(newConn));
191
			PQfinish(newConn);
192
		}
193 194
	} while (need_pass);

195 196 197
	if (password)
		free(password);

198 199 200
	/* check for version mismatch */
	_check_database_version(AH, true);

201 202
	PQsetNoticeProcessor(newConn, notice_processor, NULL);

203
	return newConn;
204 205 206
}


207 208 209 210 211
/*
 * 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 已提交
212 213 214 215 216
PGconn *
ConnectDatabase(Archive *AHX,
				const char *dbname,
				const char *pghost,
				const char *pgport,
217
				const char *username,
B
Bruce Momjian 已提交
218 219
				const int reqPwd,
				const int ignoreVersion)
P
Philip Warner 已提交
220
{
B
Bruce Momjian 已提交
221
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
222 223
	char	   *password = NULL;
	bool		need_pass = false;
P
Philip Warner 已提交
224 225

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

	if (reqPwd)
	{
230 231
		password = simple_prompt("Password: ", 100, false);
		if (password == NULL)
232
			die_horribly(AH, modulename, "out of memory\n");
233
		AH->requirePassword = true;
P
Philip Warner 已提交
234
	}
235 236 237 238
	else
		AH->requirePassword = false;

	/*
239 240
	 * Start the connection.  Loop until we have a password if requested
	 * by backend.
241 242 243 244
	 */
	do
	{
		need_pass = false;
245 246
		AH->connection = PQsetdbLogin(pghost, pgport, NULL, NULL,
									  dbname, username, password);
247 248

		if (!AH->connection)
249
			die_horribly(AH, modulename, "failed to connect to database\n");
250 251

		if (PQstatus(AH->connection) == CONNECTION_BAD &&
252
			strcmp(PQerrorMessage(AH->connection), PQnoPasswordSupplied) == 0 &&
253 254 255 256 257 258 259 260 261 262 263 264
			!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 已提交
265 266 267

	/* check to see that the backend connection was successfully made */
	if (PQstatus(AH->connection) == CONNECTION_BAD)
268
		die_horribly(AH, modulename, "connection to database \"%s\" failed: %s",
269
					 PQdb(AH->connection), PQerrorMessage(AH->connection));
P
Philip Warner 已提交
270 271 272 273

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

274 275
	PQsetNoticeProcessor(AH->connection, notice_processor, NULL);

P
Philip Warner 已提交
276 277 278
	return AH->connection;
}

279

280 281
static void
notice_processor(void *arg, const char *message)
282 283 284 285 286
{
	write_msg(NULL, "%s", message);
}


287
/* Public interface */
P
Philip Warner 已提交
288
/* Convenience function to send a query. Monitors result to handle COPY statements */
B
Bruce Momjian 已提交
289
int
290
ExecuteSqlCommand(ArchiveHandle *AH, PQExpBuffer qry, char *desc)
P
Philip Warner 已提交
291
{
292
	PGconn	   *conn = AH->connection;
B
Bruce Momjian 已提交
293
	PGresult   *res;
B
Bruce Momjian 已提交
294
	char		errStmt[DB_MAX_ERR_STMT];
P
Philip Warner 已提交
295 296

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

B
Bruce Momjian 已提交
301
	if (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)
P
Philip Warner 已提交
302 303
	{
		if (PQresultStatus(res) == PGRES_COPY_IN)
304
		{
P
Philip Warner 已提交
305
			AH->pgCopyIn = 1;
306
		}
B
Bruce Momjian 已提交
307
		else
308 309
		{
			strncpy(errStmt, qry->data, DB_MAX_ERR_STMT);
B
Bruce Momjian 已提交
310 311 312 313 314 315
			if (errStmt[DB_MAX_ERR_STMT - 1] != '\0')
			{
				errStmt[DB_MAX_ERR_STMT - 4] = '.';
				errStmt[DB_MAX_ERR_STMT - 3] = '.';
				errStmt[DB_MAX_ERR_STMT - 2] = '.';
				errStmt[DB_MAX_ERR_STMT - 1] = '\0';
316 317
			}
			warn_or_die_horribly(AH, modulename, "%s: %s    Command was: %s\n",
B
Bruce Momjian 已提交
318 319
								 desc, PQerrorMessage(AH->connection),
								 errStmt);
320
		}
P
Philip Warner 已提交
321 322 323 324 325 326 327
	}

	PQclear(res);

	return strlen(qry->data);
}

328
/*
329 330
 * Used by ExecuteSqlCommandBuf to send one buffered line when running a COPY command.
 */
331 332
static char *
_sendCopyLine(ArchiveHandle *AH, char *qry, char *eos)
P
Philip Warner 已提交
333
{
P
Peter Eisentraut 已提交
334
	size_t		loc;			/* Location of next newline */
335 336 337
	int			pos = 0;		/* Current position */
	int			sPos = 0;		/* Last pos of a slash char */
	int			isEnd = 0;
P
Philip Warner 已提交
338

339
	/* loop to find unquoted newline ending the line of COPY data */
340 341
	for (;;)
	{
342
		loc = strcspn(&qry[pos], "\n") + pos;
P
Philip Warner 已提交
343

344
		/* If no match, then wait */
345
		if (loc >= (eos - qry)) /* None found */
B
Bruce Momjian 已提交
346
		{
347 348 349
			appendBinaryPQExpBuffer(AH->pgCopyBuf, qry, (eos - qry));
			return eos;
		}
P
Philip Warner 已提交
350

351 352 353 354
		/*
		 * fprintf(stderr, "Found cr at %d, prev char was %c, next was
		 * %c\n", loc, qry[loc-1], qry[loc+1]);
		 */
B
Bruce Momjian 已提交
355

356 357 358 359
		/* Count the number of preceding slashes */
		sPos = loc;
		while (sPos > 0 && qry[sPos - 1] == '\\')
			sPos--;
P
Philip Warner 已提交
360

361
		sPos = loc - sPos;
P
Philip Warner 已提交
362

363
		/*
364 365
		 * If an odd number of preceding slashes, then \n was escaped so
		 * set the next search pos, and loop (if any left).
366 367 368 369 370 371
		 */
		if ((sPos & 1) == 1)
		{
			/* fprintf(stderr, "cr was escaped\n"); */
			pos = loc + 1;
			if (pos >= (eos - qry))
P
Philip Warner 已提交
372
			{
373 374
				appendBinaryPQExpBuffer(AH->pgCopyBuf, qry, (eos - qry));
				return eos;
P
Philip Warner 已提交
375
			}
376
		}
377 378
		else
			break;
379
	}
P
Philip Warner 已提交
380

381 382 383 384
	/* We found an unquoted newline */
	qry[loc] = '\0';
	appendPQExpBuffer(AH->pgCopyBuf, "%s\n", qry);
	isEnd = (strcmp(AH->pgCopyBuf->data, "\\.\n") == 0);
B
Bruce Momjian 已提交
385

386 387 388 389 390
	/*---------
	 * fprintf(stderr, "Sending '%s' via
	 *		COPY (at end = %d)\n\n", AH->pgCopyBuf->data, isEnd);
	 *---------
	 */
P
Philip Warner 已提交
391

392 393
	if (PQputline(AH->connection, AH->pgCopyBuf->data) != 0)
		die_horribly(AH, modulename, "error returned by PQputline\n");
P
Philip Warner 已提交
394

395
	resetPQExpBuffer(AH->pgCopyBuf);
P
Philip Warner 已提交
396

397
	/*
398
	 * fprintf(stderr, "Buffer is '%s'\n", AH->pgCopyBuf->data);
399
	 */
400

401 402 403 404
	if (isEnd)
	{
		if (PQendcopy(AH->connection) != 0)
			die_horribly(AH, modulename, "error returned by PQendcopy\n");
P
Philip Warner 已提交
405

406 407
		AH->pgCopyIn = 0;
	}
P
Philip Warner 已提交
408

409 410
	return qry + loc + 1;
}
P
Philip Warner 已提交
411

412
/*
413 414
 * Used by ExecuteSqlCommandBuf to send one buffered line of SQL
 * (not data for the copy command).
415
 */
416 417
static char *
_sendSQLLine(ArchiveHandle *AH, char *qry, char *eos)
418 419
{
	/*
420 421 422
	 * 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,
423 424 425 426 427 428
	 * un-commented semi-colon that's not within any parentheses.
	 *
	 * Note: the input can be split into bufferloads at arbitrary boundaries.
	 * Therefore all state must be kept in AH->sqlparse, not in local
	 * variables of this routine.  We assume that AH->sqlparse was
	 * filled with zeroes when created.
429
	 */
430
	for (; qry < eos; qry++)
431
	{
432
		switch (AH->sqlparse.state)
433
		{
434 435 436
			case SQL_SCAN:		/* Default state == 0, set in _allocAH */
				if (*qry == ';' && AH->sqlparse.braceDepth == 0)
				{
B
Bruce Momjian 已提交
437
					/*
438 439
					 * We've found the end of a statement. Send it and
					 * reset the buffer.
440
					 */
441 442 443 444 445
					appendPQExpBufferChar(AH->sqlBuf, ';'); /* inessential */
					ExecuteSqlCommand(AH, AH->sqlBuf,
									  "could not execute query");
					resetPQExpBuffer(AH->sqlBuf);
					AH->sqlparse.lastChar = '\0';
B
Bruce Momjian 已提交
446

447
					/*
448 449
					 * Remove any following newlines - so that
					 * embedded COPY commands don't get a starting newline.
450
					 */
451 452 453 454 455 456 457 458 459 460 461
					qry++;
					while (qry < eos && *qry == '\n')
						qry++;

					/* We've finished one line, so exit */
					return qry;
				}
				else if (*qry == '\'')
				{
					if (AH->sqlparse.lastChar == 'E')
						AH->sqlparse.state = SQL_IN_E_QUOTE;
462
					else
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
						AH->sqlparse.state = SQL_IN_SINGLE_QUOTE;
					AH->sqlparse.backSlash = false;
				}
				else if (*qry == '"')
				{
					AH->sqlparse.state = SQL_IN_DOUBLE_QUOTE;
				}
				/*
				 * Look for dollar-quotes. We make the assumption that
				 * $-quotes will not have an ident character just
				 * before them in pg_dump output.  XXX is this
				 * good enough?
				 */
				else if (*qry == '$' && !_isIdentChar(AH->sqlparse.lastChar))
				{
					AH->sqlparse.state = SQL_IN_DOLLAR_TAG;
					/* initialize separate buffer with possible tag */
					if (AH->sqlparse.tagBuf == NULL)
						AH->sqlparse.tagBuf = createPQExpBuffer();
					else
						resetPQExpBuffer(AH->sqlparse.tagBuf);
					appendPQExpBufferChar(AH->sqlparse.tagBuf, *qry);
				}
				else if (*qry == '-' && AH->sqlparse.lastChar == '-')
					AH->sqlparse.state = SQL_IN_SQL_COMMENT;
				else if (*qry == '*' && AH->sqlparse.lastChar == '/')
					AH->sqlparse.state = SQL_IN_EXT_COMMENT;
				else if (*qry == '(')
					AH->sqlparse.braceDepth++;
				else if (*qry == ')')
					AH->sqlparse.braceDepth--;
				break;

			case SQL_IN_SQL_COMMENT:
				if (*qry == '\n')
					AH->sqlparse.state = SQL_SCAN;
				break;

			case SQL_IN_EXT_COMMENT:
				/*
				 * This isn't fully correct, because we don't account for
				 * nested slash-stars, but pg_dump never emits such.
				 */
				if (AH->sqlparse.lastChar == '*' && *qry == '/')
					AH->sqlparse.state = SQL_SCAN;
				break;

			case SQL_IN_SINGLE_QUOTE:
				/* We needn't handle '' specially */
				if (*qry == '\'' && !AH->sqlparse.backSlash)
					AH->sqlparse.state = SQL_SCAN;
				else if (*qry == '\\')
					AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
				else
					AH->sqlparse.backSlash = false;
				break;

			case SQL_IN_E_QUOTE:
				/*
				 * Eventually we will need to handle '' specially, because
				 * after E'...''... we should still be in E_QUOTE state.
				 *
				 * XXX problem: how do we tell whether the dump was made
				 * by a version that thinks backslashes aren't special
				 * in non-E literals??
				 */
				if (*qry == '\'' && !AH->sqlparse.backSlash)
					AH->sqlparse.state = SQL_SCAN;
				else if (*qry == '\\')
					AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
				else
					AH->sqlparse.backSlash = false;
				break;

			case SQL_IN_DOUBLE_QUOTE:
				/* We needn't handle "" specially */
				if (*qry == '"')
					AH->sqlparse.state = SQL_SCAN;
				break;

			case SQL_IN_DOLLAR_TAG:
				if (*qry == '$')
				{
					/* Do not add the closing $ to tagBuf */
					AH->sqlparse.state = SQL_IN_DOLLAR_QUOTE;
					AH->sqlparse.minTagEndPos = AH->sqlBuf->len + AH->sqlparse.tagBuf->len + 1;
				}
				else if (_isDQChar(*qry, (AH->sqlparse.tagBuf->len == 1)))
				{
					/* Valid, so add to tag */
					appendPQExpBufferChar(AH->sqlparse.tagBuf, *qry);
				}
				else
				{
					/*
					 * Ooops, we're not really in a dollar-tag.  Valid tag
					 * chars do not include the various chars we look for
					 * in this state machine, so it's safe to just jump
					 * from this state back to SCAN.  We have to back up
					 * the qry pointer so that the current character gets
					 * rescanned in SCAN state; and then "continue" so that
					 * the bottom-of-loop actions aren't done yet.
					 */
					AH->sqlparse.state = SQL_SCAN;
					qry--;
					continue;
				}
				break;

			case SQL_IN_DOLLAR_QUOTE:
				/*
				 * If we are at a $, see whether what precedes it matches
				 * tagBuf.  (Remember that the trailing $ of the tag was
				 * not added to tagBuf.)  However, don't compare until we
				 * have enough data to be a possible match --- this is
				 * needed to avoid false match on '$a$a$...'
				 */
				if (*qry == '$' &&
					AH->sqlBuf->len >= AH->sqlparse.minTagEndPos &&
					strcmp(AH->sqlparse.tagBuf->data,
						   AH->sqlBuf->data + AH->sqlBuf->len - AH->sqlparse.tagBuf->len) == 0)
					AH->sqlparse.state = SQL_SCAN;
				break;
		}
587

588 589
		appendPQExpBufferChar(AH->sqlBuf, *qry);
		AH->sqlparse.lastChar = *qry;
590
	}
P
Philip Warner 已提交
591

592
	/*
593
	 * If we get here, we've processed entire bufferload with no complete SQL
594 595 596
	 * stmt
	 */
	return eos;
597 598 599 600 601
}


/* Convenience function to send one or more queries. Monitors result to handle COPY statements */
int
P
Peter Eisentraut 已提交
602
ExecuteSqlCommandBuf(ArchiveHandle *AH, void *qryv, size_t bufLen)
603 604 605 606 607 608 609 610 611 612 613 614
{
	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)
	{
615
		if (AH->pgCopyIn)
616
			qry = _sendCopyLine(AH, qry, eos);
617
		else
618
			qry = _sendSQLLine(AH, qry, eos);
P
Philip Warner 已提交
619 620 621 622 623
	}

	return 1;
}

B
Bruce Momjian 已提交
624 625
void
StartTransaction(ArchiveHandle *AH)
P
Philip Warner 已提交
626
{
B
Bruce Momjian 已提交
627
	PQExpBuffer qry = createPQExpBuffer();
P
Philip Warner 已提交
628

629
	appendPQExpBuffer(qry, "BEGIN");
P
Philip Warner 已提交
630

631
	ExecuteSqlCommand(AH, qry, "could not start database transaction");
632 633

	destroyPQExpBuffer(qry);
P
Philip Warner 已提交
634 635
}

B
Bruce Momjian 已提交
636 637
void
CommitTransaction(ArchiveHandle *AH)
P
Philip Warner 已提交
638
{
B
Bruce Momjian 已提交
639
	PQExpBuffer qry = createPQExpBuffer();
P
Philip Warner 已提交
640

641
	appendPQExpBuffer(qry, "COMMIT");
P
Philip Warner 已提交
642

643
	ExecuteSqlCommand(AH, qry, "could not commit database transaction");
644 645

	destroyPQExpBuffer(qry);
646
}
647

648
static bool
B
Bruce Momjian 已提交
649
_isIdentChar(unsigned char c)
650
{
B
Bruce Momjian 已提交
651 652 653 654 655 656 657 658
	if ((c >= 'a' && c <= 'z')
		|| (c >= 'A' && c <= 'Z')
		|| (c >= '0' && c <= '9')
		|| (c == '_')
		|| (c == '$')
		|| (c >= (unsigned char) '\200')		/* no need to check <=
												 * \377 */
		)
659
		return true;
660
	else
661
		return false;
662 663
}

664 665
static bool
_isDQChar(unsigned char c, bool atStart)
666
{
B
Bruce Momjian 已提交
667 668 669
	if ((c >= 'a' && c <= 'z')
		|| (c >= 'A' && c <= 'Z')
		|| (c == '_')
670
		|| (!atStart && c >= '0' && c <= '9')
B
Bruce Momjian 已提交
671 672 673
		|| (c >= (unsigned char) '\200')		/* no need to check <=
												 * \377 */
		)
674
		return true;
675
	else
676
		return false;
677
}