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 * Copyright (c) 1994, Regents of the University of California
12 * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.74 1999/12/14 00:08:17 momjian Exp $
14 *-------------------------------------------------------------------------
18 #include "access/heapam.h"
19 #include "catalog/catalog.h"
20 #include "catalog/pg_type.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/vacuum.h"
35 #include "commands/variable.h"
36 #include "commands/view.h"
37 #include "miscadmin.h"
38 #include "rewrite/rewriteDefine.h"
39 #include "rewrite/rewriteRemove.h"
40 #include "tcop/utility.h"
41 #include "utils/acl.h"
42 #include "utils/ps_status.h"
43 #include "utils/syscache.h"
45 #include "../backend/parser/parse.h"
47 void DefineUser(CreateUserStmt *stmt, CommandDest);
48 void AlterUser(AlterUserStmt *stmt, CommandDest);
49 void RemoveUser(char *username, CommandDest);
52 * CHECK_IF_ABORTED() is used to avoid doing unnecessary
53 * processing within an aborted transaction block.
56 /* we have to use IF because of the 'break' */
57 #define CHECK_IF_ABORTED() \
60 if (IsAbortedTransactionBlockState()) \
62 elog(NOTICE, "(transaction aborted): %s", \
63 "all queries ignored until end of transaction block"); \
64 commandTag = "*ABORT STATE*"; \
70 * general utility function invoker
74 ProcessUtility(Node *parsetree,
77 char *commandTag = NULL;
82 userName = GetPgUserName();
84 switch (nodeTag(parsetree))
88 * ******************************** transactions ********************************
91 case T_TransactionStmt:
93 TransactionStmt *stmt = (TransactionStmt *) parsetree;
95 switch (stmt->command)
98 PS_SET_STATUS(commandTag = "BEGIN");
100 BeginTransactionBlock();
104 PS_SET_STATUS(commandTag = "END");
105 EndTransactionBlock();
109 PS_SET_STATUS(commandTag = "ABORT");
110 UserAbortTransactionBlock();
117 * ******************************** portal manipulation ********************************
120 case T_ClosePortalStmt:
122 ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
124 PS_SET_STATUS(commandTag = "CLOSE");
127 PerformPortalClose(stmt->portalname, dest);
133 FetchStmt *stmt = (FetchStmt *) parsetree;
134 char *portalName = stmt->portalname;
138 PS_SET_STATUS(commandTag = (stmt->ismove) ? "MOVE" : "FETCH");
141 forward = (bool) (stmt->direction == FORWARD);
144 * parser ensures that count is >= 0 and 'fetch ALL' -> 0
147 count = stmt->howMany;
148 PerformPortalFetch(portalName, forward, count, commandTag,
149 (stmt->ismove) ? None : dest); /* /dev/null for MOVE */
154 * ******************************** relation and attribute
155 * manipulation ********************************
159 PS_SET_STATUS(commandTag = "CREATE");
162 DefineRelation((CreateStmt *) parsetree, RELKIND_RELATION);
167 DropStmt *stmt = (DropStmt *) parsetree;
168 List *args = stmt->relNames;
171 PS_SET_STATUS(commandTag = "DROP");
174 /* check as much as we can before we start dropping ... */
179 relname = strVal(lfirst(arg));
180 if (!allowSystemTableMods && IsSystemRelationName(relname))
181 elog(ERROR, "class \"%s\" is a system catalog",
183 rel = heap_openr(relname, AccessExclusiveLock);
184 if (stmt->sequence &&
185 rel->rd_rel->relkind != RELKIND_SEQUENCE)
186 elog(ERROR, "Use DROP TABLE to drop table '%s'",
188 if (!(stmt->sequence) &&
189 rel->rd_rel->relkind == RELKIND_SEQUENCE)
190 elog(ERROR, "Use DROP SEQUENCE to drop sequence '%s'",
192 /* close rel, but keep lock until end of xact */
193 heap_close(rel, NoLock);
195 if (!pg_ownercheck(userName, relname, RELNAME))
196 elog(ERROR, "you do not own class \"%s\"",
200 /* OK, terminate 'em all */
203 relname = strVal(lfirst(arg));
204 RemoveRelation(relname);
213 PS_SET_STATUS(commandTag = "TRUNCATE");
216 relname = ((TruncateStmt *) parsetree)->relName;
217 if (!allowSystemTableMods && IsSystemRelationName(relname))
218 elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
221 /* Grab exclusive lock in preparation for truncate... */
222 rel = heap_openr(relname, AccessExclusiveLock);
223 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
224 elog(ERROR, "TRUNCATE cannot be used on sequences. '%s' is a sequence",
226 heap_close(rel, NoLock);
229 if (!pg_ownercheck(userName, relname, RELNAME))
230 elog(ERROR, "you do not own class \"%s\"", relname);
232 TruncateRelation(relname);
239 CommentStmt *statement;
241 statement = ((CommentStmt *) parsetree);
243 PS_SET_STATUS(commandTag = "COMMENT");
245 CommentObject(statement->objtype, statement->objname,
246 statement->objproperty, statement->objlist,
255 CopyStmt *stmt = (CopyStmt *) parsetree;
257 PS_SET_STATUS(commandTag = "COPY");
260 DoCopy(stmt->relname,
263 (bool) (stmt->direction == FROM),
264 (bool) (stmt->filename == NULL),
267 * null filename means copy to/from stdout/stdin, rather
268 * than to/from a file.
274 * specify 022 umask while writing files with COPY.
282 AddAttrStmt *stmt = (AddAttrStmt *) parsetree;
284 PS_SET_STATUS(commandTag = "ADD");
288 * owner checking done in PerformAddAttribute (now
291 PerformAddAttribute(stmt->relname,
294 (ColumnDef *) stmt->colDef);
303 RenameStmt *stmt = (RenameStmt *) parsetree;
305 PS_SET_STATUS(commandTag = "RENAME");
308 relname = stmt->relname;
309 if (!allowSystemTableMods && IsSystemRelationName(relname))
310 elog(ERROR, "class \"%s\" is a system catalog",
313 if (!pg_ownercheck(userName, relname, RELNAME))
314 elog(ERROR, "you do not own class \"%s\"",
319 * XXX using len == 3 to tell the difference
320 * between "rename rel to newrel" and
321 * "rename att in rel to newatt" will not
322 * work soon because "rename type/operator/rule"
323 * stuff is being added. - cim 10/24/90
325 * [another piece of amuzing but useless anecdote -- ay]
327 if (stmt->column == NULL)
332 * Note: we also rename the "type" tuple
333 * corresponding to the relation.
336 renamerel(relname, /* old name */
337 stmt->newname); /* new name */
338 TypeRename(relname, /* old name */
339 stmt->newname); /* new name */
347 renameatt(relname, /* relname */
348 stmt->column, /* old att name */
349 stmt->newname, /* new att name */
351 stmt->inh); /* recursive? */
356 case T_ChangeACLStmt:
358 ChangeACLStmt *stmt = (ChangeACLStmt *) parsetree;
363 PS_SET_STATUS(commandTag = "CHANGE");
368 modechg = stmt->modechg;
370 foreach(i, stmt->relNames)
372 relname = strVal(lfirst(i));
373 if (!pg_ownercheck(userName, relname, RELNAME))
374 elog(ERROR, "you do not own class \"%s\"",
378 foreach(i, stmt->relNames)
380 relname = strVal(lfirst(i));
381 ChangeAcl(relname, aip, modechg);
388 * ******************************** object creation /
389 * destruction ********************************
394 DefineStmt *stmt = (DefineStmt *) parsetree;
396 PS_SET_STATUS(commandTag = "CREATE");
399 switch (stmt->defType)
402 DefineOperator(stmt->defname, /* operator name */
403 stmt->definition); /* rest */
406 DefineType(stmt->defname, stmt->definition);
409 DefineAggregate(stmt->defname, /* aggregate name */
410 stmt->definition); /* rest */
416 case T_ViewStmt: /* CREATE VIEW */
418 ViewStmt *stmt = (ViewStmt *) parsetree;
420 PS_SET_STATUS(commandTag = "CREATE");
422 DefineView(stmt->viewname, stmt->query); /* retrieve parsetree */
426 case T_ProcedureStmt: /* CREATE FUNCTION */
427 PS_SET_STATUS(commandTag = "CREATE");
429 CreateFunction((ProcedureStmt *) parsetree, dest); /* everything */
432 case T_IndexStmt: /* CREATE INDEX */
434 IndexStmt *stmt = (IndexStmt *) parsetree;
436 PS_SET_STATUS(commandTag = "CREATE");
438 DefineIndex(stmt->relname, /* relation name */
439 stmt->idxname, /* index name */
440 stmt->accessMethod, /* am name */
441 stmt->indexParams, /* parameters */
445 (Expr *) stmt->whereClause,
450 case T_RuleStmt: /* CREATE RULE */
452 RuleStmt *stmt = (RuleStmt *) parsetree;
456 relname = stmt->object->relname;
457 aclcheck_result = pg_aclcheck(relname, userName, ACL_RU);
458 if (aclcheck_result != ACLCHECK_OK)
459 elog(ERROR, "%s: %s", relname, aclcheck_error_strings[aclcheck_result]);
461 PS_SET_STATUS(commandTag = "CREATE");
463 DefineQueryRewrite(stmt);
467 case T_CreateSeqStmt:
468 PS_SET_STATUS(commandTag = "CREATE");
471 DefineSequence((CreateSeqStmt *) parsetree);
476 ExtendStmt *stmt = (ExtendStmt *) parsetree;
478 PS_SET_STATUS(commandTag = "EXTEND");
481 ExtendIndex(stmt->idxname, /* index name */
482 (Expr *) stmt->whereClause, /* where */
489 RemoveStmt *stmt = (RemoveStmt *) parsetree;
491 PS_SET_STATUS(commandTag = "DROP");
494 switch (stmt->removeType)
497 relname = stmt->name;
498 if (!allowSystemTableMods && IsSystemRelationName(relname))
499 elog(ERROR, "class \"%s\" is a system catalog index",
502 if (!pg_ownercheck(userName, relname, RELNAME))
503 elog(ERROR, "%s: %s", relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
505 RemoveIndex(relname);
509 char *rulename = stmt->name;
514 relationName = RewriteGetRuleEventRel(rulename);
515 aclcheck_result = pg_aclcheck(relationName, userName, ACL_RU);
516 if (aclcheck_result != ACLCHECK_OK)
517 elog(ERROR, "%s: %s", relationName, aclcheck_error_strings[aclcheck_result]);
519 RemoveRewriteRule(rulename);
524 /* XXX moved to remove.c */
526 RemoveType(stmt->name);
530 char *viewName = stmt->name;
535 ruleName = MakeRetrieveViewRuleName(viewName);
536 relationName = RewriteGetRuleEventRel(ruleName);
537 if (!pg_ownercheck(userName, relationName, RELNAME))
538 elog(ERROR, "%s: %s", relationName, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
541 RemoveView(viewName);
549 case T_RemoveAggrStmt:
551 RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
553 PS_SET_STATUS(commandTag = "DROP");
555 RemoveAggregate(stmt->aggname, stmt->aggtype);
559 case T_RemoveFuncStmt:
561 RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;
563 PS_SET_STATUS(commandTag = "DROP");
565 RemoveFunction(stmt->funcname,
571 case T_RemoveOperStmt:
573 RemoveOperStmt *stmt = (RemoveOperStmt *) parsetree;
574 char *type1 = (char *) NULL;
575 char *type2 = (char *) NULL;
577 PS_SET_STATUS(commandTag = "DROP");
580 if (lfirst(stmt->args) != NULL)
581 type1 = strVal(lfirst(stmt->args));
582 if (lsecond(stmt->args) != NULL)
583 type2 = strVal(lsecond(stmt->args));
584 RemoveOperator(stmt->opname, type1, type2);
589 elog(ERROR, "CREATE VERSION is not currently implemented");
594 CreatedbStmt *stmt = (CreatedbStmt *) parsetree;
596 PS_SET_STATUS(commandTag = "CREATE DATABASE");
598 createdb(stmt->dbname, stmt->dbpath, stmt->encoding, dest);
604 DropdbStmt *stmt = (DropdbStmt *) parsetree;
606 PS_SET_STATUS(commandTag = "DROP DATABASE");
608 dropdb(stmt->dbname, dest);
612 /* Query-level asynchronous notification */
615 NotifyStmt *stmt = (NotifyStmt *) parsetree;
617 PS_SET_STATUS(commandTag = "NOTIFY");
620 Async_Notify(stmt->relname);
626 ListenStmt *stmt = (ListenStmt *) parsetree;
628 PS_SET_STATUS(commandTag = "LISTEN");
631 Async_Listen(stmt->relname, MyProcPid);
637 UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
639 PS_SET_STATUS(commandTag = "UNLISTEN");
642 Async_Unlisten(stmt->relname, MyProcPid);
647 * ******************************** dynamic loader ********************************
652 LoadStmt *stmt = (LoadStmt *) parsetree;
654 PS_SET_STATUS(commandTag = "LOAD");
657 closeAllVfds(); /* probably not necessary... */
658 load_file(stmt->filename);
664 ClusterStmt *stmt = (ClusterStmt *) parsetree;
666 PS_SET_STATUS(commandTag = "CLUSTER");
669 cluster(stmt->relname, stmt->indexname);
674 PS_SET_STATUS(commandTag = "VACUUM");
676 vacuum(((VacuumStmt *) parsetree)->vacrel,
677 ((VacuumStmt *) parsetree)->verbose,
678 ((VacuumStmt *) parsetree)->analyze,
679 ((VacuumStmt *) parsetree)->va_spec);
684 ExplainStmt *stmt = (ExplainStmt *) parsetree;
686 PS_SET_STATUS(commandTag = "EXPLAIN");
689 ExplainQuery(stmt->query, stmt->verbose, dest);
696 * ******************************** Tioga-related statements *******************************
700 RecipeStmt *stmt = (RecipeStmt *) parsetree;
702 PS_SET_STATUS(commandTag = "EXECUTE RECIPE");
710 * ******************************** set variable statements *******************************
712 case T_VariableSetStmt:
714 VariableSetStmt *n = (VariableSetStmt *) parsetree;
716 SetPGVariable(n->name, n->value);
717 PS_SET_STATUS(commandTag = "SET VARIABLE");
721 case T_VariableShowStmt:
723 VariableShowStmt *n = (VariableShowStmt *) parsetree;
725 GetPGVariable(n->name);
726 PS_SET_STATUS(commandTag = "SHOW VARIABLE");
730 case T_VariableResetStmt:
732 VariableResetStmt *n = (VariableResetStmt *) parsetree;
734 ResetPGVariable(n->name);
735 PS_SET_STATUS(commandTag = "RESET VARIABLE");
740 * ******************************** TRIGGER statements *******************************
742 case T_CreateTrigStmt:
743 PS_SET_STATUS(commandTag = "CREATE");
746 CreateTrigger((CreateTrigStmt *) parsetree);
750 PS_SET_STATUS(commandTag = "DROP");
753 DropTrigger((DropTrigStmt *) parsetree);
757 * ************* PROCEDURAL LANGUAGE statements *****************
759 case T_CreatePLangStmt:
760 PS_SET_STATUS(commandTag = "CREATE");
763 CreateProceduralLanguage((CreatePLangStmt *) parsetree);
766 case T_DropPLangStmt:
767 PS_SET_STATUS(commandTag = "DROP");
770 DropProceduralLanguage((DropPLangStmt *) parsetree);
774 * ******************************** USER statements ****
777 case T_CreateUserStmt:
778 PS_SET_STATUS(commandTag = "CREATE USER");
781 DefineUser((CreateUserStmt *) parsetree, dest);
784 case T_AlterUserStmt:
785 PS_SET_STATUS(commandTag = "ALTER USER");
788 AlterUser((AlterUserStmt *) parsetree, dest);
792 PS_SET_STATUS(commandTag = "DROP USER");
795 RemoveUser(((DropUserStmt *) parsetree)->user, dest);
799 PS_SET_STATUS(commandTag = "LOCK TABLE");
802 LockTableCommand((LockStmt *) parsetree);
805 case T_ConstraintsSetStmt:
806 PS_SET_STATUS(commandTag = "SET CONSTRAINTS");
809 DeferredTriggerSetState((ConstraintsSetStmt *) parsetree);
814 * ******************************** default ********************************
818 elog(ERROR, "ProcessUtility: command #%d unsupported",
824 * tell fe/be or whatever that we're done.
827 EndCommand(commandTag, dest);