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.96 2000/10/16 17:08:07 momjian 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"
50 * general utility function invoker
54 ProcessUtility(Node *parsetree,
57 char *commandTag = NULL;
61 switch (nodeTag(parsetree))
65 * ******************************** transactions ********************************
68 case T_TransactionStmt:
70 TransactionStmt *stmt = (TransactionStmt *) parsetree;
72 switch (stmt->command)
75 set_ps_display(commandTag = "BEGIN");
76 BeginTransactionBlock();
80 set_ps_display(commandTag = "COMMIT");
81 EndTransactionBlock();
85 set_ps_display(commandTag = "ROLLBACK");
86 UserAbortTransactionBlock();
93 * ******************************** portal manipulation ********************************
96 case T_ClosePortalStmt:
98 ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
100 set_ps_display(commandTag = "CLOSE");
102 PerformPortalClose(stmt->portalname, dest);
108 FetchStmt *stmt = (FetchStmt *) parsetree;
109 char *portalName = stmt->portalname;
113 set_ps_display(commandTag = (stmt->ismove) ? "MOVE" : "FETCH");
117 forward = (bool) (stmt->direction == FORWARD);
120 * parser ensures that count is >= 0 and 'fetch ALL' -> 0
123 count = stmt->howMany;
124 PerformPortalFetch(portalName, forward, count, commandTag,
125 (stmt->ismove) ? None : dest); /* /dev/null for MOVE */
130 * ******************************** relation and attribute
131 * manipulation ********************************
135 set_ps_display(commandTag = "CREATE");
137 DefineRelation((CreateStmt *) parsetree, RELKIND_RELATION);
140 * Let AlterTableCreateToastTable decide if this
141 * one needs a secondary relation too.
144 CommandCounterIncrement();
145 AlterTableCreateToastTable(((CreateStmt *)parsetree)->relname,
151 DropStmt *stmt = (DropStmt *) parsetree;
152 List *args = stmt->relNames;
155 set_ps_display(commandTag = "DROP");
157 /* check as much as we can before we start dropping ... */
162 relname = strVal(lfirst(arg));
163 if (!allowSystemTableMods && IsSystemRelationName(relname))
164 elog(ERROR, "class \"%s\" is a system catalog",
166 rel = heap_openr(relname, AccessExclusiveLock);
167 if (stmt->sequence &&
168 rel->rd_rel->relkind != RELKIND_SEQUENCE)
169 elog(ERROR, "Use DROP TABLE to drop table '%s'",
171 if (!(stmt->sequence) &&
172 rel->rd_rel->relkind == RELKIND_SEQUENCE)
173 elog(ERROR, "Use DROP SEQUENCE to drop sequence '%s'",
175 /* close rel, but keep lock until end of xact */
176 heap_close(rel, NoLock);
177 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
178 elog(ERROR, "you do not own class \"%s\"",
181 /* OK, terminate 'em all */
184 relname = strVal(lfirst(arg));
185 RemoveRelation(relname);
194 set_ps_display(commandTag = "TRUNCATE");
196 relname = ((TruncateStmt *) parsetree)->relName;
197 if (!allowSystemTableMods && IsSystemRelationName(relname))
198 elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
201 /* Grab exclusive lock in preparation for truncate... */
202 rel = heap_openr(relname, AccessExclusiveLock);
203 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
204 elog(ERROR, "TRUNCATE cannot be used on sequences. '%s' is a sequence",
206 if (rel->rd_rel->relkind == RELKIND_VIEW)
207 elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a sequence",
209 heap_close(rel, NoLock);
211 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
212 elog(ERROR, "you do not own class \"%s\"", relname);
213 TruncateRelation(relname);
219 CommentStmt *statement;
221 statement = ((CommentStmt *) parsetree);
223 set_ps_display(commandTag = "COMMENT");
225 CommentObject(statement->objtype, statement->objname,
226 statement->objproperty, statement->objlist,
233 CopyStmt *stmt = (CopyStmt *) parsetree;
235 set_ps_display(commandTag = "COPY");
237 if (stmt->direction != FROM)
240 DoCopy(stmt->relname,
243 (bool) (stmt->direction == FROM),
244 (bool) (stmt->filename == NULL),
247 * null filename means copy to/from stdout/stdin, rather
248 * than to/from a file.
261 RenameStmt *stmt = (RenameStmt *) parsetree;
263 set_ps_display(commandTag = "ALTER");
265 relname = stmt->relname;
266 if (!allowSystemTableMods && IsSystemRelationName(relname))
267 elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
269 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
270 elog(ERROR, "permission denied");
273 * XXX using len == 3 to tell the difference
274 * between "rename rel to newrel" and
275 * "rename att in rel to newatt" will not
276 * work soon because "rename type/operator/rule"
277 * stuff is being added. - cim 10/24/90
279 * [another piece of amuzing but useless anecdote -- ay]
281 if (stmt->column == NULL)
286 * Note: we also rename the "type" tuple
287 * corresponding to the relation.
290 renamerel(relname, /* old name */
291 stmt->newname); /* new name */
299 renameatt(relname, /* relname */
300 stmt->column, /* old att name */
301 stmt->newname, /* new att name */
302 stmt->inh); /* recursive? */
307 /* various Alter Table forms */
309 case T_AlterTableStmt:
311 AlterTableStmt *stmt = (AlterTableStmt *) parsetree;
313 set_ps_display(commandTag = "ALTER");
316 * Some or all of these functions are recursive to cover
317 * inherited things, so permission checks are done there.
319 switch (stmt->subtype)
321 case 'A': /* ADD COLUMN */
322 AlterTableAddColumn(stmt->relname, stmt->inh, (ColumnDef *) stmt->def);
324 case 'T': /* ALTER COLUMN */
325 AlterTableAlterColumn(stmt->relname, stmt->inh, stmt->name, stmt->def);
327 case 'D': /* ALTER DROP */
328 AlterTableDropColumn(stmt->relname, stmt->inh, stmt->name, stmt->behavior);
330 case 'C': /* ADD CONSTRAINT */
331 AlterTableAddConstraint(stmt->relname, stmt->inh, stmt->def);
333 case 'X': /* DROP CONSTRAINT */
334 AlterTableDropConstraint(stmt->relname, stmt->inh, stmt->name, stmt->behavior);
336 case 'E': /* CREATE TOAST TABLE */
337 AlterTableCreateToastTable(stmt->relname, false);
339 case 'U': /* ALTER OWNER */
340 AlterTableOwner(stmt->relname, stmt->name);
343 elog(ERROR, "T_AlterTableStmt: unknown subtype");
350 case T_ChangeACLStmt:
352 ChangeACLStmt *stmt = (ChangeACLStmt *) parsetree;
354 set_ps_display(commandTag = "CHANGE");
356 ExecuteChangeACLStmt(stmt);
361 * ******************************** object creation /
362 * destruction ********************************
367 DefineStmt *stmt = (DefineStmt *) parsetree;
369 set_ps_display(commandTag = "CREATE");
371 switch (stmt->defType)
374 DefineOperator(stmt->defname, /* operator name */
375 stmt->definition); /* rest */
378 DefineType(stmt->defname, stmt->definition);
381 DefineAggregate(stmt->defname, /* aggregate name */
382 stmt->definition); /* rest */
388 case T_ViewStmt: /* CREATE VIEW */
390 ViewStmt *stmt = (ViewStmt *) parsetree;
392 set_ps_display(commandTag = "CREATE");
394 DefineView(stmt->viewname, stmt->query); /* retrieve parsetree */
398 case T_ProcedureStmt: /* CREATE FUNCTION */
399 set_ps_display(commandTag = "CREATE");
401 CreateFunction((ProcedureStmt *) parsetree, dest); /* everything */
404 case T_IndexStmt: /* CREATE INDEX */
406 IndexStmt *stmt = (IndexStmt *) parsetree;
408 set_ps_display(commandTag = "CREATE");
410 DefineIndex(stmt->relname, /* relation name */
411 stmt->idxname, /* index name */
412 stmt->accessMethod, /* am name */
413 stmt->indexParams, /* parameters */
417 (Expr *) stmt->whereClause,
422 case T_RuleStmt: /* CREATE RULE */
424 RuleStmt *stmt = (RuleStmt *) parsetree;
427 relname = stmt->object->relname;
428 aclcheck_result = pg_aclcheck(relname, GetUserId(), ACL_RU);
429 if (aclcheck_result != ACLCHECK_OK)
430 elog(ERROR, "%s: %s", relname, aclcheck_error_strings[aclcheck_result]);
431 set_ps_display(commandTag = "CREATE");
433 DefineQueryRewrite(stmt);
437 case T_CreateSeqStmt:
438 set_ps_display(commandTag = "CREATE");
440 DefineSequence((CreateSeqStmt *) parsetree);
445 ExtendStmt *stmt = (ExtendStmt *) parsetree;
447 set_ps_display(commandTag = "EXTEND");
449 ExtendIndex(stmt->idxname, /* index name */
450 (Expr *) stmt->whereClause, /* where */
457 RemoveStmt *stmt = (RemoveStmt *) parsetree;
459 set_ps_display(commandTag = "DROP");
461 switch (stmt->removeType)
464 relname = stmt->name;
465 if (!allowSystemTableMods && IsSystemRelationName(relname))
466 elog(ERROR, "class \"%s\" is a system catalog index",
468 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
469 elog(ERROR, "%s: %s", relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
470 RemoveIndex(relname);
474 char *rulename = stmt->name;
477 relationName = RewriteGetRuleEventRel(rulename);
478 aclcheck_result = pg_aclcheck(relationName, GetUserId(), ACL_RU);
479 if (aclcheck_result != ACLCHECK_OK)
480 elog(ERROR, "%s: %s", relationName, aclcheck_error_strings[aclcheck_result]);
481 RemoveRewriteRule(rulename);
485 /* XXX moved to remove.c */
486 RemoveType(stmt->name);
490 char *viewName = stmt->name;
493 ruleName = MakeRetrieveViewRuleName(viewName);
494 relationName = RewriteGetRuleEventRel(ruleName);
495 if (!pg_ownercheck(GetUserId(), relationName, RELNAME))
496 elog(ERROR, "%s: %s", relationName, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
498 RemoveView(viewName);
506 case T_RemoveAggrStmt:
508 RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
509 char *typename = (char *) NULL;
511 set_ps_display(commandTag = "DROP");
513 if (stmt->aggtype != NULL)
514 typename = TypeNameToInternalName((TypeName *) stmt->aggtype);
516 RemoveAggregate(stmt->aggname, typename);
520 case T_RemoveFuncStmt:
522 RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;
524 set_ps_display(commandTag = "DROP");
526 RemoveFunction(stmt->funcname, stmt->args);
530 case T_RemoveOperStmt:
532 RemoveOperStmt *stmt = (RemoveOperStmt *) parsetree;
533 TypeName *typenode1 = (TypeName *) lfirst(stmt->args);
534 TypeName *typenode2 = (TypeName *) lsecond(stmt->args);
535 char *typename1 = (char *) NULL;
536 char *typename2 = (char *) NULL;
538 set_ps_display(commandTag = "DROP");
540 if (typenode1 != NULL)
541 typename1 = TypeNameToInternalName(typenode1);
542 if (typenode2 != NULL)
543 typename2 = TypeNameToInternalName(typenode2);
545 RemoveOperator(stmt->opname, typename1, typename2);
550 elog(ERROR, "CREATE VERSION is not currently implemented");
555 CreatedbStmt *stmt = (CreatedbStmt *) parsetree;
557 set_ps_display(commandTag = "CREATE DATABASE");
559 createdb(stmt->dbname, stmt->dbpath, stmt->encoding);
565 DropdbStmt *stmt = (DropdbStmt *) parsetree;
567 set_ps_display(commandTag = "DROP DATABASE");
569 dropdb(stmt->dbname);
573 /* Query-level asynchronous notification */
576 NotifyStmt *stmt = (NotifyStmt *) parsetree;
578 set_ps_display(commandTag = "NOTIFY");
580 Async_Notify(stmt->relname);
586 ListenStmt *stmt = (ListenStmt *) parsetree;
588 set_ps_display(commandTag = "LISTEN");
590 Async_Listen(stmt->relname, MyProcPid);
596 UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
598 set_ps_display(commandTag = "UNLISTEN");
600 Async_Unlisten(stmt->relname, MyProcPid);
605 * ******************************** dynamic loader ********************************
610 LoadStmt *stmt = (LoadStmt *) parsetree;
612 set_ps_display(commandTag = "LOAD");
614 closeAllVfds(); /* probably not necessary... */
615 load_file(stmt->filename);
621 ClusterStmt *stmt = (ClusterStmt *) parsetree;
623 set_ps_display(commandTag = "CLUSTER");
625 cluster(stmt->relname, stmt->indexname);
630 set_ps_display(commandTag = "VACUUM");
632 vacuum(((VacuumStmt *) parsetree)->vacrel,
633 ((VacuumStmt *) parsetree)->verbose,
634 ((VacuumStmt *) parsetree)->analyze,
635 ((VacuumStmt *) parsetree)->va_spec);
640 ExplainStmt *stmt = (ExplainStmt *) parsetree;
642 set_ps_display(commandTag = "EXPLAIN");
644 ExplainQuery(stmt->query, stmt->verbose, dest);
651 * ******************************** Tioga-related statements *******************************
655 RecipeStmt *stmt = (RecipeStmt *) parsetree;
657 set_ps_display(commandTag = "EXECUTE RECIPE");
665 * ******************************** set variable statements *******************************
667 case T_VariableSetStmt:
669 VariableSetStmt *n = (VariableSetStmt *) parsetree;
671 SetPGVariable(n->name, n->value);
672 set_ps_display(commandTag = "SET VARIABLE");
676 case T_VariableShowStmt:
678 VariableShowStmt *n = (VariableShowStmt *) parsetree;
680 GetPGVariable(n->name);
681 set_ps_display(commandTag = "SHOW VARIABLE");
685 case T_VariableResetStmt:
687 VariableResetStmt *n = (VariableResetStmt *) parsetree;
689 ResetPGVariable(n->name);
690 set_ps_display(commandTag = "RESET VARIABLE");
695 * ******************************** TRIGGER statements *******************************
697 case T_CreateTrigStmt:
698 set_ps_display(commandTag = "CREATE");
700 CreateTrigger((CreateTrigStmt *) parsetree);
704 set_ps_display(commandTag = "DROP");
706 DropTrigger((DropTrigStmt *) parsetree);
710 * ************* PROCEDURAL LANGUAGE statements *****************
712 case T_CreatePLangStmt:
713 set_ps_display(commandTag = "CREATE");
715 CreateProceduralLanguage((CreatePLangStmt *) parsetree);
718 case T_DropPLangStmt:
719 set_ps_display(commandTag = "DROP");
721 DropProceduralLanguage((DropPLangStmt *) parsetree);
725 * ******************************** USER statements ****
728 case T_CreateUserStmt:
729 set_ps_display(commandTag = "CREATE USER");
731 CreateUser((CreateUserStmt *) parsetree);
734 case T_AlterUserStmt:
735 set_ps_display(commandTag = "ALTER USER");
737 AlterUser((AlterUserStmt *) parsetree);
741 set_ps_display(commandTag = "DROP USER");
743 DropUser((DropUserStmt *) parsetree);
747 set_ps_display(commandTag = "LOCK TABLE");
749 LockTableCommand((LockStmt *) parsetree);
752 case T_ConstraintsSetStmt:
753 set_ps_display(commandTag = "SET CONSTRAINTS");
755 DeferredTriggerSetState((ConstraintsSetStmt *) parsetree);
758 case T_CreateGroupStmt:
759 set_ps_display(commandTag = "CREATE GROUP");
761 CreateGroup((CreateGroupStmt *) parsetree);
764 case T_AlterGroupStmt:
765 set_ps_display(commandTag = "ALTER GROUP");
767 AlterGroup((AlterGroupStmt *) parsetree, "ALTER GROUP");
770 case T_DropGroupStmt:
771 set_ps_display(commandTag = "DROP GROUP");
773 DropGroup((DropGroupStmt *) parsetree);
778 ReindexStmt *stmt = (ReindexStmt *) parsetree;
780 set_ps_display(commandTag = "REINDEX");
782 switch (stmt->reindexType)
785 relname = (char *) stmt->name;
786 if (IsSystemRelationName(relname))
788 if (!allowSystemTableMods && IsSystemRelationName(relname))
789 elog(ERROR, "\"%s\" is a system index. call REINDEX under standalone postgres with -O -P options",
791 if (!IsIgnoringSystemIndexes())
792 elog(ERROR, "\"%s\" is a system index. call REINDEX under standalone postgres with -P -O options",
795 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
796 elog(ERROR, "%s: %s", relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
797 ReindexIndex(relname, stmt->force);
800 relname = (char *) stmt->name;
801 if (IsSystemRelationName(relname))
803 if (!allowSystemTableMods && IsSystemRelationName(relname))
804 elog(ERROR, "\"%s\" is a system table. call REINDEX under standalone postgres with -O -P options",
806 if (!IsIgnoringSystemIndexes())
807 elog(ERROR, "\"%s\" is a system table. call REINDEX under standalone postgres with -P -O options",
811 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
812 elog(ERROR, "%s: %s", relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
813 ReindexTable(relname, stmt->force);
816 relname = (char *) stmt->name;
817 if (!allowSystemTableMods)
818 elog(ERROR, "must be called under standalone postgres with -O -P options");
819 if (!IsIgnoringSystemIndexes())
820 elog(ERROR, "must be called under standalone postgres with -P -O options");
821 ReindexDatabase(relname, stmt->force, false);
829 * ******************************** default ********************************
833 elog(ERROR, "ProcessUtility: command #%d unsupported",
839 * tell fe/be or whatever that we're done.
842 EndCommand(commandTag, dest);