[v9.2] DROP Reworks Part.1 - Consolidate routines to handle DropStmt

Started by Kohei KaiGaiover 14 years ago11 messages
#1Kohei KaiGai
kaigai@kaigai.gr.jp
1 attachment(s)

The attached patch can be applied on the part-0 patch, and enables to
consolidate routines to handle DropStmt into one common function;
void RemoveObjects(DropStmt *stmt);

The top-half of object deletion steps are almost consist of the following steps.
1) Look up object-Id of the supplied object name
If not found, it raises an error or a notice with skip this deletion.
2) Apply ownership checks on the target object itself or the schema
that underlies
the target object.
3) Add the ObjectAddress of the target object into ObjectAddresses, then invokes
performMultipleDeletions() or performDeletion().

However, we don't need to have individual routines for each object classes,
because get_object_address takes up the portion of (1), and
check_object_ownership also takes up the portion of (2).
Here is no differences between most of objects classes on the (3).

So, the new RemoveObjects() consolidates routines to handle DropStmt for
each object classes. Instead of this common function, the following routines
were eliminated.
RemoveRelations(DropStmt *drop);
RemoveTypes(DropStmt *drop);
DropCollationsCommand(DropStmt *drop);
DropConversionsCommand(DropStmt *drop);
RemoveSchemas(DropStmt *drop);
RemoveTSParsers(DropStmt *drop);
RemoveTSDictionaries(DropStmt *drop);
RemoveTSTemplates(DropStmt *drop);
RemoveTSConfigurations(DropStmt *drop);
RemoveExtensions(DropStmt *drop);

Routines to handle other DROP statement (such as RemoveFuncStmt or
DropPLangStmt) are not scope of this patch to restrain the patch size.
However, it is not a tough work to fit these objects with this structure.
(It may need a discussion for databases, tablespaces and roles)

Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>

Attachments:

pgsql-v9.2-drop-reworks-part-1.1.patchapplication/octet-stream; name=pgsql-v9.2-drop-reworks-part-1.1.patchDownload
 src/backend/catalog/objectaddress.c   |   22 ++
 src/backend/commands/Makefile         |    2 +-
 src/backend/commands/collationcmds.c  |   61 ------
 src/backend/commands/conversioncmds.c |   61 ------
 src/backend/commands/dropcmds.c       |  355 +++++++++++++++++++++++++++++++++
 src/backend/commands/extension.c      |   63 ------
 src/backend/commands/schemacmds.c     |   63 ------
 src/backend/commands/tablecmds.c      |  248 -----------------------
 src/backend/commands/tsearchcmds.c    |  254 -----------------------
 src/backend/commands/typecmds.c       |   92 ---------
 src/backend/nodes/copyfuncs.c         |    1 +
 src/backend/nodes/equalfuncs.c        |    1 +
 src/backend/parser/gram.y             |    2 +
 src/backend/tcop/utility.c            |   57 +-----
 src/include/commands/collationcmds.h  |    1 -
 src/include/commands/conversioncmds.h |    1 -
 src/include/commands/defrem.h         |    6 +-
 src/include/commands/extension.h      |    1 -
 src/include/commands/schemacmds.h     |    1 -
 src/include/commands/tablecmds.h      |    2 -
 src/include/commands/typecmds.h       |    1 -
 src/include/nodes/parsenodes.h        |    1 +
 22 files changed, 386 insertions(+), 910 deletions(-)

diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index cb5ced4..fe40e5a 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -456,6 +456,28 @@ get_relation_by_qualified_name(ObjectType objtype, List *objname,
 		return address;
 	}
 
+	/*
+	 * In DROP INDEX, attempt to acquire lock on the parent table before
+	 * locking the index.  index_drop() will need this anyway, and since
+	 * regular queries lock tables before their indexes, we risk deadlock
+	 * if we do it the other way around.  No error if we don't find a
+	 * pg_index entry, though --- that most likely means it isn't an
+	 * index, and we'll fail below.
+	 */
+	if (objtype == OBJECT_INDEX)
+	{
+		HeapTuple	tuple;
+
+		tuple =	SearchSysCache1(INDEXRELID, ObjectIdGetDatum(address.objectId));
+		if (HeapTupleIsValid(tuple))
+		{
+			Form_pg_index index = (Form_pg_index) GETSTRUCT(tuple);
+
+			LockRelationOid(index->indrelid, AccessExclusiveLock);
+			ReleaseSysCache(tuple);
+		}
+	}
+
 	relation = relation_open(address.objectId, lockmode);
 	switch (objtype)
 	{
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 81fd658..4af7aad 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -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 \
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index 7f8a108..6872e58 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -145,67 +145,6 @@ DefineCollation(List *names, List *parameters)
 }
 
 /*
- * 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
  */
 void
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c
index 2c1c6da..8dafec9 100644
--- a/src/backend/commands/conversioncmds.c
+++ b/src/backend/commands/conversioncmds.c
@@ -120,67 +120,6 @@ CreateConversionCommand(CreateConversionStmt *stmt)
 }
 
 /*
- * 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
  */
 void
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
new file mode 100644
index 0000000..03053e0
--- /dev/null
+++ b/src/backend/commands/dropcmds.c
@@ -0,0 +1,355 @@
+/*
+ * dropcmd.c
+ *    routine to support DROP statement commonly
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaddress.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_collation.h"
+#include "catalog/pg_conversion.h"
+#include "catalog/pg_extension.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_ts_config.h"
+#include "catalog/pg_ts_dict.h"
+#include "catalog/pg_ts_parser.h"
+#include "catalog/pg_ts_template.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "parser/parse_type.h"
+#include "utils/acl.h"
+#include "utils/inval.h"
+#include "utils/syscache.h"
+
+static void
+notice_object_non_existent(ObjectType type, List *objname, List *objargs)
+{
+	switch (type)
+	{
+		case OBJECT_TABLE:
+			ereport(NOTICE,
+					(errmsg("table \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_INDEX:
+			ereport(NOTICE,
+					(errmsg("index \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_SEQUENCE:
+			ereport(NOTICE,
+					(errmsg("sequence \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_VIEW:
+			ereport(NOTICE,
+					(errmsg("view \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_FOREIGN_TABLE:
+			ereport(NOTICE,
+					(errmsg("foreign table \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_TYPE:
+		case OBJECT_DOMAIN:
+			{
+				TypeName   *typename = makeTypeNameFromNameList(objname);
+				ereport(NOTICE,
+						(errmsg("type \"%s\" does not exist, skipping",
+								TypeNameToString(typename))));
+			}
+			break;
+
+		case OBJECT_COLLATION:
+			ereport(NOTICE,
+					(errmsg("collation \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_CONVERSION:
+			ereport(NOTICE,
+					(errmsg("conversion \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_SCHEMA:
+			ereport(NOTICE,
+					(errmsg("schema \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_TSPARSER:
+			ereport(NOTICE,
+					(errmsg("text search parser \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_TSDICTIONARY:
+			ereport(NOTICE,
+					(errmsg("text search dictionary \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_TSTEMPLATE:
+			ereport(NOTICE,
+					(errmsg("text search template \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_TSCONFIGURATION:
+			ereport(NOTICE,
+					(errmsg("text search configuration \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_EXTENSION:
+			ereport(NOTICE,
+					(errmsg("extension \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		default:
+			elog(ERROR, "unrecognized drop object type: %d", (int) type);
+			break;
+	}
+}
+
+static Oid
+get_object_namespace(ObjectType type, const ObjectAddress *address)
+{
+	HeapTuple	tuple;
+	Oid			objectId = address->objectId;
+	Oid			namespaceId = InvalidOid;
+
+	switch (type)
+	{
+		case OBJECT_TABLE:
+		case OBJECT_INDEX:
+		case OBJECT_SEQUENCE:
+		case OBJECT_VIEW:
+		case OBJECT_FOREIGN_TABLE:
+			tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for relation %u", objectId);
+			namespaceId = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case OBJECT_TYPE:
+		case OBJECT_DOMAIN:
+			tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for type %u", objectId);
+			namespaceId = ((Form_pg_type) GETSTRUCT(tuple))->typnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case OBJECT_COLLATION:
+			tuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for collation %u", objectId);
+			namespaceId = ((Form_pg_collation) GETSTRUCT(tuple))->collnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case OBJECT_CONVERSION:
+			tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for conversion %u", objectId);
+			namespaceId = ((Form_pg_conversion) GETSTRUCT(tuple))->connamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case OBJECT_TSPARSER:
+			tuple = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for text search parser %u",
+					 objectId);
+			namespaceId = ((Form_pg_ts_parser) GETSTRUCT(tuple))->prsnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case OBJECT_TSDICTIONARY:
+			tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for text search dictionary %u",
+					 objectId);
+			namespaceId = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case OBJECT_TSTEMPLATE:
+			tuple = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for text search template %u",
+					 objectId);
+			namespaceId = ((Form_pg_ts_template) GETSTRUCT(tuple))->tmplnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case OBJECT_TSCONFIGURATION:
+			tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for text search dictionary %u",
+					 objectId);
+			namespaceId = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		default:
+			/*
+			 * The supplied object type does not have its namespace.
+			 */
+			namespaceId = InvalidOid;
+			break;
+	}
+	return namespaceId;
+}
+
+void
+RemoveObjects(DropStmt *stmt)
+{
+	ObjectAddresses *objects;
+	ListCell   *cell1;
+	ListCell   *cell2;
+
+	Assert(!stmt->arguments ||
+		   list_length(stmt->objects) == list_length(stmt->arguments));
+
+	objects = new_object_addresses();
+
+	for (cell1 = list_head(stmt->objects), cell2 = list_head(stmt->arguments);
+		 cell1 != NULL;
+		 cell1 = lnext(cell1), cell2 = (!cell2 ? NULL : lnext(cell2)))
+	{
+		ObjectAddress	address;
+		List	   *objname = lfirst(cell1);
+		List	   *objargs = (cell2 ? lfirst(cell2) : NIL);
+		Relation	relation = NULL;
+		Oid			namespaceId;
+
+		switch (stmt->removeType)
+		{
+			case OBJECT_TABLE:
+			case OBJECT_SEQUENCE:
+			case OBJECT_VIEW:
+			case OBJECT_INDEX:
+			case OBJECT_FOREIGN_TABLE:
+				/*
+				 * 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();
+
+				break;
+
+			case OBJECT_TYPE:
+			case OBJECT_DOMAIN:
+			case OBJECT_COLLATION:
+			case OBJECT_CONVERSION:
+			case OBJECT_SCHEMA:
+			case OBJECT_TSPARSER:
+			case OBJECT_TSDICTIONARY:
+			case OBJECT_TSTEMPLATE:
+			case OBJECT_TSCONFIGURATION:
+			case OBJECT_EXTENSION:
+				/* nothing to do */
+				break;
+
+			default:
+				elog(ERROR, "unrecognized drop object type: %d",
+					 (int) stmt->removeType);
+				break;
+		}
+
+		/*
+		 * Resolve object name and arguments into ObjectAddress
+		 */
+		address = get_object_address(stmt->removeType,
+									 objname, objargs,
+									 stmt->missing_ok,
+									 &relation,
+									 AccessExclusiveLock);
+
+		/*
+		 * Raise an notice, if supplied object was not found
+		 */
+		if (!OidIsValid(address.objectId))
+		{
+			notice_object_non_existent(stmt->removeType,
+									   objname, objargs);
+			continue;
+		}
+
+		/*
+		 * Permission checks
+		 */
+		namespaceId = get_object_namespace(stmt->removeType, &address);
+		if (!OidIsValid(namespaceId) ||
+			!pg_namespace_ownercheck(namespaceId, GetUserId()))
+			check_object_ownership(GetUserId(), stmt->removeType, address,
+								   objname, objargs, relation);
+
+		/*
+		 * Rest of sanity checks on objects deletion
+		 */
+		switch (stmt->removeType)
+		{
+			case OBJECT_TABLE:
+            case OBJECT_SEQUENCE:
+            case OBJECT_VIEW:
+            case OBJECT_INDEX:
+            case OBJECT_FOREIGN_TABLE:
+				if (!!allowSystemTableMods && IsSystemRelation(relation))
+					ereport(ERROR,
+							(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+						errmsg("permission denied: \"%s\" is a system catalog",
+							   RelationGetRelationName(relation))));
+				break;
+
+			default:
+				/* nothing to do */
+				break;
+		}
+
+		/*
+		 * If get_object_address() opened the relation for us, we close it
+		 * to keep the reference count correct - but we retain any locks
+		 * acquired by get_object_address() until commit time, to guard
+		 * against concurrent activities.
+		 */
+		if (relation)
+		{
+			heap_close(relation, NoLock);
+			RelationForgetRelation(address.objectId);
+		}
+
+		add_exact_object_address(&address, objects);
+	}
+	performMultipleDeletions(objects, stmt->behavior);
+
+	free_object_addresses(objects);
+}
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index cf13200..87f46ca 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1554,69 +1554,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.
  *
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index 82bbf8f..3840ec0 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -147,69 +147,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.
  */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6279f2b..25aca501 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -181,59 +181,6 @@ 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.")},
-	{RELKIND_FOREIGN_TABLE,
-		ERRCODE_UNDEFINED_OBJECT,
-		gettext_noop("foreign table \"%s\" does not exist"),
-		gettext_noop("foreign table \"%s\" does not exist, skipping"),
-		gettext_noop("\"%s\" is not a foreign table"),
-	gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
-	{'\0', 0, NULL, NULL, NULL, NULL}
-};
-
 /* Alter table target-type flags for ATSimplePermissions */
 #define		ATT_TABLE				0x0001
 #define		ATT_VIEW				0x0002
@@ -633,201 +580,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 }
 
 /*
- * 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("%s", _(wentry->drophint_msg)) : 0));
-}
-
-/*
- * RemoveRelations
- *		Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
- *		DROP FOREIGN TABLE
- */
-void
-RemoveRelations(DropStmt *drop)
-{
-	ObjectAddresses *objects;
-	char		relkind;
-	ListCell   *cell;
-
-	/*
-	 * 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.
-	 */
-
-	/* Determine required relkind */
-	switch (drop->removeType)
-	{
-		case OBJECT_TABLE:
-			relkind = RELKIND_RELATION;
-			break;
-
-		case OBJECT_INDEX:
-			relkind = RELKIND_INDEX;
-			break;
-
-		case OBJECT_SEQUENCE:
-			relkind = RELKIND_SEQUENCE;
-			break;
-
-		case OBJECT_VIEW:
-			relkind = RELKIND_VIEW;
-			break;
-
-		case OBJECT_FOREIGN_TABLE:
-			relkind = RELKIND_FOREIGN_TABLE;
-			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;
-		}
-
-		/*
-		 * In DROP INDEX, attempt to acquire lock on the parent table before
-		 * locking the index.  index_drop() will need this anyway, and since
-		 * regular queries lock tables before their indexes, we risk deadlock
-		 * if we do it the other way around.  No error if we don't find a
-		 * pg_index entry, though --- that most likely means it isn't an
-		 * index, and we'll fail below.
-		 */
-		if (relkind == RELKIND_INDEX)
-		{
-			tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
-			if (HeapTupleIsValid(tuple))
-			{
-				Form_pg_index index = (Form_pg_index) GETSTRUCT(tuple);
-
-				LockRelationOid(index->indrelid, AccessExclusiveLock);
-				ReleaseSysCache(tuple);
-			}
-		}
-
-		/* Get the lock before trying to fetch the syscache entry */
-		LockRelationOid(relOid, AccessExclusiveLock);
-
-		tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
-		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);
-}
-
-/*
  * ExecuteTruncate
  *		Executes a TRUNCATE command.
  *
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index d08c9bb..10a1507 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -282,65 +282,6 @@ DefineTSParser(List *names, List *parameters)
 }
 
 /*
- * 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.
  */
 void
@@ -734,76 +675,6 @@ AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid)
 }
 
 /*
- * 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.
  */
 void
@@ -1266,65 +1137,6 @@ AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid)
 }
 
 /*
- * 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.
  */
 void
@@ -1719,72 +1531,6 @@ AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid)
 }
 
 /*
- * 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.
  */
 void
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index f8eb5bc..ba75a59 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -616,98 +616,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.
  */
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c9133dd..0bd8882 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2734,6 +2734,7 @@ _copyDropStmt(DropStmt *from)
 	DropStmt   *newnode = makeNode(DropStmt);
 
 	COPY_NODE_FIELD(objects);
+	COPY_NODE_FIELD(arguments);
 	COPY_SCALAR_FIELD(removeType);
 	COPY_SCALAR_FIELD(behavior);
 	COPY_SCALAR_FIELD(missing_ok);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 3a0267c..ea972be 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1182,6 +1182,7 @@ static bool
 _equalDropStmt(DropStmt *a, DropStmt *b)
 {
 	COMPARE_NODE_FIELD(objects);
+	COMPARE_NODE_FIELD(arguments);
 	COMPARE_SCALAR_FIELD(removeType);
 	COMPARE_SCALAR_FIELD(behavior);
 	COMPARE_SCALAR_FIELD(missing_ok);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1d39674..ffc26df 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4725,6 +4725,7 @@ DropStmt:	DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior
 					n->removeType = $2;
 					n->missing_ok = TRUE;
 					n->objects = $5;
+					n->arguments = NIL;
 					n->behavior = $6;
 					$$ = (Node *)n;
 				}
@@ -4734,6 +4735,7 @@ DropStmt:	DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior
 					n->removeType = $2;
 					n->missing_ok = FALSE;
 					n->objects = $3;
+					n->arguments = NIL;
 					n->behavior = $4;
 					$$ = (Node *)n;
 				}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 0559998..df13b8c 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -636,62 +636,7 @@ standard_ProcessUtility(Node *parsetree,
 			break;
 
 		case T_DropStmt:
-			{
-				DropStmt   *stmt = (DropStmt *) parsetree;
-
-				switch (stmt->removeType)
-				{
-					case OBJECT_TABLE:
-					case OBJECT_SEQUENCE:
-					case OBJECT_VIEW:
-					case OBJECT_INDEX:
-					case OBJECT_FOREIGN_TABLE:
-						RemoveRelations(stmt);
-						break;
-
-					case OBJECT_TYPE:
-					case OBJECT_DOMAIN:
-						RemoveTypes(stmt);
-						break;
-
-					case OBJECT_COLLATION:
-						DropCollationsCommand(stmt);
-						break;
-
-					case OBJECT_CONVERSION:
-						DropConversionsCommand(stmt);
-						break;
-
-					case OBJECT_SCHEMA:
-						RemoveSchemas(stmt);
-						break;
-
-					case OBJECT_TSPARSER:
-						RemoveTSParsers(stmt);
-						break;
-
-					case OBJECT_TSDICTIONARY:
-						RemoveTSDictionaries(stmt);
-						break;
-
-					case OBJECT_TSTEMPLATE:
-						RemoveTSTemplates(stmt);
-						break;
-
-					case OBJECT_TSCONFIGURATION:
-						RemoveTSConfigurations(stmt);
-						break;
-
-					case OBJECT_EXTENSION:
-						RemoveExtensions(stmt);
-						break;
-
-					default:
-						elog(ERROR, "unrecognized drop object type: %d",
-							 (int) stmt->removeType);
-						break;
-				}
-			}
+			RemoveObjects((DropStmt *) parsetree);
 			break;
 
 		case T_TruncateStmt:
diff --git a/src/include/commands/collationcmds.h b/src/include/commands/collationcmds.h
index 6dbeb75..ce4727c 100644
--- a/src/include/commands/collationcmds.h
+++ b/src/include/commands/collationcmds.h
@@ -18,7 +18,6 @@
 #include "nodes/parsenodes.h"
 
 extern void DefineCollation(List *names, List *parameters);
-extern void DropCollationsCommand(DropStmt *drop);
 extern void RenameCollation(List *name, const char *newname);
 extern void AlterCollationOwner(List *name, Oid newOwnerId);
 extern void AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId);
diff --git a/src/include/commands/conversioncmds.h b/src/include/commands/conversioncmds.h
index f77023f..c0e7cd9 100644
--- a/src/include/commands/conversioncmds.h
+++ b/src/include/commands/conversioncmds.h
@@ -18,7 +18,6 @@
 #include "nodes/parsenodes.h"
 
 extern void CreateConversionCommand(CreateConversionStmt *parsetree);
-extern void DropConversionsCommand(DropStmt *drop);
 extern void RenameConversion(List *name, const char *newname);
 extern void AlterConversionOwner(List *name, Oid newOwnerId);
 extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId);
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index bbc024f..641497b 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -16,6 +16,8 @@
 
 #include "nodes/parsenodes.h"
 
+/* commands/dropcmds.c */
+extern void RemoveObjects(DropStmt *stmt);
 
 /* commands/indexcmds.c */
 extern void DefineIndex(RangeVar *heapRelation,
@@ -116,12 +118,10 @@ extern void DefineTSParser(List *names, List *parameters);
 extern void RenameTSParser(List *oldname, const char *newname);
 extern void AlterTSParserNamespace(List *name, const char *newschema);
 extern Oid	AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid);
-extern void RemoveTSParsers(DropStmt *drop);
 extern void RemoveTSParserById(Oid prsId);
 
 extern void DefineTSDictionary(List *names, List *parameters);
 extern void RenameTSDictionary(List *oldname, const char *newname);
-extern void RemoveTSDictionaries(DropStmt *drop);
 extern void RemoveTSDictionaryById(Oid dictId);
 extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
 extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId);
@@ -132,12 +132,10 @@ extern void DefineTSTemplate(List *names, List *parameters);
 extern void RenameTSTemplate(List *oldname, const char *newname);
 extern void AlterTSTemplateNamespace(List *name, const char *newschema);
 extern Oid	AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid);
-extern void RemoveTSTemplates(DropStmt *stmt);
 extern void RemoveTSTemplateById(Oid tmplId);
 
 extern void DefineTSConfiguration(List *names, List *parameters);
 extern void RenameTSConfiguration(List *oldname, const char *newname);
-extern void RemoveTSConfigurations(DropStmt *stmt);
 extern void RemoveTSConfigurationById(Oid cfgId);
 extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
 extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId);
diff --git a/src/include/commands/extension.h b/src/include/commands/extension.h
index 2792c6d..f22ac80 100644
--- a/src/include/commands/extension.h
+++ b/src/include/commands/extension.h
@@ -29,7 +29,6 @@ extern Oid	CurrentExtensionObject;
 
 extern void CreateExtension(CreateExtensionStmt *stmt);
 
-extern void RemoveExtensions(DropStmt *stmt);
 extern void RemoveExtensionById(Oid extId);
 
 extern Oid InsertExtensionTuple(const char *extName, Oid extOwner,
diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h
index a9f8f6c..ec8d895 100644
--- a/src/include/commands/schemacmds.h
+++ b/src/include/commands/schemacmds.h
@@ -20,7 +20,6 @@
 extern void CreateSchemaCommand(CreateSchemaStmt *parsetree,
 					const char *queryString);
 
-extern void RemoveSchemas(DropStmt *drop);
 extern void RemoveSchemaById(Oid schemaOid);
 
 extern void RenameSchema(const char *oldname, const char *newname);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 3f971eb..eb93841 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -21,8 +21,6 @@
 
 extern Oid	DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId);
 
-extern void RemoveRelations(DropStmt *drop);
-
 extern void AlterTable(AlterTableStmt *stmt);
 
 extern LOCKMODE AlterTableGetLockLevel(List *cmds);
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 6d9d1cc..ac19015 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -20,7 +20,6 @@
 #define DEFAULT_TYPDELIM		','
 
 extern void DefineType(List *names, List *parameters);
