generic-xlog.5.patch
application/octet-stream
Filename: generic-xlog.5.patch
Type: application/octet-stream
Part: 1
Message:
Re: WIP: Access method extendability
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/access/rmgrdesc/genericdesc.c | 0 | 0 |
| src/backend/access/rmgrdesc/Makefile | 0 | 0 |
| src/backend/access/transam/generic_xlog.c | 0 | 0 |
| src/backend/access/transam/Makefile | 0 | 0 |
| src/backend/access/transam/rmgr.c | 0 | 0 |
| src/backend/replication/logical/decode.c | 0 | 0 |
| src/bin/pg_xlogdump/.gitignore | 0 | 0 |
| src/bin/pg_xlogdump/rmgrdesc.c | 0 | 0 |
| src/include/access/generic_xlog.h | 0 | 0 |
| src/include/access/rmgrlist.h | 0 | 0 |
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
new file mode 100644
index c72a1f2..c0e38fd
*** a/src/backend/access/rmgrdesc/Makefile
--- b/src/backend/access/rmgrdesc/Makefile
*************** subdir = src/backend/access/rmgrdesc
*** 8,16 ****
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o gindesc.o gistdesc.o \
! hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o relmapdesc.o \
! replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
include $(top_srcdir)/src/backend/common.mk
--- 8,16 ----
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
! gindesc.o gistdesc.o hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o \
! relmapdesc.o replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/genericdesc.c b/src/backend/access/rmgrdesc/genericdesc.c
new file mode 100644
index ...46c430e
*** a/src/backend/access/rmgrdesc/genericdesc.c
--- b/src/backend/access/rmgrdesc/genericdesc.c
***************
*** 0 ****
--- 1,31 ----
+ /*-------------------------------------------------------------------------
+ *
+ * genericdesc.c
+ * rmgr descriptor routines for access/transam/generic_xlog.c
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/rmgrdesc/genericdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include "access/generic_xlog.h"
+ #include "lib/stringinfo.h"
+ #include "storage/relfilenode.h"
+
+ void
+ generic_desc(StringInfo buf, XLogReaderState *record)
+ {
+ return;
+ }
+
+ const char *
+ generic_identify(uint8 info)
+ {
+ return "Generic";
+ }
diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile
new file mode 100644
index 94455b2..16fbe47
*** a/src/backend/access/transam/Makefile
--- b/src/backend/access/transam/Makefile
*************** subdir = src/backend/access/transam
*** 12,19 ****
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
! OBJS = clog.o commit_ts.o multixact.o parallel.o rmgr.o slru.o subtrans.o \
! timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
xact.o xlog.o xlogarchive.o xlogfuncs.o \
xloginsert.o xlogreader.o xlogutils.o
--- 12,19 ----
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
! OBJS = clog.o commit_ts.o generic_xlog.o multixact.o parallel.o rmgr.o slru.o \
! subtrans.o timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
xact.o xlog.o xlogarchive.o xlogfuncs.o \
xloginsert.o xlogreader.o xlogutils.o
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
new file mode 100644
index ...3b5b0dd
*** a/src/backend/access/transam/generic_xlog.c
--- b/src/backend/access/transam/generic_xlog.c
***************
*** 0 ****
--- 1,624 ----
+ /*-------------------------------------------------------------------------
+ *
+ * generic_xlog.c
+ * WAL replay logic for generic XLOG.
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/access/transam/generic_xlog.c
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include "access/generic_xlog.h"
+ #include "access/xlogutils.h"
+ #include "miscadmin.h"
+ #include "utils/memutils.h"
+
+ #define MAX_REGIONS 256
+ #define MATCH_THRESHOLD 16
+ #define MAX_GENERIC_XLOG_PAGES 3
+
+ #define MOVE_FLAG 0x8000
+ #define LENGTH_MASK 0x7FFF
+
+ /* #define DEBUG_PRINT */
+
+ typedef struct
+ {
+ OffsetNumber dstOffset, srcOffset, length;
+ } Region;
+
+ typedef struct
+ {
+ Buffer buffer;
+ char image[BLCKSZ];
+ Region regions[MAX_REGIONS];
+ int regionsCount;
+ bool overflow;
+ char data[2 * BLCKSZ];
+ int dataLen;
+ bool fullImage;
+ } PageData;
+
+ enum GenericXlogStatus
+ {
+ GXLOG_NOT_STARTED,
+ GXLOG_LOGGED,
+ GXLOG_UNLOGGED
+ };
+
+
+ static enum GenericXlogStatus genericXlogStatus = GXLOG_NOT_STARTED;
+ static PageData pages[MAX_GENERIC_XLOG_PAGES];
+
+ static int
+ regionOffsetCmp(const void *a, const void *b)
+ {
+ OffsetNumber off = *((const OffsetNumber *)a);
+ const Region *r = (const Region *)b;
+
+ if (off < r->dstOffset)
+ return -1;
+ else if (off >= r->dstOffset + r->length)
+ return 1;
+ else
+ return 0;
+ }
+
+ static void
+ initPageData(PageData *pageData, Buffer buffer)
+ {
+ pageData->buffer = buffer;
+ memcpy(pageData->image, BufferGetPage(buffer), BLCKSZ);
+ pageData->regions[0].srcOffset = 0;
+ pageData->regions[0].dstOffset = 0;
+ pageData->regions[0].length = BLCKSZ;
+ pageData->regionsCount = 1;
+ pageData->dataLen = 0;
+ }
+
+ static Region *
+ findRegion(PageData *pageData, OffsetNumber offset)
+ {
+ Region *region;
+ Assert(offset >= 0 && offset < BLCKSZ);
+ region = bsearch(&offset, pageData->regions, pageData->regionsCount,
+ sizeof(Region), regionOffsetCmp);
+
+ /* We should always find region... */
+ if (!region)
+ elog(ERROR, "Can't find region");
+
+ return region;
+ }
+
+ static void
+ memoryMove(PageData *pageData, OffsetNumber dstOffset, OffsetNumber srcOffset,
+ OffsetNumber length)
+ {
+ OffsetNumber curOffset, curLength, leftLength = 0;
+ Region newRegions[MAX_REGIONS], *newRegion, *srcRegion, *dstRegionLeft, *dstRegionRight;
+ bool leftAdjacent, rightAdjacent;
+ int newRegionsCount, leftIndex, rightIndex, shift;
+ #ifdef DEBUG_PRINT
+ int i;
+ #endif
+
+ if (pageData->overflow)
+ return;
+
+ srcRegion = findRegion(pageData, srcOffset);
+
+ /* Make new regions */
+ curOffset = dstOffset;
+ curLength = length;
+ newRegion = newRegions;
+ while (curLength > 0)
+ {
+ OffsetNumber shift = srcOffset - srcRegion->dstOffset;
+
+ newRegion->dstOffset = curOffset;
+ newRegion->srcOffset = srcRegion->srcOffset + shift;
+ newRegion->length = Min(srcRegion->length - shift, curLength);
+ srcOffset += newRegion->length;
+ curLength -= newRegion->length;
+ srcRegion++;
+ newRegion++;
+ }
+ newRegionsCount = newRegion - newRegions;
+
+ /* Check left region */
+ dstRegionLeft = findRegion(pageData, dstOffset > 0 ? dstOffset - 1 : 0);
+ newRegion = &newRegions[0];
+ shift = dstOffset - dstRegionLeft->dstOffset;
+
+ if (shift == 0)
+ {
+ leftAdjacent = true;
+ }
+ else if (newRegion->srcOffset == dstRegionLeft->srcOffset + shift)
+ {
+ leftAdjacent = true;
+ newRegion->dstOffset -= shift;
+ newRegion->srcOffset -= shift;
+ newRegion->length += shift;
+ }
+ else
+ {
+ leftAdjacent = false;
+ leftLength = shift;
+ }
+ leftIndex = dstRegionLeft - pageData->regions + (leftAdjacent ? 0 : 1);
+
+ /* Check right region */
+ dstRegionRight = findRegion(pageData, dstOffset + length < BLCKSZ ? dstOffset + length : BLCKSZ - 1);
+ newRegion = &newRegions[newRegionsCount - 1];
+ shift = (dstRegionRight->dstOffset + dstRegionRight->length) -
+ (dstOffset + length);
+
+ if (shift == 0)
+ {
+ rightAdjacent = true;
+ }
+ else if (newRegion->srcOffset + newRegion->length + shift ==
+ dstRegionRight->srcOffset + dstRegionRight->length)
+ {
+ rightAdjacent = true;
+ newRegion->length += shift;
+ }
+ else
+ {
+ Region *tmp = dstRegionRight;
+
+ if (!leftAdjacent)
+ {
+ newRegionsCount++;
+ tmp = &newRegions[newRegionsCount - 1];
+ *tmp = *dstRegionRight;
+ rightAdjacent = true;
+ }
+ else
+ {
+ rightAdjacent = false;
+ }
+ tmp->srcOffset += (tmp->length - shift);
+ tmp->dstOffset += (tmp->length - shift);
+ tmp->length = shift;
+ }
+ if (!leftAdjacent)
+ dstRegionLeft->length = leftLength;
+ rightIndex = dstRegionRight - pageData->regions + (rightAdjacent ? 1 : 0);
+
+ /* Move data */
+ shift = newRegionsCount - (rightIndex - leftIndex);
+ if (pageData->regionsCount + shift > MAX_REGIONS)
+ {
+ initPageData(pageData, pageData->buffer);
+ pageData->overflow = true;
+ return;
+ }
+
+ #ifdef DEBUG_PRINT
+ for (i = 0; i < pageData->regionsCount; i++)
+ {
+ Region *region = &pageData->regions[i];
+ printf("old %d %d %d %d\n", i, region->dstOffset, region->srcOffset, region->length);
+ }
+ for (i = 0; i < newRegionsCount; i++)
+ {
+ Region *region = &newRegions[i];
+ printf("new %d %d %d %d\n", i, region->dstOffset, region->srcOffset, region->length);
+ }
+ printf("idx %d %d\n", leftIndex, rightIndex);
+ #endif
+
+ memmove(&pageData->regions[rightIndex + shift],
+ &pageData->regions[rightIndex],
+ sizeof(Region) * (pageData->regionsCount - rightIndex));
+ pageData->regionsCount += shift;
+ memcpy(&pageData->regions[leftIndex],
+ newRegions,
+ sizeof(Region) * newRegionsCount);
+ }
+
+ #ifdef DEBUG_PRINT
+ static void
+ printPageData(PageData *pageData)
+ {
+ int i;
+ for (i = 0; i < pageData->regionsCount; i++)
+ {
+ Region *region = &pageData->regions[i];
+ printf("cur %d %d %d %d\n", i, region->dstOffset, region->srcOffset, region->length);
+ }
+ }
+
+ Datum
+ check_move(PG_FUNCTION_ARGS)
+ {
+ PageData pageData;
+ initPageData(&pageData, false);
+ printPageData(&pageData);
+ memoryMove(&pageData, 0, 4096, 4096);
+ printPageData(&pageData);
+ memoryMove(&pageData, 0, 2048, 2048);
+ printPageData(&pageData);
+ memoryMove(&pageData, 0, 1024, 1024);
+ printPageData(&pageData);
+ PG_RETURN_VOID();
+ }
+ #endif
+
+ static void
+ writeCopyFlagment(PageData *pageData, OffsetNumber length, Pointer source)
+ {
+ Pointer ptr = pageData->data + pageData->dataLen;
+
+ memcpy(ptr, &length, sizeof(OffsetNumber));
+ ptr += sizeof(OffsetNumber);
+ memcpy(ptr, source, length);
+ ptr += length;
+
+ pageData->dataLen = ptr - pageData->data;
+ }
+
+ static void
+ writeMoveFlagment(PageData *pageData, OffsetNumber length, OffsetNumber source)
+ {
+ Pointer ptr = pageData->data + pageData->dataLen;
+
+ length |= MOVE_FLAG;
+ memcpy(ptr, &length, sizeof(OffsetNumber));
+ ptr += sizeof(OffsetNumber);
+ memcpy(ptr, &source, sizeof(OffsetNumber));
+ ptr += sizeof(OffsetNumber);
+
+ pageData->dataLen = ptr - pageData->data;
+ }
+
+ #define CHECK_SET \
+ if (i - match > MATCH_THRESHOLD) \
+ { \
+ if (notMatch < match) \
+ writeCopyFlagment(pageData, match - notMatch, page + notMatch); \
+ writeMoveFlagment(pageData, i - match, region->srcOffset + regionOffset - (i - match)); \
+ notMatch = i; \
+ } \
+
+ static void
+ writeDifferentialData(PageData *pageData)
+ {
+ Page page = BufferGetPage(pageData->buffer);
+ Pointer image = pageData->image;
+ Region *region = pageData->regions;
+ OffsetNumber i, regionOffset = 0;
+ OffsetNumber notMatch = 0, match = 0;
+
+ for (i = 0; i < BLCKSZ; i++)
+ {
+ if (regionOffset >= region->length)
+ {
+ CHECK_SET;
+ match = i;
+ region++;
+ regionOffset = 0;
+ }
+
+ if (page[i] != image[region->srcOffset + regionOffset])
+ {
+ CHECK_SET;
+ match = i + 1;
+ }
+
+ regionOffset++;
+ }
+ CHECK_SET;
+ if (notMatch < BLCKSZ)
+ {
+ writeCopyFlagment(pageData, BLCKSZ - notMatch, page + notMatch);
+ }
+ }
+
+ void
+ GenericXLogStart(Relation index)
+ {
+ int i;
+
+ if (genericXlogStatus != GXLOG_NOT_STARTED)
+ elog(ERROR, "GenericXLogStart: generic xlog is already started");
+
+ genericXlogStatus = RelationNeedsWAL(index) ? GXLOG_LOGGED : GXLOG_UNLOGGED;
+
+ for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ {
+ pages[i].buffer = InvalidBuffer;
+ }
+ }
+
+ Page
+ GenericXLogRegister(Buffer buffer, bool isNew)
+ {
+ int block_id;
+
+ if (genericXlogStatus == GXLOG_NOT_STARTED)
+ elog(ERROR, "GenericXLogRegister: generic xlog isn't started");
+
+ for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ {
+ if (BufferIsInvalid(pages[block_id].buffer))
+ {
+ initPageData(&pages[block_id], buffer);
+ pages[block_id].fullImage = isNew;
+ return (Page)pages[block_id].image;
+ }
+ else if (pages[block_id].buffer == buffer)
+ {
+ elog(ERROR, "GenericXLogRegister: duplicate buffer %d", buffer);
+ }
+ }
+
+ elog(ERROR, "GenericXLogRegister: maximum number of %d buffers is exceeded",
+ MAX_GENERIC_XLOG_PAGES);
+
+ /* keep compiler quiet */
+ return NULL;
+ }
+
+ void
+ GenericXLogMemmove(Pointer dst, Pointer src, OffsetNumber len)
+ {
+ Page page;
+ int i, block_id = -1;
+
+ if (genericXlogStatus == GXLOG_NOT_STARTED)
+ elog(ERROR, "GenericXLogMemmove: generic xlog isn't started");
+
+ /* Find block */
+ for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ {
+ page = pages[i].image;
+
+ if (dst >= page && dst < page + BLCKSZ)
+ {
+ block_id = i;
+ break;
+ }
+ }
+
+ /* Check block is found */
+ if (block_id < 0)
+ elog(ERROR, "GenericXLogMemmove: page not found");
+
+ /* Check both source and destination of memmove are inside of page */
+ if (src < page || src + len > page + BLCKSZ)
+ elog(ERROR, "GenericXLogMemmove: source is outside of page");
+ if (dst < page || dst + len > page + BLCKSZ)
+ elog(ERROR, "GenericXLogMemmove: desitnation is outside of page");
+
+ if (pages[block_id].fullImage)
+ return;
+
+ memoryMove(&pages[block_id], dst - page, src - page, len);
+ memmove(dst, src, len);
+ }
+
+ #ifdef DEBUG_PRINT
+ static void
+ printDataBlock(Pointer ptr, Pointer end)
+ {
+ OffsetNumber length, offset = 0, source;
+
+ while (ptr < end)
+ {
+ memcpy(&length, ptr, sizeof(length));
+ ptr += sizeof(length);
+
+ if (length & MOVE_FLAG)
+ {
+ length &= LENGTH_MASK;
+ memcpy(&source, ptr, sizeof(source));
+ ptr += sizeof(source);
+ printf("\t%d: move %d from %d\n", offset, length, source);
+ }
+ else
+ {
+ OffsetNumber i;
+ printf("\t%d: set %d -", offset, length);
+ for (i = 0; i < length; i++)
+ {
+ printf(" %02X", (uint8)ptr[i]);
+ }
+ printf("\n");
+ ptr += length;
+ }
+ offset += length;
+ }
+ }
+ #endif
+
+ XLogRecPtr
+ GenericXLogFinish(void)
+ {
+ XLogRecPtr lsn = InvalidXLogRecPtr;
+ int i;
+
+ if (genericXlogStatus == GXLOG_LOGGED)
+ {
+ START_CRIT_SECTION();
+ XLogBeginInsert();
+
+ for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ {
+ char tmp[BLCKSZ];
+
+ if (BufferIsInvalid(pages[i].buffer))
+ continue;
+
+ memcpy(tmp, pages[i].image, BLCKSZ);
+ memcpy(pages[i].image, BufferGetPage(pages[i].buffer), BLCKSZ);
+ memcpy(BufferGetPage(pages[i].buffer), tmp, BLCKSZ);
+
+ if (pages[i].fullImage)
+ {
+ XLogRegisterBuffer(i, pages[i].buffer, REGBUF_FORCE_IMAGE);
+ }
+ else
+ {
+ XLogRegisterBuffer(i, pages[i].buffer, REGBUF_STANDARD);
+ writeDifferentialData(&pages[i]);
+ XLogRegisterBufData(i, pages[i].data, pages[i].dataLen);
+ }
+ }
+
+ lsn = XLogInsert(RM_GENERIC_ID, 0);
+ for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ {
+ if (BufferIsInvalid(pages[i].buffer))
+ continue;
+ PageSetLSN(BufferGetPage(pages[i].buffer), lsn);
+ MarkBufferDirty(pages[i].buffer);
+ }
+ END_CRIT_SECTION();
+ }
+ else if (genericXlogStatus == GXLOG_UNLOGGED)
+ {
+ START_CRIT_SECTION();
+ for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ {
+ if (BufferIsInvalid(pages[i].buffer))
+ continue;
+ memcpy(BufferGetPage(pages[i].buffer), pages[i].image, BLCKSZ);
+ MarkBufferDirty(pages[i].buffer);
+ }
+ END_CRIT_SECTION();
+ }
+ else
+ {
+ elog(ERROR, "GenericXLogFinish: generic xlog isn't started");
+ }
+
+ #ifdef DEBUG_PRINT
+ for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ {
+ if (BufferIsInvalid(pages[i].buffer))
+ continue;
+ printf("Block %08X\n", BufferGetBlockNumber(pages[i].buffer));
+ if (pages[i].fullImage)
+ printf("full image is forced");
+ else
+ printDataBlock(pages[i].data, pages[i].data + pages[i].dataLen);
+ }
+ #endif
+
+ genericXlogStatus = GXLOG_NOT_STARTED;
+
+ return lsn;
+ }
+
+ void
+ GenericXLogAbort(void)
+ {
+ if (genericXlogStatus == GXLOG_NOT_STARTED)
+ elog(ERROR, "GenericXLogMemmove: generic xlog isn't started");
+
+ genericXlogStatus = GXLOG_NOT_STARTED;
+ }
+
+ static void
+ applyPageRedo(Page page, Page image, Pointer data, Size dataSize)
+ {
+ OffsetNumber length, offset = 0, source;
+ Pointer ptr = data, end = data + dataSize;
+
+ while (ptr < end)
+ {
+ memcpy(&length, ptr, sizeof(length));
+ ptr += sizeof(length);
+
+ if (length & MOVE_FLAG)
+ {
+ length &= LENGTH_MASK;
+ memcpy(&source, ptr, sizeof(source));
+ ptr += sizeof(source);
+
+ memcpy(page + offset, image + source, length);
+ }
+ else
+ {
+ memcpy(page + offset, ptr, length);
+
+ ptr += length;
+ }
+ offset += length;
+ }
+ }
+
+ void
+ generic_redo(XLogReaderState *record)
+ {
+ uint8 block_id;
+ Buffer buffers[MAX_GENERIC_XLOG_PAGES] = {InvalidBuffer};
+ XLogRecPtr lsn = record->EndRecPtr;
+ char image[BLCKSZ];
+
+ Assert(record->max_block_id < MAX_GENERIC_XLOG_PAGES);
+
+ #ifdef DEBUG_PRINT
+ elog(LOG, "Generic redo: %d (pid = %d)!", record->max_block_id, MyProcPid);
+ #endif
+
+ for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ {
+ XLogRedoAction action;
+
+ if (!XLogRecHasBlockRef(record, block_id))
+ continue;
+ action = XLogReadBufferForRedo(record, block_id, &buffers[block_id]);
+ #ifdef DEBUG_PRINT
+ elog(LOG, "%d - %d", block_id, action);
+ #endif
+ if (action == BLK_NEEDS_REDO)
+ {
+ Pointer blockData;
+ Size blockDataSize;
+ Page page;
+
+ page = BufferGetPage(buffers[block_id]);
+ memcpy(image, page, BLCKSZ);
+
+ blockData = XLogRecGetBlockData(record, block_id, &blockDataSize);
+
+ #ifdef DEBUG_PRINT
+ printDataBlock(blockData, blockData + blockDataSize);
+ #endif
+
+ applyPageRedo(page, image, blockData, blockDataSize);
+
+ PageSetLSN(page, lsn);
+ MarkBufferDirty(buffers[block_id]);
+ }
+ #ifdef DEBUG_PRINT
+ if (action == BLK_RESTORED)
+ {
+ int i;
+ RestoreBlockImage(record, block_id, image);
+ printf("Restored, image: ");
+ for (i = 0; i < BLCKSZ; i++)
+ {
+ printf("%02X ", (uint8)image[i]);
+ }
+ printf("\n");
+ }
+ #endif
+ }
+
+ for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ {
+ if (BufferIsValid(buffers[block_id]))
+ UnlockReleaseBuffer(buffers[block_id]);
+ }
+ }
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
new file mode 100644
index 7c4d773..7b38c16
*** a/src/backend/access/transam/rmgr.c
--- b/src/backend/access/transam/rmgr.c
***************
*** 11,16 ****
--- 11,17 ----
#include "access/commit_ts.h"
#include "access/gin.h"
#include "access/gist_private.h"
+ #include "access/generic_xlog.h"
#include "access/hash.h"
#include "access/heapam_xlog.h"
#include "access/brin_xlog.h"
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
new file mode 100644
index 88c3a49..2d69dc2
*** a/src/backend/replication/logical/decode.c
--- b/src/backend/replication/logical/decode.c
*************** LogicalDecodingProcessRecord(LogicalDeco
*** 135,140 ****
--- 135,141 ----
case RM_BRIN_ID:
case RM_COMMIT_TS_ID:
case RM_REPLORIGIN_ID:
+ case RM_GENERIC_ID:
break;
case RM_NEXT_ID:
elog(ERROR, "unexpected RM_NEXT_ID rmgr_id: %u", (RmgrIds) XLogRecGetRmid(buf.record));
diff --git a/src/bin/pg_xlogdump/.gitignore b/src/bin/pg_xlogdump/.gitignore
new file mode 100644
index eebaf30..33a1acf
*** a/src/bin/pg_xlogdump/.gitignore
--- b/src/bin/pg_xlogdump/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
/clogdesc.c
/committsdesc.c
/dbasedesc.c
+ /genericdesc.c
/gindesc.c
/gistdesc.c
/hashdesc.c
diff --git a/src/bin/pg_xlogdump/rmgrdesc.c b/src/bin/pg_xlogdump/rmgrdesc.c
new file mode 100644
index f9cd395..cff7e59
*** a/src/bin/pg_xlogdump/rmgrdesc.c
--- b/src/bin/pg_xlogdump/rmgrdesc.c
***************
*** 11,16 ****
--- 11,17 ----
#include "access/brin_xlog.h"
#include "access/clog.h"
#include "access/commit_ts.h"
+ #include "access/generic_xlog.h"
#include "access/gin.h"
#include "access/gist_private.h"
#include "access/hash.h"
diff --git a/src/include/access/generic_xlog.h b/src/include/access/generic_xlog.h
new file mode 100644
index ...45c593d
*** a/src/include/access/generic_xlog.h
--- b/src/include/access/generic_xlog.h
***************
*** 0 ****
--- 1,32 ----
+ /*-------------------------------------------------------------------------
+ *
+ * generic_xlog.h
+ * POSTGRES generic XLOG definitions.
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/generic_xlog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef GENERIC_XLOG_H
+ #define GENERIC_XLOG_H
+
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "storage/bufpage.h"
+ #include "utils/rel.h"
+
+ extern void GenericXLogStart(Relation index);
+ extern Page GenericXLogRegister(Buffer buffer, bool isNew);
+ extern void GenericXLogMemmove(Pointer dst, Pointer src, OffsetNumber len);
+ extern XLogRecPtr GenericXLogFinish(void);
+ extern void GenericXLogAbort(void);
+
+ extern void generic_redo(XLogReaderState *record);
+ extern const char *generic_identify(uint8 info);
+ extern void generic_desc(StringInfo buf, XLogReaderState *record);
+
+ #endif /* GENERIC_XLOG_H */
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
new file mode 100644
index fab912d..3cfe6f7
*** a/src/include/access/rmgrlist.h
--- b/src/include/access/rmgrlist.h
*************** PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo
*** 45,47 ****
--- 45,48 ----
PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL)
PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL)
PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL)
+ PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL)