On Mon, Mar 05, 2018 at 12:57:38PM +0000, Aleksander Alekseev wrote:
> It looks like that this patch doesn't apply anymore: 
> http://commitfest.cputube.org/
> 
> The new status of this patch is: Waiting on Author

Yes, this happens because patch 0003 from the last series has been
committed as a26116c6.  Attached is a rebased set, though the patches
have no actual changes as those are able to apply correctly.
--
Michael
From 7fedf5f334acf0bddcf2f5672df368302ceb698b Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Tue, 13 Feb 2018 11:12:44 +0900
Subject: [PATCH 1/3] Extend lookup routines for FDW and foreign server with
 NULL handling

The cache lookup routines for foreign-data wrappers and foreign servers
are extended with an extra argument able to control if an error or a
NULL object is returned to the caller in the event of an undefined
object.
---
 contrib/dblink/dblink.c             |  2 +-
 contrib/file_fdw/file_fdw.c         |  4 ++--
 contrib/postgres_fdw/connection.c   |  4 ++--
 contrib/postgres_fdw/postgres_fdw.c |  8 ++++----
 doc/src/sgml/fdwhandler.sgml        | 10 ++++++++--
 src/backend/catalog/objectaddress.c | 12 ++++++------
 src/backend/commands/foreigncmds.c  | 14 ++++++++------
 src/backend/commands/tablecmds.c    |  8 ++++----
 src/backend/foreign/foreign.c       | 28 ++++++++++++++++++++++------
 src/include/foreign/foreign.h       |  4 ++--
 10 files changed, 59 insertions(+), 35 deletions(-)

diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index ae7e24ad08..a67febac6f 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -2784,7 +2784,7 @@ get_connect_string(const char *servername)
 		Oid			userid = GetUserId();
 
 		user_mapping = GetUserMapping(userid, serverid);
-		fdw = GetForeignDataWrapper(fdwid);
+		fdw = GetForeignDataWrapper(fdwid, false);
 
 		/* Check permissions, user must have usage on the server. */
 		aclresult = pg_foreign_server_aclcheck(serverid, userid, ACL_USAGE);
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index cf0a3629bc..d837f977e8 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -357,8 +357,8 @@ fileGetOptions(Oid foreigntableid,
 	 * Simplify?)
 	 */
 	table = GetForeignTable(foreigntableid);
-	server = GetForeignServer(table->serverid);
-	wrapper = GetForeignDataWrapper(server->fdwid);
+	server = GetForeignServer(table->serverid, false);
+	wrapper = GetForeignDataWrapper(server->fdwid, false);
 
 	options = NIL;
 	options = list_concat(options, wrapper->options);
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 00c926b983..3503595b7b 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -182,7 +182,7 @@ GetConnection(UserMapping *user, bool will_prep_stmt)
 	 */
 	if (entry->conn == NULL)
 	{
-		ForeignServer *server = GetForeignServer(user->serverid);
+		ForeignServer *server = GetForeignServer(user->serverid, false);
 
 		/* Reset all transient state fields, to be sure all are clean */
 		entry->xact_depth = 0;
@@ -1003,7 +1003,7 @@ pgfdw_reject_incomplete_xact_state_change(ConnCacheEntry *entry)
 	if (!HeapTupleIsValid(tup))
 		elog(ERROR, "cache lookup failed for user mapping %u", entry->key);
 	umform = (Form_pg_user_mapping) GETSTRUCT(tup);
-	server = GetForeignServer(umform->umserver);
+	server = GetForeignServer(umform->umserver, false);
 	ReleaseSysCache(tup);
 
 	ereport(ERROR,
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index e8a0d5482a..590469fe9e 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -520,7 +520,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 
 	/* Look up foreign-table catalog info. */
 	fpinfo->table = GetForeignTable(foreigntableid);
-	fpinfo->server = GetForeignServer(fpinfo->table->serverid);
+	fpinfo->server = GetForeignServer(fpinfo->table->serverid, false);
 
 	/*
 	 * Extract user-settable option values.  Note that per-table setting of
@@ -2053,7 +2053,7 @@ postgresIsForeignRelUpdatable(Relation rel)
 	updatable = true;
 
 	table = GetForeignTable(RelationGetRelid(rel));
-	server = GetForeignServer(table->serverid);
+	server = GetForeignServer(table->serverid, false);
 
 	foreach(lc, server->options)
 	{
@@ -3998,7 +3998,7 @@ postgresAcquireSampleRowsFunc(Relation relation, int elevel,
 	 * owner, even if the ANALYZE was started by some other user.
 	 */
 	table = GetForeignTable(RelationGetRelid(relation));
-	server = GetForeignServer(table->serverid);
+	server = GetForeignServer(table->serverid, false);
 	user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
 	conn = GetConnection(user, false);
 
@@ -4221,7 +4221,7 @@ postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
 	 * Get connection to the foreign server.  Connection manager will
 	 * establish new connection if necessary.
 	 */
-	server = GetForeignServer(serverOid);
+	server = GetForeignServer(serverOid, false);
 	mapping = GetUserMapping(GetUserId(), server->serverid);
 	conn = GetConnection(mapping, false);
 
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 0ed3a47233..af6b5822f6 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -1333,25 +1333,31 @@ ReparameterizeForeignPathByChild(PlannerInfo *root, List *fdw_private,
     <para>
 <programlisting>
 ForeignDataWrapper *
-GetForeignDataWrapper(Oid fdwid);
+GetForeignDataWrapper(Oid fdwid, bool missing_ok);
 </programlisting>
 
      This function returns a <structname>ForeignDataWrapper</structname>
      object for the foreign-data wrapper with the given OID.  A
      <structname>ForeignDataWrapper</structname> object contains properties
      of the FDW (see <filename>foreign/foreign.h</filename> for details).
+     If <literal>missing_ok</literal> is true, a <literal>NULL</literal>
+     result is returned to the caller instead of an error for an undefined
+     foreign-data wrapper.
     </para>
 
     <para>
 <programlisting>
 ForeignServer *
-GetForeignServer(Oid serverid);
+GetForeignServer(Oid serverid, bool missing_ok);
 </programlisting>
 
      This function returns a <structname>ForeignServer</structname> object
      for the foreign server with the given OID.  A
      <structname>ForeignServer</structname> object contains properties
      of the server (see <filename>foreign/foreign.h</filename> for details).
+     If <literal>missing_ok</literal> is true, a <literal>NULL</literal>
+     result is returned to the caller instead of an error for an undefined
+     foreign server.
     </para>
 
     <para>
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 119297b33a..a748df7a3a 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -3206,7 +3206,7 @@ getObjectDescription(const ObjectAddress *object)
 			{
 				ForeignDataWrapper *fdw;
 
-				fdw = GetForeignDataWrapper(object->objectId);
+				fdw = GetForeignDataWrapper(object->objectId, false);
 				appendStringInfo(&buffer, _("foreign-data wrapper %s"), fdw->fdwname);
 				break;
 			}
@@ -3215,7 +3215,7 @@ getObjectDescription(const ObjectAddress *object)
 			{
 				ForeignServer *srv;
 
-				srv = GetForeignServer(object->objectId);
+				srv = GetForeignServer(object->objectId, false);
 				appendStringInfo(&buffer, _("server %s"), srv->servername);
 				break;
 			}
@@ -3235,7 +3235,7 @@ getObjectDescription(const ObjectAddress *object)
 						 object->objectId);
 				umform = (Form_pg_user_mapping) GETSTRUCT(tup);
 				useid = umform->umuser;
-				srv = GetForeignServer(umform->umserver);
+				srv = GetForeignServer(umform->umserver, false);
 
 				ReleaseSysCache(tup);
 
@@ -4713,7 +4713,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 			{
 				ForeignDataWrapper *fdw;
 
-				fdw = GetForeignDataWrapper(object->objectId);
+				fdw = GetForeignDataWrapper(object->objectId, false);
 				appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
 				if (objname)
 					*objname = list_make1(pstrdup(fdw->fdwname));
@@ -4724,7 +4724,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 			{
 				ForeignServer *srv;
 
-				srv = GetForeignServer(object->objectId);
+				srv = GetForeignServer(object->objectId, false);
 				appendStringInfoString(&buffer,
 									   quote_identifier(srv->servername));
 				if (objname)
@@ -4747,7 +4747,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 						 object->objectId);
 				umform = (Form_pg_user_mapping) GETSTRUCT(tup);
 				useid = umform->umuser;
-				srv = GetForeignServer(umform->umserver);
+				srv = GetForeignServer(umform->umserver, false);
 
 				ReleaseSysCache(tup);
 
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 5c53aeeaeb..645e2efb74 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -368,7 +368,8 @@ AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
 			aclresult = pg_foreign_data_wrapper_aclcheck(form->srvfdw, newOwnerId, ACL_USAGE);
 			if (aclresult != ACLCHECK_OK)
 			{
-				ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
+				ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw,
+																false);
 
 				aclcheck_error(aclresult, OBJECT_FDW, fdw->fdwname);
 			}
@@ -1033,7 +1034,8 @@ AlterForeignServer(AlterForeignServerStmt *stmt)
 
 	if (stmt->options)
 	{
-		ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
+		ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw,
+														false);
 		Datum		datum;
 		bool		isnull;
 
@@ -1187,7 +1189,7 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
 							stmt->servername)));
 	}
 
-	fdw = GetForeignDataWrapper(srv->fdwid);
+	fdw = GetForeignDataWrapper(srv->fdwid, false);
 
 	/*
 	 * Insert tuple into pg_user_mapping.
@@ -1299,7 +1301,7 @@ AlterUserMapping(AlterUserMappingStmt *stmt)
 		 * Process the options.
 		 */
 
-		fdw = GetForeignDataWrapper(srv->fdwid);
+		fdw = GetForeignDataWrapper(srv->fdwid, false);
 
 		datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
 								tp,
@@ -1479,7 +1481,7 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
 	if (aclresult != ACLCHECK_OK)
 		aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
 
-	fdw = GetForeignDataWrapper(server->fdwid);
+	fdw = GetForeignDataWrapper(server->fdwid, false);
 
 	/*
 	 * Insert tuple into pg_foreign_table.
@@ -1542,7 +1544,7 @@ ImportForeignSchema(ImportForeignSchemaStmt *stmt)
 	(void) LookupCreationNamespace(stmt->local_schema);
 
 	/* Get the FDW and check it supports IMPORT */
-	fdw = GetForeignDataWrapper(server->fdwid);
+	fdw = GetForeignDataWrapper(server->fdwid, false);
 	if (!OidIsValid(fdw->fdwhandler))
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 74e020bffc..d472c94030 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9563,8 +9563,8 @@ ATExecAlterColumnGenericOptions(Relation rel,
 				 errmsg("foreign table \"%s\" does not exist",
 						RelationGetRelationName(rel))));
 	fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
-	server = GetForeignServer(fttableform->ftserver);
-	fdw = GetForeignDataWrapper(server->fdwid);
+	server = GetForeignServer(fttableform->ftserver, false);
+	fdw = GetForeignDataWrapper(server->fdwid, false);
 
 	heap_close(ftrel, AccessShareLock);
 	ReleaseSysCache(tuple);
@@ -12466,8 +12466,8 @@ ATExecGenericOptions(Relation rel, List *options)
 				 errmsg("foreign table \"%s\" does not exist",
 						RelationGetRelationName(rel))));
 	tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
-	server = GetForeignServer(tableform->ftserver);
-	fdw = GetForeignDataWrapper(server->fdwid);
+	server = GetForeignServer(tableform->ftserver, false);
+	fdw = GetForeignDataWrapper(server->fdwid, false);
 
 	memset(repl_val, 0, sizeof(repl_val));
 	memset(repl_null, false, sizeof(repl_null));
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index eac78a5d31..07b5857bfa 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -32,7 +32,7 @@
  * GetForeignDataWrapper -	look up the foreign-data wrapper by OID.
  */
 ForeignDataWrapper *
-GetForeignDataWrapper(Oid fdwid)
+GetForeignDataWrapper(Oid fdwid, bool missing_ok)
 {
 	Form_pg_foreign_data_wrapper fdwform;
 	ForeignDataWrapper *fdw;
@@ -43,7 +43,11 @@ GetForeignDataWrapper(Oid fdwid)
 	tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
 
 	if (!HeapTupleIsValid(tp))
-		elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
+	{
+		if (!missing_ok)
+			elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
+		return NULL;
+	}
 
 	fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
 
@@ -82,7 +86,11 @@ GetForeignDataWrapperByName(const char *fdwname, bool missing_ok)
 	if (!OidIsValid(fdwId))
 		return NULL;
 
-	return GetForeignDataWrapper(fdwId);
+	/*
+	 * missing_ok set to true makes no sense here as a lookup has already
+	 * happened.
+	 */
+	return GetForeignDataWrapper(fdwId, false);
 }
 
 
@@ -90,7 +98,7 @@ GetForeignDataWrapperByName(const char *fdwname, bool missing_ok)
  * GetForeignServer - look up the foreign server definition.
  */
 ForeignServer *
-GetForeignServer(Oid serverid)
+GetForeignServer(Oid serverid, bool missing_ok)
 {
 	Form_pg_foreign_server serverform;
 	ForeignServer *server;
@@ -101,7 +109,11 @@ GetForeignServer(Oid serverid)
 	tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
 
 	if (!HeapTupleIsValid(tp))
-		elog(ERROR, "cache lookup failed for foreign server %u", serverid);
+	{
+		if (!missing_ok)
+			elog(ERROR, "cache lookup failed for foreign server %u", serverid);
+		return NULL;
+	}
 
 	serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
 
@@ -152,7 +164,11 @@ GetForeignServerByName(const char *srvname, bool missing_ok)
 	if (!OidIsValid(serverid))
 		return NULL;
 
-	return GetForeignServer(serverid);
+	/*
+	 * missing_ok set to true makes no sense here as a lookup has already
+	 * happened.
+	 */
+	return GetForeignServer(serverid, false);
 }
 
 
diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h
index 3ca12e64d2..df969d04ea 100644
--- a/src/include/foreign/foreign.h
+++ b/src/include/foreign/foreign.h
@@ -69,10 +69,10 @@ typedef struct ForeignTable
 } ForeignTable;
 
 
-extern ForeignServer *GetForeignServer(Oid serverid);
+extern ForeignServer *GetForeignServer(Oid serverid, bool missing_ok);
 extern ForeignServer *GetForeignServerByName(const char *name, bool missing_ok);
 extern UserMapping *GetUserMapping(Oid userid, Oid serverid);
-extern ForeignDataWrapper *GetForeignDataWrapper(Oid fdwid);
+extern ForeignDataWrapper *GetForeignDataWrapper(Oid fdwid, bool missing_ok);
 extern ForeignDataWrapper *GetForeignDataWrapperByName(const char *name,
 							bool missing_ok);
 extern ForeignTable *GetForeignTable(Oid relid);
-- 
2.16.2

From 656b2ddc55a18ac39a56ea73f8616bb67da8218a Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Tue, 13 Feb 2018 11:17:56 +0900
Subject: [PATCH 2/3] Refactor routines for subscription and publication
 lookups

Those routines gain a missing_ok argument, allowing a caller to get a
NULL result instead of an error if set to true.
---
 src/backend/catalog/objectaddress.c   | 13 +++++++------
 src/backend/catalog/pg_publication.c  | 11 +++++++++--
 src/backend/catalog/pg_subscription.c | 11 +++++++++--
 src/include/catalog/pg_publication.h  |  2 +-
 src/include/catalog/pg_subscription.h |  2 +-
 5 files changed, 27 insertions(+), 12 deletions(-)

diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index a748df7a3a..4925601a24 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -3387,7 +3387,8 @@ getObjectDescription(const ObjectAddress *object)
 		case OCLASS_PUBLICATION:
 			{
 				appendStringInfo(&buffer, _("publication %s"),
-								 get_publication_name(object->objectId));
+								 get_publication_name(object->objectId,
+													  false));
 				break;
 			}
 
@@ -3404,7 +3405,7 @@ getObjectDescription(const ObjectAddress *object)
 						 object->objectId);
 
 				prform = (Form_pg_publication_rel) GETSTRUCT(tup);
-				pubname = get_publication_name(prform->prpubid);
+				pubname = get_publication_name(prform->prpubid, false);
 
 				appendStringInfo(&buffer, _("publication table %s in publication %s"),
 								 get_rel_name(prform->prrelid), pubname);
@@ -3415,7 +3416,7 @@ getObjectDescription(const ObjectAddress *object)
 		case OCLASS_SUBSCRIPTION:
 			{
 				appendStringInfo(&buffer, _("subscription %s"),
-								 get_subscription_name(object->objectId));
+								 get_subscription_name(object->objectId, false));
 				break;
 			}
 