-extern void RemoveTypes(DropStmt *drop);
 extern void RemoveTypeById(Oid typeOid);
 extern void DefineDomain(CreateDomainStmt *stmt);
 extern void DefineEnum(CreateEnumStmt *stmt);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ee1881b..d02208f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1916,6 +1916,7 @@ typedef struct DropStmt
 {
 	NodeTag		type;
 	List	   *objects;		/* list of sublists of names (as Values) */
+	List	   *arguments;		/* list of sublists of arguments (as Values) */
 	ObjectType	removeType;		/* object type */
 	DropBehavior behavior;		/* RESTRICT or CASCADE behavior */
 	bool		missing_ok;		/* skip error if object is missing? */
#2Kohei KaiGai
kaigai@kaigai.gr.jp
In reply to: Kohei KaiGai (#1)
1 attachment(s)
Re: [v9.2] DROP Reworks Part.1 - Consolidate routines to handle DropStmt

The attached patch is rebased one to consolidate routines to remove objects
using the revised get_object_address().

The new RemoveObjects() replaces the following routines; having
similar structure.
- RemoveRelations
- RemoveTypes
- DropCollationsCommand
- DropConversionsCommand
- RemoveSchemas
- RemoveTSParsers
- RemoveTSDictionaries
- RemoveTSTemplates
- RemoveTSConfigurations
- RemoveExtensions

I guess the most arguable part of this patch is modifications to
get_relation_by_qualified_name().

This patch breaks down the relation_openrv_extended() into
a pair of RangeVarGetRelid() and relation_open() to inject
LockRelationOid() between them, because index_drop() logic
requires the table owning the target index to be locked prior to
the index itself.

Thanks,

2011/6/14 Kohei KaiGai <kaigai@kaigai.gr.jp>:

The attached patch can be applied on the part-0 patch, and enables to
consolidate routines to handle DropStmt into one common function;
   void RemoveObjects(DropStmt *stmt);

The top-half of object deletion steps are almost consist of the following steps.
1) Look up object-Id of the supplied object name
  If not found, it raises an error or a notice with skip this deletion.
2) Apply ownership checks on the target object itself or the schema
that underlies
 the target object.
3) Add the ObjectAddress of the target object into ObjectAddresses, then invokes
  performMultipleDeletions() or performDeletion().

However, we don't need to have individual routines for each object classes,
because get_object_address takes up the portion of (1), and
check_object_ownership also takes up the portion of (2).
Here is no differences between most of objects classes on the (3).

So, the new RemoveObjects() consolidates routines to handle DropStmt for
each object classes. Instead of this common function, the following routines
were eliminated.
 RemoveRelations(DropStmt *drop);
 RemoveTypes(DropStmt *drop);
 DropCollationsCommand(DropStmt *drop);
 DropConversionsCommand(DropStmt *drop);
 RemoveSchemas(DropStmt *drop);
 RemoveTSParsers(DropStmt *drop);
 RemoveTSDictionaries(DropStmt *drop);
 RemoveTSTemplates(DropStmt *drop);
 RemoveTSConfigurations(DropStmt *drop);
 RemoveExtensions(DropStmt *drop);

Routines to handle other DROP statement (such as RemoveFuncStmt or
DropPLangStmt) are not scope of this patch to restrain the patch size.
However, it is not a tough work to fit these objects with this structure.
(It may need a discussion for databases, tablespaces and roles)

Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>

--
KaiGai Kohei <kaigai@kaigai.gr.jp>

Attachments:

pgsql-v9.2-drop-reworks-part-1.v2.patchapplication/octet-stream; name=pgsql-v9.2-drop-reworks-part-1.v2.patchDownload
 src/backend/catalog/objectaddress.c          |   38 +++-
 src/backend/commands/Makefile                |    2 +-
 src/backend/commands/collationcmds.c         |   61 -----
 src/backend/commands/conversioncmds.c        |   61 -----
 src/backend/commands/dropcmds.c              |  313 ++++++++++++++++++++++++++
 src/backend/commands/extension.c             |   63 -----
 src/backend/commands/schemacmds.c            |   63 -----
 src/backend/commands/tablecmds.c             |  248 --------------------
 src/backend/commands/tsearchcmds.c           |  254 ---------------------
 src/backend/commands/typecmds.c              |   92 --------
 src/backend/nodes/copyfuncs.c                |    1 +
 src/backend/nodes/equalfuncs.c               |    1 +
 src/backend/parser/gram.y                    |    2 +
 src/backend/tcop/utility.c                   |   57 +-----
 src/include/commands/collationcmds.h         |    1 -
 src/include/commands/conversioncmds.h        |    1 -
 src/include/commands/defrem.h                |    6 +-
 src/include/commands/extension.h             |    1 -
 src/include/commands/schemacmds.h            |    1 -
 src/include/commands/tablecmds.h             |    2 -
 src/include/commands/typecmds.h              |    1 -
 src/include/nodes/parsenodes.h               |    1 +
 src/test/regress/expected/alter_table.out    |    2 +-
 src/test/regress/expected/drop_if_exists.out |   16 +-
 src/test/regress/expected/errors.out         |    4 +-
 src/test/regress/expected/foreign_data.out   |    2 +-
 src/test/regress/expected/foreign_key.out    |    8 +-
 src/test/regress/expected/inet.out           |    2 +-
 src/test/regress/expected/prepared_xacts.out |    2 +-
 src/test/regress/expected/privileges.out     |    2 +-
 src/test/regress/expected/sequence.out       |    2 +-
 31 files changed, 375 insertions(+), 935 deletions(-)

diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 8de5bec..861eb45 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -400,17 +400,46 @@ get_relation_by_qualified_name(ObjectType objtype, List *objname,
 							   bool missing_ok)
 {
 	Relation	relation;
+	RangeVar   *range;
 	ObjectAddress	address;
 
+	if (lockmode != NoLock)
+		AcceptInvalidationMessages();
+
+	range = makeRangeVarFromNameList(objname);
+
 	address.classId = RelationRelationId;
-	address.objectId = InvalidOid;
+	address.objectId = RangeVarGetRelid(range, missing_ok);
 	address.objectSubId = 0;
 
-	relation = relation_openrv_extended(makeRangeVarFromNameList(objname),
-										lockmode, missing_ok);
-	if (!relation)
+	if (!OidIsValid(address.objectId))
 		return address;
 
+	/*
+	 * In DROP INDEX, attempt to acquire lock on the parent table before
+	 * locking the index.  index_drop() will need this anyway, and since
+	 * regular queries lock tables before their indexes, we risk deadlock
+	 * if we do it the other way around.  No error if we don't find a
+	 * pg_index entry, though --- that most likely means it isn't an
+	 * index, and we'll fail below.
+	 */
+	if (objtype == OBJECT_INDEX)
+	{
+		HeapTuple   tuple;
+
+		tuple = SearchSysCache1(INDEXRELID,
+								ObjectIdGetDatum(address.objectId));
+		if (HeapTupleIsValid(tuple))
+	    {
+			Form_pg_index index = (Form_pg_index) GETSTRUCT(tuple);
+
+			LockRelationOid(index->indrelid, AccessExclusiveLock);
+			ReleaseSysCache(tuple);
+		}
+	}
+
+	relation = relation_open(address.objectId, lockmode);
+
 	switch (objtype)
 	{
 		case OBJECT_INDEX:
@@ -454,7 +483,6 @@ get_relation_by_qualified_name(ObjectType objtype, List *objname,
 	}
 
 	/* Done */
-	address.objectId = RelationGetRelid(relation);
 	*relp = relation;
 
 	return address;
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 81fd658..4af7aad 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -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 \
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index 7f8a108..6872e58 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -145,67 +145,6 @@ DefineCollation(List *names, List *parameters)
 }
 
 /*
- * 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
  */
 void
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c
index 2c1c6da..8dafec9 100644
--- a/src/backend/commands/conversioncmds.c
+++ b/src/backend/commands/conversioncmds.c
@@ -120,67 +120,6 @@ CreateConversionCommand(CreateConversionStmt *stmt)
 }
 
 /*
- * 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
  */
 void
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
new file mode 100644
index 0000000..dc3e317
--- /dev/null
+++ b/src/backend/commands/dropcmds.c
@@ -0,0 +1,313 @@
+/*
+ * dropcmd.c
+ *    routine to support DROP statement commonly
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaddress.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_collation.h"
+#include "catalog/pg_conversion.h"
+#include "catalog/pg_extension.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_ts_config.h"
+#include "catalog/pg_ts_dict.h"
+#include "catalog/pg_ts_parser.h"
+#include "catalog/pg_ts_template.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "parser/parse_type.h"
+#include "utils/acl.h"
+#include "utils/inval.h"
+#include "utils/syscache.h"
+
+static void
+notice_object_non_existent(ObjectType type, List *objname, List *objargs)
+{
+	switch (type)
+	{
+		case OBJECT_TABLE:
+			ereport(NOTICE,
+					(errmsg("table \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_INDEX:
+			ereport(NOTICE,
+					(errmsg("index \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_SEQUENCE:
+			ereport(NOTICE,
+					(errmsg("sequence \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_VIEW:
+			ereport(NOTICE,
+					(errmsg("view \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_FOREIGN_TABLE:
+			ereport(NOTICE,
+					(errmsg("foreign table \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_TYPE:
+		case OBJECT_DOMAIN:
+			{
+				TypeName   *typename = makeTypeNameFromNameList(objname);
+				ereport(NOTICE,
+						(errmsg("type \"%s\" does not exist, skipping",
+								TypeNameToString(typename))));
+			}
+			break;
+
+		case OBJECT_COLLATION:
+			ereport(NOTICE,
+					(errmsg("collation \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_CONVERSION:
+			ereport(NOTICE,
+					(errmsg("conversion \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_SCHEMA:
+			ereport(NOTICE,
+					(errmsg("schema \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_TSPARSER:
+			ereport(NOTICE,
+					(errmsg("text search parser \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_TSDICTIONARY:
+			ereport(NOTICE,
+					(errmsg("text search dictionary \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_TSTEMPLATE:
+			ereport(NOTICE,
+					(errmsg("text search template \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_TSCONFIGURATION:
+			ereport(NOTICE,
+					(errmsg("text search configuration \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_EXTENSION:
+			ereport(NOTICE,
+					(errmsg("extension \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		default:
+			elog(ERROR, "unrecognized drop object type: %d", (int) type);
+			break;
+	}
+}
+
+static Oid
+get_object_namespace(ObjectType type, const ObjectAddress *address)
+{
+	HeapTuple	tuple;
+	Oid			objectId = address->objectId;
+	Oid			namespaceId = InvalidOid;
+
+	switch (type)
+	{
+		case OBJECT_TABLE:
+		case OBJECT_INDEX:
+		case OBJECT_SEQUENCE:
+		case OBJECT_VIEW:
+		case OBJECT_FOREIGN_TABLE:
+			tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for relation %u", objectId);
+			namespaceId = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case OBJECT_TYPE:
+		case OBJECT_DOMAIN:
+			tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for type %u", objectId);
+			namespaceId = ((Form_pg_type) GETSTRUCT(tuple))->typnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case OBJECT_COLLATION:
+			tuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for collation %u", objectId);
+			namespaceId = ((Form_pg_collation) GETSTRUCT(tuple))->collnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case OBJECT_CONVERSION:
+			tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for conversion %u", objectId);
+			namespaceId = ((Form_pg_conversion) GETSTRUCT(tuple))->connamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case OBJECT_TSPARSER:
+			tuple = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for text search parser %u",
+					 objectId);
+			namespaceId = ((Form_pg_ts_parser) GETSTRUCT(tuple))->prsnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case OBJECT_TSDICTIONARY:
+			tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for text search dictionary %u",
+					 objectId);
+			namespaceId = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case OBJECT_TSTEMPLATE:
+			tuple = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for text search template %u",
+					 objectId);
+			namespaceId = ((Form_pg_ts_template) GETSTRUCT(tuple))->tmplnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case OBJECT_TSCONFIGURATION:
+			tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for text search dictionary %u",
+					 objectId);
+			namespaceId = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		default:
+			/*
+			 * The supplied object type does not have its namespace.
+			 */
+			namespaceId = InvalidOid;
+			break;
+	}
+	return namespaceId;
+}
+
+void
+RemoveObjects(DropStmt *stmt)
+{
+	ObjectAddresses *objects;
+	ListCell   *cell1;
+	ListCell   *cell2;
+
+	Assert(!stmt->arguments ||
+		   list_length(stmt->objects) == list_length(stmt->arguments));
+
+	objects = new_object_addresses();
+
+	for (cell1 = list_head(stmt->objects), cell2 = list_head(stmt->arguments);
+		 cell1 != NULL;
+		 cell1 = lnext(cell1), cell2 = (!cell2 ? NULL : lnext(cell2)))
+	{
+		ObjectAddress	address;
+		List	   *objname = lfirst(cell1);
+		List	   *objargs = (cell2 ? lfirst(cell2) : NIL);
+		Relation	relation = NULL;
+		Oid			namespaceId;
+
+		/*
+		 * Resolve object name and arguments into ObjectAddress
+		 */
+		address = get_object_address(stmt->removeType,
+									 objname, objargs,
+									 &relation,
+									 AccessExclusiveLock,
+									 stmt->missing_ok);
+
+		/*
+		 * Raise an notice, if supplied object was not found
+		 */
+		if (!OidIsValid(address.objectId))
+		{
+			notice_object_non_existent(stmt->removeType,
+									   objname, objargs);
+			continue;
+		}
+
+		/*
+		 * Permission checks
+		 */
+		namespaceId = get_object_namespace(stmt->removeType, &address);
+		if (!OidIsValid(namespaceId) ||
+			!pg_namespace_ownercheck(namespaceId, GetUserId()))
+			check_object_ownership(GetUserId(), stmt->removeType, address,
+								   objname, objargs, relation);
+
+		/*
+		 * Rest of sanity checks on objects deletion
+		 */
+		switch (stmt->removeType)
+		{
+			case OBJECT_TABLE:
+            case OBJECT_SEQUENCE:
+            case OBJECT_VIEW:
+            case OBJECT_INDEX:
+            case OBJECT_FOREIGN_TABLE:
+				if (!!allowSystemTableMods && IsSystemRelation(relation))
+					ereport(ERROR,
+							(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+						errmsg("permission denied: \"%s\" is a system catalog",
+							   RelationGetRelationName(relation))));
+				break;
+
+			default:
+				/* nothing to do */
+				break;
+		}
+
+		/*
+		 * If get_object_address() opened the relation for us, we close it
+		 * to keep the reference count correct - but we retain any locks
+		 * acquired by get_object_address() until commit time, to guard
+		 * against concurrent activities.
+		 */
+		if (relation)
+		{
+			heap_close(relation, NoLock);
+			RelationForgetRelation(address.objectId);
+		}
+
+		add_exact_object_address(&address, objects);
+	}
+	performMultipleDeletions(objects, stmt->behavior);
+
+	free_object_addresses(objects);
+}
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 08fb3d5..9ab8b1d 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1554,69 +1554,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.
  *
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index 82bbf8f..3840ec0 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -147,69 +147,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.
  */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index b2ba11c..648e781 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -182,59 +182,6 @@ 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.")},
-	{RELKIND_FOREIGN_TABLE,
-		ERRCODE_UNDEFINED_OBJECT,
-		gettext_noop("foreign table \"%s\" does not exist"),
-		gettext_noop("foreign table \"%s\" does not exist, skipping"),
-		gettext_noop("\"%s\" is not a foreign table"),
-	gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
-	{'\0', 0, NULL, NULL, NULL, NULL}
-};
-
 /* Alter table target-type flags for ATSimplePermissions */
 #define		ATT_TABLE				0x0001
 #define		ATT_VIEW				0x0002
@@ -634,201 +581,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 }
 
 /*
- * 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("%s", _(wentry->drophint_msg)) : 0));
-}
-
-/*
- * RemoveRelations
- *		Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
- *		DROP FOREIGN TABLE
- */
-void
-RemoveRelations(DropStmt *drop)
-{
-	ObjectAddresses *objects;
-	char		relkind;
-	ListCell   *cell;
-
-	/*
-	 * 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.
-	 */
-
-	/* Determine required relkind */
-	switch (drop->removeType)
-	{
-		case OBJECT_TABLE:
-			relkind = RELKIND_RELATION;
-			break;
-
-		case OBJECT_INDEX:
-			relkind = RELKIND_INDEX;
-			break;
-
-		case OBJECT_SEQUENCE:
-			relkind = RELKIND_SEQUENCE;
-			break;
-
-		case OBJECT_VIEW:
-			relkind = RELKIND_VIEW;
-			break;
-
-		case OBJECT_FOREIGN_TABLE:
-			relkind = RELKIND_FOREIGN_TABLE;
-			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;
-		}
-
-		/*
-		 * In DROP INDEX, attempt to acquire lock on the parent table before
-		 * locking the index.  index_drop() will need this anyway, and since
-		 * regular queries lock tables before their indexes, we risk deadlock
-		 * if we do it the other way around.  No error if we don't find a
-		 * pg_index entry, though --- that most likely means it isn't an
-		 * index, and we'll fail below.
-		 */
-		if (relkind == RELKIND_INDEX)
-		{
-			tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
-			if (HeapTupleIsValid(tuple))
-			{
-				Form_pg_index index = (Form_pg_index) GETSTRUCT(tuple);
-
-				LockRelationOid(index->indrelid, AccessExclusiveLock);
-				ReleaseSysCache(tuple);
-			}
-		}
-
-		/* Get the lock before trying to fetch the syscache entry */
-		LockRelationOid(relOid, AccessExclusiveLock);
-
-		tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
-		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);
-}
-
-/*
  * ExecuteTruncate
  *		Executes a TRUNCATE command.
  *
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index 3355eaa..197dce7 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -283,65 +283,6 @@ DefineTSParser(List *names, List *parameters)
 }
 
 /*
- * 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.
  */
 void
@@ -735,76 +676,6 @@ AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid)
 }
 
 /*
- * 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.
  */
 void
@@ -1267,65 +1138,6 @@ AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid)
 }
 
 /*
- * 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.
  */
 void
@@ -1720,72 +1532,6 @@ AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid)
 }
 
 /*
- * 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.
  */
 void
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 66c11de..fcb8395 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -616,98 +616,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.
  */
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c9133dd..0bd8882 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2734,6 +2734,7 @@ _copyDropStmt(DropStmt *from)
 	DropStmt   *newnode = makeNode(DropStmt);
 
 	COPY_NODE_FIELD(objects);
+	COPY_NODE_FIELD(arguments);
 	COPY_SCALAR_FIELD(removeType);
 	COPY_SCALAR_FIELD(behavior);
 	COPY_SCALAR_FIELD(missing_ok);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 3a0267c..ea972be 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1182,6 +1182,7 @@ static bool
 _equalDropStmt(DropStmt *a, DropStmt *b)
 {
 	COMPARE_NODE_FIELD(objects);
+	COMPARE_NODE_FIELD(arguments);
 	COMPARE_SCALAR_FIELD(removeType);
 	COMPARE_SCALAR_FIELD(behavior);
 	COMPARE_SCALAR_FIELD(missing_ok);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 62cff8a..9e27bf2 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4721,6 +4721,7 @@ DropStmt:	DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior
 					n->removeType = $2;
 					n->missing_ok = TRUE;
 					n->objects = $5;
+					n->arguments = NIL;
 					n->behavior = $6;
 					$$ = (Node *)n;
 				}
@@ -4730,6 +4731,7 @@ DropStmt:	DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior
 					n->removeType = $2;
 					n->missing_ok = FALSE;
 					n->objects = $3;
+					n->arguments = NIL;
 					n->behavior = $4;
 					$$ = (Node *)n;
 				}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 0559998..df13b8c 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -636,62 +636,7 @@ standard_ProcessUtility(Node *parsetree,
 			break;
 
 		case T_DropStmt:
-			{
-				DropStmt   *stmt = (DropStmt *) parsetree;
-
-				switch (stmt->removeType)
-				{
-					case OBJECT_TABLE:
-					case OBJECT_SEQUENCE:
-					case OBJECT_VIEW:
-					case OBJECT_INDEX:
-					case OBJECT_FOREIGN_TABLE:
-						RemoveRelations(stmt);
-						break;
-
-					case OBJECT_TYPE:
-					case OBJECT_DOMAIN:
-						RemoveTypes(stmt);
-						break;
-
-					case OBJECT_COLLATION:
-						DropCollationsCommand(stmt);
-						break;
-
-					case OBJECT_CONVERSION:
-						DropConversionsCommand(stmt);
-						break;
-
-					case OBJECT_SCHEMA:
-						RemoveSchemas(stmt);
-						break;
-
-					case OBJECT_TSPARSER:
-						RemoveTSParsers(stmt);
-						break;
-
-					case OBJECT_TSDICTIONARY:
-						RemoveTSDictionaries(stmt);
-						break;
-
-					case OBJECT_TSTEMPLATE:
-						RemoveTSTemplates(stmt);
-						break;
-
-					case OBJECT_TSCONFIGURATION:
-						RemoveTSConfigurations(stmt);
-						break;
-
-					case OBJECT_EXTENSION:
-						RemoveExtensions(stmt);
-						break;
-
-					default:
-						elog(ERROR, "unrecognized drop object type: %d",
-							 (int) stmt->removeType);
-						break;
-				}
-			}
+			RemoveObjects((DropStmt *) parsetree);
 			break;
 
 		case T_TruncateStmt:
diff --git a/src/include/commands/collationcmds.h b/src/include/commands/collationcmds.h
index 6dbeb75..ce4727c 100644
--- a/src/include/commands/collationcmds.h
+++ b/src/include/commands/collationcmds.h
@@ -18,7 +18,6 @@
 #include "nodes/parsenodes.h"
 
 extern void DefineCollation(List *names, List *parameters);
-extern void DropCollationsCommand(DropStmt *drop);
 extern void RenameCollation(List *name, const char *newname);
 extern void AlterCollationOwner(List *name, Oid newOwnerId);
 extern void AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId);
diff --git a/src/include/commands/conversioncmds.h b/src/include/commands/conversioncmds.h
index f77023f..c0e7cd9 100644
--- a/src/include/commands/conversioncmds.h
+++ b/src/include/commands/conversioncmds.h
@@ -18,7 +18,6 @@
 #include "nodes/parsenodes.h"
 
 extern void CreateConversionCommand(CreateConversionStmt *parsetree);
-extern void DropConversionsCommand(DropStmt *drop);
 extern void RenameConversion(List *name, const char *newname);
 extern void AlterConversionOwner(List *name, Oid newOwnerId);
 extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId);
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index bbc024f..641497b 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -16,6 +16,8 @@
 
 #include "nodes/parsenodes.h"
 
+/* commands/dropcmds.c */
+extern void RemoveObjects(DropStmt *stmt);
 
 /* commands/indexcmds.c */
 extern void DefineIndex(RangeVar *heapRelation,
@@ -116,12 +118,10 @@ extern void DefineTSParser(List *names, List *parameters);
 extern void RenameTSParser(List *oldname, const char *newname);
 extern void AlterTSParserNamespace(List *name, const char *newschema);
 extern Oid	AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid);
-extern void RemoveTSParsers(DropStmt *drop);
 extern void RemoveTSParserById(Oid prsId);
 
 extern void DefineTSDictionary(List *names, List *parameters);
 extern void RenameTSDictionary(List *oldname, const char *newname);
-extern void RemoveTSDictionaries(DropStmt *drop);
 extern void RemoveTSDictionaryById(Oid dictId);
 extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
 extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId);
@@ -132,12 +132,10 @@ extern void DefineTSTemplate(List *names, List *parameters);
 extern void RenameTSTemplate(List *oldname, const char *newname);
 extern void AlterTSTemplateNamespace(List *name, const char *newschema);
 extern Oid	AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid);
-extern void RemoveTSTemplates(DropStmt *stmt);
 extern void RemoveTSTemplateById(Oid tmplId);
 
 extern void DefineTSConfiguration(List *names, List *parameters);
 extern void RenameTSConfiguration(List *oldname, const char *newname);
-extern void RemoveTSConfigurations(DropStmt *stmt);
 extern void RemoveTSConfigurationById(Oid cfgId);
 extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
 extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId);
diff --git a/src/include/commands/extension.h b/src/include/commands/extension.h
index 2792c6d..f22ac80 100644
--- a/src/include/commands/extension.h
+++ b/src/include/commands/extension.h
@@ -29,7 +29,6 @@ extern Oid	CurrentExtensionObject;
 
 extern void CreateExtension(CreateExtensionStmt *stmt);
 
