/*-------------------------------------------------------------------------- * * test_resowner_basic.c * Test basic ResourceOwner functionality * * Copyright (c) 2022-2023, PostgreSQL Global Development Group * * IDENTIFICATION * src/test/modules/test_resowner/test_resowner_basic.c * * ------------------------------------------------------------------------- */ #include "postgres.h" #include "fmgr.h" #include "lib/ilist.h" #include "utils/memutils.h" #include "utils/resowner.h" PG_MODULE_MAGIC; static void ReleaseString(Datum res); static char *PrintString(Datum res); /* * A resource that tracks strings and prints the string when it's released. * This makes the order that the resources are released visible. */ static const ResourceOwnerDesc string_desc = { .name = "test resource", .release_phase = RESOURCE_RELEASE_AFTER_LOCKS, .release_priority = RELEASE_PRIO_FIRST, .ReleaseResource = ReleaseString, .DebugPrint = PrintString }; static void ReleaseString(Datum res) { elog(NOTICE, "releasing string: %s", DatumGetPointer(res)); } static char * PrintString(Datum res) { return psprintf("test string \"%s\"", DatumGetPointer(res)); } /* demonstrates phases and priorities between a parent and child context */ PG_FUNCTION_INFO_V1(test_resowner_priorities); Datum test_resowner_priorities(PG_FUNCTION_ARGS) { int32 nkinds = PG_GETARG_INT32(0); int32 nresources = PG_GETARG_INT32(1); ResourceOwner parent, child; ResourceOwnerDesc *before_desc; ResourceOwnerDesc *after_desc; if (nkinds <= 0) elog(ERROR, "nkinds must be greater than zero"); if (nresources <= 0) elog(ERROR, "nresources must be greater than zero"); parent = ResourceOwnerCreate(CurrentResourceOwner, "test parent"); child = ResourceOwnerCreate(parent, "test child"); before_desc = palloc(nkinds * sizeof(ResourceOwnerDesc)); for (int i = 0; i < nkinds; i++) { before_desc[i].name = psprintf("test resource before locks %d", i); before_desc[i].release_phase = RESOURCE_RELEASE_BEFORE_LOCKS; before_desc[i].release_priority = RELEASE_PRIO_FIRST + i; before_desc[i].ReleaseResource = ReleaseString; before_desc[i].DebugPrint = PrintString; } after_desc = palloc(nkinds * sizeof(ResourceOwnerDesc)); for (int i = 0; i < nkinds; i++) { after_desc[i].name = psprintf("test resource after locks %d", i); after_desc[i].release_phase = RESOURCE_RELEASE_AFTER_LOCKS; after_desc[i].release_priority = RELEASE_PRIO_FIRST + i; after_desc[i].ReleaseResource = ReleaseString; after_desc[i].DebugPrint = PrintString; } /* Add a bunch of resources to child, with different priorities */ for (int i = 0; i < nresources; i++) { ResourceOwnerDesc *kind = &before_desc[i % nkinds]; ResourceOwnerEnlarge(child); ResourceOwnerRemember(child, CStringGetDatum(psprintf("child before locks priority %d", kind->release_priority)), kind); } for (int i = 0; i < nresources; i++) { ResourceOwnerDesc *kind = &after_desc[i % nkinds]; ResourceOwnerEnlarge(child); ResourceOwnerRemember(child, CStringGetDatum(psprintf("child after locks priority %d", kind->release_priority)), kind); } /* And also to the parent */ for (int i = 0; i < nresources; i++) { ResourceOwnerDesc *kind = &after_desc[i % nkinds]; ResourceOwnerEnlarge(parent); ResourceOwnerRemember(parent, CStringGetDatum(psprintf("parent after locks priority %d", kind->release_priority)), kind); } for (int i = 0; i < nresources; i++) { ResourceOwnerDesc *kind = &before_desc[i % nkinds]; ResourceOwnerEnlarge(parent); ResourceOwnerRemember(parent, CStringGetDatum(psprintf("parent before locks priority %d", kind->release_priority)), kind); } elog(NOTICE, "releasing resources before locks"); ResourceOwnerRelease(parent, RESOURCE_RELEASE_BEFORE_LOCKS, false, false); elog(NOTICE, "releasing locks"); ResourceOwnerRelease(parent, RESOURCE_RELEASE_LOCKS, false, false); elog(NOTICE, "releasing resources after locks"); ResourceOwnerRelease(parent, RESOURCE_RELEASE_AFTER_LOCKS, false, false); ResourceOwnerDelete(parent); PG_RETURN_VOID(); } PG_FUNCTION_INFO_V1(test_resowner_leak); Datum test_resowner_leak(PG_FUNCTION_ARGS) { ResourceOwner resowner; resowner = ResourceOwnerCreate(CurrentResourceOwner, "TestOwner"); ResourceOwnerEnlarge(resowner); ResourceOwnerRemember(resowner, CStringGetDatum("my string"), &string_desc); /* don't call ResourceOwnerForget, so that it is leaked */ ResourceOwnerRelease(resowner, RESOURCE_RELEASE_BEFORE_LOCKS, true, false); ResourceOwnerRelease(resowner, RESOURCE_RELEASE_LOCKS, true, false); ResourceOwnerRelease(resowner, RESOURCE_RELEASE_AFTER_LOCKS, true, false); ResourceOwnerDelete(resowner); PG_RETURN_VOID(); } PG_FUNCTION_INFO_V1(test_resowner_remember_between_phases); Datum test_resowner_remember_between_phases(PG_FUNCTION_ARGS) { ResourceOwner resowner; resowner = ResourceOwnerCreate(CurrentResourceOwner, "TestOwner"); ResourceOwnerRelease(resowner, RESOURCE_RELEASE_BEFORE_LOCKS, true, false); /* * Try to remember a new resource. Fails because we already called * ResourceOwnerRelease. */ ResourceOwnerEnlarge(resowner); ResourceOwnerRemember(resowner, CStringGetDatum("my string"), &string_desc); /* unreachable */ elog(ERROR, "ResourceOwnerEnlarge should have errored out"); PG_RETURN_VOID(); } PG_FUNCTION_INFO_V1(test_resowner_forget_between_phases); Datum test_resowner_forget_between_phases(PG_FUNCTION_ARGS) { ResourceOwner resowner; Datum str_resource; resowner = ResourceOwnerCreate(CurrentResourceOwner, "TestOwner"); ResourceOwnerEnlarge(resowner); str_resource = CStringGetDatum("my string"); ResourceOwnerRemember(resowner, str_resource, &string_desc); ResourceOwnerRelease(resowner, RESOURCE_RELEASE_BEFORE_LOCKS, true, false); /* * Try to forget the resource that was remembered earlier. Fails because * we already called ResourceOwnerRelease. */ ResourceOwnerForget(resowner, str_resource, &string_desc); /* unreachable */ elog(ERROR, "ResourceOwnerForget should have errored out"); PG_RETURN_VOID(); }