common.c 32.8 KB
Newer Older
P
Peter Eisentraut 已提交
1 2 3
/*
 * psql - the PostgreSQL interactive terminal
 *
4
 * Copyright (c) 2000-2008, PostgreSQL Global Development Group
P
Peter Eisentraut 已提交
5
 *
6
 * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.139 2008/05/14 19:10:29 tgl Exp $
P
Peter Eisentraut 已提交
7
 */
8
#include "postgres_fe.h"
9 10
#include "common.h"

11
#include <ctype.h>
12 13
#include <signal.h>
#ifndef WIN32
B
Bruce Momjian 已提交
14
#include <unistd.h>				/* for write() */
15
#else
16
#include <io.h>					/* for _write() */
17
#include <win32.h>
18 19
#endif

20 21
#include "portability/instr_time.h"

22
#include "pqsignal.h"
23 24

#include "settings.h"
25
#include "command.h"
26
#include "copy.h"
27
#include "mbprint.h"
28

29

30

31
static bool ExecQueryUsingCursor(const char *query, double *elapsed_msec);
32
static bool command_no_begin(const char *query);
33
static bool is_select_command(const char *query);
34

35
/*
36 37
 * "Safe" wrapper around strdup()
 */
B
Bruce Momjian 已提交
38
char *
39
pg_strdup(const char *string)
40
{
B
Bruce Momjian 已提交
41 42 43 44
	char	   *tmp;

	if (!string)
	{
45
		fprintf(stderr, _("%s: pg_strdup: cannot duplicate null pointer (internal error)\n"),
46
				pset.progname);
B
Bruce Momjian 已提交
47 48 49 50 51
		exit(EXIT_FAILURE);
	}
	tmp = strdup(string);
	if (!tmp)
	{
52
		psql_error("out of memory\n");
B
Bruce Momjian 已提交
53 54 55
		exit(EXIT_FAILURE);
	}
	return tmp;
56 57
}

58
void *
59
pg_malloc(size_t size)
60 61 62 63 64 65 66 67 68 69 70 71 72
{
	void	   *tmp;

	tmp = malloc(size);
	if (!tmp)
	{
		psql_error("out of memory\n");
		exit(EXIT_FAILURE);
	}
	return tmp;
}

void *
73
pg_malloc_zero(size_t size)
74 75 76
{
	void	   *tmp;

77
	tmp = pg_malloc(size);
78 79 80 81 82
	memset(tmp, 0, size);
	return tmp;
}

void *
83
pg_calloc(size_t nmemb, size_t size)
84 85 86 87 88 89 90 91 92 93 94
{
	void	   *tmp;

	tmp = calloc(nmemb, size);
	if (!tmp)
	{
		psql_error("out of memory");
		exit(EXIT_FAILURE);
	}
	return tmp;
}
95 96 97 98 99 100 101 102 103 104

/*
 * setQFout
 * -- handler for -o command line option and \o command
 *
 * Tries to open file fname (or pipe if fname starts with '|')
 * and stores the file handle in pset)
 * Upon failure, sets stdout and returns false.
 */
bool
105
setQFout(const char *fname)
106
{
B
Bruce Momjian 已提交
107
	bool		status = true;
108

B
Bruce Momjian 已提交
109
	/* Close old file/pipe */
110
	if (pset.queryFout && pset.queryFout != stdout && pset.queryFout != stderr)
B
Bruce Momjian 已提交
111
	{
112 113
		if (pset.queryFoutPipe)
			pclose(pset.queryFout);
B
Bruce Momjian 已提交
114
		else
115
			fclose(pset.queryFout);
B
Bruce Momjian 已提交
116
	}
117

B
Bruce Momjian 已提交
118 119 120
	/* If no filename, set stdout */
	if (!fname || fname[0] == '\0')
	{
121 122
		pset.queryFout = stdout;
		pset.queryFoutPipe = false;
B
Bruce Momjian 已提交
123 124 125
	}
	else if (*fname == '|')
	{
126 127
		pset.queryFout = popen(fname + 1, "w");
		pset.queryFoutPipe = true;
B
Bruce Momjian 已提交
128 129 130
	}
	else
	{
131 132
		pset.queryFout = fopen(fname, "w");
		pset.queryFoutPipe = false;
B
Bruce Momjian 已提交
133 134
	}

135
	if (!(pset.queryFout))
B
Bruce Momjian 已提交
136
	{
P
Peter Eisentraut 已提交
137
		psql_error("%s: %s\n", fname, strerror(errno));
138 139
		pset.queryFout = stdout;
		pset.queryFoutPipe = false;
B
Bruce Momjian 已提交
140 141 142 143
		status = false;
	}

	/* Direct signals */
B
Hi!  
Bruce Momjian 已提交
144
#ifndef WIN32
145
	pqsignal(SIGPIPE, pset.queryFoutPipe ? SIG_IGN : SIG_DFL);
B
Hi!  
Bruce Momjian 已提交
146
#endif
B
Bruce Momjian 已提交
147 148

	return status;
149 150 151 152
}



P
Peter Eisentraut 已提交
153 154
/*
 * Error reporting for scripts. Errors should look like
155
 *	 psql:filename:lineno: message
P
Peter Eisentraut 已提交
156 157 158
 *
 */
void
B
Bruce Momjian 已提交
159
psql_error(const char *fmt,...)
P
Peter Eisentraut 已提交
160
{
161
	va_list		ap;
P
Peter Eisentraut 已提交
162

163 164 165
	fflush(stdout);
	if (pset.queryFout != stdout)
		fflush(pset.queryFout);
P
Peter Eisentraut 已提交
166

167
	if (pset.inputfile)
168
		fprintf(stderr, "%s:%s:" UINT64_FORMAT ": ", pset.progname, pset.inputfile, pset.lineno);
169
	va_start(ap, fmt);
170
	vfprintf(stderr, _(fmt), ap);
171
	va_end(ap);
P
Peter Eisentraut 已提交
172 173 174
}


175 176

/*
177
 * for backend Notice messages (INFO, WARNING, etc)
178
 */
P
Peter Eisentraut 已提交
179
void
180
NoticeProcessor(void *arg, const char *message)
P
Peter Eisentraut 已提交
181
{
182 183
	(void) arg;					/* not used */
	psql_error("%s", message);
P
Peter Eisentraut 已提交
184 185 186 187
}



