postgres/src/backend/commands/operatorcmds.c
Tom Lane 7762619e95 Replace pg_shadow and pg_group by new role-capable catalogs pg_authid
and pg_auth_members.  There are still many loose ends to finish in this
patch (no documentation, no regression tests, no pg_dump support for
instance).  But I'm going to commit it now anyway so that Alvaro can
make some progress on shared dependencies.  The catalog changes should
be pretty much done.
2005-06-28 05:09:14 +00:00

319 lines
9.5 KiB
C

/*-------------------------------------------------------------------------
*
* operatorcmds.c
*
* Routines for operator manipulation commands
*
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.22 2005/06/28 05:08:54 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
* appropriate arguments/flags, passing the results to the
* corresponding "FooDefine" routines (in src/catalog) that do
* the actual catalog-munging. These routines also verify permission
* of the user to execute the command.
*
* NOTES
* These things must be defined and committed in the following order:
* "create function":
* input/output, recv/send procedures
* "create type":
* type
* "create operator":
* operators
*
* Most of the parse-tree manipulation routines are defined in
* commands/manip.c.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_operator.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "parser/parse_oper.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
/*
* DefineOperator
* this function extracts all the information from the
* parameter list generated by the parser and then has
* OperatorCreate() do all the actual work.
*
* 'parameters' is a list of DefElem
*/
void
DefineOperator(List *names, List *parameters)
{
char *oprName;
Oid oprNamespace;
AclResult aclresult;
bool canHash = false; /* operator hashes */
bool canMerge = false; /* operator merges */
List *functionName = NIL; /* function for operator */
TypeName *typeName1 = NULL; /* first type name */
TypeName *typeName2 = NULL; /* second type name */
Oid typeId1 = InvalidOid; /* types converted to OID */
Oid typeId2 = InvalidOid;
List *commutatorName = NIL; /* optional commutator operator
* name */
List *negatorName = NIL; /* optional negator operator name */
List *restrictionName = NIL; /* optional restrict. sel.
* procedure */
List *joinName = NIL; /* optional join sel. procedure */
List *leftSortName = NIL; /* optional left sort operator */
List *rightSortName = NIL; /* optional right sort operator */
List *ltCompareName = NIL; /* optional < compare operator */
List *gtCompareName = NIL; /* optional > compare operator */
ListCell *pl;
/* Convert list of names to a name and namespace */
oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
/* Check we have creation rights in target namespace */
aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(oprNamespace));
/*
* loop over the definition list and extract the information we need.
*/
foreach(pl, parameters)
{
DefElem *defel = (DefElem *) lfirst(pl);
if (pg_strcasecmp(defel->defname, "leftarg") == 0)
{
typeName1 = defGetTypeName(defel);
if (typeName1->setof)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("setof type not allowed for operator argument")));
}
else if (pg_strcasecmp(defel->defname, "rightarg") == 0)
{
typeName2 = defGetTypeName(defel);
if (typeName2->setof)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("setof type not allowed for operator argument")));
}
else if (pg_strcasecmp(defel->defname, "procedure") == 0)
functionName = defGetQualifiedName(defel);
else if (pg_strcasecmp(defel->defname, "commutator") == 0)
commutatorName = defGetQualifiedName(defel);
else if (pg_strcasecmp(defel->defname, "negator") == 0)
negatorName = defGetQualifiedName(defel);
else if (pg_strcasecmp(defel->defname, "restrict") == 0)
restrictionName = defGetQualifiedName(defel);
else if (pg_strcasecmp(defel->defname, "join") == 0)
joinName = defGetQualifiedName(defel);
else if (pg_strcasecmp(defel->defname, "hashes") == 0)
canHash = defGetBoolean(defel);
else if (pg_strcasecmp(defel->defname, "merges") == 0)
canMerge = defGetBoolean(defel);
else if (pg_strcasecmp(defel->defname, "sort1") == 0)
leftSortName = defGetQualifiedName(defel);
else if (pg_strcasecmp(defel->defname, "sort2") == 0)
rightSortName = defGetQualifiedName(defel);
else if (pg_strcasecmp(defel->defname, "ltcmp") == 0)
ltCompareName = defGetQualifiedName(defel);
else if (pg_strcasecmp(defel->defname, "gtcmp") == 0)
gtCompareName = defGetQualifiedName(defel);
else
ereport(WARNING,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("operator attribute \"%s\" not recognized",
defel->defname)));
}
/*
* make sure we have our required definitions
*/
if (functionName == NIL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("operator procedure must be specified")));
/* Transform type names to type OIDs */
if (typeName1)
typeId1 = typenameTypeId(typeName1);
if (typeName2)
typeId2 = typenameTypeId(typeName2);
/*
* If any of the mergejoin support operators were given, then canMerge
* is implicit. If canMerge is specified or implicit, fill in default
* operator names for any missing mergejoin support operators.
*/
if (leftSortName || rightSortName || ltCompareName || gtCompareName)
canMerge = true;
if (canMerge)
{
if (!leftSortName)
leftSortName = list_make1(makeString("<"));
if (!rightSortName)
rightSortName = list_make1(makeString("<"));
if (!ltCompareName)
ltCompareName = list_make1(makeString("<"));
if (!gtCompareName)
gtCompareName = list_make1(makeString(">"));
}
/*
* now have OperatorCreate do all the work..
*/
OperatorCreate(oprName, /* operator name */
oprNamespace, /* namespace */
typeId1, /* left type id */
typeId2, /* right type id */
functionName, /* function for operator */
commutatorName, /* optional commutator operator
* name */
negatorName, /* optional negator operator name */
restrictionName, /* optional restrict. sel.
* procedure */
joinName, /* optional join sel. procedure name */
canHash, /* operator hashes */
leftSortName, /* optional left sort operator */
rightSortName, /* optional right sort operator */
ltCompareName, /* optional < comparison op */
gtCompareName); /* optional < comparison op */
}
/*
* RemoveOperator
* Deletes an operator.
*/
void
RemoveOperator(RemoveOperStmt *stmt)
{
List *operatorName = stmt->opname;
TypeName *typeName1 = (TypeName *) linitial(stmt->args);
TypeName *typeName2 = (TypeName *) lsecond(stmt->args);
Oid operOid;
HeapTuple tup;
ObjectAddress object;
operOid = LookupOperNameTypeNames(operatorName, typeName1, typeName2,
false);
tup = SearchSysCache(OPEROID,
ObjectIdGetDatum(operOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for operator %u", operOid);
/* Permission check: must own operator or its namespace */
if (!pg_oper_ownercheck(operOid, GetUserId()) &&
!pg_namespace_ownercheck(((Form_pg_operator) GETSTRUCT(tup))->oprnamespace,
GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
NameListToString(operatorName));
ReleaseSysCache(tup);
/*
* Do the deletion
*/
object.classId = OperatorRelationId;
object.objectId = operOid;
object.objectSubId = 0;
performDeletion(&object, stmt->behavior);
}
/*
* Guts of operator deletion.
*/
void
RemoveOperatorById(Oid operOid)
{
Relation relation;
HeapTuple tup;
relation = heap_open(OperatorRelationId, RowExclusiveLock);
tup = SearchSysCache(OPEROID,
ObjectIdGetDatum(operOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for operator %u", operOid);
simple_heap_delete(relation, &tup->t_self);
ReleaseSysCache(tup);
heap_close(relation, RowExclusiveLock);
}
/*
* change operator owner
*/
void
AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
Oid newOwnerId)
{
Oid operOid;
HeapTuple tup;
Relation rel;
Form_pg_operator oprForm;
rel = heap_open(OperatorRelationId, RowExclusiveLock);
operOid = LookupOperNameTypeNames(name, typeName1, typeName2,
false);
tup = SearchSysCacheCopy(OPEROID,
ObjectIdGetDatum(operOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for operator %u", operOid);
oprForm = (Form_pg_operator) GETSTRUCT(tup);
/*
* If the new owner is the same as the existing owner, consider the
* command to have succeeded. This is for dump restoration purposes.
*/
if (oprForm->oprowner != newOwnerId)
{
/* Otherwise, must be superuser to change object ownership */
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to change owner")));
/*
* Modify the owner --- okay to scribble on tup because it's a
* copy
*/
oprForm->oprowner = newOwnerId;
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
}
heap_close(rel, NoLock);
heap_freetuple(tup);
}