1 /*-------------------------------------------------------------------------
4 * Contains functions which control the execution of the POSTGRES utility
5 * commands. At one time acted as an interface between the Lisp and C
8 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
9 * Portions Copyright (c) 1994, Regents of the University of California
13 * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.153 2002/04/30 01:26:26 tgl Exp $
15 *-------------------------------------------------------------------------
19 #include "access/heapam.h"
20 #include "catalog/catalog.h"
21 #include "catalog/namespace.h"
22 #include "catalog/pg_shadow.h"
23 #include "commands/async.h"
24 #include "commands/cluster.h"
25 #include "commands/comment.h"
26 #include "commands/copy.h"
27 #include "commands/dbcommands.h"
28 #include "commands/defrem.h"
29 #include "commands/explain.h"
30 #include "commands/lockcmds.h"
31 #include "commands/portalcmds.h"
32 #include "commands/proclang.h"
33 #include "commands/schemacmds.h"
34 #include "commands/sequence.h"
35 #include "commands/tablecmds.h"
36 #include "commands/trigger.h"
37 #include "commands/user.h"
38 #include "commands/vacuum.h"
39 #include "commands/variable.h"
40 #include "commands/view.h"
41 #include "miscadmin.h"
42 #include "nodes/makefuncs.h"
43 #include "parser/parse.h"
44 #include "parser/parse_clause.h"
45 #include "parser/parse_expr.h"
46 #include "parser/parse_type.h"
47 #include "rewrite/rewriteDefine.h"
48 #include "rewrite/rewriteRemove.h"
49 #include "tcop/utility.h"
50 #include "utils/acl.h"
51 #include "utils/lsyscache.h"
52 #include "utils/syscache.h"
53 #include "access/xlog.h"
56 * Error-checking support for DROP commands
67 static struct kindstrings kindstringarray[] = {
68 {RELKIND_RELATION, "a", "table", "TABLE"},
69 {RELKIND_SEQUENCE, "a", "sequence", "SEQUENCE"},
70 {RELKIND_VIEW, "a", "view", "VIEW"},
71 {RELKIND_INDEX, "an", "index", "INDEX"},
72 {'\0', "a", "???", "???"}
77 DropErrorMsg(char *relname, char wrongkind, char rightkind)
79 struct kindstrings *rentry;
80 struct kindstrings *wentry;
82 for (rentry = kindstringarray; rentry->kind != '\0'; rentry++)
83 if (rentry->kind == rightkind)
85 Assert(rentry->kind != '\0');
87 for (wentry = kindstringarray; wentry->kind != '\0'; wentry++)
88 if (wentry->kind == wrongkind)
90 /* wrongkind could be something we don't have in our table... */
91 if (wentry->kind != '\0')
92 elog(ERROR, "\"%s\" is not %s %s. Use DROP %s to remove %s %s",
93 relname, rentry->indef_article, rentry->name,
94 wentry->command, wentry->indef_article, wentry->name);
96 elog(ERROR, "\"%s\" is not %s %s",
97 relname, rentry->indef_article, rentry->name);
101 CheckDropPermissions(RangeVar *rel, char rightkind)
103 struct kindstrings *rentry;
106 Form_pg_class classform;
108 for (rentry = kindstringarray; rentry->kind != '\0'; rentry++)
109 if (rentry->kind == rightkind)
111 Assert(rentry->kind != '\0');
113 relOid = RangeVarGetRelid(rel, true);
114 if (!OidIsValid(relOid))
115 elog(ERROR, "%s \"%s\" does not exist", rentry->name, rel->relname);
116 tuple = SearchSysCache(RELOID,
117 ObjectIdGetDatum(relOid),
119 if (!HeapTupleIsValid(tuple))
120 elog(ERROR, "%s \"%s\" does not exist", rentry->name, rel->relname);
122 classform = (Form_pg_class) GETSTRUCT(tuple);
124 if (classform->relkind != rightkind)
125 DropErrorMsg(rel->relname, classform->relkind, rightkind);
127 /* Allow DROP to either table owner or schema owner */
128 if (!pg_class_ownercheck(relOid, GetUserId()) &&
129 !pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
130 aclcheck_error(ACLCHECK_NOT_OWNER, rel->relname);
132 if (!allowSystemTableMods && IsSystemClass(classform))
133 elog(ERROR, "%s \"%s\" is a system %s",
134 rentry->name, rel->relname, rentry->name);
136 ReleaseSysCache(tuple);
140 CheckOwnership(RangeVar *rel, bool noCatalogs)
145 relOid = RangeVarGetRelid(rel, false);
146 tuple = SearchSysCache(RELOID,
147 ObjectIdGetDatum(relOid),
149 if (!HeapTupleIsValid(tuple))
150 elog(ERROR, "Relation \"%s\" does not exist", rel->relname);
152 if (!pg_class_ownercheck(relOid, GetUserId()))
153 aclcheck_error(ACLCHECK_NOT_OWNER, rel->relname);
157 if (!allowSystemTableMods &&
158 IsSystemClass((Form_pg_class) GETSTRUCT(tuple)))
159 elog(ERROR, "relation \"%s\" is a system catalog",
163 ReleaseSysCache(tuple);
169 * general utility function invoker
171 * parsetree: the parse tree for the utility statement
172 * dest: where to send results
173 * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
174 * in which to store a command completion status string.
176 * completionTag is only set nonempty if we want to return a nondefault
177 * status (currently, only used for MOVE/FETCH).
179 * completionTag may be NULL if caller doesn't want a status string.
182 ProcessUtility(Node *parsetree,
189 completionTag[0] = '\0';
191 switch (nodeTag(parsetree))
194 * ******************************** transactions ********************************
197 case T_TransactionStmt:
199 TransactionStmt *stmt = (TransactionStmt *) parsetree;
201 switch (stmt->command)
204 BeginTransactionBlock();
208 EndTransactionBlock();
212 UserAbortTransactionBlock();
219 * ******************************** portal manipulation ********************************
222 case T_ClosePortalStmt:
224 ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
226 PerformPortalClose(stmt->portalname, dest);
232 FetchStmt *stmt = (FetchStmt *) parsetree;
233 char *portalName = stmt->portalname;
239 forward = (bool) (stmt->direction == FORWARD);
242 * parser ensures that count is >= 0 and 'fetch ALL' -> 0
245 count = stmt->howMany;
246 PerformPortalFetch(portalName, forward, count,
247 (stmt->ismove) ? None : dest,
253 * relation and attribute manipulation
255 case T_CreateSchemaStmt:
257 CreateSchemaStmt *stmt = (CreateSchemaStmt *) parsetree;
259 CreateSchemaCommand(stmt);
267 relOid = DefineRelation((CreateStmt *) parsetree,
271 * Let AlterTableCreateToastTable decide if this one needs a
272 * secondary relation too.
274 CommandCounterIncrement();
275 AlterTableCreateToastTable(relOid, true);
281 DropStmt *stmt = (DropStmt *) parsetree;
284 foreach(arg, stmt->objects)
286 List *names = (List *) lfirst(arg);
289 switch (stmt->removeType)
292 rel = makeRangeVarFromNameList(names);
293 CheckDropPermissions(rel, RELKIND_RELATION);
298 rel = makeRangeVarFromNameList(names);
299 CheckDropPermissions(rel, RELKIND_SEQUENCE);
304 rel = makeRangeVarFromNameList(names);
305 CheckDropPermissions(rel, RELKIND_VIEW);
310 rel = makeRangeVarFromNameList(names);
311 CheckDropPermissions(rel, RELKIND_INDEX);
316 /* RemoveType does its own permissions checks */
321 /* RemoveDomain does its own permissions checks */
322 RemoveDomain(names, stmt->behavior);
327 * Make sure subsequent loop iterations will see
328 * results of this one; needed if removing multiple
329 * rules for same table, for example.
331 CommandCounterIncrement();
338 TruncateStmt *stmt = (TruncateStmt *) parsetree;
340 TruncateRelation(stmt->relation);
345 CommentObject((CommentStmt *) parsetree);
350 CopyStmt *stmt = (CopyStmt *) parsetree;
352 if (stmt->direction != FROM)
355 DoCopy(stmt->relation,
358 (bool) (stmt->direction == FROM),
359 (bool) (stmt->filename == NULL),
362 * null filename means copy to/from stdout/stdin, rather
363 * than to/from a file.
376 RenameStmt *stmt = (RenameStmt *) parsetree;
379 CheckOwnership(stmt->relation, true);
381 relid = RangeVarGetRelid(stmt->relation, false);
383 switch (stmt->renameType)
388 * RENAME TABLE requires that we (still) hold CREATE
389 * rights on the containing namespace, as well as
390 * ownership of the table. But skip check for
393 Oid namespaceId = get_rel_namespace(relid);
395 if (!isTempNamespace(namespaceId))
399 aclresult = pg_namespace_aclcheck(namespaceId,
402 if (aclresult != ACLCHECK_OK)
403 aclcheck_error(aclresult,
404 get_namespace_name(namespaceId));
407 renamerel(relid, stmt->newname);
412 stmt->oldname, /* old att name */
413 stmt->newname, /* new att name */
414 interpretInhOption(stmt->relation->inhOpt)); /* recursive? */
418 stmt->oldname, /* old att name */
419 stmt->newname); /* new att name */
422 elog(ERROR, "ProcessUtility: Invalid target for RENAME: %d",
426 elog(ERROR, "ProcessUtility: Invalid target for RENAME: %d",
432 /* various Alter Table forms */
434 case T_AlterTableStmt:
436 AlterTableStmt *stmt = (AlterTableStmt *) parsetree;
439 relid = RangeVarGetRelid(stmt->relation, false);
442 * Some or all of these functions are recursive to cover
443 * inherited things, so permission checks are done there.
445 switch (stmt->subtype)
447 case 'A': /* ADD COLUMN */
449 * Recursively add column to table and,
450 * if requested, to descendants
452 AlterTableAddColumn(relid,
453 interpretInhOption(stmt->relation->inhOpt),
454 (ColumnDef *) stmt->def);
456 case 'T': /* ALTER COLUMN DEFAULT */
458 * Recursively alter column default for table and,
459 * if requested, for descendants
461 AlterTableAlterColumnDefault(relid,
462 interpretInhOption(stmt->relation->inhOpt),
466 case 'N': /* ALTER COLUMN DROP NOT NULL */
467 AlterTableAlterColumnDropNotNull(relid,
468 interpretInhOption(stmt->relation->inhOpt),
471 case 'O': /* ALTER COLUMN SET NOT NULL */
472 AlterTableAlterColumnSetNotNull(relid,
473 interpretInhOption(stmt->relation->inhOpt),
476 case 'S': /* ALTER COLUMN STATISTICS */
477 case 'M': /* ALTER COLUMN STORAGE */
479 * Recursively alter column statistics for table and,
480 * if requested, for descendants
482 AlterTableAlterColumnFlags(relid,
483 interpretInhOption(stmt->relation->inhOpt),
488 case 'D': /* DROP COLUMN */
490 * XXX We don't actually recurse yet, but what we should do would be:
491 * Recursively drop column from table and,
492 * if requested, from descendants
494 AlterTableDropColumn(relid,
495 interpretInhOption(stmt->relation->inhOpt),
499 case 'C': /* ADD CONSTRAINT */
501 * Recursively add constraint to table and,
502 * if requested, to descendants
504 AlterTableAddConstraint(relid,
505 interpretInhOption(stmt->relation->inhOpt),
508 case 'X': /* DROP CONSTRAINT */
510 * Recursively drop constraint from table and,
511 * if requested, from descendants
513 AlterTableDropConstraint(relid,
514 interpretInhOption(stmt->relation->inhOpt),
518 case 'E': /* CREATE TOAST TABLE */
519 AlterTableCreateToastTable(relid, false);
521 case 'U': /* ALTER OWNER */
522 /* check that we are the superuser */
524 elog(ERROR, "ALTER TABLE: permission denied");
525 /* get_usesysid raises an error if no such user */
526 AlterTableOwner(relid,
527 get_usesysid(stmt->name));
530 elog(ERROR, "T_AlterTableStmt: unknown subtype");
539 GrantStmt *stmt = (GrantStmt *) parsetree;
541 ExecuteGrantStmt(stmt);
546 * ******************************** object creation /
547 * destruction ********************************
552 DefineStmt *stmt = (DefineStmt *) parsetree;
554 switch (stmt->defType)
557 DefineOperator(stmt->defnames, stmt->definition);
560 DefineType(stmt->defnames, stmt->definition);
563 DefineAggregate(stmt->defnames, stmt->definition);
569 case T_ViewStmt: /* CREATE VIEW */
571 ViewStmt *stmt = (ViewStmt *) parsetree;
573 DefineView(stmt->view, stmt->query);
577 case T_ProcedureStmt: /* CREATE FUNCTION */
578 CreateFunction((ProcedureStmt *) parsetree);
581 case T_IndexStmt: /* CREATE INDEX */
583 IndexStmt *stmt = (IndexStmt *) parsetree;
585 CheckOwnership(stmt->relation, true);
587 DefineIndex(stmt->relation, /* relation */
588 stmt->idxname, /* index name */
589 stmt->accessMethod, /* am name */
590 stmt->indexParams, /* parameters */
593 (Expr *) stmt->whereClause,
598 case T_RuleStmt: /* CREATE RULE */
599 DefineQueryRewrite((RuleStmt *) parsetree);
602 case T_CreateSeqStmt:
603 DefineSequence((CreateSeqStmt *) parsetree);
606 case T_RemoveAggrStmt:
608 RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
610 RemoveAggregate(stmt->aggname, stmt->aggtype);
614 case T_RemoveFuncStmt:
616 RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;
618 RemoveFunction(stmt->funcname, stmt->args);
622 case T_RemoveOperStmt:
624 RemoveOperStmt *stmt = (RemoveOperStmt *) parsetree;
625 TypeName *typenode1 = (TypeName *) lfirst(stmt->args);
626 TypeName *typenode2 = (TypeName *) lsecond(stmt->args);
628 RemoveOperator(stmt->opname, typenode1, typenode2);
634 CreatedbStmt *stmt = (CreatedbStmt *) parsetree;
636 createdb(stmt->dbname, stmt->dbowner,
637 stmt->dbpath, stmt->dbtemplate,
642 case T_AlterDatabaseSetStmt:
643 AlterDatabaseSet((AlterDatabaseSetStmt *)parsetree);
648 DropdbStmt *stmt = (DropdbStmt *) parsetree;
650 dropdb(stmt->dbname);
654 /* Query-level asynchronous notification */
657 NotifyStmt *stmt = (NotifyStmt *) parsetree;
659 Async_Notify(stmt->relation->relname);
665 ListenStmt *stmt = (ListenStmt *) parsetree;
667 Async_Listen(stmt->relation->relname, MyProcPid);
673 UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
675 Async_Unlisten(stmt->relation->relname, MyProcPid);
681 LoadStmt *stmt = (LoadStmt *) parsetree;
683 closeAllVfds(); /* probably not necessary... */
684 load_file(stmt->filename);
690 ClusterStmt *stmt = (ClusterStmt *) parsetree;
692 CheckOwnership(stmt->relation, true);
694 cluster(stmt->relation, stmt->indexname);
699 vacuum((VacuumStmt *) parsetree);
703 ExplainQuery((ExplainStmt *) parsetree, dest);
710 RecipeStmt *stmt = (RecipeStmt *) parsetree;
717 case T_VariableSetStmt:
719 VariableSetStmt *n = (VariableSetStmt *) parsetree;
721 SetPGVariable(n->name, n->args);
725 case T_VariableShowStmt:
727 VariableShowStmt *n = (VariableShowStmt *) parsetree;
729 GetPGVariable(n->name);
733 case T_VariableResetStmt:
735 VariableResetStmt *n = (VariableResetStmt *) parsetree;
737 ResetPGVariable(n->name);
741 case T_CreateTrigStmt:
742 CreateTrigger((CreateTrigStmt *) parsetree);
745 case T_DropPropertyStmt:
747 DropPropertyStmt *stmt = (DropPropertyStmt *) parsetree;
750 relId = RangeVarGetRelid(stmt->relation, false);
752 switch (stmt->removeType)
755 /* RemoveRewriteRule checks permissions */
756 RemoveRewriteRule(relId, stmt->property);
759 /* DropTrigger checks permissions */
760 DropTrigger(relId, stmt->property);
766 case T_CreatePLangStmt:
767 CreateProceduralLanguage((CreatePLangStmt *) parsetree);
770 case T_DropPLangStmt:
771 DropProceduralLanguage((DropPLangStmt *) parsetree);
775 * ******************************** DOMAIN statements ****
777 case T_CreateDomainStmt:
778 DefineDomain((CreateDomainStmt *) parsetree);
782 * ******************************** USER statements ****
784 case T_CreateUserStmt:
785 CreateUser((CreateUserStmt *) parsetree);
788 case T_AlterUserStmt:
789 AlterUser((AlterUserStmt *) parsetree);
792 case T_AlterUserSetStmt:
793 AlterUserSet((AlterUserSetStmt *) parsetree);
797 DropUser((DropUserStmt *) parsetree);
801 LockTableCommand((LockStmt *) parsetree);
804 case T_ConstraintsSetStmt:
805 DeferredTriggerSetState((ConstraintsSetStmt *) parsetree);
808 case T_CreateGroupStmt:
809 CreateGroup((CreateGroupStmt *) parsetree);
812 case T_AlterGroupStmt:
813 AlterGroup((AlterGroupStmt *) parsetree, "ALTER GROUP");
816 case T_DropGroupStmt:
817 DropGroup((DropGroupStmt *) parsetree);
820 case T_CheckPointStmt:
823 elog(ERROR, "permission denied");
824 CreateCheckPoint(false);
830 ReindexStmt *stmt = (ReindexStmt *) parsetree;
832 switch (stmt->reindexType)
835 relname = (char *) stmt->relation->relname;
836 CheckOwnership(stmt->relation, false);
837 ReindexIndex(stmt->relation, stmt->force);
840 CheckOwnership(stmt->relation, false);
841 ReindexTable(stmt->relation, stmt->force);
844 relname = (char *) stmt->name;
845 ReindexDatabase(relname, stmt->force, false);
853 elog(ERROR, "ProcessUtility: command #%d unsupported",