188
/*
189
 * Code to support query cancellation
190
 *
191
 * Before we start a query, we enable the SIGINT signal catcher to send a
192
 * cancel request to the backend. Note that sending the cancel directly from
193
 * the signal handler is safe because PQcancel() is written to make it
194
 * so. We use write() to report to stderr because it's better to use simple
195
 * facilities in a signal handler.
196 197 198 199
 *
 * On win32, the signal cancelling happens on a separate thread, because
 * that's how SetConsoleCtrlHandler works. The PQcancel function is safe
 * for this (unlike PQrequestCancel). However, a CRITICAL_SECTION is required
200
 * to protect the PGcancel structure against being changed while the signal
201
 * thread is using it.
202 203 204 205 206 207 208 209 210
 *
 * SIGINT is supposed to abort all long-running psql operations, not only
 * database queries.  In most places, this is accomplished by checking
 * cancel_pressed during long-running loops.  However, that won't work when
 * blocked on user input (in readline() or fgets()).  In those places, we
 * set sigint_interrupt_enabled TRUE while blocked, instructing the signal
 * catcher to longjmp through sigint_interrupt_jmp.  We assume readline and
 * fgets are coded to handle possible interruption.  (XXX currently this does
 * not work on win32, so control-C is less useful there)
211
 */
212 213 214 215
volatile bool sigint_interrupt_enabled = false;

sigjmp_buf	sigint_interrupt_jmp;

B
Bruce Momjian 已提交
216
static PGcancel *volatile cancelConn = NULL;
B
Bruce Momjian 已提交
217

218 219 220
#ifdef WIN32
static CRITICAL_SECTION cancelConnLock;
#endif
221

222
#define write_stderr(str)	write(fileno(stderr), str, strlen(str))
223

224

225
#ifndef WIN32
226

227
static void
228
handle_sigint(SIGNAL_ARGS)
229
{
230
	int			save_errno = errno;
B
Bruce Momjian 已提交
231
	char		errbuf[256];
232

233 234 235 236 237 238
	/* if we are waiting for input, longjmp out of it */
	if (sigint_interrupt_enabled)
	{
		sigint_interrupt_enabled = false;
		siglongjmp(sigint_interrupt_jmp, 1);
	}
239

240
	/* else, set cancel flag to stop any long-running loops */
241
	cancel_pressed = true;
242

243 244
	/* and send QueryCancel if we are processing a database query */
	if (cancelConn != NULL)
B
Bruce Momjian 已提交
245
	{
246 247 248 249 250 251 252
		if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
			write_stderr("Cancel request sent\n");
		else
		{
			write_stderr("Could not send cancel request: ");
			write_stderr(errbuf);
		}
B
Bruce Momjian 已提交
253
	}
254

255
	errno = save_errno;			/* just in case the write changed it */
256
}
257 258 259 260 261 262

void
setup_cancel_handler(void)
{
	pqsignal(SIGINT, handle_sigint);
}
B
Bruce Momjian 已提交
263
#else							/* WIN32 */
264 265 266 267

static BOOL WINAPI
consoleHandler(DWORD dwCtrlType)
{
B
Bruce Momjian 已提交
268
	char		errbuf[256];
269 270 271 272

	if (dwCtrlType == CTRL_C_EVENT ||
		dwCtrlType == CTRL_BREAK_EVENT)
	{
273 274 275 276 277 278
		/*
		 * Can't longjmp here, because we are in wrong thread :-(
		 */

		/* set cancel flag to stop any long-running loops */
		cancel_pressed = true;
279

280
		/* and send QueryCancel if we are processing a database query */
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
		EnterCriticalSection(&cancelConnLock);
		if (cancelConn != NULL)
		{
			if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
				write_stderr("Cancel request sent\n");
			else
			{
				write_stderr("Could not send cancel request: ");
				write_stderr(errbuf);
			}
		}
		LeaveCriticalSection(&cancelConnLock);

		return TRUE;
	}
	else
		/* Return FALSE for any signals not being handled */
		return FALSE;
}

301 302 303
void
setup_cancel_handler(void)
{
304
	InitializeCriticalSection(&cancelConnLock);
305

306
	SetConsoleCtrlHandler(consoleHandler, TRUE);
307
}
B
Bruce Momjian 已提交
308
#endif   /* WIN32 */
309

310 311 312 313 314

/* ConnectionUp
 *
 * Returns whether our backend connection is still there.
 */
B
Bruce Momjian 已提交
315
static bool
N
Neil Conway 已提交
316
ConnectionUp(void)
317 318 319 320 321 322 323 324 325 326 327 328
{
	return PQstatus(pset.db) != CONNECTION_BAD;
}



/* CheckConnection
 *
 * Verify that we still have a good connection to the backend, and if not,
 * see if it can be restored.
 *
 * Returns true if either the connection was still there, or it could be
B
Bruce Momjian 已提交
329
 * restored successfully; false otherwise.	If, however, there was no
330 331 332 333
 * connection and the session is non-interactive, this will exit the program
 * with a code of EXIT_BADCONN.
 */
static bool
334
CheckConnection(void)
335
{
B
Bruce Momjian 已提交
336 337
	bool		OK;

338 339 340 341 342 343 344 345 346
	OK = ConnectionUp();
	if (!OK)
	{
		if (!pset.cur_cmd_interactive)
		{
			psql_error("connection to server was lost\n");
			exit(EXIT_BADCONN);
		}

347
		fputs(_("The connection to the server was lost. Attempting reset: "), stderr);
348 349 350 351
		PQreset(pset.db);
		OK = ConnectionUp();
		if (!OK)
		{
352
			fputs(_("Failed.\n"), stderr);
353 354 355
			PQfinish(pset.db);
			pset.db = NULL;
			ResetCancelConn();
356
			UnsyncVariables();
357 358
		}
		else
359
			fputs(_("Succeeded.\n"), stderr);
360 361 362 363 364 365 366 367 368 369 370 371
	}

	return OK;
}



/*
 * SetCancelConn
 *
 * Set cancelConn to point to the current database connection.
 */
