diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c new file mode 100644 index 330d7fd..9d2298b *** a/contrib/bloom/blinsert.c --- b/contrib/bloom/blinsert.c *************** flushCachedPage(Relation index, BloomBui *** 49,55 **** GenericXLogState *state; state = GenericXLogStart(index); ! page = GenericXLogRegister(state, buffer, true); memcpy(page, buildstate->data, BLCKSZ); GenericXLogFinish(state); UnlockReleaseBuffer(buffer); --- 49,55 ---- GenericXLogState *state; state = GenericXLogStart(index); ! page = GenericXLogRegisterBuffer(state, buffer, true); memcpy(page, buildstate->data, BLCKSZ); GenericXLogFinish(state); UnlockReleaseBuffer(buffer); *************** blinsert(Relation index, Datum *values, *** 221,227 **** LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); state = GenericXLogStart(index); ! page = GenericXLogRegister(state, buffer, false); if (BloomPageAddItem(&blstate, page, itup)) { --- 221,227 ---- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); state = GenericXLogStart(index); ! page = GenericXLogRegisterBuffer(state, buffer, false); if (BloomPageAddItem(&blstate, page, itup)) { *************** blinsert(Relation index, Datum *values, *** 268,274 **** state = GenericXLogStart(index); /* get modifiable copy of metapage */ ! metaPage = GenericXLogRegister(state, metaBuffer, false); metaData = BloomPageGetMeta(metaPage); if (nStart >= metaData->nEnd) --- 268,274 ---- state = GenericXLogStart(index); /* get modifiable copy of metapage */ ! metaPage = GenericXLogRegisterBuffer(state, metaBuffer, false); metaData = BloomPageGetMeta(metaPage); if (nStart >= metaData->nEnd) *************** blinsert(Relation index, Datum *values, *** 279,285 **** buffer = ReadBuffer(index, blkno); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); ! page = GenericXLogRegister(state, buffer, false); if (BloomPageAddItem(&blstate, page, itup)) { --- 279,285 ---- buffer = ReadBuffer(index, blkno); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); ! page = GenericXLogRegisterBuffer(state, buffer, false); if (BloomPageAddItem(&blstate, page, itup)) { *************** blinsert(Relation index, Datum *values, *** 305,311 **** */ buffer = BloomNewBuffer(index); ! page = GenericXLogRegister(state, buffer, true); BloomInitPage(page, 0); if (!BloomPageAddItem(&blstate, page, itup)) --- 305,311 ---- */ buffer = BloomNewBuffer(index); ! page = GenericXLogRegisterBuffer(state, buffer, true); BloomInitPage(page, 0); if (!BloomPageAddItem(&blstate, page, itup)) diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c new file mode 100644 index 6c7dc1d..be14fd7 *** a/contrib/bloom/blutils.c --- b/contrib/bloom/blutils.c *************** BloomInitMetapage(Relation index) *** 417,423 **** /* Initialize contents of meta page */ state = GenericXLogStart(index); ! metaPage = GenericXLogRegister(state, metaBuffer, true); BloomInitPage(metaPage, BLOOM_META); metadata = BloomPageGetMeta(metaPage); --- 417,423 ---- /* Initialize contents of meta page */ state = GenericXLogStart(index); ! metaPage = GenericXLogRegisterBuffer(state, metaBuffer, true); BloomInitPage(metaPage, BLOOM_META); metadata = BloomPageGetMeta(metaPage); diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c new file mode 100644 index ee40ebb..2a1e8a5 *** a/contrib/bloom/blvacuum.c --- b/contrib/bloom/blvacuum.c *************** blbulkdelete(IndexVacuumInfo *info, Inde *** 65,71 **** LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); gxlogState = GenericXLogStart(index); ! page = GenericXLogRegister(gxlogState, buffer, false); if (BloomPageIsDeleted(page)) { --- 65,71 ---- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); gxlogState = GenericXLogStart(index); ! page = GenericXLogRegisterBuffer(gxlogState, buffer, false); if (BloomPageIsDeleted(page)) { *************** blbulkdelete(IndexVacuumInfo *info, Inde *** 145,151 **** LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); gxlogState = GenericXLogStart(index); ! page = GenericXLogRegister(gxlogState, buffer, false); metaData = BloomPageGetMeta(page); memcpy(metaData->notFullPage, notFullPage, sizeof(BlockNumber) * countPage); --- 145,151 ---- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); gxlogState = GenericXLogStart(index); ! page = GenericXLogRegisterBuffer(gxlogState, buffer, false); metaData = BloomPageGetMeta(page); memcpy(metaData->notFullPage, notFullPage, sizeof(BlockNumber) * countPage); diff --git a/doc/src/sgml/generic-wal.sgml b/doc/src/sgml/generic-wal.sgml new file mode 100644 index 2398d86..b644bfb *** a/doc/src/sgml/generic-wal.sgml --- b/doc/src/sgml/generic-wal.sgml *************** *** 31,45 **** ! page = GenericXLogRegister(state, buffer, isNew) — ! register a buffer to be modified within the current generic WAL record. This function returns a pointer to a temporary copy of the buffer's page, where modifications should be made. (Do not modify the ! buffer's contents directly.) The third argument indicates if the page ! is new; if true, this will result in a full-page image rather than a ! delta update being included in the WAL record. ! GenericXLogRegister can be repeated if the WAL-logged ! action needs to modify multiple pages. --- 31,46 ---- ! page = GenericXLogRegisterBuffer(state, buffer, flags) ! — register a buffer to be modified within the current generic WAL record. This function returns a pointer to a temporary copy of the buffer's page, where modifications should be made. (Do not modify the ! buffer's contents directly.) The third argument contains set of flags ! indicating how buffer should be logged. When ! GENERIC_XLOG_FULL_IMAGE is set then a full-page image rather ! than a delta update being included in the WAL record. There could appear ! more flags in future. GenericXLogRegisterBuffer can be ! repeated if the WAL-logged action needs to modify multiple pages. *************** *** 71,83 **** ! No direct modifications of buffers are allowed! All modifications ! must be done in copies acquired from GenericXLogRegister(). In other words, code that makes generic WAL records should never call BufferGetPage() for itself. However, it remains the caller's responsibility to pin/unpin and lock/unlock the buffers at appropriate times. Exclusive lock must be held on each target buffer ! from before GenericXLogRegister() until after GenericXLogFinish(). --- 72,84 ---- ! No direct modifications of buffers are allowed! All modifications must ! be done in copies acquired from GenericXLogRegisterBuffer(). In other words, code that makes generic WAL records should never call BufferGetPage() for itself. However, it remains the caller's responsibility to pin/unpin and lock/unlock the buffers at appropriate times. Exclusive lock must be held on each target buffer ! from before GenericXLogRegisterBuffer() until after GenericXLogFinish(). diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c new file mode 100644 index 072838a..d2fc910 *** a/src/backend/access/transam/generic_xlog.c --- b/src/backend/access/transam/generic_xlog.c *************** *** 50,56 **** typedef struct { Buffer buffer; /* registered buffer */ ! bool fullImage; /* are we taking a full image of this page? */ int deltaLen; /* space consumed in delta field */ char image[BLCKSZ]; /* copy of page image for modification */ char delta[MAX_DELTA_SIZE]; /* delta between page images */ --- 50,56 ---- typedef struct { Buffer buffer; /* registered buffer */ ! uint32 flags; /* buffer flags */ int deltaLen; /* space consumed in delta field */ char image[BLCKSZ]; /* copy of page image for modification */ char delta[MAX_DELTA_SIZE]; /* delta between page images */ *************** GenericXLogStart(Relation relation) *** 282,288 **** * If the buffer is already registered, just return its existing entry. */ Page ! GenericXLogRegister(GenericXLogState *state, Buffer buffer, bool isNew) { int block_id; --- 282,288 ---- * If the buffer is already registered, just return its existing entry. */ Page ! GenericXLogRegisterBuffer(GenericXLogState *state, Buffer buffer, uint16 flags) { int block_id; *************** GenericXLogRegister(GenericXLogState *st *** 295,301 **** { /* Empty slot, so use it (there cannot be a match later) */ page->buffer = buffer; ! page->fullImage = isNew; memcpy(page->image, BufferGetPage(buffer, NULL, NULL, BGP_NO_SNAPSHOT_TEST), BLCKSZ); --- 295,301 ---- { /* Empty slot, so use it (there cannot be a match later) */ page->buffer = buffer; ! page->flags = flags; memcpy(page->image, BufferGetPage(buffer, NULL, NULL, BGP_NO_SNAPSHOT_TEST), BLCKSZ); *************** GenericXLogFinish(GenericXLogState *stat *** 345,351 **** page = BufferGetPage(pageData->buffer, NULL, NULL, BGP_NO_SNAPSHOT_TEST); ! if (pageData->fullImage) { /* A full page image does not require anything special */ memcpy(page, pageData->image, BLCKSZ); --- 345,351 ---- page = BufferGetPage(pageData->buffer, NULL, NULL, BGP_NO_SNAPSHOT_TEST); ! if (pageData->flags & GENERIC_XLOG_FULL_IMAGE) { /* A full page image does not require anything special */ memcpy(page, pageData->image, BLCKSZ); diff --git a/src/include/access/generic_xlog.h b/src/include/access/generic_xlog.h new file mode 100644 index 01743e3..c422955 *** a/src/include/access/generic_xlog.h --- b/src/include/access/generic_xlog.h *************** *** 22,35 **** #define MAX_GENERIC_XLOG_PAGES XLR_NORMAL_MAX_BLOCK_ID /* state of generic xlog record construction */ struct GenericXLogState; typedef struct GenericXLogState GenericXLogState; /* API for construction of generic xlog records */ extern GenericXLogState *GenericXLogStart(Relation relation); ! extern Page GenericXLogRegister(GenericXLogState *state, Buffer buffer, ! bool isNew); extern XLogRecPtr GenericXLogFinish(GenericXLogState *state); extern void GenericXLogAbort(GenericXLogState *state); --- 22,38 ---- #define MAX_GENERIC_XLOG_PAGES XLR_NORMAL_MAX_BLOCK_ID + /* Flags for GenericXLogRegisterBuffer */ + #define GENERIC_XLOG_FULL_IMAGE 1 + /* state of generic xlog record construction */ struct GenericXLogState; typedef struct GenericXLogState GenericXLogState; /* API for construction of generic xlog records */ extern GenericXLogState *GenericXLogStart(Relation relation); ! extern Page GenericXLogRegisterBuffer(GenericXLogState *state, Buffer buffer, ! uint16 flags); extern XLogRecPtr GenericXLogFinish(GenericXLogState *state); extern void GenericXLogAbort(GenericXLogState *state);