postgres/src/backend/tcop/utility.c
Tom Lane 708f82f191 Fix bug noted by Bruce: FETCH in an already-aborted transaction block
would crash, due to premature invocation of SetQuerySnapshot().  Clean
up problems with handling of multiple queries by splitting
pg_parse_and_plan into two routines.  The old code would not, for
example, do the right thing with END; SELECT... submitted in one query
string when it had been in transaction abort state, because it'd decide
to skip planning the SELECT before it had executed the END.  New
arrangement is simpler and doesn't force caller to plan if only
parse+rewrite is needed.
2000-04-04 21:44:40 +00:00

925 lines
23 KiB
C

/*-------------------------------------------------------------------------
*
* utility.c
* Contains functions which control the execution of the POSTGRES utility
* commands. At one time acted as an interface between the Lisp and C
* systems.
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.85 2000/04/04 21:44:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/catalog.h"
#include "catalog/pg_type.h"
#include "commands/async.h"
#include "commands/cluster.h"
#include "commands/command.h"
#include "commands/comment.h"
#include "commands/copy.h"
#include "commands/creatinh.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/explain.h"
#include "commands/proclang.h"
#include "commands/rename.h"
#include "commands/sequence.h"
#include "commands/trigger.h"
#include "commands/user.h"
#include "commands/vacuum.h"
#include "commands/variable.h"
#include "commands/view.h"
#include "miscadmin.h"
#include "parser/parse.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteRemove.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/ps_status.h"
#include "utils/syscache.h"
/* ----------------
* CHECK_IF_ABORTED() is used to avoid doing unnecessary
* processing within an aborted transaction block.
* ----------------
*/
/* we have to use IF because of the 'break' */
#define CHECK_IF_ABORTED() \
if (1) \
{ \
if (IsAbortedTransactionBlockState()) \
{ \
elog(NOTICE, "current transaction is aborted, " \
"queries ignored until end of transaction block"); \
commandTag = "*ABORT STATE*"; \
break; \
} \
} else
/* ----------------
* general utility function invoker
* ----------------
*/
void
ProcessUtility(Node *parsetree,
CommandDest dest)
{
char *commandTag = NULL;
char *relname;
char *relationName;
char *userName;
userName = GetPgUserName();
switch (nodeTag(parsetree))
{
/*
* ******************************** transactions ********************************
*
*/
case T_TransactionStmt:
{
TransactionStmt *stmt = (TransactionStmt *) parsetree;
switch (stmt->command)
{
case BEGIN_TRANS:
PS_SET_STATUS(commandTag = "BEGIN");
CHECK_IF_ABORTED();
BeginTransactionBlock();
break;
case COMMIT:
PS_SET_STATUS(commandTag = "COMMIT");
EndTransactionBlock();
break;
case ROLLBACK:
PS_SET_STATUS(commandTag = "ROLLBACK");
UserAbortTransactionBlock();
break;
}
}
break;
/*
* ******************************** portal manipulation ********************************
*
*/
case T_ClosePortalStmt:
{
ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
PS_SET_STATUS(commandTag = "CLOSE");
CHECK_IF_ABORTED();
PerformPortalClose(stmt->portalname, dest);
}
break;
case T_FetchStmt:
{
FetchStmt *stmt = (FetchStmt *) parsetree;
char *portalName = stmt->portalname;
bool forward;
int count;
PS_SET_STATUS(commandTag = (stmt->ismove) ? "MOVE" : "FETCH");
CHECK_IF_ABORTED();
SetQuerySnapshot();
forward = (bool) (stmt->direction == FORWARD);
/*
* parser ensures that count is >= 0 and 'fetch ALL' -> 0
*/
count = stmt->howMany;
PerformPortalFetch(portalName, forward, count, commandTag,
(stmt->ismove) ? None : dest); /* /dev/null for MOVE */
}
break;
/*
* ******************************** relation and attribute
* manipulation ********************************
*
*/
case T_CreateStmt:
PS_SET_STATUS(commandTag = "CREATE");
CHECK_IF_ABORTED();
DefineRelation((CreateStmt *) parsetree, RELKIND_RELATION);
break;
case T_DropStmt:
{
DropStmt *stmt = (DropStmt *) parsetree;
List *args = stmt->relNames;
List *arg;
PS_SET_STATUS(commandTag = "DROP");
CHECK_IF_ABORTED();
/* check as much as we can before we start dropping ... */
foreach(arg, args)
{
Relation rel;
relname = strVal(lfirst(arg));
if (!allowSystemTableMods && IsSystemRelationName(relname))
elog(ERROR, "class \"%s\" is a system catalog",
relname);
rel = heap_openr(relname, AccessExclusiveLock);
if (stmt->sequence &&
rel->rd_rel->relkind != RELKIND_SEQUENCE)
elog(ERROR, "Use DROP TABLE to drop table '%s'",
relname);
if (!(stmt->sequence) &&
rel->rd_rel->relkind == RELKIND_SEQUENCE)
elog(ERROR, "Use DROP SEQUENCE to drop sequence '%s'",
relname);
/* close rel, but keep lock until end of xact */
heap_close(rel, NoLock);
#ifndef NO_SECURITY
if (!pg_ownercheck(userName, relname, RELNAME))
elog(ERROR, "you do not own class \"%s\"",
relname);
#endif
}
/* OK, terminate 'em all */
foreach(arg, args)
{
relname = strVal(lfirst(arg));
RemoveRelation(relname);
}
}
break;
case T_TruncateStmt:
{
Relation rel;
PS_SET_STATUS(commandTag = "TRUNCATE");
CHECK_IF_ABORTED();
relname = ((TruncateStmt *) parsetree)->relName;
if (!allowSystemTableMods && IsSystemRelationName(relname))
elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
relname);
/* Grab exclusive lock in preparation for truncate... */
rel = heap_openr(relname, AccessExclusiveLock);
if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
elog(ERROR, "TRUNCATE cannot be used on sequences. '%s' is a sequence",
relname);
heap_close(rel, NoLock);
#ifndef NO_SECURITY
if (!pg_ownercheck(userName, relname, RELNAME))
elog(ERROR, "you do not own class \"%s\"", relname);
#endif
TruncateRelation(relname);
}
break;
case T_CommentStmt:
{
CommentStmt *statement;
statement = ((CommentStmt *) parsetree);
PS_SET_STATUS(commandTag = "COMMENT");
CHECK_IF_ABORTED();
CommentObject(statement->objtype, statement->objname,
statement->objproperty, statement->objlist,
statement->comment);
}
break;
case T_CopyStmt:
{
CopyStmt *stmt = (CopyStmt *) parsetree;
PS_SET_STATUS(commandTag = "COPY");
CHECK_IF_ABORTED();
if (stmt->direction != FROM)
SetQuerySnapshot();
DoCopy(stmt->relname,
stmt->binary,
stmt->oids,
(bool) (stmt->direction == FROM),
(bool) (stmt->filename == NULL),
/*
* null filename means copy to/from stdout/stdin, rather
* than to/from a file.
*/
stmt->filename,
stmt->delimiter,
stmt->null_print);
}
break;
/*
* schema
*/
case T_RenameStmt:
{
RenameStmt *stmt = (RenameStmt *) parsetree;
PS_SET_STATUS(commandTag = "ALTER");
CHECK_IF_ABORTED();
relname = stmt->relname;
if (!allowSystemTableMods && IsSystemRelationName(relname))
elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
relname);
#ifndef NO_SECURITY
if (!pg_ownercheck(userName, relname, RELNAME))
elog(ERROR, "permission denied");
#endif
/* ----------------
* XXX using len == 3 to tell the difference
* between "rename rel to newrel" and
* "rename att in rel to newatt" will not
* work soon because "rename type/operator/rule"
* stuff is being added. - cim 10/24/90
* ----------------
* [another piece of amuzing but useless anecdote -- ay]
*/
if (stmt->column == NULL)
{
/* ----------------
* rename relation
*
* Note: we also rename the "type" tuple
* corresponding to the relation.
* ----------------
*/
renamerel(relname, /* old name */
stmt->newname); /* new name */
TypeRename(relname, /* old name */
stmt->newname); /* new name */
}
else
{
/* ----------------
* rename attribute
* ----------------
*/
renameatt(relname, /* relname */
stmt->column, /* old att name */
stmt->newname, /* new att name */
userName,
stmt->inh); /* recursive? */
}
}
break;
/* various Alter Table forms */
case T_AlterTableStmt:
{
AlterTableStmt *stmt = (AlterTableStmt *) parsetree;
PS_SET_STATUS(commandTag = "ALTER");
CHECK_IF_ABORTED();
/*
* Some or all of these functions are recursive to cover inherited things,
* so permission checks are done there.
*/
switch(stmt->subtype)
{
case 'A': /* ADD COLUMN */
AlterTableAddColumn(stmt->relname, stmt->inh, (ColumnDef *) stmt->def);
break;
case 'T': /* ALTER COLUMN */
AlterTableAlterColumn(stmt->relname, stmt->inh, stmt->name, stmt->def);
break;
case 'D': /* ALTER DROP */
AlterTableDropColumn(stmt->relname, stmt->inh, stmt->name, stmt->behavior);
break;
case 'C': /* ADD CONSTRAINT */
AlterTableAddConstraint(stmt->relname, stmt->inh, stmt->def);
break;
case 'X': /* DROP CONSTRAINT */
AlterTableDropConstraint(stmt->relname, stmt->inh, stmt->name, stmt->behavior);
break;
default: /* oops */
elog(ERROR, "T_AlterTableStmt: unknown subtype");
break;
}
}
break;
case T_ChangeACLStmt:
{
ChangeACLStmt *stmt = (ChangeACLStmt *) parsetree;
List *i;
AclItem *aip;
unsigned modechg;
PS_SET_STATUS(commandTag = "CHANGE");
CHECK_IF_ABORTED();
aip = stmt->aclitem;
modechg = stmt->modechg;
foreach(i, stmt->relNames)
{
Relation rel;
relname = strVal(lfirst(i));
rel = heap_openr(relname, AccessExclusiveLock);
if (rel && rel->rd_rel->relkind == RELKIND_INDEX)
elog(ERROR, "\"%s\" is an index relation",
relname);
/* close rel, but keep lock until end of xact */
heap_close(rel, NoLock);
#ifndef NO_SECURITY
if (!pg_ownercheck(userName, relname, RELNAME))
elog(ERROR, "you do not own class \"%s\"",
relname);
#endif
ChangeAcl(relname, aip, modechg);
}
}
break;
/*
* ******************************** object creation /
* destruction ********************************
*
*/
case T_DefineStmt:
{
DefineStmt *stmt = (DefineStmt *) parsetree;
PS_SET_STATUS(commandTag = "CREATE");
CHECK_IF_ABORTED();
switch (stmt->defType)
{
case OPERATOR:
DefineOperator(stmt->defname, /* operator name */
stmt->definition); /* rest */
break;
case TYPE_P:
DefineType(stmt->defname, stmt->definition);
break;
case AGGREGATE:
DefineAggregate(stmt->defname, /* aggregate name */
stmt->definition); /* rest */
break;
}
}
break;
case T_ViewStmt: /* CREATE VIEW */
{
ViewStmt *stmt = (ViewStmt *) parsetree;
PS_SET_STATUS(commandTag = "CREATE");
CHECK_IF_ABORTED();
DefineView(stmt->viewname, stmt->query); /* retrieve parsetree */
}
break;
case T_ProcedureStmt: /* CREATE FUNCTION */
PS_SET_STATUS(commandTag = "CREATE");
CHECK_IF_ABORTED();
CreateFunction((ProcedureStmt *) parsetree, dest); /* everything */
break;
case T_IndexStmt: /* CREATE INDEX */
{
IndexStmt *stmt = (IndexStmt *) parsetree;
PS_SET_STATUS(commandTag = "CREATE");
CHECK_IF_ABORTED();
DefineIndex(stmt->relname, /* relation name */
stmt->idxname, /* index name */
stmt->accessMethod, /* am name */
stmt->indexParams, /* parameters */
stmt->withClause,
stmt->unique,
stmt->primary,
(Expr *) stmt->whereClause,
stmt->rangetable);
}
break;
case T_RuleStmt: /* CREATE RULE */
{
RuleStmt *stmt = (RuleStmt *) parsetree;
int aclcheck_result;
#ifndef NO_SECURITY
relname = stmt->object->relname;
aclcheck_result = pg_aclcheck(relname, userName, ACL_RU);
if (aclcheck_result != ACLCHECK_OK)
elog(ERROR, "%s: %s", relname, aclcheck_error_strings[aclcheck_result]);
#endif
PS_SET_STATUS(commandTag = "CREATE");
CHECK_IF_ABORTED();
DefineQueryRewrite(stmt);
}
break;
case T_CreateSeqStmt:
PS_SET_STATUS(commandTag = "CREATE");
CHECK_IF_ABORTED();
DefineSequence((CreateSeqStmt *) parsetree);
break;
case T_ExtendStmt:
{
ExtendStmt *stmt = (ExtendStmt *) parsetree;
PS_SET_STATUS(commandTag = "EXTEND");
CHECK_IF_ABORTED();
ExtendIndex(stmt->idxname, /* index name */
(Expr *) stmt->whereClause, /* where */
stmt->rangetable);
}
break;
case T_RemoveStmt:
{
RemoveStmt *stmt = (RemoveStmt *) parsetree;
PS_SET_STATUS(commandTag = "DROP");
CHECK_IF_ABORTED();
switch (stmt->removeType)
{
case INDEX:
relname = stmt->name;
if (!allowSystemTableMods && IsSystemRelationName(relname))
elog(ERROR, "class \"%s\" is a system catalog index",
relname);
#ifndef NO_SECURITY
if (!pg_ownercheck(userName, relname, RELNAME))
elog(ERROR, "%s: %s", relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
#endif
RemoveIndex(relname);
break;
case RULE:
{
char *rulename = stmt->name;
int aclcheck_result;
#ifndef NO_SECURITY
relationName = RewriteGetRuleEventRel(rulename);
aclcheck_result = pg_aclcheck(relationName, userName, ACL_RU);
if (aclcheck_result != ACLCHECK_OK)
elog(ERROR, "%s: %s", relationName, aclcheck_error_strings[aclcheck_result]);
#endif
RemoveRewriteRule(rulename);
}
break;
case TYPE_P:
#ifndef NO_SECURITY
/* XXX moved to remove.c */
#endif
RemoveType(stmt->name);
break;
case VIEW:
{
char *viewName = stmt->name;
char *ruleName;
#ifndef NO_SECURITY
ruleName = MakeRetrieveViewRuleName(viewName);
relationName = RewriteGetRuleEventRel(ruleName);
if (!pg_ownercheck(userName, relationName, RELNAME))
elog(ERROR, "%s: %s", relationName, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
pfree(ruleName);
#endif
RemoveView(viewName);
}
break;
}
break;
}
break;
case T_RemoveAggrStmt:
{
RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
PS_SET_STATUS(commandTag = "DROP");
CHECK_IF_ABORTED();
RemoveAggregate(stmt->aggname, stmt->aggtype);
}
break;
case T_RemoveFuncStmt:
{
RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;
PS_SET_STATUS(commandTag = "DROP");
CHECK_IF_ABORTED();
RemoveFunction(stmt->funcname,
length(stmt->args),
stmt->args);
}
break;
case T_RemoveOperStmt:
{
RemoveOperStmt *stmt = (RemoveOperStmt *) parsetree;
char *type1 = (char *) NULL;
char *type2 = (char *) NULL;
PS_SET_STATUS(commandTag = "DROP");
CHECK_IF_ABORTED();
if (lfirst(stmt->args) != NULL)
type1 = strVal(lfirst(stmt->args));
if (lsecond(stmt->args) != NULL)
type2 = strVal(lsecond(stmt->args));
RemoveOperator(stmt->opname, type1, type2);
}
break;
case T_VersionStmt:
elog(ERROR, "CREATE VERSION is not currently implemented");
break;
case T_CreatedbStmt:
{
CreatedbStmt *stmt = (CreatedbStmt *) parsetree;
PS_SET_STATUS(commandTag = "CREATE DATABASE");
CHECK_IF_ABORTED();
createdb(stmt->dbname, stmt->dbpath, stmt->encoding);
}
break;
case T_DropdbStmt:
{
DropdbStmt *stmt = (DropdbStmt *) parsetree;
PS_SET_STATUS(commandTag = "DROP DATABASE");
CHECK_IF_ABORTED();
dropdb(stmt->dbname);
}
break;
/* Query-level asynchronous notification */
case T_NotifyStmt:
{
NotifyStmt *stmt = (NotifyStmt *) parsetree;
PS_SET_STATUS(commandTag = "NOTIFY");
CHECK_IF_ABORTED();
Async_Notify(stmt->relname);
}
break;
case T_ListenStmt:
{
ListenStmt *stmt = (ListenStmt *) parsetree;
PS_SET_STATUS(commandTag = "LISTEN");
CHECK_IF_ABORTED();
Async_Listen(stmt->relname, MyProcPid);
}
break;
case T_UnlistenStmt:
{
UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
PS_SET_STATUS(commandTag = "UNLISTEN");
CHECK_IF_ABORTED();
Async_Unlisten(stmt->relname, MyProcPid);
}
break;
/*
* ******************************** dynamic loader ********************************
*
*/
case T_LoadStmt:
{
LoadStmt *stmt = (LoadStmt *) parsetree;
PS_SET_STATUS(commandTag = "LOAD");
CHECK_IF_ABORTED();
closeAllVfds(); /* probably not necessary... */
load_file(stmt->filename);
}
break;
case T_ClusterStmt:
{
ClusterStmt *stmt = (ClusterStmt *) parsetree;
PS_SET_STATUS(commandTag = "CLUSTER");
CHECK_IF_ABORTED();
cluster(stmt->relname, stmt->indexname);
}
break;
case T_VacuumStmt:
PS_SET_STATUS(commandTag = "VACUUM");
CHECK_IF_ABORTED();
vacuum(((VacuumStmt *) parsetree)->vacrel,
((VacuumStmt *) parsetree)->verbose,
((VacuumStmt *) parsetree)->analyze,
((VacuumStmt *) parsetree)->va_spec);
break;
case T_ExplainStmt:
{
ExplainStmt *stmt = (ExplainStmt *) parsetree;
PS_SET_STATUS(commandTag = "EXPLAIN");
CHECK_IF_ABORTED();
ExplainQuery(stmt->query, stmt->verbose, dest);
}
break;
#ifdef NOT_USED
/*
* ******************************** Tioga-related statements *******************************
*/
case T_RecipeStmt:
{
RecipeStmt *stmt = (RecipeStmt *) parsetree;
PS_SET_STATUS(commandTag = "EXECUTE RECIPE");
CHECK_IF_ABORTED();
beginRecipe(stmt);
}
break;
#endif
/*
* ******************************** set variable statements *******************************
*/
case T_VariableSetStmt:
{
VariableSetStmt *n = (VariableSetStmt *) parsetree;
SetPGVariable(n->name, n->value);
PS_SET_STATUS(commandTag = "SET VARIABLE");
}
break;
case T_VariableShowStmt:
{
VariableShowStmt *n = (VariableShowStmt *) parsetree;
GetPGVariable(n->name);
PS_SET_STATUS(commandTag = "SHOW VARIABLE");
}
break;
case T_VariableResetStmt:
{
VariableResetStmt *n = (VariableResetStmt *) parsetree;
ResetPGVariable(n->name);
PS_SET_STATUS(commandTag = "RESET VARIABLE");
}
break;
/*
* ******************************** TRIGGER statements *******************************
*/
case T_CreateTrigStmt:
PS_SET_STATUS(commandTag = "CREATE");
CHECK_IF_ABORTED();
CreateTrigger((CreateTrigStmt *) parsetree);
break;
case T_DropTrigStmt:
PS_SET_STATUS(commandTag = "DROP");
CHECK_IF_ABORTED();
DropTrigger((DropTrigStmt *) parsetree);
break;
/*
* ************* PROCEDURAL LANGUAGE statements *****************
*/
case T_CreatePLangStmt:
PS_SET_STATUS(commandTag = "CREATE");
CHECK_IF_ABORTED();
CreateProceduralLanguage((CreatePLangStmt *) parsetree);
break;
case T_DropPLangStmt:
PS_SET_STATUS(commandTag = "DROP");
CHECK_IF_ABORTED();
DropProceduralLanguage((DropPLangStmt *) parsetree);
break;
/*
* ******************************** USER statements ****
*
*/
case T_CreateUserStmt:
PS_SET_STATUS(commandTag = "CREATE USER");
CHECK_IF_ABORTED();
CreateUser((CreateUserStmt *) parsetree);
break;
case T_AlterUserStmt:
PS_SET_STATUS(commandTag = "ALTER USER");
CHECK_IF_ABORTED();
AlterUser((AlterUserStmt *) parsetree);
break;
case T_DropUserStmt:
PS_SET_STATUS(commandTag = "DROP USER");
CHECK_IF_ABORTED();
DropUser((DropUserStmt *) parsetree);
break;
case T_LockStmt:
PS_SET_STATUS(commandTag = "LOCK TABLE");
CHECK_IF_ABORTED();
LockTableCommand((LockStmt *) parsetree);
break;
case T_ConstraintsSetStmt:
PS_SET_STATUS(commandTag = "SET CONSTRAINTS");
CHECK_IF_ABORTED();
DeferredTriggerSetState((ConstraintsSetStmt *) parsetree);
break;
case T_CreateGroupStmt:
PS_SET_STATUS(commandTag = "CREATE GROUP");
CHECK_IF_ABORTED();
CreateGroup((CreateGroupStmt *) parsetree);
break;
case T_AlterGroupStmt:
PS_SET_STATUS(commandTag = "ALTER GROUP");
CHECK_IF_ABORTED();
AlterGroup((AlterGroupStmt *) parsetree, "ALTER GROUP");
break;
case T_DropGroupStmt:
PS_SET_STATUS(commandTag = "DROP GROUP");
CHECK_IF_ABORTED();
DropGroup((DropGroupStmt *) parsetree);
break;
case T_ReindexStmt:
{
ReindexStmt *stmt = (ReindexStmt *) parsetree;
PS_SET_STATUS(commandTag = "REINDEX");
CHECK_IF_ABORTED();
switch (stmt->reindexType)
{
case INDEX:
relname = (char*) stmt->name;
if (IsSystemRelationName(relname))
{
if (!allowSystemTableMods && IsSystemRelationName(relname))
elog(ERROR, "class \"%s\" is a system catalog index",
relname);
if (!IsIgnoringSystemIndexes())
elog(ERROR, "class \"%s\" is a system catalog index",
relname);
}
#ifndef NO_SECURITY
if (!pg_ownercheck(userName, relname, RELNAME))
elog(ERROR, "%s: %s", relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
#endif
ReindexIndex(relname, stmt->force);
break;
case TABLE:
relname = (char*) stmt->name;
if (IsSystemRelationName(relname))
{
if (!allowSystemTableMods && IsSystemRelationName(relname))
elog(ERROR, "class \"%s\" is a system catalog index",
relname);
if (!IsIgnoringSystemIndexes())
elog(ERROR, "class \"%s\" is a system catalog index",
relname);
}
#ifndef NO_SECURITY
if (!pg_ownercheck(userName, relname, RELNAME))
elog(ERROR, "%s: %s", relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
#endif
ReindexTable(relname, stmt->force);
break;
case DATABASE:
relname = (char*) stmt->name;
if (!allowSystemTableMods)
elog(ERROR, "-O option is needed");
if (!IsIgnoringSystemIndexes())
elog(ERROR, "-P option is needed");
ReindexDatabase(relname, stmt->force, false);
break;
}
break;
}
break;
/*
* ******************************** default ********************************
*
*/
default:
elog(ERROR, "ProcessUtility: command #%d unsupported",
nodeTag(parsetree));
break;
}
/* ----------------
* tell fe/be or whatever that we're done.
* ----------------
*/
EndCommand(commandTag, dest);
}