-extern void RemoveExtensions(DropStmt *stmt);
 extern void RemoveExtensionById(Oid extId);
 
 extern Oid InsertExtensionTuple(const char *extName, Oid extOwner,
diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h
index a9f8f6c..ec8d895 100644
--- a/src/include/commands/schemacmds.h
+++ b/src/include/commands/schemacmds.h
@@ -20,7 +20,6 @@
 extern void CreateSchemaCommand(CreateSchemaStmt *parsetree,
 					const char *queryString);
 
-extern void RemoveSchemas(DropStmt *drop);
 extern void RemoveSchemaById(Oid schemaOid);
 
 extern void RenameSchema(const char *oldname, const char *newname);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 3f971eb..eb93841 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -21,8 +21,6 @@
 
 extern Oid	DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId);
 
-extern void RemoveRelations(DropStmt *drop);
-
 extern void AlterTable(AlterTableStmt *stmt);
 
 extern LOCKMODE AlterTableGetLockLevel(List *cmds);
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 6d9d1cc..ac19015 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -20,7 +20,6 @@
 #define DEFAULT_TYPDELIM		','
 
 extern void DefineType(List *names, List *parameters);
-extern void RemoveTypes(DropStmt *drop);
 extern void RemoveTypeById(Oid typeOid);
 extern void DefineDomain(CreateDomainStmt *stmt);
 extern void DefineEnum(CreateEnumStmt *stmt);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index c65e3cd..9b4bfa1 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1916,6 +1916,7 @@ typedef struct DropStmt
 {
 	NodeTag		type;
 	List	   *objects;		/* list of sublists of names (as Values) */
+	List	   *arguments;		/* list of sublists of arguments (as Values) */
 	ObjectType	removeType;		/* object type */
 	DropBehavior behavior;		/* RESTRICT or CASCADE behavior */
 	bool		missing_ok;		/* skip error if object is missing? */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 9ab84f9..74a16a5 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -1554,7 +1554,7 @@ create type lockmodes as enum (
 ,'AccessExclusiveLock'
 );
 drop view my_locks;
-ERROR:  view "my_locks" does not exist
+ERROR:  relation "my_locks" does not exist
 create or replace view my_locks as
 select case when c.relname like 'pg_toast%' then 'pg_toast' else c.relname end, max(mode::lockmodes) as max_lockmode
 from pg_locks l join pg_class c on l.relation = c.oid
diff --git a/src/test/regress/expected/drop_if_exists.out b/src/test/regress/expected/drop_if_exists.out
index 2a23b4c..403bc06 100644
--- a/src/test/regress/expected/drop_if_exists.out
+++ b/src/test/regress/expected/drop_if_exists.out
@@ -3,37 +3,37 @@
 --
 -- table (will be really dropped at the end)
 DROP TABLE test_exists;
-ERROR:  table "test_exists" does not exist
+ERROR:  relation "test_exists" does not exist
 DROP TABLE IF EXISTS test_exists;
 NOTICE:  table "test_exists" does not exist, skipping
 CREATE TABLE test_exists (a int, b text);
 -- view
 DROP VIEW test_view_exists;
-ERROR:  view "test_view_exists" does not exist
+ERROR:  relation "test_view_exists" does not exist
 DROP VIEW IF EXISTS test_view_exists;
 NOTICE:  view "test_view_exists" does not exist, skipping
 CREATE VIEW test_view_exists AS select * from test_exists;
 DROP VIEW IF EXISTS test_view_exists;
 DROP VIEW test_view_exists;
-ERROR:  view "test_view_exists" does not exist
+ERROR:  relation "test_view_exists" does not exist
 -- index
 DROP INDEX test_index_exists;
-ERROR:  index "test_index_exists" does not exist
+ERROR:  relation "test_index_exists" does not exist
 DROP INDEX IF EXISTS test_index_exists;
 NOTICE:  index "test_index_exists" does not exist, skipping
 CREATE INDEX test_index_exists on test_exists(a);
 DROP INDEX IF EXISTS test_index_exists;
 DROP INDEX test_index_exists;
-ERROR:  index "test_index_exists" does not exist
+ERROR:  relation "test_index_exists" does not exist
 -- sequence
 DROP SEQUENCE test_sequence_exists;
-ERROR:  sequence "test_sequence_exists" does not exist
+ERROR:  relation "test_sequence_exists" does not exist
 DROP SEQUENCE IF EXISTS test_sequence_exists;
 NOTICE:  sequence "test_sequence_exists" does not exist, skipping
 CREATE SEQUENCE test_sequence_exists;
 DROP SEQUENCE IF EXISTS test_sequence_exists;
 DROP SEQUENCE test_sequence_exists;
-ERROR:  sequence "test_sequence_exists" does not exist
+ERROR:  relation "test_sequence_exists" does not exist
 -- schema
 DROP SCHEMA test_schema_exists;
 ERROR:  schema "test_schema_exists" does not exist
@@ -64,7 +64,7 @@ ERROR:  type "test_domain_exists" does not exist
 -- drop the table
 DROP TABLE IF EXISTS test_exists;
 DROP TABLE test_exists;
-ERROR:  table "test_exists" does not exist
+ERROR:  relation "test_exists" does not exist
 ---
 --- role/user/group
 ---
diff --git a/src/test/regress/expected/errors.out b/src/test/regress/expected/errors.out
index 4a10c6a..073dfec 100644
--- a/src/test/regress/expected/errors.out
+++ b/src/test/regress/expected/errors.out
@@ -76,7 +76,7 @@ LINE 1: drop table;
                   ^
 -- no such relation
 drop table nonesuch;
-ERROR:  table "nonesuch" does not exist
+ERROR:  relation "nonesuch" does not exist
 --
 -- ALTER TABLE
 -- relation renaming
@@ -146,7 +146,7 @@ LINE 1: drop index 314159;
                    ^
 -- no such index
 drop index nonesuch;
-ERROR:  index "nonesuch" does not exist
+ERROR:  relation "nonesuch" does not exist
 --
 -- DROP AGGREGATE
 -- missing aggregate name
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index def850b..9a8a6dc 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -1066,7 +1066,7 @@ ERROR:  must be owner of foreign server s9
 RESET ROLE;
 -- DROP FOREIGN TABLE
 DROP FOREIGN TABLE no_table;                                    -- ERROR
-ERROR:  foreign table "no_table" does not exist
+ERROR:  relation "no_table" does not exist
 DROP FOREIGN TABLE IF EXISTS no_table;
 NOTICE:  foreign table "no_table" does not exist, skipping
 DROP FOREIGN TABLE foreign_schema.foreign_table_1;
diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out
index 87d573b..e7f7853 100644
--- a/src/test/regress/expected/foreign_key.out
+++ b/src/test/regress/expected/foreign_key.out
@@ -730,9 +730,9 @@ ERROR:  column "ftest2" referenced in foreign key constraint does not exist
 CREATE TABLE FKTABLE_FAIL2 ( ftest1 int, CONSTRAINT fkfail1 FOREIGN KEY (ftest1) REFERENCES PKTABLE(ptest2));
 ERROR:  column "ptest2" referenced in foreign key constraint does not exist
 DROP TABLE FKTABLE_FAIL1;
-ERROR:  table "fktable_fail1" does not exist
+ERROR:  relation "fktable_fail1" does not exist
 DROP TABLE FKTABLE_FAIL2;
-ERROR:  table "fktable_fail2" does not exist
+ERROR:  relation "fktable_fail2" does not exist
 DROP TABLE PKTABLE;
 -- Test for referencing column number smaller than referenced constraint
 CREATE TABLE PKTABLE (ptest1 int, ptest2 int, UNIQUE(ptest1, ptest2));
@@ -740,7 +740,7 @@ NOTICE:  CREATE TABLE / UNIQUE will create implicit index "pktable_ptest1_ptest2
 CREATE TABLE FKTABLE_FAIL1 (ftest1 int REFERENCES pktable(ptest1));
 ERROR:  there is no unique constraint matching given keys for referenced table "pktable"
 DROP TABLE FKTABLE_FAIL1;
-ERROR:  table "fktable_fail1" does not exist
+ERROR:  relation "fktable_fail1" does not exist
 DROP TABLE PKTABLE;
 --
 -- Tests for mismatched types
@@ -989,7 +989,7 @@ NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" fo
 ERROR:  foreign key constraint "pktable_ptest2_fkey" cannot be implemented
 DETAIL:  Key columns "ptest2" and "base1" are of incompatible types: inet and integer.
 drop table pktable;
-ERROR:  table "pktable" does not exist
+ERROR:  relation "pktable" does not exist
 drop table pktable_base;
 --
 -- Deferrable constraints
diff --git a/src/test/regress/expected/inet.out b/src/test/regress/expected/inet.out
index 356a397..1a2ab6d 100644
--- a/src/test/regress/expected/inet.out
+++ b/src/test/regress/expected/inet.out
@@ -3,7 +3,7 @@
 --
 -- prepare the table...
 DROP TABLE INET_TBL;
-ERROR:  table "inet_tbl" does not exist
+ERROR:  relation "inet_tbl" does not exist
 CREATE TABLE INET_TBL (c cidr, i inet);
 INSERT INTO INET_TBL (c, i) VALUES ('192.168.1', '192.168.1.226/24');
 INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.0/26', '192.168.1.226');
diff --git a/src/test/regress/expected/prepared_xacts.out b/src/test/regress/expected/prepared_xacts.out
index 328cd74..b25768e 100644
--- a/src/test/regress/expected/prepared_xacts.out
+++ b/src/test/regress/expected/prepared_xacts.out
@@ -250,5 +250,5 @@ SELECT gid FROM pg_prepared_xacts;
 -- Clean up
 DROP TABLE pxtest2;
 DROP TABLE pxtest3;  -- will still be there if prepared xacts are disabled
-ERROR:  table "pxtest3" does not exist
+ERROR:  relation "pxtest3" does not exist
 DROP TABLE pxtest4;
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 5cda230..9100f1d 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -1228,7 +1228,7 @@ DROP VIEW atestv3 CASCADE;
 NOTICE:  drop cascades to view atestv4
 -- this should complain "does not exist"
 DROP VIEW atestv4;
-ERROR:  view "atestv4" does not exist
+ERROR:  relation "atestv4" does not exist
 DROP TABLE atest1;
 DROP TABLE atest2;
 DROP TABLE atest3;
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 968fcce..b9a5833 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -234,7 +234,7 @@ DROP SEQUENCE myseq3;
 DROP TABLE t1;
 -- Fails because no longer existent:
 DROP SEQUENCE t1_f1_seq;
-ERROR:  sequence "t1_f1_seq" does not exist
+ERROR:  relation "t1_f1_seq" does not exist
 -- Now OK:
 DROP SEQUENCE myseq2;
 --
#3Robert Haas
robertmhaas@gmail.com
In reply to: Kohei KaiGai (#2)
Re: [v9.2] DROP Reworks Part.1 - Consolidate routines to handle DropStmt

On Tue, Jun 28, 2011 at 4:17 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

The attached patch is rebased one to consolidate routines to remove objects
using the revised get_object_address().

The new RemoveObjects() replaces the following routines; having
similar structure.
 - RemoveRelations
 - RemoveTypes
 - DropCollationsCommand
 - DropConversionsCommand
 - RemoveSchemas
 - RemoveTSParsers
 - RemoveTSDictionaries
 - RemoveTSTemplates
 - RemoveTSConfigurations
 - RemoveExtensions

I guess the most arguable part of this patch is modifications to
get_relation_by_qualified_name().

This patch breaks down the relation_openrv_extended() into
a pair of RangeVarGetRelid() and relation_open() to inject
LockRelationOid() between them, because index_drop() logic
requires the table owning the target index to be locked prior to
the index itself.

This patch removes an impressive amount of boilerplate code and
replaces it with something much more compact. I like that. In the
interest of full disclosure, I suggested this approach to KaiGai at
PGCon, so I'm biased: but even so, I'm pleasantly surprised by the
amount of consolidation that appears possible here.

I think that get_object_namespace() needs to be rethought. If you
take a look at AlterObjectNamespace() and its callers, you'll notice
that we already have, encoded in those call sites, the knowledge of
which object types can be looked up in which system caches, and which
columns within the resulting heap tuples will contain the schema OIDs.
Here, we're essentially duplicating that information in another
place, which doesn't seem good. I think perhaps we should create a
big static array, each row of which would contain:

- ObjectType
- system cache ID for OID lookups
- system catalog table OID for scans
- attribute number for the name attribute, where applicable (see
AlterObjectNamespace)
- attribute number for the namespace attribute
- attribute number for the owner attribute
- ...and maybe some other properties

We could then create a function (in objectaddress.c, probably) that
you call with an ObjectType argument, and it returns a pointer to the
appropriate struct entry, or calls elog() if none is found. This
function could be used by the existing object_exists() functions in
lieu of having its own giant switch statement, by
AlterObjectNamespace(), and by this new consolidated DROP code. If
you agree with this approach, we should probably make it a separate
patch.

Other minor things I noticed:

- get_object_namespace() itself does not need to take both an
ObjectType and an ObjectAddress - just an ObjectAddress should be
sufficient.

- dropcmds.c has a header comment saying dropcmd.c

- RemoveObjects() could use forboth() instead of inventing its own way
to loop over two lists in parallel

- Why does RemoveObjects() need to call RelationForgetRelation()?

- It might be cleaner, rather than hacking up
get_relation_by_qualified_name(), to just have special-purpose code
for dropping relations. it looks like you will need at least two
special cases for relations as opposed to other types of objects that
someone might want to drop, so it may make sense to keep them
separate. Remember, in any case, that we don't want to redefine
get_relation_by_qualified_name() for other callers, who don't have the
same needs as RemoveObjects(). This would also avoid things like
this:

-ERROR:  view "atestv4" does not exist
+ERROR:  relation "atestv4" does not exist

...which are probably not desirable.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#4Alvaro Herrera
alvherre@commandprompt.com
In reply to: Robert Haas (#3)
Re: [v9.2] DROP Reworks Part.1 - Consolidate routines to handle DropStmt

Excerpts from Robert Haas's message of mié jul 06 12:40:39 -0400 2011:

This patch removes an impressive amount of boilerplate code and
replaces it with something much more compact. I like that. In the
interest of full disclosure, I suggested this approach to KaiGai at
PGCon, so I'm biased: but even so, I'm pleasantly surprised by the
amount of consolidation that appears possible here.

Yeah. Myself, I love the fact that the dropmsgstrings thing is gone. I
wonder if the routine to obtain "foo doesn't exist, skipping" messages
could be replaced by judicious use of getObjectDescription.

Here, we're essentially duplicating that information in another
place, which doesn't seem good. I think perhaps we should create a
big static array, each row of which would contain:

- ObjectType
- system cache ID for OID lookups
- system catalog table OID for scans
- attribute number for the name attribute, where applicable (see
AlterObjectNamespace)
- attribute number for the namespace attribute
- attribute number for the owner attribute
- ...and maybe some other properties

+1 for this general approach.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#5Robert Haas
robertmhaas@gmail.com
In reply to: Alvaro Herrera (#4)
Re: [v9.2] DROP Reworks Part.1 - Consolidate routines to handle DropStmt

On Wed, Jul 6, 2011 at 1:09 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Excerpts from Robert Haas's message of mié jul 06 12:40:39 -0400 2011:

This patch removes an impressive amount of boilerplate code and
replaces it with something much more compact.   I like that.  In the
interest of full disclosure, I suggested this approach to KaiGai at
PGCon, so I'm biased: but even so, I'm pleasantly surprised by the
amount of consolidation that appears possible here.

Yeah.  Myself, I love the fact that the dropmsgstrings thing is gone.  I
wonder if the routine to obtain "foo doesn't exist, skipping" messages
could be replaced by judicious use of getObjectDescription.

I've been told we don't want to go further in that direction for
reasons of translatability.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#6Alvaro Herrera
alvherre@commandprompt.com
In reply to: Robert Haas (#5)
Re: [v9.2] DROP Reworks Part.1 - Consolidate routines to handle DropStmt

Excerpts from Robert Haas's message of mié jul 06 14:02:13 -0400 2011:

On Wed, Jul 6, 2011 at 1:09 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Excerpts from Robert Haas's message of mié jul 06 12:40:39 -0400 2011:

This patch removes an impressive amount of boilerplate code and
replaces it with something much more compact.   I like that.  In the
interest of full disclosure, I suggested this approach to KaiGai at
PGCon, so I'm biased: but even so, I'm pleasantly surprised by the
amount of consolidation that appears possible here.

Yeah.  Myself, I love the fact that the dropmsgstrings thing is gone.  I
wonder if the routine to obtain "foo doesn't exist, skipping" messages
could be replaced by judicious use of getObjectDescription.

I've been told we don't want to go further in that direction for
reasons of translatability.

Well, you can split sentences using a colon instead of building them;
something like
errmsg("object cannot be found, skipping: %s", getObjectDescription(object))
which renders as
object cannot be found, skipping: table foobar

Now people can complain that these messages are "worse" than the
originals which were more specific in nature, but I don't personally see
a problem with that. I dunno what's the general opinion though.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#7Robert Haas
robertmhaas@gmail.com
In reply to: Alvaro Herrera (#6)
Re: [v9.2] DROP Reworks Part.1 - Consolidate routines to handle DropStmt

On Wed, Jul 6, 2011 at 5:06 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Excerpts from Robert Haas's message of mié jul 06 14:02:13 -0400 2011:

On Wed, Jul 6, 2011 at 1:09 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Excerpts from Robert Haas's message of mié jul 06 12:40:39 -0400 2011:

This patch removes an impressive amount of boilerplate code and
replaces it with something much more compact.   I like that.  In the
interest of full disclosure, I suggested this approach to KaiGai at
PGCon, so I'm biased: but even so, I'm pleasantly surprised by the
amount of consolidation that appears possible here.

Yeah.  Myself, I love the fact that the dropmsgstrings thing is gone.  I
wonder if the routine to obtain "foo doesn't exist, skipping" messages
could be replaced by judicious use of getObjectDescription.

I've been told we don't want to go further in that direction for
reasons of translatability.

Well, you can split sentences using a colon instead of building them;
something like
       errmsg("object cannot be found, skipping: %s", getObjectDescription(object))
which renders as
       object cannot be found, skipping: table foobar

Now people can complain that these messages are "worse" than the
originals which were more specific in nature, but I don't personally see
a problem with that.  I dunno what's the general opinion though.

I'm going to try to stay out of it, since I only use PostgreSQL in English...

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#8Kohei KaiGai
kaigai@kaigai.gr.jp
In reply to: Robert Haas (#3)
1 attachment(s)
Re: [v9.2] DROP Reworks Part.1 - Consolidate routines to handle DropStmt

Robert, Thanks for your reviewing.

2011/7/6 Robert Haas <robertmhaas@gmail.com>:

On Tue, Jun 28, 2011 at 4:17 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
This patch removes an impressive amount of boilerplate code and
replaces it with something much more compact.   I like that.  In the
interest of full disclosure, I suggested this approach to KaiGai at
PGCon, so I'm biased: but even so, I'm pleasantly surprised by the
amount of consolidation that appears possible here.

I think that get_object_namespace() needs to be rethought.  If you
take a look at AlterObjectNamespace() and its callers, you'll notice
that we already have, encoded in those call sites, the knowledge of
which object types can be looked up in which system caches, and which
columns within the resulting heap tuples will contain the schema OIDs.
 Here, we're essentially duplicating that information in another
place, which doesn't seem good.  I think perhaps we should create a
big static array, each row of which would contain:

- ObjectType
- system cache ID for OID lookups
- system catalog table OID for scans
- attribute number for the name attribute, where applicable (see
AlterObjectNamespace)
- attribute number for the namespace attribute
- attribute number for the owner attribute
- ...and maybe some other properties

We could then create a function (in objectaddress.c, probably) that
you call with an ObjectType argument, and it returns a pointer to the
appropriate struct entry, or calls elog() if none is found.  This
function could be used by the existing object_exists() functions in
lieu of having its own giant switch statement, by
AlterObjectNamespace(), and by this new consolidated DROP code.  If
you agree with this approach, we should probably make it a separate
patch.

I definitely agree with this idea. It will enables to eliminate ugly switch
statements for error-messaging reasons.
This reworked DROP code also contains these switches, such as
notice_object_non_existent() or get_relation_address(). It will be
cleaned up with this table.

Other minor things I noticed:

- get_object_namespace() itself does not need to take both an
ObjectType and an ObjectAddress - just an ObjectAddress should be
sufficient.

OK, it was fixed.

- dropcmds.c has a header comment saying dropcmd.c

Oops, thank you for this pointing out.

- RemoveObjects() could use forboth() instead of inventing its own way
to loop over two lists in parallel

forboth() is not available in this case, because DropStmt->arguments
shall be NIL for currently supported object types.
So, I revised the code as follows:

ListCell *cell1;
ListCell *cell2 = NULL;

foreach(cell1, stmt->objects)
{
List *objname = lfirst(cell1);
List *objargs = NIL;

if (stmt->arguments)
{
cell2 = (!cell2 ? list_head(stmt->arguments) : lnext(cell2));
objargs = lfirst(cell2);
}

- Why does RemoveObjects() need to call RelationForgetRelation()?

I thought here is a risk to reference a phantom entry of dropped relation
because of relation_open() in get_object_address(), however, doDeletion()
eventually called RelationForgetRelation().
So, it is not necessary here.

- It might be cleaner, rather than hacking up
get_relation_by_qualified_name(), to just have special-purpose code
for dropping relations.  it looks like you will need at least two
special cases for relations as opposed to other types of objects that
someone might want to drop, so it may make sense to keep them
separate.  Remember, in any case, that we don't want to redefine
get_relation_by_qualified_name() for other callers, who don't have the
same needs as RemoveObjects().  This would also avoid things like
this:

-ERROR:  view "atestv4" does not exist
+ERROR:  relation "atestv4" does not exist

...which are probably not desirable.

I put a new get_relation_address() in dropcmds.c as a special case in
deletion of relation, because of
- Locks to the table owning the index to be removed
- Error message issues
- Sanity checks when we try to drop system catalogs.

Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>

Attachments:

pgsql-v9.2-drop-reworks-part-1.v3.patchtext/x-patch; charset=US-ASCII; name=pgsql-v9.2-drop-reworks-part-1.v3.patchDownload
 src/backend/commands/Makefile         |    2 +-
 src/backend/commands/collationcmds.c  |   61 -----
 src/backend/commands/conversioncmds.c |   61 -----
 src/backend/commands/dropcmds.c       |  456 +++++++++++++++++++++++++++++++++
 src/backend/commands/extension.c      |   63 -----
 src/backend/commands/schemacmds.c     |   63 -----
 src/backend/commands/tablecmds.c      |  248 ------------------
 src/backend/commands/tsearchcmds.c    |  254 ------------------
 src/backend/commands/typecmds.c       |   92 -------
 src/backend/nodes/copyfuncs.c         |    1 +
 src/backend/nodes/equalfuncs.c        |    1 +
 src/backend/parser/gram.y             |    2 +
 src/backend/tcop/utility.c            |   57 +----
 src/include/commands/collationcmds.h  |    1 -
 src/include/commands/conversioncmds.h |    1 -
 src/include/commands/defrem.h         |    6 +-
 src/include/commands/extension.h      |    1 -
 src/include/commands/schemacmds.h     |    1 -
 src/include/commands/tablecmds.h      |    2 -
 src/include/commands/typecmds.h       |    1 -
 src/include/nodes/parsenodes.h        |    1 +
 21 files changed, 465 insertions(+), 910 deletions(-)

diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 81fd658..4af7aad 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -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 \
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index 9e6138b..3f9f660 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -146,67 +146,6 @@ DefineCollation(List *names, List *parameters)
 }
 
 /*
- * 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
  */
 void
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c
index 2c1c6da..8dafec9 100644
--- a/src/backend/commands/conversioncmds.c
+++ b/src/backend/commands/conversioncmds.c
@@ -120,67 +120,6 @@ CreateConversionCommand(CreateConversionStmt *stmt)
 }
 
 /*
- * 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
  */
 void
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
new file mode 100644
index 0000000..64e8c3a
--- /dev/null
+++ b/src/backend/commands/dropcmds.c
@@ -0,0 +1,456 @@
+/*
+ * dropcmds.c
+ *    routine to support DROP statement commonly
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaddress.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_collation.h"
+#include "catalog/pg_conversion.h"
+#include "catalog/pg_extension.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_ts_config.h"
+#include "catalog/pg_ts_dict.h"
+#include "catalog/pg_ts_parser.h"
+#include "catalog/pg_ts_template.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "parser/parse_type.h"
+#include "storage/lmgr.h"
+#include "utils/acl.h"
+#include "utils/inval.h"
+#include "utils/syscache.h"
+
+static void
+notice_object_non_existent(ObjectType type, List *objname, List *objargs)
+{
+	switch (type)
+	{
+		case OBJECT_TABLE:
+			ereport(NOTICE,
+					(errmsg("table \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_INDEX:
+			ereport(NOTICE,
+					(errmsg("index \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_SEQUENCE:
+			ereport(NOTICE,
+					(errmsg("sequence \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_VIEW:
+			ereport(NOTICE,
+					(errmsg("view \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_FOREIGN_TABLE:
+			ereport(NOTICE,
+					(errmsg("foreign table \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_TYPE:
+		case OBJECT_DOMAIN:
+			{
+				TypeName   *typename = makeTypeNameFromNameList(objname);
+				ereport(NOTICE,
+						(errmsg("type \"%s\" does not exist, skipping",
+								TypeNameToString(typename))));
+			}
+			break;
+
+		case OBJECT_COLLATION:
+			ereport(NOTICE,
+					(errmsg("collation \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_CONVERSION:
+			ereport(NOTICE,
+					(errmsg("conversion \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_SCHEMA:
+			ereport(NOTICE,
+					(errmsg("schema \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_TSPARSER:
+			ereport(NOTICE,
+					(errmsg("text search parser \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_TSDICTIONARY:
+			ereport(NOTICE,
+					(errmsg("text search dictionary \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_TSTEMPLATE:
+			ereport(NOTICE,
+					(errmsg("text search template \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_TSCONFIGURATION:
+			ereport(NOTICE,
+					(errmsg("text search configuration \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		case OBJECT_EXTENSION:
+			ereport(NOTICE,
+					(errmsg("extension \"%s\" does not exist, skipping",
+							NameListToString(objname))));
+			break;
+
+		default:
+			elog(ERROR, "unrecognized drop object type: %d", (int) type);
+			break;
+	}
+}
+
+static Oid
+get_object_namespace(const ObjectAddress *address)
+{
+	Oid			namespaceId = InvalidOid;
+	Oid			objectId = address->objectId;
+	HeapTuple	tuple;
+
+	switch (address->classId)
+	{
+		case RelationRelationId:
+			tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for relation %u", objectId);
+			namespaceId = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case TypeRelationId:
+			tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for type %u", objectId);
+			namespaceId = ((Form_pg_type) GETSTRUCT(tuple))->typnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case CollationRelationId:
+			tuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for collation %u", objectId);
+			namespaceId = ((Form_pg_collation) GETSTRUCT(tuple))->collnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case ConversionRelationId:
+			tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for conversion %u", objectId);
+			namespaceId = ((Form_pg_conversion) GETSTRUCT(tuple))->connamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case TSParserRelationId:
+			tuple = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for text search parser %u",
+					 objectId);
+			namespaceId = ((Form_pg_ts_parser) GETSTRUCT(tuple))->prsnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case TSDictionaryRelationId:
+			tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for text search dictionary %u",
+					 objectId);
+			namespaceId = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case TSTemplateRelationId:
+			tuple = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for text search template %u",
+					 objectId);
+			namespaceId = ((Form_pg_ts_template) GETSTRUCT(tuple))->tmplnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		case TSConfigRelationId:
+			tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(objectId));
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for text search dictionary %u",
+					 objectId);
+			namespaceId = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgnamespace;
+			ReleaseSysCache(tuple);
+			break;
+
+		default:
+			/*
+			 * The supplied object type does not have its namespace.
+			 */
+			namespaceId = InvalidOid;
+			break;
+	}
+	return namespaceId;
+}
+
+static ObjectAddress
+get_relation_address(ObjectType objtype, List *objname,
+					 Relation *relp, bool missing_ok)
+{
+	RangeVar	   *range = makeRangeVarFromNameList(objname);
+	Relation		relation;
+	ObjectAddress	address;
+
+	/*
+	 * 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 */
+	address.classId = RelationRelationId;
+	address.objectId = RangeVarGetRelid(range, true);
+	address.objectSubId = 0;
+
+	if (!address.objectId)
+	{
+		if (!missing_ok)
+		{
+			switch (objtype)
+			{
+				case OBJECT_INDEX:
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_OBJECT),
+							 errmsg("index \"%s\" does not exist",
+									NameListToString(objname))));
+					break;
+				case OBJECT_SEQUENCE:
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_TABLE),
+							 errmsg("sequence \"%s\" does not exist",
+									NameListToString(objname))));
+					break;
+				case OBJECT_TABLE:
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_TABLE),
+							 errmsg("table \"%s\" does not exist",
+									NameListToString(objname))));
+					break;
+				case OBJECT_VIEW:
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_TABLE),
+							 errmsg("view \"%s\" does not exist",
+									NameListToString(objname))));
+					break;
+				case OBJECT_FOREIGN_TABLE:
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_OBJECT),
+							 errmsg("foreign table \"%s\" does not exist",
+									NameListToString(objname))));
+					break;
+				default:
+					elog(ERROR, "unrecognized objtype: %d", (int) objtype);
+					break;
+			}
+		}
+		return address;
+	}
+
+	/*
+	 * In DROP INDEX, attempt to acquire lock on the parent table before
+	 * locking the index.  index_drop() will need this anyway, and since
+	 * regular queries lock tables before their indexes, we risk deadlock
+	 * if we do it the other way around.  No error if we don't find a
+	 * pg_index entry, though --- that most likely means it isn't an
+	 * index, and we'll fail below.
+	 */
+	if (objtype == OBJECT_INDEX)
+	{
+		HeapTuple	tuple =
+			SearchSysCache1(INDEXRELID, ObjectIdGetDatum(address.objectId));
+
+		if (HeapTupleIsValid(tuple))
+		{
+			Form_pg_index index = (Form_pg_index) GETSTRUCT(tuple);
+
+			LockRelationOid(index->indrelid, AccessExclusiveLock);
+			ReleaseSysCache(tuple);
+		}
+	}
+
+	/*
+	 * Lock relation to be removed
+	 */
+	relation = relation_open(address.objectId, AccessExclusiveLock);
+
+	switch (objtype)
+	{
+		case OBJECT_INDEX:
+			if (RelationGetForm(relation)->relkind != RELKIND_INDEX)
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("\"%s\" is not an index",
+								NameListToString(objname))));
+			break;
+		case OBJECT_SEQUENCE:
+			if (RelationGetForm(relation)->relkind != RELKIND_SEQUENCE)
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("\"%s\" is not a sequence",
+								NameListToString(objname))));
+			break;
+		case OBJECT_TABLE:
+			if (RelationGetForm(relation)->relkind != RELKIND_RELATION)
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("\"%s\" is not a table",
+								NameListToString(objname))));
+			break;
+		case OBJECT_VIEW:
+			if (RelationGetForm(relation)->relkind != RELKIND_VIEW)
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("\"%s\" is not a view",
+								NameListToString(objname))));
+			break;
+		case OBJECT_FOREIGN_TABLE:
+			if (RelationGetForm(relation)->relkind != RELKIND_FOREIGN_TABLE)
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("\"%s\" is not a foreign table",
+                                NameListToString(objname))));
+			break;
+		default:
+			elog(ERROR, "unrecognized objtype: %d", (int) objtype);
+			break;
+	}
+
+	/*
+	 * Sanity checks to prevent system catalog unintentionally
+	 */
+	if (!!allowSystemTableMods && IsSystemRelation(relation))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied: \"%s\" is a system catalog",
+						NameListToString(objname))));
+	/* Done */
+	*relp = relation;
+	return address;
+}
+
+void
+RemoveObjects(DropStmt *stmt)
+{
+	ObjectAddresses *objects;
+	ListCell   *cell1;
+	ListCell   *cell2 = NULL;
+
+	Assert(!stmt->arguments ||
+		   list_length(stmt->objects) == list_length(stmt->arguments));
+
+	objects = new_object_addresses();
+
+	foreach(cell1, stmt->objects)
+	{
+		ObjectAddress	address;
+		List	   *objname = lfirst(cell1);
+		List	   *objargs = NIL;
+		Relation	relation = NULL;
+		Oid			namespaceId;
+
+		if (stmt->arguments)
+		{
+			cell2 = (!cell2 ? list_head(stmt->arguments) : lnext(cell2));
+			objargs = lfirst(cell2);
+		}
+
+		/*
+		 * Resolve object name and arguments into ObjectAddress
+		 */
+		switch (stmt->removeType)
+		{
+			case OBJECT_INDEX:
+			case OBJECT_SEQUENCE:
+			case OBJECT_TABLE:
+			case OBJECT_VIEW:
+			case OBJECT_FOREIGN_TABLE:
+				address = get_relation_address(stmt->removeType,
+											   objname,
+											   &relation,
+											   stmt->missing_ok);
+				break;
+
+			default:
+				address = get_object_address(stmt->removeType,
+											 objname, objargs,
+											 &relation,
+											 AccessExclusiveLock,
+											 stmt->missing_ok);
+				break;
+		}
+
+		/*
+		 * Raise an notice, if supplied object was not found
+		 */
+		if (!OidIsValid(address.objectId))
+		{
+			notice_object_non_existent(stmt->removeType,
+									   objname, objargs);
+			continue;
+		}
+
+		/*
+		 * Permission checks
+		 */
+		namespaceId = get_object_namespace(&address);
+		if (!OidIsValid(namespaceId) ||
+			!pg_namespace_ownercheck(namespaceId, GetUserId()))
+			check_object_ownership(GetUserId(), stmt->removeType, address,
+								   objname, objargs, relation);
+
+		/*
+		 * If get_object_address() opened the relation for us, we close it
+		 * to keep the reference count correct - but we retain any locks
+		 * acquired by get_object_address() until commit time, to guard
+		 * against concurrent activities.
+		 */
+		if (relation)
+			heap_close(relation, NoLock);
+
+		add_exact_object_address(&address, objects);
+	}
+	performMultipleDeletions(objects, stmt->behavior);
+
+	free_object_addresses(objects);
+}
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index a0385eb..fdd0d28 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1555,69 +1555,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.
  *
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index 5dd5763..1f941c9 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -148,69 +148,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.
  */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3bc350a..039d49d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -182,59 +182,6 @@ 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.")},
