common.c 26.6 KB
Newer Older
Peter Eisentraut's avatar
Peter Eisentraut committed
1 2 3
/*
 * psql - the PostgreSQL interactive terminal
 *
4
 * Copyright (c) 2000-2005, PostgreSQL Global Development Group
Peter Eisentraut's avatar
Peter Eisentraut committed
5
 *
6
 * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.95 2005/01/01 05:43:08 momjian Exp $
Peter Eisentraut's avatar
Peter Eisentraut committed
7
 */
8
#include "postgres_fe.h"
9 10
#include "common.h"

11
#include <ctype.h>
12 13 14 15 16
#ifndef HAVE_STRDUP
#include <strdup.h>
#endif
#include <signal.h>
#ifndef WIN32
17
#include <sys/time.h>
Bruce Momjian's avatar
Bruce Momjian committed
18
#include <unistd.h>				/* for write() */
19
#include <setjmp.h>
20
#else
21
#include <io.h>					/* for _write() */
22
#include <win32.h>
23
#include <sys/timeb.h>			/* for _ftime() */
24 25
#endif

26 27
#include "libpq-fe.h"
#include "pqsignal.h"
28 29 30

#include "settings.h"
#include "variables.h"
31
#include "command.h"
32 33 34
#include "copy.h"
#include "prompt.h"
#include "print.h"
35
#include "mainloop.h"
36
#include "mb/pg_wchar.h"
37

38 39 40 41 42 43 44 45

/* Workarounds for Windows */
/* Probably to be moved up the source tree in the future, perhaps to be replaced by
 * more specific checks like configure-style HAVE_GETTIMEOFDAY macros.
 */
#ifndef WIN32

typedef struct timeval TimevalStruct;
Bruce Momjian's avatar
Bruce Momjian committed
46

47
#define GETTIMEOFDAY(T) gettimeofday(T, NULL)
48 49 50
#define DIFF_MSEC(T, U) \
	((((int) ((T)->tv_sec - (U)->tv_sec)) * 1000000.0 + \
	  ((int) ((T)->tv_usec - (U)->tv_usec))) / 1000.0)
51 52 53 54

#else

typedef struct _timeb TimevalStruct;
Bruce Momjian's avatar
Bruce Momjian committed
55

56
#define GETTIMEOFDAY(T) _ftime(T)
57 58 59
#define DIFF_MSEC(T, U) \
	(((T)->time - (U)->time) * 1000.0 + \
	 ((T)->millitm - (U)->millitm))
60 61
#endif

62
extern bool prompt_state;
63

64

65
static bool command_no_begin(const char *query);
66 67


68
/*
69 70
 * "Safe" wrapper around strdup()
 */
Bruce Momjian's avatar
Bruce Momjian committed
71
char *
72
pg_strdup(const char *string)
73
{
Bruce Momjian's avatar
Bruce Momjian committed
74 75 76 77
	char	   *tmp;

	if (!string)
	{
78
		fprintf(stderr, gettext("%s: xstrdup: cannot duplicate null pointer (internal error)\n"),
79
				pset.progname);
Bruce Momjian's avatar
Bruce Momjian committed
80 81 82 83 84
		exit(EXIT_FAILURE);
	}
	tmp = strdup(string);
	if (!tmp)
	{
85
		psql_error("out of memory\n");
Bruce Momjian's avatar
Bruce Momjian committed
86 87 88
		exit(EXIT_FAILURE);
	}
	return tmp;
89 90
}

