postgresql/src/test/modules/test_resowner/test_resowner_basic.c

212 lines
6.0 KiB
C

/*--------------------------------------------------------------------------
*
* 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();
}