/* * CRC-check an XLOG record. We do not believe the contents of an XLOG * record (other than to the minimal extent of computing the amount of * data to read in) until we've checked the CRCs. * * We assume all of the record has been read into memory at *record. */ bool recordIsValid(XLogRecord *record, XLogRecPtr *recptr) { pg_crc32 crc; int i; uint32 len = record->xl_len; BkpBlock bkpb; char *blk; /* First the rmgr data */ INIT_CRC32C(crc); COMP_CRC32C(crc, XLogRecGetData(record), len); /* Add in the backup blocks, if any */ blk = (char *) XLogRecGetData(record) + len; for (i = 0; i < XLR_MAX_BKP_BLOCKS; i++) { uint32 blen; if (!(record->xl_info & XLR_SET_BKP_BLOCK(i))) continue; memcpy(&bkpb, blk, sizeof(BkpBlock)); if (bkpb.hole_offset + bkpb.hole_length > BLCKSZ) { ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("incorrect hole size in record at %X/%X", recptr->xlogid, recptr->xrecoff))); return false; } blen = sizeof(BkpBlock) + BLCKSZ - bkpb.hole_length; COMP_CRC32C(crc, blk, blen); blk += blen; } /* Check that xl_tot_len agrees with our calculation */ if (blk != (char *) record + record->xl_tot_len) { ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("incorrect total length in record at %X/%X", recptr->xlogid, recptr->xrecoff))); return false; } /* Finally include the record header */ COMP_CRC32C(crc, (char *) record + sizeof(pg_crc32), SizeOfXLogRecord - sizeof(pg_crc32)); FIN_CRC32C(crc); if (!EQ_CRC32C(record->xl_crc, crc)) { ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("incorrect resource manager data checksum in record at %X/%X", recptr->xlogid, recptr->xrecoff))); return false; } return true; }
static void print_backup_blocks(XLogRecPtr cur, XLogRecord *rec) { char *blk; int i; char buf[1024]; /* * backup blocks by full_page_write */ blk = (char*)XLogRecGetData(rec) + rec->xl_len; for (i = 0; i < XLR_MAX_BKP_BLOCKS; i++) { BkpBlock bkb; if (!(rec->xl_info & (XLR_SET_BKP_BLOCK(i)))) continue; memcpy(&bkb, blk, sizeof(BkpBlock)); getSpaceName(bkb.node.spcNode, spaceName, sizeof(spaceName)); getDbName(bkb.node.dbNode, dbName, sizeof(dbName)); getRelName(bkb.node.relNode, relName, sizeof(relName)); snprintf(buf, sizeof(buf), "bkpblock[%d]: s/d/r:%s/%s/%s blk:%u hole_off/len:%u/%u\n", i+1, spaceName, dbName, relName, bkb.block, bkb.hole_offset, bkb.hole_length); blk += sizeof(BkpBlock) + (BLCKSZ - bkb.hole_length); if (!enable_stats) printf("[cur:%u/%X, xid:%d, rmid:%d(%s), len:%d/%d, prev:%u/%X] %s", cur.xlogid, cur.xrecoff, rec->xl_xid, rec->xl_rmid, RM_names[rec->xl_rmid], rec->xl_len, rec->xl_tot_len, rec->xl_prev.xlogid, rec->xl_prev.xrecoff, buf); xlogstats.bkpblock_count++; xlogstats.bkpblock_len += (BLCKSZ - bkb.hole_length); } }
static void print_backup_blocks(XLogRecPtr cur, XLogRecord *rec) { char *blk; int i; char buf[1024]; /* * backup blocks by full_page_write */ blk = (char*)XLogRecGetData(rec) + rec->xl_len; for (i = 0; i < XLR_MAX_BKP_BLOCKS; i++) { BkpBlock bkb; if (!(rec->xl_info & (XLR_SET_BKP_BLOCK(i)))) continue; memcpy(&bkb, blk, sizeof(BkpBlock)); getSpaceName(bkb.node.spcNode, spaceName, sizeof(spaceName)); getDbName(bkb.node.dbNode, dbName, sizeof(dbName)); getRelName(bkb.node.relNode, relName, sizeof(relName)); snprintf(buf, sizeof(buf), "bkpblock[%d]: s/d/r:%s/%s/%s blk:%u hole_off/len:%u/%u\n", i+1, spaceName, dbName, relName, bkb.block, bkb.hole_offset, bkb.hole_length); blk += sizeof(BkpBlock) + (BLCKSZ - bkb.hole_length); if (!enable_stats) { PRINT_XLOGRECORD_HEADER(cur, rec); printf("%s", buf); } xlogstats.bkpblock_count++; xlogstats.bkpblock_len += (BLCKSZ - bkb.hole_length); } }
/* * CRC-check an XLOG record. We do not believe the contents of an XLOG * record (other than to the minimal extent of computing the amount of * data to read in) until we've checked the CRCs. * * We assume all of the record has been read into memory at *record. */ static bool RecordIsValid(XLogRecord *record, XLogRecPtr recptr) { pg_crc32 crc; int i; uint32 len = record->xl_len; BkpBlock bkpb; char *blk; /* First the rmgr data */ INIT_CRC32(crc); COMP_CRC32(crc, XLogRecGetData(record), len); /* Add in the backup blocks, if any */ blk = (char *) XLogRecGetData(record) + len; for (i = 0; i < XLR_MAX_BKP_BLOCKS; i++) { uint32 blen; if (!(record->xl_info & XLR_SET_BKP_BLOCK(i))) continue; memcpy(&bkpb, blk, sizeof(BkpBlock)); if (bkpb.hole_offset + bkpb.hole_length > BLCKSZ) { printf("incorrect hole size in record at %X/%X\n", recptr.xlogid, recptr.xrecoff); return false; } blen = sizeof(BkpBlock) + BLCKSZ - bkpb.hole_length; COMP_CRC32(crc, blk, blen); blk += blen; } /* skip total xl_tot_len check if physical log has been removed. */ #if PG_VERSION_NUM < 80300 || PG_VERSION_NUM >= 90200 if (record->xl_info & XLR_BKP_BLOCK_MASK) #else if (!(record->xl_info & XLR_BKP_REMOVABLE) || record->xl_info & XLR_BKP_BLOCK_MASK) #endif { /* Check that xl_tot_len agrees with our calculation */ if (blk != (char *) record + record->xl_tot_len) { printf("incorrect total length in record at %X/%X\n", recptr.xlogid, recptr.xrecoff); return false; } } /* Finally include the record header */ COMP_CRC32(crc, (char *) record + sizeof(pg_crc32), SizeOfXLogRecord - sizeof(pg_crc32)); FIN_CRC32(crc); if (!EQ_CRC32(record->xl_crc, crc)) { printf("incorrect resource manager data checksum in record at %X/%X\n", recptr.xlogid, recptr.xrecoff); return false; } return true; }
static void spgRedoPickSplit(XLogRecPtr lsn, XLogRecord *record) { char *ptr = XLogRecGetData(record); spgxlogPickSplit *xldata = (spgxlogPickSplit *) ptr; SpGistInnerTuple innerTuple; SpGistState state; OffsetNumber *toDelete; OffsetNumber *toInsert; uint8 *leafPageSelect; Buffer srcBuffer; Buffer destBuffer; Page page; int bbi; int i; fillFakeState(&state, xldata->stateSrc); ptr += MAXALIGN(sizeof(spgxlogPickSplit)); innerTuple = (SpGistInnerTuple) ptr; ptr += innerTuple->size; toDelete = (OffsetNumber *) ptr; ptr += MAXALIGN(sizeof(OffsetNumber) * xldata->nDelete); toInsert = (OffsetNumber *) ptr; ptr += MAXALIGN(sizeof(OffsetNumber) * xldata->nInsert); leafPageSelect = (uint8 *) ptr; ptr += MAXALIGN(sizeof(uint8) * xldata->nInsert); /* now ptr points to the list of leaf tuples */ /* * It's a bit tricky to identify which pages have been handled as * full-page images, so we explicitly count each referenced buffer. */ bbi = 0; if (xldata->blknoSrc == SPGIST_HEAD_BLKNO) { /* when splitting root, we touch it only in the guise of new inner */ srcBuffer = InvalidBuffer; } else if (xldata->initSrc) { /* just re-init the source page */ srcBuffer = XLogReadBuffer(xldata->node, xldata->blknoSrc, true); Assert(BufferIsValid(srcBuffer)); page = (Page) BufferGetPage(srcBuffer); SpGistInitBuffer(srcBuffer, SPGIST_LEAF); /* don't update LSN etc till we're done with it */ } else { /* delete the specified tuples from source page */ if (!(record->xl_info & XLR_SET_BKP_BLOCK(bbi))) { srcBuffer = XLogReadBuffer(xldata->node, xldata->blknoSrc, false); if (BufferIsValid(srcBuffer)) { page = BufferGetPage(srcBuffer); if (!XLByteLE(lsn, PageGetLSN(page))) { /* * We have it a bit easier here than in doPickSplit(), * because we know the inner tuple's location already, * so we can inject the correct redirection tuple now. */ if (!state.isBuild) spgPageIndexMultiDelete(&state, page, toDelete, xldata->nDelete, SPGIST_REDIRECT, SPGIST_PLACEHOLDER, xldata->blknoInner, xldata->offnumInner); else spgPageIndexMultiDelete(&state, page, toDelete, xldata->nDelete, SPGIST_PLACEHOLDER, SPGIST_PLACEHOLDER, InvalidBlockNumber, InvalidOffsetNumber); /* don't update LSN etc till we're done with it */ } } } else srcBuffer = InvalidBuffer; bbi++; } /* try to access dest page if any */ if (xldata->blknoDest == InvalidBlockNumber) { destBuffer = InvalidBuffer; } else if (xldata->initDest) { /* just re-init the dest page */ destBuffer = XLogReadBuffer(xldata->node, xldata->blknoDest, true); Assert(BufferIsValid(destBuffer)); page = (Page) BufferGetPage(destBuffer); SpGistInitBuffer(destBuffer, SPGIST_LEAF); /* don't update LSN etc till we're done with it */ } else { if (!(record->xl_info & XLR_SET_BKP_BLOCK(bbi))) destBuffer = XLogReadBuffer(xldata->node, xldata->blknoDest, false); else destBuffer = InvalidBuffer; bbi++; } /* restore leaf tuples to src and/or dest page */ for (i = 0; i < xldata->nInsert; i++) { SpGistLeafTuple lt = (SpGistLeafTuple) ptr; Buffer leafBuffer; ptr += lt->size; leafBuffer = leafPageSelect[i] ? destBuffer : srcBuffer; if (!BufferIsValid(leafBuffer)) continue; /* no need to touch this page */ page = BufferGetPage(leafBuffer); if (!XLByteLE(lsn, PageGetLSN(page))) { addOrReplaceTuple(page, (Item) lt, lt->size, toInsert[i]); } } /* Now update src and dest page LSNs */ if (BufferIsValid(srcBuffer)) { page = BufferGetPage(srcBuffer); if (!XLByteLE(lsn, PageGetLSN(page))) { PageSetLSN(page, lsn); PageSetTLI(page, ThisTimeLineID); MarkBufferDirty(srcBuffer); } UnlockReleaseBuffer(srcBuffer); } if (BufferIsValid(destBuffer)) { page = BufferGetPage(destBuffer); if (!XLByteLE(lsn, PageGetLSN(page))) { PageSetLSN(page, lsn); PageSetTLI(page, ThisTimeLineID); MarkBufferDirty(destBuffer); } UnlockReleaseBuffer(destBuffer); } /* restore new inner tuple */ if (!(record->xl_info & XLR_SET_BKP_BLOCK(bbi))) { Buffer buffer = XLogReadBuffer(xldata->node, xldata->blknoInner, xldata->initInner); if (BufferIsValid(buffer)) { page = BufferGetPage(buffer); if (xldata->initInner) SpGistInitBuffer(buffer, 0); if (!XLByteLE(lsn, PageGetLSN(page))) { addOrReplaceTuple(page, (Item) innerTuple, innerTuple->size, xldata->offnumInner); /* if inner is also parent, update link while we're here */ if (xldata->blknoInner == xldata->blknoParent) { SpGistInnerTuple parent; parent = (SpGistInnerTuple) PageGetItem(page, PageGetItemId(page, xldata->offnumParent)); spgUpdateNodeLink(parent, xldata->nodeI, xldata->blknoInner, xldata->offnumInner); } PageSetLSN(page, lsn); PageSetTLI(page, ThisTimeLineID); MarkBufferDirty(buffer); } UnlockReleaseBuffer(buffer); } } bbi++; /* update parent downlink, unless we did it above */ if (xldata->blknoParent == InvalidBlockNumber) { /* no parent cause we split the root */ Assert(xldata->blknoInner == SPGIST_HEAD_BLKNO); } else if (xldata->blknoInner != xldata->blknoParent) { if (!(record->xl_info & XLR_SET_BKP_BLOCK(bbi))) { Buffer buffer = XLogReadBuffer(xldata->node, xldata->blknoParent, false); if (BufferIsValid(buffer)) { page = BufferGetPage(buffer); if (!XLByteLE(lsn, PageGetLSN(page))) { SpGistInnerTuple parent; parent = (SpGistInnerTuple) PageGetItem(page, PageGetItemId(page, xldata->offnumParent)); spgUpdateNodeLink(parent, xldata->nodeI, xldata->blknoInner, xldata->offnumInner); PageSetLSN(page, lsn); PageSetTLI(page, ThisTimeLineID); MarkBufferDirty(buffer); } UnlockReleaseBuffer(buffer); } } } }
static void spgRedoAddNode(XLogRecPtr lsn, XLogRecord *record) { char *ptr = XLogRecGetData(record); spgxlogAddNode *xldata = (spgxlogAddNode *) ptr; SpGistInnerTuple innerTuple; SpGistState state; Buffer buffer; Page page; int bbi; /* we assume this is adequately aligned */ ptr += sizeof(spgxlogAddNode); innerTuple = (SpGistInnerTuple) ptr; fillFakeState(&state, xldata->stateSrc); if (xldata->blknoNew == InvalidBlockNumber) { /* update in place */ Assert(xldata->blknoParent == InvalidBlockNumber); if (!(record->xl_info & XLR_BKP_BLOCK_1)) { buffer = XLogReadBuffer(xldata->node, xldata->blkno, false); if (BufferIsValid(buffer)) { page = BufferGetPage(buffer); if (!XLByteLE(lsn, PageGetLSN(page))) { PageIndexTupleDelete(page, xldata->offnum); if (PageAddItem(page, (Item) innerTuple, innerTuple->size, xldata->offnum, false, false) != xldata->offnum) elog(ERROR, "failed to add item of size %u to SPGiST index page", innerTuple->size); PageSetLSN(page, lsn); PageSetTLI(page, ThisTimeLineID); MarkBufferDirty(buffer); } UnlockReleaseBuffer(buffer); } } } else { /* Install new tuple first so redirect is valid */ if (!(record->xl_info & XLR_BKP_BLOCK_2)) { buffer = XLogReadBuffer(xldata->node, xldata->blknoNew, xldata->newPage); if (BufferIsValid(buffer)) { page = BufferGetPage(buffer); if (xldata->newPage) SpGistInitBuffer(buffer, 0); if (!XLByteLE(lsn, PageGetLSN(page))) { addOrReplaceTuple(page, (Item) innerTuple, innerTuple->size, xldata->offnumNew); PageSetLSN(page, lsn); PageSetTLI(page, ThisTimeLineID); MarkBufferDirty(buffer); } UnlockReleaseBuffer(buffer); } } /* Delete old tuple, replacing it with redirect or placeholder tuple */ if (!(record->xl_info & XLR_BKP_BLOCK_1)) { buffer = XLogReadBuffer(xldata->node, xldata->blkno, false); if (BufferIsValid(buffer)) { page = BufferGetPage(buffer); if (!XLByteLE(lsn, PageGetLSN(page))) { SpGistDeadTuple dt; if (state.isBuild) dt = spgFormDeadTuple(&state, SPGIST_PLACEHOLDER, InvalidBlockNumber, InvalidOffsetNumber); else dt = spgFormDeadTuple(&state, SPGIST_REDIRECT, xldata->blknoNew, xldata->offnumNew); PageIndexTupleDelete(page, xldata->offnum); if (PageAddItem(page, (Item) dt, dt->size, xldata->offnum, false, false) != xldata->offnum) elog(ERROR, "failed to add item of size %u to SPGiST index page", dt->size); if (state.isBuild) SpGistPageGetOpaque(page)->nPlaceholder++; else SpGistPageGetOpaque(page)->nRedirection++; PageSetLSN(page, lsn); PageSetTLI(page, ThisTimeLineID); MarkBufferDirty(buffer); } UnlockReleaseBuffer(buffer); } } /* * Update parent downlink. Since parent could be in either of the * previous two buffers, it's a bit tricky to determine which BKP bit * applies. */ if (xldata->blknoParent == xldata->blkno) bbi = 0; else if (xldata->blknoParent == xldata->blknoNew) bbi = 1; else bbi = 2; if (!(record->xl_info & XLR_SET_BKP_BLOCK(bbi))) { buffer = XLogReadBuffer(xldata->node, xldata->blknoParent, false); if (BufferIsValid(buffer)) { page = BufferGetPage(buffer); if (!XLByteLE(lsn, PageGetLSN(page))) { SpGistInnerTuple innerTuple; innerTuple = (SpGistInnerTuple) PageGetItem(page, PageGetItemId(page, xldata->offnumParent)); spgUpdateNodeLink(innerTuple, xldata->nodeI, xldata->blknoNew, xldata->offnumNew); PageSetLSN(page, lsn); PageSetTLI(page, ThisTimeLineID); MarkBufferDirty(buffer); } UnlockReleaseBuffer(buffer); } } } }