91
void *
92
pg_malloc(size_t size)
93 94 95 96 97 98 99 100 101 102 103 104 105
{
	void	   *tmp;

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

void *
106
pg_malloc_zero(size_t size)
107 108 109
{
	void	   *tmp;

110
	tmp = pg_malloc(size);
111 112 113 114 115
	memset(tmp, 0, size);
	return tmp;
}

void *
116
pg_calloc(size_t nmemb, size_t size)
117 118 119 120 121 122 123 124 125 126 127
{
	void	   *tmp;

	tmp = calloc(nmemb, size);
	if (!tmp)
	{
		psql_error("out of memory");
		exit(EXIT_FAILURE);
	}
	return tmp;
}
128 129 130 131 132 133 134 135 136 137

/*
 * 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
138
setQFout(const char *fname)
139
{
Bruce Momjian's avatar
Bruce Momjian committed
140
	bool		status = true;
141

Bruce Momjian's avatar
Bruce Momjian committed
142
	/* Close old file/pipe */
143
	if (pset.queryFout && pset.queryFout != stdout && pset.queryFout != stderr)
Bruce Momjian's avatar
Bruce Momjian committed
144
	{
145 146
		if (pset.queryFoutPipe)
			pclose(pset.queryFout);
Bruce Momjian's avatar
Bruce Momjian committed
147
		else
148
			fclose(pset.queryFout);
Bruce Momjian's avatar
Bruce Momjian committed
149
	}
150

Bruce Momjian's avatar
Bruce Momjian committed
151 152 153
	/* If no filename, set stdout */
	if (!fname || fname[0] == '\0')
	{
154 155
		pset.queryFout = stdout;
		pset.queryFoutPipe = false;
Bruce Momjian's avatar
Bruce Momjian committed
156 157 158
	}
	else if (*fname == '|')
	{
159 160
		pset.queryFout = popen(fname + 1, "w");
		pset.queryFoutPipe = true;
Bruce Momjian's avatar
Bruce Momjian committed
161 162 163
	}
	else
	{
164 165
		pset.queryFout = fopen(fname, "w");
		pset.queryFoutPipe = false;
Bruce Momjian's avatar
Bruce Momjian committed
166 167
	}

168
	if (!(pset.queryFout))
Bruce Momjian's avatar
Bruce Momjian committed
169
	{
Peter Eisentraut's avatar
Peter Eisentraut committed
170
		psql_error("%s: %s\n", fname, strerror(errno));
171 172
		pset.queryFout = stdout;
		pset.queryFoutPipe = false;
Bruce Momjian's avatar
Bruce Momjian committed
173 174 175 176
		status = false;
	}

	/* Direct signals */
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
177
#ifndef WIN32
178
	pqsignal(SIGPIPE, pset.queryFoutPipe ? SIG_IGN : SIG_DFL);
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
179
#endif
Bruce Momjian's avatar
Bruce Momjian committed
180 181

	return status;
182 183 184 185
}



Peter Eisentraut's avatar
Peter Eisentraut committed
186 187
/*
 * Error reporting for scripts. Errors should look like
188
 *	 psql:filename:lineno: message
Peter Eisentraut's avatar
Peter Eisentraut committed
189 190 191
 *
 */
void
192
psql_error(const char *fmt,...)
Peter Eisentraut's avatar
Peter Eisentraut committed
193
{
194
	va_list		ap;
Peter Eisentraut's avatar
Peter Eisentraut committed
195

196 197 198
	fflush(stdout);
	if (pset.queryFout != stdout)
		fflush(pset.queryFout);
Peter Eisentraut's avatar
Peter Eisentraut committed
199

200 201 202
	if (pset.inputfile)
		fprintf(stderr, "%s:%s:%u: ", pset.progname, pset.inputfile, pset.lineno);
	va_start(ap, fmt);
203
	vfprintf(stderr, gettext(fmt), ap);
204
	va_end(ap);
Peter Eisentraut's avatar
Peter Eisentraut committed
205 206 207
}


208 209

/*
210
 * for backend Notice messages (INFO, WARNING, etc)
211
 */
Peter Eisentraut's avatar
Peter Eisentraut committed
212
void
213
NoticeProcessor(void *arg, const char *message)
Peter Eisentraut's avatar
Peter Eisentraut committed
214
{
215 216
	(void) arg;					/* not used */
	psql_error("%s", message);
Peter Eisentraut's avatar
Peter Eisentraut committed
217 218 219 220
}



221
/*
222
 * Code to support query cancellation
223
 *
224 225
 * Before we start a query, we enable a SIGINT signal catcher that sends a
 * cancel request to the backend. Note that sending the cancel directly from
226 227
 * the signal handler is safe because PQcancel() is written to make it
 * so. We use write() to print to stderr because it's better to use simple
228
 * facilities in a signal handler.
229 230 231 232 233 234
 *
 * 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
 * to protect the PGcancel structure against being changed while the other
 * thread is using it.
235
 */
236 237 238 239
static PGcancel *cancelConn = NULL;
#ifdef WIN32
static CRITICAL_SECTION cancelConnLock;
#endif
240 241 242

volatile bool cancel_pressed = false;

243
#define write_stderr(str)	write(fileno(stderr), str, strlen(str))
244

245

246
#ifndef WIN32
247

248
void
249
handle_sigint(SIGNAL_ARGS)
250
{
251
	int			save_errno = errno;
252
	char        errbuf[256];
253

254 255
	/* Don't muck around if prompting for a password. */
	if (prompt_state)
256
		return;
257

Bruce Momjian's avatar
Bruce Momjian committed
258
	if (cancelConn == NULL)
259
		siglongjmp(main_loop_jmp, 1);
260

261
	cancel_pressed = true;
262

263
	if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
264
		write_stderr("Cancel request sent\n");
Bruce Momjian's avatar
Bruce Momjian committed
265 266
	else
	{
267
		write_stderr("Could not send cancel request: ");
268
		write_stderr(errbuf);
Bruce Momjian's avatar
Bruce Momjian committed
269
	}
270
	errno = save_errno;			/* just in case the write changed it */
271 272
}

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
#else /* WIN32 */