-	{RELKIND_FOREIGN_TABLE,
-		ERRCODE_UNDEFINED_OBJECT,
-		gettext_noop("foreign table \"%s\" does not exist"),
-		gettext_noop("foreign table \"%s\" does not exist, skipping"),
-		gettext_noop("\"%s\" is not a foreign table"),
-	gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
-	{'\0', 0, NULL, NULL, NULL, NULL}
-};
-
 /* Alter table target-type flags for ATSimplePermissions */
 #define		ATT_TABLE				0x0001
 #define		ATT_VIEW				0x0002
@@ -638,201 +585,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 }
 
 /*
- * 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("%s", _(wentry->drophint_msg)) : 0));
-}
-
-/*
- * RemoveRelations
- *		Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
- *		DROP FOREIGN TABLE
- */
-void
-RemoveRelations(DropStmt *drop)
-{
-	ObjectAddresses *objects;
-	char		relkind;
-	ListCell   *cell;
-
-	/*
-	 * 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.
-	 */
-
-	/* Determine required relkind */
-	switch (drop->removeType)
-	{
-		case OBJECT_TABLE:
-			relkind = RELKIND_RELATION;
-			break;
-
-		case OBJECT_INDEX:
-			relkind = RELKIND_INDEX;
-			break;
-
-		case OBJECT_SEQUENCE:
-			relkind = RELKIND_SEQUENCE;
-			break;
-
-		case OBJECT_VIEW:
-			relkind = RELKIND_VIEW;
-			break;
-
-		case OBJECT_FOREIGN_TABLE:
-			relkind = RELKIND_FOREIGN_TABLE;
-			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;
-		}
-
-		/*
-		 * In DROP INDEX, attempt to acquire lock on the parent table before
-		 * locking the index.  index_drop() will need this anyway, and since
-		 * regular queries lock tables before their indexes, we risk deadlock
-		 * if we do it the other way around.  No error if we don't find a
-		 * pg_index entry, though --- that most likely means it isn't an
-		 * index, and we'll fail below.
-		 */
-		if (relkind == RELKIND_INDEX)
-		{
-			tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
-			if (HeapTupleIsValid(tuple))
-			{
-				Form_pg_index index = (Form_pg_index) GETSTRUCT(tuple);
-
-				LockRelationOid(index->indrelid, AccessExclusiveLock);
-				ReleaseSysCache(tuple);
-			}
-		}
-
-		/* Get the lock before trying to fetch the syscache entry */
-		LockRelationOid(relOid, AccessExclusiveLock);
-
-		tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
-		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);
-}
-
-/*
  * ExecuteTruncate
  *		Executes a TRUNCATE command.
  *
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index 3355eaa..197dce7 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -283,65 +283,6 @@ DefineTSParser(List *names, List *parameters)
 }
 
 /*
- * 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.
  */
 void
@@ -735,76 +676,6 @@ AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid)
 }
 
 /*
- * 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.
  */
 void
@@ -1267,65 +1138,6 @@ AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid)
 }
 
 /*
- * 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.
  */
 void
@@ -1720,72 +1532,6 @@ AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid)
 }
 
 /*
- * 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.
  */
 void
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7c27f85..5069c57 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -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.
  */
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c9133dd..0bd8882 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2734,6 +2734,7 @@ _copyDropStmt(DropStmt *from)
 	DropStmt   *newnode = makeNode(DropStmt);
 
 	COPY_NODE_FIELD(objects);
+	COPY_NODE_FIELD(arguments);
 	COPY_SCALAR_FIELD(removeType);
 	COPY_SCALAR_FIELD(behavior);
 	COPY_SCALAR_FIELD(missing_ok);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 3a0267c..ea972be 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1182,6 +1182,7 @@ static bool
 _equalDropStmt(DropStmt *a, DropStmt *b)
 {
 	COMPARE_NODE_FIELD(objects);
+	COMPARE_NODE_FIELD(arguments);
 	COMPARE_SCALAR_FIELD(removeType);
 	COMPARE_SCALAR_FIELD(behavior);
 	COMPARE_SCALAR_FIELD(missing_ok);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7226032..2da12f3 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4722,6 +4722,7 @@ DropStmt:	DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior
 					n->removeType = $2;
 					n->missing_ok = TRUE;
 					n->objects = $5;
+					n->arguments = NIL;
 					n->behavior = $6;
 					$$ = (Node *)n;
 				}