372
void
B
Bruce Momjian 已提交
373
SetCancelConn(void)
374
{
B
Bruce Momjian 已提交
375
	PGcancel   *oldCancelConn;
376

377 378 379 380 381
#ifdef WIN32
	EnterCriticalSection(&cancelConnLock);
#endif

	/* Free the old one if we have one */
382 383 384 385 386 387
	oldCancelConn = cancelConn;
	/* be sure handle_sigint doesn't use pointer while freeing */
	cancelConn = NULL;

	if (oldCancelConn != NULL)
		PQfreeCancel(oldCancelConn);
388 389 390 391 392 393

	cancelConn = PQgetCancel(pset.db);

#ifdef WIN32
	LeaveCriticalSection(&cancelConnLock);
#endif
394 395 396 397 398 399
}


/*
 * ResetCancelConn
 *
400
 * Free the current cancel connection, if any, and set to NULL.
401
 */
B
Bruce Momjian 已提交
402 403
void
ResetCancelConn(void)
404
{
B
Bruce Momjian 已提交
405
	PGcancel   *oldCancelConn;
406

407 408 409 410
#ifdef WIN32
	EnterCriticalSection(&cancelConnLock);
#endif

411 412
	oldCancelConn = cancelConn;
	/* be sure handle_sigint doesn't use pointer while freeing */
B
Bruce Momjian 已提交
413
	cancelConn = NULL;
414

415 416 417
	if (oldCancelConn != NULL)
		PQfreeCancel(oldCancelConn);

418 419 420
#ifdef WIN32
	LeaveCriticalSection(&cancelConnLock);
#endif
421 422 423 424 425 426 427
}


/*
 * AcceptResult
 *
 * Checks whether a result is valid, giving an error message if necessary;
428
 * and ensures that the connection to the backend is still up.
429 430 431 432
 *
 * Returns true for valid result, false for error state.
 */
static bool
433
AcceptResult(const PGresult *result)
434
{
B
Bruce Momjian 已提交
435
	bool		OK = true;
436 437

	if (!result)
B
Bruce Momjian 已提交
438 439 440 441 442 443
		OK = false;
	else
		switch (PQresultStatus(result))
		{
			case PGRES_COMMAND_OK:
			case PGRES_TUPLES_OK:
444
			case PGRES_EMPTY_QUERY:
B
Bruce Momjian 已提交
445 446
			case PGRES_COPY_IN:
			case PGRES_COPY_OUT:
447
				/* Fine, do nothing */
B
Bruce Momjian 已提交
448 449 450 451 452 453
				break;

			default:
				OK = false;
				break;
		}
454

B
Bruce Momjian 已提交
455
	if (!OK)
456
	{
457
		const char *error = PQerrorMessage(pset.db);
B
Bruce Momjian 已提交
458

459 460 461
		if (strlen(error))
			psql_error("%s", error);

462
		CheckConnection();
463 464 465 466 467 468 469
	}

	return OK;
}



B
Bruce Momjian 已提交
470 471
/*
 * PSQLexec
472 473
 *
 * This is the way to send "backdoor" queries (those not directly entered
474
 * by the user). It is subject to -E but not -e.
475
 *
476 477 478
 * In autocommit-off mode, a new transaction block is started if start_xact
 * is true; nothing special is done when start_xact is false.  Typically,
 * start_xact = false is used for SELECTs and explicit BEGIN/COMMIT commands.
479
 *
480 481 482
 * Caller is responsible for handling the ensuing processing if a COPY
 * command is sent.
 *
483 484
 * Note: we don't bother to check PQclientEncoding; it is assumed that no
 * caller uses this path to issue "SET CLIENT_ENCODING".
485
 */
486
PGresult *
487
PSQLexec(const char *query, bool start_xact)
488
{
489
	PGresult   *res;
B
Bruce Momjian 已提交
490

491
	if (!pset.db)
B
Bruce Momjian 已提交
492
	{
493
		psql_error("You are currently not connected to a database.\n");
B
Bruce Momjian 已提交
494 495 496
		return NULL;
	}

497
	if (pset.echo_hidden != PSQL_ECHO_HIDDEN_OFF)
B
Bruce Momjian 已提交
498
	{
499 500 501
		printf(_("********* QUERY **********\n"
				 "%s\n"
				 "**************************\n\n"), query);
B
Bruce Momjian 已提交
502
		fflush(stdout);
503 504
		if (pset.logfile)
		{
505 506 507 508
			fprintf(pset.logfile,
					_("********* QUERY **********\n"
					  "%s\n"
					  "**************************\n\n"), query);
509 510
			fflush(pset.logfile);
		}
B
Bruce Momjian 已提交
511

512
		if (pset.echo_hidden == PSQL_ECHO_HIDDEN_NOEXEC)
513
			return NULL;
514
	}
B
Bruce Momjian 已提交
515

516 517
	SetCancelConn();

518 519 520
	if (start_xact &&
		!pset.autocommit &&
		PQtransactionStatus(pset.db) == PQTRANS_IDLE)
521 522 523
	{
		res = PQexec(pset.db, "BEGIN");
		if (PQresultStatus(res) != PGRES_COMMAND_OK)
524
		{
525 526 527 528
			psql_error("%s", PQerrorMessage(pset.db));
			PQclear(res);
			ResetCancelConn();
			return NULL;
529
		}
530
		PQclear(res);
531
	}
532

533 534
	res = PQexec(pset.db, query);

535 536 537
	ResetCancelConn();

	if (!AcceptResult(res))
538
	{
539
		PQclear(res);
540
		res = NULL;
541
	}
542

543
	return res;
544 545 546 547 548
}



/*
549
 * PrintNotifications: check for asynchronous notifications, and print them out
550
 */
551 552
static void
PrintNotifications(void)
553
{
554
	PGnotify   *notify;
B
Bruce Momjian 已提交
555

556
	while ((notify = PQnotifies(pset.db)))
557
	{
558
		fprintf(pset.queryFout, _("Asynchronous notification \"%s\" received from server process with PID %d.\n"),
559 560
				notify->relname, notify->be_pid);
		fflush(pset.queryFout);
561
		PQfreemem(notify);
562
	}
563
}
564

B
Bruce Momjian 已提交
565

566 567 568 569 570
/*
 * PrintQueryTuples: assuming query result is OK, print its tuples
 *
 * Returns true if successful, false otherwise.
 */
