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