1
0
mirror of https://github.com/postgres/postgres.git synced 2025-10-16 17:07:43 +03:00

Further consolidation of DROP statement handling.

This gets rid of an impressive amount of duplicative code, with only
minimal behavior changes.  DROP FOREIGN DATA WRAPPER now requires object
ownership rather than superuser privileges, matching the documentation
we already have.  We also eliminate the historical warning about dropping
a built-in function as unuseful.  All operations are now performed in the
same order for all object types handled by dropcmds.c.

KaiGai Kohei, with minor revisions by me
This commit is contained in:
Robert Haas
2011-11-17 21:31:29 -05:00
parent 709aca5960
commit fc6d1006bd
21 changed files with 241 additions and 1148 deletions

View File

@@ -208,58 +208,9 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
/*
* RemoveAggregate
* Deletes an aggregate.
* RenameAggregate
* Rename an aggregate.
*/
void
RemoveAggregate(RemoveFuncStmt *stmt)
{
List *aggName = stmt->name;
List *aggArgs = stmt->args;
Oid procOid;
HeapTuple tup;
ObjectAddress object;
/* Look up function and make sure it's an aggregate */
procOid = LookupAggNameTypeNames(aggName, aggArgs, stmt->missing_ok);
if (!OidIsValid(procOid))
{
/* we only get here if stmt->missing_ok is true */
ereport(NOTICE,
(errmsg("aggregate %s(%s) does not exist, skipping",
NameListToString(aggName),
TypeNameListToString(aggArgs))));
return;
}
/*
* Find the function tuple, do permissions and validity checks
*/
tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", procOid);
/* Permission check: must own agg or its namespace */
if (!pg_proc_ownercheck(procOid, GetUserId()) &&
!pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace,
GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(aggName));
ReleaseSysCache(tup);
/*
* Do the deletion
*/
object.classId = ProcedureRelationId;
object.objectId = procOid;
object.objectSubId = 0;
performDeletion(&object, stmt->behavior);
}
void
RenameAggregate(List *name, List *args, const char *newname)
{

View File

@@ -20,13 +20,17 @@
#include "catalog/namespace.h"
#include "catalog/objectaddress.h"
#include "catalog/pg_class.h"
#include "catalog/pg_proc.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
static void does_not_exist_skipping(ObjectType objtype, List *objname);
static void does_not_exist_skipping(ObjectType objtype,
List *objname, List *objargs);
/*
* Drop one or more objects.
@@ -44,6 +48,7 @@ RemoveObjects(DropStmt *stmt)
{
ObjectAddresses *objects;
ListCell *cell1;
ListCell *cell2 = NULL;
objects = new_object_addresses();
@@ -51,12 +56,19 @@ RemoveObjects(DropStmt *stmt)
{
ObjectAddress address;
List *objname = lfirst(cell1);
List *objargs = NIL;
Relation relation = NULL;
Oid namespaceId;
if (stmt->arguments)
{
cell2 = (!cell2 ? list_head(stmt->arguments) : lnext(cell2));
objargs = lfirst(cell2);
}
/* Get an ObjectAddress for the object. */
address = get_object_address(stmt->removeType,
objname, NIL,
objname, objargs,
&relation,
AccessExclusiveLock,
stmt->missing_ok);
@@ -64,16 +76,40 @@ RemoveObjects(DropStmt *stmt)
/* Issue NOTICE if supplied object was not found. */
if (!OidIsValid(address.objectId))
{
does_not_exist_skipping(stmt->removeType, objname);
does_not_exist_skipping(stmt->removeType, objname, objargs);
continue;
}
/*
* Although COMMENT ON FUNCTION, SECURITY LABEL ON FUNCTION, etc. are
* happy to operate on an aggregate as on any other function, we have
* historically not allowed this for DROP FUNCTION.
*/
if (stmt->removeType == OBJECT_FUNCTION)
{
Oid funcOid = address.objectId;
HeapTuple tup;
tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", funcOid);
if (((Form_pg_proc) GETSTRUCT(tup))->proisagg)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is an aggregate function",
NameListToString(objname)),
errhint("Use DROP AGGREGATE to drop aggregate functions.")));
ReleaseSysCache(tup);
}
/* Check permissions. */
namespaceId = get_object_namespace(&address);
if (!OidIsValid(namespaceId) ||
!pg_namespace_ownercheck(namespaceId, GetUserId()))
check_object_ownership(GetUserId(), stmt->removeType, address,
objname, NIL, relation);
objname, objargs, relation);
/* Release any relcache reference count, but keep lock until commit. */
if (relation)
@@ -94,10 +130,11 @@ RemoveObjects(DropStmt *stmt)
* get_object_address() will throw an ERROR.
*/
static void
does_not_exist_skipping(ObjectType objtype, List *objname)
does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs)
{
const char *msg = NULL;
char *name = NULL;
char *args = NULL;
switch (objtype)
{
@@ -138,10 +175,68 @@ does_not_exist_skipping(ObjectType objtype, List *objname)
msg = gettext_noop("extension \"%s\" does not exist, skipping");
name = NameListToString(objname);
break;
case OBJECT_FUNCTION:
msg = gettext_noop("function %s(%s) does not exist, skipping");
name = NameListToString(objname);
args = TypeNameListToString(objargs);
break;
case OBJECT_AGGREGATE:
msg = gettext_noop("aggregate %s(%s) does not exist, skipping");
name = NameListToString(objname);
args = TypeNameListToString(objargs);
break;
case OBJECT_OPERATOR:
msg = gettext_noop("operator %s does not exist, skipping");
name = NameListToString(objname);
break;
case OBJECT_LANGUAGE:
msg = gettext_noop("language \"%s\" does not exist, skipping");
name = NameListToString(objname);
break;
case OBJECT_CAST:
msg = gettext_noop("cast from type %s to type %s does not exist, skipping");
name = format_type_be(typenameTypeId(NULL,
(TypeName *) linitial(objname)));
args = format_type_be(typenameTypeId(NULL,
(TypeName *) linitial(objargs)));
break;
case OBJECT_TRIGGER:
msg = gettext_noop("trigger \"%s\" for table \"%s\" does not exist, skipping");
name = strVal(llast(objname));
args = NameListToString(list_truncate(objname,
list_length(objname) - 1));
break;
case OBJECT_RULE:
msg = gettext_noop("rule \"%s\" for relation \"%s\" does not exist, skipping");
name = strVal(llast(objname));
args = NameListToString(list_truncate(objname,
list_length(objname) - 1));
break;
case OBJECT_FDW:
msg = gettext_noop("foreign-data wrapper \"%s\" does not exist, skipping");
name = NameListToString(objname);
break;
case OBJECT_FOREIGN_SERVER:
msg = gettext_noop("server \"%s\" does not exist, skipping");
name = NameListToString(objname);
break;
case OBJECT_OPCLASS:
msg = gettext_noop("operator class \"%s\" does not exist for access method \"%s\", skipping");
name = NameListToString(objname);
args = strVal(linitial(objargs));
break;
case OBJECT_OPFAMILY:
msg = gettext_noop("operator family \"%s\" does not exist for access method \"%s\", skipping");
name = NameListToString(objname);
args = strVal(linitial(objargs));
break;
default:
elog(ERROR, "unexpected object type (%d)", (int)objtype);
break;
}
ereport(NOTICE, (errmsg(msg, name)));
if (!args)
ereport(NOTICE, (errmsg(msg, name)));
else
ereport(NOTICE, (errmsg(msg, name, args)));
}