B
Bruce Momjian 已提交
571
static bool
572 573
PrintQueryTuples(const PGresult *results)
{
574 575
	printQueryOpt my_popt = pset.popt;

B
Bruce Momjian 已提交
576 577 578
	/* write output to \g argument, if any */
	if (pset.gfname)
	{
579
		/* keep this code in sync with ExecQueryUsingCursor */
B
Bruce Momjian 已提交
580 581
		FILE	   *queryFout_copy = pset.queryFout;
		bool		queryFoutPipe_copy = pset.queryFoutPipe;
582

B
Bruce Momjian 已提交
583
		pset.queryFout = stdout;	/* so it doesn't get closed */
584

B
Bruce Momjian 已提交
585 586 587 588 589
		/* open file/pipe */
		if (!setQFout(pset.gfname))
		{
			pset.queryFout = queryFout_copy;
			pset.queryFoutPipe = queryFoutPipe_copy;
590
			return false;
B
Bruce Momjian 已提交
591
		}
592

593
		printQuery(results, &my_popt, pset.queryFout, pset.logfile);
594

B
Bruce Momjian 已提交
595 596
		/* close file/pipe, restore old setting */
		setQFout(NULL);
597

B
Bruce Momjian 已提交
598 599
		pset.queryFout = queryFout_copy;
		pset.queryFoutPipe = queryFoutPipe_copy;
600

B
Bruce Momjian 已提交
601 602 603 604
		free(pset.gfname);
		pset.gfname = NULL;
	}
	else
605
		printQuery(results, &my_popt, pset.queryFout, pset.logfile);
606 607 608 609 610 611

	return true;
}


/*
612
 * ProcessCopyResult: if command was a COPY FROM STDIN/TO STDOUT, handle it
613 614 615 616 617 618
 *
 * Note: Utility function for use by SendQuery() only.
 *
 * Returns true if the query executed successfully, false otherwise.
 */
static bool
619
ProcessCopyResult(PGresult *results)
620
{
B
Bruce Momjian 已提交
621
	bool		success = false;
622

B
Bruce Momjian 已提交
623 624
	if (!results)
		return false;
625 626 627 628

	switch (PQresultStatus(results))
	{
		case PGRES_TUPLES_OK:
629
		case PGRES_COMMAND_OK:
B
Bruce Momjian 已提交
630
		case PGRES_EMPTY_QUERY:
631
			/* nothing to do here */
B
Bruce Momjian 已提交
632 633
			success = true;
			break;
634 635

		case PGRES_COPY_OUT:
636
			SetCancelConn();
637
			success = handleCopyOut(pset.db, pset.queryFout);
638
			ResetCancelConn();
639 640 641
			break;

		case PGRES_COPY_IN:
642
			SetCancelConn();
643 644
			success = handleCopyIn(pset.db, pset.cur_cmd_source,
								   PQbinaryTuples(results));
645
			ResetCancelConn();
646 647 648 649 650 651 652 653 654 655 656 657 658 659
			break;

		default:
			break;
	}

	/* may need this to recover from conn loss during COPY */
	if (!CheckConnection())
		return false;

	return success;
}


660 661 662 663 664 665 666 667 668 669
/*
 * PrintQueryStatus: report command status as required
 *
 * Note: Utility function for use by PrintQueryResults() only.
 */
static void
PrintQueryStatus(PGresult *results)
{
	char		buf[16];

670
	if (!pset.quiet)
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
	{
		if (pset.popt.topt.format == PRINT_HTML)
		{
			fputs("<p>", pset.queryFout);
			html_escaped_print(PQcmdStatus(results), pset.queryFout);
			fputs("</p>\n", pset.queryFout);
		}
		else
			fprintf(pset.queryFout, "%s\n", PQcmdStatus(results));
	}

	if (pset.logfile)
		fprintf(pset.logfile, "%s\n", PQcmdStatus(results));

	snprintf(buf, sizeof(buf), "%u", (unsigned int) PQoidValue(results));
	SetVariable(pset.vars, "LASTOID", buf);
}


690 691 692 693 694 695 696 697 698 699 700
/*
 * PrintQueryResults: print out query results as required
 *
 * Note: Utility function for use by SendQuery() only.
 *
 * Returns true if the query executed successfully, false otherwise.
 */
static bool
PrintQueryResults(PGresult *results)
{
	bool		success = false;
701
	const char *cmdstatus;
702 703 704 705 706 707 708

	if (!results)
		return false;

	switch (PQresultStatus(results))
	{
		case PGRES_TUPLES_OK:
709
			/* print the data ... */
710
			success = PrintQueryTuples(results);
711 712 713 714 715 716
			/* if it's INSERT/UPDATE/DELETE RETURNING, also print status */
			cmdstatus = PQcmdStatus(results);
			if (strncmp(cmdstatus, "INSERT", 6) == 0 ||
				strncmp(cmdstatus, "UPDATE", 6) == 0 ||
				strncmp(cmdstatus, "DELETE", 6) == 0)
				PrintQueryStatus(results);
717 718
			break;

B
Bruce Momjian 已提交
719
		case PGRES_COMMAND_OK:
720 721 722
			PrintQueryStatus(results);
			success = true;
			break;
723 724 725

		case PGRES_EMPTY_QUERY:
			success = true;
B
Bruce Momjian 已提交
726
			break;
727

728
		case PGRES_COPY_OUT:
B
Bruce Momjian 已提交
729
		case PGRES_COPY_IN:
730 731
			/* nothing to do here */
			success = true;
B
Bruce Momjian 已提交
732
			break;
733

734
		default:
B
Bruce Momjian 已提交
735 736
			break;
	}
737

B
Bruce Momjian 已提交
738
	fflush(pset.queryFout);
739

740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
	return success;
}


/*
 * SendQuery: send the query string to the backend
 * (and print out results)
 *
 * Note: This is the "front door" way to send a query. That is, use it to
 * send queries actually entered by the user. These queries will be subject to
 * single step mode.
 * To send "back door" queries (generated by slash commands, etc.) in a
 * controlled way, use PSQLexec().
 *
 * Returns true if the query executed successfully, false otherwise.
 */
bool
SendQuery(const char *query)
{
B
Bruce Momjian 已提交
759
	PGresult   *results;
760 761
	PGTransactionStatusType transaction_status;
	double		elapsed_msec = 0;
B
Bruce Momjian 已提交
762 763 764 765
	bool		OK,
				on_error_rollback_savepoint = false;
	static bool on_error_rollback_warning = false;

766
	if (!pset.db)
767
	{
768 769
		psql_error("You are currently not connected to a database.\n");
		return false;
770
	}
771

772
	if (pset.singlestep)
773
	{
774 775
		char		buf[3];

776
		printf(_("***(Single step mode: verify command)*******************************************\n"
B
Bruce Momjian 已提交
777 778
				 "%s\n"
				 "***(press return to proceed or enter x and return to cancel)********************\n"),
779 780 781 782
			   query);
		fflush(stdout);
		if (fgets(buf, sizeof(buf), stdin) != NULL)
			if (buf[0] == 'x')
783 784
				return false;
	}
785
	else if (pset.echo == PSQL_ECHO_QUERIES)
786
	{
787
		puts(query);
788 789
		fflush(stdout);
	}
B
Bruce Momjian 已提交
790

791 792
	if (pset.logfile)
	{
793 794 795 796
		fprintf(pset.logfile,
				_("********* QUERY **********\n"
				  "%s\n"
				  "**************************\n\n"), query);
797 798 799
		fflush(pset.logfile);
	}

800
	SetCancelConn();
801

802 803 804
	transaction_status = PQtransactionStatus(pset.db);

	if (transaction_status == PQTRANS_IDLE &&
805
		!pset.autocommit &&
806
		!command_no_begin(query))
807 808 809 810 811 812 813 814 815 816
	{
		results = PQexec(pset.db, "BEGIN");
		if (PQresultStatus(results) != PGRES_COMMAND_OK)
		{
			psql_error("%s", PQerrorMessage(pset.db));
			PQclear(results);
			ResetCancelConn();
			return false;
		}
		PQclear(results);
817
		transaction_status = PQtransactionStatus(pset.db);
818
	}
819 820

	if (transaction_status == PQTRANS_INTRANS &&
821
		pset.on_error_rollback != PSQL_ERROR_ROLLBACK_OFF &&
822
		(pset.cur_cmd_interactive ||
823
		 pset.on_error_rollback == PSQL_ERROR_ROLLBACK_ON))
824 825 826 827
	{
		if (on_error_rollback_warning == false && pset.sversion < 80000)
		{
			fprintf(stderr, _("The server version (%d) does not support savepoints for ON_ERROR_ROLLBACK.\n"),
828
					pset.sversion);
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
			on_error_rollback_warning = true;
		}
		else
		{
			results = PQexec(pset.db, "SAVEPOINT pg_psql_temporary_savepoint");
			if (PQresultStatus(results) != PGRES_COMMAND_OK)
			{
				psql_error("%s", PQerrorMessage(pset.db));
				PQclear(results);
				ResetCancelConn();
				return false;
			}
			PQclear(results);
			on_error_rollback_savepoint = true;
		}
	}
845

846 847 848
	if (pset.fetch_count <= 0 || !is_select_command(query))
	{
		/* Default fetch-it-all-and-print mode */
849
		instr_time	before,
B
Bruce Momjian 已提交
850
					after;
851

852
		if (pset.timing)
853
			INSTR_TIME_SET_CURRENT(before);
854

855
		results = PQexec(pset.db, query);
856

857 858 859
		/* these operations are included in the timing result: */
		ResetCancelConn();
		OK = (AcceptResult(results) && ProcessCopyResult(results));
860

861 862
		if (pset.timing)
		{
863 864 865
			INSTR_TIME_SET_CURRENT(after);
			INSTR_TIME_SUBTRACT(after, before);
			elapsed_msec = INSTR_TIME_GET_MILLISEC(after);
866 867 868 869 870 871 872 873 874 875 876 877 878
		}

		/* but printing results isn't: */
		if (OK)
			OK = PrintQueryResults(results);
	}
	else
	{
		/* Fetch-in-segments mode */
		OK = ExecQueryUsingCursor(query, &elapsed_msec);
		ResetCancelConn();
		results = NULL;			/* PQclear(NULL) does nothing */
	}
879

880 881 882
	/* If we made a temporary savepoint, possibly release/rollback */
	if (on_error_rollback_savepoint)
	{
883 884
		PGresult   *svptres;

885 886 887 888
		transaction_status = PQtransactionStatus(pset.db);

		/* We always rollback on an error */
		if (transaction_status == PQTRANS_INERROR)
889
			svptres = PQexec(pset.db, "ROLLBACK TO pg_psql_temporary_savepoint");
890 891
		/* If they are no longer in a transaction, then do nothing */
		else if (transaction_status != PQTRANS_INTRANS)
892
			svptres = NULL;
893 894
		else
		{
B
Bruce Momjian 已提交
895 896 897 898
			/*
			 * Do nothing if they are messing with savepoints themselves: If
			 * the user did RELEASE or ROLLBACK, our savepoint is gone. If
			 * they issued a SAVEPOINT, releasing ours would remove theirs.
899
			 */
900 901 902 903
			if (results &&
				(strcmp(PQcmdStatus(results), "SAVEPOINT") == 0 ||
				 strcmp(PQcmdStatus(results), "RELEASE") == 0 ||
				 strcmp(PQcmdStatus(results), "ROLLBACK") == 0))
904
				svptres = NULL;
905
			else
906
				svptres = PQexec(pset.db, "RELEASE pg_psql_temporary_savepoint");
907
		}
908
		if (svptres && PQresultStatus(svptres) != PGRES_COMMAND_OK)
909 910 911
		{
			psql_error("%s", PQerrorMessage(pset.db));
			PQclear(results);
912
			PQclear(svptres);
913 914 915
			ResetCancelConn();
			return false;
		}
916 917

		PQclear(svptres);
918 919
	}

920 921
	PQclear(results);

922
	/* Possible microtiming output */
923
	if (OK && pset.timing && !pset.quiet)
924
		printf(_("Time: %.3f ms\n"), elapsed_msec);
925

926 927 928 929 930 931 932 933 934 935 936 937
	/* check for events that may occur during query execution */

	if (pset.encoding != PQclientEncoding(pset.db) &&
		PQclientEncoding(pset.db) >= 0)
	{
		/* track effects of SET CLIENT_ENCODING */
		pset.encoding = PQclientEncoding(pset.db);
		pset.popt.topt.encoding = pset.encoding;
		SetVariable(pset.vars, "ENCODING",
					pg_encoding_to_char(pset.encoding));
	}

938
	PrintNotifications();
939

940
	return OK;
941
}
942

943

944 945 946 947 948 949 950 951 952 953 954 955 956 957
/*
 * ExecQueryUsingCursor: run a SELECT-like query using a cursor
 *
 * This feature allows result sets larger than RAM to be dealt with.
 *
 * Returns true if the query executed successfully, false otherwise.
 *
 * If pset.timing is on, total query time (exclusive of result-printing) is
 * stored into *elapsed_msec.
 */
static bool
ExecQueryUsingCursor(const char *query, double *elapsed_msec)
{
	bool		OK = true;
B
Bruce Momjian 已提交
958 959
	PGresult   *results;
	PQExpBufferData buf;
960 961 962
	printQueryOpt my_popt = pset.popt;
	FILE	   *queryFout_copy = pset.queryFout;
	bool		queryFoutPipe_copy = pset.queryFoutPipe;
B
Bruce Momjian 已提交
963 964 965 966
	bool		started_txn = false;
	bool		did_pager = false;
	int			ntuples;
	char		fetch_cmd[64];
967
	instr_time	before,
B
Bruce Momjian 已提交
968
				after;
969 970 971 972 973 974 975 976 977

	*elapsed_msec = 0;

	/* initialize print options for partial table output */
	my_popt.topt.start_table = true;
	my_popt.topt.stop_table = false;
	my_popt.topt.prior_records = 0;

	if (pset.timing)
978
		INSTR_TIME_SET_CURRENT(before);
979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006

	/* if we're not in a transaction, start one */
	if (PQtransactionStatus(pset.db) == PQTRANS_IDLE)
	{
		results = PQexec(pset.db, "BEGIN");
		OK = AcceptResult(results) &&
			(PQresultStatus(results) == PGRES_COMMAND_OK);
		PQclear(results);
		if (!OK)
			return false;
		started_txn = true;
	}

	/* Send DECLARE CURSOR */
	initPQExpBuffer(&buf);
	appendPQExpBuffer(&buf, "DECLARE _psql_cursor NO SCROLL CURSOR FOR\n%s",
					  query);

	results = PQexec(pset.db, buf.data);
	OK = AcceptResult(results) &&
		(PQresultStatus(results) == PGRES_COMMAND_OK);
	PQclear(results);
	termPQExpBuffer(&buf);
	if (!OK)
		goto cleanup;

	if (pset.timing)
	{
1007 1008 1009
		INSTR_TIME_SET_CURRENT(after);
		INSTR_TIME_SUBTRACT(after, before);
		*elapsed_msec += INSTR_TIME_GET_MILLISEC(after);
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
	}

	snprintf(fetch_cmd, sizeof(fetch_cmd),
			 "FETCH FORWARD %d FROM _psql_cursor",
			 pset.fetch_count);

	/* prepare to write output to \g argument, if any */
	if (pset.gfname)
	{
		/* keep this code in sync with PrintQueryTuples */
B
Bruce Momjian 已提交
1020
		pset.queryFout = stdout;	/* so it doesn't get closed */
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034

		/* open file/pipe */
		if (!setQFout(pset.gfname))
		{
			pset.queryFout = queryFout_copy;
			pset.queryFoutPipe = queryFoutPipe_copy;
			OK = false;
			goto cleanup;
		}
	}

	for (;;)
	{
		if (pset.timing)
1035
			INSTR_TIME_SET_CURRENT(before);
1036 1037 1038 1039 1040 1041

		/* get FETCH_COUNT tuples at a time */
		results = PQexec(pset.db, fetch_cmd);

		if (pset.timing)
		{
1042 1043 1044
			INSTR_TIME_SET_CURRENT(after);
			INSTR_TIME_SUBTRACT(after, before);
			*elapsed_msec += INSTR_TIME_GET_MILLISEC(after);
1045 1046
		}

1047
		if (PQresultStatus(results) != PGRES_TUPLES_OK)
1048
		{
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
			/* shut down pager before printing error message */
			if (did_pager)
			{
				ClosePager(pset.queryFout);
				pset.queryFout = queryFout_copy;
				pset.queryFoutPipe = queryFoutPipe_copy;
				did_pager = false;
			}

			OK = AcceptResult(results);
			psql_assert(!OK);
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
			PQclear(results);
			break;
		}

		ntuples = PQntuples(results);

		if (ntuples < pset.fetch_count)
		{
			/* this is the last result set, so allow footer decoration */
			my_popt.topt.stop_table = true;
		}
		else if (pset.queryFout == stdout && !did_pager)
		{
			/*
			 * If query requires multiple result sets, hack to ensure that
			 * only one pager instance is used for the whole mess
			 */
			pset.queryFout = PageOutput(100000, my_popt.topt.pager);
			did_pager = true;
		}

		printQuery(results, &my_popt, pset.queryFout, pset.logfile);

1083
		/*
B
Bruce Momjian 已提交
1084 1085
		 * Make sure to flush the output stream, so intermediate results are
		 * visible to the client immediately.
1086 1087 1088
		 */
		fflush(pset.queryFout);

1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
		/* after the first result set, disallow header decoration */
		my_popt.topt.start_table = false;
		my_popt.topt.prior_records += ntuples;

		PQclear(results);

		if (ntuples < pset.fetch_count || cancel_pressed)
			break;
	}

	/* close \g argument file/pipe, restore old setting */
	if (pset.gfname)
	{
		/* keep this code in sync with PrintQueryTuples */
		setQFout(NULL);

		pset.queryFout = queryFout_copy;
		pset.queryFoutPipe = queryFoutPipe_copy;

		free(pset.gfname);
		pset.gfname = NULL;
	}
	else if (did_pager)
	{
		ClosePager(pset.queryFout);
		pset.queryFout = queryFout_copy;
		pset.queryFoutPipe = queryFoutPipe_copy;
	}

cleanup:
	if (pset.timing)
1120
		INSTR_TIME_SET_CURRENT(before);
1121 1122

	/*
B
Bruce Momjian 已提交
1123 1124 1125
	 * We try to close the cursor on either success or failure, but on failure
	 * ignore the result (it's probably just a bleat about being in an aborted
	 * transaction)
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
	 */
	results = PQexec(pset.db, "CLOSE _psql_cursor");
	if (OK)
	{
		OK = AcceptResult(results) &&
			(PQresultStatus(results) == PGRES_COMMAND_OK);
	}
	PQclear(results);

	if (started_txn)
	{
		results = PQexec(pset.db, OK ? "COMMIT" : "ROLLBACK");
		OK &= AcceptResult(results) &&
			(PQresultStatus(results) == PGRES_COMMAND_OK);
		PQclear(results);
	}

	if (pset.timing)
	{
1145 1146 1147
		INSTR_TIME_SET_CURRENT(after);
		INSTR_TIME_SUBTRACT(after, before);
		*elapsed_msec += INSTR_TIME_GET_MILLISEC(after);
1148 1149 1150 1151 1152 1153
	}

	return OK;
}


