blob: ae21444c47637103b3dc73fadc60a23d271ef7c4 [file]
/*--------------------------------------------------------------------------
*
* test_slru.c
* Test correctness of SLRU functions.
*
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/test/modules/test_slru/test_slru.c
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/slru.h"
#include "access/transam.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/shmem.h"
#include "utils/builtins.h"
PG_MODULE_MAGIC;
/*
* SQL-callable entry points
*/
PG_FUNCTION_INFO_V1(test_slru_page_write);
PG_FUNCTION_INFO_V1(test_slru_page_writeall);
PG_FUNCTION_INFO_V1(test_slru_page_read);
PG_FUNCTION_INFO_V1(test_slru_page_readonly);
PG_FUNCTION_INFO_V1(test_slru_page_exists);
PG_FUNCTION_INFO_V1(test_slru_page_sync);
PG_FUNCTION_INFO_V1(test_slru_page_delete);
PG_FUNCTION_INFO_V1(test_slru_page_truncate);
PG_FUNCTION_INFO_V1(test_slru_delete_all);
/* Number of SLRU page slots */
#define NUM_TEST_BUFFERS 16
/* SLRU control lock */
LWLock TestSLRULock;
#define TestSLRULock (&TestSLRULock)
static SlruCtlData TestSlruCtlData;
#define TestSlruCtl (&TestSlruCtlData)
static shmem_request_hook_type prev_shmem_request_hook = NULL;
static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
static bool
test_slru_scan_cb(SlruCtl ctl, char *filename, int segpage, void *data)
{
elog(NOTICE, "Calling test_slru_scan_cb()");
return SlruScanDirCbDeleteAll(ctl, filename, segpage, data);
}
Datum
test_slru_page_write(PG_FUNCTION_ARGS)
{
int pageno = PG_GETARG_INT32(0);
char *data = text_to_cstring(PG_GETARG_TEXT_PP(1));
int slotno;
LWLockAcquire(TestSLRULock, LW_EXCLUSIVE);
slotno = SimpleLruZeroPage(TestSlruCtl, pageno);
/* these should match */
Assert(TestSlruCtl->shared->page_number[slotno] == pageno);
/* mark the page as dirty so as it would get written */
TestSlruCtl->shared->page_dirty[slotno] = true;
TestSlruCtl->shared->page_status[slotno] = SLRU_PAGE_VALID;
/* write given data to the page, up to the limit of the page */
strncpy(TestSlruCtl->shared->page_buffer[slotno], data,
BLCKSZ - 1);
SimpleLruWritePage(TestSlruCtl, slotno);
LWLockRelease(TestSLRULock);
PG_RETURN_VOID();
}
Datum
test_slru_page_writeall(PG_FUNCTION_ARGS)
{
SimpleLruWriteAll(TestSlruCtl, true);
PG_RETURN_VOID();
}
Datum
test_slru_page_read(PG_FUNCTION_ARGS)
{
int pageno = PG_GETARG_INT32(0);
bool write_ok = PG_GETARG_BOOL(1);
char *data = NULL;
int slotno;
/* find page in buffers, reading it if necessary */
LWLockAcquire(TestSLRULock, LW_EXCLUSIVE);
slotno = SimpleLruReadPage(TestSlruCtl, pageno,
write_ok, InvalidTransactionId);
data = (char *) TestSlruCtl->shared->page_buffer[slotno];
LWLockRelease(TestSLRULock);
PG_RETURN_TEXT_P(cstring_to_text(data));
}
Datum
test_slru_page_readonly(PG_FUNCTION_ARGS)
{
int pageno = PG_GETARG_INT32(0);
char *data = NULL;
int slotno;
/* find page in buffers, reading it if necessary */
slotno = SimpleLruReadPage_ReadOnly(TestSlruCtl,
pageno,
InvalidTransactionId);
Assert(LWLockHeldByMe(TestSLRULock));
data = (char *) TestSlruCtl->shared->page_buffer[slotno];
LWLockRelease(TestSLRULock);
PG_RETURN_TEXT_P(cstring_to_text(data));
}
Datum
test_slru_page_exists(PG_FUNCTION_ARGS)
{
int pageno = PG_GETARG_INT32(0);
bool found;
LWLockAcquire(TestSLRULock, LW_EXCLUSIVE);
found = SimpleLruDoesPhysicalPageExist(TestSlruCtl, pageno);
LWLockRelease(TestSLRULock);
PG_RETURN_BOOL(found);
}
Datum
test_slru_page_sync(PG_FUNCTION_ARGS)
{
int pageno = PG_GETARG_INT32(0);
FileTag ftag;
char path[MAXPGPATH];
/* note that this flushes the full file a segment is located in */
ftag.segno = pageno / SLRU_PAGES_PER_SEGMENT;
SlruSyncFileTag(TestSlruCtl, &ftag, path);
elog(NOTICE, "Called SlruSyncFileTag() for segment %u on path %s",
ftag.segno, path);
PG_RETURN_VOID();
}
Datum
test_slru_page_delete(PG_FUNCTION_ARGS)
{
int pageno = PG_GETARG_INT32(0);
FileTag ftag;
ftag.segno = pageno / SLRU_PAGES_PER_SEGMENT;
SlruDeleteSegment(TestSlruCtl, ftag.segno);
elog(NOTICE, "Called SlruDeleteSegment() for segment %u", ftag.segno);
PG_RETURN_VOID();
}
Datum
test_slru_page_truncate(PG_FUNCTION_ARGS)
{
int pageno = PG_GETARG_INT32(0);
SimpleLruTruncate(TestSlruCtl, pageno);
PG_RETURN_VOID();
}
Datum
test_slru_delete_all(PG_FUNCTION_ARGS)
{
/* this calls SlruScanDirCbDeleteAll() internally, ensuring deletion */
SlruScanDirectory(TestSlruCtl, test_slru_scan_cb, NULL);
PG_RETURN_VOID();
}
/*
* Module load callbacks and initialization.
*/
static void
test_slru_shmem_request(void)
{
if (prev_shmem_request_hook)
prev_shmem_request_hook();
/* reserve shared memory for the test SLRU */
RequestAddinShmemSpace(SimpleLruShmemSize(NUM_TEST_BUFFERS, 0));
}
static bool
test_slru_page_precedes_logically(int page1, int page2)
{
return page1 < page2;
}
static void
test_slru_shmem_startup(void)
{
const char slru_dir_name[] = "pg_test_slru";
int test_tranche_id;
if (prev_shmem_startup_hook)
prev_shmem_startup_hook();
/*
* Create the SLRU directory if it does not exist yet, from the root of
* the data directory.
*/
(void) MakePGDirectory(slru_dir_name);
/* initialize the SLRU facility */
test_tranche_id = LWLockNewTrancheId();
LWLockRegisterTranche(test_tranche_id, "test_slru_tranche");
LWLockInitialize(TestSLRULock, test_tranche_id);
TestSlruCtl->PagePrecedes = test_slru_page_precedes_logically;
SimpleLruInit(TestSlruCtl, "TestSLRU",
NUM_TEST_BUFFERS, 0, TestSLRULock, slru_dir_name,
test_tranche_id, SYNC_HANDLER_NONE);
}
void
_PG_init(void)
{
if (!process_shared_preload_libraries_in_progress)
ereport(ERROR,
(errmsg("cannot load \"%s\" after startup", "test_slru"),
errdetail("\"%s\" must be loaded with shared_preload_libraries.",
"test_slru")));
prev_shmem_request_hook = shmem_request_hook;
shmem_request_hook = test_slru_shmem_request;
prev_shmem_startup_hook = shmem_startup_hook;
shmem_startup_hook = test_slru_shmem_startup;
}