Álvaro, all,

On Fri, Dec 14, 2018 at 09:04:36AM +0900, Michael Paquier wrote:
> Thanks again for looking up at what was proposed.  I'll see if I can
> finish the refactoring part for the next CF, and be done with this
> stuff.

Please find attached an updated patch set which reworks the SQL
interface for object addresses so as NULL-ness is handled consistently
for all object types.  This has needed a couple of extensions to some
current object-lookup APIs, which are added as refactoring bits
without breaking existing callers:
- Procedures and operators gain a new _extended routine which can take
a set of flags to enforce a NULL result or force qualification.
- format_type_extended needs an extra flag to enforce NULL.

On top of the refactoring issues, here is the list of changes to make
things work:
- The object type can never be NULL, meaning that for undefined
relations, routines or constraints the caller gets a generic name even
if the object is not defined.  (That's the definition mentioned a
couple of messages upthread).
- Large object existence is checked with LargeObjectExists().
- Table columns show all the fields as NULL if the object identity
cannot be found instead of showing the table and its schema.

With that all the regression tests show a consistent behavior for all
object types when those are undefined.  Thoughts?
--
Michael
From 5f62f834ffb5f310e6469f6e7496f898cefb26ac Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Tue, 25 Dec 2018 12:21:24 +0900
Subject: [PATCH 1/3] Add flag to format_type_extended to enforce NULL-ness

If a type scanned is undefined, type format routines have two behaviors
depending on if FORMAT_TYPE_ALLOW_INVALID is defined by the caller:
- Generate an error
- Return undefined type name "???" or "-".

The current interface is unhelpful for callers willing to format
properly a type name, but still make sure that the type is defined as
there could be types matching the undefined strings.  In order to
counter that, add a new flag called FORMAT_TYPE_FORCE_NULL which returns
a NULL result instead of "??? or "-" which does not generate an error.
They will be used for future patches to improve the SQL interface for
object addresses.
---
 src/backend/utils/adt/format_type.c | 20 ++++++++++++++++----
 src/include/utils/builtins.h        |  1 +
 2 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c
index f1f0ba3e0b..c41271ba4c 100644
--- a/src/backend/utils/adt/format_type.c
+++ b/src/backend/utils/adt/format_type.c
@@ -98,6 +98,9 @@ format_type(PG_FUNCTION_ARGS)
  * - FORMAT_TYPE_ALLOW_INVALID
  *			if the type OID is invalid or unknown, return ??? or such instead
  *			of failing
+ * - FORMAT_TYPE_FORCE_NULL
+ *			if the type OID is invalid or unknown, return NULL instead of ???
+ *			or such
  * - FORMAT_TYPE_FORCE_QUALIFY
  *			always schema-qualify type names, regardless of search_path
  *
@@ -116,13 +119,20 @@ format_type_extended(Oid type_oid, int32 typemod, bits16 flags)
 	char	   *buf;
 	bool		with_typemod;
 