1154
/*
1155
 * Advance the given char pointer over white space and SQL comments.
1156
 */
1157 1158
static const char *
skip_white_space(const char *query)
1159
{
B
Bruce Momjian 已提交
1160
	int			cnestlevel = 0; /* slash-star comment nest level */
1161 1162 1163

	while (*query)
	{
B
Bruce Momjian 已提交
1164
		int			mblen = PQmblen(query, pset.encoding);
1165 1166

		/*
B
Bruce Momjian 已提交
1167 1168 1169 1170 1171 1172
		 * Note: we assume the encoding is a superset of ASCII, so that for
		 * example "query[0] == '/'" is meaningful.  However, we do NOT assume
		 * that the second and subsequent bytes of a multibyte character
		 * couldn't look like ASCII characters; so it is critical to advance
		 * by mblen, not 1, whenever we haven't exactly identified the
		 * character we are skipping over.
1173
		 */
1174
		if (isspace((unsigned char) *query))
1175 1176
			query += mblen;
		else if (query[0] == '/' && query[1] == '*')
1177
		{
1178
			cnestlevel++;
1179 1180
			query += 2;
		}
1181 1182 1183 1184 1185 1186
		else if (cnestlevel > 0 && query[0] == '*' && query[1] == '/')
		{
			cnestlevel--;
			query += 2;
		}
		else if (cnestlevel == 0 && query[0] == '-' && query[1] == '-')
1187 1188
		{
			query += 2;
B
Bruce Momjian 已提交
1189

1190
			/*
B
Bruce Momjian 已提交
1191 1192
			 * We have to skip to end of line since any slash-star inside the
			 * -- comment does NOT start a slash-star comment.
1193
			 */
1194 1195
			while (*query)
			{
1196
				if (*query == '\n')
1197
				{
1198
					query++;
1199 1200
					break;
				}
1201
				query += PQmblen(query, pset.encoding);
1202 1203
			}
		}
1204 1205
		else if (cnestlevel > 0)
			query += mblen;
1206 1207 1208 1209
		else
			break;				/* found first token */
	}

1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225
	return query;
}


/*
 * Check whether a command is one of those for which we should NOT start
 * a new transaction block (ie, send a preceding BEGIN).
 *
 * These include the transaction control statements themselves, plus
 * certain statements that the backend disallows inside transaction blocks.
 */
static bool
command_no_begin(const char *query)
{
	int			wordlen;

1226
	/*
1227 1228 1229 1230 1231 1232
	 * First we must advance over any whitespace and comments.
	 */
	query = skip_white_space(query);

	/*
	 * Check word length (since "beginx" is not "begin").
1233 1234 1235
	 */
	wordlen = 0;
	while (isalpha((unsigned char) query[wordlen]))
1236
		wordlen += PQmblen(&query[wordlen], pset.encoding);
1237

1238
	/*
B
Bruce Momjian 已提交
1239 1240 1241
	 * Transaction control commands.  These should include every keyword that
	 * gives rise to a TransactionStmt in the backend grammar, except for the
	 * savepoint-related commands.
1242
	 *
1243 1244
	 * (We assume that START must be START TRANSACTION, since there is
	 * presently no other "START foo" command.)
1245 1246 1247
	 */
	if (wordlen == 5 && pg_strncasecmp(query, "abort", 5) == 0)
		return true;
1248
	if (wordlen == 5 && pg_strncasecmp(query, "begin", 5) == 0)
1249
		return true;
1250 1251
	if (wordlen == 5 && pg_strncasecmp(query, "start", 5) == 0)
		return true;
1252
	if (wordlen == 6 && pg_strncasecmp(query, "commit", 6) == 0)
1253
		return true;
1254
	if (wordlen == 3 && pg_strncasecmp(query, "end", 3) == 0)
1255
		return true;
1256
	if (wordlen == 8 && pg_strncasecmp(query, "rollback", 8) == 0)
1257
		return true;
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272
	if (wordlen == 7 && pg_strncasecmp(query, "prepare", 7) == 0)
	{
		/* PREPARE TRANSACTION is a TC command, PREPARE foo is not */
		query += wordlen;

		query = skip_white_space(query);

		wordlen = 0;
		while (isalpha((unsigned char) query[wordlen]))
			wordlen += PQmblen(&query[wordlen], pset.encoding);

		if (wordlen == 11 && pg_strncasecmp(query, "transaction", 11) == 0)
			return true;
		return false;
	}
1273 1274

	/*
B
Bruce Momjian 已提交
1275 1276 1277
	 * Commands not allowed within transactions.  The statements checked for
	 * here should be exactly those that call PreventTransactionChain() in the
	 * backend.
1278 1279
	 */
	if (wordlen == 6 && pg_strncasecmp(query, "vacuum", 6) == 0)
1280
		return true;
1281
	if (wordlen == 7 && pg_strncasecmp(query, "cluster", 7) == 0)
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335
	{
		/* CLUSTER with any arguments is allowed in transactions */
		query += wordlen;

		query = skip_white_space(query);

		if (isalpha((unsigned char) query[0]))
			return false;		/* has additional words */
		return true;			/* it's CLUSTER without arguments */
	}

	if (wordlen == 6 && pg_strncasecmp(query, "create", 6) == 0)
	{
		query += wordlen;

		query = skip_white_space(query);

		wordlen = 0;
		while (isalpha((unsigned char) query[wordlen]))
			wordlen += PQmblen(&query[wordlen], pset.encoding);

		if (wordlen == 8 && pg_strncasecmp(query, "database", 8) == 0)
			return true;
		if (wordlen == 10 && pg_strncasecmp(query, "tablespace", 10) == 0)
			return true;

		/* CREATE [UNIQUE] INDEX CONCURRENTLY isn't allowed in xacts */
		if (wordlen == 6 && pg_strncasecmp(query, "unique", 6) == 0)
		{
			query += wordlen;

			query = skip_white_space(query);

			wordlen = 0;
			while (isalpha((unsigned char) query[wordlen]))
				wordlen += PQmblen(&query[wordlen], pset.encoding);
		}

		if (wordlen == 5 && pg_strncasecmp(query, "index", 5) == 0)
		{
			query += wordlen;

			query = skip_white_space(query);

			wordlen = 0;
			while (isalpha((unsigned char) query[wordlen]))
				wordlen += PQmblen(&query[wordlen], pset.encoding);

			if (wordlen == 12 && pg_strncasecmp(query, "concurrently", 12) == 0)
				return true;
		}

		return false;
	}
1336

1337
	/*
B
Bruce Momjian 已提交
1338 1339 1340
	 * Note: these tests will match DROP SYSTEM and REINDEX TABLESPACE, which
	 * aren't really valid commands so we don't care much. The other four
	 * possible matches are correct.
1341
	 */
1342
	if ((wordlen == 4 && pg_strncasecmp(query, "drop", 4) == 0) ||
1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354
		(wordlen == 7 && pg_strncasecmp(query, "reindex", 7) == 0))
	{
		query += wordlen;

		query = skip_white_space(query);

		wordlen = 0;
		while (isalpha((unsigned char) query[wordlen]))
			wordlen += PQmblen(&query[wordlen], pset.encoding);

		if (wordlen == 8 && pg_strncasecmp(query, "database", 8) == 0)
			return true;
1355 1356
		if (wordlen == 6 && pg_strncasecmp(query, "system", 6) == 0)
			return true;
1357 1358 1359 1360
		if (wordlen == 10 && pg_strncasecmp(query, "tablespace", 10) == 0)
			return true;
	}

1361 1362 1363
	return false;
}

