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-2000, PostgreSQL, Inc
9 * Portions Copyright (c) 1994, Regents of the University of California
13 * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.103 2000/11/16 22:30:30 tgl Exp $
15 *-------------------------------------------------------------------------
19 #include "access/heapam.h"
20 #include "catalog/catalog.h"
21 #include "commands/async.h"
22 #include "commands/cluster.h"
23 #include "commands/command.h"
24 #include "commands/comment.h"
25 #include "commands/copy.h"
26 #include "commands/creatinh.h"
27 #include "commands/dbcommands.h"
28 #include "commands/defrem.h"
29 #include "commands/explain.h"
30 #include "commands/proclang.h"
31 #include "commands/rename.h"
32 #include "commands/sequence.h"
33 #include "commands/trigger.h"
34 #include "commands/user.h"
35 #include "commands/vacuum.h"
36 #include "commands/variable.h"
37 #include "commands/view.h"
38 #include "miscadmin.h"
39 #include "parser/parse.h"
40 #include "parser/parse_expr.h"
41 #include "rewrite/rewriteDefine.h"
42 #include "rewrite/rewriteRemove.h"
43 #include "tcop/utility.h"
44 #include "utils/acl.h"
45 #include "utils/ps_status.h"
46 #include "utils/syscache.h"
47 #include "access/xlog.h"
50 * Error-checking support for DROP commands
60 static struct kindstrings kindstringarray[] = {
61 { RELKIND_RELATION, "a", "table", "TABLE" },
62 { RELKIND_SEQUENCE, "a", "sequence", "SEQUENCE" },
63 { RELKIND_VIEW, "a", "view", "VIEW" },
64 { RELKIND_INDEX, "an", "index", "INDEX" },
65 { '\0', "a", "???", "???" }
70 DropErrorMsg(char* relname, char wrongkind, char rightkind)
72 struct kindstrings *rentry;
73 struct kindstrings *wentry;
75 for (rentry = kindstringarray; rentry->kind != '\0'; rentry++)
76 if (rentry->kind == rightkind)
78 Assert(rentry->kind != '\0');
80 for (wentry = kindstringarray; wentry->kind != '\0'; wentry++)
81 if (wentry->kind == wrongkind)
83 /* wrongkind could be something we don't have in our table... */
84 if (wentry->kind != '\0')
85 elog(ERROR, "\"%s\" is not %s %s. Use DROP %s to remove %s %s",
86 relname, rentry->indef_article, rentry->name,
87 wentry->command, wentry->indef_article, wentry->name);
89 elog(ERROR, "\"%s\" is not %s %s",
90 relname, rentry->indef_article, rentry->name);
94 CheckDropPermissions(char *name, char rightkind)
96 struct kindstrings *rentry;
98 Form_pg_class classform;
100 for (rentry = kindstringarray; rentry->kind != '\0'; rentry++)
101 if (rentry->kind == rightkind)
103 Assert(rentry->kind != '\0');
105 tuple = SearchSysCache(RELNAME,
106 PointerGetDatum(name),
108 if (!HeapTupleIsValid(tuple))
109 elog(ERROR, "%s \"%s\" does not exist", rentry->name, name);
111 classform = (Form_pg_class) GETSTRUCT(tuple);
113 if (classform->relkind != rightkind)
114 DropErrorMsg(name, classform->relkind, rightkind);
116 if (!pg_ownercheck(GetUserId(), name, RELNAME))
117 elog(ERROR, "you do not own %s \"%s\"",
120 if (!allowSystemTableMods && IsSystemRelationName(name))
121 elog(ERROR, "%s \"%s\" is a system %s",
122 rentry->name, name, rentry->name);
124 ReleaseSysCache(tuple);
129 * general utility function invoker
133 ProcessUtility(Node *parsetree,
136 char *commandTag = NULL;
140 switch (nodeTag(parsetree))
144 * ******************************** transactions ********************************
147 case T_TransactionStmt:
149 TransactionStmt *stmt = (TransactionStmt *) parsetree;
151 switch (stmt->command)
154 set_ps_display(commandTag = "BEGIN");
155 BeginTransactionBlock();
159 set_ps_display(commandTag = "COMMIT");
160 EndTransactionBlock();
164 set_ps_display(commandTag = "ROLLBACK");
165 UserAbortTransactionBlock();
172 * ******************************** portal manipulation ********************************
175 case T_ClosePortalStmt:
177 ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
179 set_ps_display(commandTag = "CLOSE");
181 PerformPortalClose(stmt->portalname, dest);
187 FetchStmt *stmt = (FetchStmt *) parsetree;
188 char *portalName = stmt->portalname;
192 set_ps_display(commandTag = (stmt->ismove) ? "MOVE" : "FETCH");
196 forward = (bool) (stmt->direction == FORWARD);
199 * parser ensures that count is >= 0 and 'fetch ALL' -> 0
202 count = stmt->howMany;
203 PerformPortalFetch(portalName, forward, count, commandTag,
204 (stmt->ismove) ? None : dest); /* /dev/null for MOVE */
209 * ******************************** relation and attribute
210 * manipulation ********************************
214 set_ps_display(commandTag = "CREATE");
216 DefineRelation((CreateStmt *) parsetree, RELKIND_RELATION);
219 * Let AlterTableCreateToastTable decide if this
220 * one needs a secondary relation too.
222 CommandCounterIncrement();
223 AlterTableCreateToastTable(((CreateStmt *)parsetree)->relname,
229 DropStmt *stmt = (DropStmt *) parsetree;
230 List *args = stmt->names;
233 set_ps_display(commandTag = "DROP");
237 relname = strVal(lfirst(arg));
239 switch(stmt->removeType)
242 CheckDropPermissions(relname, RELKIND_RELATION);
243 RemoveRelation(relname);
247 CheckDropPermissions(relname, RELKIND_SEQUENCE);
248 RemoveRelation(relname);
252 CheckDropPermissions(relname, RELKIND_VIEW);
257 CheckDropPermissions(relname, RELKIND_INDEX);
258 RemoveIndex(relname);
263 char *rulename = relname;
266 relationName = RewriteGetRuleEventRel(rulename);
267 aclcheck_result = pg_aclcheck(relationName, GetUserId(), ACL_RU);
268 if (aclcheck_result != ACLCHECK_OK)
269 elog(ERROR, "%s: %s", relationName,
270 aclcheck_error_strings[aclcheck_result]);
271 RemoveRewriteRule(rulename);
276 /* RemoveType does its own permissions checks */
282 * Make sure subsequent loop iterations will see results
283 * of this one; needed if removing multiple rules for
284 * same table, for example.
286 CommandCounterIncrement();
295 set_ps_display(commandTag = "TRUNCATE");
297 relname = ((TruncateStmt *) parsetree)->relName;
298 if (!allowSystemTableMods && IsSystemRelationName(relname))
299 elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
302 /* Grab exclusive lock in preparation for truncate... */
303 rel = heap_openr(relname, AccessExclusiveLock);
304 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
305 elog(ERROR, "TRUNCATE cannot be used on sequences. '%s' is a sequence",
307 if (rel->rd_rel->relkind == RELKIND_VIEW)
308 elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a sequence",
310 heap_close(rel, NoLock);
312 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
313 elog(ERROR, "you do not own class \"%s\"", relname);
314 TruncateRelation(relname);
320 CommentStmt *statement;
322 statement = ((CommentStmt *) parsetree);
324 set_ps_display(commandTag = "COMMENT");
326 CommentObject(statement->objtype, statement->objname,
327 statement->objproperty, statement->objlist,
334 CopyStmt *stmt = (CopyStmt *) parsetree;
336 set_ps_display(commandTag = "COPY");
338 if (stmt->direction != FROM)
341 DoCopy(stmt->relname,
344 (bool) (stmt->direction == FROM),
345 (bool) (stmt->filename == NULL),
348 * null filename means copy to/from stdout/stdin, rather
349 * than to/from a file.
362 RenameStmt *stmt = (RenameStmt *) parsetree;
364 set_ps_display(commandTag = "ALTER");
366 relname = stmt->relname;
367 if (!allowSystemTableMods && IsSystemRelationName(relname))
368 elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
370 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
371 elog(ERROR, "permission denied");
374 * XXX using len == 3 to tell the difference
375 * between "rename rel to newrel" and
376 * "rename att in rel to newatt" will not
377 * work soon because "rename type/operator/rule"
378 * stuff is being added. - cim 10/24/90
380 * [another piece of amuzing but useless anecdote -- ay]
382 if (stmt->column == NULL)
387 * Note: we also rename the "type" tuple
388 * corresponding to the relation.
391 renamerel(relname, /* old name */
392 stmt->newname); /* new name */
400 renameatt(relname, /* relname */
401 stmt->column, /* old att name */
402 stmt->newname, /* new att name */
403 stmt->inh); /* recursive? */
408 /* various Alter Table forms */
410 case T_AlterTableStmt:
412 AlterTableStmt *stmt = (AlterTableStmt *) parsetree;
414 set_ps_display(commandTag = "ALTER");
417 * Some or all of these functions are recursive to cover
418 * inherited things, so permission checks are done there.
420 switch (stmt->subtype)
422 case 'A': /* ADD COLUMN */
423 AlterTableAddColumn(stmt->relname, stmt->inh, (ColumnDef *) stmt->def);
425 case 'T': /* ALTER COLUMN */
426 AlterTableAlterColumn(stmt->relname, stmt->inh, stmt->name, stmt->def);
428 case 'D': /* ALTER DROP */
429 AlterTableDropColumn(stmt->relname, stmt->inh, stmt->name, stmt->behavior);
431 case 'C': /* ADD CONSTRAINT */
432 AlterTableAddConstraint(stmt->relname, stmt->inh, stmt->def);
434 case 'X': /* DROP CONSTRAINT */
435 AlterTableDropConstraint(stmt->relname, stmt->inh, stmt->name, stmt->behavior);
437 case 'E': /* CREATE TOAST TABLE */
438 AlterTableCreateToastTable(stmt->relname, false);
440 case 'U': /* ALTER OWNER */
441 AlterTableOwner(stmt->relname, stmt->name);
444 elog(ERROR, "T_AlterTableStmt: unknown subtype");
451 case T_ChangeACLStmt:
453 ChangeACLStmt *stmt = (ChangeACLStmt *) parsetree;
455 set_ps_display(commandTag = "CHANGE");
457 ExecuteChangeACLStmt(stmt);
462 * ******************************** object creation /
463 * destruction ********************************
468 DefineStmt *stmt = (DefineStmt *) parsetree;
470 set_ps_display(commandTag = "CREATE");
472 switch (stmt->defType)
475 DefineOperator(stmt->defname, /* operator name */
476 stmt->definition); /* rest */
479 DefineType(stmt->defname, stmt->definition);
482 DefineAggregate(stmt->defname, /* aggregate name */
483 stmt->definition); /* rest */
489 case T_ViewStmt: /* CREATE VIEW */
491 ViewStmt *stmt = (ViewStmt *) parsetree;
493 set_ps_display(commandTag = "CREATE");
495 DefineView(stmt->viewname, stmt->query); /* retrieve parsetree */
499 case T_ProcedureStmt: /* CREATE FUNCTION */
500 set_ps_display(commandTag = "CREATE");
502 CreateFunction((ProcedureStmt *) parsetree, dest); /* everything */
505 case T_IndexStmt: /* CREATE INDEX */
507 IndexStmt *stmt = (IndexStmt *) parsetree;
509 set_ps_display(commandTag = "CREATE");
511 DefineIndex(stmt->relname, /* relation name */
512 stmt->idxname, /* index name */
513 stmt->accessMethod, /* am name */
514 stmt->indexParams, /* parameters */
518 (Expr *) stmt->whereClause,
523 case T_RuleStmt: /* CREATE RULE */
525 RuleStmt *stmt = (RuleStmt *) parsetree;
528 relname = stmt->object->relname;
529 aclcheck_result = pg_aclcheck(relname, GetUserId(), ACL_RU);
530 if (aclcheck_result != ACLCHECK_OK)
531 elog(ERROR, "%s: %s", relname, aclcheck_error_strings[aclcheck_result]);
532 set_ps_display(commandTag = "CREATE");
534 DefineQueryRewrite(stmt);
538 case T_CreateSeqStmt:
539 set_ps_display(commandTag = "CREATE");
541 DefineSequence((CreateSeqStmt *) parsetree);
546 ExtendStmt *stmt = (ExtendStmt *) parsetree;
548 set_ps_display(commandTag = "EXTEND");
550 ExtendIndex(stmt->idxname, /* index name */
551 (Expr *) stmt->whereClause, /* where */
556 case T_RemoveAggrStmt:
558 RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
559 char *typename = (char *) NULL;
561 set_ps_display(commandTag = "DROP");
563 if (stmt->aggtype != NULL)
564 typename = TypeNameToInternalName((TypeName *) stmt->aggtype);
566 RemoveAggregate(stmt->aggname, typename);
570 case T_RemoveFuncStmt:
572 RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;
574 set_ps_display(commandTag = "DROP");
576 RemoveFunction(stmt->funcname, stmt->args);
580 case T_RemoveOperStmt:
582 RemoveOperStmt *stmt = (RemoveOperStmt *) parsetree;
583 TypeName *typenode1 = (TypeName *) lfirst(stmt->args);
584 TypeName *typenode2 = (TypeName *) lsecond(stmt->args);
585 char *typename1 = (char *) NULL;
586 char *typename2 = (char *) NULL;
588 set_ps_display(commandTag = "DROP");
590 if (typenode1 != NULL)
591 typename1 = TypeNameToInternalName(typenode1);
592 if (typenode2 != NULL)
593 typename2 = TypeNameToInternalName(typenode2);
595 RemoveOperator(stmt->opname, typename1, typename2);
600 elog(ERROR, "CREATE VERSION is not currently implemented");
605 CreatedbStmt *stmt = (CreatedbStmt *) parsetree;
607 set_ps_display(commandTag = "CREATE DATABASE");
609 createdb(stmt->dbname, stmt->dbpath,
610 stmt->dbtemplate, stmt->encoding);
616 DropdbStmt *stmt = (DropdbStmt *) parsetree;
618 set_ps_display(commandTag = "DROP DATABASE");
620 dropdb(stmt->dbname);
624 /* Query-level asynchronous notification */
627 NotifyStmt *stmt = (NotifyStmt *) parsetree;
629 set_ps_display(commandTag = "NOTIFY");
631 Async_Notify(stmt->relname);
637 ListenStmt *stmt = (ListenStmt *) parsetree;
639 set_ps_display(commandTag = "LISTEN");
641 Async_Listen(stmt->relname, MyProcPid);
647 UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
649 set_ps_display(commandTag = "UNLISTEN");
651 Async_Unlisten(stmt->relname, MyProcPid);
656 * ******************************** dynamic loader ********************************
661 LoadStmt *stmt = (LoadStmt *) parsetree;
663 set_ps_display(commandTag = "LOAD");
665 closeAllVfds(); /* probably not necessary... */
666 load_file(stmt->filename);
672 ClusterStmt *stmt = (ClusterStmt *) parsetree;
674 set_ps_display(commandTag = "CLUSTER");
676 relname = stmt->relname;
677 if (IsSystemRelationName(relname))
678 elog(ERROR, "CLUSTER: relation \"%s\" is a system catalog",
680 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
681 elog(ERROR, "permission denied");
683 cluster(relname, stmt->indexname);
688 set_ps_display(commandTag = "VACUUM");
690 vacuum(((VacuumStmt *) parsetree)->vacrel,
691 ((VacuumStmt *) parsetree)->verbose,
692 ((VacuumStmt *) parsetree)->analyze,
693 ((VacuumStmt *) parsetree)->va_spec);
698 ExplainStmt *stmt = (ExplainStmt *) parsetree;
700 set_ps_display(commandTag = "EXPLAIN");
702 ExplainQuery(stmt->query, stmt->verbose, dest);
709 * ******************************** Tioga-related statements *******************************
713 RecipeStmt *stmt = (RecipeStmt *) parsetree;
715 set_ps_display(commandTag = "EXECUTE RECIPE");
723 * ******************************** set variable statements *******************************
725 case T_VariableSetStmt:
727 VariableSetStmt *n = (VariableSetStmt *) parsetree;
729 SetPGVariable(n->name, n->value);
730 set_ps_display(commandTag = "SET VARIABLE");
734 case T_VariableShowStmt:
736 VariableShowStmt *n = (VariableShowStmt *) parsetree;
738 GetPGVariable(n->name);
739 set_ps_display(commandTag = "SHOW VARIABLE");
743 case T_VariableResetStmt:
745 VariableResetStmt *n = (VariableResetStmt *) parsetree;
747 ResetPGVariable(n->name);
748 set_ps_display(commandTag = "RESET VARIABLE");
753 * ******************************** TRIGGER statements *******************************
755 case T_CreateTrigStmt:
756 set_ps_display(commandTag = "CREATE");
758 CreateTrigger((CreateTrigStmt *) parsetree);
762 set_ps_display(commandTag = "DROP");
764 DropTrigger((DropTrigStmt *) parsetree);
768 * ************* PROCEDURAL LANGUAGE statements *****************
770 case T_CreatePLangStmt:
771 set_ps_display(commandTag = "CREATE");
773 CreateProceduralLanguage((CreatePLangStmt *) parsetree);
776 case T_DropPLangStmt:
777 set_ps_display(commandTag = "DROP");
779 DropProceduralLanguage((DropPLangStmt *) parsetree);
783 * ******************************** USER statements ****
786 case T_CreateUserStmt:
787 set_ps_display(commandTag = "CREATE USER");
789 CreateUser((CreateUserStmt *) parsetree);
792 case T_AlterUserStmt:
793 set_ps_display(commandTag = "ALTER USER");
795 AlterUser((AlterUserStmt *) parsetree);
799 set_ps_display(commandTag = "DROP USER");
801 DropUser((DropUserStmt *) parsetree);
805 set_ps_display(commandTag = "LOCK TABLE");
807 LockTableCommand((LockStmt *) parsetree);
810 case T_ConstraintsSetStmt:
811 set_ps_display(commandTag = "SET CONSTRAINTS");
813 DeferredTriggerSetState((ConstraintsSetStmt *) parsetree);
816 case T_CreateGroupStmt:
817 set_ps_display(commandTag = "CREATE GROUP");
819 CreateGroup((CreateGroupStmt *) parsetree);
822 case T_AlterGroupStmt:
823 set_ps_display(commandTag = "ALTER GROUP");
825 AlterGroup((AlterGroupStmt *) parsetree, "ALTER GROUP");
828 case T_DropGroupStmt:
829 set_ps_display(commandTag = "DROP GROUP");
831 DropGroup((DropGroupStmt *) parsetree);
834 case T_CheckPointStmt:
836 set_ps_display(commandTag = "CHECKPOINT");
838 CreateCheckPoint(false);
844 ReindexStmt *stmt = (ReindexStmt *) parsetree;
846 set_ps_display(commandTag = "REINDEX");
848 switch (stmt->reindexType)
851 relname = (char *) stmt->name;
852 if (IsSystemRelationName(relname))
854 if (!allowSystemTableMods && IsSystemRelationName(relname))
855 elog(ERROR, "\"%s\" is a system index. call REINDEX under standalone postgres with -O -P options",
857 if (!IsIgnoringSystemIndexes())
858 elog(ERROR, "\"%s\" is a system index. call REINDEX under standalone postgres with -P -O options",
861 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
862 elog(ERROR, "%s: %s", relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
863 ReindexIndex(relname, stmt->force);
866 relname = (char *) stmt->name;
867 if (IsSystemRelationName(relname))
869 if (!allowSystemTableMods && IsSystemRelationName(relname))
870 elog(ERROR, "\"%s\" is a system table. call REINDEX under standalone postgres with -O -P options",
872 if (!IsIgnoringSystemIndexes())
873 elog(ERROR, "\"%s\" is a system table. call REINDEX under standalone postgres with -P -O options",
877 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
878 elog(ERROR, "%s: %s", relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
879 ReindexTable(relname, stmt->force);
882 relname = (char *) stmt->name;
883 if (!allowSystemTableMods)
884 elog(ERROR, "must be called under standalone postgres with -O -P options");
885 if (!IsIgnoringSystemIndexes())
886 elog(ERROR, "must be called under standalone postgres with -P -O options");
887 ReindexDatabase(relname, stmt->force, false);
895 * ******************************** default ********************************
899 elog(ERROR, "ProcessUtility: command #%d unsupported",
905 * tell fe/be or whatever that we're done.
908 EndCommand(commandTag, dest);