@@ -4913,7 +4914,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 			{
 				char	   *pubname;
 
-				pubname = get_publication_name(object->objectId);
+				pubname = get_publication_name(object->objectId, false);
 				appendStringInfoString(&buffer,
 									   quote_identifier(pubname));
 				if (objname)
@@ -4934,7 +4935,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 						 object->objectId);
 
 				prform = (Form_pg_publication_rel) GETSTRUCT(tup);
-				pubname = get_publication_name(prform->prpubid);
+				pubname = get_publication_name(prform->prpubid, false);
 
 				appendStringInfo(&buffer, _("%s in publication %s"),
 								 get_rel_name(prform->prrelid), pubname);
@@ -4953,7 +4954,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 			{
 				char	   *subname;
 
-				subname = get_subscription_name(object->objectId);
+				subname = get_subscription_name(object->objectId, false);
 				appendStringInfoString(&buffer,
 									   quote_identifier(subname));
 				if (objname)
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index ba18258ebb..06867edc0c 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -426,9 +426,12 @@ get_publication_oid(const char *pubname, bool missing_ok)
 
 /*
  * get_publication_name - given a publication Oid, look up the name
+ *
+ * If missing_ok is false, throw an error if name not found.  If true, just
+ * return NULL.
  */
 char *
-get_publication_name(Oid pubid)
+get_publication_name(Oid pubid, bool missing_ok)
 {
 	HeapTuple	tup;
 	char	   *pubname;
@@ -437,7 +440,11 @@ get_publication_name(Oid pubid)
 	tup = SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(pubid));
 
 	if (!HeapTupleIsValid(tup))
-		elog(ERROR, "cache lookup failed for publication %u", pubid);
+	{
+		if (!missing_ok)
+			elog(ERROR, "cache lookup failed for publication %u", pubid);
+		return NULL;
+	}
 
 	pubform = (Form_pg_publication) GETSTRUCT(tup);
 	pubname = pstrdup(NameStr(pubform->pubname));
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index 8e16d3b7bc..89d5520e4e 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -179,9 +179,12 @@ get_subscription_oid(const char *subname, bool missing_ok)
 
 /*
  * get_subscription_name - given a subscription OID, look up the name
+ *
+ * If missing_ok is false, throw an error if name not found.  If true, just
+ * return NULL.
  */
 char *
-get_subscription_name(Oid subid)
+get_subscription_name(Oid subid, bool missing_ok)
 {
 	HeapTuple	tup;
 	char	   *subname;
@@ -190,7 +193,11 @@ get_subscription_name(Oid subid)
 	tup = SearchSysCache1(SUBSCRIPTIONOID, ObjectIdGetDatum(subid));
 
 	if (!HeapTupleIsValid(tup))
-		elog(ERROR, "cache lookup failed for subscription %u", subid);
+	{
+		if (!missing_ok)
+			elog(ERROR, "cache lookup failed for subscription %u", subid);
+		return NULL;
+	}
 
 	subform = (Form_pg_subscription) GETSTRUCT(tup);
 	subname = pstrdup(NameStr(subform->subname));
diff --git a/src/include/catalog/pg_publication.h b/src/include/catalog/pg_publication.h
index 37e77b8be7..92721348c7 100644
--- a/src/include/catalog/pg_publication.h
+++ b/src/include/catalog/pg_publication.h
@@ -98,7 +98,7 @@ extern ObjectAddress publication_add_relation(Oid pubid, Relation targetrel,
 						 bool if_not_exists);
 
 extern Oid	get_publication_oid(const char *pubname, bool missing_ok);
-extern char *get_publication_name(Oid pubid);
+extern char *get_publication_name(Oid pubid, bool missing_ok);
 
 extern Datum pg_get_publication_tables(PG_FUNCTION_ARGS);
 
diff --git a/src/include/catalog/pg_subscription.h b/src/include/catalog/pg_subscription.h
index 46d0b48232..307893b1f1 100644
--- a/src/include/catalog/pg_subscription.h
+++ b/src/include/catalog/pg_subscription.h
@@ -89,7 +89,7 @@ typedef struct Subscription
 extern Subscription *GetSubscription(Oid subid, bool missing_ok);
 extern void FreeSubscription(Subscription *sub);
 extern Oid	get_subscription_oid(const char *subname, bool missing_ok);
-extern char *get_subscription_name(Oid subid);
+extern char *get_subscription_name(Oid subid, bool missing_ok);
 
 extern int	CountDBSubscriptions(Oid dbid);
 
-- 
2.16.2

From 9aa60a457f836987e8425a3fb2cdca9cb8f4eef3 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Fri, 12 Jan 2018 10:55:12 +0900
Subject: [PATCH 3/3] Eliminate user-visible cache lookup errors for objaddr 
 SQL functions

When using the following functions, users could see various types of
errors like "cache lookup failed for OID XXX":
* pg_describe_object
* pg_identify_object_as_address
* pg_identify_object
All the lower set of APIs managing object addresses for all types of
the system are made smarter by gaining a missing_ok argument that allows
any caller to control if he is willing to have an actual error or if
he wants to control error at its level by having empty objects if they
are undefined.

Regression tests added in this commit failed with such errors before
being patched.
---
 contrib/sepgsql/database.c                   |   6 +-
 contrib/sepgsql/dml.c                        |   4 +-
 contrib/sepgsql/label.c                      |   4 +-
 contrib/sepgsql/proc.c                       |  14 +-
 contrib/sepgsql/relation.c                   |  20 +-
 contrib/sepgsql/schema.c                     |   6 +-
 doc/src/sgml/func.sgml                       |   7 +-
 src/backend/catalog/dependency.c             |  19 +-
 src/backend/catalog/objectaddress.c          | 999 +++++++++++++++++++--------
 src/backend/catalog/pg_depend.c              |   4 +-
 src/backend/catalog/pg_shdepend.c            |   8 +-
 src/backend/commands/event_trigger.c         |   9 +-
 src/backend/commands/extension.c             |   6 +-
 src/backend/commands/tablecmds.c             |  10 +-
 src/backend/utils/adt/regproc.c              |  22 +-
 src/include/catalog/objectaddress.h          |  12 +-
 src/include/utils/regproc.h                  |   4 +-
 src/test/regress/expected/object_address.out | 708 +++++++++++++++++++
 src/test/regress/sql/object_address.sql      | 124 ++++
 19 files changed, 1612 insertions(+), 374 deletions(-)

diff --git a/contrib/sepgsql/database.c b/contrib/sepgsql/database.c
index c641ec3565..dda2ff1cc4 100644
--- a/contrib/sepgsql/database.c
+++ b/contrib/sepgsql/database.c
@@ -142,7 +142,7 @@ sepgsql_database_drop(Oid databaseId)
 	object.classId = DatabaseRelationId;
 	object.objectId = databaseId;
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	sepgsql_avc_check_perms(&object,
 							SEPG_CLASS_DB_DATABASE,
@@ -169,7 +169,7 @@ sepgsql_database_setattr(Oid databaseId)
 	object.classId = DatabaseRelationId;
 	object.objectId = databaseId;
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	sepgsql_avc_check_perms(&object,
 							SEPG_CLASS_DB_DATABASE,
@@ -193,7 +193,7 @@ sepgsql_database_relabel(Oid databaseId, const char *seclabel)
 	object.classId = DatabaseRelationId;
 	object.objectId = databaseId;
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	/*
 	 * check db_database:{setattr relabelfrom} permission
diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c
index c1fa320eb4..709d89e4d2 100644
--- a/contrib/sepgsql/dml.c
+++ b/contrib/sepgsql/dml.c
@@ -183,7 +183,7 @@ check_relation_privileges(Oid relOid,
 	object.classId = RelationRelationId;
 	object.objectId = relOid;
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 	switch (relkind)
 	{
 		case RELKIND_RELATION:
@@ -260,7 +260,7 @@ check_relation_privileges(Oid relOid,
 		object.classId = RelationRelationId;
 		object.objectId = relOid;
 		object.objectSubId = attnum;
-		audit_name = getObjectDescription(&object);
+		audit_name = getObjectDescription(&object, false);
 
 		result = sepgsql_avc_check_perms(&object,
 										 SEPG_CLASS_DB_COLUMN,
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
index 7554017923..ee591ca10b 100644
--- a/contrib/sepgsql/label.c
+++ b/contrib/sepgsql/label.c
@@ -372,7 +372,7 @@ sepgsql_fmgr_hook(FmgrHookEventType event,
 					sepgsql_avc_check_perms(&object,
 											SEPG_CLASS_DB_PROCEDURE,
 											SEPG_DB_PROCEDURE__ENTRYPOINT,
-											getObjectDescription(&object),
+											getObjectDescription(&object, false),
 											true);
 
 					sepgsql_avc_check_perms_label(stack->new_label,
@@ -543,7 +543,7 @@ sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel)
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("sepgsql provider does not support labels on %s",
-							getObjectTypeDescription(object))));
+							getObjectTypeDescription(object, false))));
 			break;
 	}
 }
diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c
index c6a817d7c5..934a2cfaed 100644
--- a/contrib/sepgsql/proc.c
+++ b/contrib/sepgsql/proc.c
@@ -81,7 +81,7 @@ sepgsql_proc_post_create(Oid functionId)
 	sepgsql_avc_check_perms(&object,
 							SEPG_CLASS_DB_SCHEMA,
 							SEPG_DB_SCHEMA__ADD_NAME,
-							getObjectIdentity(&object),
+							getObjectIdentity(&object, false),
 							true);
 
 	/*
@@ -115,7 +115,7 @@ sepgsql_proc_post_create(Oid functionId)
 		object.classId = TypeRelationId;
 		object.objectId = proForm->proargtypes.values[i];
 		object.objectSubId = 0;
-		appendStringInfoString(&audit_name, getObjectIdentity(&object));
+		appendStringInfoString(&audit_name, getObjectIdentity(&object, false));
 	}
 	appendStringInfoChar(&audit_name, ')');
 
@@ -165,7 +165,7 @@ sepgsql_proc_drop(Oid functionId)
 	object.classId = NamespaceRelationId;
 	object.objectId = get_func_namespace(functionId);
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	sepgsql_avc_check_perms(&object,
 							SEPG_CLASS_DB_SCHEMA,
@@ -180,7 +180,7 @@ sepgsql_proc_drop(Oid functionId)
 	object.classId = ProcedureRelationId;
 	object.objectId = functionId;
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	sepgsql_avc_check_perms(&object,
 							SEPG_CLASS_DB_PROCEDURE,
@@ -205,7 +205,7 @@ sepgsql_proc_relabel(Oid functionId, const char *seclabel)
 	object.classId = ProcedureRelationId;
 	object.objectId = functionId;
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	/*
 	 * check db_procedure:{setattr relabelfrom} permission
@@ -293,7 +293,7 @@ sepgsql_proc_setattr(Oid functionId)
 	object.classId = ProcedureRelationId;
 	object.objectId = functionId;
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	sepgsql_avc_check_perms(&object,
 							SEPG_CLASS_DB_PROCEDURE,
@@ -325,7 +325,7 @@ sepgsql_proc_execute(Oid functionId)
 	object.classId = ProcedureRelationId;
 	object.objectId = functionId;
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 	sepgsql_avc_check_perms(&object,
 							SEPG_CLASS_DB_PROCEDURE,
 							SEPG_DB_PROCEDURE__EXECUTE,
diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c
index f0c22715aa..3f3b0bf17c 100644
--- a/contrib/sepgsql/relation.c
+++ b/contrib/sepgsql/relation.c
@@ -103,7 +103,7 @@ sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
 
 	initStringInfo(&audit_name);
 	appendStringInfo(&audit_name, "%s.%s",
-					 getObjectIdentity(&object),
+					 getObjectIdentity(&object, false),
 					 quote_identifier(NameStr(attForm->attname)));
 	sepgsql_avc_check_perms_label(ncontext,
 								  SEPG_CLASS_DB_COLUMN,
@@ -147,7 +147,7 @@ sepgsql_attribute_drop(Oid relOid, AttrNumber attnum)
 	object.classId = RelationRelationId;
 	object.objectId = relOid;
 	object.objectSubId = attnum;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	sepgsql_avc_check_perms(&object,
 							SEPG_CLASS_DB_COLUMN,
@@ -179,7 +179,7 @@ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
 	object.classId = RelationRelationId;
 	object.objectId = relOid;
 	object.objectSubId = attnum;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	/*
 	 * check db_column:{setattr relabelfrom} permission
@@ -223,7 +223,7 @@ sepgsql_attribute_setattr(Oid relOid, AttrNumber attnum)
 	object.classId = RelationRelationId;
 	object.objectId = relOid;
 	object.objectSubId = attnum;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	sepgsql_avc_check_perms(&object,
 							SEPG_CLASS_DB_COLUMN,
@@ -289,7 +289,7 @@ sepgsql_relation_post_create(Oid relOid)
 	sepgsql_avc_check_perms(&object,
 							SEPG_CLASS_DB_SCHEMA,
 							SEPG_DB_SCHEMA__ADD_NAME,
-							getObjectIdentity(&object),
+							getObjectIdentity(&object, false),
 							true);
 
 	switch (classForm->relkind)
@@ -451,7 +451,7 @@ sepgsql_relation_drop(Oid relOid)
 	object.classId = NamespaceRelationId;
 	object.objectId = get_rel_namespace(relOid);
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	sepgsql_avc_check_perms(&object,
 							SEPG_CLASS_DB_SCHEMA,
@@ -473,7 +473,7 @@ sepgsql_relation_drop(Oid relOid)
 	object.classId = RelationRelationId;
 	object.objectId = relOid;
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	sepgsql_avc_check_perms(&object,
 							tclass,
@@ -504,7 +504,7 @@ sepgsql_relation_drop(Oid relOid)
 			object.classId = RelationRelationId;
 			object.objectId = relOid;
 			object.objectSubId = attForm->attnum;
-			audit_name = getObjectIdentity(&object);
+			audit_name = getObjectIdentity(&object, false);
 
 			sepgsql_avc_check_perms(&object,
 									SEPG_CLASS_DB_COLUMN,
@@ -545,7 +545,7 @@ sepgsql_relation_relabel(Oid relOid, const char *seclabel)
 	object.classId = RelationRelationId;
 	object.objectId = relOid;
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	/*
 	 * check db_xxx:{setattr relabelfrom} permission
@@ -656,7 +656,7 @@ sepgsql_relation_setattr(Oid relOid)
 	object.classId = RelationRelationId;
 	object.objectId = relOid;
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	sepgsql_avc_check_perms(&object,
 							tclass,
diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c
index bc15a36a45..eca5350260 100644
--- a/contrib/sepgsql/schema.c
+++ b/contrib/sepgsql/schema.c
@@ -124,7 +124,7 @@ sepgsql_schema_drop(Oid namespaceId)
 	object.classId = NamespaceRelationId;
 	object.objectId = namespaceId;
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	sepgsql_avc_check_perms(&object,
 							SEPG_CLASS_DB_SCHEMA,
@@ -149,7 +149,7 @@ sepgsql_schema_relabel(Oid namespaceId, const char *seclabel)
 	object.classId = NamespaceRelationId;
 	object.objectId = namespaceId;
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	/*
 	 * check db_schema:{setattr relabelfrom} permission
@@ -187,7 +187,7 @@ check_schema_perms(Oid namespaceId, uint32 required, bool abort_on_violation)
 	object.classId = NamespaceRelationId;
 	object.objectId = namespaceId;
 	object.objectSubId = 0;
-	audit_name = getObjectIdentity(&object);
+	audit_name = getObjectIdentity(&object, false);
 
 	result = sepgsql_avc_check_perms(&object,
 									 SEPG_CLASS_DB_SCHEMA,
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 2f59af25a6..3442158350 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17618,7 +17618,8 @@ SELECT collation for ('foo' COLLATE "de_DE");
    This description is intended to be human-readable, and might be translated,
    depending on server configuration.
    This is useful to determine the identity of an object as stored in the
-   <structname>pg_depend</structname> catalog.
+   <structname>pg_depend</structname> catalog. This function returns
+   <literal>NULL</literal> values for undefined objects.
   </para>
 
   <para>
@@ -17634,7 +17635,8 @@ SELECT collation for ('foo' COLLATE "de_DE");
    identifier of the object, otherwise <literal>NULL</literal>;
    <parameter>identity</parameter> is the complete object identity, with the precise format
    depending on object type, and each part within the format being
-   schema-qualified and quoted as necessary.
+   schema-qualified and quoted as necessary. Undefined objects are identified
+   with <literal>NULL</literal> values.
   </para>
 
   <para>
@@ -17649,6 +17651,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
    <function>pg_get_object_address</function> to obtain the internal address
    of the object.
    This function is the inverse of <function>pg_get_object_address</function>.
+   Undefined objects are identified with <literal>NULL</literal> values.
   </para>
 
   <para>
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index b7e39af7a2..4afbdf6d99 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -608,11 +608,11 @@ findDependentObjects(const ObjectAddress *object,
 						ReleaseDeletionLock(object);
 						return;
 					}
-					otherObjDesc = getObjectDescription(&otherObject);
+					otherObjDesc = getObjectDescription(&otherObject, false);
 					ereport(ERROR,
 							(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
 							 errmsg("cannot drop %s because %s requires it",
-									getObjectDescription(object),
+									getObjectDescription(object, false),
 									otherObjDesc),
 							 errhint("You can drop %s instead.",
 									 otherObjDesc)));
@@ -692,11 +692,11 @@ findDependentObjects(const ObjectAddress *object,
 				 * the depender fields...
 				 */
 				elog(ERROR, "incorrect use of PIN dependency with %s",
-					 getObjectDescription(object));
+					 getObjectDescription(object, false));
 				break;
 			default:
 				elog(ERROR, "unrecognized dependency type '%c' for %s",
-					 foundDep->deptype, getObjectDescription(object));
+					 foundDep->deptype, getObjectDescription(object, false));
 				break;
 		}
 	}