View File

@@ -685,50 +685,6 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
}
/*
* Drop foreign-data wrapper
*/
void
RemoveForeignDataWrapper(DropFdwStmt *stmt)
{
Oid fdwId;
ObjectAddress object;
fdwId = get_foreign_data_wrapper_oid(stmt->fdwname, true);
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to drop foreign-data wrapper \"%s\"",
stmt->fdwname),
errhint("Must be superuser to drop a foreign-data wrapper.")));
if (!OidIsValid(fdwId))
{
if (!stmt->missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("foreign-data wrapper \"%s\" does not exist",
stmt->fdwname)));
/* IF EXISTS specified, just note it */
ereport(NOTICE,
(errmsg("foreign-data wrapper \"%s\" does not exist, skipping",
stmt->fdwname)));
return;
}
/*
* Do the deletion
*/
object.classId = ForeignDataWrapperRelationId;
object.objectId = fdwId;
object.objectSubId = 0;
performDeletion(&object, stmt->behavior);
}
/*
* Drop foreign-data wrapper by OID
*/
@@ -957,45 +913,6 @@ AlterForeignServer(AlterForeignServerStmt *stmt)
}
/*
* Drop foreign server
*/
void
RemoveForeignServer(DropForeignServerStmt *stmt)
{
Oid srvId;
ObjectAddress object;
srvId = get_foreign_server_oid(stmt->servername, true);
if (!OidIsValid(srvId))
{
/* Server not found, complain or notice */
if (!stmt->missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("server \"%s\" does not exist", stmt->servername)));
/* IF EXISTS specified, just note it */
ereport(NOTICE,
(errmsg("server \"%s\" does not exist, skipping",
stmt->servername)));
return;
}
/* Only allow DROP if the server is owned by the user. */
if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
stmt->servername);
object.classId = ForeignServerRelationId;
object.objectId = srvId;
object.objectSubId = 0;
performDeletion(&object, stmt->behavior);
}
/*
* Drop foreign server by OID
*/

