1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
/*--------------------------------------------------------------------------
*
* test_resowner_basic.c
* Test basic ResourceOwner functionality
*
* Copyright (c) 2022-2025, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/test/modules/test_resowner/test_resowner_basic.c
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "fmgr.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();
}
|