@@ -788,12 +788,12 @@ findDependentObjects(const ObjectAddress *object,
 				ereport(ERROR,
 						(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
 						 errmsg("cannot drop %s because it is required by the database system",
-								getObjectDescription(object))));
+								getObjectDescription(object, false))));
 				subflags = 0;	/* keep compiler quiet */
 				break;
 			default:
 				elog(ERROR, "unrecognized dependency type '%c' for %s",
-					 foundDep->deptype, getObjectDescription(object));
+					 foundDep->deptype, getObjectDescription(object, false));
 				subflags = 0;	/* keep compiler quiet */
 				break;
 		}
@@ -887,7 +887,7 @@ reportDependentObjects(const ObjectAddresses *targetObjects,
 		if (extra->flags & DEPFLAG_ORIGINAL)
 			continue;
 
-		objDesc = getObjectDescription(obj);
+		objDesc = getObjectDescription(obj, false);
 
 		/*
 		 * If, at any stage of the recursive search, we reached the object via
@@ -910,7 +910,8 @@ reportDependentObjects(const ObjectAddresses *targetObjects,
 		}
 		else if (behavior == DROP_RESTRICT)
 		{
-			char	   *otherDesc = getObjectDescription(&extra->dependee);
+			char	   *otherDesc = getObjectDescription(&extra->dependee,
+														 false);
 
 			if (numReportedClient < MAX_REPORTED_DEPS)
 			{
@@ -968,7 +969,7 @@ reportDependentObjects(const ObjectAddresses *targetObjects,
 			ereport(ERROR,
 					(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
 					 errmsg("cannot drop %s because other objects depend on it",
-							getObjectDescription(origObject)),
+							getObjectDescription(origObject, false)),
 					 errdetail("%s", clientdetail.data),
 					 errdetail_log("%s", logdetail.data),
 					 errhint("Use DROP ... CASCADE to drop the dependent objects too.")));
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 4925601a24..304525eeb3 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -754,14 +754,20 @@ static ObjectAddress get_object_address_defacl(List *object,
 						  bool missing_ok);
 static const ObjectPropertyType *get_object_property_data(Oid class_id);
 
-static void getRelationDescription(StringInfo buffer, Oid relid);
-static void getOpFamilyDescription(StringInfo buffer, Oid opfid);
+static void getRelationDescription(StringInfo buffer, Oid relid,
+								   bool missing_ok);
+static void getOpFamilyDescription(StringInfo buffer, Oid opfid,
+								   bool missing_ok);
 static void getRelationTypeDescription(StringInfo buffer, Oid relid,
-						   int32 objectSubId);
-static void getProcedureTypeDescription(StringInfo buffer, Oid procid);
-static void getConstraintTypeDescription(StringInfo buffer, Oid constroid);
-static void getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **object);
-static void getRelationIdentity(StringInfo buffer, Oid relid, List **object);
+									   int32 objectSubId, bool missing_ok);
+static void getProcedureTypeDescription(StringInfo buffer, Oid procid,
+										bool missing_ok);
+static void getConstraintTypeDescription(StringInfo buffer, Oid constroid,
+										 bool missing_ok);
+static void getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **object,
+								bool missing_ok);
+static void getRelationIdentity(StringInfo buffer, Oid relid, List **object,
+								bool missing_ok);
 
 /*
  * Translate an object name and arguments (as passed by the parser) to an
@@ -1630,7 +1636,7 @@ get_object_address_opf_member(ObjectType objtype,
 										membernum,
 										TypeNameToString(typenames[0]),
 										TypeNameToString(typenames[1]),
-										getObjectDescription(&famaddr))));
+										getObjectDescription(&famaddr, false))));
 				}
 				else
 				{
@@ -1661,7 +1667,7 @@ get_object_address_opf_member(ObjectType objtype,
 										membernum,
 										TypeNameToString(typenames[0]),
 										TypeNameToString(typenames[1]),
-										getObjectDescription(&famaddr))));
+										getObjectDescription(&famaddr, false))));
 				}
 				else
 				{
@@ -2669,10 +2675,12 @@ get_catalog_object_by_oid(Relation catalog, Oid objectId)
 /*
  * getObjectDescription: build an object description for messages
  *
- * The result is a palloc'd string.
+ * The result is a palloc'd string. In the event of an undefined object,
+ * NULL is returned if missing_ok is true. If missing_ok is false an
+ * error is generated instead.
  */
 char *
-getObjectDescription(const ObjectAddress *object)
+getObjectDescription(const ObjectAddress *object, bool missing_ok)
 {
 	StringInfoData buffer;
 
@@ -2681,22 +2689,28 @@ getObjectDescription(const ObjectAddress *object)
 	switch (getObjectClass(object))
 	{
 		case OCLASS_CLASS:
-			getRelationDescription(&buffer, object->objectId);
+			getRelationDescription(&buffer, object->objectId, missing_ok);
 			if (object->objectSubId != 0)
-				appendStringInfo(&buffer, _(" column %s"),
-								 get_attname(object->objectId,
-											 object->objectSubId,
-											 false));
+			{
+				char *attname = get_attname(object->objectId,
+											object->objectSubId,
+											missing_ok);
+				if (attname)
+					appendStringInfo(&buffer, _(" column %s"), attname);
+			}
 			break;
 
 		case OCLASS_PROC:
+			/* returns numerical OID for undefined function */
 			appendStringInfo(&buffer, _("function %s"),
 							 format_procedure(object->objectId));
 			break;
 
 		case OCLASS_TYPE:
+			/* returns "???" for undefined type */
 			appendStringInfo(&buffer, _("type %s"),
-							 format_type_be(object->objectId));
+							 format_type_extended(object->objectId, -1,
+												  FORMAT_TYPE_ALLOW_INVALID));
 			break;
 
 		case OCLASS_CAST:
@@ -2719,15 +2733,19 @@ getObjectDescription(const ObjectAddress *object)
 
 				tup = systable_getnext(rcscan);
 
-				if (!HeapTupleIsValid(tup))
+				if (HeapTupleIsValid(tup))
+				{
+					castForm = (Form_pg_cast) GETSTRUCT(tup);
+
+					appendStringInfo(&buffer, _("cast from %s to %s"),
+									 format_type_be(castForm->castsource),
+									 format_type_be(castForm->casttarget));
+				}
+				else if (!missing_ok)
+				{
 					elog(ERROR, "could not find tuple for cast %u",
 						 object->objectId);
-
-				castForm = (Form_pg_cast) GETSTRUCT(tup);
-
-				appendStringInfo(&buffer, _("cast from %s to %s"),
-								 format_type_be(castForm->castsource),
-								 format_type_be(castForm->casttarget));
+				}
 
 				systable_endscan(rcscan);
 				heap_close(castDesc, AccessShareLock);
@@ -2741,13 +2759,18 @@ getObjectDescription(const ObjectAddress *object)
 
 				collTup = SearchSysCache1(COLLOID,
 										  ObjectIdGetDatum(object->objectId));
-				if (!HeapTupleIsValid(collTup))
+				if (HeapTupleIsValid(collTup))
+				{
+					coll = (Form_pg_collation) GETSTRUCT(collTup);
+					appendStringInfo(&buffer, _("collation %s"),
+									 NameStr(coll->collname));
+					ReleaseSysCache(collTup);
+				}
+				else if (!missing_ok)
+				{
 					elog(ERROR, "cache lookup failed for collation %u",
 						 object->objectId);
-				coll = (Form_pg_collation) GETSTRUCT(collTup);
-				appendStringInfo(&buffer, _("collation %s"),
-								 NameStr(coll->collname));
-				ReleaseSysCache(collTup);
+				}
 				break;
 			}
 
@@ -2759,8 +2782,13 @@ getObjectDescription(const ObjectAddress *object)
 				conTup = SearchSysCache1(CONSTROID,
 										 ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(conTup))
-					elog(ERROR, "cache lookup failed for constraint %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for constraint %u",
+							 object->objectId);
+					break;
+				}
+
 				con = (Form_pg_constraint) GETSTRUCT(conTup);
 
 				if (OidIsValid(con->conrelid))
@@ -2768,7 +2796,7 @@ getObjectDescription(const ObjectAddress *object)
 					StringInfoData rel;
 
 					initStringInfo(&rel);
-					getRelationDescription(&rel, con->conrelid);
+					getRelationDescription(&rel, con->conrelid, false);
 					appendStringInfo(&buffer, _("constraint %s on %s"),
 									 NameStr(con->conname), rel.data);
 					pfree(rel.data);
@@ -2790,8 +2818,12 @@ getObjectDescription(const ObjectAddress *object)
 				conTup = SearchSysCache1(CONVOID,
 										 ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(conTup))
-					elog(ERROR, "cache lookup failed for conversion %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for conversion %u",
+							 object->objectId);
+					break;
+				}
 				appendStringInfo(&buffer, _("conversion %s"),
 								 NameStr(((Form_pg_conversion) GETSTRUCT(conTup))->conname));
 				ReleaseSysCache(conTup);
@@ -2819,18 +2851,22 @@ getObjectDescription(const ObjectAddress *object)
 
 				tup = systable_getnext(adscan);
 
-				if (!HeapTupleIsValid(tup))
+				if (HeapTupleIsValid(tup))
+				{
+					attrdef = (Form_pg_attrdef) GETSTRUCT(tup);
+
+					colobject.classId = RelationRelationId;
+					colobject.objectId = attrdef->adrelid;
+					colobject.objectSubId = attrdef->adnum;
+
+					appendStringInfo(&buffer, _("default for %s"),
+									 getObjectDescription(&colobject, false));
+				}
+				else if (!missing_ok)
+				{
 					elog(ERROR, "could not find tuple for attrdef %u",
 						 object->objectId);
-
-				attrdef = (Form_pg_attrdef) GETSTRUCT(tup);
-
-				colobject.classId = RelationRelationId;
-				colobject.objectId = attrdef->adrelid;
-				colobject.objectSubId = attrdef->adnum;
-
-				appendStringInfo(&buffer, _("default for %s"),
-								 getObjectDescription(&colobject));
+				}
 
 				systable_endscan(adscan);
 				heap_close(attrdefDesc, AccessShareLock);
@@ -2838,9 +2874,15 @@ getObjectDescription(const ObjectAddress *object)
 			}
 
 		case OCLASS_LANGUAGE:
-			appendStringInfo(&buffer, _("language %s"),
-							 get_language_name(object->objectId, false));
-			break;
+			{
+				char *langname = get_language_name(object->objectId,
+												   missing_ok);
+
+				if (langname)
+					appendStringInfo(&buffer, _("language %s"),
+									 get_language_name(object->objectId, false));
+				break;
+			}
 
 		case OCLASS_LARGEOBJECT:
 			appendStringInfo(&buffer, _("large object %u"),
@@ -2848,6 +2890,7 @@ getObjectDescription(const ObjectAddress *object)
 			break;
 
 		case OCLASS_OPERATOR:
+			/* returns numerical OID for undefined operator */
 			appendStringInfo(&buffer, _("operator %s"),
 							 format_operator(object->objectId));
 			break;
@@ -2863,8 +2906,13 @@ getObjectDescription(const ObjectAddress *object)
 				opcTup = SearchSysCache1(CLAOID,
 										 ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(opcTup))
-					elog(ERROR, "cache lookup failed for opclass %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for opclass %u",
+							 object->objectId);
+					break;
+				}
+
 				opcForm = (Form_pg_opclass) GETSTRUCT(opcTup);
 
 				amTup = SearchSysCache1(AMOID,
@@ -2891,7 +2939,7 @@ getObjectDescription(const ObjectAddress *object)
 			}
 
 		case OCLASS_OPFAMILY:
-			getOpFamilyDescription(&buffer, object->objectId);
+			getOpFamilyDescription(&buffer, object->objectId, missing_ok);
 			break;
 
 		case OCLASS_AM:
@@ -2901,8 +2949,13 @@ getObjectDescription(const ObjectAddress *object)
 				tup = SearchSysCache1(AMOID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for access method %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for access method %u",
+							 object->objectId);
+					break;
+				}
+
 				appendStringInfo(&buffer, _("access method %s"),
 								 NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
 				ReleaseSysCache(tup);
@@ -2931,28 +2984,32 @@ getObjectDescription(const ObjectAddress *object)
 
 				tup = systable_getnext(amscan);
 
-				if (!HeapTupleIsValid(tup))
+				if (HeapTupleIsValid(tup))
+				{
+					amopForm = (Form_pg_amop) GETSTRUCT(tup);
+
+					initStringInfo(&opfam);
+					getOpFamilyDescription(&opfam, amopForm->amopfamily, false);
+
+					/*------
+					   translator: %d is the operator strategy (a number), the
+					   first two %s's are data type names, the third %s is the
+					   description of the operator family, and the last %s is
+					   the textual form of the operator with arguments.  */
+					appendStringInfo(&buffer, _("operator %d (%s, %s) of %s: %s"),
+									 amopForm->amopstrategy,
+									 format_type_be(amopForm->amoplefttype),
+									 format_type_be(amopForm->amoprighttype),
+									 opfam.data,
+									 format_operator(amopForm->amopopr));
+
+					pfree(opfam.data);
+				}
+				else if (!missing_ok)
+				{
 					elog(ERROR, "could not find tuple for amop entry %u",
 						 object->objectId);
-
-				amopForm = (Form_pg_amop) GETSTRUCT(tup);
-
-				initStringInfo(&opfam);
-				getOpFamilyDescription(&opfam, amopForm->amopfamily);
-
-				/*------
-				   translator: %d is the operator strategy (a number), the
-				   first two %s's are data type names, the third %s is the
-				   description of the operator family, and the last %s is the
-				   textual form of the operator with arguments.  */
-				appendStringInfo(&buffer, _("operator %d (%s, %s) of %s: %s"),
-								 amopForm->amopstrategy,
-								 format_type_be(amopForm->amoplefttype),
-								 format_type_be(amopForm->amoprighttype),
-								 opfam.data,
-								 format_operator(amopForm->amopopr));
-
-				pfree(opfam.data);
+				}
 
 				systable_endscan(amscan);
 				heap_close(amopDesc, AccessShareLock);
@@ -2981,28 +3038,32 @@ getObjectDescription(const ObjectAddress *object)
 
 				tup = systable_getnext(amscan);
 
-				if (!HeapTupleIsValid(tup))
+				if (HeapTupleIsValid(tup))
+				{
+					amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
+
+					initStringInfo(&opfam);
+					getOpFamilyDescription(&opfam, amprocForm->amprocfamily, false);
+
+					/*------
+					   translator: %d is the function number, the first two
+					   %s's are data type names, the third %s is the description
+					   of the operator family, and the last %s is the textual
+					   form of the function with arguments.  */
+					appendStringInfo(&buffer, _("function %d (%s, %s) of %s: %s"),
+									 amprocForm->amprocnum,
+									 format_type_be(amprocForm->amproclefttype),
+									 format_type_be(amprocForm->amprocrighttype),
+									 opfam.data,
+									 format_procedure(amprocForm->amproc));
+
+					pfree(opfam.data);
+				}
+				else if (!missing_ok)
+				{
 					elog(ERROR, "could not find tuple for amproc entry %u",
 						 object->objectId);
-
-				amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
-
-				initStringInfo(&opfam);
-				getOpFamilyDescription(&opfam, amprocForm->amprocfamily);
-
-				/*------
-				   translator: %d is the function number, the first two %s's
-				   are data type names, the third %s is the description of the
-				   operator family, and the last %s is the textual form of the
-				   function with arguments.  */
-				appendStringInfo(&buffer, _("function %d (%s, %s) of %s: %s"),
-								 amprocForm->amprocnum,
-								 format_type_be(amprocForm->amproclefttype),
-								 format_type_be(amprocForm->amprocrighttype),
-								 opfam.data,
-								 format_procedure(amprocForm->amproc));
-
-				pfree(opfam.data);
+				}
 
 				systable_endscan(amscan);
 				heap_close(amprocDesc, AccessShareLock);
@@ -3029,15 +3090,19 @@ getObjectDescription(const ObjectAddress *object)
 
 				tup = systable_getnext(rcscan);
 
-				if (!HeapTupleIsValid(tup))
+				if (HeapTupleIsValid(tup))
+				{
+					rule = (Form_pg_rewrite) GETSTRUCT(tup);
+
+					appendStringInfo(&buffer, _("rule %s on "),
+									 NameStr(rule->rulename));
+					getRelationDescription(&buffer, rule->ev_class, false);
+				}
+				else if (!missing_ok)
+				{
 					elog(ERROR, "could not find tuple for rule %u",
 						 object->objectId);
-
-				rule = (Form_pg_rewrite) GETSTRUCT(tup);
-
-				appendStringInfo(&buffer, _("rule %s on "),
-								 NameStr(rule->rulename));
-				getRelationDescription(&buffer, rule->ev_class);
+				}
 
 				systable_endscan(rcscan);
 				heap_close(ruleDesc, AccessShareLock);
@@ -3064,15 +3129,19 @@ getObjectDescription(const ObjectAddress *object)
 
 				tup = systable_getnext(tgscan);
 
-				if (!HeapTupleIsValid(tup))
+				if (HeapTupleIsValid(tup))
+				{
+					trig = (Form_pg_trigger) GETSTRUCT(tup);
+
+					appendStringInfo(&buffer, _("trigger %s on "),
+									 NameStr(trig->tgname));
+					getRelationDescription(&buffer, trig->tgrelid, false);
+				}
+				else if (!missing_ok)
+				{
 					elog(ERROR, "could not find tuple for trigger %u",
 						 object->objectId);
-
-				trig = (Form_pg_trigger) GETSTRUCT(tup);
-
-				appendStringInfo(&buffer, _("trigger %s on "),
-								 NameStr(trig->tgname));
-				getRelationDescription(&buffer, trig->tgrelid);
+				}
 
 				systable_endscan(tgscan);
 				heap_close(trigDesc, AccessShareLock);
@@ -3085,8 +3154,12 @@ getObjectDescription(const ObjectAddress *object)
 
 				nspname = get_namespace_name(object->objectId);
 				if (!nspname)
-					elog(ERROR, "cache lookup failed for namespace %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for namespace %u",
+							 object->objectId);
+					break;
+				}
 				appendStringInfo(&buffer, _("schema %s"), nspname);
 				break;
 			}
@@ -3099,8 +3172,12 @@ getObjectDescription(const ObjectAddress *object)
 				stxTup = SearchSysCache1(STATEXTOID,
 										 ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(stxTup))
-					elog(ERROR, "could not find tuple for statistics object %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for statistics object %u",
+							 object->objectId);
+					break;
+				}
 
 				stxForm = (Form_pg_statistic_ext) GETSTRUCT(stxTup);
 
@@ -3118,8 +3195,12 @@ getObjectDescription(const ObjectAddress *object)
 				tup = SearchSysCache1(TSPARSEROID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for text search parser %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for text search parser %u",
+							 object->objectId);
+					break;
+				}
 				appendStringInfo(&buffer, _("text search parser %s"),
 								 NameStr(((Form_pg_ts_parser) GETSTRUCT(tup))->prsname));
 				ReleaseSysCache(tup);
@@ -3133,8 +3214,12 @@ getObjectDescription(const ObjectAddress *object)
 				tup = SearchSysCache1(TSDICTOID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for text search dictionary %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for text search dictionary %u",
+							 object->objectId);
+					break;
+				}
 				appendStringInfo(&buffer, _("text search dictionary %s"),
 								 NameStr(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname));
 				ReleaseSysCache(tup);
@@ -3148,8 +3233,12 @@ getObjectDescription(const ObjectAddress *object)
 				tup = SearchSysCache1(TSTEMPLATEOID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for text search template %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for text search template %u",
+							 object->objectId);
+					break;
+				}
 				appendStringInfo(&buffer, _("text search template %s"),
 								 NameStr(((Form_pg_ts_template) GETSTRUCT(tup))->tmplname));
 				ReleaseSysCache(tup);
@@ -3163,8 +3252,12 @@ getObjectDescription(const ObjectAddress *object)
 				tup = SearchSysCache1(TSCONFIGOID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for text search configuration %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for text search configuration %u",
+							 object->objectId);
+					break;
+				}
 				appendStringInfo(&buffer, _("text search configuration %s"),
 								 NameStr(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname));
 				ReleaseSysCache(tup);
@@ -3173,8 +3266,11 @@ getObjectDescription(const ObjectAddress *object)
 
 		case OCLASS_ROLE:
 			{
-				appendStringInfo(&buffer, _("role %s"),
-								 GetUserNameFromId(object->objectId, false));
+				char	  *username = GetUserNameFromId(object->objectId,
+														missing_ok);
+
+				if (username)
+					appendStringInfo(&buffer, _("role %s"), username);
 				break;
 			}
 
@@ -3184,8 +3280,12 @@ getObjectDescription(const ObjectAddress *object)
 
 				datname = get_database_name(object->objectId);
 				if (!datname)
-					elog(ERROR, "cache lookup failed for database %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for database %u",
+							 object->objectId);
+					break;
+				}
 				appendStringInfo(&buffer, _("database %s"), datname);
 				break;
 			}
@@ -3196,8 +3296,12 @@ getObjectDescription(const ObjectAddress *object)
 
 				tblspace = get_tablespace_name(object->objectId);
 				if (!tblspace)
-					elog(ERROR, "cache lookup failed for tablespace %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for tablespace %u",
+							 object->objectId);
+					break;
+				}
 				appendStringInfo(&buffer, _("tablespace %s"), tblspace);
 				break;
 			}
@@ -3206,8 +3310,9 @@ getObjectDescription(const ObjectAddress *object)
 			{
 				ForeignDataWrapper *fdw;
 
-				fdw = GetForeignDataWrapper(object->objectId, false);
-				appendStringInfo(&buffer, _("foreign-data wrapper %s"), fdw->fdwname);
+				fdw = GetForeignDataWrapper(object->objectId, missing_ok);
+				if (fdw)
+					appendStringInfo(&buffer, _("foreign-data wrapper %s"), fdw->fdwname);
 				break;
 			}
 
@@ -3215,8 +3320,9 @@ getObjectDescription(const ObjectAddress *object)
 			{
 				ForeignServer *srv;
 
-				srv = GetForeignServer(object->objectId, false);
-				appendStringInfo(&buffer, _("server %s"), srv->servername);
+				srv = GetForeignServer(object->objectId, missing_ok);
+				if (srv)
+					appendStringInfo(&buffer, _("server %s"), srv->servername);
 				break;
 			}
 
@@ -3231,8 +3337,13 @@ getObjectDescription(const ObjectAddress *object)
 				tup = SearchSysCache1(USERMAPPINGOID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for user mapping %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for user mapping %u",
+							 object->objectId);
+					break;
+				}
+
 				umform = (Form_pg_user_mapping) GETSTRUCT(tup);
 				useid = umform->umuser;
 				srv = GetForeignServer(umform->umserver, false);
@@ -3270,8 +3381,15 @@ getObjectDescription(const ObjectAddress *object)
 				tup = systable_getnext(rcscan);
 
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "could not find tuple for default ACL %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "could not find tuple for default ACL %u",
+							 object->objectId);
+
+					systable_endscan(rcscan);
+					heap_close(defaclrel, AccessShareLock);
+					break;
+				}
 
 				defacl = (Form_pg_default_acl) GETSTRUCT(tup);
 
@@ -3328,8 +3446,12 @@ getObjectDescription(const ObjectAddress *object)
 
 				extname = get_extension_name(object->objectId);
 				if (!extname)
-					elog(ERROR, "cache lookup failed for extension %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for extension %u",
+							 object->objectId);
+					break;
+				}
 				appendStringInfo(&buffer, _("extension %s"), extname);
 				break;
 			}
@@ -3341,8 +3463,12 @@ getObjectDescription(const ObjectAddress *object)
 				tup = SearchSysCache1(EVENTTRIGGEROID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for event trigger %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for event trigger %u",
+							 object->objectId);
+					break;
+				}
 				appendStringInfo(&buffer, _("event trigger %s"),
 								 NameStr(((Form_pg_event_trigger) GETSTRUCT(tup))->evtname));
 				ReleaseSysCache(tup);
@@ -3369,15 +3495,20 @@ getObjectDescription(const ObjectAddress *object)
 
 				tuple = systable_getnext(sscan);
 
-				if (!HeapTupleIsValid(tuple))
+				if (HeapTupleIsValid(tuple))
+				{
+					form_policy = (Form_pg_policy) GETSTRUCT(tuple);
+
+					appendStringInfo(&buffer, _("policy %s on "),
+									 NameStr(form_policy->polname));
+					getRelationDescription(&buffer, form_policy->polrelid,
+										   false);
+				}
+				else if (!missing_ok)
+				{
 					elog(ERROR, "could not find tuple for policy %u",
 						 object->objectId);
-
-				form_policy = (Form_pg_policy) GETSTRUCT(tuple);
-
-				appendStringInfo(&buffer, _("policy %s on "),
-								 NameStr(form_policy->polname));
-				getRelationDescription(&buffer, form_policy->polrelid);
+				}
 
 				systable_endscan(sscan);
 				heap_close(policy_rel, AccessShareLock);
@@ -3386,9 +3517,11 @@ getObjectDescription(const ObjectAddress *object)
 
 		case OCLASS_PUBLICATION:
 			{
-				appendStringInfo(&buffer, _("publication %s"),
-								 get_publication_name(object->objectId,
-													  false));
+				char *pubname = get_publication_name(object->objectId,
+													 missing_ok);
+
+				if (pubname)
+					appendStringInfo(&buffer, _("publication %s"), pubname);
 				break;
 			}
 
@@ -3401,8 +3534,12 @@ getObjectDescription(const ObjectAddress *object)
 				tup = SearchSysCache1(PUBLICATIONREL,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for publication table %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for publication table %u",
+							 object->objectId);
+					break;
+				}
 
 				prform = (Form_pg_publication_rel) GETSTRUCT(tup);
 				pubname = get_publication_name(prform->prpubid, false);
@@ -3415,8 +3552,10 @@ getObjectDescription(const ObjectAddress *object)
 
 		case OCLASS_SUBSCRIPTION:
 			{
-				appendStringInfo(&buffer, _("subscription %s"),
-								 get_subscription_name(object->objectId, false));
+				char *subname = get_subscription_name(object->objectId, missing_ok);
+
+				if (subname)
+					appendStringInfo(&buffer, _("subscription %s"), subname);
 				break;
 			}
 
@@ -3428,8 +3567,12 @@ getObjectDescription(const ObjectAddress *object)
 				trfTup = SearchSysCache1(TRFOID,
 										 ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(trfTup))
-					elog(ERROR, "could not find tuple for transform %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "could not find tuple for transform %u",
+							 object->objectId);
+					break;
+				}
 
 				trfForm = (Form_pg_transform) GETSTRUCT(trfTup);
 
@@ -3447,6 +3590,10 @@ getObjectDescription(const ObjectAddress *object)
 			 */
 	}
 
+	/* an empty buffer is equivalent to no object found */
+	if (buffer.len == 0)
+		return NULL;
+
 	return buffer.data;
 }
 
@@ -3462,14 +3609,14 @@ getObjectDescriptionOids(Oid classid, Oid objid)
 	address.objectId = objid;
 	address.objectSubId = 0;
 
-	return getObjectDescription(&address);
+	return getObjectDescription(&address, false);
 }
 
 /*
  * subroutine for getObjectDescription: describe a relation
  */
 static void
-getRelationDescription(StringInfo buffer, Oid relid)
+getRelationDescription(StringInfo buffer, Oid relid, bool missing_ok)
 {
 	HeapTuple	relTup;
 	Form_pg_class relForm;
@@ -3479,7 +3626,11 @@ getRelationDescription(StringInfo buffer, Oid relid)
 	relTup = SearchSysCache1(RELOID,
 							 ObjectIdGetDatum(relid));
 	if (!HeapTupleIsValid(relTup))
-		elog(ERROR, "cache lookup failed for relation %u", relid);
+	{
+		if (!missing_ok)
+			elog(ERROR, "cache lookup failed for relation %u", relid);
+		return;
+	}
 	relForm = (Form_pg_class) GETSTRUCT(relTup);
 
 	/* Qualify the name if not visible in search path */
@@ -3540,7 +3691,7 @@ getRelationDescription(StringInfo buffer, Oid relid)
  * subroutine for getObjectDescription: describe an operator family
  */
 static void
-getOpFamilyDescription(StringInfo buffer, Oid opfid)
+getOpFamilyDescription(StringInfo buffer, Oid opfid, bool missing_ok)
 {
 	HeapTuple	opfTup;
 	Form_pg_opfamily opfForm;
@@ -3550,7 +3701,11 @@ getOpFamilyDescription(StringInfo buffer, Oid opfid)
 
 	opfTup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfid));
 	if (!HeapTupleIsValid(opfTup))
-		elog(ERROR, "cache lookup failed for opfamily %u", opfid);
+	{
+		if (!missing_ok)
+			elog(ERROR, "cache lookup failed for opfamily %u", opfid);
+		return;
+	}
 	opfForm = (Form_pg_opfamily) GETSTRUCT(opfTup);
 
 	amTup = SearchSysCache1(AMOID, ObjectIdGetDatum(opfForm->opfmethod));
@@ -3594,7 +3749,11 @@ pg_describe_object(PG_FUNCTION_ARGS)
 	address.objectId = objid;
 	address.objectSubId = objsubid;
 
-	description = getObjectDescription(&address);
+	description = getObjectDescription(&address, true);
+
+	if (description == NULL)
+		PG_RETURN_NULL();
+
 	PG_RETURN_TEXT_P(cstring_to_text(description));
 }
 
@@ -3609,6 +3768,8 @@ pg_identify_object(PG_FUNCTION_ARGS)
 	int32		objsubid = PG_GETARG_INT32(2);
 	Oid			schema_oid = InvalidOid;
 	const char *objname = NULL;
+	char	   *objtype;
+	char	   *objidentity;
 	ObjectAddress address;
 	Datum		values[4];
 	bool		nulls[4];
@@ -3682,8 +3843,14 @@ pg_identify_object(PG_FUNCTION_ARGS)
 	}
 
 	/* object type */
-	values[0] = CStringGetTextDatum(getObjectTypeDescription(&address));
-	nulls[0] = false;
+	objtype = getObjectTypeDescription(&address, true);
+	if (objtype)
+	{
+		values[0] = CStringGetTextDatum(getObjectTypeDescription(&address, false));
+		nulls[0] = false;
+	}
+	else
+		nulls[0] = true;
 
 	/* schema name */
 	if (OidIsValid(schema_oid))
@@ -3706,8 +3873,14 @@ pg_identify_object(PG_FUNCTION_ARGS)
 		nulls[2] = true;
 
 	/* object identity */
-	values[3] = CStringGetTextDatum(getObjectIdentity(&address));
-	nulls[3] = false;
+	objidentity = getObjectIdentity(&address, true);
+	if (objidentity)
+	{
+		values[3] = CStringGetTextDatum(getObjectIdentity(&address, true));
+		nulls[3] = false;
+	}
+	else
+		nulls[3] = true;
 
 	htup = heap_form_tuple(tupdesc, values, nulls);
 
@@ -3724,6 +3897,7 @@ pg_identify_object_as_address(PG_FUNCTION_ARGS)
 	Oid			objid = PG_GETARG_OID(1);
 	int32		objsubid = PG_GETARG_INT32(2);
 	ObjectAddress address;
+	char	   *type;
 	char	   *identity;
 	List	   *names;
 	List	   *args;
@@ -3751,23 +3925,37 @@ pg_identify_object_as_address(PG_FUNCTION_ARGS)
 	tupdesc = BlessTupleDesc(tupdesc);
 
 	/* object type */