View File

@@ -960,72 +960,6 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
}
/*
* RemoveFunction
* Deletes a function.
*/
void
RemoveFunction(RemoveFuncStmt *stmt)
{
List *functionName = stmt->name;
List *argTypes = stmt->args; /* list of TypeName nodes */
Oid funcOid;
HeapTuple tup;
ObjectAddress object;
/*
* Find the function, do permissions and validity checks
*/
funcOid = LookupFuncNameTypeNames(functionName, argTypes, stmt->missing_ok);
if (!OidIsValid(funcOid))
{
/* can only get here if stmt->missing_ok */
ereport(NOTICE,
(errmsg("function %s(%s) does not exist, skipping",
NameListToString(functionName),
TypeNameListToString(argTypes))));
return;
}
tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", funcOid);
/* Permission check: must own func or its namespace */
if (!pg_proc_ownercheck(funcOid, GetUserId()) &&
!pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace,
GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(functionName));
if (((Form_pg_proc) GETSTRUCT(tup))->proisagg)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is an aggregate function",
NameListToString(functionName)),
errhint("Use DROP AGGREGATE to drop aggregate functions.")));
if (((Form_pg_proc) GETSTRUCT(tup))->prolang == INTERNALlanguageId)
{
/* "Helpful" NOTICE when removing a builtin function ... */
ereport(NOTICE,
(errcode(ERRCODE_WARNING),
errmsg("removing built-in function \"%s\"",
NameListToString(functionName))));
}
ReleaseSysCache(tup);
/*
* Do the deletion
*/
object.classId = ProcedureRelationId;
object.objectId = funcOid;
object.objectSubId = 0;
performDeletion(&object, stmt->behavior);
}
/*
* Guts of function deletion.
*
@@ -1772,51 +1706,6 @@ CreateCast(CreateCastStmt *stmt)
heap_close(relation, RowExclusiveLock);
}
/*
* DROP CAST
*/
void
DropCast(DropCastStmt *stmt)
{
Oid sourcetypeid;
Oid targettypeid;
ObjectAddress object;
/* when dropping a cast, the types must exist even if you use IF EXISTS */
sourcetypeid = typenameTypeId(NULL, stmt->sourcetype);
targettypeid = typenameTypeId(NULL, stmt->targettype);
object.classId = CastRelationId;
object.objectId = get_cast_oid(sourcetypeid, targettypeid,
stmt->missing_ok);
object.objectSubId = 0;
if (!OidIsValid(object.objectId))
{
ereport(NOTICE,
(errmsg("cast from type %s to type %s does not exist, skipping",
format_type_be(sourcetypeid),
format_type_be(targettypeid))));
return;
}
/* Permission check */
if (!pg_type_ownercheck(sourcetypeid, GetUserId())
&& !pg_type_ownercheck(targettypeid, GetUserId()))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be owner of type %s or type %s",
format_type_be(sourcetypeid),
format_type_be(targettypeid))));
/*
* Do the deletion
*/
performDeletion(&object, stmt->behavior);
}
/*
* get_cast_oid - given two type OIDs, look up a cast OID
*

View File

@@ -1543,104 +1543,6 @@ dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
}
}
/*
* RemoveOpClass
* Deletes an opclass.
*/
void
RemoveOpClass(RemoveOpClassStmt *stmt)
{
Oid amID,
opcID;
HeapTuple tuple;
ObjectAddress object;
/* Get the access method's OID. */
amID = get_am_oid(stmt->amname, false);
/* Look up the opclass. */
tuple = OpClassCacheLookup(amID, stmt->opclassname, stmt->missing_ok);
if (!HeapTupleIsValid(tuple))
{
ereport(NOTICE,
(errmsg("operator class \"%s\" does not exist for access method \"%s\", skipping",
NameListToString(stmt->opclassname), stmt->amname)));
return;
}
opcID = HeapTupleGetOid(tuple);
/* Permission check: must own opclass or its namespace */
if (!pg_opclass_ownercheck(opcID, GetUserId()) &&
!pg_namespace_ownercheck(((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace,
GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
NameListToString(stmt->opclassname));
ReleaseSysCache(tuple);
/*
* Do the deletion
*/
object.classId = OperatorClassRelationId;
object.objectId = opcID;
object.objectSubId = 0;
performDeletion(&object, stmt->behavior);
}
/*
* RemoveOpFamily
* Deletes an opfamily.
*/
void
RemoveOpFamily(RemoveOpFamilyStmt *stmt)
{
Oid amID,
opfID;
HeapTuple tuple;
ObjectAddress object;
/*
* Get the access method's OID.
*/
amID = get_am_oid(stmt->amname, false);
/*
* Look up the opfamily.
*/
tuple = OpFamilyCacheLookup(amID, stmt->opfamilyname, stmt->missing_ok);
if (!HeapTupleIsValid(tuple))
{
ereport(NOTICE,
(errmsg("operator family \"%s\" does not exist for access method \"%s\", skipping",
NameListToString(stmt->opfamilyname), stmt->amname)));
return;
}
opfID = HeapTupleGetOid(tuple);
/* Permission check: must own opfamily or its namespace */
if (!pg_opfamily_ownercheck(opfID, GetUserId()) &&
!pg_namespace_ownercheck(((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace,
GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
NameListToString(stmt->opfamilyname));
ReleaseSysCache(tuple);
/*
* Do the deletion
*/
object.classId = OperatorFamilyRelationId;
object.objectId = opfID;
object.objectSubId = 0;
performDeletion(&object, stmt->behavior);
}
/*
* Deletion subroutines for use by dependency.c.
*/

View File

@@ -290,56 +290,6 @@ DefineOperator(List *names, List *parameters)
}
/*
* RemoveOperator
* Deletes an operator.
*/
void
RemoveOperator(RemoveFuncStmt *stmt)
{
List *operatorName = stmt->name;
TypeName *typeName1 = (TypeName *) linitial(stmt->args);
TypeName *typeName2 = (TypeName *) lsecond(stmt->args);
Oid operOid;
HeapTuple tup;
ObjectAddress object;
Assert(list_length(stmt->args) == 2);
operOid = LookupOperNameTypeNames(NULL, operatorName,
typeName1, typeName2,
stmt->missing_ok, -1);
if (stmt->missing_ok && !OidIsValid(operOid))
{
ereport(NOTICE,
(errmsg("operator %s does not exist, skipping",
NameListToString(operatorName))));
return;
}
tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
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.
*/

View File

@@ -507,43 +507,6 @@ PLTemplateExists(const char *languageName)
return (find_language_template(languageName) != NULL);
}
/* ---------------------------------------------------------------------
* DROP PROCEDURAL LANGUAGE
* ---------------------------------------------------------------------
*/
void
DropProceduralLanguage(DropPLangStmt *stmt)
{
Oid oid;
ObjectAddress object;
oid = get_language_oid(stmt->plname, stmt->missing_ok);
if (!OidIsValid(oid))
{
ereport(NOTICE,
(errmsg("language \"%s\" does not exist, skipping",
stmt->plname)));
return;
}
/*
* Check permission
*/
if (!pg_language_ownercheck(oid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE,
stmt->plname);
object.classId = LanguageRelationId;
object.objectId = oid;
object.objectSubId = 0;
/*
* Do the deletion
*/
performDeletion(&object, stmt->behavior);
}
/*
* Guts of language dropping.
*/

View File

@@ -1026,42 +1026,6 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
}
}
/*
* DropTrigger - drop an individual trigger by name
*/
void
DropTrigger(RangeVar *relation, const char *trigname, DropBehavior behavior,
bool missing_ok)
{
Oid relid;
ObjectAddress object;
/* lock level should match RemoveTriggerById */
relid = RangeVarGetRelid(relation, AccessExclusiveLock, false, false);
object.classId = TriggerRelationId;
object.objectId = get_trigger_oid(relid, trigname, missing_ok);
object.objectSubId = 0;
if (!OidIsValid(object.objectId))
{
ereport(NOTICE,
(errmsg("trigger \"%s\" for table \"%s\" does not exist, skipping",
trigname, get_rel_name(relid))));
return;
}
if (!pg_class_ownercheck(relid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
get_rel_name(relid));
/*
* Do the deletion
*/
performDeletion(&object, behavior);
}
/*
* Guts of trigger deletion.
*/