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:
@@ -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 \
|
||||
|
@@ -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
|
||||
*/
|
||||
|
@@ -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
|
||||
*/
|
||||
|
147
src/backend/commands/dropcmds.c
Normal file
147
src/backend/commands/dropcmds.c
Normal 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)));
|
||||
}
|
@@ -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.
|
||||
*
|
||||
|
@@ -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.
|
||||
*/
|
||||
|
@@ -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.
|
||||
*/
|
||||
|
@@ -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.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user