1364

1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393
/*
 * Check whether the specified command is a SELECT (or VALUES).
 */
static bool
is_select_command(const char *query)
{
	int			wordlen;

	/*
	 * First advance over any whitespace, comments and left parentheses.
	 */
	for (;;)
	{
		query = skip_white_space(query);
		if (query[0] == '(')
			query++;
		else
			break;
	}

	/*
	 * Check word length (since "selectx" is not "select").
	 */
	wordlen = 0;
	while (isalpha((unsigned char) query[wordlen]))
		wordlen += PQmblen(&query[wordlen], pset.encoding);

	if (wordlen == 6 && pg_strncasecmp(query, "select", 6) == 0)
		return true;
B
Bruce Momjian 已提交
1394

1395 1396 1397 1398 1399 1400 1401
	if (wordlen == 6 && pg_strncasecmp(query, "values", 6) == 0)
		return true;

	return false;
}


1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422
/*
 * Test if the current user is a database superuser.
 *
 * Note: this will correctly detect superuserness only with a protocol-3.0
 * or newer backend; otherwise it will always say "false".
 */
bool
is_superuser(void)
{
	const char *val;

	if (!pset.db)
		return false;

	val = PQparameterStatus(pset.db, "is_superuser");

	if (val && strcmp(val, "on") == 0)
		return true;

	return false;
}
1423 1424


1425 1426 1427
/*
 * Test if the current session uses standard string literals.
 *
1428 1429
 * Note: With a pre-protocol-3.0 connection this will always say "false",
 * which should be the right answer.
1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447
 */
bool
standard_strings(void)
{
	const char *val;

	if (!pset.db)
		return false;

	val = PQparameterStatus(pset.db, "standard_conforming_strings");

	if (val && strcmp(val, "on") == 0)
		return true;

	return false;
}


1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468
/*
 * Return the session user of the current connection.
 *
 * Note: this will correctly detect the session user only with a
 * protocol-3.0 or newer backend; otherwise it will return the
 * connection user.
 */
const char *
session_username(void)
{
	const char *val;

	if (!pset.db)
		return NULL;

	val = PQparameterStatus(pset.db, "session_authorization");
	if (val)
		return val;
	else
		return PQuser(pset.db);
}
1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481


/* expand_tilde
 *
 * substitute '~' with HOME or '~username' with username's home dir
 *
 */
char *
expand_tilde(char **filename)
{
	if (!filename || !(*filename))
		return NULL;

B
Bruce Momjian 已提交
1482
	/*
B
Bruce Momjian 已提交
1483 1484 1485
	 * WIN32 doesn't use tilde expansion for file names. Also, it uses tilde
	 * for short versions of long file names, though the tilde is usually
	 * toward the end, not at the beginning.
B
Bruce Momjian 已提交
1486
	 */
1487 1488 1489 1490 1491 1492 1493 1494 1495
#ifndef WIN32

	/* try tilde expansion */
	if (**filename == '~')
	{
		char	   *fn;
		char		oldp,
				   *p;
		struct passwd *pw;
1496
		char		home[MAXPGPATH];
1497 1498

		fn = *filename;
1499
		*home = '\0';
1500 1501 1502 1503 1504 1505 1506 1507 1508

		p = fn + 1;
		while (*p != '/' && *p != '\0')
			p++;

		oldp = *p;
		*p = '\0';

		if (*(fn + 1) == '\0')
B
Bruce Momjian 已提交
1509
			get_home_path(home);	/* ~ or ~/ only */
1510
		else if ((pw = getpwnam(fn + 1)) != NULL)
B
Bruce Momjian 已提交
1511
			strlcpy(home, pw->pw_dir, sizeof(home));	/* ~user */
1512 1513

		*p = oldp;
1514
		if (strlen(home) != 0)
1515 1516 1517
		{
			char	   *newfn;

1518
			newfn = pg_malloc(strlen(home) + strlen(p) + 1);
1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529
			strcpy(newfn, home);
			strcat(newfn, p);

			free(fn);
			*filename = newfn;
		}
	}
#endif

	return *filename;
}