-	values[0] = CStringGetTextDatum(getObjectTypeDescription(&address));
-	nulls[0] = false;
+	type = getObjectTypeDescription(&address, true);
+	if (type)
+	{
+		values[0] = CStringGetTextDatum(getObjectTypeDescription(&address, false));
+		nulls[0] = false;
+	}
+	else
+		nulls[0] = true;
 
 	/* object identity */
-	identity = getObjectIdentityParts(&address, &names, &args);
-	pfree(identity);
-
-	/* object_names */
-	values[1] = PointerGetDatum(strlist_to_textarray(names));
-	nulls[1] = false;
-
-	/* object_args */
-	if (args)
-		values[2] = PointerGetDatum(strlist_to_textarray(args));
+	identity = getObjectIdentityParts(&address, &names, &args, true);
+	if (identity == NULL)
+	{
+		nulls[1] = true;
+		nulls[2] = true;
+	}
 	else
-		values[2] = PointerGetDatum(construct_empty_array(TEXTOID));
-	nulls[2] = false;
+	{
+		pfree(identity);
+
+		/* object_names */
+		values[1] = PointerGetDatum(strlist_to_textarray(names));
+		nulls[1] = false;
+
+		/* object_args */
+		if (args)
+			values[2] = PointerGetDatum(strlist_to_textarray(args));
+		else
+			values[2] = PointerGetDatum(construct_empty_array(TEXTOID));
+		nulls[2] = false;
+	}
 
 	htup = heap_form_tuple(tupdesc, values, nulls);
 
@@ -3781,7 +3969,7 @@ pg_identify_object_as_address(PG_FUNCTION_ARGS)
  * Keep ObjectTypeMap in sync with this.
  */
 char *
-getObjectTypeDescription(const ObjectAddress *object)
+getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
 {
 	StringInfoData buffer;
 
@@ -3791,11 +3979,13 @@ getObjectTypeDescription(const ObjectAddress *object)
 	{
 		case OCLASS_CLASS:
 			getRelationTypeDescription(&buffer, object->objectId,
-									   object->objectSubId);
+									   object->objectSubId,
+									   missing_ok);
 			break;
 
 		case OCLASS_PROC:
-			getProcedureTypeDescription(&buffer, object->objectId);
+			getProcedureTypeDescription(&buffer, object->objectId,
+										missing_ok);
 			break;
 
 		case OCLASS_TYPE:
@@ -3811,7 +4001,8 @@ getObjectTypeDescription(const ObjectAddress *object)
 			break;
 
 		case OCLASS_CONSTRAINT:
-			getConstraintTypeDescription(&buffer, object->objectId);
+			getConstraintTypeDescription(&buffer, object->objectId,
+										 missing_ok);
 			break;
 
 		case OCLASS_CONVERSION:
@@ -3948,6 +4139,10 @@ getObjectTypeDescription(const ObjectAddress *object)
 			 */
 	}
 
+	/* an empty string is equivalent to no object found */
+	if (buffer.len == 0)
+		return NULL;
+
 	return buffer.data;
 }
 
@@ -3955,7 +4150,8 @@ getObjectTypeDescription(const ObjectAddress *object)
  * subroutine for getObjectTypeDescription: describe a relation type
  */
 static void
-getRelationTypeDescription(StringInfo buffer, Oid relid, int32 objectSubId)
+getRelationTypeDescription(StringInfo buffer, Oid relid, int32 objectSubId,
+						   bool missing_ok)
 {
 	HeapTuple	relTup;
 	Form_pg_class relForm;
@@ -3963,7 +4159,12 @@ getRelationTypeDescription(StringInfo buffer, Oid relid, int32 objectSubId)
 	relTup = SearchSysCache1(RELOID,
 							 ObjectIdGetDatum(relid));
 	if (!HeapTupleIsValid(relTup))
-		elog(ERROR, "cache lookup failed for relation %u", relid);
+	{
+		if (!missing_ok)
+			elog(ERROR, "cache lookup failed for relation %u", relid);
+		Assert(buffer->len == 0);
+		return;
+	}
 	relForm = (Form_pg_class) GETSTRUCT(relTup);
 
 	switch (relForm->relkind)
@@ -4010,7 +4211,7 @@ getRelationTypeDescription(StringInfo buffer, Oid relid, int32 objectSubId)
  * subroutine for getObjectTypeDescription: describe a constraint type
  */
 static void
-getConstraintTypeDescription(StringInfo buffer, Oid constroid)
+getConstraintTypeDescription(StringInfo buffer, Oid constroid, bool missing_ok)
 {
 	Relation	constrRel;
 	HeapTuple	constrTup;
@@ -4019,7 +4220,13 @@ getConstraintTypeDescription(StringInfo buffer, Oid constroid)
 	constrRel = heap_open(ConstraintRelationId, AccessShareLock);
 	constrTup = get_catalog_object_by_oid(constrRel, constroid);
 	if (!HeapTupleIsValid(constrTup))
-		elog(ERROR, "cache lookup failed for constraint %u", constroid);
+	{
+		if (!missing_ok)
+			elog(ERROR, "cache lookup failed for constraint %u", constroid);
+
+		heap_close(constrRel, AccessShareLock);
+		return;
+	}
 
 	constrForm = (Form_pg_constraint) GETSTRUCT(constrTup);
 
@@ -4037,7 +4244,8 @@ getConstraintTypeDescription(StringInfo buffer, Oid constroid)
  * subroutine for getObjectTypeDescription: describe a procedure type
  */
 static void
-getProcedureTypeDescription(StringInfo buffer, Oid procid)
+getProcedureTypeDescription(StringInfo buffer, Oid procid,
+							bool missing_ok)
 {
 	HeapTuple	procTup;
 	Form_pg_proc procForm;
@@ -4045,7 +4253,11 @@ getProcedureTypeDescription(StringInfo buffer, Oid procid)
 	procTup = SearchSysCache1(PROCOID,
 							  ObjectIdGetDatum(procid));
 	if (!HeapTupleIsValid(procTup))
-		elog(ERROR, "cache lookup failed for procedure %u", procid);
+	{
+		if (!missing_ok)
+			elog(ERROR, "cache lookup failed for procedure %u", procid);
+		return;
+	}
 	procForm = (Form_pg_proc) GETSTRUCT(procTup);
 
 	if (procForm->prokind == PROKIND_AGGREGATE)
@@ -4062,12 +4274,13 @@ getProcedureTypeDescription(StringInfo buffer, Oid procid)
  * Obtain a given object's identity, as a palloc'ed string.
  *
  * This is for machine consumption, so it's not translated.  All elements are
- * schema-qualified when appropriate.
+ * schema-qualified when appropriate. Returns NULL if the object defined
+ * cannot be found.
  */
 char *
-getObjectIdentity(const ObjectAddress *object)
+getObjectIdentity(const ObjectAddress *object, bool missing_ok)
 {
-	return getObjectIdentityParts(object, NULL, NULL);
+	return getObjectIdentityParts(object, NULL, NULL, missing_ok);
 }
 
 /*
@@ -4076,11 +4289,13 @@ getObjectIdentity(const ObjectAddress *object)
  * There are two sets of return values: the identity itself as a palloc'd
  * string is returned.  objname and objargs, if not NULL, are output parameters
  * that receive lists of C-strings that are useful to give back to
- * get_object_address() to reconstruct the ObjectAddress.
+ * get_object_address() to reconstruct the ObjectAddress. Returns NULL if
+ * the object defined cannot be found.
  */
 char *
 getObjectIdentityParts(const ObjectAddress *object,
-					   List **objname, List **objargs)
+					   List **objname, List **objargs,
+					   bool missing_ok)
 {
 	StringInfoData buffer;
 
@@ -4102,31 +4317,41 @@ getObjectIdentityParts(const ObjectAddress *object,
 	switch (getObjectClass(object))
 	{
 		case OCLASS_CLASS:
-			getRelationIdentity(&buffer, object->objectId, objname);
+			getRelationIdentity(&buffer, object->objectId, objname,
+								missing_ok);
+			if (objname && *objname == NIL)
+				break;
 			if (object->objectSubId != 0)
 			{
-				char	   *attr;
-
-				attr = get_attname(object->objectId, object->objectSubId,
-								   false);
-				appendStringInfo(&buffer, ".%s", quote_identifier(attr));
-				if (objname)
-					*objname = lappend(*objname, attr);
+				char	   *attr = get_attname(object->objectId,
+											   object->objectSubId,
+											   missing_ok);
+				if (attr)
+				{
+					appendStringInfo(&buffer, ".%s", quote_identifier(attr));
+					if (objname)
+						*objname = lappend(*objname, attr);
+				}
 			}
 			break;
 
 		case OCLASS_PROC:
+			/* returns numerical OID for undefined function */
 			appendStringInfoString(&buffer,
 								   format_procedure_qualified(object->objectId));
 			if (objname)
-				format_procedure_parts(object->objectId, objname, objargs);
+				format_procedure_parts(object->objectId, objname, objargs,
+									   missing_ok);
 			break;
 
 		case OCLASS_TYPE:
 			{
 				char	   *typeout;
 
-				typeout = format_type_be_qualified(object->objectId);
+				/* returns "???" for undefined type */
+				typeout = format_type_extended(object->objectId, -1,
+					FORMAT_TYPE_ALLOW_INVALID | FORMAT_TYPE_FORCE_QUALIFY);
+
 				appendStringInfoString(&buffer, typeout);
 				if (objname)
 					*objname = list_make1(typeout);
@@ -4144,8 +4369,14 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = get_catalog_object_by_oid(castRel, object->objectId);
 
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "could not find tuple for cast %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "could not find tuple for cast %u",
+							 object->objectId);
+
+					heap_close(castRel, AccessShareLock);
+					break;
+				}
 
 				castForm = (Form_pg_cast) GETSTRUCT(tup);
 
@@ -4172,8 +4403,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 				collTup = SearchSysCache1(COLLOID,
 										  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(collTup))
-					elog(ERROR, "cache lookup failed for collation %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for collation %u",
+							 object->objectId);
+					break;
+				}
 				coll = (Form_pg_collation) GETSTRUCT(collTup);
 				schema = get_namespace_name_or_temp(coll->collnamespace);
 				appendStringInfoString(&buffer,
@@ -4194,15 +4429,20 @@ getObjectIdentityParts(const ObjectAddress *object,
 				conTup = SearchSysCache1(CONSTROID,
 										 ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(conTup))
-					elog(ERROR, "cache lookup failed for constraint %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for constraint %u",
+							 object->objectId);
+					break;
+				}
 				con = (Form_pg_constraint) GETSTRUCT(conTup);
 
 				if (OidIsValid(con->conrelid))
 				{
 					appendStringInfo(&buffer, "%s on ",
 									 quote_identifier(NameStr(con->conname)));
-					getRelationIdentity(&buffer, con->conrelid, objname);
+					getRelationIdentity(&buffer, con->conrelid, objname,
+										false);
 					if (objname)
 						*objname = lappend(*objname, pstrdup(NameStr(con->conname)));
 				}
@@ -4217,7 +4457,8 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 					appendStringInfo(&buffer, "%s on %s",
 									 quote_identifier(NameStr(con->conname)),
-									 getObjectIdentityParts(&domain, objname, objargs));
+									 getObjectIdentityParts(&domain, objname,
+															objargs, false));
 
 					if (objname)
 						*objargs = lappend(*objargs, pstrdup(NameStr(con->conname)));
@@ -4236,8 +4477,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 				conTup = SearchSysCache1(CONVOID,
 										 ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(conTup))
-					elog(ERROR, "cache lookup failed for conversion %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for conversion %u",
+							 object->objectId);
+					break;
+				}
 				conForm = (Form_pg_conversion) GETSTRUCT(conTup);
 				schema = get_namespace_name_or_temp(conForm->connamespace);
 				appendStringInfoString(&buffer,
@@ -4272,19 +4517,24 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 				tup = systable_getnext(adscan);
 
-				if (!HeapTupleIsValid(tup))
+				if (HeapTupleIsValid(tup))
+				{
+					attrdef = (Form_pg_attrdef) GETSTRUCT(tup);
+
+					colobject.classId = RelationRelationId;
+					colobject.objectId = attrdef->adrelid;
+					colobject.objectSubId = attrdef->adnum;
+
+					appendStringInfo(&buffer, "for %s",
+									 getObjectIdentityParts(&colobject,
+															objname, objargs,
+															false));
+				}
+				else if (!missing_ok)
+				{
 					elog(ERROR, "could not find tuple for attrdef %u",
 						 object->objectId);
-
-				attrdef = (Form_pg_attrdef) GETSTRUCT(tup);
-
-				colobject.classId = RelationRelationId;
-				colobject.objectId = attrdef->adrelid;
-				colobject.objectSubId = attrdef->adnum;
-
-				appendStringInfo(&buffer, "for %s",
-								 getObjectIdentityParts(&colobject,
-														objname, objargs));
+				}
 
 				systable_endscan(adscan);
 				heap_close(attrdefDesc, AccessShareLock);
@@ -4299,8 +4549,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 				langTup = SearchSysCache1(LANGOID,
 										  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(langTup))
-					elog(ERROR, "cache lookup failed for language %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for language %u",
+							 object->objectId);
+					break;
+				}
 				langForm = (Form_pg_language) GETSTRUCT(langTup);
 				appendStringInfoString(&buffer,
 									   quote_identifier(NameStr(langForm->lanname)));
@@ -4317,10 +4571,11 @@ getObjectIdentityParts(const ObjectAddress *object,
 			break;
 
 		case OCLASS_OPERATOR:
+			/* returns numerical OID for undefined operator */
 			appendStringInfoString(&buffer,
 								   format_operator_qualified(object->objectId));
 			if (objname)
-				format_operator_parts(object->objectId, objname, objargs);
+				format_operator_parts(object->objectId, objname, objargs, missing_ok);
 			break;
 
 		case OCLASS_OPCLASS:
@@ -4334,8 +4589,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 				opcTup = SearchSysCache1(CLAOID,
 										 ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(opcTup))
-					elog(ERROR, "cache lookup failed for opclass %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for opclass %u",
+							 object->objectId);
+					break;
+				}
 				opcForm = (Form_pg_opclass) GETSTRUCT(opcTup);
 				schema = get_namespace_name_or_temp(opcForm->opcnamespace);
 
@@ -4361,7 +4620,8 @@ getObjectIdentityParts(const ObjectAddress *object,
 			}
 
 		case OCLASS_OPFAMILY:
-			getOpFamilyIdentity(&buffer, object->objectId, objname);
+			getOpFamilyIdentity(&buffer, object->objectId, objname,
+								missing_ok);
 			break;
 
 		case OCLASS_AM:
@@ -4370,8 +4630,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 				amname = get_am_name(object->objectId);
 				if (!amname)
-					elog(ERROR, "cache lookup failed for access method %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for access method %u",
+							 object->objectId);
+					break;
+				}
 				appendStringInfoString(&buffer, quote_identifier(amname));
 				if (objname)
 					*objname = list_make1(amname);
@@ -4403,13 +4667,21 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = systable_getnext(amscan);
 
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "could not find tuple for amop entry %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "could not find tuple for amop entry %u",
+							 object->objectId);
+
+					systable_endscan(amscan);
+					heap_close(amopDesc, AccessShareLock);
+					break;
+				}
 
 				amopForm = (Form_pg_amop) GETSTRUCT(tup);
 
 				initStringInfo(&opfam);
-				getOpFamilyIdentity(&opfam, amopForm->amopfamily, objname);
+				getOpFamilyIdentity(&opfam, amopForm->amopfamily, objname,
+									false);
 
 				ltype = format_type_be_qualified(amopForm->amoplefttype);
 				rtype = format_type_be_qualified(amopForm->amoprighttype);
@@ -4457,13 +4729,21 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = systable_getnext(amscan);
 
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "could not find tuple for amproc entry %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "could not find tuple for amproc entry %u",
+							 object->objectId);
+
+					systable_endscan(amscan);
+					heap_close(amprocDesc, AccessShareLock);
+					break;
+				}
 
 				amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
 
 				initStringInfo(&opfam);
-				getOpFamilyIdentity(&opfam, amprocForm->amprocfamily, objname);
+				getOpFamilyIdentity(&opfam, amprocForm->amprocfamily, objname,
+									false);
 
 				ltype = format_type_be_qualified(amprocForm->amproclefttype);
 				rtype = format_type_be_qualified(amprocForm->amprocrighttype);
@@ -4497,14 +4777,20 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = get_catalog_object_by_oid(ruleDesc, object->objectId);
 
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "could not find tuple for rule %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "could not find tuple for rule %u",
+							 object->objectId);
+
+					heap_close(ruleDesc, AccessShareLock);
+					break;
+				}
 
 				rule = (Form_pg_rewrite) GETSTRUCT(tup);
 
 				appendStringInfo(&buffer, "%s on ",
 								 quote_identifier(NameStr(rule->rulename)));
-				getRelationIdentity(&buffer, rule->ev_class, objname);
+				getRelationIdentity(&buffer, rule->ev_class, objname, false);
 				if (objname)
 					*objname = lappend(*objname, pstrdup(NameStr(rule->rulename)));
 
@@ -4523,14 +4809,20 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = get_catalog_object_by_oid(trigDesc, object->objectId);
 
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "could not find tuple for trigger %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "could not find tuple for trigger %u",
+							 object->objectId);
+
+					heap_close(trigDesc, AccessShareLock);
+					break;
+				}
 
 				trig = (Form_pg_trigger) GETSTRUCT(tup);
 
 				appendStringInfo(&buffer, "%s on ",
 								 quote_identifier(NameStr(trig->tgname)));
-				getRelationIdentity(&buffer, trig->tgrelid, objname);
+				getRelationIdentity(&buffer, trig->tgrelid, objname, false);
 				if (objname)
 					*objname = lappend(*objname, pstrdup(NameStr(trig->tgname)));
 
@@ -4544,8 +4836,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 				nspname = get_namespace_name_or_temp(object->objectId);
 				if (!nspname)
-					elog(ERROR, "cache lookup failed for namespace %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for namespace %u",
+							 object->objectId);
+					break;
+				}
 				appendStringInfoString(&buffer,
 									   quote_identifier(nspname));
 				if (objname)
@@ -4562,8 +4858,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = SearchSysCache1(STATEXTOID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for statistics object %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for statistics object %u",
+							 object->objectId);
+					break;
+				}
 				formStatistic = (Form_pg_statistic_ext) GETSTRUCT(tup);
 				schema = get_namespace_name_or_temp(formStatistic->stxnamespace);
 				appendStringInfoString(&buffer,
@@ -4585,8 +4885,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = SearchSysCache1(TSPARSEROID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for text search parser %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for text search parser %u",
+							 object->objectId);
+					break;
+				}
 				formParser = (Form_pg_ts_parser) GETSTRUCT(tup);
 				schema = get_namespace_name_or_temp(formParser->prsnamespace);
 				appendStringInfoString(&buffer,
@@ -4608,8 +4912,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = SearchSysCache1(TSDICTOID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for text search dictionary %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for text search dictionary %u",
+							 object->objectId);
+					break;
+				}
 				formDict = (Form_pg_ts_dict) GETSTRUCT(tup);
 				schema = get_namespace_name_or_temp(formDict->dictnamespace);
 				appendStringInfoString(&buffer,
@@ -4631,8 +4939,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = SearchSysCache1(TSTEMPLATEOID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for text search template %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for text search template %u",
+							 object->objectId);
+					break;
+				}
 				formTmpl = (Form_pg_ts_template) GETSTRUCT(tup);
 				schema = get_namespace_name_or_temp(formTmpl->tmplnamespace);
 				appendStringInfoString(&buffer,
@@ -4654,8 +4966,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = SearchSysCache1(TSCONFIGOID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for text search configuration %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for text search configuration %u",
+							 object->objectId);
+					break;
+				}
 				formCfg = (Form_pg_ts_config) GETSTRUCT(tup);
 				schema = get_namespace_name_or_temp(formCfg->cfgnamespace);
 				appendStringInfoString(&buffer,
@@ -4672,7 +4988,9 @@ getObjectIdentityParts(const ObjectAddress *object,
 			{
 				char	   *username;
 
-				username = GetUserNameFromId(object->objectId, false);
+				username = GetUserNameFromId(object->objectId, missing_ok);
+				if (!username)
+					break;
 				if (objname)
 					*objname = list_make1(username);
 				appendStringInfoString(&buffer,
@@ -4686,8 +5004,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 				datname = get_database_name(object->objectId);
 				if (!datname)
-					elog(ERROR, "cache lookup failed for database %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for database %u",
+							 object->objectId);
+					break;
+				}
 				if (objname)
 					*objname = list_make1(datname);
 				appendStringInfoString(&buffer,
@@ -4701,8 +5023,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 				tblspace = get_tablespace_name(object->objectId);
 				if (!tblspace)
-					elog(ERROR, "cache lookup failed for tablespace %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for tablespace %u",
+							 object->objectId);
+					break;
+				}
 				if (objname)
 					*objname = list_make1(tblspace);
 				appendStringInfoString(&buffer,
@@ -4714,10 +5040,13 @@ getObjectIdentityParts(const ObjectAddress *object,
 			{
 				ForeignDataWrapper *fdw;
 
-				fdw = GetForeignDataWrapper(object->objectId, false);
-				appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
-				if (objname)
-					*objname = list_make1(pstrdup(fdw->fdwname));
+				fdw = GetForeignDataWrapper(object->objectId, missing_ok);
+				if (fdw)
+				{
+					appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
+					if (objname)
+						*objname = list_make1(pstrdup(fdw->fdwname));
+				}
 				break;
 			}
 
@@ -4725,11 +5054,14 @@ getObjectIdentityParts(const ObjectAddress *object,
 			{
 				ForeignServer *srv;
 
-				srv = GetForeignServer(object->objectId, false);
-				appendStringInfoString(&buffer,
-									   quote_identifier(srv->servername));
-				if (objname)
-					*objname = list_make1(pstrdup(srv->servername));
+				srv = GetForeignServer(object->objectId, missing_ok);
+				if (srv)
+				{
+					appendStringInfoString(&buffer,
+										   quote_identifier(srv->servername));
+					if (objname)
+						*objname = list_make1(pstrdup(srv->servername));
+				}
 				break;
 			}
 
@@ -4744,8 +5076,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = SearchSysCache1(USERMAPPINGOID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for user mapping %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for user mapping %u",
+							 object->objectId);
+					break;
+				}
 				umform = (Form_pg_user_mapping) GETSTRUCT(tup);
 				useid = umform->umuser;
 				srv = GetForeignServer(umform->umserver, false);
@@ -4792,8 +5128,16 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = systable_getnext(rcscan);
 
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "could not find tuple for default ACL %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "could not find tuple for default ACL %u",
+							 object->objectId);
+
+					systable_endscan(rcscan);
+					heap_close(defaclrel, AccessShareLock);
+					break;
+
+				}
 
 				defacl = (Form_pg_default_acl) GETSTRUCT(tup);
 
@@ -4855,8 +5199,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 				extname = get_extension_name(object->objectId);
 				if (!extname)
-					elog(ERROR, "cache lookup failed for extension %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for extension %u",
+							 object->objectId);
+					break;
+				}
 				appendStringInfoString(&buffer, quote_identifier(extname));
 				if (objname)
 					*objname = list_make1(extname);
@@ -4875,8 +5223,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = SearchSysCache1(EVENTTRIGGEROID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for event trigger %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for event trigger %u",
+							 object->objectId);
+					break;
+				}
 				trigForm = (Form_pg_event_trigger) GETSTRUCT(tup);
 				appendStringInfoString(&buffer,
 									   quote_identifier(NameStr(trigForm->evtname)));
@@ -4895,14 +5247,20 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = get_catalog_object_by_oid(polDesc, object->objectId);
 
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "could not find tuple for policy %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "could not find tuple for policy %u",
+							 object->objectId);
+
+					heap_close(polDesc, AccessShareLock);
+					break;
+				}
 
 				policy = (Form_pg_policy) GETSTRUCT(tup);
 
 				appendStringInfo(&buffer, "%s on ",
 								 quote_identifier(NameStr(policy->polname)));
-				getRelationIdentity(&buffer, policy->polrelid, objname);
+				getRelationIdentity(&buffer, policy->polrelid, objname, false);
 				if (objname)
 					*objname = lappend(*objname, pstrdup(NameStr(policy->polname)));
 
@@ -4914,11 +5272,14 @@ getObjectIdentityParts(const ObjectAddress *object,
 			{
 				char	   *pubname;
 
-				pubname = get_publication_name(object->objectId, false);
-				appendStringInfoString(&buffer,
-									   quote_identifier(pubname));
-				if (objname)
-					*objname = list_make1(pubname);
+				pubname = get_publication_name(object->objectId, missing_ok);
+				if (pubname)
+				{
+					appendStringInfoString(&buffer,
+										   quote_identifier(pubname));
+					if (objname)
+						*objname = list_make1(pubname);
+				}
 				break;
 			}
 
@@ -4931,8 +5292,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = SearchSysCache1(PUBLICATIONREL,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "cache lookup failed for publication table %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for publication table %u",
+							 object->objectId);
+					break;
+				}
 
 				prform = (Form_pg_publication_rel) GETSTRUCT(tup);
 				pubname = get_publication_name(prform->prpubid, false);
@@ -4942,7 +5307,8 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 				if (objname)
 				{
-					getRelationIdentity(&buffer, prform->prrelid, objname);
+					getRelationIdentity(&buffer, prform->prrelid, objname,
+										false);
 					*objargs = list_make1(pubname);
 				}
 
@@ -4954,11 +5320,14 @@ getObjectIdentityParts(const ObjectAddress *object,
 			{
 				char	   *subname;
 
-				subname = get_subscription_name(object->objectId, false);
-				appendStringInfoString(&buffer,
-									   quote_identifier(subname));
-				if (objname)
-					*objname = list_make1(subname);
+				subname = get_subscription_name(object->objectId, missing_ok);
+				if (subname)
+				{
+					appendStringInfoString(&buffer,
+										   quote_identifier(subname));
+					if (objname)
+						*objname = list_make1(subname);
+				}
 				break;
 			}
 
@@ -4975,8 +5344,14 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = get_catalog_object_by_oid(transformDesc, object->objectId);
 
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "could not find tuple for transform %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "could not find tuple for transform %u",
+							 object->objectId);
+
+					heap_close(transformDesc, AccessShareLock);
+					break;
+				}
 
 				transform = (Form_pg_transform) GETSTRUCT(tup);
 
@@ -5002,20 +5377,20 @@ getObjectIdentityParts(const ObjectAddress *object,
 			 */
 	}
 
-	/*
-	 * If a get_object_address representation was requested, make sure we are
-	 * providing one.  We don't check objargs, because many of the cases above
-	 * leave it as NIL.
-	 */
-	if (objname && *objname == NIL)
-		elog(ERROR, "requested object address for unsupported object class %d: text result \"%s\"",
-			 (int) getObjectClass(object), buffer.data);
+	/* an empty string is equivalent to no object defined */
+	if (buffer.len == 0)
+	{
+		Assert((objname == NULL || *objname == NIL) &&
+			   (objargs == NULL || *objargs == NIL));
+		return NULL;
+	}
 
 	return buffer.data;
 }
 
 static void
-getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **object)
+getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **object,
+					bool missing_ok)
 {
 	HeapTuple	opfTup;
 	Form_pg_opfamily opfForm;
@@ -5025,7 +5400,11 @@ getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **object)
 
 	opfTup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfid));
 	if (!HeapTupleIsValid(opfTup))
-		elog(ERROR, "cache lookup failed for opfamily %u", opfid);
+	{
+		if (!missing_ok)
+			elog(ERROR, "cache lookup failed for opfamily %u", opfid);
+		return;
+	}
 	opfForm = (Form_pg_opfamily) GETSTRUCT(opfTup);
 
 	amTup = SearchSysCache1(AMOID, ObjectIdGetDatum(opfForm->opfmethod));
@@ -5054,7 +5433,8 @@ getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **object)
  * StringInfo.
  */
 static void
-getRelationIdentity(StringInfo buffer, Oid relid, List **object)
+getRelationIdentity(StringInfo buffer, Oid relid, List **object,
+					bool missing_ok)
 {
 	HeapTuple	relTup;
 	Form_pg_class relForm;
@@ -5063,7 +5443,14 @@ getRelationIdentity(StringInfo buffer, Oid relid, List **object)
 	relTup = SearchSysCache1(RELOID,
 							 ObjectIdGetDatum(relid));
 	if (!HeapTupleIsValid(relTup))
-		elog(ERROR, "cache lookup failed for relation %u", relid);
+	{
+		if (!missing_ok)
+			elog(ERROR, "cache lookup failed for relation %u", relid);
+
+		if (object)
+			*object = NIL;
+		return;
+	}
 	relForm = (Form_pg_class) GETSTRUCT(relTup);
 
 	schema = get_namespace_name_or_temp(relForm->relnamespace);
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 2ea05f350b..451e262fef 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -161,7 +161,7 @@ recordDependencyOnCurrentExtension(const ObjectAddress *object,
 				ereport(ERROR,
 						(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 						 errmsg("%s is already a member of extension \"%s\"",
-								getObjectDescription(object),
+								getObjectDescription(object, false),
 								get_extension_name(oldext))));
 			}
 		}
@@ -320,7 +320,7 @@ changeDependencyFor(Oid classId, Oid objectId,
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot remove dependency on %s because it is a system object",
-						getObjectDescription(&objAddr))));
+						getObjectDescription(&objAddr, false))));
 
 	/*
 	 * We can handle adding a dependency on something pinned, though, since
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
index faf42b7640..39e5f6e634 100644
--- a/src/backend/catalog/pg_shdepend.c
+++ b/src/backend/catalog/pg_shdepend.c
@@ -572,7 +572,7 @@ checkSharedDependencies(Oid classId, Oid objectId,
 			ereport(ERROR,
 					(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
 					 errmsg("cannot drop %s because it is required by the database system",
-							getObjectDescription(&object))));
+							getObjectDescription(&object, false))));
 		}
 
 		object.classId = sdepForm->classid;
@@ -1063,7 +1063,7 @@ storeObjectDescription(StringInfo descs,
 					   SharedDependencyType deptype,
 					   int count)
 {
-	char	   *objdesc = getObjectDescription(object);
+	char	   *objdesc = getObjectDescription(object, false);
 
 	/* separate entries with a newline */
 	if (descs->len != 0)
@@ -1199,7 +1199,7 @@ shdepDropOwned(List *roleids, DropBehavior behavior)
 					(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
 					 errmsg("cannot drop objects owned by %s because they are "
 							"required by the database system",
-							getObjectDescription(&obj))));
+							getObjectDescription(&obj, false))));
 		}
 
 		ScanKeyInit(&key[0],
@@ -1313,7 +1313,7 @@ shdepReassignOwned(List *roleids, Oid newrole)
 			ereport(ERROR,
 					(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
 					 errmsg("cannot reassign ownership of objects owned by %s because they are required by the database system",
-							getObjectDescription(&obj))));
+							getObjectDescription(&obj, false))));
 
 			/*
 			 * There's no need to tell the whole truth, which is that we
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 549c7ea51d..e5faf5e0f5 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1399,10 +1399,11 @@ EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool no
 
 	/* object identity, objname and objargs */
 	obj->objidentity =
-		getObjectIdentityParts(&obj->address, &obj->addrnames, &obj->addrargs);
+		getObjectIdentityParts(&obj->address, &obj->addrnames, &obj->addrargs,
+							   false);
 
 	/* object type */
-	obj->objecttype = getObjectTypeDescription(&obj->address);
+	obj->objecttype = getObjectTypeDescription(&obj->address, false);
 
 	slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
 
@@ -2060,8 +2061,8 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
 					else if (cmd->type == SCT_AlterTSConfig)
 						addr = cmd->d.atscfg.address;
 
-					type = getObjectTypeDescription(&addr);
-					identity = getObjectIdentity(&addr);
+					type = getObjectTypeDescription(&addr, false);
+					identity = getObjectIdentity(&addr, false);
 
 					/*
 					 * Obtain schema name, if any ("pg_temp" if a temp
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 2e4538146d..a4c622ee11 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -2826,7 +2826,7 @@ AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *o
 					 errmsg("extension \"%s\" does not support SET SCHEMA",
 							NameStr(extForm->extname)),
 					 errdetail("%s is not in the extension's schema \"%s\"",
-							   getObjectDescription(&dep),
+							   getObjectDescription(&dep, false),
 							   get_namespace_name(oldNspOid))));
 	}
 
@@ -3216,7 +3216,7 @@ ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt,
 			ereport(ERROR,
 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 					 errmsg("%s is already a member of extension \"%s\"",
-							getObjectDescription(&object),
+							getObjectDescription(&object, false),
 							get_extension_name(oldExtension))));
 
 		/*
@@ -3256,7 +3256,7 @@ ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt,
 			ereport(ERROR,
 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 					 errmsg("%s is not a member of extension \"%s\"",
-							getObjectDescription(&object),
+							getObjectDescription(&object, false),
 							stmt->extname)));
 
 		/*
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d472c94030..ad84a1ecab 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9265,7 +9265,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 					{
 						/* Not expecting any other direct dependencies... */
 						elog(ERROR, "unexpected object depending on column: %s",
-							 getObjectDescription(&foundObject));
+							 getObjectDescription(&foundObject, false));
 					}
 					break;
 				}
@@ -9313,7 +9313,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 						 errmsg("cannot alter type of a column used by a view or rule"),
 						 errdetail("%s depends on column \"%s\"",
-								   getObjectDescription(&foundObject),
+								   getObjectDescription(&foundObject, false),
 								   colName)));
 				break;
 
@@ -9332,7 +9332,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 						 errmsg("cannot alter type of a column used in a trigger definition"),
 						 errdetail("%s depends on column \"%s\"",
-								   getObjectDescription(&foundObject),
+								   getObjectDescription(&foundObject, false),
 								   colName)));
 				break;
 
@@ -9350,7 +9350,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 						 errmsg("cannot alter type of a column used in a policy definition"),
 						 errdetail("%s depends on column \"%s\"",
-								   getObjectDescription(&foundObject),
+								   getObjectDescription(&foundObject, false),
 								   colName)));
 				break;
 
@@ -9411,7 +9411,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 				 * a column.
 				 */
 				elog(ERROR, "unexpected object depending on column: %s",
-					 getObjectDescription(&foundObject));
+					 getObjectDescription(&foundObject, false));
 				break;
 
 				/*
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index a0079821fe..623346aafc 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -405,7 +405,8 @@ format_procedure_internal(Oid procedure_oid, bool force_qualify)
  * This can be used to feed get_object_address.
  */
 void
-format_procedure_parts(Oid procedure_oid, List **objnames, List **objargs)
+format_procedure_parts(Oid procedure_oid, List **objnames, List **objargs,
+					   bool missing_ok)
 {
 	HeapTuple	proctup;
 	Form_pg_proc procform;
@@ -415,7 +416,11 @@ format_procedure_parts(Oid procedure_oid, List **objnames, List **objargs)
 	proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procedure_oid));
 
 	if (!HeapTupleIsValid(proctup))
-		elog(ERROR, "cache lookup failed for procedure with OID %u", procedure_oid);
+	{
+		if (!missing_ok)
+			elog(ERROR, "cache lookup failed for procedure with OID %u", procedure_oid);
+		return;
+	}
 
 	procform = (Form_pg_proc) GETSTRUCT(proctup);
 	nargs = procform->pronargs;
@@ -751,7 +756,7 @@ to_regoperator(PG_FUNCTION_ARGS)
  * This exports the useful functionality of regoperatorout for use
  * in other backend modules.  The result is a palloc'd string.
  */
-static char *
+char *
 format_operator_internal(Oid operator_oid, bool force_qualify)
 {
 	char	   *result;
@@ -829,15 +834,20 @@ format_operator_qualified(Oid operator_oid)
 }
 
 void
-format_operator_parts(Oid operator_oid, List **objnames, List **objargs)
+format_operator_parts(Oid operator_oid, List **objnames, List **objargs,
+					  bool missing_ok)
 {
 	HeapTuple	opertup;
 	Form_pg_operator oprForm;
 
 	opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operator_oid));
 	if (!HeapTupleIsValid(opertup))
-		elog(ERROR, "cache lookup failed for operator with OID %u",
-			 operator_oid);
+	{
+		if (!missing_ok)
+			elog(ERROR, "cache lookup failed for operator with OID %u",
+				 operator_oid);
+		return;
+	}
 
 	oprForm = (Form_pg_operator) GETSTRUCT(opertup);
 	*objnames = list_make2(get_namespace_name_or_temp(oprForm->oprnamespace),
diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h
index 6a9b1eec73..5a1e2a8f23 100644
--- a/src/include/catalog/objectaddress.h
+++ b/src/include/catalog/objectaddress.h
@@ -68,14 +68,18 @@ extern bool get_object_namensp_unique(Oid class_id);
 extern HeapTuple get_catalog_object_by_oid(Relation catalog,
 						  Oid objectId);
 
-extern char *getObjectDescription(const ObjectAddress *object);
+extern char *getObjectDescription(const ObjectAddress *object,
+								  bool missing_ok);
 extern char *getObjectDescriptionOids(Oid classid, Oid objid);
 
 extern int	read_objtype_from_string(const char *objtype);
-extern char *getObjectTypeDescription(const ObjectAddress *object);
-extern char *getObjectIdentity(const ObjectAddress *address);
+extern char *getObjectTypeDescription(const ObjectAddress *object,
+									  bool missing_ok);
+extern char *getObjectIdentity(const ObjectAddress *address,
+							   bool missing_ok);
 extern char *getObjectIdentityParts(const ObjectAddress *address,
-					   List **objname, List **objargs);
+									List **objname, List **objargs,
+									bool missing_ok);
 extern ArrayType *strlist_to_textarray(List *list);
 
 extern ObjectType get_relkind_objtype(char relkind);
diff --git a/src/include/utils/regproc.h b/src/include/utils/regproc.h
index 5b9a8cbee8..c09debbe26 100644
--- a/src/include/utils/regproc.h
+++ b/src/include/utils/regproc.h
@@ -19,10 +19,10 @@ extern List *stringToQualifiedNameList(const char *string);
 extern char *format_procedure(Oid procedure_oid);
 extern char *format_procedure_qualified(Oid procedure_oid);
 extern void format_procedure_parts(Oid operator_oid, List **objnames,
-					   List **objargs);
+								   List **objargs, bool missing_ok);
 extern char *format_operator(Oid operator_oid);
 extern char *format_operator_qualified(Oid operator_oid);
 extern void format_operator_parts(Oid operator_oid, List **objnames,
-					  List **objargs);
+								  List **objargs, bool missing_ok);
 
 #endif
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index bfd9d54c11..d74715eff7 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -492,3 +492,711 @@ DROP SCHEMA addr_nsp CASCADE;
 NOTICE:  drop cascades to 13 other objects
 DROP OWNED BY regress_addr_user;
 DROP USER regress_addr_user;
+--
+-- Checks for invalid objects
+--
+-- Keep those checks in the same order as getObjectIdentityParts()
+SELECT * FROM pg_identify_object('pg_class'::regclass, 0, 0); -- no relation
+ type | schema | name | identity 
+------+--------+------+----------
+      |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_class'::regclass, 'pg_class'::regclass, -8); -- no column for relation
+     type     |   schema   |   name   |      identity       
+--------------+------------+----------+---------------------
+ table column | pg_catalog | pg_class | pg_catalog.pg_class
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_proc'::regclass, 0, 0); -- no function
+ type | schema | name | identity 
+------+--------+------+----------
+      |        |      | 0
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_type'::regclass, 0, 0); -- no type
+ type | schema | name | identity 
+------+--------+------+----------
+ type |        |      | -
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_cast'::regclass, 0, 0); -- no cast
+ type | schema | name | identity 
+------+--------+------+----------
+ cast |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_collation'::regclass, 0, 0); -- no collation
+   type    | schema | name | identity 
+-----------+--------+------+----------
+ collation |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_constraint'::regclass, 0, 0); -- no constraint
+ type | schema | name | identity 
+------+--------+------+----------
+      |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_conversion'::regclass, 0, 0); -- no conversion
+    type    | schema | name | identity 
+------------+--------+------+----------
+ conversion |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_attrdef'::regclass, 0, 0); -- no default attribute
+     type      | schema | name | identity 
+---------------+--------+------+----------
+ default value |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_language'::regclass, 0, 0); -- no language
+   type   | schema | name | identity 
+----------+--------+------+----------
+ language |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_largeobject'::regclass, 0, 0); -- no large object, no error
+     type     | schema | name | identity 
+--------------+--------+------+----------
+ large object |        |      | 0
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_operator'::regclass, 0, 0); -- no operator
+   type   | schema | name | identity 
+----------+--------+------+----------
+ operator |        |      | 0
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_opclass'::regclass, 0, 0); -- no opclass, no need to check for no access method
+      type      | schema | name | identity 
+----------------+--------+------+----------
+ operator class |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_opfamily'::regclass, 0, 0); -- no opfamily
+      type       | schema | name | identity 
+-----------------+--------+------+----------
+ operator family |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_am'::regclass, 0, 0); -- no access method
+     type      | schema | name | identity 
+---------------+--------+------+----------
+ access method |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_amop'::regclass, 0, 0); -- no AM operator
+           type            | schema | name | identity 
+---------------------------+--------+------+----------
+ operator of access method |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_amproc'::regclass, 0, 0); -- no AM proc
+           type            | schema | name | identity 
+---------------------------+--------+------+----------
+ function of access method |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_rewrite'::regclass, 0, 0); -- no rewrite
+ type | schema | name | identity 
+------+--------+------+----------
+ rule |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_trigger'::regclass, 0, 0); -- no trigger
+  type   | schema | name | identity 
+---------+--------+------+----------
+ trigger |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_namespace'::regclass, 0, 0); -- no schema
+  type  | schema | name | identity 
+--------+--------+------+----------
+ schema |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_statistic_ext'::regclass, 0, 0); -- no statistics
+       type        | schema | name | identity 
+-------------------+--------+------+----------
+ statistics object |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_ts_parser'::regclass, 0, 0); -- no TS parser
+        type        | schema | name | identity 
+--------------------+--------+------+----------
+ text search parser |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_ts_dict'::regclass, 0, 0); -- no TS dictionnary
+          type          | schema | name | identity 
+------------------------+--------+------+----------
+ text search dictionary |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_ts_template'::regclass, 0, 0); -- no TS template
+         type         | schema | name | identity 
+----------------------+--------+------+----------
+ text search template |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_ts_config'::regclass, 0, 0); -- no TS configuration
+           type            | schema | name | identity 
+---------------------------+--------+------+----------
+ text search configuration |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_authid'::regclass, 0, 0); -- no role
+ type | schema | name | identity 
+------+--------+------+----------
+ role |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_database'::regclass, 0, 0); -- no database
+   type   | schema | name | identity 
+----------+--------+------+----------
+ database |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_tablespace'::regclass, 0, 0); -- no tablespace
+    type    | schema | name | identity 
+------------+--------+------+----------
+ tablespace |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_foreign_data_wrapper'::regclass, 0, 0); -- no FDW
+         type         | schema | name | identity 
+----------------------+--------+------+----------
+ foreign-data wrapper |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_foreign_server'::regclass, 0, 0); -- no server
+  type  | schema | name | identity 
+--------+--------+------+----------
+ server |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_user_mapping'::regclass, 0, 0); -- no user mapping
+     type     | schema | name | identity 
+--------------+--------+------+----------
+ user mapping |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_default_acl'::regclass, 0, 0); -- no default ACL
+    type     | schema | name | identity 
+-------------+--------+------+----------
+ default acl |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_extension'::regclass, 0, 0); -- no extension
+   type    | schema | name | identity 
+-----------+--------+------+----------
+ extension |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_event_trigger'::regclass, 0, 0); -- no event trigger
+     type      | schema | name | identity 
+---------------+--------+------+----------
+ event trigger |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_policy'::regclass, 0, 0); -- no policy
+  type  | schema | name | identity 
+--------+--------+------+----------
+ policy |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_publication'::regclass, 0, 0); -- no publication
+    type     | schema | name | identity 
+-------------+--------+------+----------
+ publication |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_publication_rel'::regclass, 0, 0); -- no publication relation
+         type         | schema | name | identity 
+----------------------+--------+------+----------
+ publication relation |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_subscription'::regclass, 0, 0); -- no subscription
+     type     | schema | name | identity 
+--------------+--------+------+----------
+ subscription |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_transform'::regclass, 0, 0); -- no transformation
+   type    | schema | name | identity 
+-----------+--------+------+----------
+ transform |        |      | 
+(1 row)
+
+-- Keep those checks in the same order as getObjectDescription()
+SELECT pg_describe_object('pg_class'::regclass, 0, 0); -- no relation
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_class'::regclass, 'pg_class'::regclass, -8); -- no column for relation
+ pg_describe_object 
+--------------------
+ table pg_class
+(1 row)
+
+SELECT pg_describe_object('pg_proc'::regclass, 0, 0); -- no function
+ pg_describe_object 
+--------------------
+ function 0
+(1 row)
+
+SELECT pg_describe_object('pg_type'::regclass, 0, 0); -- no type
+ pg_describe_object 
+--------------------
+ type -
+(1 row)
+
+SELECT pg_describe_object('pg_cast'::regclass, 0, 0); -- no cast
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_collation'::regclass, 0, 0); -- no collation
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_constraint'::regclass, 0, 0); -- no constraint
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_conversion'::regclass, 0, 0); -- no conversion
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_attrdef'::regclass, 0, 0); -- no default attribute
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_language'::regclass, 0, 0); -- no language
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_largeobject'::regclass, 0, 0); -- no large object, no error
+ pg_describe_object 
+--------------------
+ large object 0
+(1 row)
+
+SELECT pg_describe_object('pg_operator'::regclass, 0, 0); -- no operator
+ pg_describe_object 
+--------------------
+ operator 0
+(1 row)
+
+SELECT pg_describe_object('pg_opclass'::regclass, 0, 0); -- no opclass, no need to check for no access method
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_opfamily'::regclass, 0, 0); -- no opfamily
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_am'::regclass, 0, 0); -- no access method
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_amop'::regclass, 0, 0); -- no AM operator
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_amproc'::regclass, 0, 0); -- no AM proc
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_rewrite'::regclass, 0, 0); -- no rewrite
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_trigger'::regclass, 0, 0); -- no trigger
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_namespace'::regclass, 0, 0); -- no schema
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_statistic_ext'::regclass, 0, 0); -- no statistics
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_ts_parser'::regclass, 0, 0); -- no TS parser
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_ts_dict'::regclass, 0, 0); -- no TS dictionnary
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_ts_template'::regclass, 0, 0); -- no TS template
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_ts_config'::regclass, 0, 0); -- no TS configuration
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_authid'::regclass, 0, 0); -- no role
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_database'::regclass, 0, 0); -- no database
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_tablespace'::regclass, 0, 0); -- no tablespace
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_foreign_data_wrapper'::regclass, 0, 0); -- no FDW
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_foreign_server'::regclass, 0, 0); -- no server
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_user_mapping'::regclass, 0, 0); -- no user mapping
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_default_acl'::regclass, 0, 0); -- no default ACL
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_extension'::regclass, 0, 0); -- no extension
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_event_trigger'::regclass, 0, 0); -- no event trigger
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_policy'::regclass, 0, 0); -- no policy
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_publication'::regclass, 0, 0); -- no publication
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_publication_rel'::regclass, 0, 0); -- no publication relation
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_subscription'::regclass, 0, 0); -- no subscription
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_transform'::regclass, 0, 0); -- no transformation
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+-- Keep those checks in the same order as getObjectTypeDescription()
+SELECT * FROM pg_identify_object_as_address('pg_class'::regclass, 0, 0); -- no relation
+ type | object_names | object_args 
+------+--------------+-------------
+      |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_class'::regclass, 'pg_class'::regclass, -8); -- no column for relation
+     type     |     object_names      | object_args 
+--------------+-----------------------+-------------
+ table column | {pg_catalog,pg_class} | {}
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_proc'::regclass, 0, 0); -- no function
+ type | object_names | object_args 
+------+--------------+-------------
+      | {}           | {}
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_type'::regclass, 0, 0); -- no type
+ type | object_names | object_args 
+------+--------------+-------------
+ type | {-}          | {}
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_cast'::regclass, 0, 0); -- no cast
+ type | object_names | object_args 
+------+--------------+-------------
+ cast |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_collation'::regclass, 0, 0); -- no collation
+   type    | object_names | object_args 
+-----------+--------------+-------------
+ collation |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_constraint'::regclass, 0, 0); -- no constraint
+ type | object_names | object_args 
+------+--------------+-------------
+      |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_conversion'::regclass, 0, 0); -- no conversion
+    type    | object_names | object_args 
+------------+--------------+-------------
+ conversion |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_attrdef'::regclass, 0, 0); -- no default attribute
+     type      | object_names | object_args 
+---------------+--------------+-------------
+ default value |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_language'::regclass, 0, 0); -- no language
+   type   | object_names | object_args 
+----------+--------------+-------------
+ language |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_largeobject'::regclass, 0, 0); -- no large object, no error
+     type     | object_names | object_args 
+--------------+--------------+-------------
+ large object | {0}          | {}
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_operator'::regclass, 0, 0); -- no operator
+   type   | object_names | object_args 
+----------+--------------+-------------
+ operator | {}           | {}
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_opclass'::regclass, 0, 0); -- no opclass, no need to check for no access method
+      type      | object_names | object_args 
+----------------+--------------+-------------
+ operator class |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_opfamily'::regclass, 0, 0); -- no opfamily
+      type       | object_names | object_args 
+-----------------+--------------+-------------
+ operator family |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_am'::regclass, 0, 0); -- no access method
+     type      | object_names | object_args 
+---------------+--------------+-------------
+ access method |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_amop'::regclass, 0, 0); -- no AM operator
+           type            | object_names | object_args 
+---------------------------+--------------+-------------
+ operator of access method |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_amproc'::regclass, 0, 0); -- no AM proc
+           type            | object_names | object_args 
+---------------------------+--------------+-------------
+ function of access method |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_rewrite'::regclass, 0, 0); -- no rewrite
+ type | object_names | object_args 
+------+--------------+-------------
+ rule |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_trigger'::regclass, 0, 0); -- no trigger
+  type   | object_names | object_args 
+---------+--------------+-------------
+ trigger |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_namespace'::regclass, 0, 0); -- no schema
+  type  | object_names | object_args 
+--------+--------------+-------------
+ schema |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_statistic_ext'::regclass, 0, 0); -- no statistics
+       type        | object_names | object_args 
+-------------------+--------------+-------------
+ statistics object |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_ts_parser'::regclass, 0, 0); -- no TS parser
+        type        | object_names | object_args 
+--------------------+--------------+-------------
+ text search parser |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_ts_dict'::regclass, 0, 0); -- no TS dictionnary
+          type          | object_names | object_args 
+------------------------+--------------+-------------
+ text search dictionary |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_ts_template'::regclass, 0, 0); -- no TS template
+         type         | object_names | object_args 
+----------------------+--------------+-------------
+ text search template |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_ts_config'::regclass, 0, 0); -- no TS configuration
+           type            | object_names | object_args 
+---------------------------+--------------+-------------
+ text search configuration |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_authid'::regclass, 0, 0); -- no role
+ type | object_names | object_args 
+------+--------------+-------------
+ role |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_database'::regclass, 0, 0); -- no database
+   type   | object_names | object_args 
+----------+--------------+-------------
+ database |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_tablespace'::regclass, 0, 0); -- no tablespace
+    type    | object_names | object_args 
+------------+--------------+-------------
+ tablespace |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_foreign_data_wrapper'::regclass, 0, 0); -- no FDW
+         type         | object_names | object_args 
+----------------------+--------------+-------------
+ foreign-data wrapper |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_foreign_server'::regclass, 0, 0); -- no server
+  type  | object_names | object_args 
+--------+--------------+-------------
+ server |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_user_mapping'::regclass, 0, 0); -- no user mapping
+     type     | object_names | object_args 
+--------------+--------------+-------------
+ user mapping |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_default_acl'::regclass, 0, 0); -- no default ACL
+    type     | object_names | object_args 
+-------------+--------------+-------------
+ default acl |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_extension'::regclass, 0, 0); -- no extension
+   type    | object_names | object_args 
+-----------+--------------+-------------
+ extension |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_event_trigger'::regclass, 0, 0); -- no event trigger
+     type      | object_names | object_args 
+---------------+--------------+-------------
+ event trigger |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_policy'::regclass, 0, 0); -- no policy
+  type  | object_names | object_args 
+--------+--------------+-------------
+ policy |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_publication'::regclass, 0, 0); -- no publication
+    type     | object_names | object_args 
+-------------+--------------+-------------
+ publication |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_publication_rel'::regclass, 0, 0); -- no publication relation
+         type         | object_names | object_args 
+----------------------+--------------+-------------
+ publication relation |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_subscription'::regclass, 0, 0); -- no subscription
+     type     | object_names | object_args 
+--------------+--------------+-------------
+ subscription |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_transform'::regclass, 0, 0); -- no transformation
+   type    | object_names | object_args 
+-----------+--------------+-------------
+ transform |              | 
+(1 row)
+
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 55faa71edf..0772173775 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -213,3 +213,127 @@ DROP SCHEMA addr_nsp CASCADE;
 
 DROP OWNED BY regress_addr_user;
 DROP USER regress_addr_user;
+
+--
+-- Checks for invalid objects
+--
+-- Keep those checks in the same order as getObjectIdentityParts()
+SELECT * FROM pg_identify_object('pg_class'::regclass, 0, 0); -- no relation
+SELECT * FROM pg_identify_object('pg_class'::regclass, 'pg_class'::regclass, -8); -- no column for relation
+SELECT * FROM pg_identify_object('pg_proc'::regclass, 0, 0); -- no function
+SELECT * FROM pg_identify_object('pg_type'::regclass, 0, 0); -- no type
+SELECT * FROM pg_identify_object('pg_cast'::regclass, 0, 0); -- no cast
+SELECT * FROM pg_identify_object('pg_collation'::regclass, 0, 0); -- no collation
+SELECT * FROM pg_identify_object('pg_constraint'::regclass, 0, 0); -- no constraint
+SELECT * FROM pg_identify_object('pg_conversion'::regclass, 0, 0); -- no conversion
+SELECT * FROM pg_identify_object('pg_attrdef'::regclass, 0, 0); -- no default attribute
+SELECT * FROM pg_identify_object('pg_language'::regclass, 0, 0); -- no language
+SELECT * FROM pg_identify_object('pg_largeobject'::regclass, 0, 0); -- no large object, no error
+SELECT * FROM pg_identify_object('pg_operator'::regclass, 0, 0); -- no operator
+SELECT * FROM pg_identify_object('pg_opclass'::regclass, 0, 0); -- no opclass, no need to check for no access method
+SELECT * FROM pg_identify_object('pg_opfamily'::regclass, 0, 0); -- no opfamily
+SELECT * FROM pg_identify_object('pg_am'::regclass, 0, 0); -- no access method
+SELECT * FROM pg_identify_object('pg_amop'::regclass, 0, 0); -- no AM operator
+SELECT * FROM pg_identify_object('pg_amproc'::regclass, 0, 0); -- no AM proc
+SELECT * FROM pg_identify_object('pg_rewrite'::regclass, 0, 0); -- no rewrite
+SELECT * FROM pg_identify_object('pg_trigger'::regclass, 0, 0); -- no trigger
+SELECT * FROM pg_identify_object('pg_namespace'::regclass, 0, 0); -- no schema
+SELECT * FROM pg_identify_object('pg_statistic_ext'::regclass, 0, 0); -- no statistics
+SELECT * FROM pg_identify_object('pg_ts_parser'::regclass, 0, 0); -- no TS parser
+SELECT * FROM pg_identify_object('pg_ts_dict'::regclass, 0, 0); -- no TS dictionnary
+SELECT * FROM pg_identify_object('pg_ts_template'::regclass, 0, 0); -- no TS template
+SELECT * FROM pg_identify_object('pg_ts_config'::regclass, 0, 0); -- no TS configuration
+SELECT * FROM pg_identify_object('pg_authid'::regclass, 0, 0); -- no role
+SELECT * FROM pg_identify_object('pg_database'::regclass, 0, 0); -- no database
+SELECT * FROM pg_identify_object('pg_tablespace'::regclass, 0, 0); -- no tablespace
+SELECT * FROM pg_identify_object('pg_foreign_data_wrapper'::regclass, 0, 0); -- no FDW
+SELECT * FROM pg_identify_object('pg_foreign_server'::regclass, 0, 0); -- no server
+SELECT * FROM pg_identify_object('pg_user_mapping'::regclass, 0, 0); -- no user mapping
+SELECT * FROM pg_identify_object('pg_default_acl'::regclass, 0, 0); -- no default ACL
+SELECT * FROM pg_identify_object('pg_extension'::regclass, 0, 0); -- no extension
+SELECT * FROM pg_identify_object('pg_event_trigger'::regclass, 0, 0); -- no event trigger
+SELECT * FROM pg_identify_object('pg_policy'::regclass, 0, 0); -- no policy
+SELECT * FROM pg_identify_object('pg_publication'::regclass, 0, 0); -- no publication
+SELECT * FROM pg_identify_object('pg_publication_rel'::regclass, 0, 0); -- no publication relation
+SELECT * FROM pg_identify_object('pg_subscription'::regclass, 0, 0); -- no subscription
+SELECT * FROM pg_identify_object('pg_transform'::regclass, 0, 0); -- no transformation
+-- Keep those checks in the same order as getObjectDescription()
+SELECT pg_describe_object('pg_class'::regclass, 0, 0); -- no relation
+SELECT pg_describe_object('pg_class'::regclass, 'pg_class'::regclass, -8); -- no column for relation
+SELECT pg_describe_object('pg_proc'::regclass, 0, 0); -- no function
+SELECT pg_describe_object('pg_type'::regclass, 0, 0); -- no type
+SELECT pg_describe_object('pg_cast'::regclass, 0, 0); -- no cast
+SELECT pg_describe_object('pg_collation'::regclass, 0, 0); -- no collation
+SELECT pg_describe_object('pg_constraint'::regclass, 0, 0); -- no constraint
+SELECT pg_describe_object('pg_conversion'::regclass, 0, 0); -- no conversion
+SELECT pg_describe_object('pg_attrdef'::regclass, 0, 0); -- no default attribute
+SELECT pg_describe_object('pg_language'::regclass, 0, 0); -- no language
+SELECT pg_describe_object('pg_largeobject'::regclass, 0, 0); -- no large object, no error
+SELECT pg_describe_object('pg_operator'::regclass, 0, 0); -- no operator
+SELECT pg_describe_object('pg_opclass'::regclass, 0, 0); -- no opclass, no need to check for no access method
+SELECT pg_describe_object('pg_opfamily'::regclass, 0, 0); -- no opfamily
+SELECT pg_describe_object('pg_am'::regclass, 0, 0); -- no access method
+SELECT pg_describe_object('pg_amop'::regclass, 0, 0); -- no AM operator
+SELECT pg_describe_object('pg_amproc'::regclass, 0, 0); -- no AM proc
+SELECT pg_describe_object('pg_rewrite'::regclass, 0, 0); -- no rewrite
+SELECT pg_describe_object('pg_trigger'::regclass, 0, 0); -- no trigger
+SELECT pg_describe_object('pg_namespace'::regclass, 0, 0); -- no schema
+SELECT pg_describe_object('pg_statistic_ext'::regclass, 0, 0); -- no statistics
+SELECT pg_describe_object('pg_ts_parser'::regclass, 0, 0); -- no TS parser
+SELECT pg_describe_object('pg_ts_dict'::regclass, 0, 0); -- no TS dictionnary
+SELECT pg_describe_object('pg_ts_template'::regclass, 0, 0); -- no TS template
+SELECT pg_describe_object('pg_ts_config'::regclass, 0, 0); -- no TS configuration
+SELECT pg_describe_object('pg_authid'::regclass, 0, 0); -- no role
+SELECT pg_describe_object('pg_database'::regclass, 0, 0); -- no database
+SELECT pg_describe_object('pg_tablespace'::regclass, 0, 0); -- no tablespace
+SELECT pg_describe_object('pg_foreign_data_wrapper'::regclass, 0, 0); -- no FDW
+SELECT pg_describe_object('pg_foreign_server'::regclass, 0, 0); -- no server
+SELECT pg_describe_object('pg_user_mapping'::regclass, 0, 0); -- no user mapping
+SELECT pg_describe_object('pg_default_acl'::regclass, 0, 0); -- no default ACL
+SELECT pg_describe_object('pg_extension'::regclass, 0, 0); -- no extension
+SELECT pg_describe_object('pg_event_trigger'::regclass, 0, 0); -- no event trigger
+SELECT pg_describe_object('pg_policy'::regclass, 0, 0); -- no policy
+SELECT pg_describe_object('pg_publication'::regclass, 0, 0); -- no publication
+SELECT pg_describe_object('pg_publication_rel'::regclass, 0, 0); -- no publication relation
+SELECT pg_describe_object('pg_subscription'::regclass, 0, 0); -- no subscription
+SELECT pg_describe_object('pg_transform'::regclass, 0, 0); -- no transformation
+-- Keep those checks in the same order as getObjectTypeDescription()
+SELECT * FROM pg_identify_object_as_address('pg_class'::regclass, 0, 0); -- no relation
+SELECT * FROM pg_identify_object_as_address('pg_class'::regclass, 'pg_class'::regclass, -8); -- no column for relation
+SELECT * FROM pg_identify_object_as_address('pg_proc'::regclass, 0, 0); -- no function
+SELECT * FROM pg_identify_object_as_address('pg_type'::regclass, 0, 0); -- no type
+SELECT * FROM pg_identify_object_as_address('pg_cast'::regclass, 0, 0); -- no cast
+SELECT * FROM pg_identify_object_as_address('pg_collation'::regclass, 0, 0); -- no collation
+SELECT * FROM pg_identify_object_as_address('pg_constraint'::regclass, 0, 0); -- no constraint
+SELECT * FROM pg_identify_object_as_address('pg_conversion'::regclass, 0, 0); -- no conversion
+SELECT * FROM pg_identify_object_as_address('pg_attrdef'::regclass, 0, 0); -- no default attribute
+SELECT * FROM pg_identify_object_as_address('pg_language'::regclass, 0, 0); -- no language
+SELECT * FROM pg_identify_object_as_address('pg_largeobject'::regclass, 0, 0); -- no large object, no error
+SELECT * FROM pg_identify_object_as_address('pg_operator'::regclass, 0, 0); -- no operator
+SELECT * FROM pg_identify_object_as_address('pg_opclass'::regclass, 0, 0); -- no opclass, no need to check for no access method
+SELECT * FROM pg_identify_object_as_address('pg_opfamily'::regclass, 0, 0); -- no opfamily
+SELECT * FROM pg_identify_object_as_address('pg_am'::regclass, 0, 0); -- no access method
+SELECT * FROM pg_identify_object_as_address('pg_amop'::regclass, 0, 0); -- no AM operator
+SELECT * FROM pg_identify_object_as_address('pg_amproc'::regclass, 0, 0); -- no AM proc
+SELECT * FROM pg_identify_object_as_address('pg_rewrite'::regclass, 0, 0); -- no rewrite
+SELECT * FROM pg_identify_object_as_address('pg_trigger'::regclass, 0, 0); -- no trigger
+SELECT * FROM pg_identify_object_as_address('pg_namespace'::regclass, 0, 0); -- no schema
+SELECT * FROM pg_identify_object_as_address('pg_statistic_ext'::regclass, 0, 0); -- no statistics
+SELECT * FROM pg_identify_object_as_address('pg_ts_parser'::regclass, 0, 0); -- no TS parser
+SELECT * FROM pg_identify_object_as_address('pg_ts_dict'::regclass, 0, 0); -- no TS dictionnary
+SELECT * FROM pg_identify_object_as_address('pg_ts_template'::regclass, 0, 0); -- no TS template
+SELECT * FROM pg_identify_object_as_address('pg_ts_config'::regclass, 0, 0); -- no TS configuration
+SELECT * FROM pg_identify_object_as_address('pg_authid'::regclass, 0, 0); -- no role
+SELECT * FROM pg_identify_object_as_address('pg_database'::regclass, 0, 0); -- no database
+SELECT * FROM pg_identify_object_as_address('pg_tablespace'::regclass, 0, 0); -- no tablespace
+SELECT * FROM pg_identify_object_as_address('pg_foreign_data_wrapper'::regclass, 0, 0); -- no FDW
+SELECT * FROM pg_identify_object_as_address('pg_foreign_server'::regclass, 0, 0); -- no server
+SELECT * FROM pg_identify_object_as_address('pg_user_mapping'::regclass, 0, 0); -- no user mapping
+SELECT * FROM pg_identify_object_as_address('pg_default_acl'::regclass, 0, 0); -- no default ACL
+SELECT * FROM pg_identify_object_as_address('pg_extension'::regclass, 0, 0); -- no extension
+SELECT * FROM pg_identify_object_as_address('pg_event_trigger'::regclass, 0, 0); -- no event trigger
+SELECT * FROM pg_identify_object_as_address('pg_policy'::regclass, 0, 0); -- no policy
+SELECT * FROM pg_identify_object_as_address('pg_publication'::regclass, 0, 0); -- no publication
+SELECT * FROM pg_identify_object_as_address('pg_publication_rel'::regclass, 0, 0); -- no publication relation
+SELECT * FROM pg_identify_object_as_address('pg_subscription'::regclass, 0, 0); -- no subscription
+SELECT * FROM pg_identify_object_as_address('pg_transform'::regclass, 0, 0); -- no transformation
-- 
2.16.2

Attachment: signature.asc
Description: PGP signature

Reply via email to