diff --git a/contrib/sepgsql/database.c b/contrib/sepgsql/database.c
index 4e83b7bfa8..ec20378593 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 53f6f41c5c..75ee612bcd 100644
--- a/contrib/sepgsql/dml.c
+++ b/contrib/sepgsql/dml.c
@@ -179,7 +179,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:
@@ -256,7 +256,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 147ab67f32..32e405530b 100644
--- a/contrib/sepgsql/label.c
+++ b/contrib/sepgsql/label.c
@@ -355,7 +355,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,
@@ -523,7 +523,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 2c244a9003..d5d7dbe103 100644
--- a/contrib/sepgsql/proc.c
+++ b/contrib/sepgsql/proc.c
@@ -80,7 +80,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);
/*
@@ -114,7 +114,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, ')');
@@ -164,7 +164,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,
@@ -179,7 +179,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,
@@ -204,7 +204,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
@@ -292,7 +292,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,
@@ -324,7 +324,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 380bc6094d..b50f386f5b 100644
--- a/contrib/sepgsql/relation.c
+++ b/contrib/sepgsql/relation.c
@@ -102,7 +102,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,
@@ -146,7 +146,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,
@@ -178,7 +178,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
@@ -222,7 +222,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,
@@ -288,7 +288,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)
@@ -450,7 +450,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,
@@ -472,7 +472,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,
@@ -503,7 +503,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,
@@ -584,7 +584,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
@@ -695,7 +695,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 90ecbc1725..3b2b80be83 100644
--- a/contrib/sepgsql/schema.c
+++ b/contrib/sepgsql/schema.c
@@ -123,7 +123,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,
@@ -148,7 +148,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
@@ -186,7 +186,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 cc83d6652e..959f6a1c2f 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -22826,7 +22826,8 @@ SELECT collation for ('foo' COLLATE "de_DE");
object). This description is intended to be human-readable, and might
be translated, depending on server configuration. This is especially
useful to determine the identity of an object referenced in the
- pg_depend catalog.
+ pg_depend catalog. This function returns
+ NULL values for undefined objects.
@@ -22858,7 +22859,8 @@ SELECT collation for ('foo' COLLATE "de_DE");
otherwise NULL;
identity 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.
+ format being schema-qualified and quoted as necessary. Undefined
+ objects are identified with NULL values.
@@ -22915,6 +22917,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
objsubid is the sub-object ID, or zero if none.
This function is the inverse
of pg_identify_object_as_address.
+ Undefined objects are identified with NULL values.
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index b33a2f94af..f515e2c308 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -743,8 +743,8 @@ findDependentObjects(const ObjectAddress *object,
if (!object_address_present_add_flags(object, objflags,
targetObjects))
elog(ERROR, "deletion of owning object %s failed to delete %s",
- getObjectDescription(&otherObject),
- getObjectDescription(object));
+ getObjectDescription(&otherObject, false),
+ getObjectDescription(object, false));
/* And we're done here. */
return;
@@ -790,11 +790,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;
}
}
@@ -812,14 +812,14 @@ findDependentObjects(const ObjectAddress *object,
char *otherObjDesc;
if (OidIsValid(partitionObject.classId))
- otherObjDesc = getObjectDescription(&partitionObject);
+ otherObjDesc = getObjectDescription(&partitionObject, false);
else
- otherObjDesc = getObjectDescription(&owningObject);
+ otherObjDesc = getObjectDescription(&owningObject, false);
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("cannot drop %s because %s requires it",
- getObjectDescription(object), otherObjDesc),
+ getObjectDescription(object, false), otherObjDesc),
errhint("You can drop %s instead.", otherObjDesc)));
}
@@ -929,12 +929,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;
}
@@ -1052,12 +1052,13 @@ reportDependentObjects(const ObjectAddresses *targetObjects,
!(extra->flags & DEPFLAG_PARTITION))
{
const ObjectAddress *object = &targetObjects->refs[i];
- char *otherObjDesc = getObjectDescription(&extra->dependee);
+ char *otherObjDesc = getObjectDescription(&extra->dependee,
+ false);
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("cannot drop %s because %s requires it",
- getObjectDescription(object), otherObjDesc),
+ getObjectDescription(object, false), otherObjDesc),
errhint("You can drop %s instead.", otherObjDesc)));
}
}
@@ -1105,7 +1106,7 @@ reportDependentObjects(const ObjectAddresses *targetObjects,
if (extra->flags & DEPFLAG_SUBOBJECT)
continue;
- objDesc = getObjectDescription(obj);
+ objDesc = getObjectDescription(obj, false);
/*
* If, at any stage of the recursive search, we reached the object via
@@ -1129,7 +1130,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)
{
@@ -1187,7 +1189,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 534df8e802..6dfe1be2cc 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -879,14 +879,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
@@ -1759,7 +1765,7 @@ get_object_address_opf_member(ObjectType objtype,
membernum,
TypeNameToString(typenames[0]),
TypeNameToString(typenames[1]),
- getObjectDescription(&famaddr))));
+ getObjectDescription(&famaddr, false))));
}
else
{
@@ -1790,7 +1796,7 @@ get_object_address_opf_member(ObjectType objtype,
membernum,
TypeNameToString(typenames[0]),
TypeNameToString(typenames[1]),
- getObjectDescription(&famaddr))));
+ getObjectDescription(&famaddr, false))));
}
else
{
@@ -2844,10 +2850,11 @@ 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. NULL is returned for an undefined
+ * object if missing_ok is true, else an error is generated.
*/
char *
-getObjectDescription(const ObjectAddress *object)
+getObjectDescription(const ObjectAddress *object, bool missing_ok)
{
StringInfoData buffer;
@@ -2857,33 +2864,49 @@ 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,
- object->objectSubId,
- false),
- rel.data);
+ attname, rel.data);
pfree(rel.data);
}
break;
case OCLASS_PROC:
- appendStringInfo(&buffer, _("function %s"),
- format_procedure(object->objectId));
- break;
+ {
+ bits16 flags = FORMAT_PROC_INVALID_AS_NULL;
+ char *proname = format_procedure_extended(object->objectId,
+ flags);
+ if (proname == NULL)
+ break;
+
+ appendStringInfo(&buffer, _("function %s"), proname);
+ break;
+ }
case OCLASS_TYPE:
- appendStringInfo(&buffer, _("type %s"),
- format_type_be(object->objectId));
- break;
+ {
+ bits16 flags = FORMAT_TYPE_INVALID_AS_NULL;
+ char *typname = format_type_extended(object->objectId, -1,
+ flags);
+ if (typname == NULL)
+ break;
+
+ appendStringInfo(&buffer, _("type %s"), typname);
+ break;
+ }
case OCLASS_CAST:
{
@@ -2906,8 +2929,15 @@ 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);
+ table_close(castDesc, AccessShareLock);
+ break;
+ }
castForm = (Form_pg_cast) GETSTRUCT(tup);
@@ -2929,8 +2959,13 @@ 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 */
@@ -2954,8 +2989,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))
@@ -2963,7 +3003,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);
@@ -2988,8 +3028,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 */
@@ -3027,8 +3072,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);
+ table_close(attrdefDesc, AccessShareLock);
+ break;
+ }
attrdef = (Form_pg_attrdef) GETSTRUCT(tup);
@@ -3038,7 +3090,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);
table_close(attrdefDesc, AccessShareLock);
@@ -3046,19 +3098,35 @@ 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;
+ {
+ bits16 flags = FORMAT_OPERATOR_INVALID_AS_NULL;
+ char *oprname = format_operator_extended(object->objectId,
+ flags);
+
+ if (oprname == NULL)
+ break;
+
+ appendStringInfo(&buffer, _("operator %s"), oprname);
+ break;
+ }
case OCLASS_OPCLASS:
{
@@ -3071,8 +3139,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,
@@ -3099,7 +3172,7 @@ getObjectDescription(const ObjectAddress *object)
}
case OCLASS_OPFAMILY:
- getOpFamilyDescription(&buffer, object->objectId);
+ getOpFamilyDescription(&buffer, object->objectId, missing_ok);
break;
case OCLASS_AM:
@@ -3109,8 +3182,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);
@@ -3140,13 +3218,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);
+ table_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
@@ -3190,13 +3275,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);
+ table_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
@@ -3239,12 +3331,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);
+ table_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"),
@@ -3277,12 +3377,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);
+ table_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"),
@@ -3299,8 +3407,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;
}
@@ -3314,8 +3426,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 */
@@ -3341,8 +3458,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 */
@@ -3367,8 +3488,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 */
@@ -3393,8 +3519,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 */
@@ -3419,8 +3550,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 */
@@ -3438,8 +3574,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;
}
@@ -3449,8 +3588,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;
}
@@ -3461,8 +3604,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;
}
@@ -3471,8 +3618,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;
}
@@ -3480,8 +3629,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;
}
@@ -3496,8 +3646,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);
@@ -3537,8 +3692,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);
+ table_close(defaclrel, AccessShareLock);
+ break;
+ }
defacl = (Form_pg_default_acl) GETSTRUCT(tup);
@@ -3621,8 +3783,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;
}
@@ -3634,8 +3800,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);
@@ -3664,12 +3834,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);
+ table_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"),
@@ -3682,9 +3860,10 @@ 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;
}
@@ -3698,14 +3877,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"),
@@ -3717,9 +3900,10 @@ getObjectDescription(const ObjectAddress *object)
case OCLASS_SUBSCRIPTION:
{
- appendStringInfo(&buffer, _("subscription %s"),
- get_subscription_name(object->objectId,
- false));
+ char *subname = get_subscription_name(object->objectId,
+ missing_ok);
+ if (subname)
+ appendStringInfo(&buffer, _("subscription %s"), subname);
break;
}
@@ -3731,8 +3915,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);
@@ -3750,6 +3938,10 @@ getObjectDescription(const ObjectAddress *object)
*/
}
+ /* an empty buffer is equivalent to no object found */
+ if (buffer.len == 0)
+ return NULL;
+
return buffer.data;
}
@@ -3765,7 +3957,7 @@ getObjectDescriptionOids(Oid classid, Oid objid)
address.objectId = objid;
address.objectSubId = 0;
- return getObjectDescription(&address);
+ return getObjectDescription(&address, false);
}
/*
@@ -3774,7 +3966,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;
@@ -3784,7 +3976,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 */
@@ -3845,7 +4041,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;
@@ -3855,7 +4051,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));
@@ -3899,7 +4099,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));
}
@@ -3914,6 +4118,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];
@@ -3988,12 +4193,18 @@ pg_identify_object(PG_FUNCTION_ARGS)
table_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
+ * could not 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));
@@ -4004,7 +4215,7 @@ pg_identify_object(PG_FUNCTION_ARGS)
nulls[1] = true;
/* object name */
- if (objname)
+ if (objname && objidentity)
{
values[2] = CStringGetTextDatum(objname);
nulls[2] = false;
@@ -4013,8 +4224,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);
@@ -4058,26 +4274,34 @@ 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 */
- if (names != NIL)
- values[1] = PointerGetDatum(strlist_to_textarray(names));
+ identity = getObjectIdentityParts(&address, &names, &args, true);
+ if (identity == NULL)
+ {
+ nulls[1] = true;
+ nulls[2] = true;
+ }
else
- values[1] = PointerGetDatum(construct_empty_array(TEXTOID));
- nulls[1] = false;
+ {
+ pfree(identity);
- /* object_args */
- if (args)
- values[2] = PointerGetDatum(strlist_to_textarray(args));
- else
- values[2] = PointerGetDatum(construct_empty_array(TEXTOID));
- nulls[2] = false;
+ /* object_names */
+ if (names != NIL)
+ values[1] = PointerGetDatum(strlist_to_textarray(names));
+ else
+ values[1] = PointerGetDatum(construct_empty_array(TEXTOID));
+ 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);
@@ -4091,7 +4315,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;
@@ -4101,11 +4325,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:
@@ -4121,7 +4347,8 @@ getObjectTypeDescription(const ObjectAddress *object)
break;
case OCLASS_CONSTRAINT:
- getConstraintTypeDescription(&buffer, object->objectId);
+ getConstraintTypeDescription(&buffer, object->objectId,
+ missing_ok);
break;
case OCLASS_CONVERSION:
@@ -4258,6 +4485,10 @@ getObjectTypeDescription(const ObjectAddress *object)
*/
}
+ /* an empty string is equivalent to no object found */
+ if (buffer.len == 0)
+ return NULL;
+
return buffer.data;
}
@@ -4265,7 +4496,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;
@@ -4273,7 +4505,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)
@@ -4320,7 +4559,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;
@@ -4330,7 +4569,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);
+
+ table_close(constrRel, AccessShareLock);
+
+ /* fallback to "constraint" for an undefined object */
+ appendStringInfoString(buffer, "constraint");
+ return;
+ }
constrForm = (Form_pg_constraint) GETSTRUCT(constrTup);
@@ -4348,7 +4596,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;
@@ -4356,7 +4605,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 "procedure" for an undefined object */
+ appendStringInfoString(buffer, "routine");
+ return;
+ }
procForm = (Form_pg_proc) GETSTRUCT(procTup);
if (procForm->prokind == PROKIND_AGGREGATE)
@@ -4373,12 +4629,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 could not
+ * 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);
}
/*
@@ -4387,11 +4644,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 could not be found.
*/
char *
getObjectIdentityParts(const ObjectAddress *object,
- List **objname, List **objargs)
+ List **objname, List **objargs,
+ bool missing_ok)
{
StringInfoData buffer;
@@ -4413,31 +4672,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;
+ {
+ bits16 flags = FORMAT_PROC_FORCE_QUALIFY | FORMAT_PROC_INVALID_AS_NULL;
+ char *proname = format_procedure_extended(object->objectId,
+ flags);
+ if (proname == NULL)
+ break;
+
+ appendStringInfoString(&buffer, proname);
+ if (objname)
+ format_procedure_parts(object->objectId, objname, objargs,
+ missing_ok);
+ break;
+ }
case OCLASS_TYPE:
{
+ bits16 flags = FORMAT_TYPE_INVALID_AS_NULL | FORMAT_TYPE_FORCE_QUALIFY;
char *typeout;
- typeout = format_type_be_qualified(object->objectId);
+ typeout = format_type_extended(object->objectId, -1, flags);
+
+ if (typeout == NULL)
+ break;
+
appendStringInfoString(&buffer, typeout);
if (objname)
*objname = list_make1(typeout);
@@ -4456,8 +4747,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);
+
+ table_close(castRel, AccessShareLock);
+ break;
+ }
castForm = (Form_pg_cast) GETSTRUCT(tup);
@@ -4484,8 +4781,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,
@@ -4506,15 +4807,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)));
}
@@ -4529,7 +4835,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)));
@@ -4548,8 +4855,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,
@@ -4585,8 +4896,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);
+ table_close(attrdefDesc, AccessShareLock);
+ break;
+ }
attrdef = (Form_pg_attrdef) GETSTRUCT(tup);
@@ -4596,7 +4914,8 @@ getObjectIdentityParts(const ObjectAddress *object,
appendStringInfo(&buffer, "for %s",
getObjectIdentityParts(&colobject,
- objname, objargs));
+ objname, objargs,
+ false));
systable_endscan(adscan);
table_close(attrdefDesc, AccessShareLock);
@@ -4611,8 +4930,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)));
@@ -4622,6 +4945,8 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
case OCLASS_LARGEOBJECT:
+ if (!LargeObjectExists(object->objectId))
+ break;
appendStringInfo(&buffer, "%u",
object->objectId);
if (objname)
@@ -4629,11 +4954,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;
+ {
+ bits16 flags = FORMAT_OPERATOR_FORCE_QUALIFY | FORMAT_OPERATOR_INVALID_AS_NULL;
+ char *oprname = format_operator_extended(object->objectId,
+ flags);
+ if (oprname == NULL)
+ break;
+
+ appendStringInfoString(&buffer, oprname);
+ if (objname)
+ format_operator_parts(object->objectId, objname, objargs, missing_ok);
+ break;
+ }
case OCLASS_OPCLASS:
{
@@ -4646,8 +4978,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);
@@ -4673,7 +5009,8 @@ getObjectIdentityParts(const ObjectAddress *object,
}
case OCLASS_OPFAMILY:
- getOpFamilyIdentity(&buffer, object->objectId, objname);
+ getOpFamilyIdentity(&buffer, object->objectId, objname,
+ missing_ok);
break;
case OCLASS_AM:
@@ -4682,8 +5019,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);
@@ -4715,13 +5056,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);
+ table_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);
@@ -4769,13 +5118,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);
+ table_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);
@@ -4810,14 +5167,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);
+
+ table_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)));
@@ -4837,14 +5200,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);
+
+ table_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)));
@@ -4858,8 +5227,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)
@@ -4876,8 +5249,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,
@@ -4899,8 +5276,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,
@@ -4922,8 +5303,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,
@@ -4945,8 +5330,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,
@@ -4968,8 +5357,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,
@@ -4986,7 +5379,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,
@@ -5000,8 +5395,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,
@@ -5015,8 +5414,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,
@@ -5028,10 +5431,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;
}
@@ -5039,11 +5446,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;
}
@@ -5058,8 +5469,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);
@@ -5106,8 +5521,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);
+ table_close(defaclrel, AccessShareLock);
+ break;
+
+ }
defacl = (Form_pg_default_acl) GETSTRUCT(tup);
@@ -5169,8 +5592,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);
@@ -5189,8 +5616,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)));
@@ -5210,14 +5641,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);
+
+ table_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)));
@@ -5229,11 +5666,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;
}
@@ -5246,13 +5686,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)
@@ -5266,11 +5710,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;
}
@@ -5289,8 +5736,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);
+
+ table_close(transformDesc, AccessShareLock);
+ break;
+ }
transform = (Form_pg_transform) GETSTRUCT(tup);
@@ -5316,20 +5769,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 buffer is equivalent to no object found */
+ 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;
@@ -5339,7 +5806,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));
@@ -5368,7 +5839,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;
@@ -5377,7 +5849,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 21cfdcace9..70baf03178 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -160,7 +160,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))));
}
}
@@ -536,7 +536,7 @@ changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
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 f776e821b3..082b935a69 100644
--- a/src/backend/catalog/pg_shdepend.c
+++ b/src/backend/catalog/pg_shdepend.c
@@ -638,7 +638,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;
@@ -1147,7 +1147,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)
@@ -1283,7 +1283,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],
@@ -1429,7 +1429,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 151cdfff39..7844880170 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1267,10 +1267,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);
@@ -1929,8 +1930,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 3b69ab7ed5..c796fcd8da 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -2919,7 +2919,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))));
}
@@ -3328,7 +3328,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))));
/*
@@ -3368,7 +3368,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 20049f2c55..27b596cb59 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -11304,7 +11304,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;
}
@@ -11320,7 +11320,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;
@@ -11339,7 +11339,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;
@@ -11357,7 +11357,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;
@@ -11418,7 +11418,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
* a column.
*/
elog(ERROR, "unexpected object depending on column: %s",
- getObjectDescription(&foundObject));
+ getObjectDescription(&foundObject, false));
break;
/*
@@ -11474,7 +11474,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
foundDep->refobjsubid != 0)
)
elog(ERROR, "found unexpected dependency for column: %s",
- getObjectDescription(&foundObject));
+ getObjectDescription(&foundObject, false));
CatalogTupleDelete(depRel, &depTup->t_self);
}
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index b41189db5c..6c1ee9c92d 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -418,7 +418,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;
@@ -428,7 +429,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;
@@ -856,15 +861,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 144715d4f4..b876617c9d 100644
--- a/src/include/catalog/objectaddress.h
+++ b/src/include/catalog/objectaddress.h
@@ -70,14 +70,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 struct 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 145452a5ad..330417e888 100644
--- a/src/include/utils/regproc.h
+++ b/src/include/utils/regproc.h
@@ -29,10 +29,11 @@ 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 d6d1470156..388097a695 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -521,3 +521,103 @@ drop cascades to function trig()
drop cascades to function proc(integer)
DROP OWNED BY regress_addr_user;
DROP USER regress_addr_user;
+--
+-- Checks for invalid objects
+--
+-- Make sure that NULL handling is correct.
+\pset null 'NULL'
+-- Temporarily disable fancy output, so as future additions never create
+-- a large amount of diffs.
+\a\t
+-- Keep this list in the same order as getObjectIdentityParts()
+-- in objectaddress.c.
+WITH objects (classid, objid, objsubid) AS (VALUES
+ ('pg_class'::regclass, 0, 0), -- no relation
+ ('pg_class'::regclass, 'pg_class'::regclass, 100), -- no column for relation
+ ('pg_proc'::regclass, 0, 0), -- no function
+ ('pg_type'::regclass, 0, 0), -- no type
+ ('pg_cast'::regclass, 0, 0), -- no cast
+ ('pg_collation'::regclass, 0, 0), -- no collation
+ ('pg_constraint'::regclass, 0, 0), -- no constraint
+ ('pg_conversion'::regclass, 0, 0), -- no conversion
+ ('pg_attrdef'::regclass, 0, 0), -- no default attribute
+ ('pg_language'::regclass, 0, 0), -- no language
+ ('pg_largeobject'::regclass, 0, 0), -- no large object, no error
+ ('pg_operator'::regclass, 0, 0), -- no operator
+ ('pg_opclass'::regclass, 0, 0), -- no opclass, no need to check for no access method
+ ('pg_opfamily'::regclass, 0, 0), -- no opfamily
+ ('pg_am'::regclass, 0, 0), -- no access method
+ ('pg_amop'::regclass, 0, 0), -- no AM operator
+ ('pg_amproc'::regclass, 0, 0), -- no AM proc
+ ('pg_rewrite'::regclass, 0, 0), -- no rewrite
+ ('pg_trigger'::regclass, 0, 0), -- no trigger
+ ('pg_namespace'::regclass, 0, 0), -- no schema
+ ('pg_statistic_ext'::regclass, 0, 0), -- no statistics
+ ('pg_ts_parser'::regclass, 0, 0), -- no TS parser
+ ('pg_ts_dict'::regclass, 0, 0), -- no TS dictionnary
+ ('pg_ts_template'::regclass, 0, 0), -- no TS template
+ ('pg_ts_config'::regclass, 0, 0), -- no TS configuration
+ ('pg_authid'::regclass, 0, 0), -- no role
+ ('pg_database'::regclass, 0, 0), -- no database
+ ('pg_tablespace'::regclass, 0, 0), -- no tablespace
+ ('pg_foreign_data_wrapper'::regclass, 0, 0), -- no FDW
+ ('pg_foreign_server'::regclass, 0, 0), -- no server
+ ('pg_user_mapping'::regclass, 0, 0), -- no user mapping
+ ('pg_default_acl'::regclass, 0, 0), -- no default ACL
+ ('pg_extension'::regclass, 0, 0), -- no extension
+ ('pg_event_trigger'::regclass, 0, 0), -- no event trigger
+ ('pg_policy'::regclass, 0, 0), -- no policy
+ ('pg_publication'::regclass, 0, 0), -- no publication
+ ('pg_publication_rel'::regclass, 0, 0), -- no publication relation
+ ('pg_subscription'::regclass, 0, 0), -- no subscription
+ ('pg_transform'::regclass, 0, 0) -- no transformation
+ )
+SELECT ROW(pg_identify_object(objects.classid, objects.objid, objects.objsubid))
+ AS ident,
+ ROW(pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid))
+ AS addr,
+ pg_describe_object(objects.classid, objects.objid, objects.objsubid)
+ AS descr
+FROM objects
+ORDER BY objects.classid, objects.objid, objects.objsubid;
+("(""default acl"",,,)")|("(""default acl"",,)")|NULL
+("(tablespace,,,)")|("(tablespace,,)")|NULL
+("(type,,,)")|("(type,,)")|NULL
+("(routine,,,)")|("(routine,,)")|NULL
+("(relation,,,)")|("(relation,,)")|NULL
+("(""table column"",,,)")|("(""table column"",,)")|NULL
+("(role,,,)")|("(role,,)")|NULL
+("(database,,,)")|("(database,,)")|NULL
+("(server,,,)")|("(server,,)")|NULL
+("(""user mapping"",,,)")|("(""user mapping"",,)")|NULL
+("(""foreign-data wrapper"",,,)")|("(""foreign-data wrapper"",,)")|NULL
+("(""access method"",,,)")|("(""access method"",,)")|NULL
+("(""operator of access method"",,,)")|("(""operator of access method"",,)")|NULL
+("(""function of access method"",,,)")|("(""function of access method"",,)")|NULL
+("(""default value"",,,)")|("(""default value"",,)")|NULL
+("(cast,,,)")|("(cast,,)")|NULL
+("(constraint,,,)")|("(constraint,,)")|NULL
+("(conversion,,,)")|("(conversion,,)")|NULL
+("(language,,,)")|("(language,,)")|NULL
+("(""large object"",,,)")|("(""large object"",,)")|NULL
+("(schema,,,)")|("(schema,,)")|NULL
+("(""operator class"",,,)")|("(""operator class"",,)")|NULL
+("(operator,,,)")|("(operator,,)")|NULL
+("(rule,,,)")|("(rule,,)")|NULL
+("(trigger,,,)")|("(trigger,,)")|NULL
+("(""operator family"",,,)")|("(""operator family"",,)")|NULL
+("(extension,,,)")|("(extension,,)")|NULL
+("(policy,,,)")|("(policy,,)")|NULL
+("(""statistics object"",,,)")|("(""statistics object"",,)")|NULL
+("(collation,,,)")|("(collation,,)")|NULL
+("(""event trigger"",,,)")|("(""event trigger"",,)")|NULL
+("(transform,,,)")|("(transform,,)")|NULL
+("(""text search dictionary"",,,)")|("(""text search dictionary"",,)")|NULL
+("(""text search parser"",,,)")|("(""text search parser"",,)")|NULL
+("(""text search configuration"",,,)")|("(""text search configuration"",,)")|NULL
+("(""text search template"",,,)")|("(""text search template"",,)")|NULL
+("(subscription,,,)")|("(subscription,,)")|NULL
+("(publication,,,)")|("(publication,,)")|NULL
+("(""publication relation"",,,)")|("(""publication relation"",,)")|NULL
+-- restore normal output mode
+\a\t
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 8e06248eb5..2f4f66e3e1 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -221,3 +221,67 @@ DROP SCHEMA addr_nsp CASCADE;
DROP OWNED BY regress_addr_user;
DROP USER regress_addr_user;
+
+--
+-- Checks for invalid objects
+--
+-- Make sure that NULL handling is correct.
+\pset null 'NULL'
+-- Temporarily disable fancy output, so as future additions never create
+-- a large amount of diffs.
+\a\t
+
+-- Keep this list in the same order as getObjectIdentityParts()
+-- in objectaddress.c.
+WITH objects (classid, objid, objsubid) AS (VALUES
+ ('pg_class'::regclass, 0, 0), -- no relation
+ ('pg_class'::regclass, 'pg_class'::regclass, 100), -- no column for relation
+ ('pg_proc'::regclass, 0, 0), -- no function
+ ('pg_type'::regclass, 0, 0), -- no type
+ ('pg_cast'::regclass, 0, 0), -- no cast
+ ('pg_collation'::regclass, 0, 0), -- no collation
+ ('pg_constraint'::regclass, 0, 0), -- no constraint
+ ('pg_conversion'::regclass, 0, 0), -- no conversion
+ ('pg_attrdef'::regclass, 0, 0), -- no default attribute
+ ('pg_language'::regclass, 0, 0), -- no language
+ ('pg_largeobject'::regclass, 0, 0), -- no large object, no error
+ ('pg_operator'::regclass, 0, 0), -- no operator
+ ('pg_opclass'::regclass, 0, 0), -- no opclass, no need to check for no access method
+ ('pg_opfamily'::regclass, 0, 0), -- no opfamily
+ ('pg_am'::regclass, 0, 0), -- no access method
+ ('pg_amop'::regclass, 0, 0), -- no AM operator
+ ('pg_amproc'::regclass, 0, 0), -- no AM proc
+ ('pg_rewrite'::regclass, 0, 0), -- no rewrite
+ ('pg_trigger'::regclass, 0, 0), -- no trigger
+ ('pg_namespace'::regclass, 0, 0), -- no schema
+ ('pg_statistic_ext'::regclass, 0, 0), -- no statistics
+ ('pg_ts_parser'::regclass, 0, 0), -- no TS parser
+ ('pg_ts_dict'::regclass, 0, 0), -- no TS dictionnary
+ ('pg_ts_template'::regclass, 0, 0), -- no TS template
+ ('pg_ts_config'::regclass, 0, 0), -- no TS configuration
+ ('pg_authid'::regclass, 0, 0), -- no role
+ ('pg_database'::regclass, 0, 0), -- no database
+ ('pg_tablespace'::regclass, 0, 0), -- no tablespace
+ ('pg_foreign_data_wrapper'::regclass, 0, 0), -- no FDW
+ ('pg_foreign_server'::regclass, 0, 0), -- no server
+ ('pg_user_mapping'::regclass, 0, 0), -- no user mapping
+ ('pg_default_acl'::regclass, 0, 0), -- no default ACL
+ ('pg_extension'::regclass, 0, 0), -- no extension
+ ('pg_event_trigger'::regclass, 0, 0), -- no event trigger
+ ('pg_policy'::regclass, 0, 0), -- no policy
+ ('pg_publication'::regclass, 0, 0), -- no publication
+ ('pg_publication_rel'::regclass, 0, 0), -- no publication relation
+ ('pg_subscription'::regclass, 0, 0), -- no subscription
+ ('pg_transform'::regclass, 0, 0) -- no transformation
+ )
+SELECT ROW(pg_identify_object(objects.classid, objects.objid, objects.objsubid))
+ AS ident,
+ ROW(pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid))
+ AS addr,
+ pg_describe_object(objects.classid, objects.objid, objects.objsubid)
+ AS descr
+FROM objects
+ORDER BY objects.classid, objects.objid, objects.objsubid;
+
+-- restore normal output mode
+\a\t