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

@ -66,6 +66,174 @@
#include "utils/syscache.h"
#include "utils/tqual.h"
/*
* ObjectProperty
*
* This array provides a common part of system object structure; to help
* consolidate routines to handle various kind of object classes.
*/
typedef struct
{
Oid class_oid; /* oid of catalog */
Oid oid_index_oid; /* oid of index on system oid column */
int oid_catcache_id; /* id of catcache on system oid column */
AttrNumber attnum_namespace; /* attnum of namespace field */
} ObjectPropertyType;
static ObjectPropertyType ObjectProperty[] =
{
{
CastRelationId,
CastOidIndexId,
-1,
InvalidAttrNumber
},
{
CollationRelationId,
CollationOidIndexId,
COLLOID,
Anum_pg_collation_collnamespace
},
{
ConstraintRelationId,
ConstraintOidIndexId,
CONSTROID,
Anum_pg_constraint_connamespace
},
{
ConversionRelationId,
ConversionOidIndexId,
CONVOID,
Anum_pg_conversion_connamespace
},
{
DatabaseRelationId,
DatabaseOidIndexId,
DATABASEOID,
InvalidAttrNumber
},
{
ExtensionRelationId,
ExtensionOidIndexId,
-1,
Anum_pg_extension_extnamespace
},
{
ForeignDataWrapperRelationId,
ForeignDataWrapperOidIndexId,
FOREIGNDATAWRAPPEROID,
InvalidAttrNumber
},
{
ForeignServerRelationId,
ForeignServerOidIndexId,
FOREIGNSERVEROID,
InvalidAttrNumber
},
{
ProcedureRelationId,
ProcedureOidIndexId,
PROCOID,
Anum_pg_proc_pronamespace
},
{
LanguageRelationId,
LanguageOidIndexId,
LANGOID,
InvalidAttrNumber,
},
{
LargeObjectMetadataRelationId,
LargeObjectMetadataOidIndexId,
-1,
InvalidAttrNumber
},
{
OperatorClassRelationId,
OpclassOidIndexId,
CLAOID,
Anum_pg_opclass_opcnamespace,
},
{
OperatorRelationId,
OperatorOidIndexId,
OPEROID,
Anum_pg_operator_oprnamespace
},
{
OperatorFamilyRelationId,
OpfamilyOidIndexId,
OPFAMILYOID,
Anum_pg_opfamily_opfnamespace
},
{
AuthIdRelationId,
AuthIdOidIndexId,
AUTHOID,
InvalidAttrNumber
},
{
RewriteRelationId,
RewriteOidIndexId,
-1,
InvalidAttrNumber
},
{
NamespaceRelationId,
NamespaceOidIndexId,
NAMESPACEOID,
InvalidAttrNumber
},
{
RelationRelationId,
ClassOidIndexId,
RELOID,
Anum_pg_class_relnamespace
},
{
TableSpaceRelationId,
TablespaceOidIndexId,
TABLESPACEOID,
InvalidAttrNumber
},
{
TriggerRelationId,
TriggerOidIndexId,
-1,
InvalidAttrNumber
},
{
TSConfigRelationId,
TSConfigOidIndexId,
TSCONFIGOID,
Anum_pg_ts_config_cfgnamespace
},
{
TSDictionaryRelationId,
TSDictionaryOidIndexId,
TSDICTOID,
Anum_pg_ts_dict_dictnamespace
},
{
TSParserRelationId,
TSParserOidIndexId,
TSPARSEROID,
Anum_pg_ts_parser_prsnamespace
},
{
TSTemplateRelationId,
TSTemplateOidIndexId,
TSTEMPLATEOID,
Anum_pg_ts_template_tmplnamespace,
},
{
TypeRelationId,
TypeOidIndexId,
TYPEOID,
Anum_pg_type_typnamespace
}
};
static ObjectAddress get_object_address_unqualified(ObjectType objtype,
List *qualname, bool missing_ok);
static ObjectAddress get_relation_by_qualified_name(ObjectType objtype,
@ -81,148 +249,7 @@ static ObjectAddress get_object_address_type(ObjectType objtype,
static ObjectAddress get_object_address_opcf(ObjectType objtype, List *objname,
List *objargs, bool missing_ok);
static bool object_exists(ObjectAddress address);
/*
* ObjectProperty
*
* This array provides a common part of system object structure; to help
* consolidate routines to handle various kind of object classes.
*/
typedef struct
{
Oid class_oid; /* oid of catalog */
Oid oid_index_oid; /* oid of index on system oid column */
int oid_catcache_id; /* id of catcache on system oid column */
} ObjectPropertyType;
static ObjectPropertyType ObjectProperty[] =
{
{
CastRelationId,
CastOidIndexId,
-1
},
{
CollationRelationId,
CollationOidIndexId,
COLLOID
},
{
ConstraintRelationId,
ConstraintOidIndexId,
CONSTROID
},
{
ConversionRelationId,
ConversionOidIndexId,
CONVOID
},
{
DatabaseRelationId,
DatabaseOidIndexId,
DATABASEOID
},
{
ExtensionRelationId,
ExtensionOidIndexId,
-1
},
{
ForeignDataWrapperRelationId,
ForeignDataWrapperOidIndexId,
FOREIGNDATAWRAPPEROID
},
{
ForeignServerRelationId,
ForeignServerOidIndexId,
FOREIGNSERVEROID
},
{
ProcedureRelationId,
ProcedureOidIndexId,
PROCOID
},
{
LanguageRelationId,
LanguageOidIndexId,
LANGOID
},
{
LargeObjectMetadataRelationId,
LargeObjectMetadataOidIndexId,
-1
},
{
OperatorClassRelationId,
OpclassOidIndexId,
CLAOID
},
{
OperatorRelationId,
OperatorOidIndexId,
OPEROID
},
{
OperatorFamilyRelationId,
OpfamilyOidIndexId,
OPFAMILYOID
},
{
AuthIdRelationId,
AuthIdOidIndexId,
AUTHOID
},
{
RewriteRelationId,
RewriteOidIndexId,
-1
},
{
NamespaceRelationId,
NamespaceOidIndexId,
NAMESPACEOID
},
{
RelationRelationId,
ClassOidIndexId,
RELOID
},
{
TableSpaceRelationId,
TablespaceOidIndexId,
TABLESPACEOID,
},
{
TriggerRelationId,
TriggerOidIndexId,
-1
},
{
TSConfigRelationId,
TSConfigOidIndexId,
TSCONFIGOID
},
{
TSDictionaryRelationId,
TSDictionaryOidIndexId,
TSDICTOID
},
{
TSParserRelationId,
TSParserOidIndexId,
TSPARSEROID
},
{
TSTemplateRelationId,
TSTemplateOidIndexId,
TSTEMPLATEOID
},
{
TypeRelationId,
TypeOidIndexId,
TYPEOID
}
};
static ObjectPropertyType *get_object_property_data(Oid class_id);
/*
* Translate an object name and arguments (as passed by the parser) to an
@ -828,6 +855,7 @@ object_exists(ObjectAddress address)
ScanKeyData skey[1];
SysScanDesc sd;
bool found;
ObjectPropertyType *property;
/* Sub-objects require special treatment. */
if (address.objectSubId != 0)
@ -847,35 +875,22 @@ object_exists(ObjectAddress address)
}
return found;
}
else
{
int index;
/*
* Weird backward compatibility hack: ObjectAddress notation uses
* LargeObjectRelationId for large objects, but since PostgreSQL
* 9.0, the relevant catalog is actually LargeObjectMetadataRelationId.
*/
if (address.classId == LargeObjectRelationId)
address.classId = LargeObjectMetadataRelationId;
/*
* Weird backward compatibility hack: ObjectAddress notation uses
* LargeObjectRelationId for large objects, but since PostgreSQL
* 9.0, the relevant catalog is actually LargeObjectMetadataRelationId.
*/
if (address.classId == LargeObjectRelationId)
address.classId = LargeObjectMetadataRelationId;
/*
* For object types that have a relevant syscache, we use it; for
* everything else, we'll have to do an index-scan. Search the
* ObjectProperty array to find out which it is.
*/
for (index = 0; index < lengthof(ObjectProperty); index++)
{
if (ObjectProperty[index].class_oid == address.classId)
{
cache = ObjectProperty[index].oid_catcache_id;
indexoid = ObjectProperty[index].oid_index_oid;
break;
}
}
if (index == lengthof(ObjectProperty))
elog(ERROR, "unrecognized classid: %u", address.classId);
}
/*
* For object types that have a relevant syscache, we use it; for
* everything else, we'll have to do an index-scan.
*/
property = get_object_property_data(address.classId);
cache = property->oid_catcache_id;
indexoid = property->oid_index_oid;
/* Found a syscache? */
if (cache != -1)
@ -895,7 +910,6 @@ object_exists(ObjectAddress address)
return found;
}
/*
* Check ownership of an object previously identified by get_object_address.
*/
@ -1060,3 +1074,58 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
(int) objtype);
}
}
/*
* get_object_namespace
*
* Find the schema containing the specified object. For non-schema objects,
* this function returns InvalidOid.
*/
Oid
get_object_namespace(const ObjectAddress *address)
{
int cache;
HeapTuple tuple;
bool isnull;
Oid oid;
ObjectPropertyType *property;
/* If not owned by a namespace, just return InvalidOid. */
property = get_object_property_data(address->classId);
if (property->attnum_namespace == InvalidAttrNumber)
return InvalidOid;
/* Currently, we can only handle object types with system caches. */
cache = property->oid_catcache_id;
Assert(cache != -1);
/* Fetch tuple from syscache and extract namespace attribute. */
tuple = SearchSysCache1(cache, ObjectIdGetDatum(address->objectId));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for cache %d oid %u",
cache, address->objectId);
oid = DatumGetObjectId(SysCacheGetAttr(cache,
tuple,
property->attnum_namespace,
&isnull));
Assert(!isnull);
ReleaseSysCache(tuple);
return oid;
}
/*
* Find ObjectProperty structure by class_id.
*/
static ObjectPropertyType *
get_object_property_data(Oid class_id)
{
int index;
for (index = 0; index < lengthof(ObjectProperty); index++)
if (ObjectProperty[index].class_oid == class_id)
return &ObjectProperty[index];
elog(ERROR, "unrecognized class id: %u", class_id);
return NULL; /* not reached */
}

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.
*/

View File

@ -644,61 +644,18 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_DropStmt:
switch (((DropStmt *) parsetree)->removeType)
{
DropStmt *stmt = (DropStmt *) parsetree;
switch (stmt->removeType)
{
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
case OBJECT_VIEW:
case OBJECT_INDEX:
case OBJECT_FOREIGN_TABLE:
RemoveRelations(stmt);
break;
case OBJECT_TYPE:
case OBJECT_DOMAIN:
RemoveTypes(stmt);
break;
case OBJECT_COLLATION:
DropCollationsCommand(stmt);
break;
case OBJECT_CONVERSION:
DropConversionsCommand(stmt);
break;
case OBJECT_SCHEMA:
RemoveSchemas(stmt);
break;
case OBJECT_TSPARSER:
RemoveTSParsers(stmt);
break;
case OBJECT_TSDICTIONARY:
RemoveTSDictionaries(stmt);
break;
case OBJECT_TSTEMPLATE:
RemoveTSTemplates(stmt);
break;
case OBJECT_TSCONFIGURATION:
RemoveTSConfigurations(stmt);
break;
case OBJECT_EXTENSION:
RemoveExtensions(stmt);
break;
default:
elog(ERROR, "unrecognized drop object type: %d",
(int) stmt->removeType);
break;
}
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
case OBJECT_VIEW:
case OBJECT_INDEX:
case OBJECT_FOREIGN_TABLE:
RemoveRelations((DropStmt *) parsetree);
break;
default:
RemoveObjects((DropStmt *) parsetree);
break;
}
break;

View File

@ -35,4 +35,6 @@ extern void check_object_ownership(Oid roleid,
ObjectType objtype, ObjectAddress address,
List *objname, List *objargs, Relation relation);
extern Oid get_object_namespace(const ObjectAddress *address);
#endif /* PARSE_OBJECT_H */

View File

@ -18,7 +18,6 @@
#include "nodes/parsenodes.h"
extern void DefineCollation(List *names, List *parameters);
extern void DropCollationsCommand(DropStmt *drop);
extern void RenameCollation(List *name, const char *newname);
extern void AlterCollationOwner(List *name, Oid newOwnerId);
extern void AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId);

View File

@ -18,7 +18,6 @@
#include "nodes/parsenodes.h"
extern void CreateConversionCommand(CreateConversionStmt *parsetree);
extern void DropConversionsCommand(DropStmt *drop);
extern void RenameConversion(List *name, const char *newname);
extern void AlterConversionOwner(List *name, Oid newOwnerId);
extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId);

View File

@ -16,6 +16,8 @@
#include "nodes/parsenodes.h"
/* commands/dropcmds.c */
extern void RemoveObjects(DropStmt *stmt);
/* commands/indexcmds.c */
extern Oid DefineIndex(RangeVar *heapRelation,
@ -122,12 +124,10 @@ extern void DefineTSParser(List *names, List *parameters);
extern void RenameTSParser(List *oldname, const char *newname);
extern void AlterTSParserNamespace(List *name, const char *newschema);
extern Oid AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid);
extern void RemoveTSParsers(DropStmt *drop);
extern void RemoveTSParserById(Oid prsId);
extern void DefineTSDictionary(List *names, List *parameters);
extern void RenameTSDictionary(List *oldname, const char *newname);
extern void RemoveTSDictionaries(DropStmt *drop);
extern void RemoveTSDictionaryById(Oid dictId);
extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId);
@ -138,12 +138,10 @@ extern void DefineTSTemplate(List *names, List *parameters);
extern void RenameTSTemplate(List *oldname, const char *newname);
extern void AlterTSTemplateNamespace(List *name, const char *newschema);
extern Oid AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid);
extern void RemoveTSTemplates(DropStmt *stmt);
extern void RemoveTSTemplateById(Oid tmplId);
extern void DefineTSConfiguration(List *names, List *parameters);
extern void RenameTSConfiguration(List *oldname, const char *newname);
extern void RemoveTSConfigurations(DropStmt *stmt);
extern void RemoveTSConfigurationById(Oid cfgId);
extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId);

View File

@ -29,7 +29,6 @@ extern Oid CurrentExtensionObject;
extern void CreateExtension(CreateExtensionStmt *stmt);
extern void RemoveExtensions(DropStmt *stmt);
extern void RemoveExtensionById(Oid extId);
extern Oid InsertExtensionTuple(const char *extName, Oid extOwner,

View File

@ -20,7 +20,6 @@
extern void CreateSchemaCommand(CreateSchemaStmt *parsetree,
const char *queryString);
extern void RemoveSchemas(DropStmt *drop);
extern void RemoveSchemaById(Oid schemaOid);
extern void RenameSchema(const char *oldname, const char *newname);

View File

@ -20,7 +20,6 @@
#define DEFAULT_TYPDELIM ','
extern void DefineType(List *names, List *parameters);
extern void RemoveTypes(DropStmt *drop);
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void DefineEnum(CreateEnumStmt *stmt);