static BOOL WINAPI
consoleHandler(DWORD dwCtrlType)
{
	char        errbuf[256];

	if (dwCtrlType == CTRL_C_EVENT ||
		dwCtrlType == CTRL_BREAK_EVENT)
	{
		if (prompt_state)
			return TRUE;

		/* Perform query cancel */
		EnterCriticalSection(&cancelConnLock);
		if (cancelConn != NULL)
		{
			cancel_pressed = true;

			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;
}

void
310
setup_win32_locks(void)
311 312
{
	InitializeCriticalSection(&cancelConnLock);
313 314 315 316 317
}

void
setup_cancel_handler(void)
{
318 319 320 321
	SetConsoleCtrlHandler(consoleHandler, TRUE);
}

#endif /* WIN32 */
322

323 324 325 326 327

/* ConnectionUp
 *
 * Returns whether our backend connection is still there.
 */
Bruce Momjian's avatar
Bruce Momjian committed
328
static bool
329
ConnectionUp(void)
330 331 332 333 334 335 336 337 338 339 340 341
{
	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
Bruce Momjian's avatar
Bruce Momjian committed
342
 * restored successfully; false otherwise.	If, however, there was no
343 344 345 346
 * connection and the session is non-interactive, this will exit the program
 * with a code of EXIT_BADCONN.
 */
static bool
347
CheckConnection(void)
348
{
Bruce Momjian's avatar
Bruce Momjian committed
349 350
	bool		OK;

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
	OK = ConnectionUp();
	if (!OK)
	{
		if (!pset.cur_cmd_interactive)
		{
			psql_error("connection to server was lost\n");
			exit(EXIT_BADCONN);
		}

		fputs(gettext("The connection to the server was lost. Attempting reset: "), stderr);
		PQreset(pset.db);
		OK = ConnectionUp();
		if (!OK)
		{
			fputs(gettext("Failed.\n"), stderr);
			PQfinish(pset.db);
			pset.db = NULL;
			ResetCancelConn();
369
			UnsyncVariables();
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
		}
		else
			fputs(gettext("Succeeded.\n"), stderr);
	}

	return OK;
}



/*
 * SetCancelConn
 *
 * Set cancelConn to point to the current database connection.
 */
Bruce Momjian's avatar
Bruce Momjian committed
385 386
static void
SetCancelConn(void)
387
{
388 389 390 391 392 393 394 395 396 397 398 399 400
#ifdef WIN32
	EnterCriticalSection(&cancelConnLock);
#endif

	/* Free the old one if we have one */
	if (cancelConn != NULL)
		PQfreeCancel(cancelConn);

	cancelConn = PQgetCancel(pset.db);

#ifdef WIN32
	LeaveCriticalSection(&cancelConnLock);
#endif
401 402 403 404 405 406
}


/*
 * ResetCancelConn
 *
407
 * Free the current cancel connection, if any, and set to NULL.
408
 */
Bruce Momjian's avatar
Bruce Momjian committed
409 410
void
ResetCancelConn(void)
411
{
412 413 414 415 416 417 418
#ifdef WIN32
	EnterCriticalSection(&cancelConnLock);
#endif

	if (cancelConn)
		PQfreeCancel(cancelConn);

Bruce Momjian's avatar
Bruce Momjian committed
419
	cancelConn = NULL;
420 421 422 423

#ifdef WIN32
	LeaveCriticalSection(&cancelConnLock);
#endif
424 425 426
}


427 428 429 430 431 432 433 434 435 436 437 438 439
/*
 * on errors, print syntax error position if available.
 *
 * the query is expected to be in the client encoding.
 */
static void
ReportSyntaxErrorPosition(const PGresult *result, const char *query)
{
#define DISPLAY_SIZE	60		/* screen width limit, in screen cols */
#define MIN_RIGHT_CUT	10		/* try to keep this far away from EOL */

	int			loc = 0;
	const char *sp;
Bruce Momjian's avatar
Bruce Momjian committed
440 441 442 443 444 445 446 447 448 449 450 451 452
	int			clen,
				slen,
				i,
			   *qidx,
			   *scridx,
				qoffset,
				scroffset,
				ibeg,
				iend,
				loc_line;
	char	   *wquery;
	bool		beg_trunc,
				end_trunc;
453 454
	PQExpBufferData msg;

455 456 457
	if (pset.verbosity == PQERRORS_TERSE)
		return;

458 459
	sp = PQresultErrorField(result, PG_DIAG_STATEMENT_POSITION);
	if (sp == NULL)
460 461 462 463 464 465 466 467
	{
		sp = PQresultErrorField(result, PG_DIAG_INTERNAL_POSITION);
		if (sp == NULL)
			return;				/* no syntax error */
		query = PQresultErrorField(result, PG_DIAG_INTERNAL_QUERY);
	}
	if (query == NULL)
		return;					/* nothing to reference location to */
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

	if (sscanf(sp, "%d", &loc) != 1)
	{
		psql_error("INTERNAL ERROR: unexpected statement position \"%s\"\n",
				   sp);
		return;
	}

	/* Make a writable copy of the query, and a buffer for messages. */
	wquery = pg_strdup(query);

	initPQExpBuffer(&msg);

	/*
	 * The returned cursor position is measured in logical characters.
	 * Each character might occupy multiple physical bytes in the string,
	 * and in some Far Eastern character sets it might take more than one
	 * screen column as well.  We compute the starting byte offset and
	 * starting screen column of each logical character, and store these
	 * in qidx[] and scridx[] respectively.
	 */

	/* we need a safe allocation size... */
	slen = strlen(query) + 1;

	qidx = (int *) pg_malloc(slen * sizeof(int));
	scridx = (int *) pg_malloc(slen * sizeof(int));

	qoffset = 0;
	scroffset = 0;
	for (i = 0; query[qoffset] != '\0'; i++)
	{
		qidx[i] = qoffset;
		scridx[i] = scroffset;
502
		scroffset += PQdsplen(&query[qoffset], pset.encoding);
503 504 505 506 507 508 509 510
		qoffset += PQmblen(&query[qoffset], pset.encoding);
	}
	qidx[i] = qoffset;
	scridx[i] = scroffset;
	clen = i;
	psql_assert(clen < slen);

	/* convert loc to zero-based offset in qidx/scridx arrays */
Bruce Momjian's avatar
Bruce Momjian committed
511
	loc--;
512 513 514 515

	/* do we have something to show? */
	if (loc >= 0 && loc <= clen)
	{
Bruce Momjian's avatar
Bruce Momjian committed
516
		/* input line number of our syntax error. */
517 518
		loc_line = 1;
		/* first included char of extract. */
Bruce Momjian's avatar
Bruce Momjian committed
519
		ibeg = 0;
520
		/* last-plus-1 included char of extract. */
Bruce Momjian's avatar
Bruce Momjian committed
521
		iend = clen;
522 523 524 525 526 527 528

		/*
		 * Replace tabs with spaces in the writable copy.  (Later we might
		 * want to think about coping with their variable screen width,
		 * but not today.)
		 *
		 * Extract line number and begin and end indexes of line containing
Bruce Momjian's avatar
Bruce Momjian committed
529
		 * error location.	There will not be any newlines or carriage
530 531
		 * returns in the selected extract.
		 */
Bruce Momjian's avatar
Bruce Momjian committed
532
		for (i = 0; i < clen; i++)
533 534
		{
			/* character length must be 1 or it's not ASCII */
Bruce Momjian's avatar
Bruce Momjian committed
535
			if ((qidx[i + 1] - qidx[i]) == 1)
536
			{
Bruce Momjian's avatar
Bruce Momjian committed
537
				if (wquery[qidx[i]] == '\t')
538 539 540 541 542 543
					wquery[qidx[i]] = ' ';
				else if (wquery[qidx[i]] == '\r' || wquery[qidx[i]] == '\n')
				{
					if (i < loc)
					{
						/*
Bruce Momjian's avatar
Bruce Momjian committed
544
						 * count lines before loc.	Each \r or \n counts
545 546 547 548
						 * as a line except when \r \n appear together.
						 */
						if (wquery[qidx[i]] == '\r' ||
							i == 0 ||
Bruce Momjian's avatar
Bruce Momjian committed
549 550
							(qidx[i] - qidx[i - 1]) != 1 ||
							wquery[qidx[i - 1]] != '\r')
551 552
							loc_line++;
						/* extract beginning = last line start before loc. */
Bruce Momjian's avatar
Bruce Momjian committed
553
						ibeg = i + 1;
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
					}
					else
					{
						/* set extract end. */
						iend = i;
						/* done scanning. */
						break;
					}
				}
			}
		}

		/* If the line extracted is too long, we truncate it. */
		beg_trunc = false;
		end_trunc = false;
Bruce Momjian's avatar
Bruce Momjian committed
569
		if (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE)
570 571 572 573 574 575
		{
			/*
			 * We first truncate right if it is enough.  This code might
			 * be off a space or so on enforcing MIN_RIGHT_CUT if there's
			 * a wide character right there, but that should be okay.
			 */
Bruce Momjian's avatar
Bruce Momjian committed
576
			if (scridx[ibeg] + DISPLAY_SIZE >= scridx[loc] + MIN_RIGHT_CUT)
577
			{
Bruce Momjian's avatar
Bruce Momjian committed
578
				while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE)
579 580 581 582 583 584
					iend--;
				end_trunc = true;
			}
			else
			{
				/* Truncate right if not too close to loc. */
Bruce Momjian's avatar
Bruce Momjian committed
585
				while (scridx[loc] + MIN_RIGHT_CUT < scridx[iend])
586 587 588 589 590 591
				{
					iend--;
					end_trunc = true;
				}

				/* Truncate left if still too long. */
Bruce Momjian's avatar
Bruce Momjian committed
592
				while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE)
593 594 595 596 597 598 599 600
				{
					ibeg++;
					beg_trunc = true;
				}
			}
		}

		/* the extract MUST contain the target position! */
Bruce Momjian's avatar
Bruce Momjian committed
601
		psql_assert(ibeg <= loc && loc <= iend);
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616

		/* truncate working copy at desired endpoint */
		wquery[qidx[iend]] = '\0';

		/* Begin building the finished message. */
		printfPQExpBuffer(&msg, gettext("LINE %d: "), loc_line);
		if (beg_trunc)
			appendPQExpBufferStr(&msg, "...");

		/*
		 * While we have the prefix in the msg buffer, compute its screen
		 * width.
		 */
		scroffset = 0;
		for (i = 0; i < msg.len; i += PQmblen(&msg.data[i], pset.encoding))
617
			scroffset += PQdsplen(&msg.data[i], pset.encoding);
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644

		/* Finish and emit the message. */
		appendPQExpBufferStr(&msg, &wquery[qidx[ibeg]]);
		if (end_trunc)
			appendPQExpBufferStr(&msg, "...");

		psql_error("%s\n", msg.data);

		/* Now emit the cursor marker line. */
		scroffset += scridx[loc] - scridx[ibeg];
		resetPQExpBuffer(&msg);
		for (i = 0; i < scroffset; i++)
			appendPQExpBufferChar(&msg, ' ');
		appendPQExpBufferChar(&msg, '^');

		psql_error("%s\n", msg.data);
	}

	/* Clean up. */
	termPQExpBuffer(&msg);

	free(wquery);
	free(qidx);
	free(scridx);
}


645 646 647 648
/*
 * AcceptResult
 *
 * Checks whether a result is valid, giving an error message if necessary;
649 650
 * resets cancelConn as needed, and ensures that the connection to the backend
 * is still up.
651 652 653 654
 *
 * Returns true for valid result, false for error state.
 */
static bool
655
AcceptResult(const PGresult *result, const char *query)
656
{
Bruce Momjian's avatar
Bruce Momjian committed
657
	bool		OK = true;
658 659 660 661

	ResetCancelConn();

	if (!result)
Bruce Momjian's avatar
Bruce Momjian committed
662 663 664 665 666 667
		OK = false;
	else
		switch (PQresultStatus(result))
		{
			case PGRES_COMMAND_OK:
			case PGRES_TUPLES_OK:
668
			case PGRES_EMPTY_QUERY:
Bruce Momjian's avatar
Bruce Momjian committed
669 670 671 672 673 674 675 676 677 678 679 680 681
			case PGRES_COPY_IN:
				/* Fine, do nothing */
				break;

			case PGRES_COPY_OUT:
				/* keep cancel connection for copy out state */
				SetCancelConn();
				break;

			default:
				OK = false;
				break;
		}
682

Bruce Momjian's avatar
Bruce Momjian committed
683
	if (!OK)
684 685
	{
		psql_error("%s", PQerrorMessage(pset.db));
686
		ReportSyntaxErrorPosition(result, query);
687
		CheckConnection();
688 689 690 691 692 693 694
	}

	return OK;
}



Bruce Momjian's avatar
Bruce Momjian committed
695 696
/*
 * PSQLexec
697 698
 *
 * This is the way to send "backdoor" queries (those not directly entered
699
 * by the user). It is subject to -E but not -e.
700
 *
701 702 703
 * 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.
704 705 706
 *
 * Note: we don't bother to check PQclientEncoding; it is assumed that no
 * caller uses this path to issue "SET CLIENT_ENCODING".
707
 */
708
PGresult *
709
PSQLexec(const char *query, bool start_xact)
710
{
711
	PGresult   *res;
Bruce Momjian's avatar
Bruce Momjian committed
712
	int			echo_hidden;
Bruce Momjian's avatar
Bruce Momjian committed
713

714
	if (!pset.db)
Bruce Momjian's avatar
Bruce Momjian committed
715
	{
716
		psql_error("You are currently not connected to a database.\n");
Bruce Momjian's avatar
Bruce Momjian committed
717 718 719
		return NULL;
	}

720
	echo_hidden = SwitchVariable(pset.vars, "ECHO_HIDDEN", "noexec", NULL);
721
	if (echo_hidden != VAR_NOTSET)
Bruce Momjian's avatar
Bruce Momjian committed
722
	{
723 724 725
		printf("********* QUERY **********\n"
			   "%s\n"
			   "**************************\n\n", query);
Bruce Momjian's avatar
Bruce Momjian committed
726 727
		fflush(stdout);

728 729
		if (echo_hidden == 1)	/* noexec? */
			return NULL;
730
	}
Bruce Momjian's avatar
Bruce Momjian committed
731

732 733
	SetCancelConn();

734 735 736 737 738
	if (start_xact && PQtransactionStatus(pset.db) == PQTRANS_IDLE &&
		!GetVariableBool(pset.vars, "AUTOCOMMIT"))
	{
		res = PQexec(pset.db, "BEGIN");
		if (PQresultStatus(res) != PGRES_COMMAND_OK)
739
		{
740 741 742 743
			psql_error("%s", PQerrorMessage(pset.db));
			PQclear(res);
			ResetCancelConn();
			return NULL;
744
		}
745
		PQclear(res);
746
	}
747

748 749
	res = PQexec(pset.db, query);

750
	if (!AcceptResult(res, query) && res)
751
	{
752
		PQclear(res);
753
		res = NULL;
754
	}
755

756
	return res;
757 758 759 760 761
}



/*
762
 * PrintNotifications: check for asynchronous notifications, and print them out
763
 */
764 765
static void
PrintNotifications(void)
766
{
767
	PGnotify   *notify;
Bruce Momjian's avatar
Bruce Momjian committed
768

769
	while ((notify = PQnotifies(pset.db)))
770
	{
771
		fprintf(pset.queryFout, gettext("Asynchronous notification \"%s\" received from server process with PID %d.\n"),
772 773
				notify->relname, notify->be_pid);
		fflush(pset.queryFout);
774
		PQfreemem(notify);
775
	}
776
}
777

Bruce Momjian's avatar
Bruce Momjian committed
778

779 780 781 782 783
/*
 * PrintQueryTuples: assuming query result is OK, print its tuples
 *
 * Returns true if successful, false otherwise.
 */
Bruce Momjian's avatar
Bruce Momjian committed
784
static bool
785 786
PrintQueryTuples(const PGresult *results)
{
Bruce Momjian's avatar
Bruce Momjian committed
787 788 789 790 791
	/* write output to \g argument, if any */
	if (pset.gfname)
	{
		FILE	   *queryFout_copy = pset.queryFout;
		bool		queryFoutPipe_copy = pset.queryFoutPipe;
792

Bruce Momjian's avatar
Bruce Momjian committed
793
		pset.queryFout = stdout;	/* so it doesn't get closed */
794

Bruce Momjian's avatar
Bruce Momjian committed
795 796 797 798 799
		/* open file/pipe */
		if (!setQFout(pset.gfname))
		{
			pset.queryFout = queryFout_copy;
			pset.queryFoutPipe = queryFoutPipe_copy;
800
			return false;
Bruce Momjian's avatar
Bruce Momjian committed
801
		}
802

Bruce Momjian's avatar
Bruce Momjian committed
803
		printQuery(results, &pset.popt, pset.queryFout);
804

Bruce Momjian's avatar
Bruce Momjian committed
805 806
		/* close file/pipe, restore old setting */
		setQFout(NULL);
807

Bruce Momjian's avatar
Bruce Momjian committed
808 809
		pset.queryFout = queryFout_copy;
		pset.queryFoutPipe = queryFoutPipe_copy;
810

Bruce Momjian's avatar
Bruce Momjian committed
811 812 813 814 815
		free(pset.gfname);
		pset.gfname = NULL;
	}
	else
		printQuery(results, &pset.popt, pset.queryFout);
816 817 818 819 820 821

	return true;
}


/*
822
 * ProcessCopyResult: if command was a COPY FROM STDIN/TO STDOUT, handle it
823 824 825 826 827 828
 *
 * Note: Utility function for use by SendQuery() only.
 *
 * Returns true if the query executed successfully, false otherwise.
 */
static bool
829
ProcessCopyResult(PGresult *results)
830
{
Bruce Momjian's avatar
Bruce Momjian committed
831
	bool		success = false;
832

Bruce Momjian's avatar
Bruce Momjian committed
833 834
	if (!results)
		return false;
835 836 837 838

	switch (PQresultStatus(results))
	{
		case PGRES_TUPLES_OK:
839
		case PGRES_COMMAND_OK:
Bruce Momjian's avatar
Bruce Momjian committed
840
		case PGRES_EMPTY_QUERY:
841
			/* nothing to do here */
Bruce Momjian's avatar
Bruce Momjian committed
842 843
			success = true;
			break;
844 845 846 847 848 849

		case PGRES_COPY_OUT:
			success = handleCopyOut(pset.db, pset.queryFout);
			break;

		case PGRES_COPY_IN:
850
			success = handleCopyIn(pset.db, pset.cur_cmd_source);
851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885
			break;

		default:
			break;
	}

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

	return success;
}


/*
 * 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;

	if (!results)
		return false;

	switch (PQresultStatus(results))
	{
		case PGRES_TUPLES_OK:
			success = PrintQueryTuples(results);
			break;

Bruce Momjian's avatar
Bruce Momjian committed
886 887 888 889
		case PGRES_COMMAND_OK:
			{
				char		buf[10];

890
				success = true;
891 892
				snprintf(buf, sizeof(buf),
						 "%u", (unsigned int) PQoidValue(results));
Bruce Momjian's avatar
Bruce Momjian committed
893
				if (!QUIET())
894
				{
Bruce Momjian's avatar
Bruce Momjian committed
895 896 897
					if (pset.popt.topt.format == PRINT_HTML)
					{
						fputs("<p>", pset.queryFout);
898 899
						html_escaped_print(PQcmdStatus(results),
										   pset.queryFout);
Bruce Momjian's avatar
Bruce Momjian committed
900 901 902 903
						fputs("</p>\n", pset.queryFout);
					}
					else
						fprintf(pset.queryFout, "%s\n", PQcmdStatus(results));
904
				}
Bruce Momjian's avatar
Bruce Momjian committed
905
				SetVariable(pset.vars, "LASTOID", buf);
906
				break;
Bruce Momjian's avatar
Bruce Momjian committed
907
			}
908 909 910

		case PGRES_EMPTY_QUERY:
			success = true;
Bruce Momjian's avatar
Bruce Momjian committed
911
			break;
912

913
		case PGRES_COPY_OUT:
Bruce Momjian's avatar
Bruce Momjian committed
914
		case PGRES_COPY_IN:
915 916
			/* nothing to do here */
			success = true;
Bruce Momjian's avatar
Bruce Momjian committed
917
			break;
918

919
		default:
Bruce Momjian's avatar
Bruce Momjian committed
920 921
			break;
	}
