212 lines
6.0 KiB
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();
|
|
}
|