-	if (type_oid == InvalidOid && (flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
-		return pstrdup("-");
+	if (type_oid == InvalidOid)
+	{
+		if ((flags & FORMAT_TYPE_FORCE_NULL) != 0)
+			return NULL;
+		else if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
+			return pstrdup("-");
+	}
 
 	tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
 	if (!HeapTupleIsValid(tuple))
 	{
-		if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
+		if ((flags & FORMAT_TYPE_FORCE_NULL) != 0)
+			return NULL;
+		else if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
 			return pstrdup("???");
 		else
 			elog(ERROR, "cache lookup failed for type %u", type_oid);
@@ -145,7 +155,9 @@ format_type_extended(Oid type_oid, int32 typemod, bits16 flags)
 		tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_base_type));
 		if (!HeapTupleIsValid(tuple))
 		{
-			if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
+			if ((flags & FORMAT_TYPE_FORCE_NULL) != 0)
+				return NULL;
+			else if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
 				return pstrdup("???[]");
 			else
 				elog(ERROR, "cache lookup failed for type %u", type_oid);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 61785a2433..8a17164bb7 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -109,6 +109,7 @@ extern Datum numeric_float8_no_overflow(PG_FUNCTION_ARGS);
 #define FORMAT_TYPE_TYPEMOD_GIVEN	0x01	/* typemod defined by caller */
 #define FORMAT_TYPE_ALLOW_INVALID	0x02	/* allow invalid types */
 #define FORMAT_TYPE_FORCE_QUALIFY	0x04	/* force qualification of type */
+#define FORMAT_TYPE_FORCE_NULL		0x08	/* force NULL if undefined */
 extern char *format_type_extended(Oid type_oid, int32 typemod, bits16 flags);
 
 extern char *format_type_be(Oid type_oid);
-- 
2.20.1

From a35f6d0f42085cd4db15d9c63a23847d0cdaca96 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Tue, 25 Dec 2018 13:13:28 +0900
Subject: [PATCH 2/3] Refactor format procedure and operator APIs to be more
 modular

This introduce a new set of extended routines for procedure and operator
lookups, with a flags bitmask argument that can modify the default
behavior:
- Force schema qualification
- Force NULL as result instead of a numeric OID for an undefined
object.
---
 src/backend/utils/adt/regproc.c | 61 +++++++++++++++++++++++----------
 src/include/utils/regproc.h     | 10 ++++++
 2 files changed, 52 insertions(+), 19 deletions(-)

diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index a0079821fe..e3f02b52d9 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -40,8 +40,6 @@
 #include "utils/regproc.h"
 #include "utils/varlena.h"
 
-static char *format_operator_internal(Oid operator_oid, bool force_qualify);
-static char *format_procedure_internal(Oid procedure_oid, bool force_qualify);
 static void parseNameAndArgTypes(const char *string, bool allowNone,
 					 List **names, int *nargs, Oid *argtypes);
 
@@ -322,24 +320,27 @@ to_regprocedure(PG_FUNCTION_ARGS)
 char *
 format_procedure(Oid procedure_oid)
 {
-	return format_procedure_internal(procedure_oid, false);
+	return format_procedure_extended(procedure_oid, 0);
 }
 
 char *
 format_procedure_qualified(Oid procedure_oid)
 {
-	return format_procedure_internal(procedure_oid, true);
+	return format_procedure_extended(procedure_oid, FORMAT_PROC_FORCE_QUALIFY);
 }
 
 /*
  * Routine to produce regprocedure names; see format_procedure above.
  *
- * force_qualify says whether to schema-qualify; if true, the name is always
- * qualified regardless of search_path visibility.  Otherwise the name is only
- * qualified if the function is not in path.
+ * The following bits in 'flags' modify the behavior:
+ * - FORMAT_PROC_FORCE_NULL
+ *			if the type OID is invalid or unknown, return NULL instead of
+ *			the numeric OID.
+ * - FORMAT_PROC_FORCE_QUALIFY
+ *			always schema-qualify type names, regardless of search_path
  */
-static char *
-format_procedure_internal(Oid procedure_oid, bool force_qualify)
+char *
+format_procedure_extended(Oid procedure_oid, bits16 flags)
 {
 	char	   *result;
 	HeapTuple	proctup;
@@ -364,7 +365,8 @@ format_procedure_internal(Oid procedure_oid, bool force_qualify)
 		 * Would this proc be found (given the right args) by regprocedurein?
 		 * If not, or if caller requests it, we need to qualify it.
 		 */
-		if (!force_qualify && FunctionIsVisible(procedure_oid))
+		if ((flags & FORMAT_PROC_FORCE_QUALIFY) == 0 &&
+			FunctionIsVisible(procedure_oid))
 			nspname = NULL;
 		else
 			nspname = get_namespace_name(procform->pronamespace);
@@ -378,7 +380,7 @@ format_procedure_internal(Oid procedure_oid, bool force_qualify)
 			if (i > 0)
 				appendStringInfoChar(&buf, ',');
 			appendStringInfoString(&buf,
-								   force_qualify ?
+								   (flags & FORMAT_PROC_FORCE_QUALIFY) != 0 ?
 								   format_type_be_qualified(thisargtype) :
 								   format_type_be(thisargtype));
 		}
@@ -388,6 +390,11 @@ format_procedure_internal(Oid procedure_oid, bool force_qualify)
 
 		ReleaseSysCache(proctup);
 	}
+	else if ((flags & FORMAT_PROC_FORCE_NULL) != 0)
+	{
+		/* If object is undefined, return NULL as wanted by caller */
+		result = NULL;
+	}
 	else
 	{
 		/* If OID doesn't match any pg_proc entry, return it numerically */
@@ -746,13 +753,22 @@ to_regoperator(PG_FUNCTION_ARGS)
 }
 
 /*
- * format_operator		- converts operator OID to "opr_name(args)"
+ * format_operator_extended
+ *
+ * Converts operator OID to "opr_name(args)"
  *
  * This exports the useful functionality of regoperatorout for use
  * in other backend modules.  The result is a palloc'd string.
+ *
+ * The following bits in 'flags' modify the behavior:
+ * - FORMAT_OPERATOR_FORCE_NULL
+ *			if the type OID is invalid or unknown, return NULL instead of
+ *			the numeric OID.
+ * - FORMAT_OPERATOR_FORCE_QUALIFY
+ *			always schema-qualify type names, regardless of search_path
  */
-static char *
-format_operator_internal(Oid operator_oid, bool force_qualify)
+char *
+format_operator_extended(Oid operator_oid, bits16 flags)
 {
 	char	   *result;
 	HeapTuple	opertup;
@@ -775,7 +791,8 @@ format_operator_internal(Oid operator_oid, bool force_qualify)
 		 * Would this oper be found (given the right args) by regoperatorin?
 		 * If not, or if caller explicitly requests it, we need to qualify it.
 		 */
-		if (force_qualify || !OperatorIsVisible(operator_oid))
+		if ((flags & FORMAT_OPERATOR_FORCE_QUALIFY) != 0 ||
+			!OperatorIsVisible(operator_oid))
 		{
 			nspname = get_namespace_name(operform->oprnamespace);
 			appendStringInfo(&buf, "%s.",
@@ -786,7 +803,7 @@ format_operator_internal(Oid operator_oid, bool force_qualify)
 
 		if (operform->oprleft)
 			appendStringInfo(&buf, "%s,",
-							 force_qualify ?
+							 (flags & FORMAT_OPERATOR_FORCE_QUALIFY) != 0 ?
 							 format_type_be_qualified(operform->oprleft) :
 							 format_type_be(operform->oprleft));
 		else
@@ -794,7 +811,7 @@ format_operator_internal(Oid operator_oid, bool force_qualify)
 
 		if (operform->oprright)
 			appendStringInfo(&buf, "%s)",
-							 force_qualify ?
+							 (flags & FORMAT_OPERATOR_FORCE_QUALIFY) != 0 ?
 							 format_type_be_qualified(operform->oprright) :
 							 format_type_be(operform->oprright));
 		else
@@ -804,6 +821,11 @@ format_operator_internal(Oid operator_oid, bool force_qualify)
 
 		ReleaseSysCache(opertup);
 	}
+	else if ((flags & FORMAT_OPERATOR_FORCE_NULL) != 0)
+	{
+		/* If object is undefined, return NULL as wanted by caller */
+		result = NULL;
+	}
 	else
 	{
 		/*
@@ -819,13 +841,14 @@ format_operator_internal(Oid operator_oid, bool force_qualify)
 char *
 format_operator(Oid operator_oid)
 {
-	return format_operator_internal(operator_oid, false);
+	return format_operator_extended(operator_oid, 0);
 }
 
 char *
 format_operator_qualified(Oid operator_oid)
 {
-	return format_operator_internal(operator_oid, true);
+	return format_operator_extended(operator_oid,
+									FORMAT_OPERATOR_FORCE_QUALIFY);
 }
 
 void
diff --git a/src/include/utils/regproc.h b/src/include/utils/regproc.h
index 5b9a8cbee8..3c22312a5b 100644
--- a/src/include/utils/regproc.h
+++ b/src/include/utils/regproc.h
@@ -15,6 +15,16 @@
 
 #include "nodes/pg_list.h"
 
+/* Control flags for format_procedure_extended */
+#define FORMAT_PROC_FORCE_NULL		0x01	/* NULL if undefined */
+#define FORMAT_PROC_FORCE_QUALIFY	0x02	/* force qualification */
+extern char *format_procedure_extended(Oid type_oid, bits16 flags);
+
+/* Control flags for format_operator_extended */
+#define FORMAT_OPERATOR_FORCE_NULL		0x01	/* NULL if undefined */
+#define FORMAT_OPERATOR_FORCE_QUALIFY	0x02	/* force qualification */
+extern char *format_operator_extended(Oid type_oid, bits16 flags);
+
 extern List *stringToQualifiedNameList(const char *string);
 extern char *format_procedure(Oid procedure_oid);
 extern char *format_procedure_qualified(Oid procedure_oid);
-- 
2.20.1

From e24c94fa48408ce626d97bb402aa3ac3c4133931 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Tue, 25 Dec 2018 13:33:23 +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          | 994 ++++++++++++++-----
 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              |  20 +-
 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, 1659 insertions(+), 320 deletions(-)

diff --git a/contrib/sepgsql/database.c b/contrib/sepgsql/database.c
index 102c9c8012..5cffd4b655 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 9bdbd7b60f..ffb84ac6b7 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 acffc468d2..ef43b3101c 100644
--- a/contrib/sepgsql/label.c
+++ b/contrib/sepgsql/label.c
@@ -364,7 +364,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,
@@ -535,7 +535,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 a4acfd92da..1920d43368 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 312740c598..da2340c70a 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 7b098318ed..5843b4cccf 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 37860996a6..180cb77ca1 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17946,7 +17946,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>
@@ -17962,7 +17963,8 @@ SELECT collation for ('foo' COLLATE "de_DE");
    uniquely identify the object, otherwise <literal>NULL</literal>;
    <parameter>identity</parameter> is the complete object identity, with the
    precise format depending on object type, and each name within the format
-   being schema-qualified and quoted as necessary.
+   being schema-qualified and quoted as necessary. Undefined objects are
+   identified with <literal>NULL</literal> values.
   </para>
 
   <para>
@@ -17978,6 +17980,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 7dfa3278a5..7eca6f3129 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -605,11 +605,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)));
@@ -689,11 +689,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;
 		}
 	}
@@ -785,12 +785,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;
 		}
@@ -884,7 +884,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
@@ -907,7 +907,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)
 			{
@@ -965,7 +966,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 d5e30649ff..90c084d073 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -787,14 +787,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
@@ -1667,7 +1673,7 @@ get_object_address_opf_member(ObjectType objtype,
 										membernum,
 										TypeNameToString(typenames[0]),
 										TypeNameToString(typenames[1]),
-										getObjectDescription(&famaddr))));
+										getObjectDescription(&famaddr, false))));
 				}
 				else
 				{
@@ -1698,7 +1704,7 @@ get_object_address_opf_member(ObjectType objtype,
 										membernum,
 										TypeNameToString(typenames[0]),
 										TypeNameToString(typenames[1]),
-										getObjectDescription(&famaddr))));
+										getObjectDescription(&famaddr, false))));
 				}
 				else
 				{
@@ -2715,10 +2721,12 @@ get_catalog_object_by_oid(Relation catalog, AttrNumber oidcol, 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, and an error is generated if
+ * missing_ok is false.
  */
 char *
-getObjectDescription(const ObjectAddress *object)
+getObjectDescription(const ObjectAddress *object, bool missing_ok)
 {
 	StringInfoData buffer;
 
@@ -2728,14 +2736,19 @@ getObjectDescription(const ObjectAddress *object)
 	{
 		case OCLASS_CLASS:
 			if (object->objectSubId == 0)
-				getRelationDescription(&buffer, object->objectId);
+				getRelationDescription(&buffer, object->objectId, missing_ok);
 			else
 			{
 				/* column, not whole relation */
 				StringInfoData rel;
+				char *attname = get_attname(object->objectId,
+											object->objectSubId,
+											missing_ok);
+				if (!attname)
+					break;
 
 				initStringInfo(&rel);
-				getRelationDescription(&rel, object->objectId);
+				getRelationDescription(&rel, object->objectId, missing_ok);
 				/* translator: second %s is, e.g., "table %s" */
 				appendStringInfo(&buffer, _("column %s of %s"),
 								 get_attname(object->objectId,
@@ -2747,14 +2760,28 @@ getObjectDescription(const ObjectAddress *object)
 			break;
 
 		case OCLASS_PROC:
-			appendStringInfo(&buffer, _("function %s"),
-							 format_procedure(object->objectId));
-			break;
+			{
+				char *proname = format_procedure_extended(object->objectId,
+													FORMAT_PROC_FORCE_NULL);
+
+				if (proname == NULL)
+					break;
+
+				appendStringInfo(&buffer, _("function %s"), proname);
+				break;
+			}
 
 		case OCLASS_TYPE:
-			appendStringInfo(&buffer, _("type %s"),
-							 format_type_be(object->objectId));
-			break;
+			{
+				char *typname = format_type_extended(object->objectId, -1,
+													 FORMAT_TYPE_FORCE_NULL);
+
+				if (typname == NULL)
+					break;
+
+				appendStringInfo(&buffer, _("type %s"), typname);
+				break;
+			}
 
 		case OCLASS_CAST:
 			{
@@ -2777,8 +2804,17 @@ getObjectDescription(const ObjectAddress *object)
 				tup = systable_getnext(rcscan);
 
 				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);
+					}
+
+					systable_endscan(rcscan);
+					heap_close(castDesc, AccessShareLock);
+					break;
+				}
 
 				castForm = (Form_pg_cast) GETSTRUCT(tup);
 
@@ -2800,8 +2836,15 @@ getObjectDescription(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);
 
 				/* Qualify the name if not visible in search path */
@@ -2825,8 +2868,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))
@@ -2834,7 +2882,7 @@ getObjectDescription(const ObjectAddress *object)
 					StringInfoData rel;
 
 					initStringInfo(&rel);
-					getRelationDescription(&rel, con->conrelid);
+					getRelationDescription(&rel, con->conrelid, false);
 					/* translator: second %s is, e.g., "table %s" */
 					appendStringInfo(&buffer, _("constraint %s on %s"),
 									 NameStr(con->conname), rel.data);
@@ -2859,8 +2907,13 @@ 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;
+				}
+
 				conv = (Form_pg_conversion) GETSTRUCT(conTup);
 
 				/* Qualify the name if not visible in search path */
@@ -2898,8 +2951,15 @@ getObjectDescription(const ObjectAddress *object)
 				tup = systable_getnext(adscan);
 
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "could not find tuple for attrdef %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "could not find tuple for attrdef %u",
+							 object->objectId);
+
+					systable_endscan(adscan);
+					heap_close(attrdefDesc, AccessShareLock);
+					break;
+				}
 
 				attrdef = (Form_pg_attrdef) GETSTRUCT(tup);
 
@@ -2909,7 +2969,7 @@ getObjectDescription(const ObjectAddress *object)
 
 				/* translator: %s is typically "column %s of table %s" */
 				appendStringInfo(&buffer, _("default value for %s"),
-								 getObjectDescription(&colobject));
+								 getObjectDescription(&colobject, false));
 
 				systable_endscan(adscan);
 				heap_close(attrdefDesc, AccessShareLock);
@@ -2917,19 +2977,34 @@ 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:
+			if (!LargeObjectExists(object->objectId))
+				break;
 			appendStringInfo(&buffer, _("large object %u"),
 							 object->objectId);
 			break;
 
 		case OCLASS_OPERATOR:
-			appendStringInfo(&buffer, _("operator %s"),
-							 format_operator(object->objectId));
-			break;
+			{
+				char *oprname = format_operator_extended(object->objectId,
+													FORMAT_PROC_FORCE_NULL);
+
+				if (oprname == NULL)
+					break;
+
+				appendStringInfo(&buffer, _("operator %s"), oprname);
+				break;
+			}
 
 		case OCLASS_OPCLASS:
 			{
@@ -2942,8 +3017,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,
@@ -2970,7 +3050,7 @@ getObjectDescription(const ObjectAddress *object)
 			}
 
 		case OCLASS_OPFAMILY:
-			getOpFamilyDescription(&buffer, object->objectId);
+			getOpFamilyDescription(&buffer, object->objectId, missing_ok);
 			break;
 
 		case OCLASS_AM:
@@ -2980,8 +3060,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);
@@ -3011,13 +3096,20 @@ getObjectDescription(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);
-				getOpFamilyDescription(&opfam, amopForm->amopfamily);
+				getOpFamilyDescription(&opfam, amopForm->amopfamily, false);
 
 				/*------
 				   translator: %d is the operator strategy (a number), the
@@ -3061,13 +3153,20 @@ getObjectDescription(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);
-				getOpFamilyDescription(&opfam, amprocForm->amprocfamily);
+				getOpFamilyDescription(&opfam, amprocForm->amprocfamily, false);
 
 				/*------
 				   translator: %d is the function number, the first two %s's
@@ -3110,12 +3209,20 @@ getObjectDescription(const ObjectAddress *object)
 				tup = systable_getnext(rcscan);
 
 				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);
+
+					systable_endscan(rcscan);
+					heap_close(ruleDesc, AccessShareLock);
+					break;
+				}
+
 				rule = (Form_pg_rewrite) GETSTRUCT(tup);
 
 				initStringInfo(&rel);
-				getRelationDescription(&rel, rule->ev_class);
+				getRelationDescription(&rel, rule->ev_class, false);
 
 				/* translator: second %s is, e.g., "table %s" */
 				appendStringInfo(&buffer, _("rule %s on %s"),
@@ -3148,12 +3255,20 @@ getObjectDescription(const ObjectAddress *object)
 				tup = systable_getnext(tgscan);
 
 				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);
+
+					systable_endscan(tgscan);
+					heap_close(trigDesc, AccessShareLock);
+					break;
+				}
+
 				trig = (Form_pg_trigger) GETSTRUCT(tup);
 
 				initStringInfo(&rel);
-				getRelationDescription(&rel, trig->tgrelid);
+				getRelationDescription(&rel, trig->tgrelid, false);
 
 				/* translator: second %s is, e.g., "table %s" */
 				appendStringInfo(&buffer, _("trigger %s on %s"),
@@ -3170,8 +3285,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;
 			}
@@ -3185,8 +3304,13 @@ 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, "could not find tuple for statistics object %u",
+							 object->objectId);
+					break;
+				}
+
 				stxForm = (Form_pg_statistic_ext) GETSTRUCT(stxTup);
 
 				/* Qualify the name if not visible in search path */
@@ -3212,8 +3336,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;
+				}
 				prsForm = (Form_pg_ts_parser) GETSTRUCT(tup);
 
 				/* Qualify the name if not visible in search path */
@@ -3238,8 +3366,13 @@ 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;
+				}
+
 				dictForm = (Form_pg_ts_dict) GETSTRUCT(tup);
 
 				/* Qualify the name if not visible in search path */
@@ -3264,8 +3397,13 @@ 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;
+				}
+
 				tmplForm = (Form_pg_ts_template) GETSTRUCT(tup);
 
 				/* Qualify the name if not visible in search path */
@@ -3290,8 +3428,13 @@ 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;
+				}
+
 				cfgForm = (Form_pg_ts_config) GETSTRUCT(tup);
 
 				/* Qualify the name if not visible in search path */
@@ -3309,8 +3452,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;
 			}
 
@@ -3320,8 +3466,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;
 			}
@@ -3332,8 +3482,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;
 			}
@@ -3342,8 +3496,10 @@ getObjectDescription(const ObjectAddress *object)
 			{
 				ForeignDataWrapper *fdw;
 
-				fdw = GetForeignDataWrapper(object->objectId);
-				appendStringInfo(&buffer, _("foreign-data wrapper %s"), fdw->fdwname);
+				fdw = GetForeignDataWrapperExtended(object->objectId,
+													missing_ok);
+				if (fdw)
+					appendStringInfo(&buffer, _("foreign-data wrapper %s"), fdw->fdwname);
 				break;
 			}
 
@@ -3351,8 +3507,9 @@ getObjectDescription(const ObjectAddress *object)
 			{
 				ForeignServer *srv;
 
-				srv = GetForeignServer(object->objectId);
-				appendStringInfo(&buffer, _("server %s"), srv->servername);
+				srv = GetForeignServerExtended(object->objectId, missing_ok);
+				if (srv)
+					appendStringInfo(&buffer, _("server %s"), srv->servername);
 				break;
 			}
 
@@ -3367,8 +3524,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);
@@ -3408,8 +3570,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);
 
@@ -3492,8 +3661,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;
 			}
@@ -3505,8 +3678,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);
@@ -3535,12 +3712,20 @@ getObjectDescription(const ObjectAddress *object)
 				tuple = systable_getnext(sscan);
 
 				if (!HeapTupleIsValid(tuple))
-					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);
+
+					systable_endscan(sscan);
+					heap_close(policy_rel, AccessShareLock);
+					break;
+				}
+
 				form_policy = (Form_pg_policy) GETSTRUCT(tuple);
 
 				initStringInfo(&rel);
-				getRelationDescription(&rel, form_policy->polrelid);
+				getRelationDescription(&rel, form_policy->polrelid, false);
 
 				/* translator: second %s is, e.g., "table %s" */
 				appendStringInfo(&buffer, _("policy %s on %s"),
@@ -3553,9 +3738,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;
 			}
 
@@ -3569,14 +3756,18 @@ 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);
 
 				initStringInfo(&rel);
-				getRelationDescription(&rel, prform->prrelid);
+				getRelationDescription(&rel, prform->prrelid, false);
 
 				/* translator: first %s is, e.g., "table %s" */
 				appendStringInfo(&buffer, _("publication of %s in publication %s"),
@@ -3588,9 +3779,11 @@ 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;
 			}
 
@@ -3602,8 +3795,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);
 
@@ -3621,6 +3818,10 @@ getObjectDescription(const ObjectAddress *object)
 			 */
 	}
 
+	/* an empty buffer is equivalent to no object found */
+	if (buffer.len == 0)
+		return NULL;
+
 	return buffer.data;
 }
 
@@ -3636,7 +3837,7 @@ getObjectDescriptionOids(Oid classid, Oid objid)
 	address.objectId = objid;
 	address.objectSubId = 0;
 
-	return getObjectDescription(&address);
+	return getObjectDescription(&address, false);
 }
 
 /*
@@ -3645,7 +3846,7 @@ getObjectDescriptionOids(Oid classid, Oid objid)
  * The result is appended to "buffer".
  */
 static void
-getRelationDescription(StringInfo buffer, Oid relid)
+getRelationDescription(StringInfo buffer, Oid relid, bool missing_ok)
 {
 	HeapTuple	relTup;
 	Form_pg_class relForm;
@@ -3655,7 +3856,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 */
@@ -3716,7 +3921,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;
@@ -3726,7 +3931,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));
@@ -3770,7 +3979,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));
 }
 
@@ -3785,6 +3998,7 @@ pg_identify_object(PG_FUNCTION_ARGS)
 	int32		objsubid = PG_GETARG_INT32(2);
 	Oid			schema_oid = InvalidOid;
 	const char *objname = NULL;
+	char	   *objidentity;
 	ObjectAddress address;
 	Datum		values[4];
 	bool		nulls[4];
@@ -3859,12 +4073,18 @@ pg_identify_object(PG_FUNCTION_ARGS)
 		heap_close(catalog, AccessShareLock);
 	}
 
-	/* object type */
-	values[0] = CStringGetTextDatum(getObjectTypeDescription(&address));
+	/* object type, which can never be NULL */
+	values[0] = CStringGetTextDatum(getObjectTypeDescription(&address, true));
 	nulls[0] = false;
 
+	/*
+	 * Before doing anything, extract the object identity.  If the identity
+	 * cannot be found, set all the fields except the object type to NULL.
+	 */
+	objidentity = getObjectIdentity(&address, true);
+
 	/* schema name */
-	if (OidIsValid(schema_oid))
+	if (OidIsValid(schema_oid) && objidentity)
 	{
 		const char *schema = quote_identifier(get_namespace_name(schema_oid));
 
@@ -3875,7 +4095,7 @@ pg_identify_object(PG_FUNCTION_ARGS)
 		nulls[1] = true;
 
 	/* object name */
-	if (objname)
+	if (objname && objidentity)
 	{
 		values[2] = CStringGetTextDatum(objname);
 		nulls[2] = false;
@@ -3884,8 +4104,13 @@ pg_identify_object(PG_FUNCTION_ARGS)
 		nulls[2] = true;
 
 	/* object identity */
-	values[3] = CStringGetTextDatum(getObjectIdentity(&address));
-	nulls[3] = false;
+	if (objidentity)
+	{
+		values[3] = CStringGetTextDatum(objidentity);
+		nulls[3] = false;
+	}
+	else
+		nulls[3] = true;
 
 	htup = heap_form_tuple(tupdesc, values, nulls);
 
@@ -3929,23 +4154,31 @@ pg_identify_object_as_address(PG_FUNCTION_ARGS)
 	tupdesc = BlessTupleDesc(tupdesc);
 
 	/* object type */
-	values[0] = CStringGetTextDatum(getObjectTypeDescription(&address));
+	values[0] = CStringGetTextDatum(getObjectTypeDescription(&address, true));
 	nulls[0] = false;
 
 	/* 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);
 
@@ -3959,7 +4192,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;
 
@@ -3969,11 +4202,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:
@@ -3989,7 +4224,8 @@ getObjectTypeDescription(const ObjectAddress *object)
 			break;
 
 		case OCLASS_CONSTRAINT:
-			getConstraintTypeDescription(&buffer, object->objectId);
+			getConstraintTypeDescription(&buffer, object->objectId,
+										 missing_ok);
 			break;
 
 		case OCLASS_CONVERSION:
@@ -4126,6 +4362,10 @@ getObjectTypeDescription(const ObjectAddress *object)
 			 */
 	}
 
+	/* an empty string is equivalent to no object found */
+	if (buffer.len == 0)
+		return NULL;
+
 	return buffer.data;
 }
 
@@ -4133,7 +4373,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;
@@ -4141,7 +4382,14 @@ 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);
+
+		/* fallback to "relation" for an undefined object */
+		appendStringInfoString(buffer, "relation");
+		return;
+	}
 	relForm = (Form_pg_class) GETSTRUCT(relTup);
 
 	switch (relForm->relkind)
@@ -4188,7 +4436,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;
@@ -4198,7 +4446,16 @@ getConstraintTypeDescription(StringInfo buffer, Oid constroid)
 	constrTup = get_catalog_object_by_oid(constrRel, Anum_pg_constraint_oid,
 										  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);
+
+		/* fallback to "constraint" for an undefined object */
+		appendStringInfoString(buffer, "constraint");
+		return;
+	}
 
 	constrForm = (Form_pg_constraint) GETSTRUCT(constrTup);
 
@@ -4216,7 +4473,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;
@@ -4224,7 +4482,14 @@ 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);
+
+		/* fallback to "routine" for an undefined object */
+		appendStringInfoString(buffer, "routine");
+		return;
+	}
 	procForm = (Form_pg_proc) GETSTRUCT(procTup);
 
 	if (procForm->prokind == PROKIND_AGGREGATE)
@@ -4241,12 +4506,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);
 }
 
 /*
@@ -4255,11 +4521,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;
 
@@ -4281,31 +4549,63 @@ getObjectIdentityParts(const ObjectAddress *object,
 	switch (getObjectClass(object))
 	{
 		case OCLASS_CLASS:
-			getRelationIdentity(&buffer, object->objectId, objname);
-			if (object->objectSubId != 0)
 			{
-				char	   *attr;
+				char	   *attr = NULL;
 
-				attr = get_attname(object->objectId, object->objectSubId,
-								   false);
-				appendStringInfo(&buffer, ".%s", quote_identifier(attr));
-				if (objname)
-					*objname = lappend(*objname, attr);
+				/*
+				 * Check for the attribute first, so as if it is missing
+				 * we can skip the entire relation description.
+				 */
+				if (object->objectSubId != 0)
+				{
+					attr = get_attname(object->objectId,
+									   object->objectSubId,
+									   missing_ok);
+
+					if (missing_ok && attr == NULL)
+						break;
+				}
+
+				getRelationIdentity(&buffer, object->objectId, objname,
+									missing_ok);
+				if (objname && *objname == NIL)
+					break;
+
+				if (attr)
+				{
+					appendStringInfo(&buffer, ".%s",
+									 quote_identifier(attr));
+					if (objname)
+						*objname = lappend(*objname, attr);
+				}
 			}
 			break;
 
 		case OCLASS_PROC:
-			appendStringInfoString(&buffer,
-								   format_procedure_qualified(object->objectId));
-			if (objname)
-				format_procedure_parts(object->objectId, objname, objargs);
-			break;
+			{
+				char *proname = format_procedure_extended(object->objectId,
+						FORMAT_PROC_FORCE_QUALIFY | FORMAT_PROC_FORCE_NULL);
+
+				if (proname == NULL)
+					break;
+
+				appendStringInfoString(&buffer, proname);
+				if (objname)
+					format_procedure_parts(object->objectId, objname, objargs,
+										   missing_ok);
+				break;
+			}
 
 		case OCLASS_TYPE:
 			{
 				char	   *typeout;
 
-				typeout = format_type_be_qualified(object->objectId);
+				typeout = format_type_extended(object->objectId, -1,
+					FORMAT_TYPE_FORCE_NULL | FORMAT_TYPE_FORCE_QUALIFY);
+
+				if (typeout == NULL)
+					break;
+
 				appendStringInfoString(&buffer, typeout);
 				if (objname)
 					*objname = list_make1(typeout);
@@ -4324,8 +4624,14 @@ getObjectIdentityParts(const ObjectAddress *object,
 												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);
 
@@ -4352,8 +4658,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,
@@ -4374,15 +4684,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)));
 				}
@@ -4397,7 +4712,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)));
@@ -4416,8 +4732,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,
@@ -4453,8 +4773,15 @@ getObjectIdentityParts(const ObjectAddress *object,
 				tup = systable_getnext(adscan);
 
 				if (!HeapTupleIsValid(tup))
-					elog(ERROR, "could not find tuple for attrdef %u",
-						 object->objectId);
+				{
+					if (!missing_ok)
+						elog(ERROR, "could not find tuple for attrdef %u",
+							 object->objectId);
+
+					systable_endscan(adscan);
+					heap_close(attrdefDesc, AccessShareLock);
+					break;
+				}
 
 				attrdef = (Form_pg_attrdef) GETSTRUCT(tup);
 
@@ -4464,7 +4791,8 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 				appendStringInfo(&buffer, "for %s",
 								 getObjectIdentityParts(&colobject,
-														objname, objargs));
+														objname, objargs,
+														false));
 
 				systable_endscan(adscan);
 				heap_close(attrdefDesc, AccessShareLock);
@@ -4479,8 +4807,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)));
@@ -4490,6 +4822,8 @@ getObjectIdentityParts(const ObjectAddress *object,
 				break;
 			}
 		case OCLASS_LARGEOBJECT:
+			if (!LargeObjectExists(object->objectId))
+				break;
 			appendStringInfo(&buffer, "%u",
 							 object->objectId);
 			if (objname)
@@ -4497,11 +4831,18 @@ getObjectIdentityParts(const ObjectAddress *object,
 			break;
 
 		case OCLASS_OPERATOR:
-			appendStringInfoString(&buffer,
-								   format_operator_qualified(object->objectId));
-			if (objname)
-				format_operator_parts(object->objectId, objname, objargs);
-			break;
+			{
+				char *oprname = format_operator_extended(object->objectId,
+						FORMAT_OPERATOR_FORCE_QUALIFY | FORMAT_OPERATOR_FORCE_NULL);
+
+				if (oprname == NULL)
+					break;
+
+				appendStringInfoString(&buffer, oprname);
+				if (objname)
+					format_operator_parts(object->objectId, objname, objargs, missing_ok);
+				break;
+			}
 
 		case OCLASS_OPCLASS:
 			{
@@ -4514,8 +4855,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);
 
@@ -4541,7 +4886,8 @@ getObjectIdentityParts(const ObjectAddress *object,
 			}
 
 		case OCLASS_OPFAMILY:
-			getOpFamilyIdentity(&buffer, object->objectId, objname);
+			getOpFamilyIdentity(&buffer, object->objectId, objname,
+								missing_ok);
 			break;
 
 		case OCLASS_AM:
@@ -4550,8 +4896,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);
@@ -4583,13 +4933,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);
@@ -4637,13 +4995,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);
@@ -4678,14 +5044,20 @@ getObjectIdentityParts(const ObjectAddress *object,
 												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)));
 
@@ -4705,14 +5077,20 @@ getObjectIdentityParts(const ObjectAddress *object,
 												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)));
 
@@ -4726,8 +5104,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)
@@ -4744,8 +5126,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,
@@ -4767,8 +5153,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,
@@ -4790,8 +5180,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,
@@ -4813,8 +5207,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,
@@ -4836,8 +5234,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,
@@ -4854,7 +5256,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,
@@ -4868,8 +5272,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,
@@ -4883,8 +5291,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,
@@ -4896,10 +5308,14 @@ getObjectIdentityParts(const ObjectAddress *object,
 			{
 				ForeignDataWrapper *fdw;
 
-				fdw = GetForeignDataWrapper(object->objectId);
-				appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
-				if (objname)
-					*objname = list_make1(pstrdup(fdw->fdwname));
+				fdw = GetForeignDataWrapperExtended(object->objectId,
+													missing_ok);
+				if (fdw)
+				{
+					appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
+					if (objname)
+						*objname = list_make1(pstrdup(fdw->fdwname));
+				}
 				break;
 			}
 
@@ -4907,11 +5323,15 @@ getObjectIdentityParts(const ObjectAddress *object,
 			{
 				ForeignServer *srv;
 
-				srv = GetForeignServer(object->objectId);
-				appendStringInfoString(&buffer,
-									   quote_identifier(srv->servername));
-				if (objname)
-					*objname = list_make1(pstrdup(srv->servername));
+				srv = GetForeignServerExtended(object->objectId,
+											   missing_ok);
+				if (srv)
+				{
+					appendStringInfoString(&buffer,
+										   quote_identifier(srv->servername));
+					if (objname)
+						*objname = list_make1(pstrdup(srv->servername));
+				}
 				break;
 			}
 
@@ -4926,8 +5346,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);
@@ -4974,8 +5398,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);
 
@@ -5037,8 +5469,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);
@@ -5057,8 +5493,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)));
@@ -5078,14 +5518,20 @@ getObjectIdentityParts(const ObjectAddress *object,
 												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)));
 
@@ -5097,11 +5543,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;
 			}
 
@@ -5114,13 +5563,17 @@ 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);
 
-				getRelationIdentity(&buffer, prform->prrelid, objname);
+				getRelationIdentity(&buffer, prform->prrelid, objname, false);
 				appendStringInfo(&buffer, " in publication %s", pubname);
 
 				if (objargs)
@@ -5134,11 +5587,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;
 			}
 
@@ -5157,8 +5613,14 @@ getObjectIdentityParts(const ObjectAddress *object,
 												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);
 
@@ -5184,20 +5646,34 @@ 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);
+	if (!missing_ok)
+	{
+		/*
+		 * 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);
+	}
+	else
+	{
+		/* 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;
@@ -5207,7 +5683,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));
@@ -5236,7 +5716,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;
@@ -5245,7 +5726,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 3e7c1067d8..608d66a1c6 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1430,10 +1430,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);
 
@@ -2092,8 +2093,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 31dcfe7b11..b80d233ede 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 588c1ec536..fbdc389e4c 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9403,7 +9403,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;
 				}
@@ -9430,7 +9430,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;
 
@@ -9449,7 +9449,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;
 
@@ -9467,7 +9467,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;
 
@@ -9528,7 +9528,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 e3f02b52d9..f521a2ecce 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -412,7 +412,8 @@ format_procedure_extended(Oid procedure_oid, bits16 flags)
  * 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;
@@ -422,7 +423,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;
@@ -852,15 +857,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 3f95733eb1..fc420351be 100644
--- a/src/include/catalog/objectaddress.h
+++ b/src/include/catalog/objectaddress.h
@@ -69,14 +69,18 @@ extern bool get_object_namensp_unique(Oid class_id);
 extern HeapTuple get_catalog_object_by_oid(Relation catalog,
 						  AttrNumber oidcol, 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 3c22312a5b..32deead409 100644
--- a/src/include/utils/regproc.h
+++ b/src/include/utils/regproc.h
@@ -29,10 +29,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 4085e451e4..705979cefd 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -499,3 +499,711 @@ DROP SCHEMA addr_nsp CASCADE;
 NOTICE:  drop cascades to 14 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 
+----------+--------+------+----------
+ relation |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_class'::regclass, 'pg_class'::regclass, 100); -- no column for relation
+     type     | schema | name | identity 
+--------------+--------+------+----------
+ table column |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_proc'::regclass, 0, 0); -- no function
+  type   | schema | name | identity 
+---------+--------+------+----------
+ routine |        |      | 
+(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 
+------------+--------+------+----------
+ constraint |        |      | 
+(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 |        |      | 
+(1 row)
+
+SELECT * FROM pg_identify_object('pg_operator'::regclass, 0, 0); -- no operator
+   type   | schema | name | identity 
+----------+--------+------+----------
+ operator |        |      | 
+(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 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_proc'::regclass, 0, 0); -- no function
+ pg_describe_object 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_type'::regclass, 0, 0); -- no type
+ pg_describe_object 
+--------------------
+ 
+(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 
+--------------------
+ 
+(1 row)
+
+SELECT pg_describe_object('pg_operator'::regclass, 0, 0); -- no operator
+ pg_describe_object 
+--------------------
+ 
+(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 
+----------+--------------+-------------
+ relation |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_class'::regclass, 'pg_class'::regclass, 100); -- no column for relation
+     type     | object_names | object_args 
+--------------+--------------+-------------
+ table column |              | 
+(1 row)
+
+SELECT * FROM pg_identify_object_as_address('pg_proc'::regclass, 0, 0); -- no function
+  type   | object_names | object_args 
+---------+--------------+-------------
+ routine |              | 
+(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 
+------------+--------------+-------------
+ constraint |              | 
+(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 |              | 
+(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 d7df322873..51fdf12056 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -218,3 +218,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, 100); -- 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, 100); -- 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.20.1

Attachment: signature.asc
Description: PGP signature

Reply via email to