1
0
mirror of https://github.com/postgres/postgres.git synced 2025-10-18 04:29:09 +03:00

Consolidate DROP handling for some object types.

This gets rid of a significant amount of duplicative code.

KaiGai Kohei, reviewed in earlier versions by Dimitri Fontaine, with
further review and cleanup by me.
This commit is contained in:
Robert Haas
2011-10-19 23:25:20 -04:00
parent 3301c83536
commit 82a4a777d9
17 changed files with 402 additions and 828 deletions

View File

@@ -14,7 +14,7 @@ include $(top_builddir)/src/Makefile.global
OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
collationcmds.o constraint.o conversioncmds.o copy.o \
dbcommands.o define.o discard.o explain.o extension.o \
dbcommands.o define.o discard.o dropcmds.o explain.o extension.o \
foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
portalcmds.o prepare.o proclang.o \

View File

@@ -144,67 +144,6 @@ DefineCollation(List *names, List *parameters)
(void) pg_newlocale_from_collation(newoid);
}
/*
* DROP COLLATION
*/
void
DropCollationsCommand(DropStmt *drop)
{
ObjectAddresses *objects;
ListCell *cell;
/*
* First we identify all the collations, then we delete them in a single
* performMultipleDeletions() call. This is to avoid unwanted DROP
* RESTRICT errors if one of the collations depends on another. (Not that
* that is very likely, but we may as well do this consistently.)
*/
objects = new_object_addresses();
foreach(cell, drop->objects)
{
List *name = (List *) lfirst(cell);
Oid collationOid;
HeapTuple tuple;
Form_pg_collation coll;
ObjectAddress object;
collationOid = get_collation_oid(name, drop->missing_ok);
if (!OidIsValid(collationOid))
{
ereport(NOTICE,
(errmsg("collation \"%s\" does not exist, skipping",
NameListToString(name))));
continue;
}
tuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(collationOid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for collation %u",
collationOid);
coll = (Form_pg_collation) GETSTRUCT(tuple);
/* Permission check: must own collation or its namespace */
if (!pg_collation_ownercheck(collationOid, GetUserId()) &&
!pg_namespace_ownercheck(coll->collnamespace, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION,
NameStr(coll->collname));
object.classId = CollationRelationId;
object.objectId = collationOid;
object.objectSubId = 0;
add_exact_object_address(&object, objects);
ReleaseSysCache(tuple);
}
performMultipleDeletions(objects, drop->behavior);
free_object_addresses(objects);
}
/*
* Rename collation
*/

View File

@@ -117,67 +117,6 @@ CreateConversionCommand(CreateConversionStmt *stmt)
from_encoding, to_encoding, funcoid, stmt->def);
}
/*
* DROP CONVERSION
*/
void
DropConversionsCommand(DropStmt *drop)
{
ObjectAddresses *objects;
ListCell *cell;
/*
* First we identify all the conversions, then we delete them in a single
* performMultipleDeletions() call. This is to avoid unwanted DROP
* RESTRICT errors if one of the conversions depends on another. (Not that
* that is very likely, but we may as well do this consistently.)
*/
objects = new_object_addresses();
foreach(cell, drop->objects)
{
List *name = (List *) lfirst(cell);
Oid conversionOid;
HeapTuple tuple;
Form_pg_conversion con;
ObjectAddress object;
conversionOid = get_conversion_oid(name, drop->missing_ok);
if (!OidIsValid(conversionOid))
{
ereport(NOTICE,
(errmsg("conversion \"%s\" does not exist, skipping",
NameListToString(name))));
continue;
}
tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(conversionOid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for conversion %u",
conversionOid);
con = (Form_pg_conversion) GETSTRUCT(tuple);
/* Permission check: must own conversion or its namespace */
if (!pg_conversion_ownercheck(conversionOid, GetUserId()) &&
!pg_namespace_ownercheck(con->connamespace, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
NameStr(con->conname));
object.classId = ConversionRelationId;
object.objectId = conversionOid;
object.objectSubId = 0;
add_exact_object_address(&object, objects);
ReleaseSysCache(tuple);
}
performMultipleDeletions(objects, drop->behavior);
free_object_addresses(objects);
}
/*
* Rename conversion
*/

View File

@@ -0,0 +1,147 @@
/*-------------------------------------------------------------------------
*
* dropcmds.c
* handle various "DROP" operations
*
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/catalog/dropcmds.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/objectaddress.h"
#include "catalog/pg_class.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
static void does_not_exist_skipping(ObjectType objtype, List *objname);
/*
* Drop one or more objects.
*
* We don't currently handle all object types here. Relations, for example,
* require special handling, because (for example) indexes have additional
* locking requirements.
*
* We look up all the objects first, and then delete them in a single
* performMultipleDeletions() call. This avoids unnecessary DROP RESTRICT
* errors if there are dependencies between them.
*/
void
RemoveObjects(DropStmt *stmt)
{
ObjectAddresses *objects;
ListCell *cell1;
objects = new_object_addresses();
foreach(cell1, stmt->objects)
{
ObjectAddress address;
List *objname = lfirst(cell1);
Relation relation = NULL;
Oid namespaceId;
/* Get an ObjectAddress for the object. */
address = get_object_address(stmt->removeType,
objname, NIL,
&relation,
AccessExclusiveLock,
stmt->missing_ok);
/* Issue NOTICE if supplied object was not found. */
if (!OidIsValid(address.objectId))
{
does_not_exist_skipping(stmt->removeType, objname);
continue;
}
/* 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);
/* Release any relcache reference count, but keep lock until commit. */
if (relation)
heap_close(relation, NoLock);
add_exact_object_address(&address, objects);
}
/* Here we really delete them. */
performMultipleDeletions(objects, stmt->behavior);
free_object_addresses(objects);
}
/*
* Generate a NOTICE stating that the named object was not found, and is
* being skipped. This is only relevant when "IF EXISTS" is used; otherwise,
* get_object_address() will throw an ERROR.
*/
static void
does_not_exist_skipping(ObjectType objtype, List *objname)
{
const char *msg = NULL;
char *name = NULL;
switch (objtype)
{
case OBJECT_TYPE:
case OBJECT_DOMAIN:
msg = gettext_noop("type \"%s\" does not exist, skipping");
name = TypeNameToString(makeTypeNameFromNameList(objname));
break;
case OBJECT_COLLATION:
msg = gettext_noop("collation \"%s\" does not exist, skipping");
name = NameListToString(objname);
break;
case OBJECT_CONVERSION:
msg = gettext_noop("conversion \"%s\" does not exist, skipping");
name = NameListToString(objname);
break;
case OBJECT_SCHEMA:
msg = gettext_noop("schema \"%s\" does not exist, skipping");
name = NameListToString(objname);
break;
case OBJECT_TSPARSER:
msg = gettext_noop("text search parser \"%s\" does not exist, skipping");
name = NameListToString(objname);
break;
case OBJECT_TSDICTIONARY:
msg = gettext_noop("text search dictionary \"%s\" does not exist, skipping");
name = NameListToString(objname);
break;
case OBJECT_TSTEMPLATE:
msg = gettext_noop("text search template \"%s\" does not exist, skipping");
name = NameListToString(objname);
break;
case OBJECT_TSCONFIGURATION:
msg = gettext_noop("text search configuration \"%s\" does not exist, skipping");
name = NameListToString(objname);
break;
case OBJECT_EXTENSION:
msg = gettext_noop("extension \"%s\" does not exist, skipping");
name = NameListToString(objname);
break;
default:
elog(ERROR, "unexpected object type (%d)", (int)objtype);
break;
}
ereport(NOTICE, (errmsg(msg, name)));
}

View File

@@ -1563,69 +1563,6 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
return extensionOid;
}
/*
* RemoveExtensions
* Implements DROP EXTENSION.
*/
void
RemoveExtensions(DropStmt *drop)
{
ObjectAddresses *objects;
ListCell *cell;
/*
* First we identify all the extensions, then we delete them in a single
* performMultipleDeletions() call. This is to avoid unwanted DROP
* RESTRICT errors if one of the extensions depends on another.
*/
objects = new_object_addresses();
foreach(cell, drop->objects)
{
List *names = (List *) lfirst(cell);
char *extensionName;
Oid extensionId;
ObjectAddress object;
if (list_length(names) != 1)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("extension name cannot be qualified")));
extensionName = strVal(linitial(names));
extensionId = get_extension_oid(extensionName, drop->missing_ok);
if (!OidIsValid(extensionId))
{
ereport(NOTICE,
(errmsg("extension \"%s\" does not exist, skipping",
extensionName)));
continue;
}
/* Permission check: must own extension */
if (!pg_extension_ownercheck(extensionId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTENSION,
extensionName);
object.classId = ExtensionRelationId;
object.objectId = extensionId;
object.objectSubId = 0;
add_exact_object_address(&object, objects);
}
/*
* Do the deletions. Objects contained in the extension(s) are removed by
* means of their dependency links to the extensions.
*/
performMultipleDeletions(objects, drop->behavior);
free_object_addresses(objects);
}
/*
* Guts of extension deletion.
*

View File

@@ -146,69 +146,6 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
SetUserIdAndSecContext(saved_uid, save_sec_context);
}
/*
* RemoveSchemas
* Implements DROP SCHEMA.
*/
void
RemoveSchemas(DropStmt *drop)
{
ObjectAddresses *objects;
ListCell *cell;
/*
* First we identify all the schemas, then we delete them in a single
* performMultipleDeletions() call. This is to avoid unwanted DROP
* RESTRICT errors if one of the schemas depends on another.
*/
objects = new_object_addresses();
foreach(cell, drop->objects)
{
List *names = (List *) lfirst(cell);
char *namespaceName;
Oid namespaceId;
ObjectAddress object;
if (list_length(names) != 1)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("schema name cannot be qualified")));
namespaceName = strVal(linitial(names));
namespaceId = get_namespace_oid(namespaceName, drop->missing_ok);
if (!OidIsValid(namespaceId))
{
ereport(NOTICE,
(errmsg("schema \"%s\" does not exist, skipping",
namespaceName)));
continue;
}
/* Permission check */
if (!pg_namespace_ownercheck(namespaceId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
namespaceName);
object.classId = NamespaceRelationId;
object.objectId = namespaceId;
object.objectSubId = 0;
add_exact_object_address(&object, objects);
}
/*
* Do the deletions. Objects contained in the schema(s) are removed by
* means of their dependency links to the schema.
*/
performMultipleDeletions(objects, drop->behavior);
free_object_addresses(objects);
}
/*
* Guts of schema deletion.
*/

View File

@@ -278,65 +278,6 @@ DefineTSParser(List *names, List *parameters)
heap_close(prsRel, RowExclusiveLock);
}
/*
* DROP TEXT SEARCH PARSER
*/
void
RemoveTSParsers(DropStmt *drop)
{
ObjectAddresses *objects;
ListCell *cell;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to drop text search parsers")));
/*
* First we identify all the objects, then we delete them in a single
* performMultipleDeletions() call. This is to avoid unwanted DROP
* RESTRICT errors if one of the objects depends on another.
*/
objects = new_object_addresses();
foreach(cell, drop->objects)
{
List *names = (List *) lfirst(cell);
Oid prsOid;
ObjectAddress object;
prsOid = get_ts_parser_oid(names, true);
if (!OidIsValid(prsOid))
{
if (!drop->missing_ok)
{
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("text search parser \"%s\" does not exist",
NameListToString(names))));
}
else
{
ereport(NOTICE,
(errmsg("text search parser \"%s\" does not exist, skipping",
NameListToString(names))));
}
continue;
}
object.classId = TSParserRelationId;
object.objectId = prsOid;
object.objectSubId = 0;
add_exact_object_address(&object, objects);
}
performMultipleDeletions(objects, drop->behavior);
free_object_addresses(objects);
}
/*
* Guts of TS parser deletion.
*/
@@ -730,76 +671,6 @@ AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid)
return oldNspOid;
}
/*
* DROP TEXT SEARCH DICTIONARY
*/
void
RemoveTSDictionaries(DropStmt *drop)
{
ObjectAddresses *objects;
ListCell *cell;
/*
* First we identify all the objects, then we delete them in a single
* performMultipleDeletions() call. This is to avoid unwanted DROP
* RESTRICT errors if one of the objects depends on another.
*/
objects = new_object_addresses();
foreach(cell, drop->objects)
{
List *names = (List *) lfirst(cell);
Oid dictOid;
ObjectAddress object;
HeapTuple tup;
Oid namespaceId;
dictOid = get_ts_dict_oid(names, true);
if (!OidIsValid(dictOid))
{
if (!drop->missing_ok)
{
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("text search dictionary \"%s\" does not exist",
NameListToString(names))));
}
else
{
ereport(NOTICE,
(errmsg("text search dictionary \"%s\" does not exist, skipping",
NameListToString(names))));
}
continue;
}
tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for text search dictionary %u",
dictOid);
/* Permission check: must own dictionary or its namespace */
namespaceId = ((Form_pg_ts_dict) GETSTRUCT(tup))->dictnamespace;
if (!pg_ts_dict_ownercheck(dictOid, GetUserId()) &&
!pg_namespace_ownercheck(namespaceId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
NameListToString(names));
object.classId = TSDictionaryRelationId;
object.objectId = dictOid;
object.objectSubId = 0;
add_exact_object_address(&object, objects);
ReleaseSysCache(tup);
}
performMultipleDeletions(objects, drop->behavior);
free_object_addresses(objects);
}
/*
* Guts of TS dictionary deletion.
*/
@@ -1262,65 +1133,6 @@ AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid)
return oldNspOid;
}
/*
* DROP TEXT SEARCH TEMPLATE
*/
void
RemoveTSTemplates(DropStmt *drop)
{
ObjectAddresses *objects;
ListCell *cell;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to drop text search templates")));
/*
* First we identify all the objects, then we delete them in a single
* performMultipleDeletions() call. This is to avoid unwanted DROP
* RESTRICT errors if one of the objects depends on another.
*/
objects = new_object_addresses();
foreach(cell, drop->objects)
{
List *names = (List *) lfirst(cell);
Oid tmplOid;
ObjectAddress object;
tmplOid = get_ts_template_oid(names, true);
if (!OidIsValid(tmplOid))
{
if (!drop->missing_ok)
{
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("text search template \"%s\" does not exist",
NameListToString(names))));
}
else
{
ereport(NOTICE,
(errmsg("text search template \"%s\" does not exist, skipping",
NameListToString(names))));
}
continue;
}
object.classId = TSTemplateRelationId;
object.objectId = tmplOid;
object.objectSubId = 0;
add_exact_object_address(&object, objects);
}
performMultipleDeletions(objects, drop->behavior);
free_object_addresses(objects);
}
/*
* Guts of TS template deletion.
*/
@@ -1714,72 +1526,6 @@ AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid)
return oldNspOid;
}
/*
* DROP TEXT SEARCH CONFIGURATION
*/
void
RemoveTSConfigurations(DropStmt *drop)
{
ObjectAddresses *objects;
ListCell *cell;
/*
* First we identify all the objects, then we delete them in a single
* performMultipleDeletions() call. This is to avoid unwanted DROP
* RESTRICT errors if one of the objects depends on another.
*/
objects = new_object_addresses();
foreach(cell, drop->objects)
{
List *names = (List *) lfirst(cell);
Oid cfgOid;
Oid namespaceId;
ObjectAddress object;
HeapTuple tup;
tup = GetTSConfigTuple(names);
if (!HeapTupleIsValid(tup))
{
if (!drop->missing_ok)
{
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("text search configuration \"%s\" does not exist",
NameListToString(names))));
}
else
{
ereport(NOTICE,
(errmsg("text search configuration \"%s\" does not exist, skipping",
NameListToString(names))));
}
continue;
}
/* Permission check: must own configuration or its namespace */
cfgOid = HeapTupleGetOid(tup);
namespaceId = ((Form_pg_ts_config) GETSTRUCT(tup))->cfgnamespace;
if (!pg_ts_config_ownercheck(cfgOid, GetUserId()) &&
!pg_namespace_ownercheck(namespaceId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
NameListToString(names));
object.classId = TSConfigRelationId;
object.objectId = cfgOid;
object.objectSubId = 0;
add_exact_object_address(&object, objects);
ReleaseSysCache(tup);
}
performMultipleDeletions(objects, drop->behavior);
free_object_addresses(objects);
}
/*
* Guts of TS configuration deletion.
*/

