Line data Source code
1 : /*--------------------------------------------------------------------------
2 : *
3 : * test_slru.c
4 : * Test correctness of SLRU functions.
5 : *
6 : * Portions Copyright (c) 1996-2026, 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 :
25 4 : PG_MODULE_MAGIC;
26 :
27 : /*
28 : * SQL-callable entry points
29 : */
30 4 : PG_FUNCTION_INFO_V1(test_slru_page_write);
31 4 : PG_FUNCTION_INFO_V1(test_slru_page_writeall);
32 4 : PG_FUNCTION_INFO_V1(test_slru_page_read);
33 4 : PG_FUNCTION_INFO_V1(test_slru_page_readonly);
34 4 : PG_FUNCTION_INFO_V1(test_slru_page_exists);
35 4 : PG_FUNCTION_INFO_V1(test_slru_page_sync);
36 4 : PG_FUNCTION_INFO_V1(test_slru_page_delete);
37 4 : PG_FUNCTION_INFO_V1(test_slru_page_truncate);
38 4 : PG_FUNCTION_INFO_V1(test_slru_delete_all);
39 :
40 : /* Number of SLRU page slots */
41 : #define NUM_TEST_BUFFERS 16
42 :
43 : static void test_slru_shmem_request(void *arg);
44 : static bool test_slru_page_precedes_logically(int64 page1, int64 page2);
45 : static int test_slru_errdetail_for_io_error(const void *opaque_data);
46 :
47 : static const char *TestSlruDir = "pg_test_slru";
48 :
49 : static SlruDesc TestSlruDesc;
50 :
51 : static const ShmemCallbacks test_slru_shmem_callbacks = {
52 : .request_fn = test_slru_shmem_request
53 : };
54 :
55 : #define TestSlruCtl (&TestSlruDesc)
56 :
57 : static bool
58 1 : test_slru_scan_cb(SlruDesc *ctl, char *filename, int64 segpage, void *data)
59 : {
60 1 : elog(NOTICE, "Calling test_slru_scan_cb()");
61 1 : return SlruScanDirCbDeleteAll(ctl, filename, segpage, data);
62 : }
63 :
64 : Datum
65 98 : test_slru_page_write(PG_FUNCTION_ARGS)
66 : {
67 98 : int64 pageno = PG_GETARG_INT64(0);
68 98 : char *data = text_to_cstring(PG_GETARG_TEXT_PP(1));
69 : int slotno;
70 98 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
71 :
72 98 : LWLockAcquire(lock, LW_EXCLUSIVE);
73 98 : slotno = SimpleLruZeroPage(TestSlruCtl, pageno);
74 :
75 : /* these should match */
76 : Assert(TestSlruCtl->shared->page_number[slotno] == pageno);
77 :
78 : /* mark the page as dirty so as it would get written */
79 98 : TestSlruCtl->shared->page_dirty[slotno] = true;
80 98 : TestSlruCtl->shared->page_status[slotno] = SLRU_PAGE_VALID;
81 :
82 : /* write given data to the page, up to the limit of the page */
83 98 : strncpy(TestSlruCtl->shared->page_buffer[slotno], data,
84 : BLCKSZ - 1);
85 :
86 98 : SimpleLruWritePage(TestSlruCtl, slotno);
87 98 : LWLockRelease(lock);
88 :
89 98 : PG_RETURN_VOID();
90 : }
91 :
92 : Datum
93 2 : test_slru_page_writeall(PG_FUNCTION_ARGS)
94 : {
95 2 : SimpleLruWriteAll(TestSlruCtl, true);
96 2 : PG_RETURN_VOID();
97 : }
98 :
99 : Datum
100 5 : test_slru_page_read(PG_FUNCTION_ARGS)
101 : {
102 5 : int64 pageno = PG_GETARG_INT64(0);
103 5 : bool write_ok = PG_GETARG_BOOL(1);
104 5 : TransactionId xid = PG_GETARG_TRANSACTIONID(2);
105 5 : char *data = NULL;
106 : int slotno;
107 5 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
108 :
109 : /* find page in buffers, reading it if necessary */
110 5 : LWLockAcquire(lock, LW_EXCLUSIVE);
111 5 : slotno = SimpleLruReadPage(TestSlruCtl, pageno, write_ok, &xid);
112 4 : data = (char *) TestSlruCtl->shared->page_buffer[slotno];
113 4 : LWLockRelease(lock);
114 :
115 4 : PG_RETURN_TEXT_P(cstring_to_text(data));
116 : }
117 :
118 : Datum
119 4 : test_slru_page_readonly(PG_FUNCTION_ARGS)
120 : {
121 4 : int64 pageno = PG_GETARG_INT64(0);
122 4 : char *data = NULL;
123 : int slotno;
124 4 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
125 :
126 : /* find page in buffers, reading it if necessary */
127 4 : slotno = SimpleLruReadPage_ReadOnly(TestSlruCtl,
128 : pageno,
129 : NULL);
130 : Assert(LWLockHeldByMe(lock));
131 4 : data = (char *) TestSlruCtl->shared->page_buffer[slotno];
132 4 : LWLockRelease(lock);
133 :
134 4 : PG_RETURN_TEXT_P(cstring_to_text(data));
135 : }
136 :
137 : Datum
138 18 : test_slru_page_exists(PG_FUNCTION_ARGS)
139 : {
140 18 : int64 pageno = PG_GETARG_INT64(0);
141 : bool found;
142 18 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
143 :
144 18 : LWLockAcquire(lock, LW_EXCLUSIVE);
145 18 : found = SimpleLruDoesPhysicalPageExist(TestSlruCtl, pageno);
146 18 : LWLockRelease(lock);
147 :
148 18 : PG_RETURN_BOOL(found);
149 : }
150 :
151 : Datum
152 2 : test_slru_page_sync(PG_FUNCTION_ARGS)
153 : {
154 2 : int64 pageno = PG_GETARG_INT64(0);
155 : FileTag ftag;
156 : char path[MAXPGPATH];
157 :
158 : /* note that this flushes the full file a segment is located in */
159 2 : ftag.segno = pageno / SLRU_PAGES_PER_SEGMENT;
160 2 : SlruSyncFileTag(TestSlruCtl, &ftag, path);
161 :
162 2 : elog(NOTICE, "Called SlruSyncFileTag() for segment %" PRIu64 " on path %s",
163 : ftag.segno, path);
164 :
165 2 : PG_RETURN_VOID();
166 : }
167 :
168 : Datum
169 2 : test_slru_page_delete(PG_FUNCTION_ARGS)
170 : {
171 2 : int64 pageno = PG_GETARG_INT64(0);
172 : FileTag ftag;
173 :
174 2 : ftag.segno = pageno / SLRU_PAGES_PER_SEGMENT;
175 2 : SlruDeleteSegment(TestSlruCtl, ftag.segno);
176 :
177 2 : elog(NOTICE, "Called SlruDeleteSegment() for segment %" PRIu64,
178 : ftag.segno);
179 :
180 2 : PG_RETURN_VOID();
181 : }
182 :
183 : Datum
184 2 : test_slru_page_truncate(PG_FUNCTION_ARGS)
185 : {
186 2 : int64 pageno = PG_GETARG_INT64(0);
187 :
188 2 : SimpleLruTruncate(TestSlruCtl, pageno);
189 2 : PG_RETURN_VOID();
190 : }
191 :
192 : Datum
193 2 : test_slru_delete_all(PG_FUNCTION_ARGS)
194 : {
195 : /* this calls SlruScanDirCbDeleteAll() internally, ensuring deletion */
196 2 : SlruScanDirectory(TestSlruCtl, test_slru_scan_cb, NULL);
197 :
198 2 : PG_RETURN_VOID();
199 : }
200 :
201 : static bool
202 15 : test_slru_page_precedes_logically(int64 page1, int64 page2)
203 : {
204 15 : return page1 < page2;
205 : }
206 :
207 : static int
208 1 : test_slru_errdetail_for_io_error(const void *opaque_data)
209 : {
210 1 : TransactionId xid = *(const TransactionId *) opaque_data;
211 :
212 1 : return errdetail("Could not access test_slru entry %u.", xid);
213 : }
214 :
215 : void
216 4 : _PG_init(void)
217 : {
218 4 : if (!process_shared_preload_libraries_in_progress)
219 0 : ereport(ERROR,
220 : (errmsg("cannot load \"%s\" after startup", "test_slru"),
221 : errdetail("\"%s\" must be loaded with \"shared_preload_libraries\".",
222 : "test_slru")));
223 :
224 : /*
225 : * Create the SLRU directory if it does not exist yet, from the root of
226 : * the data directory.
227 : */
228 4 : (void) MakePGDirectory(TestSlruDir);
229 :
230 4 : RegisterShmemCallbacks(&test_slru_shmem_callbacks);
231 4 : }
232 :
233 : static void
234 4 : test_slru_shmem_request(void *arg)
235 : {
236 4 : SimpleLruRequest(.desc = &TestSlruDesc,
237 : .name = "TestSLRU",
238 : .Dir = TestSlruDir,
239 :
240 : /*
241 : * Short segments names are well tested elsewhere so in this test we are
242 : * focusing on long names.
243 : */
244 : .long_segment_names = true,
245 :
246 : .nslots = NUM_TEST_BUFFERS,
247 : .nlsns = 0,
248 :
249 : .sync_handler = SYNC_HANDLER_NONE,
250 : .PagePrecedes = test_slru_page_precedes_logically,
251 : .errdetail_for_io_error = test_slru_errdetail_for_io_error,
252 :
253 : /* let slru.c assign these */
254 : .buffer_tranche_id = 0,
255 : .bank_tranche_id = 0,
256 : );
257 4 : }
|