Skip to content

Commit

Permalink
Add custom object class support
Browse files Browse the repository at this point in the history
Extension may have some database objects that need to exist/delete
consistently. We might use dependencies to record these relation-ship
and remove them consistently and clearly.

More generally, this patch allows extensions to create their own
auxiliary relations like system catalog in schema `pg_ext_aux`.
The auxiliary relations can't exist alone and have their own
dynamic oclass and callbacks to implement, including do_delete.
  • Loading branch information
wuhao authored and gfphoenix78 committed Dec 15, 2023
1 parent 8d43fd8 commit 7fc983d
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 0 deletions.
82 changes: 82 additions & 0 deletions src/backend/catalog/dependency.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}

Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
}
27 changes: 27 additions & 0 deletions src/backend/catalog/objectaddress.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions src/backend/commands/alter.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 10 additions & 0 deletions src/backend/commands/event_trigger.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions src/backend/commands/tablecmds.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}

Expand Down
35 changes: 35 additions & 0 deletions src/include/catalog/dependency.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

0 comments on commit 7fc983d

Please sign in to comment.