922

Bruce Momjian's avatar
Bruce Momjian committed
923
	fflush(pset.queryFout);
924

925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944
	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)
{
	PGresult   *results;
Bruce Momjian's avatar
Bruce Momjian committed
945 946 947
	TimevalStruct before,
				after;
	bool		OK;
948 949

	if (!pset.db)
950
	{
951 952
		psql_error("You are currently not connected to a database.\n");
		return false;
953
	}
954 955

	if (GetVariableBool(pset.vars, "SINGLESTEP"))
956
	{
957 958
		char		buf[3];

959
		printf(gettext("***(Single step mode: verify command)*******************************************\n"
960 961 962 963 964 965
					   "%s\n"
					   "***(press return to proceed or enter x and return to cancel)********************\n"),
			   query);
		fflush(stdout);
		if (fgets(buf, sizeof(buf), stdin) != NULL)
			if (buf[0] == 'x')
966 967
				return false;
	}
968
	else if (VariableEquals(pset.vars, "ECHO", "queries"))
969
	{
970
		puts(query);
971 972
		fflush(stdout);
	}
Bruce Momjian's avatar
Bruce Momjian committed
973

974
	SetCancelConn();
975

976 977
	if (PQtransactionStatus(pset.db) == PQTRANS_IDLE &&
		!GetVariableBool(pset.vars, "AUTOCOMMIT") &&
978
		!command_no_begin(query))
979 980 981 982 983 984 985 986 987 988 989 990
	{
		results = PQexec(pset.db, "BEGIN");
		if (PQresultStatus(results) != PGRES_COMMAND_OK)
		{
			psql_error("%s", PQerrorMessage(pset.db));
			PQclear(results);
			ResetCancelConn();
			return false;
		}
		PQclear(results);
	}

991 992
	if (pset.timing)
		GETTIMEOFDAY(&before);
993

994
	results = PQexec(pset.db, query);
995 996

	/* these operations are included in the timing result: */
997
	OK = (AcceptResult(results, query) && ProcessCopyResult(results));
998

999 1000 1001
	if (pset.timing)
		GETTIMEOFDAY(&after);

1002 1003 1004 1005
	/* but printing results isn't: */
	if (OK)
		OK = PrintQueryResults(results);

1006 1007
	PQclear(results);

1008 1009 1010 1011
	/* Possible microtiming output */
	if (OK && pset.timing)
		printf(gettext("Time: %.3f ms\n"), DIFF_MSEC(&after, &before));

1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
	/* 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));
	}

1024
	PrintNotifications();
1025

1026
	return OK;
1027
}
1028

1029

1030
/*
1031
 * Advance the given char pointer over white space and SQL comments.
1032
 */
1033 1034
static const char *
skip_white_space(const char *query)
1035
{
1036
	int			cnestlevel = 0;		/* slash-star comment nest level */
1037 1038 1039

	while (*query)
	{
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
		int		mblen = PQmblen(query, pset.encoding);

		/*
		 * 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.
		 */
1050
		if (isspace((unsigned char) *query))
1051 1052
			query += mblen;
		else if (query[0] == '/' && query[1] == '*')
1053
		{
1054
			cnestlevel++;
1055 1056
			query += 2;
		}
1057 1058 1059 1060 1061 1062
		else if (cnestlevel > 0 && query[0] == '*' && query[1] == '/')
		{
			cnestlevel--;
			query += 2;
		}
		else if (cnestlevel == 0 && query[0] == '-' && query[1] == '-')
1063 1064
		{
			query += 2;
1065 1066 1067 1068
			/*
			 * We have to skip to end of line since any slash-star inside
			 * the -- comment does NOT start a slash-star comment.
			 */
1069 1070
			while (*query)
			{
1071
				if (*query == '\n')
1072
				{
1073
					query++;
1074 1075
					break;
				}
1076
				query += PQmblen(query, pset.encoding);
1077 1078
			}
		}
1079 1080
		else if (cnestlevel > 0)
			query += mblen;
1081 1082 1083 1084
		else
			break;				/* found first token */
	}

1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
	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;

1101
	/*
1102 1103 1104 1105 1106 1107
	 * First we must advance over any whitespace and comments.
	 */
	query = skip_white_space(query);

	/*
	 * Check word length (since "beginx" is not "begin").
1108 1109 1110
	 */
	wordlen = 0;
	while (isalpha((unsigned char) query[wordlen]))
1111
		wordlen += PQmblen(&query[wordlen], pset.encoding);
1112

1113 1114 1115 1116 1117 1118 1119 1120 1121 1122
	/*
	 * Transaction control commands.  These should include every keyword
	 * that gives rise to a TransactionStmt in the backend grammar, except
	 * for the savepoint-related commands.
	 *
	 * (We assume that START must be START TRANSACTION, since there is 
	 * presently no other "START foo" command.)
	 */
	if (wordlen == 5 && pg_strncasecmp(query, "abort", 5) == 0)
		return true;
1123
	if (wordlen == 5 && pg_strncasecmp(query, "begin", 5) == 0)
1124
		return true;
1125 1126
	if (wordlen == 5 && pg_strncasecmp(query, "start", 5) == 0)
		return true;
1127
	if (wordlen == 6 && pg_strncasecmp(query, "commit", 6) == 0)
1128
		return true;
1129
	if (wordlen == 3 && pg_strncasecmp(query, "end", 3) == 0)
1130
		return true;
1131
	if (wordlen == 8 && pg_strncasecmp(query, "rollback", 8) == 0)
1132
		return true;
1133 1134 1135 1136 1137 1138 1139 1140 1141 1142

	/*
	 * Commands not allowed within transactions.  The statements checked
	 * for here should be exactly those that call PreventTransactionChain()
	 * in the backend.
	 *
	 * Note: we are a bit sloppy about CLUSTER, which is transactional in
	 * some variants but not others.
	 */
	if (wordlen == 6 && pg_strncasecmp(query, "vacuum", 6) == 0)
1143
		return true;
1144
	if (wordlen == 7 && pg_strncasecmp(query, "cluster", 7) == 0)
1145 1146
		return true;

1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
	/*
	 * Note: these tests will match REINDEX TABLESPACE, which isn't really
	 * a valid command so we don't care much.  The other five possible
	 * matches are correct.
	 */
	if ((wordlen == 6 && pg_strncasecmp(query, "create", 6) == 0) ||
		(wordlen == 4 && pg_strncasecmp(query, "drop", 4) == 0) ||
		(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;
		if (wordlen == 10 && pg_strncasecmp(query, "tablespace", 10) == 0)
			return true;
	}

1170 1171 1172
	return false;
}

1173

Bruce Momjian's avatar
Bruce Momjian committed
1174 1175
char
parse_char(char **buf)
1176
{
Bruce Momjian's avatar
Bruce Momjian committed
1177
	long		l;
1178

Bruce Momjian's avatar
Bruce Momjian committed
1179 1180 1181
	l = strtol(*buf, buf, 0);
	--*buf;
	return (char) l;
1182 1183 1184
}


1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
/*
 * 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;
}
1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228


/*
 * 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);
}
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251


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

	/* MSDOS uses tilde for short versions of long file names, so skip it. */
#ifndef WIN32

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

		fn = *filename;
1255
		*home = '\0';
1256 1257 1258 1259 1260 1261 1262 1263 1264

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

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

		if (*(fn + 1) == '\0')
1265
			get_home_path(home);
1266
		else if ((pw = getpwnam(fn + 1)) != NULL)
1267
			StrNCpy(home, pw->pw_dir, MAXPGPATH);
1268 1269

		*p = oldp;
1270
		if (strlen(home) != 0)
1271 1272 1273
		{
			char	   *newfn;

1274
			newfn = pg_malloc(strlen(home) + strlen(p) + 1);
1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285
			strcpy(newfn, home);
			strcat(newfn, p);

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

	return *filename;
}