| /*-------------------------------------------------------------------------- |
| * |
| * test_custom_rmgrs.c |
| * Code for testing custom WAL resource managers. |
| * |
| * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * IDENTIFICATION |
| * src/test/modules/test_custom_rmgrs/test_custom_rmgrs.c |
| * |
| * Custom WAL resource manager for records containing a simple textual |
| * payload, no-op redo, and no decoding. |
| * |
| * ------------------------------------------------------------------------- |
| */ |
| |
| #include "postgres.h" |
| |
| #include "access/xlog.h" |
| #include "access/xlog_internal.h" |
| #include "access/xloginsert.h" |
| #include "fmgr.h" |
| #include "utils/pg_lsn.h" |
| #include "varatt.h" |
| |
| PG_MODULE_MAGIC; |
| |
| /* |
| * test_custom_rmgrs WAL record message. |
| */ |
| typedef struct xl_testcustomrmgrs_message |
| { |
| Size message_size; /* size of the message */ |
| char message[FLEXIBLE_ARRAY_MEMBER]; /* payload */ |
| } xl_testcustomrmgrs_message; |
| |
| #define SizeOfTestCustomRmgrsMessage (offsetof(xl_testcustomrmgrs_message, message)) |
| #define XLOG_TEST_CUSTOM_RMGRS_MESSAGE 0x00 |
| |
| /* |
| * While developing or testing, use RM_EXPERIMENTAL_ID for rmid. For a real |
| * extension, reserve a new resource manager ID to avoid conflicting with |
| * other extensions; see: |
| * https://wiki.postgresql.org/wiki/CustomWALResourceManagers |
| */ |
| #define RM_TESTCUSTOMRMGRS_ID RM_EXPERIMENTAL_ID |
| #define TESTCUSTOMRMGRS_NAME "test_custom_rmgrs" |
| |
| /* RMGR API, see xlog_internal.h */ |
| void testcustomrmgrs_redo(XLogReaderState *record); |
| void testcustomrmgrs_desc(StringInfo buf, XLogReaderState *record); |
| const char *testcustomrmgrs_identify(uint8 info); |
| |
| static const RmgrData testcustomrmgrs_rmgr = { |
| .rm_name = TESTCUSTOMRMGRS_NAME, |
| .rm_redo = testcustomrmgrs_redo, |
| .rm_desc = testcustomrmgrs_desc, |
| .rm_identify = testcustomrmgrs_identify |
| }; |
| |
| /* |
| * Module load callback |
| */ |
| void |
| _PG_init(void) |
| { |
| /* |
| * In order to create our own custom resource manager, we have to be |
| * loaded via shared_preload_libraries. Otherwise, registration will fail. |
| */ |
| RegisterCustomRmgr(RM_TESTCUSTOMRMGRS_ID, &testcustomrmgrs_rmgr); |
| } |
| |
| /* RMGR API implementation */ |
| |
| /* |
| * Redo is just a noop for this module, because we aren't testing recovery of |
| * any real structure. |
| */ |
| void |
| testcustomrmgrs_redo(XLogReaderState *record) |
| { |
| uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; |
| |
| if (info != XLOG_TEST_CUSTOM_RMGRS_MESSAGE) |
| elog(PANIC, "testcustomrmgrs_redo: unknown op code %u", info); |
| } |
| |
| void |
| testcustomrmgrs_desc(StringInfo buf, XLogReaderState *record) |
| { |
| char *rec = XLogRecGetData(record); |
| uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; |
| |
| if (info == XLOG_TEST_CUSTOM_RMGRS_MESSAGE) |
| { |
| xl_testcustomrmgrs_message *xlrec = (xl_testcustomrmgrs_message *) rec; |
| |
| appendStringInfo(buf, "payload (%zu bytes): ", xlrec->message_size); |
| appendBinaryStringInfo(buf, xlrec->message, xlrec->message_size); |
| } |
| } |
| |
| const char * |
| testcustomrmgrs_identify(uint8 info) |
| { |
| if ((info & ~XLR_INFO_MASK) == XLOG_TEST_CUSTOM_RMGRS_MESSAGE) |
| return "TEST_CUSTOM_RMGRS_MESSAGE"; |
| |
| return NULL; |
| } |
| |
| /* |
| * SQL function for writing a simple message into WAL with the help of custom |
| * WAL resource manager. |
| */ |
| PG_FUNCTION_INFO_V1(test_custom_rmgrs_insert_wal_record); |
| Datum |
| test_custom_rmgrs_insert_wal_record(PG_FUNCTION_ARGS) |
| { |
| text *arg = PG_GETARG_TEXT_PP(0); |
| char *payload = VARDATA_ANY(arg); |
| Size len = VARSIZE_ANY_EXHDR(arg); |
| XLogRecPtr lsn; |
| xl_testcustomrmgrs_message xlrec; |
| |
| xlrec.message_size = len; |
| |
| XLogBeginInsert(); |
| XLogRegisterData((char *) &xlrec, SizeOfTestCustomRmgrsMessage); |
| XLogRegisterData((char *) payload, len); |
| |
| /* Let's mark this record as unimportant, just in case. */ |
| XLogSetRecordFlags(XLOG_MARK_UNIMPORTANT); |
| |
| lsn = XLogInsert(RM_TESTCUSTOMRMGRS_ID, XLOG_TEST_CUSTOM_RMGRS_MESSAGE); |
| |
| PG_RETURN_LSN(lsn); |
| } |