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