⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 common.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * psql - the PostgreSQL interactive terminal * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.110.2.1 2005/11/22 18:23:27 momjian Exp $ */#include "postgres_fe.h"#include "common.h"#include <ctype.h>#ifndef HAVE_STRDUP#include <strdup.h>#endif#include <signal.h>#ifndef WIN32#include <sys/time.h>#include <unistd.h>				/* for write() */#include <setjmp.h>#else#include <io.h>					/* for _write() */#include <win32.h>#include <sys/timeb.h>			/* for _ftime() */#endif#include "libpq-fe.h"#include "pqsignal.h"#include "settings.h"#include "variables.h"#include "command.h"#include "copy.h"#include "prompt.h"#include "print.h"#include "mainloop.h"#include "mb/pg_wchar.h"/* 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 WIN32typedef struct timeval TimevalStruct;#define GETTIMEOFDAY(T) gettimeofday(T, NULL)#define DIFF_MSEC(T, U) \	((((int) ((T)->tv_sec - (U)->tv_sec)) * 1000000.0 + \	  ((int) ((T)->tv_usec - (U)->tv_usec))) / 1000.0)#elsetypedef struct _timeb TimevalStruct;#define GETTIMEOFDAY(T) _ftime(T)#define DIFF_MSEC(T, U) \	(((T)->time - (U)->time) * 1000.0 + \	 ((T)->millitm - (U)->millitm))#endifextern bool prompt_state;static bool command_no_begin(const char *query);/* * "Safe" wrapper around strdup() */char *pg_strdup(const char *string){	char	   *tmp;	if (!string)	{		fprintf(stderr, _("%s: pg_strdup: cannot duplicate null pointer (internal error)\n"),				pset.progname);		exit(EXIT_FAILURE);	}	tmp = strdup(string);	if (!tmp)	{		psql_error("out of memory\n");		exit(EXIT_FAILURE);	}	return tmp;}void *pg_malloc(size_t size){	void	   *tmp;	tmp = malloc(size);	if (!tmp)	{		psql_error("out of memory\n");		exit(EXIT_FAILURE);	}	return tmp;}void *pg_malloc_zero(size_t size){	void	   *tmp;	tmp = pg_malloc(size);	memset(tmp, 0, size);	return tmp;}void *pg_calloc(size_t nmemb, size_t size){	void	   *tmp;	tmp = calloc(nmemb, size);	if (!tmp)	{		psql_error("out of memory");		exit(EXIT_FAILURE);	}	return tmp;}/* * 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. */boolsetQFout(const char *fname){	bool		status = true;	/* Close old file/pipe */	if (pset.queryFout && pset.queryFout != stdout && pset.queryFout != stderr)	{		if (pset.queryFoutPipe)			pclose(pset.queryFout);		else			fclose(pset.queryFout);	}	/* If no filename, set stdout */	if (!fname || fname[0] == '\0')	{		pset.queryFout = stdout;		pset.queryFoutPipe = false;	}	else if (*fname == '|')	{		pset.queryFout = popen(fname + 1, "w");		pset.queryFoutPipe = true;	}	else	{		pset.queryFout = fopen(fname, "w");		pset.queryFoutPipe = false;	}	if (!(pset.queryFout))	{		psql_error("%s: %s\n", fname, strerror(errno));		pset.queryFout = stdout;		pset.queryFoutPipe = false;		status = false;	}	/* Direct signals */#ifndef WIN32	pqsignal(SIGPIPE, pset.queryFoutPipe ? SIG_IGN : SIG_DFL);#endif	return status;}/* * Error reporting for scripts. Errors should look like *	 psql:filename:lineno: message * */voidpsql_error(const char *fmt,...){	va_list		ap;	fflush(stdout);	if (pset.queryFout != stdout)		fflush(pset.queryFout);	if (pset.inputfile)		fprintf(stderr, "%s:%s:%u: ", pset.progname, pset.inputfile, pset.lineno);	va_start(ap, fmt);	vfprintf(stderr, _(fmt), ap);	va_end(ap);}/* * for backend Notice messages (INFO, WARNING, etc) */voidNoticeProcessor(void *arg, const char *message){	(void) arg;					/* not used */	psql_error("%s", message);}/* * Code to support query cancellation * * 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 * 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 * facilities in a signal handler. * * 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. */static PGcancel *cancelConn = NULL;#ifdef WIN32static CRITICAL_SECTION cancelConnLock;#endifvolatile bool cancel_pressed = false;#define write_stderr(str)	write(fileno(stderr), str, strlen(str))#ifndef WIN32voidhandle_sigint(SIGNAL_ARGS){	int			save_errno = errno;	char		errbuf[256];	/* Don't muck around if prompting for a password. */	if (prompt_state)		return;	if (cancelConn == NULL)		siglongjmp(main_loop_jmp, 1);	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);	}	errno = save_errno;			/* just in case the write changed it */}#else							/* WIN32 */static BOOL WINAPIconsoleHandler(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;}voidsetup_win32_locks(void){	InitializeCriticalSection(&cancelConnLock);}voidsetup_cancel_handler(void){	static bool done = false;	/* only need one handler per process */	if (!done)	{		SetConsoleCtrlHandler(consoleHandler, TRUE);		done = true;	}}#endif   /* WIN32 *//* ConnectionUp * * Returns whether our backend connection is still there. */static boolConnectionUp(void){	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 * restored successfully; false otherwise.	If, however, there was no * connection and the session is non-interactive, this will exit the program * with a code of EXIT_BADCONN. */static boolCheckConnection(void){	bool		OK;	OK = ConnectionUp();	if (!OK)	{		if (!pset.cur_cmd_interactive)		{			psql_error("connection to server was lost\n");			exit(EXIT_BADCONN);		}		fputs(_("The connection to the server was lost. Attempting reset: "), stderr);		PQreset(pset.db);		OK = ConnectionUp();		if (!OK)		{			fputs(_("Failed.\n"), stderr);			PQfinish(pset.db);			pset.db = NULL;			ResetCancelConn();			UnsyncVariables();		}		else			fputs(_("Succeeded.\n"), stderr);	}	return OK;}/* * SetCancelConn * * Set cancelConn to point to the current database connection. */static voidSetCancelConn(void){#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}/* * ResetCancelConn * * Free the current cancel connection, if any, and set to NULL. */voidResetCancelConn(void){#ifdef WIN32	EnterCriticalSection(&cancelConnLock);#endif	if (cancelConn)		PQfreeCancel(cancelConn);	cancelConn = NULL;#ifdef WIN32	LeaveCriticalSection(&cancelConnLock);#endif}/* * on errors, print syntax error position if available. * * the query is expected to be in the client encoding. */static voidReportSyntaxErrorPosition(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;	int			clen,				slen,				i,			   *qidx,			   *scridx,				qoffset,				scroffset,				ibeg,				iend,				loc_line;	char	   *wquery;	bool		beg_trunc,				end_trunc;	PQExpBufferData msg;	if (pset.verbosity == PQERRORS_TERSE)		return;	sp = PQresultErrorField(result, PG_DIAG_STATEMENT_POSITION);	if (sp == NULL)	{		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 */	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;		scroffset += PQdsplen(&query[qoffset], pset.encoding);		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 */	loc--;	/* do we have something to show? */	if (loc >= 0 && loc <= clen)	{		/* input line number of our syntax error. */		loc_line = 1;		/* first included char of extract. */		ibeg = 0;		/* last-plus-1 included char of extract. */		iend = clen;		/*		 * 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		 * error location.	There will not be any newlines or carriage returns		 * in the selected extract.		 */		for (i = 0; i < clen; i++)		{			/* character length must be 1 or it's not ASCII */			if ((qidx[i + 1] - qidx[i]) == 1)			{				if (wquery[qidx[i]] == '\t')					wquery[qidx[i]] = ' ';				else if (wquery[qidx[i]] == '\r' || wquery[qidx[i]] == '\n')				{					if (i < loc)					{						/*						 * count lines before loc.	Each \r or \n counts as a						 * line except when \r \n appear together.						 */						if (wquery[qidx[i]] == '\r' ||							i == 0 ||							(qidx[i] - qidx[i - 1]) != 1 ||							wquery[qidx[i - 1]] != '\r')							loc_line++;						/* extract beginning = last line start before loc. */						ibeg = i + 1;					}					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;		if (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE)		{			/*			 * 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.			 */			if (scridx[ibeg] + DISPLAY_SIZE >= scridx[loc] + MIN_RIGHT_CUT)			{				while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE)					iend--;				end_trunc = true;			}			else			{				/* Truncate right if not too close to loc. */				while (scridx[loc] + MIN_RIGHT_CUT < scridx[iend])				{					iend--;					end_trunc = true;				}				/* Truncate left if still too long. */				while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE)				{					ibeg++;					beg_trunc = true;				}			}		}		/* the extract MUST contain the target position! */		psql_assert(ibeg <= loc && loc <= iend);		/* truncate working copy at desired endpoint */		wquery[qidx[iend]] = '\0';		/* Begin building the finished message. */		printfPQExpBuffer(&msg, _("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))			scroffset += PQdsplen(&msg.data[i], pset.encoding);		/* 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);}/* * AcceptResult * * Checks whether a result is valid, giving an error message if necessary; * resets cancelConn as needed, and ensures that the connection to the backend * is still up. * * Returns true for valid result, false for error state. */static boolAcceptResult(const PGresult *result, const char *query){	bool		OK = true;	ResetCancelConn();	if (!result)		OK = false;	else		switch (PQresultStatus(result))		{			case PGRES_COMMAND_OK:			case PGRES_TUPLES_OK:			case PGRES_EMPTY_QUERY:			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;		}	if (!OK)	{		const char *error = PQerrorMessage(pset.db);		if (strlen(error))			psql_error("%s", error);		ReportSyntaxErrorPosition(result, query);		CheckConnection();	}	return OK;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -