Commit 2c03216d authored by Heikki Linnakangas's avatar Heikki Linnakangas

Revamp the WAL record format.

Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.

There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.

This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.

For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.

The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.

Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
parent 8dc626de
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "access/xlogreader.h" #include "access/xlogreader.h"
#include "access/xlogrecord.h" #include "access/xlogrecord.h"
#include "access/xlog_internal.h"
#include "access/transam.h" #include "access/transam.h"
#include "common/fe_memutils.h" #include "common/fe_memutils.h"
#include "getopt_long.h" #include "getopt_long.h"
...@@ -343,90 +344,117 @@ XLogDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen, ...@@ -343,90 +344,117 @@ XLogDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
* Store per-rmgr and per-record statistics for a given record. * Store per-rmgr and per-record statistics for a given record.
*/ */
static void static void
XLogDumpCountRecord(XLogDumpConfig *config, XLogDumpStats *stats, XLogRecPtr ReadRecPtr, XLogRecord *record) XLogDumpCountRecord(XLogDumpConfig *config, XLogDumpStats *stats,
XLogReaderState *record)
{ {
RmgrId rmid; RmgrId rmid;
uint8 recid; uint8 recid;
uint32 rec_len;
uint32 fpi_len;
stats->count++; stats->count++;
/* Update per-rmgr statistics */ /* Update per-rmgr statistics */
rmid = record->xl_rmid; rmid = XLogRecGetRmid(record);
rec_len = XLogRecGetDataLen(record) + SizeOfXLogRecord;
fpi_len = record->decoded_record->xl_tot_len - rec_len;
stats->rmgr_stats[rmid].count++; stats->rmgr_stats[rmid].count++;
stats->rmgr_stats[rmid].rec_len += stats->rmgr_stats[rmid].rec_len += rec_len;
record->xl_len + SizeOfXLogRecord; stats->rmgr_stats[rmid].fpi_len += fpi_len;
stats->rmgr_stats[rmid].fpi_len +=
record->xl_tot_len - (record->xl_len + SizeOfXLogRecord);
/* /*
* Update per-record statistics, where the record is identified by a * Update per-record statistics, where the record is identified by a
* combination of the RmgrId and the four bits of the xl_info field * combination of the RmgrId and the four bits of the xl_info field that
* that are the rmgr's domain (resulting in sixteen possible entries * are the rmgr's domain (resulting in sixteen possible entries per
* per RmgrId). * RmgrId).
*/ */
recid = record->xl_info >> 4; recid = XLogRecGetInfo(record) >> 4;
stats->record_stats[rmid][recid].count++; stats->record_stats[rmid][recid].count++;
stats->record_stats[rmid][recid].rec_len += stats->record_stats[rmid][recid].rec_len += rec_len;
record->xl_len + SizeOfXLogRecord; stats->record_stats[rmid][recid].fpi_len += fpi_len;
stats->record_stats[rmid][recid].fpi_len +=
record->xl_tot_len - (record->xl_len + SizeOfXLogRecord);
} }
/* /*
* Print a record to stdout * Print a record to stdout
*/ */
static void static void
XLogDumpDisplayRecord(XLogDumpConfig *config, XLogRecPtr ReadRecPtr, XLogRecord *record) XLogDumpDisplayRecord(XLogDumpConfig *config, XLogReaderState *record)
{ {
const char *id; const char *id;
const RmgrDescData *desc = &RmgrDescTable[record->xl_rmid]; const RmgrDescData *desc = &RmgrDescTable[XLogRecGetRmid(record)];
RelFileNode rnode;
id = desc->rm_identify(record->xl_info); ForkNumber forknum;
BlockNumber blk;
int block_id;
uint8 info = XLogRecGetInfo(record);
XLogRecPtr xl_prev = XLogRecGetPrev(record);
id = desc->rm_identify(info);
if (id == NULL) if (id == NULL)
id = psprintf("UNKNOWN (%x)", record->xl_info & ~XLR_INFO_MASK); id = psprintf("UNKNOWN (%x)", info & ~XLR_INFO_MASK);
printf("rmgr: %-11s len (rec/tot): %6u/%6u, tx: %10u, lsn: %X/%08X, prev %X/%08X, bkp: %u%u%u%u, desc: %s ", printf("rmgr: %-11s len (rec/tot): %6u/%6u, tx: %10u, lsn: %X/%08X, prev %X/%08X, ",
desc->rm_name, desc->rm_name,
record->xl_len, record->xl_tot_len, XLogRecGetDataLen(record), XLogRecGetTotalLen(record),
record->xl_xid, XLogRecGetXid(record),
(uint32) (ReadRecPtr >> 32), (uint32) ReadRecPtr, (uint32) (record->ReadRecPtr >> 32), (uint32) record->ReadRecPtr,
(uint32) (record->xl_prev >> 32), (uint32) record->xl_prev, (uint32) (xl_prev >> 32), (uint32) xl_prev);
!!(XLR_BKP_BLOCK(0) & record->xl_info), printf("desc: %s ", id);
!!(XLR_BKP_BLOCK(1) & record->xl_info),
!!(XLR_BKP_BLOCK(2) & record->xl_info),
!!(XLR_BKP_BLOCK(3) & record->xl_info),
id);
/* the desc routine will printf the description directly to stdout */ /* the desc routine will printf the description directly to stdout */
desc->rm_desc(NULL, record); desc->rm_desc(NULL, record);
putchar('\n'); if (!config->bkp_details)
if (config->bkp_details)
{ {
int bkpnum; /* print block references (short format) */
char *blk = (char *) XLogRecGetData(record) + record->xl_len; for (block_id = 0; block_id <= record->max_block_id; block_id++)
for (bkpnum = 0; bkpnum < XLR_MAX_BKP_BLOCKS; bkpnum++)
{ {
BkpBlock bkpb; if (!XLogRecHasBlockRef(record, block_id))
if (!(XLR_BKP_BLOCK(bkpnum) & record->xl_info))
continue; continue;
memcpy(&bkpb, blk, sizeof(BkpBlock)); XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blk);
blk += sizeof(BkpBlock); if (forknum != MAIN_FORKNUM)
blk += BLCKSZ - bkpb.hole_length; printf(", blkref #%u: rel %u/%u/%u fork %s blk %u",
block_id,
rnode.spcNode, rnode.dbNode, rnode.relNode,
forkNames[forknum],
blk);
else
printf(", blkref #%u: rel %u/%u/%u blk %u",
block_id,
rnode.spcNode, rnode.dbNode, rnode.relNode,
blk);
if (XLogRecHasBlockImage(record, block_id))
printf(" FPW");
}
putchar('\n');
}
else
{
/* print block references (detailed format) */
putchar('\n');
for (block_id = 0; block_id <= record->max_block_id; block_id++)
{
if (!XLogRecHasBlockRef(record, block_id))
continue;
printf("\tbackup bkp #%u; rel %u/%u/%u; fork: %s; block: %u; hole: offset: %u, length: %u\n", XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blk);
bkpnum, printf("\tblkref #%u: rel %u/%u/%u fork %s blk %u",
bkpb.node.spcNode, bkpb.node.dbNode, bkpb.node.relNode, block_id,
forkNames[bkpb.fork], rnode.spcNode, rnode.dbNode, rnode.relNode,
bkpb.block, bkpb.hole_offset, bkpb.hole_length); forkNames[forknum],
blk);
if (XLogRecHasBlockImage(record, block_id))
{
printf(" (FPW); hole: offset: %u, length: %u\n",
record->blocks[block_id].hole_offset,
record->blocks[block_id].hole_length);
}
putchar('\n');
} }
} }
} }
...@@ -924,9 +952,9 @@ main(int argc, char **argv) ...@@ -924,9 +952,9 @@ main(int argc, char **argv)
/* process the record */ /* process the record */
if (config.stats == true) if (config.stats == true)
XLogDumpCountRecord(&config, &stats, xlogreader_state->ReadRecPtr, record); XLogDumpCountRecord(&config, &stats, xlogreader_state);
else else
XLogDumpDisplayRecord(&config, xlogreader_state->ReadRecPtr, record); XLogDumpDisplayRecord(&config, xlogreader_state);
/* check whether we printed enough */ /* check whether we printed enough */
config.already_displayed_records++; config.already_displayed_records++;
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
typedef struct RmgrDescData typedef struct RmgrDescData
{ {
const char *rm_name; const char *rm_name;
void (*rm_desc) (StringInfo buf, XLogRecord *record); void (*rm_desc) (StringInfo buf, XLogReaderState *record);
const char *(*rm_identify) (uint8 info); const char *(*rm_identify) (uint8 info);
} RmgrDescData; } RmgrDescData;
......
...@@ -666,19 +666,16 @@ brinbuild(PG_FUNCTION_ARGS) ...@@ -666,19 +666,16 @@ brinbuild(PG_FUNCTION_ARGS)
{ {
xl_brin_createidx xlrec; xl_brin_createidx xlrec;
XLogRecPtr recptr; XLogRecPtr recptr;
XLogRecData rdata;
Page page; Page page;
xlrec.node = index->rd_node;
xlrec.version = BRIN_CURRENT_VERSION; xlrec.version = BRIN_CURRENT_VERSION;
xlrec.pagesPerRange = BrinGetPagesPerRange(index); xlrec.pagesPerRange = BrinGetPagesPerRange(index);
rdata.buffer = InvalidBuffer; XLogBeginInsert();
rdata.data = (char *) &xlrec; XLogRegisterData((char *) &xlrec, SizeOfBrinCreateIdx);
rdata.len = SizeOfBrinCreateIdx; XLogRegisterBuffer(0, meta, REGBUF_WILL_INIT);
rdata.next = NULL;
recptr = XLogInsert(RM_BRIN_ID, XLOG_BRIN_CREATE_INDEX, &rdata); recptr = XLogInsert(RM_BRIN_ID, XLOG_BRIN_CREATE_INDEX);
page = BufferGetPage(meta); page = BufferGetPage(meta);
PageSetLSN(page, recptr); PageSetLSN(page, recptr);
......
...@@ -140,27 +140,19 @@ brin_doupdate(Relation idxrel, BlockNumber pagesPerRange, ...@@ -140,27 +140,19 @@ brin_doupdate(Relation idxrel, BlockNumber pagesPerRange,
/* XLOG stuff */ /* XLOG stuff */
if (RelationNeedsWAL(idxrel)) if (RelationNeedsWAL(idxrel))
{ {
BlockNumber blk = BufferGetBlockNumber(oldbuf);
xl_brin_samepage_update xlrec; xl_brin_samepage_update xlrec;
XLogRecPtr recptr; XLogRecPtr recptr;
XLogRecData rdata[2];
uint8 info = XLOG_BRIN_SAMEPAGE_UPDATE; uint8 info = XLOG_BRIN_SAMEPAGE_UPDATE;
xlrec.node = idxrel->rd_node; xlrec.offnum = oldoff;
ItemPointerSetBlockNumber(&xlrec.tid, blk);
ItemPointerSetOffsetNumber(&xlrec.tid, oldoff);
rdata[0].data = (char *) &xlrec;
rdata[0].len = SizeOfBrinSamepageUpdate;
rdata[0].buffer = InvalidBuffer;
rdata[0].next = &(rdata[1]);
rdata[1].data = (char *) newtup; XLogBeginInsert();
rdata[1].len = newsz; XLogRegisterData((char *) &xlrec, SizeOfBrinSamepageUpdate);
rdata[1].buffer = oldbuf;
rdata[1].buffer_std = true;
rdata[1].next = NULL;
recptr = XLogInsert(RM_BRIN_ID, info, rdata); XLogRegisterBuffer(0, oldbuf, REGBUF_STANDARD);
XLogRegisterBufData(0, (char *) newtup, newsz);
recptr = XLogInsert(RM_BRIN_ID, info);
PageSetLSN(oldpage, recptr); PageSetLSN(oldpage, recptr);
} }
...@@ -211,43 +203,30 @@ brin_doupdate(Relation idxrel, BlockNumber pagesPerRange, ...@@ -211,43 +203,30 @@ brin_doupdate(Relation idxrel, BlockNumber pagesPerRange,
{ {
xl_brin_update xlrec; xl_brin_update xlrec;
XLogRecPtr recptr; XLogRecPtr recptr;
XLogRecData rdata[4];
uint8 info; uint8 info;
info = XLOG_BRIN_UPDATE | (extended ? XLOG_BRIN_INIT_PAGE : 0); info = XLOG_BRIN_UPDATE | (extended ? XLOG_BRIN_INIT_PAGE : 0);
xlrec.insert.node = idxrel->rd_node; xlrec.insert.offnum = newoff;
ItemPointerSet(&xlrec.insert.tid, BufferGetBlockNumber(newbuf), newoff);
xlrec.insert.heapBlk = heapBlk; xlrec.insert.heapBlk = heapBlk;
xlrec.insert.tuplen = newsz;
xlrec.insert.revmapBlk = BufferGetBlockNumber(revmapbuf);
xlrec.insert.pagesPerRange = pagesPerRange; xlrec.insert.pagesPerRange = pagesPerRange;
ItemPointerSet(&xlrec.oldtid, BufferGetBlockNumber(oldbuf), oldoff); xlrec.oldOffnum = oldoff;
XLogBeginInsert();
rdata[0].data = (char *) &xlrec; /* new page */
rdata[0].len = SizeOfBrinUpdate; XLogRegisterData((char *) &xlrec, SizeOfBrinUpdate);
rdata[0].buffer = InvalidBuffer;
rdata[0].next = &(rdata[1]);
rdata[1].data = (char *) newtup; XLogRegisterBuffer(0, newbuf, REGBUF_STANDARD | (extended ? REGBUF_WILL_INIT : 0));
rdata[1].len = newsz; XLogRegisterBufData(0, (char *) newtup, newsz);
rdata[1].buffer = extended ? InvalidBuffer : newbuf;
rdata[1].buffer_std = true;
rdata[1].next = &(rdata[2]);
rdata[2].data = (char *) NULL; /* revmap page */
rdata[2].len = 0; XLogRegisterBuffer(1, revmapbuf, REGBUF_STANDARD);
rdata[2].buffer = revmapbuf;
rdata[2].buffer_std = true;
rdata[2].next = &(rdata[3]);
rdata[3].data = (char *) NULL; /* old page */
rdata[3].len = 0; XLogRegisterBuffer(2, oldbuf, REGBUF_STANDARD);
rdata[3].buffer = oldbuf;
rdata[3].buffer_std = true;
rdata[3].next = NULL;
recptr = XLogInsert(RM_BRIN_ID, info, rdata); recptr = XLogInsert(RM_BRIN_ID, info);
PageSetLSN(oldpage, recptr); PageSetLSN(oldpage, recptr);
PageSetLSN(newpage, recptr); PageSetLSN(newpage, recptr);
...@@ -354,36 +333,22 @@ brin_doinsert(Relation idxrel, BlockNumber pagesPerRange, ...@@ -354,36 +333,22 @@ brin_doinsert(Relation idxrel, BlockNumber pagesPerRange,
{ {
xl_brin_insert xlrec; xl_brin_insert xlrec;
XLogRecPtr recptr; XLogRecPtr recptr;
XLogRecData rdata[3];
uint8 info; uint8 info;
info = XLOG_BRIN_INSERT | (extended ? XLOG_BRIN_INIT_PAGE : 0); info = XLOG_BRIN_INSERT | (extended ? XLOG_BRIN_INIT_PAGE : 0);
xlrec.node = idxrel->rd_node;
xlrec.heapBlk = heapBlk; xlrec.heapBlk = heapBlk;
xlrec.pagesPerRange = pagesPerRange; xlrec.pagesPerRange = pagesPerRange;
xlrec.revmapBlk = BufferGetBlockNumber(revmapbuf); xlrec.offnum = off;
xlrec.tuplen = itemsz;
ItemPointerSet(&xlrec.tid, blk, off); XLogBeginInsert();
XLogRegisterData((char *) &xlrec, SizeOfBrinInsert);
rdata[0].data = (char *) &xlrec;
rdata[0].len = SizeOfBrinInsert; XLogRegisterBuffer(0, *buffer, REGBUF_STANDARD | (extended ? REGBUF_WILL_INIT : 0));
rdata[0].buffer = InvalidBuffer; XLogRegisterBufData(0, (char *) tup, itemsz);
rdata[0].buffer_std = false;
rdata[0].next = &(rdata[1]); XLogRegisterBuffer(1, revmapbuf, 0);
rdata[1].data = (char *) tup; recptr = XLogInsert(RM_BRIN_ID, info);
rdata[1].len = itemsz;
rdata[1].buffer = extended ? InvalidBuffer : *buffer;
rdata[1].buffer_std = true;
rdata[1].next = &(rdata[2]);
rdata[2].data = (char *) NULL;
rdata[2].len = 0;
rdata[2].buffer = revmapbuf;
rdata[2].buffer_std = false;
rdata[2].next = NULL;
recptr = XLogInsert(RM_BRIN_ID, info, rdata);
PageSetLSN(page, recptr); PageSetLSN(page, recptr);
PageSetLSN(BufferGetPage(revmapbuf), recptr); PageSetLSN(BufferGetPage(revmapbuf), recptr);
......
...@@ -477,23 +477,16 @@ revmap_physical_extend(BrinRevmap *revmap) ...@@ -477,23 +477,16 @@ revmap_physical_extend(BrinRevmap *revmap)
{ {
xl_brin_revmap_extend xlrec; xl_brin_revmap_extend xlrec;
XLogRecPtr recptr; XLogRecPtr recptr;
XLogRecData rdata[2];
xlrec.node = revmap->rm_irel->rd_node;
xlrec.targetBlk = mapBlk; xlrec.targetBlk = mapBlk;
rdata[0].data = (char *) &xlrec;
rdata[0].len = SizeOfBrinRevmapExtend; XLogBeginInsert();
rdata[0].buffer = InvalidBuffer; XLogRegisterData((char *) &xlrec, SizeOfBrinRevmapExtend);
rdata[0].buffer_std = false; XLogRegisterBuffer(0, revmap->rm_metaBuf, 0);
rdata[0].next = &(rdata[1]);
XLogRegisterBuffer(1, buf, REGBUF_WILL_INIT);
rdata[1].data = (char *) NULL;
rdata[1].len = 0; recptr = XLogInsert(RM_BRIN_ID, XLOG_BRIN_REVMAP_EXTEND);
rdata[1].buffer = revmap->rm_metaBuf;
rdata[1].buffer_std = false;
rdata[1].next = NULL;
recptr = XLogInsert(RM_BRIN_ID, XLOG_BRIN_REVMAP_EXTEND, rdata);
PageSetLSN(metapage, recptr); PageSetLSN(metapage, recptr);
PageSetLSN(page, recptr); PageSetLSN(page, recptr);
} }
......
...@@ -20,17 +20,15 @@ ...@@ -20,17 +20,15 @@
* xlog replay routines * xlog replay routines
*/ */
static void static void
brin_xlog_createidx(XLogRecPtr lsn, XLogRecord *record) brin_xlog_createidx(XLogReaderState *record)
{ {
XLogRecPtr lsn = record->EndRecPtr;
xl_brin_createidx *xlrec = (xl_brin_createidx *) XLogRecGetData(record); xl_brin_createidx *xlrec = (xl_brin_createidx *) XLogRecGetData(record);
Buffer buf; Buffer buf;
Page page; Page page;
/* Backup blocks are not used in create_index records */
Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
/* create the index' metapage */ /* create the index' metapage */
buf = XLogReadBuffer(xlrec->node, BRIN_METAPAGE_BLKNO, true); buf = XLogInitBufferForRedo(record, 0);
Assert(BufferIsValid(buf)); Assert(BufferIsValid(buf));
page = (Page) BufferGetPage(buf); page = (Page) BufferGetPage(buf);
brin_metapage_init(page, xlrec->pagesPerRange, xlrec->version); brin_metapage_init(page, xlrec->pagesPerRange, xlrec->version);
...@@ -44,51 +42,47 @@ brin_xlog_createidx(XLogRecPtr lsn, XLogRecord *record) ...@@ -44,51 +42,47 @@ brin_xlog_createidx(XLogRecPtr lsn, XLogRecord *record)
* revmap. * revmap.
*/ */
static void static void
brin_xlog_insert_update(XLogRecPtr lsn, XLogRecord *record, brin_xlog_insert_update(XLogReaderState *record,
xl_brin_insert *xlrec, BrinTuple *tuple) xl_brin_insert *xlrec)
{ {
BlockNumber blkno; XLogRecPtr lsn = record->EndRecPtr;
Buffer buffer; Buffer buffer;
Page page; Page page;
XLogRedoAction action; XLogRedoAction action;
blkno = ItemPointerGetBlockNumber(&xlrec->tid);
/* /*
* If we inserted the first and only tuple on the page, re-initialize the * If we inserted the first and only tuple on the page, re-initialize the
* page from scratch. * page from scratch.
*/ */
if (record->xl_info & XLOG_BRIN_INIT_PAGE) if (XLogRecGetInfo(record) & XLOG_BRIN_INIT_PAGE)
{ {
/* buffer = XLogInitBufferForRedo(record, 0);
* No full-page image here. Don't try to read it, because there
* might be one for the revmap buffer, below.
*/
buffer = XLogReadBuffer(xlrec->node, blkno, true);
page = BufferGetPage(buffer); page = BufferGetPage(buffer);
brin_page_init(page, BRIN_PAGETYPE_REGULAR); brin_page_init(page, BRIN_PAGETYPE_REGULAR);
action = BLK_NEEDS_REDO; action = BLK_NEEDS_REDO;
} }
else else
{ {
action = XLogReadBufferForRedo(lsn, record, 0, action = XLogReadBufferForRedo(record, 0, &buffer);
xlrec->node, blkno, &buffer);
} }
/* insert the index item into the page */ /* insert the index item into the page */
if (action == BLK_NEEDS_REDO) if (action == BLK_NEEDS_REDO)
{ {
OffsetNumber offnum; OffsetNumber offnum;
BrinTuple *tuple;
Size tuplen;
tuple = (BrinTuple *) XLogRecGetBlockData(record, 0, &tuplen);
Assert(tuple->bt_blkno == xlrec->heapBlk); Assert(tuple->bt_blkno == xlrec->heapBlk);
page = (Page) BufferGetPage(buffer); page = (Page) BufferGetPage(buffer);
offnum = ItemPointerGetOffsetNumber(&(xlrec->tid)); offnum = xlrec->offnum;
if (PageGetMaxOffsetNumber(page) + 1 < offnum) if (PageGetMaxOffsetNumber(page) + 1 < offnum)
elog(PANIC, "brin_xlog_insert_update: invalid max offset number"); elog(PANIC, "brin_xlog_insert_update: invalid max offset number");
offnum = PageAddItem(page, (Item) tuple, xlrec->tuplen, offnum, true, offnum = PageAddItem(page, (Item) tuple, tuplen, offnum, true, false);
false);
if (offnum == InvalidOffsetNumber) if (offnum == InvalidOffsetNumber)
elog(PANIC, "brin_xlog_insert_update: failed to add tuple"); elog(PANIC, "brin_xlog_insert_update: failed to add tuple");
...@@ -99,16 +93,17 @@ brin_xlog_insert_update(XLogRecPtr lsn, XLogRecord *record, ...@@ -99,16 +93,17 @@ brin_xlog_insert_update(XLogRecPtr lsn, XLogRecord *record,
UnlockReleaseBuffer(buffer); UnlockReleaseBuffer(buffer);
/* update the revmap */ /* update the revmap */
action = XLogReadBufferForRedo(lsn, record, action = XLogReadBufferForRedo(record, 1, &buffer);
record->xl_info & XLOG_BRIN_INIT_PAGE ? 0 : 1,
xlrec->node,
xlrec->revmapBlk, &buffer);
if (action == BLK_NEEDS_REDO) if (action == BLK_NEEDS_REDO)
{ {
ItemPointerData tid;
BlockNumber blkno = BufferGetBlockNumber(buffer);
ItemPointerSet(&tid, blkno, xlrec->offnum);
page = (Page) BufferGetPage(buffer); page = (Page) BufferGetPage(buffer);
brinSetHeapBlockItemptr(buffer, xlrec->pagesPerRange, xlrec->heapBlk, brinSetHeapBlockItemptr(buffer, xlrec->pagesPerRange, xlrec->heapBlk,
xlrec->tid); tid);
PageSetLSN(page, lsn); PageSetLSN(page, lsn);
MarkBufferDirty(buffer); MarkBufferDirty(buffer);
} }
...@@ -122,34 +117,26 @@ brin_xlog_insert_update(XLogRecPtr lsn, XLogRecord *record, ...@@ -122,34 +117,26 @@ brin_xlog_insert_update(XLogRecPtr lsn, XLogRecord *record,
* replay a BRIN index insertion * replay a BRIN index insertion
*/ */
static void static void
brin_xlog_insert(XLogRecPtr lsn, XLogRecord *record) brin_xlog_insert(XLogReaderState *record)
{ {
xl_brin_insert *xlrec = (xl_brin_insert *) XLogRecGetData(record); xl_brin_insert *xlrec = (xl_brin_insert *) XLogRecGetData(record);
BrinTuple *newtup;
newtup = (BrinTuple *) ((char *) xlrec + SizeOfBrinInsert); brin_xlog_insert_update(record, xlrec);
brin_xlog_insert_update(lsn, record, xlrec, newtup);
} }
/* /*
* replay a BRIN index update * replay a BRIN index update
*/ */
static void static void
brin_xlog_update(XLogRecPtr lsn, XLogRecord *record) brin_xlog_update(XLogReaderState *record)
{ {
XLogRecPtr lsn = record->EndRecPtr;
xl_brin_update *xlrec = (xl_brin_update *) XLogRecGetData(record); xl_brin_update *xlrec = (xl_brin_update *) XLogRecGetData(record);
BlockNumber blkno;
Buffer buffer; Buffer buffer;
BrinTuple *newtup;
XLogRedoAction action; XLogRedoAction action;
newtup = (BrinTuple *) ((char *) xlrec + SizeOfBrinUpdate);
/* First remove the old tuple */ /* First remove the old tuple */
blkno = ItemPointerGetBlockNumber(&(xlrec->oldtid)); action = XLogReadBufferForRedo(record, 2, &buffer);
action = XLogReadBufferForRedo(lsn, record, 2, xlrec->insert.node,
blkno, &buffer);
if (action == BLK_NEEDS_REDO) if (action == BLK_NEEDS_REDO)
{ {
Page page; Page page;
...@@ -157,7 +144,7 @@ brin_xlog_update(XLogRecPtr lsn, XLogRecord *record) ...@@ -157,7 +144,7 @@ brin_xlog_update(XLogRecPtr lsn, XLogRecord *record)
page = (Page) BufferGetPage(buffer); page = (Page) BufferGetPage(buffer);
offnum = ItemPointerGetOffsetNumber(&(xlrec->oldtid)); offnum = xlrec->oldOffnum;
if (PageGetMaxOffsetNumber(page) + 1 < offnum) if (PageGetMaxOffsetNumber(page) + 1 < offnum)
elog(PANIC, "brin_xlog_update: invalid max offset number"); elog(PANIC, "brin_xlog_update: invalid max offset number");
...@@ -168,7 +155,7 @@ brin_xlog_update(XLogRecPtr lsn, XLogRecord *record) ...@@ -168,7 +155,7 @@ brin_xlog_update(XLogRecPtr lsn, XLogRecord *record)
} }
/* Then insert the new tuple and update revmap, like in an insertion. */ /* Then insert the new tuple and update revmap, like in an insertion. */
brin_xlog_insert_update(lsn, record, &xlrec->insert, newtup); brin_xlog_insert_update(record, &xlrec->insert);
if (BufferIsValid(buffer)) if (BufferIsValid(buffer))
UnlockReleaseBuffer(buffer); UnlockReleaseBuffer(buffer);
...@@ -178,30 +165,27 @@ brin_xlog_update(XLogRecPtr lsn, XLogRecord *record) ...@@ -178,30 +165,27 @@ brin_xlog_update(XLogRecPtr lsn, XLogRecord *record)
* Update a tuple on a single page. * Update a tuple on a single page.
*/ */
static void static void
brin_xlog_samepage_update(XLogRecPtr lsn, XLogRecord *record) brin_xlog_samepage_update(XLogReaderState *record)
{ {
XLogRecPtr lsn = record->EndRecPtr;
xl_brin_samepage_update *xlrec; xl_brin_samepage_update *xlrec;
BlockNumber blkno;
Buffer buffer; Buffer buffer;
XLogRedoAction action; XLogRedoAction action;
xlrec = (xl_brin_samepage_update *) XLogRecGetData(record); xlrec = (xl_brin_samepage_update *) XLogRecGetData(record);
blkno = ItemPointerGetBlockNumber(&(xlrec->tid)); action = XLogReadBufferForRedo(record, 0, &buffer);
action = XLogReadBufferForRedo(lsn, record, 0, xlrec->node, blkno,
&buffer);
if (action == BLK_NEEDS_REDO) if (action == BLK_NEEDS_REDO)
{ {
int tuplen; Size tuplen;
BrinTuple *mmtuple; BrinTuple *mmtuple;
Page page; Page page;
OffsetNumber offnum; OffsetNumber offnum;
tuplen = record->xl_len - SizeOfBrinSamepageUpdate; mmtuple = (BrinTuple *) XLogRecGetBlockData(record, 0, &tuplen);
mmtuple = (BrinTuple *) ((char *) xlrec + SizeOfBrinSamepageUpdate);
page = (Page) BufferGetPage(buffer); page = (Page) BufferGetPage(buffer);
offnum = ItemPointerGetOffsetNumber(&(xlrec->tid)); offnum = xlrec->offnum;
if (PageGetMaxOffsetNumber(page) + 1 < offnum) if (PageGetMaxOffsetNumber(page) + 1 < offnum)
elog(PANIC, "brin_xlog_samepage_update: invalid max offset number"); elog(PANIC, "brin_xlog_samepage_update: invalid max offset number");
...@@ -223,18 +207,23 @@ brin_xlog_samepage_update(XLogRecPtr lsn, XLogRecord *record) ...@@ -223,18 +207,23 @@ brin_xlog_samepage_update(XLogRecPtr lsn, XLogRecord *record)
* Replay a revmap page extension * Replay a revmap page extension
*/ */
static void static void
brin_xlog_revmap_extend(XLogRecPtr lsn, XLogRecord *record) brin_xlog_revmap_extend(XLogReaderState *record)
{ {
XLogRecPtr lsn = record->EndRecPtr;
xl_brin_revmap_extend *xlrec; xl_brin_revmap_extend *xlrec;
Buffer metabuf; Buffer metabuf;
Buffer buf; Buffer buf;
Page page; Page page;
BlockNumber targetBlk;
XLogRedoAction action; XLogRedoAction action;
xlrec = (xl_brin_revmap_extend *) XLogRecGetData(record); xlrec = (xl_brin_revmap_extend *) XLogRecGetData(record);
XLogRecGetBlockTag(record, 1, NULL, NULL, &targetBlk);
Assert(xlrec->targetBlk == targetBlk);
/* Update the metapage */ /* Update the metapage */
action = XLogReadBufferForRedo(lsn, record, 0, xlrec->node, action = XLogReadBufferForRedo(record, 0, &metabuf);
BRIN_METAPAGE_BLKNO, &metabuf);
if (action == BLK_NEEDS_REDO) if (action == BLK_NEEDS_REDO)
{ {
Page metapg; Page metapg;
...@@ -255,7 +244,7 @@ brin_xlog_revmap_extend(XLogRecPtr lsn, XLogRecord *record) ...@@ -255,7 +244,7 @@ brin_xlog_revmap_extend(XLogRecPtr lsn, XLogRecord *record)
* image here. * image here.
*/ */
buf = XLogReadBuffer(xlrec->node, xlrec->targetBlk, true); buf = XLogInitBufferForRedo(record, 1);
page = (Page) BufferGetPage(buf); page = (Page) BufferGetPage(buf);
brin_page_init(page, BRIN_PAGETYPE_REVMAP); brin_page_init(page, BRIN_PAGETYPE_REVMAP);
...@@ -268,26 +257,26 @@ brin_xlog_revmap_extend(XLogRecPtr lsn, XLogRecord *record) ...@@ -268,26 +257,26 @@ brin_xlog_revmap_extend(XLogRecPtr lsn, XLogRecord *record)
} }
void void
brin_redo(XLogRecPtr lsn, XLogRecord *record) brin_redo(XLogReaderState *record)
{ {
uint8 info = record->xl_info & ~XLR_INFO_MASK; uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
switch (info & XLOG_BRIN_OPMASK) switch (info & XLOG_BRIN_OPMASK)
{ {
case XLOG_BRIN_CREATE_INDEX: case XLOG_BRIN_CREATE_INDEX:
brin_xlog_createidx(lsn, record); brin_xlog_createidx(record);
break; break;
case XLOG_BRIN_INSERT: case XLOG_BRIN_INSERT:
brin_xlog_insert(lsn, record); brin_xlog_insert(record);
break; break;
case XLOG_BRIN_UPDATE: case XLOG_BRIN_UPDATE:
brin_xlog_update(lsn, record); brin_xlog_update(record);
break; break;
case XLOG_BRIN_SAMEPAGE_UPDATE: case XLOG_BRIN_SAMEPAGE_UPDATE:
brin_xlog_samepage_update(lsn, record); brin_xlog_samepage_update(record);
break; break;
case XLOG_BRIN_REVMAP_EXTEND: case XLOG_BRIN_REVMAP_EXTEND:
brin_xlog_revmap_extend(lsn, record); brin_xlog_revmap_extend(record);
break; break;
default: default:
elog(PANIC, "brin_redo: unknown op code %u", info); elog(PANIC, "brin_redo: unknown op code %u", info);
......
...@@ -326,7 +326,6 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack, ...@@ -326,7 +326,6 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
Buffer childbuf, GinStatsData *buildStats) Buffer childbuf, GinStatsData *buildStats)
{ {
Page page = BufferGetPage(stack->buffer); Page page = BufferGetPage(stack->buffer);
XLogRecData *payloadrdata;
GinPlaceToPageRC rc; GinPlaceToPageRC rc;
uint16 xlflags = 0; uint16 xlflags = 0;
Page childpage = NULL; Page childpage = NULL;
...@@ -351,12 +350,36 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack, ...@@ -351,12 +350,36 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
/* /*
* Try to put the incoming tuple on the page. placeToPage will decide if * Try to put the incoming tuple on the page. placeToPage will decide if
* the page needs to be split. * the page needs to be split.
*
* WAL-logging this operation is a bit funny:
*
* We're responsible for calling XLogBeginInsert() and XLogInsert().
* XLogBeginInsert() must be called before placeToPage, because
* placeToPage can register some data to the WAL record.
*
* If placeToPage returns INSERTED, placeToPage has already called
* START_CRIT_SECTION(), and we're responsible for calling
* END_CRIT_SECTION. When it returns INSERTED, it is also responsible for
* registering any data required to replay the operation with
* XLogRegisterData(0, ...). It may only add data to block index 0; the
* main data of the WAL record is reserved for this function.
*
* If placeToPage returns SPLIT, we're wholly responsible for WAL logging.
* Splits happen infrequently, so we just make a full-page image of all
* the pages involved.
*/ */
if (RelationNeedsWAL(btree->index))
XLogBeginInsert();
rc = btree->placeToPage(btree, stack->buffer, stack, rc = btree->placeToPage(btree, stack->buffer, stack,
insertdata, updateblkno, insertdata, updateblkno,
&payloadrdata, &newlpage, &newrpage); &newlpage, &newrpage);
if (rc == UNMODIFIED) if (rc == UNMODIFIED)
{
XLogResetInsertion();
return true; return true;
}
else if (rc == INSERTED) else if (rc == INSERTED)
{ {
/* placeToPage did START_CRIT_SECTION() */ /* placeToPage did START_CRIT_SECTION() */
...@@ -372,17 +395,18 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack, ...@@ -372,17 +395,18 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
if (RelationNeedsWAL(btree->index)) if (RelationNeedsWAL(btree->index))
{ {
XLogRecPtr recptr; XLogRecPtr recptr;
XLogRecData rdata[3];
ginxlogInsert xlrec; ginxlogInsert xlrec;
BlockIdData childblknos[2]; BlockIdData childblknos[2];
xlrec.node = btree->index->rd_node; /*
xlrec.blkno = BufferGetBlockNumber(stack->buffer); * placetopage already registered stack->buffer as block 0.
*/
xlrec.flags = xlflags; xlrec.flags = xlflags;
rdata[0].buffer = InvalidBuffer; if (childbuf != InvalidBuffer)
rdata[0].data = (char *) &xlrec; XLogRegisterBuffer(1, childbuf, REGBUF_STANDARD);
rdata[0].len = sizeof(ginxlogInsert);
XLogRegisterData((char *) &xlrec, sizeof(ginxlogInsert));
/* /*
* Log information about child if this was an insertion of a * Log information about child if this was an insertion of a
...@@ -390,26 +414,13 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack, ...@@ -390,26 +414,13 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
*/ */
if (childbuf != InvalidBuffer) if (childbuf != InvalidBuffer)
{ {
rdata[0].next = &rdata[1];
BlockIdSet(&childblknos[0], BufferGetBlockNumber(childbuf)); BlockIdSet(&childblknos[0], BufferGetBlockNumber(childbuf));
BlockIdSet(&childblknos[1], GinPageGetOpaque(childpage)->rightlink); BlockIdSet(&childblknos[1], GinPageGetOpaque(childpage)->rightlink);
XLogRegisterData((char *) childblknos,
rdata[1].buffer = InvalidBuffer; sizeof(BlockIdData) * 2);
rdata[1].data = (char *) childblknos;
rdata[1].len = sizeof(BlockIdData) * 2;
rdata[1].next = &rdata[2];
rdata[2].buffer = childbuf;
rdata[2].buffer_std = false;
rdata[2].data = NULL;
rdata[2].len = 0;
rdata[2].next = payloadrdata;
} }
else
rdata[0].next = payloadrdata;
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_INSERT, rdata); recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_INSERT);
PageSetLSN(page, recptr); PageSetLSN(page, recptr);
if (childbuf != InvalidBuffer) if (childbuf != InvalidBuffer)
PageSetLSN(childpage, recptr); PageSetLSN(childpage, recptr);
...@@ -421,10 +432,9 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack, ...@@ -421,10 +432,9 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
} }
else if (rc == SPLIT) else if (rc == SPLIT)
{ {
/* Didn't fit, have to split */ /* Didn't fit, had to split */
Buffer rbuffer; Buffer rbuffer;
BlockNumber savedRightLink; BlockNumber savedRightLink;
XLogRecData rdata[2];
ginxlogSplit data; ginxlogSplit data;
Buffer lbuffer = InvalidBuffer; Buffer lbuffer = InvalidBuffer;
Page newrootpg = NULL; Page newrootpg = NULL;
...@@ -448,7 +458,6 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack, ...@@ -448,7 +458,6 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
*/ */
data.node = btree->index->rd_node; data.node = btree->index->rd_node;
data.rblkno = BufferGetBlockNumber(rbuffer);
data.flags = xlflags; data.flags = xlflags;
if (childbuf != InvalidBuffer) if (childbuf != InvalidBuffer)
{ {
...@@ -462,23 +471,6 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack, ...@@ -462,23 +471,6 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
else else
data.leftChildBlkno = data.rightChildBlkno = InvalidBlockNumber; data.leftChildBlkno = data.rightChildBlkno = InvalidBlockNumber;
rdata[0].buffer = InvalidBuffer;
rdata[0].data = (char *) &data;
rdata[0].len = sizeof(ginxlogSplit);
if (childbuf != InvalidBuffer)
{
rdata[0].next = &rdata[1];
rdata[1].buffer = childbuf;
rdata[1].buffer_std = false;
rdata[1].data = NULL;
rdata[1].len = 0;
rdata[1].next = payloadrdata;
}
else
rdata[0].next = payloadrdata;
if (stack->parent == NULL) if (stack->parent == NULL)
{ {
/* /*
...@@ -496,12 +488,7 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack, ...@@ -496,12 +488,7 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
buildStats->nEntryPages++; buildStats->nEntryPages++;
} }
/* data.rrlink = InvalidBlockNumber;
* root never has a right-link, so we borrow the rrlink field to
* store the root block number.
*/
data.rrlink = BufferGetBlockNumber(stack->buffer);
data.lblkno = BufferGetBlockNumber(lbuffer);
data.flags |= GIN_SPLIT_ROOT; data.flags |= GIN_SPLIT_ROOT;
GinPageGetOpaque(newrpage)->rightlink = InvalidBlockNumber; GinPageGetOpaque(newrpage)->rightlink = InvalidBlockNumber;
...@@ -524,7 +511,6 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack, ...@@ -524,7 +511,6 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
{ {
/* split non-root page */ /* split non-root page */
data.rrlink = savedRightLink; data.rrlink = savedRightLink;
data.lblkno = BufferGetBlockNumber(stack->buffer);
GinPageGetOpaque(newrpage)->rightlink = savedRightLink; GinPageGetOpaque(newrpage)->rightlink = savedRightLink;
GinPageGetOpaque(newlpage)->flags |= GIN_INCOMPLETE_SPLIT; GinPageGetOpaque(newlpage)->flags |= GIN_INCOMPLETE_SPLIT;
...@@ -572,7 +558,28 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack, ...@@ -572,7 +558,28 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
{ {
XLogRecPtr recptr; XLogRecPtr recptr;
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_SPLIT, rdata); /*
* We just take full page images of all the split pages. Splits
* are uncommon enough that it's not worth complicating the code
* to be more efficient.
*/
if (stack->parent == NULL)
{
XLogRegisterBuffer(0, lbuffer, REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
XLogRegisterBuffer(1, rbuffer, REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
XLogRegisterBuffer(2, stack->buffer, REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
}
else
{
XLogRegisterBuffer(0, stack->buffer, REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
XLogRegisterBuffer(1, rbuffer, REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
}
if (BufferIsValid(childbuf))
XLogRegisterBuffer(3, childbuf, 0);
XLogRegisterData((char *) &data, sizeof(ginxlogSplit));
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_SPLIT);
PageSetLSN(BufferGetPage(stack->buffer), recptr); PageSetLSN(BufferGetPage(stack->buffer), recptr);
PageSetLSN(BufferGetPage(rbuffer), recptr); PageSetLSN(BufferGetPage(rbuffer), recptr);
if (stack->parent == NULL) if (stack->parent == NULL)
......
This diff is collapsed.
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
static void entrySplitPage(GinBtree btree, Buffer origbuf, static void entrySplitPage(GinBtree btree, Buffer origbuf,
GinBtreeStack *stack, GinBtreeStack *stack,
void *insertPayload, void *insertPayload,
BlockNumber updateblkno, XLogRecData **prdata, BlockNumber updateblkno,
Page *newlpage, Page *newrpage); Page *newlpage, Page *newrpage);
/* /*
...@@ -515,33 +515,33 @@ entryPreparePage(GinBtree btree, Page page, OffsetNumber off, ...@@ -515,33 +515,33 @@ entryPreparePage(GinBtree btree, Page page, OffsetNumber off,
* On insertion to an internal node, in addition to inserting the given item, * On insertion to an internal node, in addition to inserting the given item,
* the downlink of the existing item at 'off' is updated to point to * the downlink of the existing item at 'off' is updated to point to
* 'updateblkno'. * 'updateblkno'.
*
* On INSERTED, registers the buffer as buffer ID 0, with data.
* On SPLIT, returns rdata that represents the split pages in *prdata.
*/ */
static GinPlaceToPageRC static GinPlaceToPageRC
entryPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack, entryPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
void *insertPayload, BlockNumber updateblkno, void *insertPayload, BlockNumber updateblkno,
XLogRecData **prdata, Page *newlpage, Page *newrpage) Page *newlpage, Page *newrpage)
{ {
GinBtreeEntryInsertData *insertData = insertPayload; GinBtreeEntryInsertData *insertData = insertPayload;
Page page = BufferGetPage(buf); Page page = BufferGetPage(buf);
OffsetNumber off = stack->off; OffsetNumber off = stack->off;
OffsetNumber placed; OffsetNumber placed;
int cnt = 0;
/* these must be static so they can be returned to caller */ /* this must be static so it can be returned to caller. */
static XLogRecData rdata[3];
static ginxlogInsertEntry data; static ginxlogInsertEntry data;
/* quick exit if it doesn't fit */ /* quick exit if it doesn't fit */
if (!entryIsEnoughSpace(btree, buf, off, insertData)) if (!entryIsEnoughSpace(btree, buf, off, insertData))
{ {
entrySplitPage(btree, buf, stack, insertPayload, updateblkno, entrySplitPage(btree, buf, stack, insertPayload, updateblkno,
prdata, newlpage, newrpage); newlpage, newrpage);
return SPLIT; return SPLIT;
} }
START_CRIT_SECTION(); START_CRIT_SECTION();
*prdata = rdata;
entryPreparePage(btree, page, off, insertData, updateblkno); entryPreparePage(btree, page, off, insertData, updateblkno);
placed = PageAddItem(page, placed = PageAddItem(page,
...@@ -552,21 +552,17 @@ entryPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack, ...@@ -552,21 +552,17 @@ entryPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
elog(ERROR, "failed to add item to index page in \"%s\"", elog(ERROR, "failed to add item to index page in \"%s\"",
RelationGetRelationName(btree->index)); RelationGetRelationName(btree->index));
data.isDelete = insertData->isDelete; if (RelationNeedsWAL(btree->index))
data.offset = off; {
data.isDelete = insertData->isDelete;
rdata[cnt].buffer = buf; data.offset = off;
rdata[cnt].buffer_std = true;
rdata[cnt].data = (char *) &data; XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
rdata[cnt].len = offsetof(ginxlogInsertEntry, tuple); XLogRegisterBufData(0, (char *) &data,
rdata[cnt].next = &rdata[cnt + 1]; offsetof(ginxlogInsertEntry, tuple));
cnt++; XLogRegisterBufData(0, (char *) insertData->entry,
IndexTupleSize(insertData->entry));
rdata[cnt].buffer = buf; }
rdata[cnt].buffer_std = true;
rdata[cnt].data = (char *) insertData->entry;
rdata[cnt].len = IndexTupleSize(insertData->entry);
rdata[cnt].next = NULL;
return INSERTED; return INSERTED;
} }
...@@ -581,7 +577,7 @@ static void ...@@ -581,7 +577,7 @@ static void
entrySplitPage(GinBtree btree, Buffer origbuf, entrySplitPage(GinBtree btree, Buffer origbuf,
GinBtreeStack *stack, GinBtreeStack *stack,
void *insertPayload, void *insertPayload,
BlockNumber updateblkno, XLogRecData **prdata, BlockNumber updateblkno,
Page *newlpage, Page *newrpage) Page *newlpage, Page *newrpage)
{ {
GinBtreeEntryInsertData *insertData = insertPayload; GinBtreeEntryInsertData *insertData = insertPayload;
...@@ -590,7 +586,6 @@ entrySplitPage(GinBtree btree, Buffer origbuf, ...@@ -590,7 +586,6 @@ entrySplitPage(GinBtree btree, Buffer origbuf,
maxoff, maxoff,
separator = InvalidOffsetNumber; separator = InvalidOffsetNumber;
Size totalsize = 0; Size totalsize = 0;
Size tupstoresize;
Size lsize = 0, Size lsize = 0,
size; size;
char *ptr; char *ptr;
...@@ -599,13 +594,8 @@ entrySplitPage(GinBtree btree, Buffer origbuf, ...@@ -599,13 +594,8 @@ entrySplitPage(GinBtree btree, Buffer origbuf,
Page lpage = PageGetTempPageCopy(BufferGetPage(origbuf)); Page lpage = PageGetTempPageCopy(BufferGetPage(origbuf));
Page rpage = PageGetTempPageCopy(BufferGetPage(origbuf)); Page rpage = PageGetTempPageCopy(BufferGetPage(origbuf));
Size pageSize = PageGetPageSize(lpage); Size pageSize = PageGetPageSize(lpage);
char tupstore[2 * BLCKSZ];
/* these must be static so they can be returned to caller */
static XLogRecData rdata[2];
static ginxlogSplitEntry data;
static char tupstore[2 * BLCKSZ];
*prdata = rdata;
entryPreparePage(btree, lpage, off, insertData, updateblkno); entryPreparePage(btree, lpage, off, insertData, updateblkno);
/* /*
...@@ -638,7 +628,6 @@ entrySplitPage(GinBtree btree, Buffer origbuf, ...@@ -638,7 +628,6 @@ entrySplitPage(GinBtree btree, Buffer origbuf,
ptr += size; ptr += size;
totalsize += size + sizeof(ItemIdData); totalsize += size + sizeof(ItemIdData);
} }
tupstoresize = ptr - tupstore;
/* /*
* Initialize the left and right pages, and copy all the tuples back to * Initialize the left and right pages, and copy all the tuples back to
...@@ -673,19 +662,6 @@ entrySplitPage(GinBtree btree, Buffer origbuf, ...@@ -673,19 +662,6 @@ entrySplitPage(GinBtree btree, Buffer origbuf,
ptr += MAXALIGN(IndexTupleSize(itup)); ptr += MAXALIGN(IndexTupleSize(itup));
} }
data.separator = separator;
data.nitem = maxoff;
rdata[0].buffer = InvalidBuffer;
rdata[0].data = (char *) &data;
rdata[0].len = sizeof(ginxlogSplitEntry);
rdata[0].next = &rdata[1];
rdata[1].buffer = InvalidBuffer;
rdata[1].data = tupstore;
rdata[1].len = tupstoresize;
rdata[1].next = NULL;
*newlpage = lpage; *newlpage = lpage;
*newrpage = rpage; *newrpage = rpage;
} }
......
...@@ -108,26 +108,19 @@ writeListPage(Relation index, Buffer buffer, ...@@ -108,26 +108,19 @@ writeListPage(Relation index, Buffer buffer,
if (RelationNeedsWAL(index)) if (RelationNeedsWAL(index))
{ {
XLogRecData rdata[2];
ginxlogInsertListPage data; ginxlogInsertListPage data;
XLogRecPtr recptr; XLogRecPtr recptr;
data.node = index->rd_node;
data.blkno = BufferGetBlockNumber(buffer);
data.rightlink = rightlink; data.rightlink = rightlink;
data.ntuples = ntuples; data.ntuples = ntuples;
rdata[0].buffer = InvalidBuffer; XLogBeginInsert();
rdata[0].data = (char *) &data; XLogRegisterData((char *) &data, sizeof(ginxlogInsertListPage));
rdata[0].len = sizeof(ginxlogInsertListPage);
rdata[0].next = rdata + 1;
rdata[1].buffer = InvalidBuffer; XLogRegisterBuffer(0, buffer, REGBUF_WILL_INIT);
rdata[1].data = workspace; XLogRegisterBufData(0, workspace, size);
rdata[1].len = size;
rdata[1].next = NULL;
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_INSERT_LISTPAGE, rdata); recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_INSERT_LISTPAGE);
PageSetLSN(page, recptr); PageSetLSN(page, recptr);
} }
...@@ -224,26 +217,23 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector) ...@@ -224,26 +217,23 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
Buffer metabuffer; Buffer metabuffer;
Page metapage; Page metapage;
GinMetaPageData *metadata = NULL; GinMetaPageData *metadata = NULL;
XLogRecData rdata[2];
Buffer buffer = InvalidBuffer; Buffer buffer = InvalidBuffer;
Page page = NULL; Page page = NULL;
ginxlogUpdateMeta data; ginxlogUpdateMeta data;
bool separateList = false; bool separateList = false;
bool needCleanup = false; bool needCleanup = false;
int cleanupSize; int cleanupSize;
bool needWal;
if (collector->ntuples == 0) if (collector->ntuples == 0)
return; return;
needWal = RelationNeedsWAL(index);
data.node = index->rd_node; data.node = index->rd_node;
data.ntuples = 0; data.ntuples = 0;
data.newRightlink = data.prevTail = InvalidBlockNumber; data.newRightlink = data.prevTail = InvalidBlockNumber;
rdata[0].buffer = InvalidBuffer;
rdata[0].data = (char *) &data;
rdata[0].len = sizeof(ginxlogUpdateMeta);
rdata[0].next = NULL;
metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO); metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
metapage = BufferGetPage(metabuffer); metapage = BufferGetPage(metabuffer);
...@@ -283,6 +273,9 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector) ...@@ -283,6 +273,9 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
memset(&sublist, 0, sizeof(GinMetaPageData)); memset(&sublist, 0, sizeof(GinMetaPageData));
makeSublist(index, collector->tuples, collector->ntuples, &sublist); makeSublist(index, collector->tuples, collector->ntuples, &sublist);
if (needWal)
XLogBeginInsert();
/* /*
* metapage was unlocked, see above * metapage was unlocked, see above
*/ */
...@@ -315,14 +308,6 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector) ...@@ -315,14 +308,6 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
LockBuffer(buffer, GIN_EXCLUSIVE); LockBuffer(buffer, GIN_EXCLUSIVE);
page = BufferGetPage(buffer); page = BufferGetPage(buffer);
rdata[0].next = rdata + 1;
rdata[1].buffer = buffer;
rdata[1].buffer_std = true;
rdata[1].data = NULL;
rdata[1].len = 0;
rdata[1].next = NULL;
Assert(GinPageGetOpaque(page)->rightlink == InvalidBlockNumber); Assert(GinPageGetOpaque(page)->rightlink == InvalidBlockNumber);
START_CRIT_SECTION(); START_CRIT_SECTION();
...@@ -336,6 +321,9 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector) ...@@ -336,6 +321,9 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
metadata->nPendingPages += sublist.nPendingPages; metadata->nPendingPages += sublist.nPendingPages;
metadata->nPendingHeapTuples += sublist.nPendingHeapTuples; metadata->nPendingHeapTuples += sublist.nPendingHeapTuples;
if (needWal)
XLogRegisterBuffer(1, buffer, REGBUF_STANDARD);
} }
} }
else else
...@@ -348,6 +336,7 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector) ...@@ -348,6 +336,7 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
int i, int i,
tupsize; tupsize;
char *ptr; char *ptr;
char *collectordata;
buffer = ReadBuffer(index, metadata->tail); buffer = ReadBuffer(index, metadata->tail);
LockBuffer(buffer, GIN_EXCLUSIVE); LockBuffer(buffer, GIN_EXCLUSIVE);
...@@ -356,16 +345,13 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector) ...@@ -356,16 +345,13 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
off = (PageIsEmpty(page)) ? FirstOffsetNumber : off = (PageIsEmpty(page)) ? FirstOffsetNumber :
OffsetNumberNext(PageGetMaxOffsetNumber(page)); OffsetNumberNext(PageGetMaxOffsetNumber(page));
rdata[0].next = rdata + 1; collectordata = ptr = (char *) palloc(collector->sumsize);
rdata[1].buffer = buffer;
rdata[1].buffer_std = true;
ptr = rdata[1].data = (char *) palloc(collector->sumsize);
rdata[1].len = collector->sumsize;
rdata[1].next = NULL;
data.ntuples = collector->ntuples; data.ntuples = collector->ntuples;
if (needWal)
XLogBeginInsert();
START_CRIT_SECTION(); START_CRIT_SECTION();
/* /*
...@@ -390,7 +376,12 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector) ...@@ -390,7 +376,12 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
off++; off++;
} }
Assert((ptr - rdata[1].data) <= collector->sumsize); Assert((ptr - collectordata) <= collector->sumsize);
if (needWal)
{
XLogRegisterBuffer(1, buffer, REGBUF_STANDARD);
XLogRegisterBufData(1, collectordata, collector->sumsize);
}
metadata->tailFreeSize = PageGetExactFreeSpace(page); metadata->tailFreeSize = PageGetExactFreeSpace(page);
...@@ -402,13 +393,16 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector) ...@@ -402,13 +393,16 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
*/ */
MarkBufferDirty(metabuffer); MarkBufferDirty(metabuffer);
if (RelationNeedsWAL(index)) if (needWal)
{ {
XLogRecPtr recptr; XLogRecPtr recptr;
memcpy(&data.metadata, metadata, sizeof(GinMetaPageData)); memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_UPDATE_META_PAGE, rdata); XLogRegisterBuffer(0, metabuffer, REGBUF_WILL_INIT);
XLogRegisterData((char *) &data, sizeof(ginxlogUpdateMeta));
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_UPDATE_META_PAGE);
PageSetLSN(metapage, recptr); PageSetLSN(metapage, recptr);
if (buffer != InvalidBuffer) if (buffer != InvalidBuffer)
...@@ -526,20 +520,11 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead, ...@@ -526,20 +520,11 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
int i; int i;
int64 nDeletedHeapTuples = 0; int64 nDeletedHeapTuples = 0;
ginxlogDeleteListPages data; ginxlogDeleteListPages data;
XLogRecData rdata[1];
Buffer buffers[GIN_NDELETE_AT_ONCE]; Buffer buffers[GIN_NDELETE_AT_ONCE];
data.node = index->rd_node;
rdata[0].buffer = InvalidBuffer;
rdata[0].data = (char *) &data;
rdata[0].len = sizeof(ginxlogDeleteListPages);
rdata[0].next = NULL;
data.ndeleted = 0; data.ndeleted = 0;
while (data.ndeleted < GIN_NDELETE_AT_ONCE && blknoToDelete != newHead) while (data.ndeleted < GIN_NDELETE_AT_ONCE && blknoToDelete != newHead)
{ {
data.toDelete[data.ndeleted] = blknoToDelete;
buffers[data.ndeleted] = ReadBuffer(index, blknoToDelete); buffers[data.ndeleted] = ReadBuffer(index, blknoToDelete);
LockBuffer(buffers[data.ndeleted], GIN_EXCLUSIVE); LockBuffer(buffers[data.ndeleted], GIN_EXCLUSIVE);
page = BufferGetPage(buffers[data.ndeleted]); page = BufferGetPage(buffers[data.ndeleted]);
...@@ -562,6 +547,13 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead, ...@@ -562,6 +547,13 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
if (stats) if (stats)
stats->pages_deleted += data.ndeleted; stats->pages_deleted += data.ndeleted;
/*
* This operation touches an unusually large number of pages, so
* prepare the XLogInsert machinery for that before entering the
* critical section.
*/
XLogEnsureRecordSpace(data.ndeleted, 0);
START_CRIT_SECTION(); START_CRIT_SECTION();
metadata->head = blknoToDelete; metadata->head = blknoToDelete;
...@@ -592,9 +584,17 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead, ...@@ -592,9 +584,17 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
{ {
XLogRecPtr recptr; XLogRecPtr recptr;
XLogBeginInsert();
XLogRegisterBuffer(0, metabuffer, REGBUF_WILL_INIT);
for (i = 0; i < data.ndeleted; i++)
XLogRegisterBuffer(i + 1, buffers[i], REGBUF_WILL_INIT);
memcpy(&data.metadata, metadata, sizeof(GinMetaPageData)); memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_DELETE_LISTPAGE, rdata); XLogRegisterData((char *) &data,
sizeof(ginxlogDeleteListPages));
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_DELETE_LISTPAGE);
PageSetLSN(metapage, recptr); PageSetLSN(metapage, recptr);
for (i = 0; i < data.ndeleted; i++) for (i = 0; i < data.ndeleted; i++)
......
...@@ -347,15 +347,13 @@ ginbuild(PG_FUNCTION_ARGS) ...@@ -347,15 +347,13 @@ ginbuild(PG_FUNCTION_ARGS)
if (RelationNeedsWAL(index)) if (RelationNeedsWAL(index))
{ {
XLogRecPtr recptr; XLogRecPtr recptr;
XLogRecData rdata;
Page page; Page page;
rdata.buffer = InvalidBuffer; XLogBeginInsert();
rdata.data = (char *) &(index->rd_node); XLogRegisterBuffer(0, MetaBuffer, REGBUF_WILL_INIT);
rdata.len = sizeof(RelFileNode); XLogRegisterBuffer(1, RootBuffer, REGBUF_WILL_INIT);
rdata.next = NULL;
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_INDEX, &rdata); recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_INDEX);
page = BufferGetPage(RootBuffer); page = BufferGetPage(RootBuffer);
PageSetLSN(page, recptr); PageSetLSN(page, recptr);
......
...@@ -605,19 +605,17 @@ ginUpdateStats(Relation index, const GinStatsData *stats) ...@@ -605,19 +605,17 @@ ginUpdateStats(Relation index, const GinStatsData *stats)
{ {
XLogRecPtr recptr; XLogRecPtr recptr;
ginxlogUpdateMeta data; ginxlogUpdateMeta data;
XLogRecData rdata;
data.node = index->rd_node; data.node = index->rd_node;
data.ntuples = 0; data.ntuples = 0;
data.newRightlink = data.prevTail = InvalidBlockNumber; data.newRightlink = data.prevTail = InvalidBlockNumber;
memcpy(&data.metadata, metadata, sizeof(GinMetaPageData)); memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));
rdata.buffer = InvalidBuffer; XLogBeginInsert();
rdata.data = (char *) &data; XLogRegisterData((char *) &data, sizeof(ginxlogUpdateMeta));
rdata.len = sizeof(ginxlogUpdateMeta); XLogRegisterBuffer(0, metabuffer, REGBUF_WILL_INIT);
rdata.next = NULL;
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_UPDATE_META_PAGE, &rdata); recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_UPDATE_META_PAGE);
PageSetLSN(metapage, recptr); PageSetLSN(metapage, recptr);
} }
......
...@@ -89,10 +89,6 @@ xlogVacuumPage(Relation index, Buffer buffer) ...@@ -89,10 +89,6 @@ xlogVacuumPage(Relation index, Buffer buffer)
{ {
Page page = BufferGetPage(buffer); Page page = BufferGetPage(buffer);
XLogRecPtr recptr; XLogRecPtr recptr;
XLogRecData rdata[3];
ginxlogVacuumPage xlrec;
uint16 lower;
uint16 upper;
/* This is only used for entry tree leaf pages. */ /* This is only used for entry tree leaf pages. */
Assert(!GinPageIsData(page)); Assert(!GinPageIsData(page));
...@@ -101,57 +97,14 @@ xlogVacuumPage(Relation index, Buffer buffer) ...@@ -101,57 +97,14 @@ xlogVacuumPage(Relation index, Buffer buffer)
if (!RelationNeedsWAL(index)) if (!RelationNeedsWAL(index))
return; return;
xlrec.node = index->rd_node; /*
xlrec.blkno = BufferGetBlockNumber(buffer); * Always create a full image, we don't track the changes on the page at
* any more fine-grained level. This could obviously be improved...
/* Assume we can omit data between pd_lower and pd_upper */ */
lower = ((PageHeader) page)->pd_lower; XLogBeginInsert();
upper = ((PageHeader) page)->pd_upper; XLogRegisterBuffer(0, buffer, REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
Assert(lower < BLCKSZ);
Assert(upper < BLCKSZ);
if (lower >= SizeOfPageHeaderData &&
upper > lower &&
upper <= BLCKSZ)
{
xlrec.hole_offset = lower;
xlrec.hole_length = upper - lower;
}
else
{
/* No "hole" to compress out */
xlrec.hole_offset = 0;
xlrec.hole_length = 0;
}
rdata[0].data = (char *) &xlrec;
rdata[0].len = sizeof(ginxlogVacuumPage);
rdata[0].buffer = InvalidBuffer;
rdata[0].next = &rdata[1];
if (xlrec.hole_length == 0)
{
rdata[1].data = (char *) page;
rdata[1].len = BLCKSZ;
rdata[1].buffer = InvalidBuffer;
rdata[1].next = NULL;
}
else
{
/* must skip the hole */
rdata[1].data = (char *) page;
rdata[1].len = xlrec.hole_offset;
rdata[1].buffer = InvalidBuffer;
rdata[1].next = &rdata[2];
rdata[2].data = (char *) page + (xlrec.hole_offset + xlrec.hole_length);
rdata[2].len = BLCKSZ - (xlrec.hole_offset + xlrec.hole_length);
rdata[2].buffer = InvalidBuffer;
rdata[2].next = NULL;
}
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_VACUUM_PAGE, rdata); recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_VACUUM_PAGE);
PageSetLSN(page, recptr); PageSetLSN(page, recptr);
} }
...@@ -292,48 +245,27 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkn ...@@ -292,48 +245,27 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkn
if (RelationNeedsWAL(gvs->index)) if (RelationNeedsWAL(gvs->index))
{ {
XLogRecPtr recptr; XLogRecPtr recptr;
XLogRecData rdata[4];
ginxlogDeletePage data; ginxlogDeletePage data;
data.node = gvs->index->rd_node; /*
data.blkno = deleteBlkno; * We can't pass REGBUF_STANDARD for the deleted page, because we
data.parentBlkno = parentBlkno; * didn't set pd_lower on pre-9.4 versions. The page might've been
* binary-upgraded from an older version, and hence not have pd_lower
* set correctly. Ditto for the left page, but removing the item from
* the parent updated its pd_lower, so we know that's OK at this
* point.
*/
XLogBeginInsert();
XLogRegisterBuffer(0, dBuffer, 0);
XLogRegisterBuffer(1, pBuffer, REGBUF_STANDARD);
XLogRegisterBuffer(2, lBuffer, 0);
data.parentOffset = myoff; data.parentOffset = myoff;
data.leftBlkno = leftBlkno;
data.rightLink = GinPageGetOpaque(page)->rightlink; data.rightLink = GinPageGetOpaque(page)->rightlink;
/* XLogRegisterData((char *) &data, sizeof(ginxlogDeletePage));
* We can't pass buffer_std = TRUE, because we didn't set pd_lower on
* pre-9.4 versions. The page might've been binary-upgraded from an recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_DELETE_PAGE);
* older version, and hence not have pd_lower set correctly. Ditto for
* the left page, but removing the item from the parent updated its
* pd_lower, so we know that's OK at this point.
*/
rdata[0].buffer = dBuffer;
rdata[0].buffer_std = FALSE;
rdata[0].data = NULL;
rdata[0].len = 0;
rdata[0].next = rdata + 1;
rdata[1].buffer = pBuffer;
rdata[1].buffer_std = TRUE;
rdata[1].data = NULL;
rdata[1].len = 0;
rdata[1].next = rdata + 2;
rdata[2].buffer = lBuffer;
rdata[2].buffer_std = FALSE;
rdata[2].data = NULL;
rdata[2].len = 0;
rdata[2].next = rdata + 3;
rdata[3].buffer = InvalidBuffer;
rdata[3].buffer_std = FALSE;
rdata[3].len = sizeof(ginxlogDeletePage);
rdata[3].data = (char *) &data;
rdata[3].next = NULL;
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_DELETE_PAGE, rdata);
PageSetLSN(page, recptr); PageSetLSN(page, recptr);
PageSetLSN(parentPage, recptr); PageSetLSN(parentPage, recptr);
PageSetLSN(BufferGetPage(lBuffer), recptr); PageSetLSN(BufferGetPage(lBuffer), recptr);
......
This diff is collapsed.
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "access/genam.h" #include "access/genam.h"
#include "access/gist_private.h" #include "access/gist_private.h"
#include "access/xloginsert.h"
#include "catalog/index.h" #include "catalog/index.h"
#include "catalog/pg_collation.h" #include "catalog/pg_collation.h"
#include "miscadmin.h" #include "miscadmin.h"
...@@ -394,6 +395,14 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate, ...@@ -394,6 +395,14 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
GistPageSetNSN(ptr->page, oldnsn); GistPageSetNSN(ptr->page, oldnsn);
} }
/*
* gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
* insertion for that. NB: The number of pages and data segments
* specified here must match the calculations in gistXLogSplit()!
*/
if (RelationNeedsWAL(rel))
XLogEnsureRecordSpace(npage, 1 + npage * 2);
START_CRIT_SECTION(); START_CRIT_SECTION();
/* /*
......
...@@ -183,14 +183,11 @@ gistbuild(PG_FUNCTION_ARGS) ...@@ -183,14 +183,11 @@ gistbuild(PG_FUNCTION_ARGS)
if (RelationNeedsWAL(index)) if (RelationNeedsWAL(index))
{ {
XLogRecPtr recptr; XLogRecPtr recptr;
XLogRecData rdata;
rdata.data = (char *) &(index->rd_node); XLogBeginInsert();
rdata.len = sizeof(RelFileNode); XLogRegisterBuffer(0, buffer, REGBUF_WILL_INIT);
rdata.buffer = InvalidBuffer;
rdata.next = NULL;
recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_CREATE_INDEX, &rdata); recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_CREATE_INDEX);
PageSetLSN(page, recptr); PageSetLSN(page, recptr);
} }
else else
......
This diff is collapsed.
...@@ -700,7 +700,7 @@ hashvacuumcleanup(PG_FUNCTION_ARGS) ...@@ -700,7 +700,7 @@ hashvacuumcleanup(PG_FUNCTION_ARGS)
void void
hash_redo(XLogRecPtr lsn, XLogRecord *record) hash_redo(XLogReaderState *record)
{ {
elog(PANIC, "hash_redo: unimplemented"); elog(PANIC, "hash_redo: unimplemented");
} }
This diff is collapsed.
...@@ -865,7 +865,6 @@ logical_heap_rewrite_flush_mappings(RewriteState state) ...@@ -865,7 +865,6 @@ logical_heap_rewrite_flush_mappings(RewriteState state)
hash_seq_init(&seq_status, state->rs_logical_mappings); hash_seq_init(&seq_status, state->rs_logical_mappings);
while ((src = (RewriteMappingFile *) hash_seq_search(&seq_status)) != NULL) while ((src = (RewriteMappingFile *) hash_seq_search(&seq_status)) != NULL)
{ {
XLogRecData rdata[2];
char *waldata; char *waldata;
char *waldata_start; char *waldata_start;
xl_heap_rewrite_mapping xlrec; xl_heap_rewrite_mapping xlrec;
...@@ -889,11 +888,6 @@ logical_heap_rewrite_flush_mappings(RewriteState state) ...@@ -889,11 +888,6 @@ logical_heap_rewrite_flush_mappings(RewriteState state)
xlrec.offset = src->off; xlrec.offset = src->off;
xlrec.start_lsn = state->rs_begin_lsn; xlrec.start_lsn = state->rs_begin_lsn;
rdata[0].data = (char *) (&xlrec);
rdata[0].len = sizeof(xlrec);
rdata[0].buffer = InvalidBuffer;
rdata[0].next = &(rdata[1]);
/* write all mappings consecutively */ /* write all mappings consecutively */
len = src->num_mappings * sizeof(LogicalRewriteMappingData); len = src->num_mappings * sizeof(LogicalRewriteMappingData);
waldata_start = waldata = palloc(len); waldata_start = waldata = palloc(len);
...@@ -934,13 +928,12 @@ logical_heap_rewrite_flush_mappings(RewriteState state) ...@@ -934,13 +928,12 @@ logical_heap_rewrite_flush_mappings(RewriteState state)
written, len))); written, len)));
src->off += len; src->off += len;
rdata[1].data = waldata_start; XLogBeginInsert();
rdata[1].len = len; XLogRegisterData((char *) (&xlrec), sizeof(xlrec));
rdata[1].buffer = InvalidBuffer; XLogRegisterData(waldata_start, len);
rdata[1].next = NULL;
/* write xlog record */ /* write xlog record */
XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_REWRITE, rdata); XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_REWRITE);
pfree(waldata_start); pfree(waldata_start);
} }
...@@ -1123,7 +1116,7 @@ logical_rewrite_heap_tuple(RewriteState state, ItemPointerData old_tid, ...@@ -1123,7 +1116,7 @@ logical_rewrite_heap_tuple(RewriteState state, ItemPointerData old_tid,
* Replay XLOG_HEAP2_REWRITE records * Replay XLOG_HEAP2_REWRITE records
*/ */
void void
heap_xlog_logical_rewrite(XLogRecPtr lsn, XLogRecord *r) heap_xlog_logical_rewrite(XLogReaderState *r)
{ {
char path[MAXPGPATH]; char path[MAXPGPATH];
int fd; int fd;
...@@ -1138,7 +1131,7 @@ heap_xlog_logical_rewrite(XLogRecPtr lsn, XLogRecord *r) ...@@ -1138,7 +1131,7 @@ heap_xlog_logical_rewrite(XLogRecPtr lsn, XLogRecord *r)
xlrec->mapped_db, xlrec->mapped_rel, xlrec->mapped_db, xlrec->mapped_rel,
(uint32) (xlrec->start_lsn >> 32), (uint32) (xlrec->start_lsn >> 32),
(uint32) xlrec->start_lsn, (uint32) xlrec->start_lsn,
xlrec->mapped_xid, r->xl_xid); xlrec->mapped_xid, XLogRecGetXid(r));
fd = OpenTransientFile(path, fd = OpenTransientFile(path,
O_CREAT | O_WRONLY | PG_BINARY, O_CREAT | O_WRONLY | PG_BINARY,
......
...@@ -837,37 +837,25 @@ _bt_insertonpg(Relation rel, ...@@ -837,37 +837,25 @@ _bt_insertonpg(Relation rel,
if (RelationNeedsWAL(rel)) if (RelationNeedsWAL(rel))
{ {
xl_btree_insert xlrec; xl_btree_insert xlrec;
BlockNumber xlleftchild;
xl_btree_metadata xlmeta; xl_btree_metadata xlmeta;
uint8 xlinfo; uint8 xlinfo;
XLogRecPtr recptr; XLogRecPtr recptr;
XLogRecData rdata[4];
XLogRecData *nextrdata;
IndexTupleData trunctuple; IndexTupleData trunctuple;
xlrec.target.node = rel->rd_node; xlrec.offnum = itup_off;
ItemPointerSet(&(xlrec.target.tid), itup_blkno, itup_off);
rdata[0].data = (char *) &xlrec; XLogBeginInsert();
rdata[0].len = SizeOfBtreeInsert; XLogRegisterData((char *) &xlrec, SizeOfBtreeInsert);
rdata[0].buffer = InvalidBuffer;
rdata[0].next = nextrdata = &(rdata[1]);
if (P_ISLEAF(lpageop)) if (P_ISLEAF(lpageop))
xlinfo = XLOG_BTREE_INSERT_LEAF; xlinfo = XLOG_BTREE_INSERT_LEAF;
else else
{ {
/* /*
* Include the block number of the left child, whose * Register the left child whose INCOMPLETE_SPLIT flag was
* INCOMPLETE_SPLIT flag was cleared. * cleared.
*/ */
xlleftchild = BufferGetBlockNumber(cbuf); XLogRegisterBuffer(1, cbuf, REGBUF_STANDARD);
nextrdata->data = (char *) &xlleftchild;
nextrdata->len = sizeof(BlockNumber);
nextrdata->buffer = cbuf;
nextrdata->buffer_std = true;
nextrdata->next = nextrdata + 1;
nextrdata++;
xlinfo = XLOG_BTREE_INSERT_UPPER; xlinfo = XLOG_BTREE_INSERT_UPPER;
} }
...@@ -879,33 +867,25 @@ _bt_insertonpg(Relation rel, ...@@ -879,33 +867,25 @@ _bt_insertonpg(Relation rel,
xlmeta.fastroot = metad->btm_fastroot; xlmeta.fastroot = metad->btm_fastroot;
xlmeta.fastlevel = metad->btm_fastlevel; xlmeta.fastlevel = metad->btm_fastlevel;
nextrdata->data = (char *) &xlmeta; XLogRegisterBuffer(2, metabuf, REGBUF_WILL_INIT);
nextrdata->len = sizeof(xl_btree_metadata); XLogRegisterBufData(2, (char *) &xlmeta, sizeof(xl_btree_metadata));
nextrdata->buffer = InvalidBuffer;
nextrdata->next = nextrdata + 1;
nextrdata++;
xlinfo = XLOG_BTREE_INSERT_META; xlinfo = XLOG_BTREE_INSERT_META;
} }
/* Read comments in _bt_pgaddtup */ /* Read comments in _bt_pgaddtup */
XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
if (!P_ISLEAF(lpageop) && newitemoff == P_FIRSTDATAKEY(lpageop)) if (!P_ISLEAF(lpageop) && newitemoff == P_FIRSTDATAKEY(lpageop))
{ {
trunctuple = *itup; trunctuple = *itup;
trunctuple.t_info = sizeof(IndexTupleData); trunctuple.t_info = sizeof(IndexTupleData);
nextrdata->data = (char *) &trunctuple; XLogRegisterBufData(0, (char *) &trunctuple,
nextrdata->len = sizeof(IndexTupleData); sizeof(IndexTupleData));
} }
else else
{ XLogRegisterBufData(0, (char *) itup, IndexTupleDSize(*itup));
nextrdata->data = (char *) itup;
nextrdata->len = IndexTupleDSize(*itup);
}
nextrdata->buffer = buf;
nextrdata->buffer_std = true;
nextrdata->next = NULL;
recptr = XLogInsert(RM_BTREE_ID, xlinfo, rdata); recptr = XLogInsert(RM_BTREE_ID, xlinfo);
if (BufferIsValid(metabuf)) if (BufferIsValid(metabuf))
{ {
...@@ -1260,56 +1240,37 @@ _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firstright, ...@@ -1260,56 +1240,37 @@ _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firstright,
xl_btree_split xlrec; xl_btree_split xlrec;
uint8 xlinfo; uint8 xlinfo;
XLogRecPtr recptr; XLogRecPtr recptr;
XLogRecData rdata[7];
XLogRecData *lastrdata;
BlockNumber cblkno;
xlrec.node = rel->rd_node;
xlrec.leftsib = origpagenumber;
xlrec.rightsib = rightpagenumber;
xlrec.rnext = ropaque->btpo_next;
xlrec.level = ropaque->btpo.level; xlrec.level = ropaque->btpo.level;
xlrec.firstright = firstright; xlrec.firstright = firstright;
xlrec.newitemoff = newitemoff;
rdata[0].data = (char *) &xlrec; XLogBeginInsert();
rdata[0].len = SizeOfBtreeSplit; XLogRegisterData((char *) &xlrec, SizeOfBtreeSplit);
rdata[0].buffer = InvalidBuffer;
lastrdata = &rdata[0]; XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
XLogRegisterBuffer(1, rbuf, REGBUF_WILL_INIT);
/* Log the right sibling, because we've changed its prev-pointer. */
if (!P_RIGHTMOST(ropaque))
XLogRegisterBuffer(2, sbuf, REGBUF_STANDARD);
if (BufferIsValid(cbuf))
XLogRegisterBuffer(3, cbuf, REGBUF_STANDARD);
/* /*
* Log the new item and its offset, if it was inserted on the left * Log the new item, if it was inserted on the left page. (If it was
* page. (If it was put on the right page, we don't need to explicitly * put on the right page, we don't need to explicitly WAL log it
* WAL log it because it's included with all the other items on the * because it's included with all the other items on the right page.)
* right page.) Show the new item as belonging to the left page * Show the new item as belonging to the left page buffer, so that it
* buffer, so that it is not stored if XLogInsert decides it needs a * is not stored if XLogInsert decides it needs a full-page image of
* full-page image of the left page. We store the offset anyway, * the left page. We store the offset anyway, though, to support
* though, to support archive compression of these records. * archive compression of these records.
*/ */
if (newitemonleft) if (newitemonleft)
{ XLogRegisterBufData(0, (char *) newitem, MAXALIGN(newitemsz));
lastrdata->next = lastrdata + 1;
lastrdata++;
lastrdata->data = (char *) &newitemoff;
lastrdata->len = sizeof(OffsetNumber);
lastrdata->buffer = InvalidBuffer;
lastrdata->next = lastrdata + 1;
lastrdata++;
lastrdata->data = (char *) newitem;
lastrdata->len = MAXALIGN(newitemsz);
lastrdata->buffer = buf; /* backup block 0 */
lastrdata->buffer_std = true;
}
/* Log left page */ /* Log left page */
if (!isleaf) if (!isleaf)
{ {
lastrdata->next = lastrdata + 1;
lastrdata++;
/* /*
* We must also log the left page's high key, because the right * We must also log the left page's high key, because the right
* page's leftmost key is suppressed on non-leaf levels. Show it * page's leftmost key is suppressed on non-leaf levels. Show it
...@@ -1319,43 +1280,7 @@ _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firstright, ...@@ -1319,43 +1280,7 @@ _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firstright,
*/ */
itemid = PageGetItemId(origpage, P_HIKEY); itemid = PageGetItemId(origpage, P_HIKEY);
item = (IndexTuple) PageGetItem(origpage, itemid); item = (IndexTuple) PageGetItem(origpage, itemid);
lastrdata->data = (char *) item; XLogRegisterBufData(0, (char *) item, MAXALIGN(IndexTupleSize(item)));
lastrdata->len = MAXALIGN(IndexTupleSize(item));
lastrdata->buffer = buf; /* backup block 0 */
lastrdata->buffer_std = true;
}
if (isleaf && !newitemonleft)
{
lastrdata->next = lastrdata + 1;
lastrdata++;
/*
* Although we don't need to WAL-log anything on the left page, we
* still need XLogInsert to consider storing a full-page image of
* the left page, so make an empty entry referencing that buffer.
* This also ensures that the left page is always backup block 0.
*/
lastrdata->data = NULL;
lastrdata->len = 0;
lastrdata->buffer = buf; /* backup block 0 */
lastrdata->buffer_std = true;
}
/*
* Log block number of left child, whose INCOMPLETE_SPLIT flag this
* insertion clears.
*/
if (!isleaf)
{
lastrdata->next = lastrdata + 1;
lastrdata++;
cblkno = BufferGetBlockNumber(cbuf);
lastrdata->data = (char *) &cblkno;
lastrdata->len = sizeof(BlockNumber);
lastrdata->buffer = cbuf; /* backup block 1 */
lastrdata->buffer_std = true;
} }
/* /*
...@@ -1370,35 +1295,16 @@ _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firstright, ...@@ -1370,35 +1295,16 @@ _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firstright,
* and so the item pointers can be reconstructed. See comments for * and so the item pointers can be reconstructed. See comments for
* _bt_restore_page(). * _bt_restore_page().
*/ */
lastrdata->next = lastrdata + 1; XLogRegisterBufData(1,
lastrdata++; (char *) rightpage + ((PageHeader) rightpage)->pd_upper,
((PageHeader) rightpage)->pd_special - ((PageHeader) rightpage)->pd_upper);
lastrdata->data = (char *) rightpage +
((PageHeader) rightpage)->pd_upper;
lastrdata->len = ((PageHeader) rightpage)->pd_special -
((PageHeader) rightpage)->pd_upper;
lastrdata->buffer = InvalidBuffer;
/* Log the right sibling, because we've changed its' prev-pointer. */
if (!P_RIGHTMOST(ropaque))
{
lastrdata->next = lastrdata + 1;
lastrdata++;
lastrdata->data = NULL;
lastrdata->len = 0;
lastrdata->buffer = sbuf; /* bkp block 1 (leaf) or 2 (non-leaf) */
lastrdata->buffer_std = true;
}
lastrdata->next = NULL;
if (isroot) if (isroot)
xlinfo = newitemonleft ? XLOG_BTREE_SPLIT_L_ROOT : XLOG_BTREE_SPLIT_R_ROOT; xlinfo = newitemonleft ? XLOG_BTREE_SPLIT_L_ROOT : XLOG_BTREE_SPLIT_R_ROOT;
else else
xlinfo = newitemonleft ? XLOG_BTREE_SPLIT_L : XLOG_BTREE_SPLIT_R; xlinfo = newitemonleft ? XLOG_BTREE_SPLIT_L : XLOG_BTREE_SPLIT_R;
recptr = XLogInsert(RM_BTREE_ID, xlinfo, rdata); recptr = XLogInsert(RM_BTREE_ID, xlinfo);
PageSetLSN(origpage, recptr); PageSetLSN(origpage, recptr);
PageSetLSN(rightpage, recptr); PageSetLSN(rightpage, recptr);
...@@ -2090,34 +1996,35 @@ _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf) ...@@ -2090,34 +1996,35 @@ _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf)
{ {
xl_btree_newroot xlrec; xl_btree_newroot xlrec;
XLogRecPtr recptr; XLogRecPtr recptr;
XLogRecData rdata[3]; xl_btree_metadata md;
xlrec.node = rel->rd_node;
xlrec.rootblk = rootblknum; xlrec.rootblk = rootblknum;
xlrec.level = metad->btm_level; xlrec.level = metad->btm_level;
rdata[0].data = (char *) &xlrec; XLogBeginInsert();
rdata[0].len = SizeOfBtreeNewroot; XLogRegisterData((char *) &xlrec, SizeOfBtreeNewroot);
rdata[0].buffer = InvalidBuffer;
rdata[0].next = &(rdata[1]); XLogRegisterBuffer(0, rootbuf, REGBUF_WILL_INIT);
XLogRegisterBuffer(1, lbuf, REGBUF_STANDARD);
XLogRegisterBuffer(2, metabuf, REGBUF_WILL_INIT);
md.root = rootblknum;
md.level = metad->btm_level;
md.fastroot = rootblknum;
md.fastlevel = metad->btm_level;
XLogRegisterBufData(2, (char *) &md, sizeof(xl_btree_metadata));
/* /*
* Direct access to page is not good but faster - we should implement * Direct access to page is not good but faster - we should implement
* some new func in page API. * some new func in page API.
*/ */
rdata[1].data = (char *) rootpage + ((PageHeader) rootpage)->pd_upper; XLogRegisterBufData(0,
rdata[1].len = ((PageHeader) rootpage)->pd_special - (char *) rootpage + ((PageHeader) rootpage)->pd_upper,
((PageHeader) rootpage)->pd_upper; ((PageHeader) rootpage)->pd_special -
rdata[1].buffer = InvalidBuffer; ((PageHeader) rootpage)->pd_upper);
rdata[1].next = &(rdata[2]);
recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_NEWROOT);
/* Make a full-page image of the left child if needed */
rdata[2].data = NULL;
rdata[2].len = 0;
rdata[2].buffer = lbuf;
rdata[2].next = NULL;
recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_NEWROOT, rdata);
PageSetLSN(lpage, recptr); PageSetLSN(lpage, recptr);
PageSetLSN(rootpage, recptr); PageSetLSN(rootpage, recptr);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -18,10 +18,10 @@ ...@@ -18,10 +18,10 @@
void void
clog_desc(StringInfo buf, XLogRecord *record) clog_desc(StringInfo buf, XLogReaderState *record)
{ {
char *rec = XLogRecGetData(record); char *rec = XLogRecGetData(record);
uint8 info = record->xl_info & ~XLR_INFO_MASK; uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
if (info == CLOG_ZEROPAGE || info == CLOG_TRUNCATE) if (info == CLOG_ZEROPAGE || info == CLOG_TRUNCATE)
{ {
......
...@@ -19,10 +19,10 @@ ...@@ -19,10 +19,10 @@
void void
dbase_desc(StringInfo buf, XLogRecord *record) dbase_desc(StringInfo buf, XLogReaderState *record)
{ {
char *rec = XLogRecGetData(record); char *rec = XLogRecGetData(record);
uint8 info = record->xl_info & ~XLR_INFO_MASK; uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
if (info == XLOG_DBASE_CREATE) if (info == XLOG_DBASE_CREATE)
{ {
......
This diff is collapsed.
This diff is collapsed.
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
#include "access/hash.h" #include "access/hash.h"
void void
hash_desc(StringInfo buf, XLogRecord *record) hash_desc(StringInfo buf, XLogReaderState *record)
{ {
} }
......
This diff is collapsed.
...@@ -47,10 +47,10 @@ out_member(StringInfo buf, MultiXactMember *member) ...@@ -47,10 +47,10 @@ out_member(StringInfo buf, MultiXactMember *member)
} }
void void
multixact_desc(StringInfo buf, XLogRecord *record) multixact_desc(StringInfo buf, XLogReaderState *record)
{ {
char *rec = XLogRecGetData(record); char *rec = XLogRecGetData(record);
uint8 info = record->xl_info & ~XLR_INFO_MASK; uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE || if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE ||
info == XLOG_MULTIXACT_ZERO_MEM_PAGE) info == XLOG_MULTIXACT_ZERO_MEM_PAGE)
......
This diff is collapsed.
...@@ -17,10 +17,10 @@ ...@@ -17,10 +17,10 @@
#include "utils/relmapper.h" #include "utils/relmapper.h"
void void
relmap_desc(StringInfo buf, XLogRecord *record) relmap_desc(StringInfo buf, XLogReaderState *record)
{ {
char *rec = XLogRecGetData(record); char *rec = XLogRecGetData(record);
uint8 info = record->xl_info & ~XLR_INFO_MASK; uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
if (info == XLOG_RELMAP_UPDATE) if (info == XLOG_RELMAP_UPDATE)
{ {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
#include "access/transam.h" #include "access/transam.h"
#include "access/tuptoaster.h" #include "access/tuptoaster.h"
#include "access/xact.h" #include "access/xact.h"
#include "access/xlog_internal.h"
#include "catalog/catalog.h" #include "catalog/catalog.h"
#include "lib/binaryheap.h" #include "lib/binaryheap.h"
#include "miscadmin.h" #include "miscadmin.h"
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment