📄 utility.c
字号:
/*------------------------------------------------------------------------- * * 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-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.245 2005/10/15 02:49:27 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "access/twophase.h"#include "catalog/catalog.h"#include "catalog/namespace.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/tablespace.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_expr.h"#include "parser/parse_type.h"#include "postmaster/bgwriter.h"#include "rewrite/rewriteDefine.h"#include "rewrite/rewriteRemove.h"#include "storage/fd.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"/* * 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}};/* * Emit the right error message for a "DROP" command issued on a * relation of the wrong type */static voidDropErrorMsgWrongType(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));}/* * Emit the right error message for a "DROP" command issued on a * non-existent relation */static voidDropErrorMsgNonExistent(RangeVar *rel, char rightkind){ const struct msgstrings *rentry; for (rentry = msgstringarray; rentry->kind != '\0'; rentry++) { if (rentry->kind == rightkind) ereport(ERROR, (errcode(rentry->nonexistent_code), errmsg(rentry->nonexistent_msg, rel->relname))); } Assert(false); /* Should be impossible */}static voidCheckDropPermissions(RangeVar *rel, char rightkind){ Oid relOid; HeapTuple tuple; Form_pg_class classform; relOid = RangeVarGetRelid(rel, true); if (!OidIsValid(relOid)) DropErrorMsgNonExistent(rel, rightkind); 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) DropErrorMsgWrongType(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);}/* * QueryIsReadOnly: is an analyzed/rewritten query read-only? * * This is a much stricter test than we apply for XactReadOnly mode; * the query must be *in truth* read-only, because the caller wishes * not to do CommandCounterIncrement for it. */boolQueryIsReadOnly(Query *parsetree){ switch (parsetree->commandType) { case CMD_SELECT: if (parsetree->into != NULL) return false; /* SELECT INTO */ else if (parsetree->rowMarks != NIL) return false; /* SELECT FOR UPDATE/SHARE */ else return true; case CMD_UPDATE: case CMD_INSERT: case CMD_DELETE: return false; case CMD_UTILITY: /* For now, treat all utility commands as read/write */ return false; default: elog(WARNING, "unrecognized commandType: %d", (int) parsetree->commandType); break; } return false;}/* * check_xact_readonly: is a utility command read-only? * * Here we use the loose rules of XactReadOnly mode: no permanent effects * on the database are allowed. */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_AlterDatabaseStmt: case T_AlterDatabaseSetStmt: case T_AlterDomainStmt: case T_AlterFunctionStmt: case T_AlterRoleStmt: case T_AlterRoleSetStmt: case T_AlterObjectSchemaStmt: case T_AlterOwnerStmt: case T_AlterSeqStmt: case T_AlterTableStmt: case T_RenameStmt: case T_CommentStmt: case T_DefineStmt: case T_CreateCastStmt: case T_CreateConversionStmt: case T_CreatedbStmt: case T_CreateDomainStmt: case T_CreateFunctionStmt: case T_CreateRoleStmt: case T_IndexStmt: case T_CreatePLangStmt: case T_CreateOpClassStmt: case T_RuleStmt: case T_CreateSchemaStmt: case T_CreateSeqStmt: case T_CreateStmt: case T_CreateTableSpaceStmt: case T_CreateTrigStmt: case T_CompositeTypeStmt: case T_ViewStmt: case T_RemoveAggrStmt: case T_DropCastStmt: case T_DropStmt: case T_DropdbStmt: case T_DropTableSpaceStmt: case T_RemoveFuncStmt: case T_DropRoleStmt: case T_DropPLangStmt: case T_RemoveOperStmt: case T_RemoveOpClassStmt: case T_DropPropertyStmt: case T_GrantStmt: case T_GrantRoleStmt: 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 * params: parameters to use during execution (currently only used by DECLARE) * 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. * * completionTag may be NULL if caller doesn't want a status string. */voidProcessUtility(Node *parsetree, ParamListInfo params, 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) { /* * START TRANSACTION, as defined by SQL99: Identical * to BEGIN. Same code for both. */ case TRANS_STMT_BEGIN: case TRANS_STMT_START: { ListCell *lc; BeginTransactionBlock(); foreach(lc, stmt->options) { DefElem *item = (DefElem *) lfirst(lc); if (strcmp(item->defname, "transaction_isolation") == 0) SetPGVariable("transaction_isolation", list_make1(item->arg), true); else if (strcmp(item->defname, "transaction_read_only") == 0) SetPGVariable("transaction_read_only", list_make1(item->arg), true); } } break; case TRANS_STMT_COMMIT: if (!EndTransactionBlock()) { /* report unsuccessful commit in completionTag */ if (completionTag) strcpy(completionTag, "ROLLBACK"); } break; case TRANS_STMT_PREPARE: if (!PrepareTransactionBlock(stmt->gid)) { /* report unsuccessful commit in completionTag */ if (completionTag) strcpy(completionTag, "ROLLBACK"); } break; case TRANS_STMT_COMMIT_PREPARED: PreventTransactionChain(stmt, "COMMIT PREPARED"); FinishPreparedTransaction(stmt->gid, true); break; case TRANS_STMT_ROLLBACK_PREPARED: PreventTransactionChain(stmt, "ROLLBACK PREPARED"); FinishPreparedTransaction(stmt->gid, false); break; case TRANS_STMT_ROLLBACK: UserAbortTransactionBlock(); break; case TRANS_STMT_SAVEPOINT: { ListCell *cell; char *name = NULL; RequireTransactionChain((void *) stmt, "SAVEPOINT"); foreach(cell, stmt->options) { DefElem *elem = lfirst(cell); if (strcmp(elem->defname, "savepoint_name") == 0) name = strVal(elem->arg); } Assert(PointerIsValid(name)); DefineSavepoint(name); } break; case TRANS_STMT_RELEASE: RequireTransactionChain((void *) stmt, "RELEASE SAVEPOINT"); ReleaseSavepoint(stmt->options); break; case TRANS_STMT_ROLLBACK_TO: RequireTransactionChain((void *) stmt, "ROLLBACK TO SAVEPOINT"); RollbackToSavepoint(stmt->options); /* * CommitTransactionCommand is in charge of * re-defining the savepoint again */ break; } } break; /* * Portal (cursor) manipulation */ case T_DeclareCursorStmt: PerformCursorOpen((DeclareCursorStmt *) parsetree, params); 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_CreateTableSpaceStmt: CreateTableSpace((CreateTableSpaceStmt *) parsetree); break; case T_DropTableSpaceStmt: DropTableSpace((DropTableSpaceStmt *) parsetree); break; case T_DropStmt: { DropStmt *stmt = (DropStmt *) parsetree; ListCell *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,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -