diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2008-06-14 18:04:34 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2008-06-14 18:04:34 +0000 |
commit | 0cefb50f3ce964d6097aad64dabd9b544c3d2e68 (patch) | |
tree | 4e1ee9454bc958d35194062eee8894205cd08da5 /src/backend/commands | |
parent | 95ce4ee943f226152ac8c147da178af5d1a95909 (diff) | |
download | postgresql-0cefb50f3ce964d6097aad64dabd9b544c3d2e68.tar.gz postgresql-0cefb50f3ce964d6097aad64dabd9b544c3d2e68.zip |
Refactor the handling of the various DropStmt variants so that when multiple
objects are specified, we drop them all in a single performMultipleDeletions
call. This makes the RESTRICT/CASCADE checks more relaxed: it's not counted
as a cascade if one of the later objects has a dependency on an earlier one.
NOTICE messages about such cases go away, too.
In passing, fix the permissions check for DROP CONVERSION, which for some
reason was never made role-aware, and omitted the namespace-owner exemption
too.
Alex Hunsaker, with further fiddling by me.
Diffstat (limited to 'src/backend/commands')
-rw-r--r-- | src/backend/commands/conversioncmds.c | 78 | ||||
-rw-r--r-- | src/backend/commands/indexcmds.c | 30 | ||||
-rw-r--r-- | src/backend/commands/schemacmds.c | 95 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 223 | ||||
-rw-r--r-- | src/backend/commands/tsearchcmds.c | 296 | ||||
-rw-r--r-- | src/backend/commands/typecmds.c | 186 | ||||
-rw-r--r-- | src/backend/commands/view.c | 26 |
7 files changed, 588 insertions, 346 deletions
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c index 43f5329b036..c789dc94467 100644 --- a/src/backend/commands/conversioncmds.c +++ b/src/backend/commands/conversioncmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.33 2008/03/27 03:57:33 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.34 2008/06/14 18:04:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -99,31 +99,73 @@ CreateConversionCommand(CreateConversionStmt *stmt) * DROP CONVERSION */ void -DropConversionCommand(List *name, DropBehavior behavior, bool missing_ok) +DropConversionsCommand(DropStmt *drop) { - Oid conversionOid; + ObjectAddresses *objects; + ListCell *cell; - conversionOid = FindConversionByName(name); - if (!OidIsValid(conversionOid)) + /* + * 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) { - if (!missing_ok) - { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("conversion \"%s\" does not exist", - NameListToString(name)))); - } - else + List *name = (List *) lfirst(cell); + Oid conversionOid; + HeapTuple tuple; + Form_pg_conversion con; + ObjectAddress object; + + conversionOid = FindConversionByName(name); + + if (!OidIsValid(conversionOid)) { - ereport(NOTICE, - (errmsg("conversion \"%s\" does not exist, skipping", - NameListToString(name)))); + if (!drop->missing_ok) + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("conversion \"%s\" does not exist", + NameListToString(name)))); + } + else + { + ereport(NOTICE, + (errmsg("conversion \"%s\" does not exist, skipping", + NameListToString(name)))); + } + continue; } - return; + tuple = SearchSysCache(CONVOID, + ObjectIdGetDatum(conversionOid), + 0, 0, 0); + 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); } - ConversionDrop(conversionOid, behavior); + performMultipleDeletions(objects, drop->behavior); + + free_object_addresses(objects); } /* diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 05eb55e8699..a8a78b561fb 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.176 2008/05/12 20:01:59 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.177 2008/06/14 18:04:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,7 +21,6 @@ #include "access/transam.h" #include "access/xact.h" #include "catalog/catalog.h" -#include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/indexing.h" @@ -1256,33 +1255,6 @@ relationHasPrimaryKey(Relation rel) return result; } - -/* - * RemoveIndex - * Deletes an index. - */ -void -RemoveIndex(RangeVar *relation, DropBehavior behavior) -{ - Oid indOid; - char relkind; - ObjectAddress object; - - indOid = RangeVarGetRelid(relation, false); - relkind = get_rel_relkind(indOid); - if (relkind != RELKIND_INDEX) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not an index", - relation->relname))); - - object.classId = RelationRelationId; - object.objectId = indOid; - object.objectSubId = 0; - - performDeletion(&object, behavior); -} - /* * ReindexIndex * Recreate a specific index. diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c index d77294ccc22..8029013e2cc 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.49 2008/01/03 21:23:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.50 2008/06/14 18:04:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -148,57 +148,76 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) /* - * RemoveSchema - * Removes a schema. + * RemoveSchemas + * Implements DROP SCHEMA. */ void -RemoveSchema(List *names, DropBehavior behavior, bool missing_ok) +RemoveSchemas(DropStmt *drop) { - char *namespaceName; - Oid namespaceId; - ObjectAddress object; + ObjectAddresses *objects; + ListCell *cell; - if (list_length(names) != 1) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("schema name cannot be qualified"))); - namespaceName = strVal(linitial(names)); - - namespaceId = GetSysCacheOid(NAMESPACENAME, - CStringGetDatum(namespaceName), - 0, 0, 0); - if (!OidIsValid(namespaceId)) + /* + * 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) { - if (!missing_ok) - { + List *names = (List *) lfirst(cell); + char *namespaceName; + Oid namespaceId; + ObjectAddress object; + + if (list_length(names) != 1) ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_SCHEMA), - errmsg("schema \"%s\" does not exist", namespaceName))); - } - else + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("schema name cannot be qualified"))); + namespaceName = strVal(linitial(names)); + + namespaceId = GetSysCacheOid(NAMESPACENAME, + CStringGetDatum(namespaceName), + 0, 0, 0); + + if (!OidIsValid(namespaceId)) { - ereport(NOTICE, - (errmsg("schema \"%s\" does not exist, skipping", - namespaceName))); + if (!drop->missing_ok) + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_SCHEMA), + errmsg("schema \"%s\" does not exist", + namespaceName))); + } + else + { + ereport(NOTICE, + (errmsg("schema \"%s\" does not exist, skipping", + namespaceName))); + } + continue; } - return; - } + /* Permission check */ + if (!pg_namespace_ownercheck(namespaceId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, + namespaceName); - /* 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 deletion. Objects contained in the schema are removed by means - * of their dependency links to the schema. + * Do the deletions. Objects contained in the schema(s) are removed by + * means of their dependency links to the schema. */ - object.classId = NamespaceRelationId; - object.objectId = namespaceId; - object.objectSubId = 0; + performMultipleDeletions(objects, drop->behavior); - performDeletion(&object, behavior); + free_object_addresses(objects); } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 58f0b01b098..7f7102858b1 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.255 2008/05/19 04:14:24 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.256 2008/06/14 18:04:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -61,6 +61,7 @@ #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "storage/bufmgr.h" +#include "storage/lmgr.h" #include "storage/smgr.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -168,6 +169,53 @@ typedef struct NewColumnValue ExprState *exprstate; /* execution state */ } NewColumnValue; +/* + * Error-reporting support for RemoveRelations + */ +struct dropmsgstrings +{ + char kind; + int nonexistent_code; + const char *nonexistent_msg; + const char *skipping_msg; + const char *nota_msg; + const char *drophint_msg; +}; + +static const struct dropmsgstrings dropmsgstringarray[] = { + {RELKIND_RELATION, + ERRCODE_UNDEFINED_TABLE, + gettext_noop("table \"%s\" does not exist"), + gettext_noop("table \"%s\" does not exist, skipping"), + gettext_noop("\"%s\" is not a table"), + gettext_noop("Use DROP TABLE to remove a table.")}, + {RELKIND_SEQUENCE, + ERRCODE_UNDEFINED_TABLE, + gettext_noop("sequence \"%s\" does not exist"), + gettext_noop("sequence \"%s\" does not exist, skipping"), + gettext_noop("\"%s\" is not a sequence"), + gettext_noop("Use DROP SEQUENCE to remove a sequence.")}, + {RELKIND_VIEW, + ERRCODE_UNDEFINED_TABLE, + gettext_noop("view \"%s\" does not exist"), + gettext_noop("view \"%s\" does not exist, skipping"), + gettext_noop("\"%s\" is not a view"), + gettext_noop("Use DROP VIEW to remove a view.")}, + {RELKIND_INDEX, + ERRCODE_UNDEFINED_OBJECT, + gettext_noop("index \"%s\" does not exist"), + gettext_noop("index \"%s\" does not exist, skipping"), + gettext_noop("\"%s\" is not an index"), + gettext_noop("Use DROP INDEX to remove an index.")}, + {RELKIND_COMPOSITE_TYPE, + ERRCODE_UNDEFINED_OBJECT, + gettext_noop("type \"%s\" does not exist"), + gettext_noop("type \"%s\" does not exist, skipping"), + gettext_noop("\"%s\" is not a type"), + gettext_noop("Use DROP TYPE to remove a type.")}, + {'\0', 0, NULL, NULL, NULL, NULL} +}; + static void truncate_check_rel(Relation rel); static List *MergeAttributes(List *schema, List *supers, bool istemp, @@ -497,22 +545,175 @@ DefineRelation(CreateStmt *stmt, char relkind) } /* - * RemoveRelation - * Deletes a relation. + * Emit the right error or warning message for a "DROP" command issued on a + * non-existent relation + */ +static void +DropErrorMsgNonExistent(const char *relname, char rightkind, bool missing_ok) +{ + const struct dropmsgstrings *rentry; + + for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++) + { + if (rentry->kind == rightkind) + { + if (!missing_ok) + { + ereport(ERROR, + (errcode(rentry->nonexistent_code), + errmsg(rentry->nonexistent_msg, relname))); + } + else + { + ereport(NOTICE, (errmsg(rentry->skipping_msg, relname))); + break; + } + } + } + + Assert(rentry->kind != '\0'); /* Should be impossible */ +} + +/* + * Emit the right error message for a "DROP" command issued on a + * relation of the wrong type + */ +static void +DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind) +{ + const struct dropmsgstrings *rentry; + const struct dropmsgstrings *wentry; + + for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++) + if (rentry->kind == rightkind) + break; + Assert(rentry->kind != '\0'); + + for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++) + if (wentry->kind == wrongkind) + break; + /* wrongkind could be something we don't have in our table... */ + + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg(rentry->nota_msg, relname), + (wentry->kind != '\0') ? errhint(wentry->drophint_msg) : 0)); +} + +/* + * RemoveRelations + * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW */ void -RemoveRelation(const RangeVar *relation, DropBehavior behavior) +RemoveRelations(DropStmt *drop) { - Oid relOid; - ObjectAddress object; + ObjectAddresses *objects; + char relkind; + ListCell *cell; - relOid = RangeVarGetRelid(relation, false); + /* + * First we identify all the relations, then we delete them in a single + * performMultipleDeletions() call. This is to avoid unwanted + * DROP RESTRICT errors if one of the relations depends on another. + */ - object.classId = RelationRelationId; - object.objectId = relOid; - object.objectSubId = 0; + /* Determine required relkind */ + switch (drop->removeType) + { + case OBJECT_TABLE: + relkind = RELKIND_RELATION; + break; - performDeletion(&object, behavior); + case OBJECT_INDEX: + relkind = RELKIND_INDEX; + break; + + case OBJECT_SEQUENCE: + relkind = RELKIND_SEQUENCE; + break; + + case OBJECT_VIEW: + relkind = RELKIND_VIEW; + break; + + default: + elog(ERROR, "unrecognized drop object type: %d", + (int) drop->removeType); + relkind = 0; /* keep compiler quiet */ + break; + } + + /* Lock and validate each relation; build a list of object addresses */ + objects = new_object_addresses(); + + foreach(cell, drop->objects) + { + RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell)); + Oid relOid; + HeapTuple tuple; + Form_pg_class classform; + ObjectAddress obj; + + /* + * These next few steps are a great deal like relation_openrv, but we + * don't bother building a relcache entry since we don't need it. + * + * Check for shared-cache-inval messages before trying to access the + * relation. This is needed to cover the case where the name + * identifies a rel that has been dropped and recreated since the + * start of our transaction: if we don't flush the old syscache entry, + * then we'll latch onto that entry and suffer an error later. + */ + AcceptInvalidationMessages(); + + /* Look up the appropriate relation using namespace search */ + relOid = RangeVarGetRelid(rel, true); + + /* Not there? */ + if (!OidIsValid(relOid)) + { + DropErrorMsgNonExistent(rel->relname, relkind, drop->missing_ok); + continue; + } + + /* Get the lock before trying to fetch the syscache entry */ + LockRelationOid(relOid, AccessExclusiveLock); + + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(relOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relOid); + classform = (Form_pg_class) GETSTRUCT(tuple); + + if (classform->relkind != relkind) + DropErrorMsgWrongType(rel->relname, classform->relkind, relkind); + + /* Allow DROP to either table owner or schema owner */ + if (!pg_class_ownercheck(relOid, GetUserId()) && + !pg_namespace_ownercheck(classform->relnamespace, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + rel->relname); + + if (!allowSystemTableMods && IsSystemClass(classform)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied: \"%s\" is a system catalog", + rel->relname))); + + /* OK, we're ready to delete this one */ + obj.classId = RelationRelationId; + obj.objectId = relOid; + obj.objectSubId = 0; + + add_exact_object_address(&obj, objects); + + ReleaseSysCache(tuple); + } + + performMultipleDeletions(objects, drop->behavior); + + free_object_addresses(objects); } /* diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c index e3d877e03f9..ceb5fba0e6a 100644 --- a/src/backend/commands/tsearchcmds.c +++ b/src/backend/commands/tsearchcmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.11 2008/03/26 21:10:38 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.12 2008/06/14 18:04:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -271,40 +271,59 @@ DefineTSParser(List *names, List *parameters) * DROP TEXT SEARCH PARSER */ void -RemoveTSParser(List *names, DropBehavior behavior, bool missing_ok) +RemoveTSParsers(DropStmt *drop) { - Oid prsOid; - ObjectAddress object; + ObjectAddresses *objects; + ListCell *cell; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to drop text search parsers"))); - prsOid = TSParserGetPrsid(names, true); - if (!OidIsValid(prsOid)) + /* + * 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) { - if (!missing_ok) + List *names = (List *) lfirst(cell); + Oid prsOid; + ObjectAddress object; + + prsOid = TSParserGetPrsid(names, true); + + if (!OidIsValid(prsOid)) { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("text search parser \"%s\" does not exist", + 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; } - else - { - ereport(NOTICE, - (errmsg("text search parser \"%s\" does not exist, skipping", - NameListToString(names)))); - } - return; + + object.classId = TSParserRelationId; + object.objectId = prsOid; + object.objectSubId = 0; + + add_exact_object_address(&object, objects); } - object.classId = TSParserRelationId; - object.objectId = prsOid; - object.objectSubId = 0; + performMultipleDeletions(objects, drop->behavior); - performDeletion(&object, behavior); + free_object_addresses(objects); } /* @@ -613,54 +632,72 @@ RenameTSDictionary(List *oldname, const char *newname) * DROP TEXT SEARCH DICTIONARY */ void -RemoveTSDictionary(List *names, DropBehavior behavior, bool missing_ok) +RemoveTSDictionaries(DropStmt *drop) { - Oid dictOid; - ObjectAddress object; - HeapTuple tup; - Oid namespaceId; + 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(); - dictOid = TSDictionaryGetDictid(names, true); - if (!OidIsValid(dictOid)) + foreach(cell, drop->objects) { - if (!missing_ok) - { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("text search dictionary \"%s\" does not exist", - NameListToString(names)))); - } - else + List *names = (List *) lfirst(cell); + Oid dictOid; + ObjectAddress object; + HeapTuple tup; + Oid namespaceId; + + dictOid = TSDictionaryGetDictid(names, true); + + if (!OidIsValid(dictOid)) { - ereport(NOTICE, - (errmsg("text search dictionary \"%s\" does not exist, skipping", - NameListToString(names)))); + 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; } - return; - } - tup = SearchSysCache(TSDICTOID, - ObjectIdGetDatum(dictOid), - 0, 0, 0); + tup = SearchSysCache(TSDICTOID, + ObjectIdGetDatum(dictOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "cache lookup failed for text search dictionary %u", + 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)); - /* 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; - ReleaseSysCache(tup); + add_exact_object_address(&object, objects); - object.classId = TSDictionaryRelationId; - object.objectId = dictOid; - object.objectSubId = 0; + ReleaseSysCache(tup); + } + + performMultipleDeletions(objects, drop->behavior); - performDeletion(&object, behavior); + free_object_addresses(objects); } /* @@ -1086,40 +1123,59 @@ RenameTSTemplate(List *oldname, const char *newname) * DROP TEXT SEARCH TEMPLATE */ void -RemoveTSTemplate(List *names, DropBehavior behavior, bool missing_ok) +RemoveTSTemplates(DropStmt *drop) { - Oid tmplOid; - ObjectAddress object; + ObjectAddresses *objects; + ListCell *cell; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to drop text search templates"))); - tmplOid = TSTemplateGetTmplid(names, true); - if (!OidIsValid(tmplOid)) + /* + * 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) { - if (!missing_ok) - { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("text search template \"%s\" does not exist", - NameListToString(names)))); - } - else + List *names = (List *) lfirst(cell); + Oid tmplOid; + ObjectAddress object; + + tmplOid = TSTemplateGetTmplid(names, true); + + if (!OidIsValid(tmplOid)) { - ereport(NOTICE, - (errmsg("text search template \"%s\" does not exist, skipping", - NameListToString(names)))); + 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; } - return; + + object.classId = TSTemplateRelationId; + object.objectId = tmplOid; + object.objectSubId = 0; + + add_exact_object_address(&object, objects); } - object.classId = TSTemplateRelationId; - object.objectId = tmplOid; - object.objectSubId = 0; + performMultipleDeletions(objects, drop->behavior); - performDeletion(&object, behavior); + free_object_addresses(objects); } /* @@ -1474,48 +1530,66 @@ RenameTSConfiguration(List *oldname, const char *newname) * DROP TEXT SEARCH CONFIGURATION */ void -RemoveTSConfiguration(List *names, DropBehavior behavior, bool missing_ok) +RemoveTSConfigurations(DropStmt *drop) { - Oid cfgOid; - Oid namespaceId; - ObjectAddress object; - HeapTuple tup; + ObjectAddresses *objects; + ListCell *cell; - tup = GetTSConfigTuple(names); + /* + * 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(); - if (!HeapTupleIsValid(tup)) + foreach(cell, drop->objects) { - if (!missing_ok) - { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("text search configuration \"%s\" does not exist", - NameListToString(names)))); - } - else + List *names = (List *) lfirst(cell); + Oid cfgOid; + Oid namespaceId; + ObjectAddress object; + HeapTuple tup; + + tup = GetTSConfigTuple(names); + + if (!HeapTupleIsValid(tup)) { - ereport(NOTICE, - (errmsg("text search configuration \"%s\" does not exist, skipping", - NameListToString(names)))); + 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; } - return; - } - /* 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)); + /* 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)); - ReleaseSysCache(tup); + object.classId = TSConfigRelationId; + object.objectId = cfgOid; + object.objectSubId = 0; + + add_exact_object_address(&object, objects); + + ReleaseSysCache(tup); + } - object.classId = TSConfigRelationId; - object.objectId = cfgOid; - object.objectSubId = 0; + performMultipleDeletions(objects, drop->behavior); - performDeletion(&object, behavior); + free_object_addresses(objects); } /* diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 880788fd22b..793e9262e3e 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.118 2008/05/09 23:32:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.119 2008/06/14 18:04:33 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -483,66 +483,93 @@ DefineType(List *names, List *parameters) /* - * RemoveType - * Removes a datatype. + * 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 -RemoveType(List *names, DropBehavior behavior, bool missing_ok) +RemoveTypes(DropStmt *drop) { - TypeName *typename; - Oid typeoid; - HeapTuple tup; - ObjectAddress object; - Form_pg_type typ; + ObjectAddresses *objects; + ListCell *cell; - /* Make a TypeName so we can use standard type lookup machinery */ - typename = makeTypeNameFromNameList(names); + /* + * 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(); - /* Use LookupTypeName here so that shell types can be removed. */ - tup = LookupTypeName(NULL, typename, NULL); - if (tup == NULL) + foreach(cell, drop->objects) { - if (!missing_ok) + 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) { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("type \"%s\" does not exist", - TypeNameToString(typename)))); + 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; } - else + + 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, + TypeNameToString(typename)); + + if (drop->removeType == OBJECT_DOMAIN) { - ereport(NOTICE, - (errmsg("type \"%s\" does not exist, skipping", - TypeNameToString(typename)))); + /* 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)))); } - return; - } - - typeoid = typeTypeId(tup); - typ = (Form_pg_type) GETSTRUCT(tup); + /* + * Note: we need no special check for array types here, as the normal + * treatment of internal dependencies handles it just fine + */ - /* 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, - TypeNameToString(typename)); + object.classId = TypeRelationId; + object.objectId = typeoid; + object.objectSubId = 0; - /* - * Note: we need no special check for array types here, as the normal - * treatment of internal dependencies handles it just fine - */ + add_exact_object_address(&object, objects); - ReleaseSysCache(tup); + ReleaseSysCache(tup); + } - /* - * Do the deletion - */ - object.classId = TypeRelationId; - object.objectId = typeoid; - object.objectSubId = 0; + performMultipleDeletions(objects, drop->behavior); - performDeletion(&object, behavior); + free_object_addresses(objects); } @@ -924,75 +951,6 @@ DefineDomain(CreateDomainStmt *stmt) /* - * RemoveDomain - * Removes a domain. - * - * This is identical to RemoveType except we insist it be a domain. - */ -void -RemoveDomain(List *names, DropBehavior behavior, bool missing_ok) -{ - TypeName *typename; - Oid typeoid; - HeapTuple tup; - char typtype; - ObjectAddress object; - - /* 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 (!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)))); - } - - return; - } - - typeoid = typeTypeId(tup); - - /* Permission check: must own type or its namespace */ - if (!pg_type_ownercheck(typeoid, GetUserId()) && - !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace, - GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, - TypeNameToString(typename)); - - /* Check that this is actually a domain */ - typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype; - - if (typtype != TYPTYPE_DOMAIN) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a domain", - TypeNameToString(typename)))); - - ReleaseSysCache(tup); - - /* - * Do the deletion - */ - object.classId = TypeRelationId; - object.objectId = typeoid; - object.objectSubId = 0; - - performDeletion(&object, behavior); -} - -/* * DefineEnum * Registers a new enum. */ diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 0a2e3a6e465..8814cf8dc1b 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.104 2008/01/01 19:45:49 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.105 2008/06/14 18:04:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,7 +16,6 @@ #include "access/heapam.h" #include "access/xact.h" -#include "catalog/dependency.h" #include "catalog/namespace.h" #include "commands/defrem.h" #include "commands/tablecmds.h" @@ -446,26 +445,3 @@ DefineView(ViewStmt *stmt, const char *queryString) */ DefineViewRules(viewOid, viewParse, stmt->replace); } - -/* - * RemoveView - * - * Remove a view given its name - * - * We just have to drop the relation; the associated rules will be - * cleaned up automatically. - */ -void -RemoveView(const RangeVar *view, DropBehavior behavior) -{ - Oid viewOid; - ObjectAddress object; - - viewOid = RangeVarGetRelid(view, false); - - object.classId = RelationRelationId; - object.objectId = viewOid; - object.objectSubId = 0; - - performDeletion(&object, behavior); -} |