diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 80a29c0841d..d9f5ac7e23d 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -1582,10 +1582,19 @@ doDeletion(const ObjectAddress *object, int flags) elog(ERROR, "global objects cannot be deleted by doDeletion"); break; + default: + { + struct CustomObjectClass *coc; + + coc = find_custom_object_class_by_classid(object->classId, false); + if (coc->do_delete) + coc->do_delete(coc, object, flags); /* * There's intentionally no default: case here; we want the * compiler to warn if a new OCLASS hasn't been handled above. */ + break; + } } } @@ -2960,6 +2969,15 @@ getObjectClass(const ObjectAddress *object) case TaskRelationId: return OCLASS_TASK; + + default: + { + struct CustomObjectClass *coc; + coc = find_custom_object_class_by_classid(object->classId, true); + if (coc) + return coc->oclass; + break; + } } /* shouldn't get here */ @@ -3097,3 +3115,67 @@ DeleteInitPrivs(const ObjectAddress *object) table_close(relation, RowExclusiveLock); } + +/* Custom object class */ +#define FIRST_COSTOM_OBJECT_CLASS 512 +static List *custom_object_class_list = NIL; + +/* + * don't support unregister object class in a session. + * Return: the oclass allocated by the kernel + */ +int +register_custom_object_class(struct CustomObjectClass *coc) +{ + MemoryContext saved_ctx; + /* check class_id and required callbacks */ + Assert(OidIsValid(coc->class_id)); + Assert(coc->do_delete); + + if (find_custom_object_class_by_classid(coc->class_id, true)) + elog(ERROR, "class_id %u exists already", coc->class_id); + + coc->oclass = FIRST_COSTOM_OBJECT_CLASS + list_length(custom_object_class_list); + saved_ctx = MemoryContextSwitchTo(TopMemoryContext); + custom_object_class_list = lappend(custom_object_class_list, coc); + MemoryContextSwitchTo(saved_ctx); + return coc->oclass; +} + +struct CustomObjectClass * +find_custom_object_class(int oclass) +{ + int index = oclass - FIRST_COSTOM_OBJECT_CLASS; + + if (oclass < FIRST_COSTOM_OBJECT_CLASS) + elog(ERROR, "unexpected oclass, found builtin one %u", oclass); + if (index >= list_length(custom_object_class_list)) + elog(ERROR, "unexpected oclass beyond custom object class %u", oclass); + + return (struct CustomObjectClass *) list_nth(custom_object_class_list, index); +} + +struct CustomObjectClass * +find_custom_object_class_by_classid(Oid class_id, bool missing_ok) +{ + ListCell *lc; + +#ifdef USE_ASSERT_CHECKING + int n = lengthof(object_classes); + Assert(n < FIRST_COSTOM_OBJECT_CLASS); + + for (int i = 0; i < n; i++) { + if (object_classes[i] == class_id) + elog(ERROR, "find builtin object class by class id %u", class_id); + } +#endif + + foreach(lc, custom_object_class_list) { + struct CustomObjectClass *coc = (struct CustomObjectClass *)lfirst(lc); + if (coc->class_id == class_id) + return coc; + } + if (!missing_ok) + ereport(ERROR, (errmsg("found no custom object class for class_id=%u", class_id))); + return NULL; +} diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index b3695086ed3..fb0fd99dda0 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -4079,6 +4079,15 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok) appendStringInfo(&buffer, _("history password for role %s"), username); break; } + + default: + { + struct CustomObjectClass *coc; + + coc = find_custom_object_class_by_classid(object->classId, missing_ok); + if (coc && coc->object_desc) + coc->object_desc(coc, object, missing_ok, &buffer); + } } /* an empty buffer is equivalent to no object found */ @@ -4650,10 +4659,19 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok) appendStringInfoString(&buffer, "password_history"); break; + default: + { + struct CustomObjectClass *coc; + + coc = find_custom_object_class_by_classid(object->classId, false); + if (coc->object_type_desc) + coc->object_type_desc(coc, object, missing_ok, &buffer); /* * There's intentionally no default: case here; we want the * compiler to warn if a new OCLASS hasn't been handled above. */ + break; + } } /* the result can never be empty */ @@ -5988,10 +6006,19 @@ getObjectIdentityParts(const ObjectAddress *object, break; } + default: + { + struct CustomObjectClass *coc; + + coc = find_custom_object_class_by_classid(object->classId, false); + if (coc->object_identity_parts) + coc->object_identity_parts(coc, object, objname, objargs, missing_ok, &buffer); /* * There's intentionally no default: case here; we want the * compiler to warn if a new OCLASS hasn't been handled above. */ + break; + } } if (!missing_ok) diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 7337bcc31e5..38ca72b479c 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -736,10 +736,19 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid, /* ignore object types that don't have schema-qualified names */ break; + default: + { + struct CustomObjectClass *coc; + + coc = find_custom_object_class_by_classid(classId, false); + if (coc->alter_namespace) + coc->alter_namespace(coc, &dep, nspOid, objsMoved); /* * There's intentionally no default: case here; we want the * compiler to warn if a new OCLASS hasn't been handled above. */ + break; + } } return oldNspOid; diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 445244c0046..8b2994a3448 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -1069,10 +1069,20 @@ EventTriggerSupportsObjectClass(ObjectClass objclass) case OCLASS_TASK: return false; + default: + { + struct CustomObjectClass *coc; + + coc = find_custom_object_class(objclass); + Assert(coc); + if (coc->support_event_trigger) + return coc->support_event_trigger(coc); /* * There's intentionally no default: case here; we want the * compiler to warn if a new OCLASS hasn't been handled above. */ + break; + } } return true; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 29d3304c575..11a9a6b3139 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -14028,10 +14028,18 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, getObjectDescription(&foundObject, false)); break; + default: + { + struct CustomObjectClass *coc; + coc = find_custom_object_class_by_classid(foundObject.classId, false); + if (coc->alter_column_type) + coc->alter_column_type(coc); /* * There's intentionally no default: case here; we want the * compiler to warn if a new OCLASS hasn't been handled above. */ + break; + } } } diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index bee28807e9c..2f514b8c05c 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -290,4 +290,39 @@ extern void recordProfileDependency(Oid roleId, Oid profileId); extern void changeProfileDependency(Oid roleId, Oid profileId); +/* Custom object class */ +struct StringInfoData; +struct CustomObjectClass { + Oid class_id; + int oclass; + + void (*do_delete)(struct CustomObjectClass *self, + const ObjectAddress *object, int flags); + void (*object_desc)(struct CustomObjectClass *self, + const ObjectAddress *object, + bool missing_ok, + struct StringInfoData *buffer); + void (*object_type_desc)(struct CustomObjectClass *self, + const ObjectAddress *object, + bool missing_ok, + struct StringInfoData *buffer); + void (*object_identity_parts)(struct CustomObjectClass *self, + const ObjectAddress *object, + List **objname, + List **objargs, + bool missing_ok, + struct StringInfoData *buffer); + + Oid (*alter_namespace)(struct CustomObjectClass *self, + const ObjectAddress *object, + Oid nspOid, + ObjectAddresses *objsMoved); + bool (*support_event_trigger)(struct CustomObjectClass *self); + // FIXME: unclear which arguments should pass + void (*alter_column_type)(struct CustomObjectClass *self); + +}; +extern int register_custom_object_class(struct CustomObjectClass *coc); +extern struct CustomObjectClass *find_custom_object_class(int oclass); +extern struct CustomObjectClass *find_custom_object_class_by_classid(Oid class_id, bool missing_ok); #endif /* DEPENDENCY_H */