View File

@@ -618,98 +618,6 @@ DefineType(List *names, List *parameters)
pfree(array_type);
}
/*
* RemoveTypes
* Implements DROP TYPE and DROP DOMAIN
*
* Note: if DOMAIN is specified, we enforce that each type is a domain, but
* we don't enforce the converse for DROP TYPE
*/
void
RemoveTypes(DropStmt *drop)
{
ObjectAddresses *objects;
ListCell *cell;
/*
* First we identify all the types, then we delete them in a single
* performMultipleDeletions() call. This is to avoid unwanted DROP
* RESTRICT errors if one of the types depends on another.
*/
objects = new_object_addresses();
foreach(cell, drop->objects)
{
List *names = (List *) lfirst(cell);
TypeName *typename;
Oid typeoid;
HeapTuple tup;
ObjectAddress object;
Form_pg_type typ;
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeTypeNameFromNameList(names);
/* Use LookupTypeName here so that shell types can be removed. */
tup = LookupTypeName(NULL, typename, NULL);
if (tup == NULL)
{
if (!drop->missing_ok)
{
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type \"%s\" does not exist",
TypeNameToString(typename))));
}
else
{
ereport(NOTICE,
(errmsg("type \"%s\" does not exist, skipping",
TypeNameToString(typename))));
}
continue;
}
typeoid = typeTypeId(tup);
typ = (Form_pg_type) GETSTRUCT(tup);
/* Permission check: must own type or its namespace */
if (!pg_type_ownercheck(typeoid, GetUserId()) &&
!pg_namespace_ownercheck(typ->typnamespace, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
format_type_be(typeoid));
if (drop->removeType == OBJECT_DOMAIN)
{
/* Check that this is actually a domain */
if (typ->typtype != TYPTYPE_DOMAIN)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a domain",
TypeNameToString(typename))));
}
/*
* Note: we need no special check for array types here, as the normal
* treatment of internal dependencies handles it just fine
*/
object.classId = TypeRelationId;
object.objectId = typeoid;
object.objectSubId = 0;
add_exact_object_address(&object, objects);
ReleaseSysCache(tup);
}
performMultipleDeletions(objects, drop->behavior);
free_object_addresses(objects);
}
/*
* Guts of type deletion.
*/