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