Age Owner Branch data TLA Line data Source code
1 : : /*--------------------------------------------------------------------------
2 : : *
3 : : * test_slru.c
4 : : * Test correctness of SLRU functions.
5 : : *
6 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : * IDENTIFICATION
10 : : * src/test/modules/test_slru/test_slru.c
11 : : *
12 : : * -------------------------------------------------------------------------
13 : : */
14 : :
15 : : #include "postgres.h"
16 : :
17 : : #include "access/slru.h"
18 : : #include "access/transam.h"
19 : : #include "miscadmin.h"
20 : : #include "storage/fd.h"
21 : : #include "storage/ipc.h"
22 : : #include "storage/shmem.h"
23 : : #include "utils/builtins.h"
24 : :
1025 michael@paquier.xyz 25 :CBC 1 : PG_MODULE_MAGIC;
26 : :
27 : : /*
28 : : * SQL-callable entry points
29 : : */
30 : 2 : PG_FUNCTION_INFO_V1(test_slru_page_write);
31 : 2 : PG_FUNCTION_INFO_V1(test_slru_page_writeall);
32 : 2 : PG_FUNCTION_INFO_V1(test_slru_page_read);
33 : 2 : PG_FUNCTION_INFO_V1(test_slru_page_readonly);
34 : 2 : PG_FUNCTION_INFO_V1(test_slru_page_exists);
35 : 2 : PG_FUNCTION_INFO_V1(test_slru_page_sync);
36 : 2 : PG_FUNCTION_INFO_V1(test_slru_page_delete);
37 : 2 : PG_FUNCTION_INFO_V1(test_slru_page_truncate);
38 : 2 : PG_FUNCTION_INFO_V1(test_slru_delete_all);
39 : :
40 : : /* Number of SLRU page slots */
41 : : #define NUM_TEST_BUFFERS 16
42 : :
43 : : static SlruCtlData TestSlruCtlData;
44 : : #define TestSlruCtl (&TestSlruCtlData)
45 : :
46 : : static shmem_request_hook_type prev_shmem_request_hook = NULL;
47 : : static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
48 : :
49 : : static bool
647 akorotkov@postgresql 50 : 1 : test_slru_scan_cb(SlruCtl ctl, char *filename, int64 segpage, void *data)
51 : : {
1025 michael@paquier.xyz 52 [ + - ]: 1 : elog(NOTICE, "Calling test_slru_scan_cb()");
53 : 1 : return SlruScanDirCbDeleteAll(ctl, filename, segpage, data);
54 : : }
55 : :
56 : : Datum
57 : 98 : test_slru_page_write(PG_FUNCTION_ARGS)
58 : : {
647 akorotkov@postgresql 59 : 98 : int64 pageno = PG_GETARG_INT64(0);
1025 michael@paquier.xyz 60 : 98 : char *data = text_to_cstring(PG_GETARG_TEXT_PP(1));
61 : : int slotno;
556 alvherre@alvh.no-ip. 62 : 98 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
63 : :
64 : 98 : LWLockAcquire(lock, LW_EXCLUSIVE);
1025 michael@paquier.xyz 65 : 98 : slotno = SimpleLruZeroPage(TestSlruCtl, pageno);
66 : :
67 : : /* these should match */
68 [ - + ]: 98 : Assert(TestSlruCtl->shared->page_number[slotno] == pageno);
69 : :
70 : : /* mark the page as dirty so as it would get written */
71 : 98 : TestSlruCtl->shared->page_dirty[slotno] = true;
72 : 98 : TestSlruCtl->shared->page_status[slotno] = SLRU_PAGE_VALID;
73 : :
74 : : /* write given data to the page, up to the limit of the page */
75 : 98 : strncpy(TestSlruCtl->shared->page_buffer[slotno], data,
76 : : BLCKSZ - 1);
77 : :
78 : 98 : SimpleLruWritePage(TestSlruCtl, slotno);
556 alvherre@alvh.no-ip. 79 : 98 : LWLockRelease(lock);
80 : :
1025 michael@paquier.xyz 81 : 98 : PG_RETURN_VOID();
82 : : }
83 : :
84 : : Datum
85 : 2 : test_slru_page_writeall(PG_FUNCTION_ARGS)
86 : : {
87 : 2 : SimpleLruWriteAll(TestSlruCtl, true);
88 : 2 : PG_RETURN_VOID();
89 : : }
90 : :
91 : : Datum
92 : 4 : test_slru_page_read(PG_FUNCTION_ARGS)
93 : : {
647 akorotkov@postgresql 94 : 4 : int64 pageno = PG_GETARG_INT64(0);
1025 michael@paquier.xyz 95 : 4 : bool write_ok = PG_GETARG_BOOL(1);
96 : 4 : char *data = NULL;
97 : : int slotno;
556 alvherre@alvh.no-ip. 98 : 4 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
99 : :
100 : : /* find page in buffers, reading it if necessary */
101 : 4 : LWLockAcquire(lock, LW_EXCLUSIVE);
1025 michael@paquier.xyz 102 : 4 : slotno = SimpleLruReadPage(TestSlruCtl, pageno,
103 : : write_ok, InvalidTransactionId);
104 : 4 : data = (char *) TestSlruCtl->shared->page_buffer[slotno];
556 alvherre@alvh.no-ip. 105 : 4 : LWLockRelease(lock);
106 : :
1025 michael@paquier.xyz 107 : 4 : PG_RETURN_TEXT_P(cstring_to_text(data));
108 : : }
109 : :
110 : : Datum
111 : 4 : test_slru_page_readonly(PG_FUNCTION_ARGS)
112 : : {
647 akorotkov@postgresql 113 : 4 : int64 pageno = PG_GETARG_INT64(0);
1025 michael@paquier.xyz 114 : 4 : char *data = NULL;
115 : : int slotno;
556 alvherre@alvh.no-ip. 116 : 4 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
117 : :
118 : : /* find page in buffers, reading it if necessary */
1025 michael@paquier.xyz 119 : 4 : slotno = SimpleLruReadPage_ReadOnly(TestSlruCtl,
120 : : pageno,
121 : : InvalidTransactionId);
556 alvherre@alvh.no-ip. 122 [ - + ]: 4 : Assert(LWLockHeldByMe(lock));
1025 michael@paquier.xyz 123 : 4 : data = (char *) TestSlruCtl->shared->page_buffer[slotno];
556 alvherre@alvh.no-ip. 124 : 4 : LWLockRelease(lock);
125 : :
1025 michael@paquier.xyz 126 : 4 : PG_RETURN_TEXT_P(cstring_to_text(data));
127 : : }
128 : :
129 : : Datum
130 : 18 : test_slru_page_exists(PG_FUNCTION_ARGS)
131 : : {
647 akorotkov@postgresql 132 : 18 : int64 pageno = PG_GETARG_INT64(0);
133 : : bool found;
556 alvherre@alvh.no-ip. 134 : 18 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
135 : :
136 : 18 : LWLockAcquire(lock, LW_EXCLUSIVE);
1025 michael@paquier.xyz 137 : 18 : found = SimpleLruDoesPhysicalPageExist(TestSlruCtl, pageno);
556 alvherre@alvh.no-ip. 138 : 18 : LWLockRelease(lock);
139 : :
1025 michael@paquier.xyz 140 : 18 : PG_RETURN_BOOL(found);
141 : : }
142 : :
143 : : Datum
144 : 2 : test_slru_page_sync(PG_FUNCTION_ARGS)
145 : : {
647 akorotkov@postgresql 146 : 2 : int64 pageno = PG_GETARG_INT64(0);
147 : : FileTag ftag;
148 : : char path[MAXPGPATH];
149 : :
150 : : /* note that this flushes the full file a segment is located in */
1025 michael@paquier.xyz 151 : 2 : ftag.segno = pageno / SLRU_PAGES_PER_SEGMENT;
152 : 2 : SlruSyncFileTag(TestSlruCtl, &ftag, path);
153 : :
130 peter@eisentraut.org 154 [ + - ]: 2 : elog(NOTICE, "Called SlruSyncFileTag() for segment %" PRIu64 " on path %s",
155 : : ftag.segno, path);
156 : :
1025 michael@paquier.xyz 157 : 2 : PG_RETURN_VOID();
158 : : }
159 : :
160 : : Datum
161 : 2 : test_slru_page_delete(PG_FUNCTION_ARGS)
162 : : {
647 akorotkov@postgresql 163 : 2 : int64 pageno = PG_GETARG_INT64(0);
164 : : FileTag ftag;
165 : :
1025 michael@paquier.xyz 166 : 2 : ftag.segno = pageno / SLRU_PAGES_PER_SEGMENT;
167 : 2 : SlruDeleteSegment(TestSlruCtl, ftag.segno);
168 : :
130 peter@eisentraut.org 169 [ + - ]: 2 : elog(NOTICE, "Called SlruDeleteSegment() for segment %" PRIu64,
170 : : ftag.segno);
171 : :
1025 michael@paquier.xyz 172 : 2 : PG_RETURN_VOID();
173 : : }
174 : :
175 : : Datum
176 : 2 : test_slru_page_truncate(PG_FUNCTION_ARGS)
177 : : {
647 akorotkov@postgresql 178 : 2 : int64 pageno = PG_GETARG_INT64(0);
179 : :
1025 michael@paquier.xyz 180 : 2 : SimpleLruTruncate(TestSlruCtl, pageno);
181 : 2 : PG_RETURN_VOID();
182 : : }
183 : :
184 : : Datum
185 : 2 : test_slru_delete_all(PG_FUNCTION_ARGS)
186 : : {
187 : : /* this calls SlruScanDirCbDeleteAll() internally, ensuring deletion */
188 : 2 : SlruScanDirectory(TestSlruCtl, test_slru_scan_cb, NULL);
189 : :
190 : 2 : PG_RETURN_VOID();
191 : : }
192 : :
193 : : /*
194 : : * Module load callbacks and initialization.
195 : : */
196 : :
197 : : static void
198 : 1 : test_slru_shmem_request(void)
199 : : {
200 [ - + ]: 1 : if (prev_shmem_request_hook)
1025 michael@paquier.xyz 201 :UBC 0 : prev_shmem_request_hook();
202 : :
203 : : /* reserve shared memory for the test SLRU */
1025 michael@paquier.xyz 204 :CBC 1 : RequestAddinShmemSpace(SimpleLruShmemSize(NUM_TEST_BUFFERS, 0));
205 : 1 : }
206 : :
207 : : static bool
647 akorotkov@postgresql 208 : 15 : test_slru_page_precedes_logically(int64 page1, int64 page2)
209 : : {
1025 michael@paquier.xyz 210 : 15 : return page1 < page2;
211 : : }
212 : :
213 : : static void
214 : 1 : test_slru_shmem_startup(void)
215 : : {
216 : : /*
217 : : * Short segments names are well tested elsewhere so in this test we are
218 : : * focusing on long names.
219 : : */
647 akorotkov@postgresql 220 : 1 : const bool long_segment_names = true;
1025 michael@paquier.xyz 221 : 1 : const char slru_dir_name[] = "pg_test_slru";
222 : : int test_tranche_id;
223 : : int test_buffer_tranche_id;
224 : :
225 [ - + ]: 1 : if (prev_shmem_startup_hook)
1025 michael@paquier.xyz 226 :UBC 0 : prev_shmem_startup_hook();
227 : :
228 : : /*
229 : : * Create the SLRU directory if it does not exist yet, from the root of
230 : : * the data directory.
231 : : */
1025 michael@paquier.xyz 232 :CBC 1 : (void) MakePGDirectory(slru_dir_name);
233 : :
234 : : /* initialize the SLRU facility */
3 nathan@postgresql.or 235 :GNC 1 : test_tranche_id = LWLockNewTrancheId("test_slru_tranche");
236 : :
237 : 1 : test_buffer_tranche_id = LWLockNewTrancheId("test_buffer_tranche");
238 : :
1025 michael@paquier.xyz 239 :CBC 1 : TestSlruCtl->PagePrecedes = test_slru_page_precedes_logically;
240 : 1 : SimpleLruInit(TestSlruCtl, "TestSLRU",
241 : : NUM_TEST_BUFFERS, 0, slru_dir_name,
242 : : test_buffer_tranche_id, test_tranche_id, SYNC_HANDLER_NONE,
243 : : long_segment_names);
244 : 1 : }
245 : :
246 : : void
247 : 1 : _PG_init(void)
248 : : {
249 [ - + ]: 1 : if (!process_shared_preload_libraries_in_progress)
1025 michael@paquier.xyz 250 [ # # ]:UBC 0 : ereport(ERROR,
251 : : (errmsg("cannot load \"%s\" after startup", "test_slru"),
252 : : errdetail("\"%s\" must be loaded with \"shared_preload_libraries\".",
253 : : "test_slru")));
254 : :
1025 michael@paquier.xyz 255 :CBC 1 : prev_shmem_request_hook = shmem_request_hook;
256 : 1 : shmem_request_hook = test_slru_shmem_request;
257 : :
258 : 1 : prev_shmem_startup_hook = shmem_startup_hook;
259 : 1 : shmem_startup_hook = test_slru_shmem_startup;
260 : 1 : }
|