@@ -4731,6 +4732,7 @@ DropStmt:	DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior
 					n->removeType = $2;
 					n->missing_ok = FALSE;
 					n->objects = $3;
+					n->arguments = NIL;
 					n->behavior = $4;
 					$$ = (Node *)n;
 				}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 224e1f3..4336c5e 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -636,62 +636,7 @@ standard_ProcessUtility(Node *parsetree,
 			break;
 
 		case T_DropStmt:
-			{
-				DropStmt   *stmt = (DropStmt *) parsetree;
-
-				switch (stmt->removeType)
-				{
-					case OBJECT_TABLE:
-					case OBJECT_SEQUENCE:
-					case OBJECT_VIEW:
-					case OBJECT_INDEX:
-					case OBJECT_FOREIGN_TABLE:
-						RemoveRelations(stmt);
-						break;
-
-					case OBJECT_TYPE:
-					case OBJECT_DOMAIN:
-						RemoveTypes(stmt);
-						break;
-
-					case OBJECT_COLLATION:
-						DropCollationsCommand(stmt);
-						break;
-
-					case OBJECT_CONVERSION:
-						DropConversionsCommand(stmt);
-						break;
-
-					case OBJECT_SCHEMA:
-						RemoveSchemas(stmt);
-						break;
-
-					case OBJECT_TSPARSER:
-						RemoveTSParsers(stmt);
-						break;
-
-					case OBJECT_TSDICTIONARY:
-						RemoveTSDictionaries(stmt);
-						break;
-
-					case OBJECT_TSTEMPLATE:
-						RemoveTSTemplates(stmt);
-						break;
-
-					case OBJECT_TSCONFIGURATION:
-						RemoveTSConfigurations(stmt);
-						break;
-
-					case OBJECT_EXTENSION:
-						RemoveExtensions(stmt);
-						break;
-
-					default:
-						elog(ERROR, "unrecognized drop object type: %d",
-							 (int) stmt->removeType);
-						break;
-				}
-			}
+			RemoveObjects((DropStmt *) parsetree);
 			break;
 
 		case T_TruncateStmt:
diff --git a/src/include/commands/collationcmds.h b/src/include/commands/collationcmds.h
index 6dbeb75..ce4727c 100644
--- a/src/include/commands/collationcmds.h
+++ b/src/include/commands/collationcmds.h
@@ -18,7 +18,6 @@
 #include "nodes/parsenodes.h"
 
 extern void DefineCollation(List *names, List *parameters);
-extern void DropCollationsCommand(DropStmt *drop);
 extern void RenameCollation(List *name, const char *newname);
 extern void AlterCollationOwner(List *name, Oid newOwnerId);
 extern void AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId);
diff --git a/src/include/commands/conversioncmds.h b/src/include/commands/conversioncmds.h
index f77023f..c0e7cd9 100644
--- a/src/include/commands/conversioncmds.h
+++ b/src/include/commands/conversioncmds.h
@@ -18,7 +18,6 @@
 #include "nodes/parsenodes.h"
 
 extern void CreateConversionCommand(CreateConversionStmt *parsetree);
-extern void DropConversionsCommand(DropStmt *drop);
 extern void RenameConversion(List *name, const char *newname);
 extern void AlterConversionOwner(List *name, Oid newOwnerId);
 extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId);
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index bbc024f..641497b 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -16,6 +16,8 @@
 
 #include "nodes/parsenodes.h"
 
+/* commands/dropcmds.c */
+extern void RemoveObjects(DropStmt *stmt);
 
 /* commands/indexcmds.c */
 extern void DefineIndex(RangeVar *heapRelation,
@@ -116,12 +118,10 @@ extern void DefineTSParser(List *names, List *parameters);
 extern void RenameTSParser(List *oldname, const char *newname);
 extern void AlterTSParserNamespace(List *name, const char *newschema);
 extern Oid	AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid);
-extern void RemoveTSParsers(DropStmt *drop);
 extern void RemoveTSParserById(Oid prsId);
 
 extern void DefineTSDictionary(List *names, List *parameters);
 extern void RenameTSDictionary(List *oldname, const char *newname);
-extern void RemoveTSDictionaries(DropStmt *drop);
 extern void RemoveTSDictionaryById(Oid dictId);
 extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
 extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId);
@@ -132,12 +132,10 @@ extern void DefineTSTemplate(List *names, List *parameters);
 extern void RenameTSTemplate(List *oldname, const char *newname);
 extern void AlterTSTemplateNamespace(List *name, const char *newschema);
 extern Oid	AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid);
-extern void RemoveTSTemplates(DropStmt *stmt);
 extern void RemoveTSTemplateById(Oid tmplId);
 
 extern void DefineTSConfiguration(List *names, List *parameters);
 extern void RenameTSConfiguration(List *oldname, const char *newname);
-extern void RemoveTSConfigurations(DropStmt *stmt);
 extern void RemoveTSConfigurationById(Oid cfgId);
 extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
 extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId);
diff --git a/src/include/commands/extension.h b/src/include/commands/extension.h
index 2792c6d..f22ac80 100644
--- a/src/include/commands/extension.h
+++ b/src/include/commands/extension.h
@@ -29,7 +29,6 @@ extern Oid	CurrentExtensionObject;
 
 extern void CreateExtension(CreateExtensionStmt *stmt);
 
-extern void RemoveExtensions(DropStmt *stmt);
 extern void RemoveExtensionById(Oid extId);
 
 extern Oid InsertExtensionTuple(const char *extName, Oid extOwner,
diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h
index a9f8f6c..ec8d895 100644
--- a/src/include/commands/schemacmds.h
+++ b/src/include/commands/schemacmds.h
@@ -20,7 +20,6 @@
 extern void CreateSchemaCommand(CreateSchemaStmt *parsetree,
 					const char *queryString);
 
-extern void RemoveSchemas(DropStmt *drop);
 extern void RemoveSchemaById(Oid schemaOid);
 
 extern void RenameSchema(const char *oldname, const char *newname);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 3f971eb..eb93841 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -21,8 +21,6 @@
 
 extern Oid	DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId);
 
-extern void RemoveRelations(DropStmt *drop);
-
 extern void AlterTable(AlterTableStmt *stmt);
 
 extern LOCKMODE AlterTableGetLockLevel(List *cmds);
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 23726fb..429a964 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -20,7 +20,6 @@
 #define DEFAULT_TYPDELIM		','
 
 extern void DefineType(List *names, List *parameters);
-extern void RemoveTypes(DropStmt *drop);
 extern void RemoveTypeById(Oid typeOid);
 extern void DefineDomain(CreateDomainStmt *stmt);
 extern void DefineEnum(CreateEnumStmt *stmt);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 00c1269..068ba00 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1919,6 +1919,7 @@ typedef struct DropStmt
 {
 	NodeTag		type;
 	List	   *objects;		/* list of sublists of names (as Values) */
+	List	   *arguments;		/* list of sublists of arguments (as Values) */
 	ObjectType	removeType;		/* object type */
 	DropBehavior behavior;		/* RESTRICT or CASCADE behavior */
 	bool		missing_ok;		/* skip error if object is missing? */
#9Robert Haas
robertmhaas@gmail.com
In reply to: Kohei KaiGai (#8)
Re: [v9.2] DROP Reworks Part.1 - Consolidate routines to handle DropStmt

On Fri, Jul 8, 2011 at 9:06 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

I definitely agree with this idea. It will enables to eliminate ugly switch
statements for error-messaging reasons.

All right, so please submit a patch that introduces that concept
first, and then resubmit this patch rebased over those changes.

In view of the time remaining in this CommitFest, I am going to mark
this Returned with Feedback for now. The next CommitFest starts
September 15th, but I'll try to review it sooner as time permits.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#10Kohei KaiGai
kaigai@kaigai.gr.jp
In reply to: Robert Haas (#9)
Re: [v9.2] DROP Reworks Part.1 - Consolidate routines to handle DropStmt

2011/7/9 Robert Haas <robertmhaas@gmail.com>:

On Fri, Jul 8, 2011 at 9:06 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

I definitely agree with this idea. It will enables to eliminate ugly switch
statements for error-messaging reasons.

All right, so please submit a patch that introduces that concept
first, and then resubmit this patch rebased over those changes.

In view of the time remaining in this CommitFest, I am going to mark
this Returned with Feedback for now.  The next CommitFest starts
September 15th, but I'll try to review it sooner as time permits.

OK, Thanks for your reviewing.

--
KaiGai Kohei <kaigai@kaigai.gr.jp>

#11Peter Eisentraut
peter_e@gmx.net
In reply to: Robert Haas (#3)
Re: [v9.2] DROP Reworks Part.1 - Consolidate routines to handle DropStmt

On ons, 2011-07-06 at 12:40 -0400, Robert Haas wrote:

I think perhaps we should create a
big static array, each row of which would contain:

- ObjectType
- system cache ID for OID lookups
- system catalog table OID for scans
- attribute number for the name attribute, where applicable (see
AlterObjectNamespace)
- attribute number for the namespace attribute
- attribute number for the owner attribute
- ...and maybe some other properties

Yeah, I was thinking of the same thing a while ago.

For large parts of the DDL support for collations I literally copied
over the conversion support and ran sed over it. That must be made
better. Take that as a test case if you will.