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-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 : :
1266 michael@paquier.xyz 25 :CBC 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
29 heikki.linnakangas@i 58 :GNC 1 : test_slru_scan_cb(SlruDesc *ctl, char *filename, int64 segpage, void *data)
59 : : {
1266 michael@paquier.xyz 60 [ + - ]:CBC 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 : : {
888 akorotkov@postgresql 67 : 98 : int64 pageno = PG_GETARG_INT64(0);
1266 michael@paquier.xyz 68 : 98 : char *data = text_to_cstring(PG_GETARG_TEXT_PP(1));
69 : : int slotno;
797 alvherre@alvh.no-ip. 70 : 98 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
71 : :
72 : 98 : LWLockAcquire(lock, LW_EXCLUSIVE);
1266 michael@paquier.xyz 73 : 98 : slotno = SimpleLruZeroPage(TestSlruCtl, pageno);
74 : :
75 : : /* these should match */
76 [ - + ]: 98 : 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);
797 alvherre@alvh.no-ip. 87 : 98 : LWLockRelease(lock);
88 : :
1266 michael@paquier.xyz 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 : : {
888 akorotkov@postgresql 102 : 5 : int64 pageno = PG_GETARG_INT64(0);
1266 michael@paquier.xyz 103 : 5 : bool write_ok = PG_GETARG_BOOL(1);
53 heikki.linnakangas@i 104 :GNC 5 : TransactionId xid = PG_GETARG_TRANSACTIONID(2);
1266 michael@paquier.xyz 105 :CBC 5 : char *data = NULL;
106 : : int slotno;
797 alvherre@alvh.no-ip. 107 : 5 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
108 : :
109 : : /* find page in buffers, reading it if necessary */
110 : 5 : LWLockAcquire(lock, LW_EXCLUSIVE);
53 heikki.linnakangas@i 111 :GNC 5 : slotno = SimpleLruReadPage(TestSlruCtl, pageno, write_ok, &xid);
1266 michael@paquier.xyz 112 :CBC 4 : data = (char *) TestSlruCtl->shared->page_buffer[slotno];
797 alvherre@alvh.no-ip. 113 : 4 : LWLockRelease(lock);
114 : :
1266 michael@paquier.xyz 115 : 4 : PG_RETURN_TEXT_P(cstring_to_text(data));
116 : : }
117 : :
118 : : Datum
119 : 4 : test_slru_page_readonly(PG_FUNCTION_ARGS)
120 : : {
888 akorotkov@postgresql 121 : 4 : int64 pageno = PG_GETARG_INT64(0);
1266 michael@paquier.xyz 122 : 4 : char *data = NULL;
123 : : int slotno;
797 alvherre@alvh.no-ip. 124 : 4 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
125 : :
126 : : /* find page in buffers, reading it if necessary */
1266 michael@paquier.xyz 127 : 4 : slotno = SimpleLruReadPage_ReadOnly(TestSlruCtl,
128 : : pageno,
129 : : NULL);
797 alvherre@alvh.no-ip. 130 [ - + ]: 4 : Assert(LWLockHeldByMe(lock));
1266 michael@paquier.xyz 131 : 4 : data = (char *) TestSlruCtl->shared->page_buffer[slotno];
797 alvherre@alvh.no-ip. 132 : 4 : LWLockRelease(lock);
133 : :
1266 michael@paquier.xyz 134 : 4 : PG_RETURN_TEXT_P(cstring_to_text(data));
135 : : }
136 : :
137 : : Datum
138 : 18 : test_slru_page_exists(PG_FUNCTION_ARGS)
139 : : {
888 akorotkov@postgresql 140 : 18 : int64 pageno = PG_GETARG_INT64(0);
141 : : bool found;
797 alvherre@alvh.no-ip. 142 : 18 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
143 : :
144 : 18 : LWLockAcquire(lock, LW_EXCLUSIVE);
1266 michael@paquier.xyz 145 : 18 : found = SimpleLruDoesPhysicalPageExist(TestSlruCtl, pageno);
797 alvherre@alvh.no-ip. 146 : 18 : LWLockRelease(lock);
147 : :
1266 michael@paquier.xyz 148 : 18 : PG_RETURN_BOOL(found);
149 : : }
150 : :
151 : : Datum
152 : 2 : test_slru_page_sync(PG_FUNCTION_ARGS)
153 : : {
888 akorotkov@postgresql 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 */
1266 michael@paquier.xyz 159 : 2 : ftag.segno = pageno / SLRU_PAGES_PER_SEGMENT;
160 : 2 : SlruSyncFileTag(TestSlruCtl, &ftag, path);
161 : :
371 peter@eisentraut.org 162 [ + - ]: 2 : elog(NOTICE, "Called SlruSyncFileTag() for segment %" PRIu64 " on path %s",
163 : : ftag.segno, path);
164 : :
1266 michael@paquier.xyz 165 : 2 : PG_RETURN_VOID();
166 : : }
167 : :
168 : : Datum
169 : 2 : test_slru_page_delete(PG_FUNCTION_ARGS)
170 : : {
888 akorotkov@postgresql 171 : 2 : int64 pageno = PG_GETARG_INT64(0);
172 : : FileTag ftag;
173 : :
1266 michael@paquier.xyz 174 : 2 : ftag.segno = pageno / SLRU_PAGES_PER_SEGMENT;
175 : 2 : SlruDeleteSegment(TestSlruCtl, ftag.segno);
176 : :
371 peter@eisentraut.org 177 [ + - ]: 2 : elog(NOTICE, "Called SlruDeleteSegment() for segment %" PRIu64,
178 : : ftag.segno);
179 : :
1266 michael@paquier.xyz 180 : 2 : PG_RETURN_VOID();
181 : : }
182 : :
183 : : Datum
184 : 2 : test_slru_page_truncate(PG_FUNCTION_ARGS)
185 : : {
888 akorotkov@postgresql 186 : 2 : int64 pageno = PG_GETARG_INT64(0);
187 : :
1266 michael@paquier.xyz 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
888 akorotkov@postgresql 202 : 15 : test_slru_page_precedes_logically(int64 page1, int64 page2)
203 : : {
1266 michael@paquier.xyz 204 : 15 : return page1 < page2;
205 : : }
206 : :
207 : : static int
53 heikki.linnakangas@i 208 :GNC 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);
53 heikki.linnakangas@i 213 :ECB (3) : }
214 : :
215 : : void
29 heikki.linnakangas@i 216 :CBC 4 : _PG_init(void)
217 : : {
218 [ - + ]: 4 : if (!process_shared_preload_libraries_in_progress)
29 heikki.linnakangas@i 219 [ # # ]:UBC 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 : : */
29 heikki.linnakangas@i 228 :GNC 4 : (void) MakePGDirectory(TestSlruDir);
229 : :
230 : 4 : RegisterShmemCallbacks(&test_slru_shmem_callbacks);
1266 michael@paquier.xyz 231 : 4 : }
232 : :
233 : : static void
29 heikki.linnakangas@i 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 : : );
1266 michael@paquier.xyz 257 :CBC 4 : }
|