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

📄 common.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
}/* * PSQLexec * * This is the way to send "backdoor" queries (those not directly entered * by the user). It is subject to -E but not -e. * * 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. * * Note: we don't bother to check PQclientEncoding; it is assumed that no * caller uses this path to issue "SET CLIENT_ENCODING". */PGresult *PSQLexec(const char *query, bool start_xact){	PGresult   *res;	int			echo_hidden;	if (!pset.db)	{		psql_error("You are currently not connected to a database.\n");		return NULL;	}	echo_hidden = SwitchVariable(pset.vars, "ECHO_HIDDEN", "noexec", NULL);	if (echo_hidden != VAR_NOTSET)	{		printf(_("********* QUERY **********\n"				 "%s\n"				 "**************************\n\n"), query);		fflush(stdout);		if (pset.logfile)		{			fprintf(pset.logfile,					_("********* QUERY **********\n"					  "%s\n"					  "**************************\n\n"), query);			fflush(pset.logfile);		}		if (echo_hidden == 1)	/* noexec? */			return NULL;	}	SetCancelConn();	if (start_xact && PQtransactionStatus(pset.db) == PQTRANS_IDLE &&		!GetVariableBool(pset.vars, "AUTOCOMMIT"))	{		res = PQexec(pset.db, "BEGIN");		if (PQresultStatus(res) != PGRES_COMMAND_OK)		{			psql_error("%s", PQerrorMessage(pset.db));			PQclear(res);			ResetCancelConn();			return NULL;		}		PQclear(res);	}	res = PQexec(pset.db, query);	if (!AcceptResult(res, query) && res)	{		PQclear(res);		res = NULL;	}	return res;}/* * PrintNotifications: check for asynchronous notifications, and print them out */static voidPrintNotifications(void){	PGnotify   *notify;	while ((notify = PQnotifies(pset.db)))	{		fprintf(pset.queryFout, _("Asynchronous notification \"%s\" received from server process with PID %d.\n"),				notify->relname, notify->be_pid);		fflush(pset.queryFout);		PQfreemem(notify);	}}/* * PrintQueryTuples: assuming query result is OK, print its tuples * * Returns true if successful, false otherwise. */static boolPrintQueryTuples(const PGresult *results){	printQueryOpt my_popt = pset.popt;	/* write output to \g argument, if any */	if (pset.gfname)	{		FILE	   *queryFout_copy = pset.queryFout;		bool		queryFoutPipe_copy = pset.queryFoutPipe;		pset.queryFout = stdout;	/* so it doesn't get closed */		/* open file/pipe */		if (!setQFout(pset.gfname))		{			pset.queryFout = queryFout_copy;			pset.queryFoutPipe = queryFoutPipe_copy;			return false;		}		printQuery(results, &my_popt, pset.queryFout, pset.logfile);		/* close file/pipe, restore old setting */		setQFout(NULL);		pset.queryFout = queryFout_copy;		pset.queryFoutPipe = queryFoutPipe_copy;		free(pset.gfname);		pset.gfname = NULL;	}	else		printQuery(results, &my_popt, pset.queryFout, pset.logfile);	return true;}/* * ProcessCopyResult: if command was a COPY FROM STDIN/TO STDOUT, handle it * * Note: Utility function for use by SendQuery() only. * * Returns true if the query executed successfully, false otherwise. */static boolProcessCopyResult(PGresult *results){	bool		success = false;	if (!results)		return false;	switch (PQresultStatus(results))	{		case PGRES_TUPLES_OK:		case PGRES_COMMAND_OK:		case PGRES_EMPTY_QUERY:			/* nothing to do here */			success = true;			break;		case PGRES_COPY_OUT:			success = handleCopyOut(pset.db, pset.queryFout);			break;		case PGRES_COPY_IN:			success = handleCopyIn(pset.db, pset.cur_cmd_source);			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 boolPrintQueryResults(PGresult *results){	bool		success = false;	if (!results)		return false;	switch (PQresultStatus(results))	{		case PGRES_TUPLES_OK:			success = PrintQueryTuples(results);			break;		case PGRES_COMMAND_OK:			{				char		buf[10];				success = true;				snprintf(buf, sizeof(buf),						 "%u", (unsigned int) PQoidValue(results));				if (!QUIET())				{					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));				SetVariable(pset.vars, "LASTOID", buf);				break;			}		case PGRES_EMPTY_QUERY:			success = true;			break;		case PGRES_COPY_OUT:		case PGRES_COPY_IN:			/* nothing to do here */			success = true;			break;		default:			break;	}	fflush(pset.queryFout);	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. */boolSendQuery(const char *query){	PGresult   *results;	TimevalStruct before,				after;	bool		OK,				on_error_rollback_savepoint = false;	PGTransactionStatusType transaction_status;	static bool on_error_rollback_warning = false;	const char *rollback_str;	if (!pset.db)	{		psql_error("You are currently not connected to a database.\n");		return false;	}	if (GetVariableBool(pset.vars, "SINGLESTEP"))	{		char		buf[3];		printf(_("***(Single step mode: verify command)*******************************************\n"				 "%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')				return false;	}	else if (VariableEquals(pset.vars, "ECHO", "queries"))	{		puts(query);		fflush(stdout);	}	if (pset.logfile)	{		fprintf(pset.logfile,				_("********* QUERY **********\n"				  "%s\n"				  "**************************\n\n"), query);		fflush(pset.logfile);	}	SetCancelConn();	transaction_status = PQtransactionStatus(pset.db);	if (transaction_status == PQTRANS_IDLE &&		!GetVariableBool(pset.vars, "AUTOCOMMIT") &&		!command_no_begin(query))	{		results = PQexec(pset.db, "BEGIN");		if (PQresultStatus(results) != PGRES_COMMAND_OK)		{			psql_error("%s", PQerrorMessage(pset.db));			PQclear(results);			ResetCancelConn();			return false;		}		PQclear(results);		transaction_status = PQtransactionStatus(pset.db);	}	if (transaction_status == PQTRANS_INTRANS &&	  (rollback_str = GetVariable(pset.vars, "ON_ERROR_ROLLBACK")) != NULL &&	/* !off and !interactive is 'on' */		pg_strcasecmp(rollback_str, "off") != 0 &&		(pset.cur_cmd_interactive ||		 pg_strcasecmp(rollback_str, "interactive") != 0))	{		if (on_error_rollback_warning == false && pset.sversion < 80000)		{			fprintf(stderr, _("The server version (%d) does not support savepoints for ON_ERROR_ROLLBACK.\n"),					pset.sversion);			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;		}	}	if (pset.timing)		GETTIMEOFDAY(&before);	results = PQexec(pset.db, query);	/* these operations are included in the timing result: */	OK = (AcceptResult(results, query) && ProcessCopyResult(results));	if (pset.timing)		GETTIMEOFDAY(&after);	/* but printing results isn't: */	if (OK)		OK = PrintQueryResults(results);	PQclear(results);	/* If we made a temporary savepoint, possibly release/rollback */	if (on_error_rollback_savepoint)	{		transaction_status = PQtransactionStatus(pset.db);		/* We always rollback on an error */		if (transaction_status == PQTRANS_INERROR)			results = PQexec(pset.db, "ROLLBACK TO pg_psql_temporary_savepoint");		/* If they are no longer in a transaction, then do nothing */		else if (transaction_status != PQTRANS_INTRANS)			results = NULL;		else		{			/*			 * 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.			 */			if (strcmp(PQcmdStatus(results), "SAVEPOINT") == 0 ||				strcmp(PQcmdStatus(results), "RELEASE") == 0 ||				strcmp(PQcmdStatus(results), "ROLLBACK") == 0)				results = NULL;			else				results = PQexec(pset.db, "RELEASE pg_psql_temporary_savepoint");		}		if (PQresultStatus(results) != PGRES_COMMAND_OK)		{			psql_error("%s", PQerrorMessage(pset.db));			PQclear(results);			ResetCancelConn();			return false;		}		PQclear(results);	}	/* Possible microtiming output */	if (OK && pset.timing)		printf(_("Time: %.3f ms\n"), DIFF_MSEC(&after, &before));	/* 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));	}	PrintNotifications();	return OK;}/* * Advance the given char pointer over white space and SQL comments. */static const char *skip_white_space(const char *query){	int			cnestlevel = 0; /* slash-star comment nest level */	while (*query)	{		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.		 */		if (isspace((unsigned char) *query))			query += mblen;		else if (query[0] == '/' && query[1] == '*')		{			cnestlevel++;			query += 2;		}		else if (cnestlevel > 0 && query[0] == '*' && query[1] == '/')		{			cnestlevel--;			query += 2;		}		else if (cnestlevel == 0 && query[0] == '-' && query[1] == '-')		{			query += 2;			/*			 * We have to skip to end of line since any slash-star inside the			 * -- comment does NOT start a slash-star comment.			 */			while (*query)			{				if (*query == '\n')				{					query++;					break;				}				query += PQmblen(query, pset.encoding);			}		}		else if (cnestlevel > 0)			query += mblen;		else			break;				/* found first token */	}	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 boolcommand_no_begin(const char *query){	int			wordlen;	/*	 * First we must advance over any whitespace and comments.	 */	query = skip_white_space(query);	/*	 * Check word length (since "beginx" is not "begin").	 */	wordlen = 0;	while (isalpha((unsigned char) query[wordlen]))		wordlen += PQmblen(&query[wordlen], pset.encoding);	/*	 * 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;	if (wordlen == 5 && pg_strncasecmp(query, "begin", 5) == 0)		return true;	if (wordlen == 5 && pg_strncasecmp(query, "start", 5) == 0)		return true;	if (wordlen == 6 && pg_strncasecmp(query, "commit", 6) == 0)		return true;	if (wordlen == 3 && pg_strncasecmp(query, "end", 3) == 0)		return true;	if (wordlen == 8 && pg_strncasecmp(query, "rollback", 8) == 0)		return true;	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;	}	/*	 * 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)		return true;	if (wordlen == 7 && pg_strncasecmp(query, "cluster", 7) == 0)		return true;	/*	 * Note: these tests will match CREATE SYSTEM, DROP SYSTEM, and REINDEX	 * TABLESPACE, which aren't really valid commands so we don't care much.	 * The other six 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 == 6 && pg_strncasecmp(query, "system", 6) == 0)			return true;		if (wordlen == 10 && pg_strncasecmp(query, "tablespace", 10) == 0)			return true;	}	return false;}/* * 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". */boolis_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;}/* * 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);}/* expand_tilde * * substitute '~' with HOME or '~username' with username's home dir * */char *expand_tilde(char **filename){	if (!filename || !(*filename))		return NULL;	/*	 * 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.	 */#ifndef WIN32	/* try tilde expansion */	if (**filename == '~')	{		char	   *fn;		char		oldp,				   *p;		struct passwd *pw;		char		home[MAXPGPATH];		fn = *filename;		*home = '\0';		p = fn + 1;		while (*p != '/' && *p != '\0')			p++;		oldp = *p;		*p = '\0';		if (*(fn + 1) == '\0')			get_home_path(home);	/* ~ or ~/ only */		else if ((pw = getpwnam(fn + 1)) != NULL)			StrNCpy(home, pw->pw_dir, MAXPGPATH);		/* ~user */		*p = oldp;		if (strlen(home) != 0)		{			char	   *newfn;			newfn = pg_malloc(strlen(home) + strlen(p) + 1);			strcpy(newfn, home);			strcat(newfn, p);			free(fn);			*filename = newfn;		}	}#endif	return *filename;}

⌨️ 快捷键说明

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