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)