Line data Source code
1 : /*--------------------------------------------------------------------------
2 : *
3 : * test_resowner_basic.c
4 : * Test basic ResourceOwner functionality
5 : *
6 : * Copyright (c) 2022-2026, PostgreSQL Global Development Group
7 : *
8 : * IDENTIFICATION
9 : * src/test/modules/test_resowner/test_resowner_basic.c
10 : *
11 : * -------------------------------------------------------------------------
12 : */
13 : #include "postgres.h"
14 :
15 : #include "fmgr.h"
16 : #include "utils/resowner.h"
17 :
18 0 : PG_MODULE_MAGIC;
19 :
20 : static void ReleaseString(Datum res);
21 : static char *PrintString(Datum res);
22 :
23 : /*
24 : * A resource that tracks strings and prints the string when it's released.
25 : * This makes the order that the resources are released visible.
26 : */
27 : static const ResourceOwnerDesc string_desc = {
28 : .name = "test resource",
29 : .release_phase = RESOURCE_RELEASE_AFTER_LOCKS,
30 : .release_priority = RELEASE_PRIO_FIRST,
31 : .ReleaseResource = ReleaseString,
32 : .DebugPrint = PrintString
33 : };
34 :
35 : static void
36 0 : ReleaseString(Datum res)
37 : {
38 0 : elog(NOTICE, "releasing string: %s", DatumGetCString(res));
39 0 : }
40 :
41 : static char *
42 0 : PrintString(Datum res)
43 : {
44 0 : return psprintf("test string \"%s\"", DatumGetCString(res));
45 : }
46 :
47 : /* demonstrates phases and priorities between a parent and child context */
48 0 : PG_FUNCTION_INFO_V1(test_resowner_priorities);
49 : Datum
50 0 : test_resowner_priorities(PG_FUNCTION_ARGS)
51 : {
52 0 : int32 nkinds = PG_GETARG_INT32(0);
53 0 : int32 nresources = PG_GETARG_INT32(1);
54 0 : ResourceOwner parent,
55 : child;
56 0 : ResourceOwnerDesc *before_desc;
57 0 : ResourceOwnerDesc *after_desc;
58 :
59 0 : if (nkinds <= 0)
60 0 : elog(ERROR, "nkinds must be greater than zero");
61 0 : if (nresources <= 0)
62 0 : elog(ERROR, "nresources must be greater than zero");
63 :
64 0 : parent = ResourceOwnerCreate(CurrentResourceOwner, "test parent");
65 0 : child = ResourceOwnerCreate(parent, "test child");
66 :
67 0 : before_desc = palloc(nkinds * sizeof(ResourceOwnerDesc));
68 0 : for (int i = 0; i < nkinds; i++)
69 : {
70 0 : before_desc[i].name = psprintf("test resource before locks %d", i);
71 0 : before_desc[i].release_phase = RESOURCE_RELEASE_BEFORE_LOCKS;
72 0 : before_desc[i].release_priority = RELEASE_PRIO_FIRST + i;
73 0 : before_desc[i].ReleaseResource = ReleaseString;
74 0 : before_desc[i].DebugPrint = PrintString;
75 0 : }
76 0 : after_desc = palloc(nkinds * sizeof(ResourceOwnerDesc));
77 0 : for (int i = 0; i < nkinds; i++)
78 : {
79 0 : after_desc[i].name = psprintf("test resource after locks %d", i);
80 0 : after_desc[i].release_phase = RESOURCE_RELEASE_AFTER_LOCKS;
81 0 : after_desc[i].release_priority = RELEASE_PRIO_FIRST + i;
82 0 : after_desc[i].ReleaseResource = ReleaseString;
83 0 : after_desc[i].DebugPrint = PrintString;
84 0 : }
85 :
86 : /* Add a bunch of resources to child, with different priorities */
87 0 : for (int i = 0; i < nresources; i++)
88 : {
89 0 : ResourceOwnerDesc *kind = &before_desc[i % nkinds];
90 :
91 0 : ResourceOwnerEnlarge(child);
92 0 : ResourceOwnerRemember(child,
93 0 : CStringGetDatum(psprintf("child before locks priority %d", kind->release_priority)),
94 0 : kind);
95 0 : }
96 0 : for (int i = 0; i < nresources; i++)
97 : {
98 0 : ResourceOwnerDesc *kind = &after_desc[i % nkinds];
99 :
100 0 : ResourceOwnerEnlarge(child);
101 0 : ResourceOwnerRemember(child,
102 0 : CStringGetDatum(psprintf("child after locks priority %d", kind->release_priority)),
103 0 : kind);
104 0 : }
105 :
106 : /* And also to the parent */
107 0 : for (int i = 0; i < nresources; i++)
108 : {
109 0 : ResourceOwnerDesc *kind = &after_desc[i % nkinds];
110 :
111 0 : ResourceOwnerEnlarge(parent);
112 0 : ResourceOwnerRemember(parent,
113 0 : CStringGetDatum(psprintf("parent after locks priority %d", kind->release_priority)),
114 0 : kind);
115 0 : }
116 0 : for (int i = 0; i < nresources; i++)
117 : {
118 0 : ResourceOwnerDesc *kind = &before_desc[i % nkinds];
119 :
120 0 : ResourceOwnerEnlarge(parent);
121 0 : ResourceOwnerRemember(parent,
122 0 : CStringGetDatum(psprintf("parent before locks priority %d", kind->release_priority)),
123 0 : kind);
124 0 : }
125 :
126 0 : elog(NOTICE, "releasing resources before locks");
127 0 : ResourceOwnerRelease(parent, RESOURCE_RELEASE_BEFORE_LOCKS, false, false);
128 0 : elog(NOTICE, "releasing locks");
129 0 : ResourceOwnerRelease(parent, RESOURCE_RELEASE_LOCKS, false, false);
130 0 : elog(NOTICE, "releasing resources after locks");
131 0 : ResourceOwnerRelease(parent, RESOURCE_RELEASE_AFTER_LOCKS, false, false);
132 :
133 0 : ResourceOwnerDelete(parent);
134 :
135 0 : PG_RETURN_VOID();
136 0 : }
137 :
138 0 : PG_FUNCTION_INFO_V1(test_resowner_leak);
139 : Datum
140 0 : test_resowner_leak(PG_FUNCTION_ARGS)
141 : {
142 0 : ResourceOwner resowner;
143 :
144 0 : resowner = ResourceOwnerCreate(CurrentResourceOwner, "TestOwner");
145 :
146 0 : ResourceOwnerEnlarge(resowner);
147 :
148 0 : ResourceOwnerRemember(resowner, CStringGetDatum("my string"), &string_desc);
149 :
150 : /* don't call ResourceOwnerForget, so that it is leaked */
151 :
152 0 : ResourceOwnerRelease(resowner, RESOURCE_RELEASE_BEFORE_LOCKS, true, false);
153 0 : ResourceOwnerRelease(resowner, RESOURCE_RELEASE_LOCKS, true, false);
154 0 : ResourceOwnerRelease(resowner, RESOURCE_RELEASE_AFTER_LOCKS, true, false);
155 :
156 0 : ResourceOwnerDelete(resowner);
157 :
158 0 : PG_RETURN_VOID();
159 0 : }
160 :
161 0 : PG_FUNCTION_INFO_V1(test_resowner_remember_between_phases);
162 : Datum
163 0 : test_resowner_remember_between_phases(PG_FUNCTION_ARGS)
164 : {
165 0 : ResourceOwner resowner;
166 :
167 0 : resowner = ResourceOwnerCreate(CurrentResourceOwner, "TestOwner");
168 :
169 0 : ResourceOwnerRelease(resowner, RESOURCE_RELEASE_BEFORE_LOCKS, true, false);
170 :
171 : /*
172 : * Try to remember a new resource. Fails because we already called
173 : * ResourceOwnerRelease.
174 : */
175 0 : ResourceOwnerEnlarge(resowner);
176 0 : ResourceOwnerRemember(resowner, CStringGetDatum("my string"), &string_desc);
177 :
178 : /* unreachable */
179 0 : elog(ERROR, "ResourceOwnerEnlarge should have errored out");
180 :
181 0 : PG_RETURN_VOID();
182 0 : }
183 :
184 0 : PG_FUNCTION_INFO_V1(test_resowner_forget_between_phases);
185 : Datum
186 0 : test_resowner_forget_between_phases(PG_FUNCTION_ARGS)
187 : {
188 0 : ResourceOwner resowner;
189 0 : Datum str_resource;
190 :
191 0 : resowner = ResourceOwnerCreate(CurrentResourceOwner, "TestOwner");
192 :
193 0 : ResourceOwnerEnlarge(resowner);
194 0 : str_resource = CStringGetDatum("my string");
195 0 : ResourceOwnerRemember(resowner, str_resource, &string_desc);
196 :
197 0 : ResourceOwnerRelease(resowner, RESOURCE_RELEASE_BEFORE_LOCKS, true, false);
198 :
199 : /*
200 : * Try to forget the resource that was remembered earlier. Fails because
201 : * we already called ResourceOwnerRelease.
202 : */
203 0 : ResourceOwnerForget(resowner, str_resource, &string_desc);
204 :
205 : /* unreachable */
206 0 : elog(ERROR, "ResourceOwnerForget should have errored out");
207 :
208 0 : PG_RETURN_VOID();
209 0 : }
|