Latest version of rmgr hooks patch for later review in current commitfest.
(Minor update to CVS HEAD). -- Simon Riggs www.2ndQuadrant.com PostgreSQL Training, Services and Support
Index: src/backend/access/transam/rmgr.c =================================================================== RCS file: /home/sriggs/pg/REPOSITORY/pgsql/src/backend/access/transam/rmgr.c,v retrieving revision 1.27 diff -c -r1.27 rmgr.c *** src/backend/access/transam/rmgr.c 19 Nov 2008 10:34:50 -0000 1.27 --- src/backend/access/transam/rmgr.c 17 Dec 2008 19:05:51 -0000 *************** *** 7,12 **** --- 7,13 ---- */ #include "postgres.h" + #include "miscadmin.h" #include "access/clog.h" #include "access/gin.h" #include "access/gist_private.h" *************** *** 16,21 **** --- 17,23 ---- #include "access/nbtree.h" #include "access/xact.h" #include "access/xlog_internal.h" + #include "nodes/bitmapset.h" #include "catalog/storage.h" #include "commands/dbcommands.h" #include "commands/sequence.h" *************** *** 23,29 **** #include "storage/freespace.h" ! const RmgrData RmgrTable[RM_MAX_ID + 1] = { {"XLOG", xlog_redo, xlog_desc, NULL, NULL, NULL}, {"Transaction", xact_redo, xact_desc, NULL, NULL, NULL}, {"Storage", smgr_redo, smgr_desc, NULL, NULL, NULL}, --- 25,31 ---- #include "storage/freespace.h" ! const RmgrData FixedRmgrTable[RM_MAX_FIXED_ID + 1] = { {"XLOG", xlog_redo, xlog_desc, NULL, NULL, NULL}, {"Transaction", xact_redo, xact_desc, NULL, NULL, NULL}, {"Storage", smgr_redo, smgr_desc, NULL, NULL, NULL}, *************** *** 41,43 **** --- 43,366 ---- {"Gist", gist_redo, gist_desc, gist_xlog_startup, gist_xlog_cleanup, gist_safe_restartpoint}, {"Sequence", seq_redo, seq_desc, NULL, NULL, NULL} }; + + /* Main table of Resource Managers */ + RmgrData *RmgrTable; + + /* Hook for plugins to get control in RmgrInitialize() */ + rmgr_hook_type rmgr_hook = NULL; + + #define MAX_NUM_RMGRS 255 + #define RMGR_BITS_PER_WORD 32 + #define RMGR_BITMAP_WORDS ((MAX_NUM_RMGRS + 1) / RMGR_BITS_PER_WORD) + typedef struct RmgrCtlData + { + uint32 RmgrBitmap[RMGR_BITMAP_WORDS]; /* fixed size bitmapset */ + } RmgrCtlData; + + static RmgrCtlData *RmgrCtl = NULL; + + /* + * Initialization of shared memory for Rmgrs + */ + Size + RmgrShmemSize(void) + { + return sizeof(RmgrCtlData); + } + + void + RmgrShmemInit(void) + { + bool foundRmgr; + + RmgrCtl = (RmgrCtlData *) + ShmemInitStruct("Rmgr Ctl", RmgrShmemSize(), &foundRmgr); + + if (foundRmgr) + return; + + memset(RmgrCtl, 0, sizeof(RmgrCtlData)); + } + + /* + * RmgrInitialize() + * + * Create RmgrTable, populate RmgrTable with fixed Rmgrs, call plugin + * and then initialize shared list of current resource managers. + * + * RmgrInitialize must run before any other Rmgr function. Normally, + * it runs only in the Startup process (once), ensuring that the RmgrTable + * and its functions cannot be accessed by normal backends. + * + * If we have WAL_DEBUG set then the plugin will be called multiple + * times and so must be designed to return same answer every time. + */ + void + RmgrInitialize(void) + { + int rmid; + int num_rmgrs = 0; + + if (RmgrTable) + return; + + /* + * Create local copy of RmgrTable. Memory is never freed. + */ + RmgrTable = malloc(sizeof(RmgrData) * (MAX_NUM_RMGRS + 1)); + + for (rmid = 0; rmid <= MAX_NUM_RMGRS; rmid++) + { + if (rmid <= RM_MAX_FIXED_ID) + { + RmgrTable[rmid].rm_name = FixedRmgrTable[rmid].rm_name; + RmgrTable[rmid].rm_redo = FixedRmgrTable[rmid].rm_redo; + RmgrTable[rmid].rm_desc = FixedRmgrTable[rmid].rm_desc; + RmgrTable[rmid].rm_startup = FixedRmgrTable[rmid].rm_startup; + RmgrTable[rmid].rm_cleanup = FixedRmgrTable[rmid].rm_cleanup; + RmgrTable[rmid].rm_safe_restartpoint = + FixedRmgrTable[rmid].rm_safe_restartpoint; + } + else + { + RmgrTable[rmid].rm_name = NULL; + RmgrTable[rmid].rm_redo = NULL; + RmgrTable[rmid].rm_desc = NULL; + RmgrTable[rmid].rm_startup = NULL; + RmgrTable[rmid].rm_cleanup = NULL; + RmgrTable[rmid].rm_safe_restartpoint = NULL; + } + } + + /* + * Call plugin, if present and output log message to assist with + * diagnosis of recovery issues. + * + * What is the plugin allowed to do? + * All RMs are assigned to a single RmgrId. All RM functions are + * optional, though we will only allow WAL inserts for RmgrIds that + * have a defined rm_redo function. The fixed RMs can be overridden + * by providing new routines that do similar, yet slightly different + * actions such as filters. There is no direct connection between + * an RM and and entry in pg_am, so it is possible to create a new + * index type but then not be able to insert into that index, iff + * the new index code issues any WAL inserts. That error is quickly + * discovered in practice, even if the docs are ignored. That sounds + * a little strange, but then pg_am is not readable at the time we + * need to know which RM functions to use for recovery. + * + * XXX we cannot yet issue an index rebuild from rm_cleanup() + * though that feature is expected to be achieved one day + */ + if (rmgr_hook) + { + elog(LOG, "Executing Rmgr hook to extend and/or modify resource managers"); + (*rmgr_hook) (RmgrTable); + } + + /* + * Now create data structure to allow checking of RmgrId in each backend. + */ + for (rmid = 0; rmid <= MAX_NUM_RMGRS; rmid++) + { + if (RmgrTable[rmid].rm_redo != NULL) + { + int wordnum = rmid / RMGR_BITS_PER_WORD; + int bitnum = rmid % RMGR_BITS_PER_WORD; + RmgrCtl->RmgrBitmap[wordnum] |= ((uint32) 1 << bitnum); + + num_rmgrs++; + + /* + * Keep track of new or modified RMs for diagnostic purposes + */ + if (rmid <= RM_MAX_FIXED_ID) + { + if (RmgrTable[rmid].rm_name != FixedRmgrTable[rmid].rm_name || + RmgrTable[rmid].rm_redo != FixedRmgrTable[rmid].rm_redo || + RmgrTable[rmid].rm_startup != FixedRmgrTable[rmid].rm_startup || + RmgrTable[rmid].rm_cleanup != FixedRmgrTable[rmid].rm_cleanup || + RmgrTable[rmid].rm_safe_restartpoint != + FixedRmgrTable[rmid].rm_safe_restartpoint) + elog(LOG, "Rmgr (%d) %s has been modified", rmid, + FixedRmgrTable[rmid].rm_name); + } + else + elog(LOG, "Rmgr (%d) %s defined", rmid, + (RmgrTable[rmid].rm_name != NULL ? + RmgrTable[rmid].rm_name : "<Unnamed>")); + } + } + + if (num_rmgrs == 0) + elog(FATAL, "No valid resource managers defined"); + } + + + /* + * RmgrStartup() + * + * Allow resource managers to do any required startup. + * Run RM startup functions once, only ever runs in Startup process. + */ + void + RmgrStartup(void) + { + int rmid; + + Assert(RmgrTable); + for (rmid = 0; rmid <= MAX_NUM_RMGRS; rmid++) + { + if (RmgrTable[rmid].rm_startup != NULL) + { + elog(DEBUG2, "RmgrStartup (%d) %s", rmid, RmgrTable[rmid].rm_name); + RmgrTable[rmid].rm_startup(); + } + } + } + + /* + * RmgrCleanup() + * + * Allow resource managers to do any required cleanup. + * Run RM cleanup functions once, only ever runs in Startup process. + * + * Called once prior to start of normal running + */ + void + RmgrCleanup(void) + { + int rmid; + + Assert(RmgrTable); + for (rmid = 0; rmid <= MAX_NUM_RMGRS; rmid++) + { + if (RmgrTable[rmid].rm_cleanup != NULL) + { + elog(DEBUG2, "RmgrCleanup (%d) %s", rmid, RmgrTable[rmid].rm_name); + RmgrTable[rmid].rm_cleanup(); + } + } + } + + /* + * RmgrSafeRestartpoint() + * + * Is it safe to mark a restartpoint? We must ask each of the resource + * managers whether they have any partial state information that might + * prevent a correct restart from the supplied LSN. + * + * Called once every 5 minutes with default settings, sometimes more + * frequently if last call returned false. + */ + bool + RmgrSafeRestartpoint(XLogRecPtr checkPointRedo) + { + int rmid; + + Assert(RmgrTable); + for (rmid = 0; rmid <= MAX_NUM_RMGRS; rmid++) + { + if (RmgrTable[rmid].rm_safe_restartpoint != NULL) + if (!(RmgrTable[rmid].rm_safe_restartpoint())) + { + elog(DEBUG2, "RM (%d) %s not safe to record restart point at %X/%X", + rmid, + RmgrTable[rmid].rm_name, + checkPointRedo.xlogid, + checkPointRedo.xrecoff); + return false; + } + } + + return true; + } + + /* + * RmgrRedo + * + * Call the RM's redo function to apply WAL records. The RmgrId should + * previously have been checked using RmgrIdIsValid(). + */ + void + RmgrRedo(XLogRecPtr lsn, XLogRecord *rptr) + { + RmgrTable[rptr->xl_rmid].rm_redo(lsn, rptr); + } + + /* + * Error context callback for errors occurring during rm_redo(). + */ + void + rm_redo_error_callback(void *arg) + { + XLogRecord *record = (XLogRecord *) arg; + StringInfoData buf; + + initStringInfo(&buf) ; + Assert(RmgrTable); + if (RmgrTable[record->xl_rmid].rm_desc != NULL) + RmgrTable[record->xl_rmid].rm_desc(&buf, + record->xl_info, + XLogRecGetData(record)); + + /* don't bother emitting empty description */ + if (buf.len > 0) + errcontext("xlog redo %s", buf.data); + + pfree(buf.data); + } + + /* + * RmgrDesc + * + * Call the RM's desc function to print details of WAL records + * during error handling or when WAL_DEBUG is set + */ + void + RmgrDesc(StringInfo buf, XLogRecord *rptr, char *rec) + { + if (RmgrTable[rptr->xl_rmid].rm_desc != NULL) + RmgrTable[rptr->xl_rmid].rm_desc(buf, rptr->xl_info, rec); + } + + /* + * RmgrName + * + * Get the RM's namefor use when WAL_DEBUG is set + */ + const char * + RmgrName(RmgrId rmid) + { + Assert(RmgrTable); + return RmgrTable[rmid].rm_name; + } + + /* + * RmgrIsIsValid + * + * Check whether the RmgrId has a valid rm_redo function currently + * Caller throws any error, not here. + */ + bool + RmgrIdIsValid(RmgrId rmid) + { + int wordnum = rmid / RMGR_BITS_PER_WORD; + int bitnum = rmid % RMGR_BITS_PER_WORD; + + /* + * If not already done so, get set of valid RmgrIds as a bms + */ + if (!RmgrCtl) + RmgrShmemInit(); + + /* + * Check validity + */ + if ((RmgrCtl->RmgrBitmap[wordnum] & ((bitmapword) 1 << bitnum)) != 0) + return true; + + return false; + } + Index: src/backend/access/transam/xlog.c =================================================================== RCS file: /home/sriggs/pg/REPOSITORY/pgsql/src/backend/access/transam/xlog.c,v retrieving revision 1.323 diff -c -r1.323 xlog.c *** src/backend/access/transam/xlog.c 3 Dec 2008 08:20:11 -0000 1.323 --- src/backend/access/transam/xlog.c 17 Dec 2008 19:04:34 -0000 *************** *** 437,443 **** static void pg_start_backup_callback(int code, Datum arg); static bool read_backup_label(XLogRecPtr *checkPointLoc, XLogRecPtr *minRecoveryLoc); - static void rm_redo_error_callback(void *arg); static int get_sync_bit(int method); --- 437,442 ---- *************** *** 498,503 **** --- 497,513 ---- } /* + * Check that we are writing a currently valid RmgrId. If not, then + * we cannot allow this to be written to WAL otherwise we would be + * unable to recover correctly from crash and could lose data written + * after an invalid WAL record. + */ + if (!RmgrIdIsValid(rmid)) + ereport(ERROR, + (errmsg("invalid resource manager ID %u", rmid), + errhint("You must configure resource manager at startup."))); + + /* * Here we scan the rdata chain, determine which buffers must be backed * up, and compute the CRC values for the data. Note that the record * header isn't added into the CRC initially since we don't know the final *************** *** 829,834 **** --- 839,846 ---- { StringInfoData buf; + RmgrInitialize(); + initStringInfo(&buf); appendStringInfo(&buf, "INSERT @ %X/%X: ", RecPtr.xlogid, RecPtr.xrecoff); *************** *** 836,842 **** if (rdata->data != NULL) { appendStringInfo(&buf, " - "); ! RmgrTable[record->xl_rmid].rm_desc(&buf, record->xl_info, rdata->data); } elog(LOG, "%s", buf.data); pfree(buf.data); --- 848,854 ---- if (rdata->data != NULL) { appendStringInfo(&buf, " - "); ! RmgrDesc(&buf, record, rdata->data); } elog(LOG, "%s", buf.data); pfree(buf.data); *************** *** 3235,3244 **** RecPtr->xlogid, RecPtr->xrecoff))); goto next_record_is_invalid; } ! if (record->xl_rmid > RM_MAX_ID) { ereport(emode, ! (errmsg("invalid resource manager ID %u at %X/%X", record->xl_rmid, RecPtr->xlogid, RecPtr->xrecoff))); goto next_record_is_invalid; } --- 3247,3256 ---- RecPtr->xlogid, RecPtr->xrecoff))); goto next_record_is_invalid; } ! if (!RmgrIdIsValid(record->xl_rmid)) { ereport(emode, ! (errmsg("invalid resource manager ID %u" " at %X/%X", record->xl_rmid, RecPtr->xlogid, RecPtr->xrecoff))); goto next_record_is_invalid; } *************** *** 4945,4950 **** --- 4957,4971 ---- */ readRecoveryCommandFile(); + /* + * Initialize RMs. Startup functions are *not* called unless we + * are InRecovery. We do this in two steps so that we know what + * RMs are currently valid for when we call XLogInsert(). We + * specifically do not write the list of RMs to the ControlFile, + * to allow more flexibility in adding/removing RMs. + */ + RmgrInitialize(); + /* Now we can determine the list of expected TLIs */ expectedTLIs = readTimeLineHistory(recoveryTargetTLI); *************** *** 5074,5081 **** /* REDO */ if (InRecovery) { - int rmid; - /* * Update pg_control to show that we are recovering and to show the * selected checkpoint as the place we are starting from. We also mark --- 5095,5100 ---- *************** *** 5121,5132 **** BACKUP_LABEL_FILE, BACKUP_LABEL_OLD))); } ! /* Initialize resource managers */ ! for (rmid = 0; rmid <= RM_MAX_ID; rmid++) ! { ! if (RmgrTable[rmid].rm_startup != NULL) ! RmgrTable[rmid].rm_startup(); ! } /* * Find the first record that logically follows the checkpoint --- it --- 5140,5149 ---- BACKUP_LABEL_FILE, BACKUP_LABEL_OLD))); } ! /* ! * Startup Resource Managers ! */ ! RmgrStartup(); /* * Find the first record that logically follows the checkpoint --- it *************** *** 5170,5178 **** EndRecPtr.xlogid, EndRecPtr.xrecoff); xlog_outrec(&buf, record); appendStringInfo(&buf, " - "); ! RmgrTable[record->xl_rmid].rm_desc(&buf, ! record->xl_info, ! XLogRecGetData(record)); elog(LOG, "%s", buf.data); pfree(buf.data); } --- 5187,5193 ---- EndRecPtr.xlogid, EndRecPtr.xrecoff); xlog_outrec(&buf, record); appendStringInfo(&buf, " - "); ! RmgrDesc(&buf, record, XLogRecGetData(record)); elog(LOG, "%s", buf.data); pfree(buf.data); } *************** *** 5190,5196 **** } /* Setup error traceback support for ereport() */ ! errcontext.callback = rm_redo_error_callback; errcontext.arg = (void *) record; errcontext.previous = error_context_stack; error_context_stack = &errcontext; --- 5205,5211 ---- } /* Setup error traceback support for ereport() */ ! errcontext.callback = rm_redo_error_callback; /* see rmgr.c */ errcontext.arg = (void *) record; errcontext.previous = error_context_stack; error_context_stack = &errcontext; *************** *** 5206,5212 **** if (record->xl_info & XLR_BKP_BLOCK_MASK) RestoreBkpBlocks(record, EndRecPtr); ! RmgrTable[record->xl_rmid].rm_redo(EndRecPtr, record); /* Pop the error context stack */ error_context_stack = errcontext.previous; --- 5221,5227 ---- if (record->xl_info & XLR_BKP_BLOCK_MASK) RestoreBkpBlocks(record, EndRecPtr); ! RmgrRedo(EndRecPtr, record); /* Pop the error context stack */ error_context_stack = errcontext.previous; *************** *** 5353,5368 **** if (InRecovery) { ! int rmid; ! ! /* ! * Allow resource managers to do any required cleanup. ! */ ! for (rmid = 0; rmid <= RM_MAX_ID; rmid++) ! { ! if (RmgrTable[rmid].rm_cleanup != NULL) ! RmgrTable[rmid].rm_cleanup(); ! } /* * Check to see if the XLOG sequence contained any unresolved --- 5368,5374 ---- if (InRecovery) { ! RmgrCleanup(); /* * Check to see if the XLOG sequence contained any unresolved *************** *** 6102,6108 **** RecoveryRestartPoint(const CheckPoint *checkPoint) { int elapsed_secs; - int rmid; /* * Do nothing if the elapsed time since the last restartpoint is less than --- 6108,6113 ---- *************** *** 6118,6140 **** return; /* ! * Is it safe to checkpoint? We must ask each of the resource managers ! * whether they have any partial state information that might prevent a ! * correct restart from this point. If so, we skip this opportunity, but * return at the next checkpoint record for another try. */ ! for (rmid = 0; rmid <= RM_MAX_ID; rmid++) ! { ! if (RmgrTable[rmid].rm_safe_restartpoint != NULL) ! if (!(RmgrTable[rmid].rm_safe_restartpoint())) ! { ! elog(DEBUG2, "RM %d not safe to record restart point at %X/%X", ! rmid, ! checkPoint->redo.xlogid, ! checkPoint->redo.xrecoff); ! return; ! } ! } /* * OK, force data out to disk --- 6123,6133 ---- return; /* ! * Is it safe to restart from here? If not, we skip this opportunity, but * return at the next checkpoint record for another try. */ ! if (!RmgrSafeRestartpoint(checkPoint->redo)) ! return; /* * OK, force data out to disk *************** *** 6369,6375 **** appendStringInfo(buf, "; bkpb%d", i + 1); } ! appendStringInfo(buf, ": %s", RmgrTable[record->xl_rmid].rm_name); } #endif /* WAL_DEBUG */ --- 6362,6368 ---- appendStringInfo(buf, "; bkpb%d", i + 1); } ! appendStringInfo(buf, ": %s", RmgrName(record->xl_rmid)); } #endif /* WAL_DEBUG */ *************** *** 7141,7167 **** } /* - * Error context callback for errors occurring during rm_redo(). - */ - static void - rm_redo_error_callback(void *arg) - { - XLogRecord *record = (XLogRecord *) arg; - StringInfoData buf; - - initStringInfo(&buf); - RmgrTable[record->xl_rmid].rm_desc(&buf, - record->xl_info, - XLogRecGetData(record)); - - /* don't bother emitting empty description */ - if (buf.len > 0) - errcontext("xlog redo %s", buf.data); - - pfree(buf.data); - } - - /* * BackupInProgress: check if online backup mode is active * * This is done by checking for existence of the "backup_label" file. --- 7134,7139 ---- Index: src/backend/storage/ipc/ipci.c =================================================================== RCS file: /home/sriggs/pg/REPOSITORY/pgsql/src/backend/storage/ipc/ipci.c,v retrieving revision 1.97 diff -c -r1.97 ipci.c *** src/backend/storage/ipc/ipci.c 30 Sep 2008 10:52:13 -0000 1.97 --- src/backend/storage/ipc/ipci.c 17 Dec 2008 19:04:34 -0000 *************** *** 18,23 **** --- 18,24 ---- #include "access/heapam.h" #include "access/multixact.h" #include "access/nbtree.h" + #include "access/rmgr.h" #include "access/subtrans.h" #include "access/twophase.h" #include "miscadmin.h" *************** *** 101,106 **** --- 102,108 ---- size = add_size(size, LockShmemSize()); size = add_size(size, ProcGlobalShmemSize()); size = add_size(size, XLOGShmemSize()); + size = add_size(size, RmgrShmemSize()); size = add_size(size, CLOGShmemSize()); size = add_size(size, SUBTRANSShmemSize()); size = add_size(size, TwoPhaseShmemSize()); *************** *** 174,182 **** InitShmemIndex(); /* ! * Set up xlog, clog, and buffers */ XLOGShmemInit(); CLOGShmemInit(); SUBTRANSShmemInit(); TwoPhaseShmemInit(); --- 176,185 ---- InitShmemIndex(); /* ! * Set up xlog, rmgr, clog, and buffers */ XLOGShmemInit(); + RmgrShmemInit(); CLOGShmemInit(); SUBTRANSShmemInit(); TwoPhaseShmemInit(); Index: src/include/access/rmgr.h =================================================================== RCS file: /home/sriggs/pg/REPOSITORY/pgsql/src/include/access/rmgr.h,v retrieving revision 1.19 diff -c -r1.19 rmgr.h *** src/include/access/rmgr.h 19 Nov 2008 10:34:52 -0000 1.19 --- src/include/access/rmgr.h 17 Dec 2008 19:04:34 -0000 *************** *** 10,15 **** --- 10,18 ---- typedef uint8 RmgrId; + extern Size RmgrShmemSize(void); + extern void RmgrShmemInit(void); + /* * Built-in resource managers * *************** *** 23,28 **** --- 26,33 ---- #define RM_DBASE_ID 4 #define RM_TBLSPC_ID 5 #define RM_MULTIXACT_ID 6 + /* RmgrId 7 is currently unused */ + /* RmgrId 8 is currently unused */ #define RM_HEAP2_ID 9 #define RM_HEAP_ID 10 #define RM_BTREE_ID 11 *************** *** 30,35 **** #define RM_GIN_ID 13 #define RM_GIST_ID 14 #define RM_SEQ_ID 15 ! #define RM_MAX_ID RM_SEQ_ID #endif /* RMGR_H */ --- 35,49 ---- #define RM_GIN_ID 13 #define RM_GIST_ID 14 #define RM_SEQ_ID 15 ! #define RM_MAX_FIXED_ID RM_SEQ_ID ! ! /* ! * RmgrId Reservation ! * ! * RmgrIds up to 31 are reserved for PostgreSQL Core use only ! * RmgrIds between 32 and 127 are available for registration by ! * PostgreSQL-related projects ! * RmgrIds of 128 and above are always designed for each site ! */ #endif /* RMGR_H */ Index: src/include/access/xlog_internal.h =================================================================== RCS file: /home/sriggs/pg/REPOSITORY/pgsql/src/include/access/xlog_internal.h,v retrieving revision 1.24 diff -c -r1.24 xlog_internal.h *** src/include/access/xlog_internal.h 11 Aug 2008 11:05:11 -0000 1.24 --- src/include/access/xlog_internal.h 17 Dec 2008 19:04:34 -0000 *************** *** 229,235 **** */ typedef struct RmgrData { ! const char *rm_name; void (*rm_redo) (XLogRecPtr lsn, XLogRecord *rptr); void (*rm_desc) (StringInfo buf, uint8 xl_info, char *rec); void (*rm_startup) (void); --- 229,235 ---- */ typedef struct RmgrData { ! char *rm_name; void (*rm_redo) (XLogRecPtr lsn, XLogRecord *rptr); void (*rm_desc) (StringInfo buf, uint8 xl_info, char *rec); void (*rm_startup) (void); *************** *** 237,243 **** bool (*rm_safe_restartpoint) (void); } RmgrData; ! extern const RmgrData RmgrTable[]; /* * Exported to support xlog switching from bgwriter --- 237,254 ---- bool (*rm_safe_restartpoint) (void); } RmgrData; ! typedef void (*rmgr_hook_type) (RmgrData *); ! extern PGDLLIMPORT rmgr_hook_type rmgr_hook; ! ! extern void RmgrInitialize(void); ! extern void RmgrStartup(void); ! extern void RmgrCleanup(void); ! extern bool RmgrSafeRestartpoint(XLogRecPtr checkPointRedo); ! extern void RmgrRedo(XLogRecPtr lsn, XLogRecord *rptr); ! extern void RmgrDesc(StringInfo buf, XLogRecord *rptr, char *rec); ! extern void rm_redo_error_callback(void *arg); ! extern const char *RmgrName(RmgrId rmid); ! extern bool RmgrIdIsValid(RmgrId rmid); /* * Exported to support xlog switching from bgwriter
rmgr_hook_contrib.v1.tar
Description: Unix tar archive
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers