On 29/03/16 17:33, Teodor Sigaev wrote:
Does that mean this patch should be closed or is there more remaining
to commit?

Petr promises to check english in comments/docs in generic-wal patch at
this week, bloom patch depends on it. Bloom patch is ready from my point
of view.


And here it is. It's not perfect but it's better (I am not native speaker either). It's same as v12, just changed comments somewhat.

--
  Petr Jelinek                  http://www.2ndQuadrant.com/
  PostgreSQL Development, 24x7 Support, Training & Services
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 ...3d035c2
*** a/src/backend/access/rmgrdesc/genericdesc.c
--- b/src/backend/access/rmgrdesc/genericdesc.c
***************
*** 0 ****
--- 1,58 ----
+ /*-------------------------------------------------------------------------
+  *
+  * 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
+  *
+  * src/backend/access/rmgrdesc/genericdesc.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "lib/stringinfo.h"
+ #include "storage/relfilenode.h"
+ 
+ /*
+  * Description of generic xlog record: write page regions which this record
+  * overrides.
+  */
+ void
+ generic_desc(StringInfo buf, XLogReaderState *record)
+ {
+       Pointer         ptr = XLogRecGetData(record),
+                               end = ptr + XLogRecGetDataLen(record);
+ 
+       while (ptr < end)
+       {
+               OffsetNumber    offset,
+                                               length;
+ 
+               memcpy(&offset, ptr, sizeof(offset));
+               ptr += sizeof(offset);
+               memcpy(&length, ptr, sizeof(length));
+               ptr += sizeof(length);
+               ptr += length;
+ 
+               if (ptr < end)
+                       appendStringInfo(buf, "offset %u, length %u; ", offset, 
length);
+               else
+                       appendStringInfo(buf, "offset %u, length %u", offset, 
length);
+       }
+ 
+       return;
+ }
+ 
+ /*
+  * Identification of generic xlog record: we don't distinguish any subtypes
+  * inside generic xlog records.
+  */
+ 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 ...7ca03bf
*** a/src/backend/access/transam/generic_xlog.c
--- b/src/backend/access/transam/generic_xlog.c
***************
*** 0 ****
--- 1,510 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.c
+  *     Implementation of generic xlog records.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * 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"
+ 
+ /*-------------------------------------------------------------------------
+  * API for construction of generic xlog records
+  *
+  * This API allows user to construct generic xlog records which are
+  * describing difference between pages in a general way.  Thus it's useful
+  * for extensions which provide custom access methods because they can't
+  * register their own WAL redo routines.
+  *
+  * Generic xlog record should be constructed in following steps.
+  * 1) GenericXLogStart(relation) - start construction of generic xlog
+  *      record for given relation.
+  * 2) GenericXLogRegister(buffer, isNew) - register one or more buffers
+  *      for generic xlog record.  This function returns a copy of the page
+  *      image where modifications can be performed.  The second argument
+  *      indicates if block is new and full image should be taken.
+  * 3) Do modification of page images obtained in previous step.
+  * 4) GenericXLogFinish() - finish construction of generic xlog record.
+  *
+  * Please, note the following points when constructing generic xlog records.
+  * - No direct modifications of page images are allowed! All modifications
+  *     must be done in copies returned by GenericXLogRegister().  In other
+  *     words the code which makes generic xlog records should never call
+  *     BufferGetPage() function.
+  * - The xlog record construction can be canceled at any step by calling
+  *     GenericXLogAbort().  All changes made to page images copies will be
+  *     discarded.
+  * - Registrations of buffers (step 2) and modifications of page images
+  *     (step 3) can be mixed in any sequence.  The only restriction is that
+  *     you can only modify page image after registration of corresponding
+  *     buffer.
+  * - After registration, the buffer also can be unregistered by calling
+  *     GenericXLogUnregister(buffer).  In this case the changes made in
+  *     particular page image copy will be discarded.
+  * - Generic xlog assumes that pages are using standard layout.  I.e. all
+  *     information between pd_lower and pd_upper will be discarded.
+  * - Maximum number of buffers simultaneously registered for generic xlog
+  *     is MAX_GENERIC_XLOG_PAGES.  Error will be thrown if this limit
+  *     exceeded.
+  * - Since you modify copies of page images, GenericXLogStart() doesn't
+  *     start a critical section.  Thus, you can do memory allocation, error
+  *     throwing etc between GenericXLogStart() and GenericXLogFinish().
+  *     Actual critical section is present inside GenericXLogFinish().
+  * - GenericXLogFinish() takes care of marking buffers dirty and setting their
+  *     LSNs.  You don't need to do this explicitly.
+  * - For unlogged relations, everything works the same expect there is no
+  *     WAL record produced.  Thus, you typically don't need to do any explicit
+  *     checks for unlogged relations.
+  * - If registered buffer isn't new, generic xlog record contains delta
+  *     between old and new page images.  This delta is produced by per byte
+  *     comparison.  Current delta mechanist is not effective for data shift
+  *     inside the page.  However, it could be improved in further versions.
+  * - Generic xlog redo function will acquire exclusive locks to buffers
+  *     in the same order they were registered.  After redo of all changes
+  *     locks will be released in the same order.
+  *
+  * Internally, delta between pages consists of set of fragments.  Each
+  * fragment represents changes made in given region of page.  Fragment is
+  * described as following.
+  *
+  * - offset of page region (OffsetNumber)
+  * - length of page region (OffsetNumber)
+  * - data - the data to place into described region ('length' number of bytes)
+  *
+  * Unchanged regions of page are not represented in the dekta.  As a result
+  * delta can be more compact than full page image.  But if unchanged region
+  * of the page is less than fragment header (offset and length) the delta
+  * would be bigger than the full page image. For this reason we break fragment
+  * only if the unchanged region is bigger than MATCH_THRESHOLD.
+  *
+  * The worst case for delta size is when we didn't find any unchanged region
+  * in the page. Then size of delta would be size of page plus size of fragment
+  * header.
+  */
+ #define FRAGMENT_HEADER_SIZE  (2 * sizeof(OffsetNumber))
+ #define MATCH_THRESHOLD                       FRAGMENT_HEADER_SIZE
+ #define MAX_DELTA_SIZE                        BLCKSZ + FRAGMENT_HEADER_SIZE
+ 
+ /* Struct of generic xlog data for single page */
+ typedef struct
+ {
+       Buffer  buffer;                 /* registered buffer */
+       char    image[BLCKSZ];  /* copy of page image for modification */
+       char    data[MAX_DELTA_SIZE]; /* delta between page images */
+       int             dataLen;                /* space consumed in data field 
*/
+       bool    fullImage;              /* are we taking full image of this 
page? */
+ } PageData;
+ 
+ /* Enum of generic xlog (gxlog) status */
+ enum GenericXlogStatus
+ {
+       GXLOG_NOT_STARTED,      /* gxlog is not started */
+       GXLOG_LOGGED,           /* gxlog is started for logged relation */
+       GXLOG_UNLOGGED          /* gxlog is started for unlogged relation */
+ };
+ 
+ static enum GenericXlogStatus genericXlogStatus = GXLOG_NOT_STARTED;
+ static PageData                                       
pages[MAX_GENERIC_XLOG_PAGES];
+ 
+ 
+ static void writeFragment(PageData *pageData, OffsetNumber offset,
+                                                 OffsetNumber len, Pointer 
data);
+ static void writeDelta(PageData *pageData);
+ static void applyPageRedo(Page page, Pointer data, Size dataSize);
+ 
+ /*
+  * Write next fragment into delta.
+  */
+ static void
+ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
+                         Pointer data)
+ {
+       Pointer                 ptr = pageData->data + pageData->dataLen;
+ 
+       /* Check we have enough of space */
+       Assert(pageData->dataLen + sizeof(offset) +
+                  sizeof(length) + length <= sizeof(pageData->data));
+ 
+       /* Write fragment data */
+       memcpy(ptr, &offset, sizeof(offset));
+       ptr += sizeof(offset);
+       memcpy(ptr, &length, sizeof(length));
+       ptr += sizeof(length);
+       memcpy(ptr, data, length);
+       ptr += length;
+ 
+       pageData->dataLen = ptr - pageData->data;
+ }
+ 
+ /*
+  * Make delta for given page.
+  */
+ static void
+ writeDelta(PageData *pageData)
+ {
+       Page                    page = BufferGetPage(pageData->buffer),
+                                       image = (Page) pageData->image;
+       int                             i,
+                                       fragmentBegin = -1,
+                                       fragmentEnd = -1;
+       uint16                  pageLower = ((PageHeader) page)->pd_lower,
+                                       pageUpper = ((PageHeader) 
page)->pd_upper,
+                                       imageLower = ((PageHeader) 
image)->pd_lower,
+                                       imageUpper = ((PageHeader) 
image)->pd_upper;
+ 
+       for (i = 0; i < BLCKSZ; i++)
+       {
+               bool    match;
+ 
+               /*
+                * Check if bytes in old and new page images matches.  We don't 
care
+                * about data in unallocated area between pd_lower and 
pd_upper.  Thus
+                * we assume unallocated area to expand with unmatched bytes.  
Bytes
+                * inside unallocated area are assumed to always match.
+                */
+               if (i < pageLower)
+               {
+                       if (i < imageLower)
+                               match = (page[i] == image[i]);
+                       else
+                               match = false;
+               }
+               else if (i >= pageUpper)
+               {
+                       if (i >= imageUpper)
+                               match = (page[i] == image[i]);
+                       else
+                               match = false;
+               }
+               else
+               {
+                       match = true;
+               }
+ 
+               if (match)
+               {
+                       if (fragmentBegin >= 0)
+                       {
+                               /* Matched byte is potential of fragment. */
+                               if (fragmentEnd < 0)
+                                       fragmentEnd = i;
+ 
+                               /*
+                                * Write next fragment if sequence of matched 
bytes is longer
+                                * than MATCH_THRESHOLD.
+                                */
+                               if (i - fragmentEnd >= MATCH_THRESHOLD)
+                               {
+                                       writeFragment(pageData, fragmentBegin,
+                                                                 fragmentEnd - 
fragmentBegin,
+                                                                 page + 
fragmentBegin);
+                                       fragmentBegin = -1;
+                                       fragmentEnd = -1;
+                               }
+                       }
+               }
+               else
+               {
+                       /* On unmatched byte, start new fragment if it's not 
done yet */
+                       if (fragmentBegin < 0)
+                               fragmentBegin = i;
+                       fragmentEnd = -1;
+               }
+       }
+ 
+       if (fragmentBegin >= 0)
+               writeFragment(pageData, fragmentBegin,
+                                         BLCKSZ - fragmentBegin,
+                                         page + fragmentBegin);
+ 
+ #ifdef WAL_DEBUG
+       /*
+        * If xlog debug is enabled then check produced delta.  Result of delta
+        * application to saved image should be the same as current page state.
+        */
+       if (XLOG_DEBUG)
+       {
+               char    tmp[BLCKSZ];
+               memcpy(tmp, image, BLCKSZ);
+               applyPageRedo(tmp, pageData->data, pageData->dataLen);
+               if (memcmp(tmp, page, pageLower)
+                       || memcmp(tmp + pageUpper, page + pageUpper, BLCKSZ - 
pageUpper))
+                       elog(ERROR, "result of generic xlog apply doesn't 
match");
+       }
+ #endif
+ }
+ 
+ /*
+  * Start new generic xlog record.
+  */
+ void
+ GenericXLogStart(Relation relation)
+ {
+       int i;
+ 
+       if (genericXlogStatus != GXLOG_NOT_STARTED)
+               ereport(ERROR,
+                               
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                errmsg("GenericXLogStart: generic xlog is 
already started")));
+ 
+       genericXlogStatus = RelationNeedsWAL(relation) ? GXLOG_LOGGED : 
GXLOG_UNLOGGED;
+ 
+       for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+       {
+               pages[i].buffer = InvalidBuffer;
+       }
+ }
+ 
+ /*
+  * Register new buffer for generic xlog record.
+  */
+ Page
+ GenericXLogRegister(Buffer buffer, bool isNew)
+ {
+       int block_id;
+ 
+       if (genericXlogStatus == GXLOG_NOT_STARTED)
+               ereport(ERROR,
+                               
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                errmsg("GenericXLogRegister: generic xlog 
isn't started")));
+ 
+       /* Place new buffer to unused slot in array */
+       for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+       {
+               if (BufferIsInvalid(pages[block_id].buffer))
+               {
+                       pages[block_id].buffer = buffer;
+                       memcpy(pages[block_id].image, BufferGetPage(buffer), 
BLCKSZ);
+                       pages[block_id].dataLen = 0;
+                       pages[block_id].fullImage = isNew;
+                       return (Page)pages[block_id].image;
+               }
+               else if (pages[block_id].buffer == buffer)
+               {
+                       /* 
+                        * Buffer already registered.  Just return image which 
is already
+                        * prepared.
+                        */
+                       return (Page)pages[block_id].image;
+               }
+       }
+ 
+       ereport(ERROR,
+                       (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                        errmsg("GenericXLogRegister: maximum number of %d 
buffers is exceeded",
+                                       MAX_GENERIC_XLOG_PAGES)));
+ 
+       /* keep compiler quiet */
+       return NULL;
+ }
+ 
+ /*
+  * Unregister particular buffer for generic xlog record.
+  */
+ void
+ GenericXLogUnregister(Buffer buffer)
+ {
+       int block_id;
+ 
+       if (genericXlogStatus == GXLOG_NOT_STARTED)
+               ereport(ERROR,
+                               
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                errmsg("GenericXLogUnregister: generic xlog 
isn't started")));
+ 
+       /* Find block in array to unregister */
+       for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+       {
+               if (pages[block_id].buffer == buffer)
+               {
+                       /*
+                        * Preserve order of pages in array because it could 
matter for
+                        * concurrency.
+                        */
+                       memmove(&pages[block_id], &pages[block_id + 1],
+                                       (MAX_GENERIC_XLOG_PAGES - block_id - 1) 
* sizeof(PageData));
+                       pages[MAX_GENERIC_XLOG_PAGES - 1].buffer = 
InvalidBuffer;
+                       return;
+               }
+       }
+ 
+       ereport(ERROR,
+                       (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                        errmsg("GenericXLogUnregister: registered buffer not 
found")));
+ }
+ 
+ /*
+  * Put all changes in registered buffers to generic xlog record.
+  */
+ XLogRecPtr
+ GenericXLogFinish(void)
+ {
+       XLogRecPtr lsn = InvalidXLogRecPtr;
+       int i;
+ 
+       if (genericXlogStatus == GXLOG_LOGGED)
+       {
+               /* Logged relation: make xlog record in critical section. */
+               START_CRIT_SECTION();
+               XLogBeginInsert();
+ 
+               for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+               {
+                       char    tmp[BLCKSZ];
+ 
+                       if (BufferIsInvalid(pages[i].buffer))
+                               continue;
+ 
+                       /* Swap current and saved page image. */
+                       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)
+                       {
+                               /* Full page image doesn't require anything 
special from us */
+                               XLogRegisterBuffer(i, pages[i].buffer, 
REGBUF_FORCE_IMAGE);
+                       }
+                       else
+                       {
+                               /*
+                                * In normal node calculate delta and write use 
it as data
+                                * associated with this page.
+                                */
+                               XLogRegisterBuffer(i, pages[i].buffer, 
REGBUF_STANDARD);
+                               writeDelta(&pages[i]);
+                               XLogRegisterBufData(i, pages[i].data, 
pages[i].dataLen);
+                       }
+               }
+ 
+               /* Insert xlog record */
+               lsn = XLogInsert(RM_GENERIC_ID, 0);
+ 
+               /* Set LSN and make buffers dirty */
+               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)
+       {
+               /* Unlogged relation: skip xlog-related stuff */
+               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
+       {
+               ereport(ERROR,
+                               
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                errmsg("GenericXLogFinish: generic xlog isn't 
started")));
+       }
+ 
+       genericXlogStatus = GXLOG_NOT_STARTED;
+ 
+       return lsn;
+ }
+ 
+ /*
+  * Abort generic xlog record.
+  */
+ void
+ GenericXLogAbort(void)
+ {
+       if (genericXlogStatus == GXLOG_NOT_STARTED)
+               ereport(ERROR,
+                               
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                errmsg("GenericXLogAbort: generic xlog isn't 
started")));
+ 
+       genericXlogStatus = GXLOG_NOT_STARTED;
+ }
+ 
+ /*
+  * Apply delta to given page image.
+  */
+ static void
+ applyPageRedo(Page page, Pointer data, Size dataSize)
+ {
+       Pointer ptr = data, end = data + dataSize;
+ 
+       while (ptr < end)
+       {
+               OffsetNumber    offset,
+                                               length;
+ 
+               memcpy(&offset, ptr, sizeof(offset));
+               ptr += sizeof(offset);
+               memcpy(&length, ptr, sizeof(length));
+               ptr += sizeof(length);
+ 
+               memcpy(page + offset, ptr, length);
+ 
+               ptr += length;
+       }
+ }
+ 
+ /*
+  * Redo function for generic xlog record.
+  */
+ void
+ generic_redo(XLogReaderState *record)
+ {
+       uint8           block_id;
+       Buffer          buffers[MAX_GENERIC_XLOG_PAGES] = {InvalidBuffer};
+       XLogRecPtr      lsn = record->EndRecPtr;
+ 
+       Assert(record->max_block_id < MAX_GENERIC_XLOG_PAGES);
+ 
+       /* Interate over blocks */
+       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]);
+ 
+               /* Apply redo to given block if needed */
+               if (action == BLK_NEEDS_REDO)
+               {
+                       Pointer blockData;
+                       Size    blockDataSize;
+                       Page    page;
+ 
+                       page = BufferGetPage(buffers[block_id]);
+                       blockData = XLogRecGetBlockData(record, block_id, 
&blockDataSize);
+                       applyPageRedo(page, blockData, blockDataSize);
+ 
+                       PageSetLSN(page, lsn);
+                       MarkBufferDirty(buffers[block_id]);
+               }
+       }
+ 
+       /* Changes are done: unlock and release all buffers */
+       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 13af485..262deb2
*** a/src/backend/replication/logical/decode.c
--- b/src/backend/replication/logical/decode.c
*************** LogicalDecodingProcessRecord(LogicalDeco
*** 143,148 ****
--- 143,149 ----
                case RM_BRIN_ID:
                case RM_COMMIT_TS_ID:
                case RM_REPLORIGIN_ID:
+               case RM_GENERIC_ID:
                        /* just deal with xid, and done */
                        ReorderBufferProcessXid(ctx->reorder, 
XLogRecGetXid(record),
                                                                        
buf.origptr);
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 ...49249e0
*** a/src/include/access/generic_xlog.h
--- b/src/include/access/generic_xlog.h
***************
*** 0 ****
--- 1,36 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.h
+  *      Generic xlog API definition.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, 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"
+ 
+ #define MAX_GENERIC_XLOG_PAGES          3
+ 
+ /* API for construction of generic xlog records */
+ extern void GenericXLogStart(Relation relation);
+ extern Page GenericXLogRegister(Buffer buffer, bool isNew);
+ extern void GenericXLogUnregister(Buffer buffer);
+ extern XLogRecPtr GenericXLogFinish(void);
+ extern void GenericXLogAbort(void);
+ 
+ /* functions defined for rmgr */
+ 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)
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to