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.123 2001/11/20 02:46:13 tgl Exp $
15 *-------------------------------------------------------------------------
19 #include "access/heapam.h"
20 #include "catalog/catalog.h"
21 #include "catalog/pg_shadow.h"
22 #include "commands/async.h"
23 #include "commands/cluster.h"
24 #include "commands/command.h"
25 #include "commands/comment.h"
26 #include "commands/copy.h"
27 #include "commands/creatinh.h"
28 #include "commands/dbcommands.h"
29 #include "commands/defrem.h"
30 #include "commands/explain.h"
31 #include "commands/proclang.h"
32 #include "commands/rename.h"
33 #include "commands/sequence.h"
34 #include "commands/trigger.h"
35 #include "commands/user.h"
36 #include "commands/vacuum.h"
37 #include "commands/variable.h"
38 #include "commands/view.h"
39 #include "miscadmin.h"
40 #include "parser/parse.h"
41 #include "parser/parse_clause.h"
42 #include "parser/parse_expr.h"
43 #include "rewrite/rewriteDefine.h"
44 #include "rewrite/rewriteRemove.h"
45 #include "tcop/utility.h"
46 #include "utils/acl.h"
47 #include "utils/ps_status.h"
48 #include "utils/syscache.h"
49 #include "utils/temprel.h"
50 #include "access/xlog.h"
53 * Error-checking support for DROP commands
64 static struct kindstrings kindstringarray[] = {
65 {RELKIND_RELATION, "a", "table", "TABLE"},
66 {RELKIND_SEQUENCE, "a", "sequence", "SEQUENCE"},
67 {RELKIND_VIEW, "a", "view", "VIEW"},
68 {RELKIND_INDEX, "an", "index", "INDEX"},
69 {'\0', "a", "???", "???"}
74 DropErrorMsg(char *relname, char wrongkind, char rightkind)
76 struct kindstrings *rentry;
77 struct kindstrings *wentry;
79 for (rentry = kindstringarray; rentry->kind != '\0'; rentry++)
80 if (rentry->kind == rightkind)
82 Assert(rentry->kind != '\0');
84 for (wentry = kindstringarray; wentry->kind != '\0'; wentry++)
85 if (wentry->kind == wrongkind)
87 /* wrongkind could be something we don't have in our table... */
88 if (wentry->kind != '\0')
89 elog(ERROR, "\"%s\" is not %s %s. Use DROP %s to remove %s %s",
90 relname, rentry->indef_article, rentry->name,
91 wentry->command, wentry->indef_article, wentry->name);
93 elog(ERROR, "\"%s\" is not %s %s",
94 relname, rentry->indef_article, rentry->name);
98 CheckDropPermissions(char *name, char rightkind)
100 struct kindstrings *rentry;
102 Form_pg_class classform;
104 for (rentry = kindstringarray; rentry->kind != '\0'; rentry++)
105 if (rentry->kind == rightkind)
107 Assert(rentry->kind != '\0');
109 tuple = SearchSysCache(RELNAME,
110 PointerGetDatum(name),
112 if (!HeapTupleIsValid(tuple))
113 elog(ERROR, "%s \"%s\" does not exist", rentry->name, name);
115 classform = (Form_pg_class) GETSTRUCT(tuple);
117 if (classform->relkind != rightkind)
118 DropErrorMsg(name, classform->relkind, rightkind);
120 if (!pg_ownercheck(GetUserId(), name, RELNAME))
121 elog(ERROR, "you do not own %s \"%s\"",
124 if (!allowSystemTableMods && IsSystemRelationName(name) &&
125 !is_temp_relname(name))
126 elog(ERROR, "%s \"%s\" is a system %s",
127 rentry->name, name, rentry->name);
129 ReleaseSysCache(tuple);
134 * general utility function invoker
138 ProcessUtility(Node *parsetree,
141 char *commandTag = NULL;
145 switch (nodeTag(parsetree))
148 * ******************************** transactions ********************************
151 case T_TransactionStmt:
153 TransactionStmt *stmt = (TransactionStmt *) parsetree;
155 switch (stmt->command)
158 set_ps_display(commandTag = "BEGIN");
159 BeginTransactionBlock();
163 set_ps_display(commandTag = "COMMIT");
164 EndTransactionBlock();
168 set_ps_display(commandTag = "ROLLBACK");
169 UserAbortTransactionBlock();
176 * ******************************** portal manipulation ********************************
179 case T_ClosePortalStmt:
181 ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
183 set_ps_display(commandTag = "CLOSE");
185 PerformPortalClose(stmt->portalname, dest);
191 FetchStmt *stmt = (FetchStmt *) parsetree;
192 char *portalName = stmt->portalname;
196 set_ps_display(commandTag = (stmt->ismove) ? "MOVE" : "FETCH");
200 forward = (bool) (stmt->direction == FORWARD);
203 * parser ensures that count is >= 0 and 'fetch ALL' -> 0
206 count = stmt->howMany;
207 PerformPortalFetch(portalName, forward, count, commandTag,
208 (stmt->ismove) ? None : dest); /* /dev/null for MOVE */
213 * ******************************** relation and attribute
214 * manipulation ********************************
218 set_ps_display(commandTag = "CREATE");
220 DefineRelation((CreateStmt *) parsetree, RELKIND_RELATION);
223 * Let AlterTableCreateToastTable decide if this one needs a
224 * secondary relation too.
226 CommandCounterIncrement();
227 AlterTableCreateToastTable(((CreateStmt *) parsetree)->relname,
233 DropStmt *stmt = (DropStmt *) parsetree;
234 List *args = stmt->names;
237 set_ps_display(commandTag = "DROP");
241 relname = strVal(lfirst(arg));
243 switch (stmt->removeType)
246 CheckDropPermissions(relname, RELKIND_RELATION);
247 RemoveRelation(relname);
251 CheckDropPermissions(relname, RELKIND_SEQUENCE);
252 RemoveRelation(relname);
256 CheckDropPermissions(relname, RELKIND_VIEW);
261 CheckDropPermissions(relname, RELKIND_INDEX);
262 RemoveIndex(relname);
267 char *rulename = relname;
270 relationName = RewriteGetRuleEventRel(rulename);
271 aclcheck_result = pg_aclcheck(relationName, GetUserId(), ACL_RULE);
272 if (aclcheck_result != ACLCHECK_OK)
273 elog(ERROR, "%s: %s", relationName,
274 aclcheck_error_strings[aclcheck_result]);
275 RemoveRewriteRule(rulename);
280 /* RemoveType does its own permissions checks */
286 * Make sure subsequent loop iterations will see
287 * results of this one; needed if removing multiple
288 * rules for same table, for example.
290 CommandCounterIncrement();
299 set_ps_display(commandTag = "TRUNCATE");
301 relname = ((TruncateStmt *) parsetree)->relName;
302 if (!allowSystemTableMods && IsSystemRelationName(relname))
303 elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
306 /* Grab exclusive lock in preparation for truncate... */
307 rel = heap_openr(relname, AccessExclusiveLock);
308 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
309 elog(ERROR, "TRUNCATE cannot be used on sequences. '%s' is a sequence",
311 if (rel->rd_rel->relkind == RELKIND_VIEW)
312 elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a view",
314 heap_close(rel, NoLock);
316 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
317 elog(ERROR, "you do not own class \"%s\"", relname);
318 TruncateRelation(relname);
324 CommentStmt *statement;
326 statement = ((CommentStmt *) parsetree);
328 set_ps_display(commandTag = "COMMENT");
330 CommentObject(statement->objtype, statement->objname,
331 statement->objproperty, statement->objlist,
338 CopyStmt *stmt = (CopyStmt *) parsetree;
340 set_ps_display(commandTag = "COPY");
342 if (stmt->direction != FROM)
345 DoCopy(stmt->relname,
348 (bool) (stmt->direction == FROM),
349 (bool) (stmt->filename == NULL),
352 * null filename means copy to/from stdout/stdin, rather
353 * than to/from a file.
366 RenameStmt *stmt = (RenameStmt *) parsetree;
368 set_ps_display(commandTag = "ALTER");
370 relname = stmt->relname;
371 if (!allowSystemTableMods && IsSystemRelationName(relname))
372 elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
374 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
375 elog(ERROR, "permission denied");
378 * XXX using len == 3 to tell the difference
379 * between "rename rel to newrel" and
380 * "rename att in rel to newatt" will not
381 * work soon because "rename type/operator/rule"
382 * stuff is being added. - cim 10/24/90
384 * [another piece of amuzing but useless anecdote -- ay]
386 if (stmt->column == NULL)
391 * Note: we also rename the "type" tuple corresponding to
394 renamerel(relname, /* old name */
395 stmt->newname); /* new name */
402 renameatt(relname, /* relname */
403 stmt->column, /* old att name */
404 stmt->newname, /* new att name */
405 interpretInhOption(stmt->inhOpt)); /* recursive? */
410 /* various Alter Table forms */
412 case T_AlterTableStmt:
414 AlterTableStmt *stmt = (AlterTableStmt *) parsetree;
416 set_ps_display(commandTag = "ALTER");
419 * Some or all of these functions are recursive to cover
420 * inherited things, so permission checks are done there.
422 switch (stmt->subtype)
424 case 'A': /* ADD COLUMN */
425 AlterTableAddColumn(stmt->relname,
426 interpretInhOption(stmt->inhOpt),
427 (ColumnDef *) stmt->def);
429 case 'T': /* ALTER COLUMN DEFAULT */
430 AlterTableAlterColumnDefault(stmt->relname,
431 interpretInhOption(stmt->inhOpt),
435 case 'S': /* ALTER COLUMN STATISTICS */
436 AlterTableAlterColumnStatistics(stmt->relname,
437 interpretInhOption(stmt->inhOpt),
441 case 'D': /* DROP COLUMN */
442 AlterTableDropColumn(stmt->relname,
443 interpretInhOption(stmt->inhOpt),
447 case 'C': /* ADD CONSTRAINT */
448 AlterTableAddConstraint(stmt->relname,
449 interpretInhOption(stmt->inhOpt),
452 case 'X': /* DROP CONSTRAINT */
453 AlterTableDropConstraint(stmt->relname,
454 interpretInhOption(stmt->inhOpt),
458 case 'E': /* CREATE TOAST TABLE */
459 AlterTableCreateToastTable(stmt->relname,
462 case 'U': /* ALTER OWNER */
463 AlterTableOwner(stmt->relname,
467 elog(ERROR, "T_AlterTableStmt: unknown subtype");
476 GrantStmt *stmt = (GrantStmt *) parsetree;
478 commandTag = stmt->is_grant ? "GRANT" : "REVOKE";
479 set_ps_display(commandTag);
481 ExecuteGrantStmt(stmt);
486 * ******************************** object creation /
487 * destruction ********************************
492 DefineStmt *stmt = (DefineStmt *) parsetree;
494 set_ps_display(commandTag = "CREATE");
496 switch (stmt->defType)
499 DefineOperator(stmt->defname, /* operator name */
500 stmt->definition); /* rest */
503 DefineType(stmt->defname, stmt->definition);
506 DefineAggregate(stmt->defname, /* aggregate name */
507 stmt->definition); /* rest */
513 case T_ViewStmt: /* CREATE VIEW */
515 ViewStmt *stmt = (ViewStmt *) parsetree;
517 set_ps_display(commandTag = "CREATE");
519 DefineView(stmt->viewname, stmt->query); /* retrieve parsetree */
523 case T_ProcedureStmt: /* CREATE FUNCTION */
524 set_ps_display(commandTag = "CREATE");
526 CreateFunction((ProcedureStmt *) parsetree);
529 case T_IndexStmt: /* CREATE INDEX */
531 IndexStmt *stmt = (IndexStmt *) parsetree;
533 set_ps_display(commandTag = "CREATE");
535 DefineIndex(stmt->relname, /* relation name */
536 stmt->idxname, /* index name */
537 stmt->accessMethod, /* am name */
538 stmt->indexParams, /* parameters */
541 (Expr *) stmt->whereClause,
546 case T_RuleStmt: /* CREATE RULE */
548 RuleStmt *stmt = (RuleStmt *) parsetree;
551 relname = stmt->object->relname;
552 aclcheck_result = pg_aclcheck(relname, GetUserId(), ACL_RULE);
553 if (aclcheck_result != ACLCHECK_OK)
554 elog(ERROR, "%s: %s", relname, aclcheck_error_strings[aclcheck_result]);
555 set_ps_display(commandTag = "CREATE");
557 DefineQueryRewrite(stmt);
561 case T_CreateSeqStmt:
562 set_ps_display(commandTag = "CREATE");
564 DefineSequence((CreateSeqStmt *) parsetree);
567 case T_RemoveAggrStmt:
569 RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
570 char *typename = (char *) NULL;
572 set_ps_display(commandTag = "DROP");
574 if (stmt->aggtype != NULL)
575 typename = TypeNameToInternalName((TypeName *) stmt->aggtype);
577 RemoveAggregate(stmt->aggname, typename);
581 case T_RemoveFuncStmt:
583 RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;
585 set_ps_display(commandTag = "DROP");
587 RemoveFunction(stmt->funcname, stmt->args);
591 case T_RemoveOperStmt:
593 RemoveOperStmt *stmt = (RemoveOperStmt *) parsetree;
594 TypeName *typenode1 = (TypeName *) lfirst(stmt->args);
595 TypeName *typenode2 = (TypeName *) lsecond(stmt->args);
596 char *typename1 = (char *) NULL;
597 char *typename2 = (char *) NULL;
599 set_ps_display(commandTag = "DROP");
601 if (typenode1 != NULL)
602 typename1 = TypeNameToInternalName(typenode1);
603 if (typenode2 != NULL)
604 typename2 = TypeNameToInternalName(typenode2);
606 RemoveOperator(stmt->opname, typename1, typename2);
611 elog(ERROR, "CREATE VERSION is not currently implemented");
616 CreatedbStmt *stmt = (CreatedbStmt *) parsetree;
618 set_ps_display(commandTag = "CREATE DATABASE");
620 createdb(stmt->dbname, stmt->dbpath,
621 stmt->dbtemplate, stmt->encoding);
627 DropdbStmt *stmt = (DropdbStmt *) parsetree;
629 set_ps_display(commandTag = "DROP DATABASE");
631 dropdb(stmt->dbname);
635 /* Query-level asynchronous notification */
638 NotifyStmt *stmt = (NotifyStmt *) parsetree;
640 set_ps_display(commandTag = "NOTIFY");
642 Async_Notify(stmt->relname);
648 ListenStmt *stmt = (ListenStmt *) parsetree;
650 set_ps_display(commandTag = "LISTEN");
652 Async_Listen(stmt->relname, MyProcPid);
658 UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
660 set_ps_display(commandTag = "UNLISTEN");
662 Async_Unlisten(stmt->relname, MyProcPid);
667 * ******************************** dynamic loader ********************************
672 LoadStmt *stmt = (LoadStmt *) parsetree;
674 set_ps_display(commandTag = "LOAD");
676 closeAllVfds(); /* probably not necessary... */
677 load_file(stmt->filename);
683 ClusterStmt *stmt = (ClusterStmt *) parsetree;
685 set_ps_display(commandTag = "CLUSTER");
687 relname = stmt->relname;
688 if (IsSystemRelationName(relname))
689 elog(ERROR, "CLUSTER: relation \"%s\" is a system catalog",
691 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
692 elog(ERROR, "permission denied");
694 cluster(relname, stmt->indexname);
699 if (((VacuumStmt *) parsetree)->vacuum)
700 commandTag = "VACUUM";
702 commandTag = "ANALYZE";
703 set_ps_display(commandTag);
705 vacuum((VacuumStmt *) parsetree);
710 ExplainStmt *stmt = (ExplainStmt *) parsetree;
712 set_ps_display(commandTag = "EXPLAIN");
714 ExplainQuery(stmt->query, stmt->verbose, stmt->analyze, dest);
721 * ******************************** Tioga-related statements *******************************
725 RecipeStmt *stmt = (RecipeStmt *) parsetree;
727 set_ps_display(commandTag = "EXECUTE RECIPE");
735 * ******************************** set variable statements *******************************
737 case T_VariableSetStmt:
739 VariableSetStmt *n = (VariableSetStmt *) parsetree;
741 SetPGVariable(n->name, n->args);
742 set_ps_display(commandTag = "SET VARIABLE");
746 case T_VariableShowStmt:
748 VariableShowStmt *n = (VariableShowStmt *) parsetree;
750 GetPGVariable(n->name);
751 set_ps_display(commandTag = "SHOW VARIABLE");
755 case T_VariableResetStmt:
757 VariableResetStmt *n = (VariableResetStmt *) parsetree;
759 ResetPGVariable(n->name);
760 set_ps_display(commandTag = "RESET VARIABLE");
765 * ******************************** TRIGGER statements *******************************
767 case T_CreateTrigStmt:
768 set_ps_display(commandTag = "CREATE");
770 CreateTrigger((CreateTrigStmt *) parsetree);
774 set_ps_display(commandTag = "DROP");
776 DropTrigger((DropTrigStmt *) parsetree);
780 * ************* PROCEDURAL LANGUAGE statements *****************
782 case T_CreatePLangStmt:
783 set_ps_display(commandTag = "CREATE");
785 CreateProceduralLanguage((CreatePLangStmt *) parsetree);
788 case T_DropPLangStmt:
789 set_ps_display(commandTag = "DROP");
791 DropProceduralLanguage((DropPLangStmt *) parsetree);
795 * ******************************** USER statements ****
798 case T_CreateUserStmt:
799 set_ps_display(commandTag = "CREATE USER");
801 CreateUser((CreateUserStmt *) parsetree);
804 case T_AlterUserStmt:
805 set_ps_display(commandTag = "ALTER USER");
807 AlterUser((AlterUserStmt *) parsetree);
811 set_ps_display(commandTag = "DROP USER");
813 DropUser((DropUserStmt *) parsetree);
817 set_ps_display(commandTag = "LOCK TABLE");
819 LockTableCommand((LockStmt *) parsetree);
822 case T_ConstraintsSetStmt:
823 set_ps_display(commandTag = "SET CONSTRAINTS");
825 DeferredTriggerSetState((ConstraintsSetStmt *) parsetree);
828 case T_CreateGroupStmt:
829 set_ps_display(commandTag = "CREATE GROUP");
831 CreateGroup((CreateGroupStmt *) parsetree);
834 case T_AlterGroupStmt:
835 set_ps_display(commandTag = "ALTER GROUP");
837 AlterGroup((AlterGroupStmt *) parsetree, "ALTER GROUP");
840 case T_DropGroupStmt:
841 set_ps_display(commandTag = "DROP GROUP");
843 DropGroup((DropGroupStmt *) parsetree);
846 case T_CheckPointStmt:
848 set_ps_display(commandTag = "CHECKPOINT");
851 elog(ERROR, "permission denied");
852 CreateCheckPoint(false);
858 ReindexStmt *stmt = (ReindexStmt *) parsetree;
860 set_ps_display(commandTag = "REINDEX");
862 switch (stmt->reindexType)
865 relname = (char *) stmt->name;
866 if (IsSystemRelationName(relname))
868 if (!allowSystemTableMods)
869 elog(ERROR, "\"%s\" is a system index. call REINDEX under standalone postgres with -O -P options",
871 if (!IsIgnoringSystemIndexes())
872 elog(ERROR, "\"%s\" is a system index. call REINDEX under standalone postgres with -P -O options",
875 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
876 elog(ERROR, "%s: %s", relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
877 ReindexIndex(relname, stmt->force);
880 relname = (char *) stmt->name;
881 if (IsSystemRelationName(relname))
883 if (!allowSystemTableMods)
884 elog(ERROR, "\"%s\" is a system table. call REINDEX under standalone postgres with -O -P options",
886 if (!IsIgnoringSystemIndexes())
887 elog(ERROR, "\"%s\" is a system table. call REINDEX under standalone postgres with -P -O options",
890 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
891 elog(ERROR, "%s: %s", relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
892 ReindexTable(relname, stmt->force);
895 relname = (char *) stmt->name;
896 if (!allowSystemTableMods)
897 elog(ERROR, "must be called under standalone postgres with -O -P options");
898 if (!IsIgnoringSystemIndexes())
899 elog(ERROR, "must be called under standalone postgres with -P -O options");
900 ReindexDatabase(relname, stmt->force, false);
908 * ******************************** default ********************************
912 elog(ERROR, "ProcessUtility: command #%d unsupported",
918 * tell fe/be or whatever that we're done.
920 EndCommand(commandTag, dest);