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 1 : 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 142 : ReleaseString(Datum res)
37 : {
38 142 : elog(NOTICE, "releasing string: %s", DatumGetCString(res));
39 142 : }
40 :
41 : static char *
42 1 : PrintString(Datum res)
43 : {
44 1 : return psprintf("test string \"%s\"", DatumGetCString(res));
45 : }
46 :
47 : /* demonstrates phases and priorities between a parent and child context */
48 2 : PG_FUNCTION_INFO_V1(test_resowner_priorities);
49 : Datum
50 2 : test_resowner_priorities(PG_FUNCTION_ARGS)
51 : {
52 2 : int32 nkinds = PG_GETARG_INT32(0);
53 2 : int32 nresources = PG_GETARG_INT32(1);
54 : ResourceOwner parent,
55 : child;
56 : ResourceOwnerDesc *before_desc;
57 : ResourceOwnerDesc *after_desc;
58 :
59 2 : if (nkinds <= 0)
60 0 : elog(ERROR, "nkinds must be greater than zero");
61 2 : if (nresources <= 0)
62 0 : elog(ERROR, "nresources must be greater than zero");
63 :
64 2 : parent = ResourceOwnerCreate(CurrentResourceOwner, "test parent");
65 2 : child = ResourceOwnerCreate(parent, "test child");
66 :
67 2 : before_desc = palloc(nkinds * sizeof(ResourceOwnerDesc));
68 6 : for (int i = 0; i < nkinds; i++)
69 : {
70 4 : before_desc[i].name = psprintf("test resource before locks %d", i);
71 4 : before_desc[i].release_phase = RESOURCE_RELEASE_BEFORE_LOCKS;
72 4 : before_desc[i].release_priority = RELEASE_PRIO_FIRST + i;
73 4 : before_desc[i].ReleaseResource = ReleaseString;
74 4 : before_desc[i].DebugPrint = PrintString;
75 : }
76 2 : after_desc = palloc(nkinds * sizeof(ResourceOwnerDesc));
77 6 : for (int i = 0; i < nkinds; i++)
78 : {
79 4 : after_desc[i].name = psprintf("test resource after locks %d", i);
80 4 : after_desc[i].release_phase = RESOURCE_RELEASE_AFTER_LOCKS;
81 4 : after_desc[i].release_priority = RELEASE_PRIO_FIRST + i;
82 4 : after_desc[i].ReleaseResource = ReleaseString;
83 4 : after_desc[i].DebugPrint = PrintString;
84 : }
85 :
86 : /* Add a bunch of resources to child, with different priorities */
87 37 : for (int i = 0; i < nresources; i++)
88 : {
89 35 : ResourceOwnerDesc *kind = &before_desc[i % nkinds];
90 :
91 35 : ResourceOwnerEnlarge(child);
92 35 : ResourceOwnerRemember(child,
93 35 : CStringGetDatum(psprintf("child before locks priority %d", kind->release_priority)),
94 : kind);
95 : }
96 37 : for (int i = 0; i < nresources; i++)
97 : {
98 35 : ResourceOwnerDesc *kind = &after_desc[i % nkinds];
99 :
100 35 : ResourceOwnerEnlarge(child);
101 35 : ResourceOwnerRemember(child,
102 35 : CStringGetDatum(psprintf("child after locks priority %d", kind->release_priority)),
103 : kind);
104 : }
105 :
106 : /* And also to the parent */
107 37 : for (int i = 0; i < nresources; i++)
108 : {
109 35 : ResourceOwnerDesc *kind = &after_desc[i % nkinds];
110 :
111 35 : ResourceOwnerEnlarge(parent);
112 35 : ResourceOwnerRemember(parent,
113 35 : CStringGetDatum(psprintf("parent after locks priority %d", kind->release_priority)),
114 : kind);
115 : }
116 37 : for (int i = 0; i < nresources; i++)
117 : {
118 35 : ResourceOwnerDesc *kind = &before_desc[i % nkinds];
119 :
120 35 : ResourceOwnerEnlarge(parent);
121 35 : ResourceOwnerRemember(parent,
122 35 : CStringGetDatum(psprintf("parent before locks priority %d", kind->release_priority)),
123 : kind);
124 : }
125 :
126 2 : elog(NOTICE, "releasing resources before locks");
127 2 : ResourceOwnerRelease(parent, RESOURCE_RELEASE_BEFORE_LOCKS, false, false);
128 2 : elog(NOTICE, "releasing locks");
129 2 : ResourceOwnerRelease(parent, RESOURCE_RELEASE_LOCKS, false, false);
130 2 : elog(NOTICE, "releasing resources after locks");
131 2 : ResourceOwnerRelease(parent, RESOURCE_RELEASE_AFTER_LOCKS, false, false);
132 :
133 2 : ResourceOwnerDelete(parent);
134 :
135 2 : PG_RETURN_VOID();
136 : }
137 :
138 2 : PG_FUNCTION_INFO_V1(test_resowner_leak);
139 : Datum
140 1 : test_resowner_leak(PG_FUNCTION_ARGS)
141 : {
142 : ResourceOwner resowner;
143 :
144 1 : resowner = ResourceOwnerCreate(CurrentResourceOwner, "TestOwner");
145 :
146 1 : ResourceOwnerEnlarge(resowner);
147 :
148 1 : ResourceOwnerRemember(resowner, CStringGetDatum("my string"), &string_desc);
149 :
150 : /* don't call ResourceOwnerForget, so that it is leaked */
151 :
152 1 : ResourceOwnerRelease(resowner, RESOURCE_RELEASE_BEFORE_LOCKS, true, false);
153 1 : ResourceOwnerRelease(resowner, RESOURCE_RELEASE_LOCKS, true, false);
154 1 : ResourceOwnerRelease(resowner, RESOURCE_RELEASE_AFTER_LOCKS, true, false);
155 :
156 1 : ResourceOwnerDelete(resowner);
157 :
158 1 : PG_RETURN_VOID();
159 : }
160 :
161 2 : PG_FUNCTION_INFO_V1(test_resowner_remember_between_phases);
162 : Datum
163 1 : test_resowner_remember_between_phases(PG_FUNCTION_ARGS)
164 : {
165 : ResourceOwner resowner;
166 :
167 1 : resowner = ResourceOwnerCreate(CurrentResourceOwner, "TestOwner");
168 :
169 1 : 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 1 : 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 : PG_RETURN_VOID();
182 : }
183 :
184 2 : PG_FUNCTION_INFO_V1(test_resowner_forget_between_phases);
185 : Datum
186 1 : test_resowner_forget_between_phases(PG_FUNCTION_ARGS)
187 : {
188 : ResourceOwner resowner;
189 : Datum str_resource;
190 :
191 1 : resowner = ResourceOwnerCreate(CurrentResourceOwner, "TestOwner");
192 :
193 1 : ResourceOwnerEnlarge(resowner);
194 1 : str_resource = CStringGetDatum("my string");
195 1 : ResourceOwnerRemember(resowner, str_resource, &string_desc);
196 :
197 1 : 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 1 : ResourceOwnerForget(resowner, str_resource, &string_desc);
204 :
205 : /* unreachable */
206 0 : elog(ERROR, "ResourceOwnerForget should have errored out");
207 :
208 : PG_RETURN_VOID();
209 : }
|