From dced80d65ce60ddb20c7aa322339b746b1164f01 Mon Sep 17 00:00:00 2001 From: tgl Date: Sat, 14 Jun 2008 18:04:34 +0000 Subject: [PATCH] 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. --- src/backend/catalog/dependency.c | 9 +- src/backend/catalog/pg_conversion.c | 35 ---- src/backend/commands/conversioncmds.c | 76 ++++++-- src/backend/commands/indexcmds.c | 28 --- src/backend/commands/schemacmds.c | 93 ++++++---- src/backend/commands/tablecmds.c | 221 +++++++++++++++++++++- src/backend/commands/tsearchcmds.c | 294 +++++++++++++++++++----------- src/backend/commands/typecmds.c | 184 ++++++++----------- src/backend/commands/view.c | 24 --- src/backend/tcop/utility.c | 280 ++++------------------------ src/include/catalog/pg_conversion_fn.h | 3 - src/include/commands/conversioncmds.h | 3 +- src/include/commands/defrem.h | 13 +- src/include/commands/schemacmds.h | 2 +- src/include/commands/tablecmds.h | 2 +- src/include/commands/typecmds.h | 3 +- src/include/commands/view.h | 1 - src/test/regress/expected/foreign_key.out | 15 +- src/test/regress/expected/truncate.out | 6 - src/test/regress/sql/foreign_key.sql | 4 +- 20 files changed, 640 insertions(+), 656 deletions(-) diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 4a9ad9c5d0..ae5a542849 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -259,6 +259,10 @@ performMultipleDeletions(const ObjectAddresses *objects, ObjectAddresses *targetObjects; int i; + /* No work if no objects... */ + if (objects->numrefs <= 0) + return; + /* * We save some cycles by opening pg_depend just once and passing the * Relation pointer down to all the recursive deletion steps. @@ -295,11 +299,14 @@ performMultipleDeletions(const ObjectAddresses *objects, /* * Check if deletion is allowed, and report about cascaded deletes. + * + * If there's exactly one object being deleted, report it the same + * way as in performDeletion(), else we have to be vaguer. */ reportDependentObjects(targetObjects, behavior, NOTICE, - NULL); + (objects->numrefs == 1 ? objects->refs : NULL)); /* * Delete all the objects in the proper order. diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c index 5a5888e5da..2efc2eee76 100644 --- a/src/backend/catalog/pg_conversion.c +++ b/src/backend/catalog/pg_conversion.c @@ -18,7 +18,6 @@ #include "access/sysattr.h" #include "catalog/dependency.h" #include "catalog/indexing.h" -#include "catalog/namespace.h" #include "catalog/pg_conversion.h" #include "catalog/pg_conversion_fn.h" #include "catalog/pg_namespace.h" @@ -139,40 +138,6 @@ ConversionCreate(const char *conname, Oid connamespace, } /* - * ConversionDrop - * - * Drop a conversion after doing permission checks. - */ -void -ConversionDrop(Oid conversionOid, DropBehavior behavior) -{ - HeapTuple tuple; - ObjectAddress object; - - tuple = SearchSysCache(CONVOID, - ObjectIdGetDatum(conversionOid), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "cache lookup failed for conversion %u", conversionOid); - - if (!superuser() && - ((Form_pg_conversion) GETSTRUCT(tuple))->conowner != GetUserId()) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, - NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname)); - - ReleaseSysCache(tuple); - - /* - * Do the deletion - */ - object.classId = ConversionRelationId; - object.objectId = conversionOid; - object.objectSubId = 0; - - performDeletion(&object, behavior); -} - -/* * RemoveConversionById * * Remove a tuple from pg_conversion by Oid. This function is solely diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c index 3e74f2e5d8..14f819761a 100644 --- a/src/backend/commands/conversioncmds.c +++ b/src/backend/commands/conversioncmds.c @@ -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 887c35a841..4b714ca605 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -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 c6aff1c49c..4c617a5019 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -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 c3ff499ff5..a068efd2dd 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -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 22eb8632a0..407a7334c2 100644 --- a/src/backend/commands/tsearchcmds.c +++ b/src/backend/commands/tsearchcmds.c @@ -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 08acdb6b55..ecd54be078 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -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 c57ca2a243..e09ea26c9e 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -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); -} diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 5d37d5785d..8e1ad414c1 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -58,161 +58,6 @@ /* - * Error-checking support for DROP commands - */ - -struct msgstrings -{ - char kind; - int nonexistent_code; - const char *nonexistent_msg; - const char *skipping_msg; - const char *nota_msg; - const char *drophint_msg; -}; - -static const struct msgstrings msgstringarray[] = { - {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} -}; - - -/* - * Emit the right error message for a "DROP" command issued on a - * relation of the wrong type - */ -static void -DropErrorMsgWrongType(char *relname, char wrongkind, char rightkind) -{ - const struct msgstrings *rentry; - const struct msgstrings *wentry; - - for (rentry = msgstringarray; rentry->kind != '\0'; rentry++) - if (rentry->kind == rightkind) - break; - Assert(rentry->kind != '\0'); - - for (wentry = msgstringarray; 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)); -} - -/* - * Emit the right error message for a "DROP" command issued on a - * non-existent relation - */ -static void -DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok) -{ - const struct msgstrings *rentry; - - for (rentry = msgstringarray; rentry->kind != '\0'; rentry++) - { - if (rentry->kind == rightkind) - { - if (!missing_ok) - { - ereport(ERROR, - (errcode(rentry->nonexistent_code), - errmsg(rentry->nonexistent_msg, rel->relname))); - } - else - { - ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname))); - break; - } - } - } - - Assert(rentry->kind != '\0'); /* Should be impossible */ -} - -/* - * returns false if missing_ok is true and the object does not exist, - * true if object exists and permissions are OK, - * errors otherwise - * - */ - -static bool -CheckDropPermissions(RangeVar *rel, char rightkind, bool missing_ok) -{ - Oid relOid; - HeapTuple tuple; - Form_pg_class classform; - - relOid = RangeVarGetRelid(rel, true); - if (!OidIsValid(relOid)) - { - DropErrorMsgNonExistent(rel, rightkind, missing_ok); - return false; - } - - 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 != rightkind) - DropErrorMsgWrongType(rel->relname, classform->relkind, - rightkind); - - /* 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))); - - ReleaseSysCache(tuple); - - return true; -} - -/* * Verify user has ownership of specified relation, else ereport. * * If noCatalogs is true then we also deny access to system catalogs, @@ -603,96 +448,49 @@ ProcessUtility(Node *parsetree, case T_DropStmt: { DropStmt *stmt = (DropStmt *) parsetree; - ListCell *arg; - foreach(arg, stmt->objects) + switch (stmt->removeType) { - List *names = (List *) lfirst(arg); - RangeVar *rel; + case OBJECT_TABLE: + case OBJECT_SEQUENCE: + case OBJECT_VIEW: + case OBJECT_INDEX: + RemoveRelations(stmt); + break; - switch (stmt->removeType) - { - case OBJECT_TABLE: - rel = makeRangeVarFromNameList(names); - if (CheckDropPermissions(rel, RELKIND_RELATION, - stmt->missing_ok)) - RemoveRelation(rel, stmt->behavior); - break; - - case OBJECT_SEQUENCE: - rel = makeRangeVarFromNameList(names); - if (CheckDropPermissions(rel, RELKIND_SEQUENCE, - stmt->missing_ok)) - RemoveRelation(rel, stmt->behavior); - break; - - case OBJECT_VIEW: - rel = makeRangeVarFromNameList(names); - if (CheckDropPermissions(rel, RELKIND_VIEW, - stmt->missing_ok)) - RemoveView(rel, stmt->behavior); - break; - - case OBJECT_INDEX: - rel = makeRangeVarFromNameList(names); - if (CheckDropPermissions(rel, RELKIND_INDEX, - stmt->missing_ok)) - RemoveIndex(rel, stmt->behavior); - break; - - case OBJECT_TYPE: - /* RemoveType does its own permissions checks */ - RemoveType(names, stmt->behavior, - stmt->missing_ok); - break; - - case OBJECT_DOMAIN: - /* RemoveDomain does its own permissions checks */ - RemoveDomain(names, stmt->behavior, - stmt->missing_ok); - break; - - case OBJECT_CONVERSION: - DropConversionCommand(names, stmt->behavior, - stmt->missing_ok); - break; - - case OBJECT_SCHEMA: - /* RemoveSchema does its own permissions checks */ - RemoveSchema(names, stmt->behavior, - stmt->missing_ok); - break; - - case OBJECT_TSPARSER: - RemoveTSParser(names, stmt->behavior, - stmt->missing_ok); - break; - - case OBJECT_TSDICTIONARY: - RemoveTSDictionary(names, stmt->behavior, - stmt->missing_ok); - break; - - case OBJECT_TSTEMPLATE: - RemoveTSTemplate(names, stmt->behavior, - stmt->missing_ok); - break; - - case OBJECT_TSCONFIGURATION: - RemoveTSConfiguration(names, stmt->behavior, - stmt->missing_ok); - break; - - default: - elog(ERROR, "unrecognized drop object type: %d", - (int) stmt->removeType); - break; - } + case OBJECT_TYPE: + case OBJECT_DOMAIN: + RemoveTypes(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; - /* - * We used to need to do CommandCounterIncrement() here, - * but now it's done inside performDeletion(). - */ + case OBJECT_TSCONFIGURATION: + RemoveTSConfigurations(stmt); + break; + + default: + elog(ERROR, "unrecognized drop object type: %d", + (int) stmt->removeType); + break; } } break; diff --git a/src/include/catalog/pg_conversion_fn.h b/src/include/catalog/pg_conversion_fn.h index c5ff7bcb9e..c23233bfee 100644 --- a/src/include/catalog/pg_conversion_fn.h +++ b/src/include/catalog/pg_conversion_fn.h @@ -14,13 +14,10 @@ #ifndef PG_CONVERSION_FN_H #define PG_CONVERSION_FN_H -#include "nodes/parsenodes.h" - extern Oid ConversionCreate(const char *conname, Oid connamespace, Oid conowner, int32 conforencoding, int32 contoencoding, Oid conproc, bool def); -extern void ConversionDrop(Oid conversionOid, DropBehavior behavior); extern void RemoveConversionById(Oid conversionOid); extern Oid FindConversion(const char *conname, Oid connamespace); extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); diff --git a/src/include/commands/conversioncmds.h b/src/include/commands/conversioncmds.h index 7abe174d2c..e395a92a80 100644 --- a/src/include/commands/conversioncmds.h +++ b/src/include/commands/conversioncmds.h @@ -18,8 +18,7 @@ #include "nodes/parsenodes.h" extern void CreateConversionCommand(CreateConversionStmt *parsetree); -extern void DropConversionCommand(List *conversion_name, - DropBehavior behavior, bool missing_ok); +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); diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index cf97ba91a1..50bd75c309 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -34,7 +34,6 @@ extern void DefineIndex(RangeVar *heapRelation, bool skip_build, bool quiet, bool concurrent); -extern void RemoveIndex(RangeVar *relation, DropBehavior behavior); extern void ReindexIndex(RangeVar *indexRelation); extern void ReindexTable(RangeVar *relation); extern void ReindexDatabase(const char *databaseName, @@ -94,28 +93,24 @@ extern void AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwn /* commands/tsearchcmds.c */ extern void DefineTSParser(List *names, List *parameters); extern void RenameTSParser(List *oldname, const char *newname); -extern void RemoveTSParser(List *names, DropBehavior behavior, - bool missing_ok); +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 RemoveTSDictionary(List *names, DropBehavior behavior, - bool missing_ok); +extern void RemoveTSDictionaries(DropStmt *drop); extern void RemoveTSDictionaryById(Oid dictId); extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt); extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId); extern void DefineTSTemplate(List *names, List *parameters); extern void RenameTSTemplate(List *oldname, const char *newname); -extern void RemoveTSTemplate(List *names, DropBehavior behavior, - bool missing_ok); +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 RemoveTSConfiguration(List *names, DropBehavior behavior, - bool missing_ok); +extern void RemoveTSConfigurations(DropStmt *stmt); extern void RemoveTSConfigurationById(Oid cfgId); extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt); extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId); diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h index 19b0ecd23b..e5a1322338 100644 --- a/src/include/commands/schemacmds.h +++ b/src/include/commands/schemacmds.h @@ -20,7 +20,7 @@ extern void CreateSchemaCommand(CreateSchemaStmt *parsetree, const char *queryString); -extern void RemoveSchema(List *names, DropBehavior behavior, bool missing_ok); +extern void RemoveSchemas(DropStmt *drop); extern void RemoveSchemaById(Oid schemaOid); extern void RenameSchema(const char *oldname, const char *newname); diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index d89a35615c..40fdf20e54 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -20,7 +20,7 @@ extern Oid DefineRelation(CreateStmt *stmt, char relkind); -extern void RemoveRelation(const RangeVar *relation, DropBehavior behavior); +extern void RemoveRelations(DropStmt *drop); extern void AlterTable(AlterTableStmt *stmt); diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h index eae69bf4f2..3992561234 100644 --- a/src/include/commands/typecmds.h +++ b/src/include/commands/typecmds.h @@ -20,10 +20,9 @@ #define DEFAULT_TYPDELIM ',' extern void DefineType(List *names, List *parameters); -extern void RemoveType(List *names, DropBehavior behavior, bool missing_ok); +extern void RemoveTypes(DropStmt *drop); extern void RemoveTypeById(Oid typeOid); extern void DefineDomain(CreateDomainStmt *stmt); -extern void RemoveDomain(List *names, DropBehavior behavior, bool missing_ok); extern void DefineEnum(CreateEnumStmt *stmt); extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist); diff --git a/src/include/commands/view.h b/src/include/commands/view.h index 803bb82b3c..d520c37703 100644 --- a/src/include/commands/view.h +++ b/src/include/commands/view.h @@ -17,6 +17,5 @@ #include "nodes/parsenodes.h" extern void DefineView(ViewStmt *stmt, const char *queryString); -extern void RemoveView(const RangeVar *view, DropBehavior behavior); #endif /* VIEW_H */ diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index e3086d2e08..c805322b6d 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -1086,8 +1086,7 @@ INSERT INTO fktable VALUES (100, 200); COMMIT; ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_fk_fkey" DETAIL: Key (fk)=(200) is not present in table "pktable". -DROP TABLE pktable, fktable CASCADE; -NOTICE: drop cascades to constraint fktable_fk_fkey on table fktable +DROP TABLE pktable, fktable; -- test notice about expensive referential integrity checks, -- where the index cannot be used because of type incompatibilities. CREATE TEMP TABLE pktable ( @@ -1156,17 +1155,7 @@ ALTER TABLE fktable ADD CONSTRAINT fk_241_132 FOREIGN KEY (x2,x4,x1) REFERENCES pktable(id1,id3,id2); ERROR: foreign key constraint "fk_241_132" cannot be implemented DETAIL: Key columns "x2" and "id1" are of incompatible types: character varying and integer. -DROP TABLE pktable, fktable CASCADE; -NOTICE: drop cascades to 9 other objects -DETAIL: drop cascades to constraint fktable_x3_fkey on table fktable -drop cascades to constraint fk_1_3 on table fktable -drop cascades to constraint fktable_x2_fkey on table fktable -drop cascades to constraint fk_4_2 on table fktable -drop cascades to constraint fktable_x1_fkey on table fktable -drop cascades to constraint fk_5_1 on table fktable -drop cascades to constraint fk_123_123 on table fktable -drop cascades to constraint fk_213_213 on table fktable -drop cascades to constraint fk_253_213 on table fktable +DROP TABLE pktable, fktable; -- test a tricky case: we can elide firing the FK check trigger during -- an UPDATE if the UPDATE did not change the foreign key -- field. However, we can't do this if our transaction was the one that diff --git a/src/test/regress/expected/truncate.out b/src/test/regress/expected/truncate.out index 520db9d9b1..c9134ee233 100644 --- a/src/test/regress/expected/truncate.out +++ b/src/test/regress/expected/truncate.out @@ -141,12 +141,6 @@ SELECT * FROM trunc_e; (0 rows) DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to constraint trunc_b_a_fkey on table trunc_b -drop cascades to constraint trunc_e_a_fkey on table trunc_e -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to constraint trunc_d_a_fkey on table trunc_d -drop cascades to constraint trunc_e_b_fkey on table trunc_e -- Test ON TRUNCATE triggers CREATE TABLE trunc_trigger_test (f1 int, f2 text, f3 text); CREATE TABLE trunc_trigger_log (tgop text, tglevel text, tgwhen text, diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index 519a5056af..cc7b23f113 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -724,7 +724,7 @@ INSERT INTO fktable VALUES (100, 200); -- error here on commit COMMIT; -DROP TABLE pktable, fktable CASCADE; +DROP TABLE pktable, fktable; -- test notice about expensive referential integrity checks, -- where the index cannot be used because of type incompatibilities. @@ -799,7 +799,7 @@ FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id2,id3,id1); ALTER TABLE fktable ADD CONSTRAINT fk_241_132 FOREIGN KEY (x2,x4,x1) REFERENCES pktable(id1,id3,id2); -DROP TABLE pktable, fktable CASCADE; +DROP TABLE pktable, fktable; -- test a tricky case: we can elide firing the FK check trigger during -- an UPDATE if the UPDATE did not change the foreign key -- 2.11.4.GIT