utility.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,566 行 · 第 1/3 页

C
1,566
字号
/*------------------------------------------------------------------------- * * utility.c *	  Contains functions which control the execution of the POSTGRES utility *	  commands.  At one time acted as an interface between the Lisp and C *	  systems. * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.208 2003/10/02 06:34:04 petere Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "catalog/catalog.h"#include "catalog/namespace.h"#include "catalog/pg_shadow.h"#include "commands/alter.h"#include "commands/async.h"#include "commands/cluster.h"#include "commands/comment.h"#include "commands/copy.h"#include "commands/conversioncmds.h"#include "commands/dbcommands.h"#include "commands/defrem.h"#include "commands/explain.h"#include "commands/lockcmds.h"#include "commands/portalcmds.h"#include "commands/prepare.h"#include "commands/proclang.h"#include "commands/schemacmds.h"#include "commands/sequence.h"#include "commands/tablecmds.h"#include "commands/trigger.h"#include "commands/typecmds.h"#include "commands/user.h"#include "commands/vacuum.h"#include "commands/view.h"#include "miscadmin.h"#include "nodes/makefuncs.h"#include "parser/parse_clause.h"#include "parser/parse_expr.h"#include "parser/parse_type.h"#include "rewrite/rewriteDefine.h"#include "rewrite/rewriteRemove.h"#include "tcop/pquery.h"#include "tcop/utility.h"#include "utils/acl.h"#include "utils/guc.h"#include "utils/lsyscache.h"#include "utils/syscache.h"#include "access/xlog.h"/* * Error-checking support for DROP commands */struct msgstrings{	char		kind;	int			nonexistent_code;	const char *nonexistent_msg;	const char *nota_msg;	const char *drophint_msg;};static const struct msgstrings msgstringarray[] = {	{RELKIND_RELATION,		ERRCODE_UNDEFINED_TABLE,		gettext_noop("table \"%s\" does not exist"),		gettext_noop("\"%s\" is not a table"),	gettext_noop("Use DROP TABLE to remove a table.")},	{RELKIND_SEQUENCE,		ERRCODE_UNDEFINED_TABLE,		gettext_noop("sequence \"%s\" does not exist"),		gettext_noop("\"%s\" is not a sequence"),	gettext_noop("Use DROP SEQUENCE to remove a sequence.")},	{RELKIND_VIEW,		ERRCODE_UNDEFINED_TABLE,		gettext_noop("view \"%s\" does not exist"),		gettext_noop("\"%s\" is not a view"),	gettext_noop("Use DROP VIEW to remove a view.")},	{RELKIND_INDEX,		ERRCODE_UNDEFINED_OBJECT,		gettext_noop("index \"%s\" does not exist"),		gettext_noop("\"%s\" is not an index"),	gettext_noop("Use DROP INDEX to remove an index.")},	{RELKIND_COMPOSITE_TYPE,		ERRCODE_UNDEFINED_OBJECT,		gettext_noop("type \"%s\" does not exist"),		gettext_noop("\"%s\" is not a type"),	gettext_noop("Use DROP TYPE to remove a type.")},	{'\0', 0, NULL, NULL, NULL}};static voidDropErrorMsg(char *relname, char wrongkind, char rightkind){	const struct msgstrings *rentry;	const struct msgstrings *wentry;	for (rentry = msgstringarray; rentry->kind != '\0'; rentry++)		if (rentry->kind == rightkind)			break;	Assert(rentry->kind != '\0');	for (wentry = msgstringarray; wentry->kind != '\0'; wentry++)		if (wentry->kind == wrongkind)			break;	/* wrongkind could be something we don't have in our table... */	ereport(ERROR,			(errcode(ERRCODE_WRONG_OBJECT_TYPE),			 errmsg(rentry->nota_msg, relname),			 (wentry->kind != '\0') ? errhint(wentry->drophint_msg) : 0));}static voidCheckDropPermissions(RangeVar *rel, char rightkind){	const struct msgstrings *rentry;	Oid			relOid;	HeapTuple	tuple;	Form_pg_class classform;	for (rentry = msgstringarray; rentry->kind != '\0'; rentry++)		if (rentry->kind == rightkind)			break;	Assert(rentry->kind != '\0');	relOid = RangeVarGetRelid(rel, true);	if (!OidIsValid(relOid))		ereport(ERROR,				(errcode(rentry->nonexistent_code),				 errmsg(rentry->nonexistent_msg, rel->relname)));	tuple = SearchSysCache(RELOID,						   ObjectIdGetDatum(relOid),						   0, 0, 0);	if (!HeapTupleIsValid(tuple))		elog(ERROR, "cache lookup failed for relation %u", relOid);	classform = (Form_pg_class) GETSTRUCT(tuple);	if (classform->relkind != rightkind)		DropErrorMsg(rel->relname, classform->relkind, rightkind);	/* Allow DROP to either table owner or schema owner */	if (!pg_class_ownercheck(relOid, GetUserId()) &&		!pg_namespace_ownercheck(classform->relnamespace, GetUserId()))		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,					   rel->relname);	if (!allowSystemTableMods && IsSystemClass(classform))		ereport(ERROR,				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),				 errmsg("permission denied: \"%s\" is a system catalog",						rel->relname)));	ReleaseSysCache(tuple);}/* * Verify user has ownership of specified relation, else ereport. * * If noCatalogs is true then we also deny access to system catalogs, * except when allowSystemTableMods is true. */voidCheckRelationOwnership(RangeVar *rel, bool noCatalogs){	Oid			relOid;	HeapTuple	tuple;	relOid = RangeVarGetRelid(rel, false);	tuple = SearchSysCache(RELOID,						   ObjectIdGetDatum(relOid),						   0, 0, 0);	if (!HeapTupleIsValid(tuple))		/* should not happen */		elog(ERROR, "cache lookup failed for relation %u", relOid);	if (!pg_class_ownercheck(relOid, GetUserId()))		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,					   rel->relname);	if (noCatalogs)	{		if (!allowSystemTableMods &&			IsSystemClass((Form_pg_class) GETSTRUCT(tuple)))			ereport(ERROR,					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),				  errmsg("permission denied: \"%s\" is a system catalog",						 rel->relname)));	}	ReleaseSysCache(tuple);}static voidcheck_xact_readonly(Node *parsetree){	if (!XactReadOnly)		return;	/*	 * Note: Commands that need to do more complicated checking are	 * handled elsewhere.	 */	switch (nodeTag(parsetree))	{		case T_AlterDatabaseSetStmt:		case T_AlterDomainStmt:		case T_AlterGroupStmt:		case T_AlterSeqStmt:		case T_AlterTableStmt:		case T_RenameStmt:		case T_AlterUserStmt:		case T_AlterUserSetStmt:		case T_CommentStmt:		case T_DefineStmt:		case T_CreateCastStmt:		case T_CreateConversionStmt:		case T_CreatedbStmt:		case T_CreateDomainStmt:		case T_CreateFunctionStmt:		case T_CreateGroupStmt:		case T_IndexStmt:		case T_CreatePLangStmt:		case T_CreateOpClassStmt:		case T_RuleStmt:		case T_CreateSchemaStmt:		case T_CreateSeqStmt:		case T_CreateStmt:		case T_CreateTrigStmt:		case T_CompositeTypeStmt:		case T_CreateUserStmt:		case T_ViewStmt:		case T_RemoveAggrStmt:		case T_DropCastStmt:		case T_DropStmt:		case T_DropdbStmt:		case T_RemoveFuncStmt:		case T_DropGroupStmt:		case T_DropPLangStmt:		case T_RemoveOperStmt:		case T_RemoveOpClassStmt:		case T_DropPropertyStmt:		case T_DropUserStmt:		case T_GrantStmt:		case T_TruncateStmt:			ereport(ERROR,					(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),					 errmsg("transaction is read-only")));			break;		default:			/* do nothing */			break;	}}/* * ProcessUtility *		general utility function invoker * *	parsetree: the parse tree for the utility statement *	dest: where to send results *	completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE *		in which to store a command completion status string. * * completionTag is only set nonempty if we want to return a nondefault * status (currently, only used for MOVE/FETCH). * * completionTag may be NULL if caller doesn't want a status string. */voidProcessUtility(Node *parsetree,			   DestReceiver *dest,			   char *completionTag){	check_xact_readonly(parsetree);	if (completionTag)		completionTag[0] = '\0';	switch (nodeTag(parsetree))	{			/*			 * ******************** transactions ********************			 */		case T_TransactionStmt:			{				TransactionStmt *stmt = (TransactionStmt *) parsetree;				switch (stmt->kind)				{					case TRANS_STMT_BEGIN:						BeginTransactionBlock();						break;						/*						 * START TRANSACTION, as defined by SQL99:						 * Identical to BEGIN, except that it takes a few						 * additional options.						 */					case TRANS_STMT_START:						{							BeginTransactionBlock();							if (stmt->options)							{								List	   *head;								foreach(head, stmt->options)								{									DefElem    *item = (DefElem *) lfirst(head);									if (strcmp(item->defname, "transaction_isolation") == 0)										SetPGVariable("transaction_isolation",											makeList1(item->arg), false);									else if (strcmp(item->defname, "transaction_read_only") == 0)										SetPGVariable("transaction_read_only",											makeList1(item->arg), false);								}							}						}						break;					case TRANS_STMT_COMMIT:						EndTransactionBlock();						break;					case TRANS_STMT_ROLLBACK:						UserAbortTransactionBlock();						break;				}			}			break;			/*			 * Portal (cursor) manipulation			 */		case T_DeclareCursorStmt:			PerformCursorOpen((DeclareCursorStmt *) parsetree);			break;		case T_ClosePortalStmt:			{				ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;				PerformPortalClose(stmt->portalname);			}			break;		case T_FetchStmt:			PerformPortalFetch((FetchStmt *) parsetree, dest,							   completionTag);			break;			/*			 * relation and attribute manipulation			 */		case T_CreateSchemaStmt:			CreateSchemaCommand((CreateSchemaStmt *) parsetree);			break;		case T_CreateStmt:			{				Oid			relOid;				relOid = DefineRelation((CreateStmt *) parsetree,										RELKIND_RELATION);				/*				 * Let AlterTableCreateToastTable decide if this one needs				 * a secondary relation too.				 */				CommandCounterIncrement();				AlterTableCreateToastTable(relOid, true);			}			break;		case T_DropStmt:			{				DropStmt   *stmt = (DropStmt *) parsetree;				List	   *arg;				foreach(arg, stmt->objects)				{					List	   *names = (List *) lfirst(arg);					RangeVar   *rel;					switch (stmt->removeType)					{						case OBJECT_TABLE:							rel = makeRangeVarFromNameList(names);							CheckDropPermissions(rel, RELKIND_RELATION);							RemoveRelation(rel, stmt->behavior);							break;						case OBJECT_SEQUENCE:							rel = makeRangeVarFromNameList(names);							CheckDropPermissions(rel, RELKIND_SEQUENCE);							RemoveRelation(rel, stmt->behavior);							break;						case OBJECT_VIEW:							rel = makeRangeVarFromNameList(names);							CheckDropPermissions(rel, RELKIND_VIEW);							RemoveView(rel, stmt->behavior);							break;						case OBJECT_INDEX:							rel = makeRangeVarFromNameList(names);							CheckDropPermissions(rel, RELKIND_INDEX);							RemoveIndex(rel, stmt->behavior);							break;						case OBJECT_TYPE:							/* RemoveType does its own permissions checks */							RemoveType(names, stmt->behavior);							break;						case OBJECT_DOMAIN:							/*							 * RemoveDomain does its own permissions							 * checks							 */							RemoveDomain(names, stmt->behavior);							break;						case OBJECT_CONVERSION:							DropConversionCommand(names, stmt->behavior);							break;						case OBJECT_SCHEMA:							/*							 * RemoveSchema does its own permissions							 * checks							 */							RemoveSchema(names, stmt->behavior);							break;						default:							elog(ERROR, "unrecognized drop object type: %d",								 (int) stmt->removeType);							break;					}					/*					 * We used to need to do CommandCounterIncrement()					 * here, but now it's done inside performDeletion().					 */				}			}			break;		case T_TruncateStmt:			{				TruncateStmt *stmt = (TruncateStmt *) parsetree;				TruncateRelation(stmt->relation);			}			break;		case T_CommentStmt:			CommentObject((CommentStmt *) parsetree);			break;		case T_CopyStmt:			DoCopy((CopyStmt *) parsetree);			break;		case T_PrepareStmt:			PrepareQuery((PrepareStmt *) parsetree);			break;		case T_ExecuteStmt:			ExecuteQuery((ExecuteStmt *) parsetree, dest);			break;		case T_DeallocateStmt:			DeallocateQuery((DeallocateStmt *) parsetree);			break;			/*			 * schema			 */		case T_RenameStmt:			ExecRenameStmt((RenameStmt *) parsetree);			break;			/* various Alter Table forms */		case T_AlterTableStmt:			{				AlterTableStmt *stmt = (AlterTableStmt *) parsetree;				Oid			relid;				relid = RangeVarGetRelid(stmt->relation, false);				/*				 * Some or all of these functions are recursive to cover				 * inherited things, so permission checks are done there.				 */				switch (stmt->subtype)				{					case 'A':	/* ADD COLUMN */						/*						 * Recursively add column to table and, if						 * requested, to descendants						 */

⌨️ 快捷键说明

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