Datum tidne(PG_FUNCTION_ARGS) { ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0); ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1); PG_RETURN_BOOL(BlockIdGetBlockNumber(&(arg1->ip_blkid)) != BlockIdGetBlockNumber(&(arg2->ip_blkid)) || arg1->ip_posid != arg2->ip_posid); }
static void ginRedoInsert(XLogReaderState *record) { XLogRecPtr lsn = record->EndRecPtr; ginxlogInsert *data = (ginxlogInsert *) XLogRecGetData(record); Buffer buffer; #ifdef NOT_USED BlockNumber leftChildBlkno = InvalidBlockNumber; #endif BlockNumber rightChildBlkno = InvalidBlockNumber; bool isLeaf = (data->flags & GIN_INSERT_ISLEAF) != 0; /* * First clear incomplete-split flag on child page if this finishes a * split. */ if (!isLeaf) { char *payload = XLogRecGetData(record) + sizeof(ginxlogInsert); #ifdef NOT_USED leftChildBlkno = BlockIdGetBlockNumber((BlockId) payload); #endif payload += sizeof(BlockIdData); rightChildBlkno = BlockIdGetBlockNumber((BlockId) payload); payload += sizeof(BlockIdData); ginRedoClearIncompleteSplit(record, 1); } if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO) { Page page = BufferGetPage(buffer); Size len; char *payload = XLogRecGetBlockData(record, 0, &len); /* How to insert the payload is tree-type specific */ if (data->flags & GIN_INSERT_ISDATA) { Assert(GinPageIsData(page)); ginRedoInsertData(buffer, isLeaf, rightChildBlkno, payload); } else { Assert(!GinPageIsData(page)); ginRedoInsertEntry(buffer, isLeaf, rightChildBlkno, payload); } PageSetLSN(page, lsn); MarkBufferDirty(buffer); } if (BufferIsValid(buffer)) UnlockReleaseBuffer(buffer); }
Datum tidtoi8(PG_FUNCTION_ARGS) /*CDB*/ { ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0); BlockNumber blockNumber; OffsetNumber offsetNumber; blockNumber = BlockIdGetBlockNumber(&(itemPtr->ip_blkid)); offsetNumber = itemPtr->ip_posid; PG_RETURN_INT64(((int64)blockNumber << 16) + offsetNumber); }
/* FIXME This should do exactly the same checks of lp_flags as in heap.c */ uint32 check_index_tuple(Relation rel, PageHeader header, int block, int i, char *buffer) { uint32 nerrs = 0; int j, a, b, c, d; IndexTuple itup = (IndexTuple)(buffer + header->pd_linp[i].lp_off); /* FIXME This is used when checking overflowing attributes, but it's not clear what * exactly this means / how it works. Needs a bit more investigation and maybe a review * from soneone who really knows the b-tree implementation. */ int dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info); ereport(DEBUG2,(errmsg("[%d:%d] off=%d len=%d tid=(%d,%d)", block, (i+1), header->pd_linp[i].lp_off, header->pd_linp[i].lp_len, BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)), itup->t_tid.ip_posid ))); /* check intersection with other tuples */ /* [A,B] vs [C,D] */ a = header->pd_linp[i].lp_off; b = header->pd_linp[i].lp_off + header->pd_linp[i].lp_len; ereport(DEBUG2,(errmsg("[%d:%d] checking intersection with other tuples", block, (i+1)))); for (j = 0; j < i; j++) { /* FIXME Skip UNUSED/REDIRECT/DEAD tuples */ if (! (header->pd_linp[i].lp_flags == LP_NORMAL)) { ereport(DEBUG3,(errmsg("[%d:%d] skipped (not LP_NORMAL)", block, (j+1)))); continue; } c = header->pd_linp[j].lp_off; d = header->pd_linp[j].lp_off + header->pd_linp[j].lp_len; /* [A,C,B] or [A,D,B] or [C,A,D] or [C,B,D] */ if (((a < c) && (c < b)) || ((a < d) && (d < b)) || ((c < a) && (a < d)) || ((c < b) && (b < d))) { ereport(WARNING,(errmsg("[%d:%d] intersects with [%d:%d] (%d,%d) vs. (%d,%d)", block, (i+1), block, j, a, b, c, d))); ++nerrs; } } /* check attributes only for tuples with (lp_flags==LP_NORMAL) */ if (header->pd_linp[i].lp_flags == LP_NORMAL) { nerrs += check_index_tuple_attributes(rel, header, block, i + 1, buffer, dlen); } return nerrs; }
/* * ItemPointerCompare * Generic btree-style comparison for item pointers. */ int32 ItemPointerCompare(ItemPointer arg1, ItemPointer arg2) { /* * Don't use ItemPointerGetBlockNumber or ItemPointerGetOffsetNumber here, * because they assert ip_posid != 0 which might not be true for a * user-supplied TID. */ BlockNumber b1 = BlockIdGetBlockNumber(&(arg1->ip_blkid)); BlockNumber b2 = BlockIdGetBlockNumber(&(arg2->ip_blkid)); if (b1 < b2) return -1; else if (b1 > b2) return 1; else if (arg1->ip_posid < arg2->ip_posid) return -1; else if (arg1->ip_posid > arg2->ip_posid) return 1; else return 0; }
/* ======= the following taken from itemptr.h ======== */ static char * ItemPointerToBufferX(char *buffer, ItemPointer tid) { // Do not assert valid ItemPointer -- it is ok if it is (0,0)... BlockNumber blockNumber = BlockIdGetBlockNumber(&tid->ip_blkid); OffsetNumber offsetNumber = tid->ip_posid; sprintf(buffer, "(%u,%u)", blockNumber, offsetNumber); return buffer; }
/* ---------------------------------------------------------------- * tidout * ---------------------------------------------------------------- */ Datum tidout(PG_FUNCTION_ARGS) { ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0); BlockNumber blockNumber; OffsetNumber offsetNumber; char buf[32]; blockNumber = BlockIdGetBlockNumber(&(itemPtr->ip_blkid)); offsetNumber = itemPtr->ip_posid; /* Perhaps someday we should output this as a record. */ snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber); PG_RETURN_CSTRING(pstrdup(buf)); }
/* FIXME This should do exactly the same checks of lp_flags as in heap.c */ uint32 check_index_tuple(Relation rel, PageHeader header, int block, int i, char *buffer) { uint32 nerrs = 0; int j, a, b, c, d; IndexTuple itup = (IndexTuple)(buffer + header->pd_linp[i].lp_off); ereport(DEBUG2,(errmsg("[%d:%d] off=%d len=%d tid=(%d,%d)", block, (i+1), header->pd_linp[i].lp_off, header->pd_linp[i].lp_len, BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)), itup->t_tid.ip_posid ))); /* check intersection with other tuples */ /* [A,B] vs [C,D] */ a = header->pd_linp[i].lp_off; b = header->pd_linp[i].lp_off + header->pd_linp[i].lp_len; ereport(DEBUG2,(errmsg("[%d:%d] checking intersection with other tuples", block, i))); for (j = 0; j < i; j++) { /* FIXME Skip UNUSED/REDIRECT/DEAD tuples */ if (! (header->pd_linp[i].lp_flags == LP_NORMAL)) { ereport(DEBUG3,(errmsg("[%d:%d] skipped (not LP_NORMAL)", block, j))); continue; } c = header->pd_linp[j].lp_off; d = header->pd_linp[j].lp_off + header->pd_linp[j].lp_len; /* [A,C,B] or [A,D,B] or [C,A,D] or [C,B,D] */ if (((a < c) && (c < b)) || ((a < d) && (d < b)) || ((c < a) && (a < d)) || ((c < b) && (b < d))) { ereport(WARNING,(errmsg("[%d:%d] intersects with [%d:%d] (%d,%d) vs. (%d,%d)", block, (i+1), block, j, a, b, c, d))); ++nerrs; } } /* check attributes only for tuples with (lp_flags==LP_NORMAL) */ if (header->pd_linp[i].lp_flags == LP_NORMAL) { nerrs += check_index_tuple_attributes(rel, header, block, i, buffer); } return nerrs; }
/* * tidsend - converts tid to binary format */ Datum tidsend(PG_FUNCTION_ARGS) { ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0); BlockId blockId; BlockNumber blockNumber; OffsetNumber offsetNumber; StringInfoData buf; blockId = &(itemPtr->ip_blkid); blockNumber = BlockIdGetBlockNumber(blockId); offsetNumber = itemPtr->ip_posid; pq_begintypsend(&buf); pq_sendint(&buf, blockNumber, sizeof(blockNumber)); pq_sendint(&buf, offsetNumber, sizeof(offsetNumber)); PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); }
/* ---------------------------------------------------------------- * tidout * ---------------------------------------------------------------- */ Datum tidout(PG_FUNCTION_ARGS) { ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0); BlockId blockId; BlockNumber blockNumber; OffsetNumber offsetNumber; char buf[32]; if (!ItemPointerIsValid(itemPtr)) PG_RETURN_CSTRING(pstrdup("()")); blockId = &(itemPtr->ip_blkid); blockNumber = BlockIdGetBlockNumber(blockId); offsetNumber = itemPtr->ip_posid; snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber); PG_RETURN_CSTRING(pstrdup(buf)); }
/* * pgstat_heap -- returns live/dead tuples info in a heap */ static Datum pgstat_heap(Relation rel, FunctionCallInfo fcinfo) { HeapScanDesc scan; HeapTuple tuple; BlockNumber nblocks; BlockNumber block = 0; /* next block to count free space in */ BlockNumber tupblock; Buffer buffer; pgstattuple_type stat = {0}; /* Disable syncscan because we assume we scan from block zero upwards */ scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false); nblocks = scan->rs_nblocks; /* # blocks to be scanned */ /* scan the relation */ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { CHECK_FOR_INTERRUPTS(); /* must hold a buffer lock to call HeapTupleSatisfiesVisibility */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); if (HeapTupleSatisfiesVisibility(tuple, SnapshotNow, scan->rs_cbuf)) { stat.tuple_len += tuple->t_len; stat.tuple_count++; } else { stat.dead_tuple_len += tuple->t_len; stat.dead_tuple_count++; } LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); /* * To avoid physically reading the table twice, try to do the * free-space scan in parallel with the heap scan. However, * heap_getnext may find no tuples on a given page, so we cannot * simply examine the pages returned by the heap scan. */ tupblock = BlockIdGetBlockNumber(&tuple->t_self.ip_blkid); while (block <= tupblock) { CHECK_FOR_INTERRUPTS(); buffer = ReadBuffer(rel, block); LockBuffer(buffer, BUFFER_LOCK_SHARE); stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer)); UnlockReleaseBuffer(buffer); block++; } } heap_endscan(scan); while (block < nblocks) { CHECK_FOR_INTERRUPTS(); buffer = ReadBuffer(rel, block); LockBuffer(buffer, BUFFER_LOCK_SHARE); stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer)); UnlockReleaseBuffer(buffer); block++; } relation_close(rel, AccessShareLock); stat.table_len = (uint64) nblocks *BLCKSZ; return build_pgstattuple_type(&stat, fcinfo); }
static void ginRedoInsert(XLogRecPtr lsn, XLogRecord *record) { ginxlogInsert *data = (ginxlogInsert *) XLogRecGetData(record); Buffer buffer; Page page; char *payload; BlockNumber leftChildBlkno = InvalidBlockNumber; BlockNumber rightChildBlkno = InvalidBlockNumber; bool isLeaf = (data->flags & GIN_INSERT_ISLEAF) != 0; payload = XLogRecGetData(record) + sizeof(ginxlogInsert); /* * First clear incomplete-split flag on child page if this finishes a * split. */ if (!isLeaf) { leftChildBlkno = BlockIdGetBlockNumber((BlockId) payload); payload += sizeof(BlockIdData); rightChildBlkno = BlockIdGetBlockNumber((BlockId) payload); payload += sizeof(BlockIdData); if (record->xl_info & XLR_BKP_BLOCK(0)) (void) RestoreBackupBlock(lsn, record, 0, false, false); else ginRedoClearIncompleteSplit(lsn, data->node, leftChildBlkno); } /* If we have a full-page image, restore it and we're done */ if (record->xl_info & XLR_BKP_BLOCK(isLeaf ? 0 : 1)) { (void) RestoreBackupBlock(lsn, record, isLeaf ? 0 : 1, false, false); return; } buffer = XLogReadBuffer(data->node, data->blkno, false); if (!BufferIsValid(buffer)) return; /* page was deleted, nothing to do */ page = (Page) BufferGetPage(buffer); if (lsn > PageGetLSN(page)) { /* How to insert the payload is tree-type specific */ if (data->flags & GIN_INSERT_ISDATA) { Assert(GinPageIsData(page)); ginRedoInsertData(buffer, isLeaf, rightChildBlkno, payload); } else { Assert(!GinPageIsData(page)); ginRedoInsertEntry(buffer, isLeaf, rightChildBlkno, payload); } PageSetLSN(page, lsn); MarkBufferDirty(buffer); } UnlockReleaseBuffer(buffer); }
Datum bt_page_items(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(0); uint32 blkno = PG_GETARG_UINT32(1); Datum result; char *values[6]; HeapTuple tuple; FuncCallContext *fctx; MemoryContext mctx; struct user_args *uargs; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pageinspect functions")))); if (SRF_IS_FIRSTCALL()) { RangeVar *relrv; Relation rel; Buffer buffer; BTPageOpaque opaque; TupleDesc tupleDesc; fctx = SRF_FIRSTCALL_INIT(); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "relation \"%s\" is not a btree index", RelationGetRelationName(rel)); /* * Reject attempts to read non-local temporary relations; we would be * likely to get wrong data since we have no visibility into the * owning session's local buffers. */ if (RELATION_IS_OTHER_TEMP(rel)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); if (blkno == 0) elog(ERROR, "block 0 is a meta page"); CHECK_RELATION_BLOCK_RANGE(rel, blkno); buffer = ReadBuffer(rel, blkno); LockBuffer(buffer, BUFFER_LOCK_SHARE); /* * We copy the page into local storage to avoid holding pin on the * buffer longer than we must, and possibly failing to release it at * all if the calling query doesn't fetch all rows. */ mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); uargs = palloc(sizeof(struct user_args)); uargs->page = palloc(BLCKSZ); memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ); UnlockReleaseBuffer(buffer); relation_close(rel, AccessShareLock); uargs->offset = FirstOffsetNumber; opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page); if (P_ISDELETED(opaque)) elog(NOTICE, "page is deleted"); fctx->max_calls = PageGetMaxOffsetNumber(uargs->page); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc); fctx->user_fctx = uargs; MemoryContextSwitchTo(mctx); } fctx = SRF_PERCALL_SETUP(); uargs = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { ItemId id; IndexTuple itup; int j; int off; int dlen; char *dump; char *ptr; id = PageGetItemId(uargs->page, uargs->offset); if (!ItemIdIsValid(id)) elog(ERROR, "invalid ItemId"); itup = (IndexTuple) PageGetItem(uargs->page, id); j = 0; values[j++] = psprintf("%d", uargs->offset); values[j++] = psprintf("(%u,%u)", BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)), itup->t_tid.ip_posid); values[j++] = psprintf("%d", (int) IndexTupleSize(itup)); values[j++] = psprintf("%c", IndexTupleHasNulls(itup) ? 't' : 'f'); values[j++] = psprintf("%c", IndexTupleHasVarwidths(itup) ? 't' : 'f'); ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info); dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info); dump = palloc0(dlen * 3 + 1); values[j] = dump; for (off = 0; off < dlen; off++) { if (off > 0) *dump++ = ' '; sprintf(dump, "%02x", *(ptr + off) & 0xff); dump += 2; } tuple = BuildTupleFromCStrings(fctx->attinmeta, values); result = HeapTupleGetDatum(tuple); uargs->offset = uargs->offset + 1; SRF_RETURN_NEXT(fctx, result); } else { pfree(uargs->page); pfree(uargs); SRF_RETURN_DONE(fctx); } }
Datum bt_page_items(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(0); uint32 blkno = PG_GETARG_UINT32(1); RangeVar *relrv; Datum result; char *values[BTPAGEITEMS_NCOLUMNS]; BTPageOpaque opaque; HeapTuple tuple; ItemId id; FuncCallContext *fctx; MemoryContext mctx; struct user_args *uargs = NULL; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pgstattuple functions")))); if (blkno == 0) elog(ERROR, "Block 0 is a meta page."); if (SRF_IS_FIRSTCALL()) { fctx = SRF_FIRSTCALL_INIT(); mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); uargs = palloc(sizeof(struct user_args)); uargs->tupd = RelationNameGetTupleDesc(BTPAGEITEMS_TYPE); uargs->offset = FirstOffsetNumber; relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); uargs->rel = relation_openrv(relrv, AccessShareLock); CHECK_RELATION_BLOCK_RANGE(uargs->rel, blkno); uargs->buffer = ReadBuffer(uargs->rel, blkno); if (!IS_INDEX(uargs->rel) || !IS_BTREE(uargs->rel)) elog(ERROR, "bt_page_items() can be used only on b-tree index."); uargs->page = BufferGetPage(uargs->buffer); opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page); if (P_ISDELETED(opaque)) elog(NOTICE, "bt_page_items(): this page is deleted."); fctx->max_calls = PageGetMaxOffsetNumber(uargs->page); fctx->user_fctx = uargs; MemoryContextSwitchTo(mctx); } fctx = SRF_PERCALL_SETUP(); uargs = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { IndexTuple itup; id = PageGetItemId(uargs->page, uargs->offset); if (!ItemIdIsValid(id)) elog(ERROR, "Invalid ItemId."); itup = (IndexTuple) PageGetItem(uargs->page, id); { int j = 0; BlockNumber blkno = BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)); values[j] = palloc(32); snprintf(values[j++], 32, "%d", uargs->offset); values[j] = palloc(32); snprintf(values[j++], 32, "(%u,%u)", blkno, itup->t_tid.ip_posid); values[j] = palloc(32); snprintf(values[j++], 32, "%d", (int) IndexTupleSize(itup)); values[j] = palloc(32); snprintf(values[j++], 32, "%c", IndexTupleHasNulls(itup) ? 't' : 'f'); values[j] = palloc(32); snprintf(values[j++], 32, "%c", IndexTupleHasVarwidths(itup) ? 't' : 'f'); { int off; char *dump; char *ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info); dump = palloc(IndexTupleSize(itup) * 3); memset(dump, 0, IndexTupleSize(itup) * 3); for (off = 0; off < IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info); off++) { if (dump[0] == '\0') sprintf(dump, "%02x", *(ptr + off) & 0xff); else { char buf[4]; sprintf(buf, " %02x", *(ptr + off) & 0xff); strcat(dump, buf); } } values[j] = dump; } tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(uargs->tupd), values); result = TupleGetDatum(TupleDescGetSlot(uargs->tupd), tuple); } uargs->offset = uargs->offset + 1; SRF_RETURN_NEXT(fctx, result); } else { ReleaseBuffer(uargs->buffer); relation_close(uargs->rel, AccessShareLock); SRF_RETURN_DONE(fctx); } }
static void PersistentStore_DiagnoseDumpTable( PersistentStoreData *storeData, PersistentStoreSharedData *storeSharedData) { if (disable_persistent_diagnostic_dump) { return; } MIRROREDLOCK_BUFMGR_DECLARE; PersistentStoreScan storeScan; ItemPointerData persistentTid; int64 persistentSerialNum; Datum *values; BlockNumber lastDisplayedBlockNum; bool displayedOne; BlockNumber currentBlockNum; elog(LOG, "Diagnostic dump of persistent table ('%s'): maximum in-use serial number " INT64_FORMAT ", maximum free order number " INT64_FORMAT ", free TID %s, maximum known TID %s", storeData->tableName, storeSharedData->maxInUseSerialNum, storeSharedData->maxFreeOrderNum, ItemPointerToString(&storeSharedData->freeTid), ItemPointerToString2(&storeSharedData->maxTid)); values = (Datum*)palloc(storeData->numAttributes * sizeof(Datum)); PersistentStore_BeginScan( storeData, storeSharedData, &storeScan); lastDisplayedBlockNum = 0; displayedOne = false; while (PersistentStore_GetNext( &storeScan, values, &persistentTid, &persistentSerialNum)) { /* * Use the BlockIdGetBlockNumber routine because ItemPointerGetBlockNumber * asserts for valid TID. */ currentBlockNum = BlockIdGetBlockNumber(&persistentTid.ip_blkid); if (!displayedOne || currentBlockNum != lastDisplayedBlockNum) { Buffer buffer; PageHeader page; XLogRecPtr lsn; /* * Fetch the block and display the LSN. */ // -------- MirroredLock ---------- MIRROREDLOCK_BUFMGR_LOCK; buffer = ReadBuffer( storeScan.persistentRel, currentBlockNum); page = (PageHeader) BufferGetPage(buffer); lsn = PageGetLSN(page); ReleaseBuffer(buffer); MIRROREDLOCK_BUFMGR_UNLOCK; // -------- MirroredLock ---------- elog(LOG, "Diagnostic LSN %s of page %u", XLogLocationToString(&lsn), currentBlockNum); lastDisplayedBlockNum = currentBlockNum; displayedOne = true; } /* * Display the persistent tuple. */ (*storeData->printTupleCallback)( LOG, "DIAGNOSE", &persistentTid, values); } PersistentStore_EndScan(&storeScan); pfree(values); }
void gin_desc(StringInfo buf, uint8 xl_info, char *rec) { uint8 info = xl_info & ~XLR_INFO_MASK; switch (info) { case XLOG_GIN_CREATE_INDEX: appendStringInfoString(buf, "Create index, "); desc_node(buf, *(RelFileNode *) rec, GIN_ROOT_BLKNO); break; case XLOG_GIN_CREATE_PTREE: appendStringInfoString(buf, "Create posting tree, "); desc_node(buf, ((ginxlogCreatePostingTree *) rec)->node, ((ginxlogCreatePostingTree *) rec)->blkno); break; case XLOG_GIN_INSERT: { ginxlogInsert *xlrec = (ginxlogInsert *) rec; char *payload = rec + sizeof(ginxlogInsert); appendStringInfoString(buf, "Insert item, "); desc_node(buf, xlrec->node, xlrec->blkno); appendStringInfo(buf, " isdata: %c isleaf: %c", (xlrec->flags & GIN_INSERT_ISDATA) ? 'T' : 'F', (xlrec->flags & GIN_INSERT_ISLEAF) ? 'T' : 'F'); if (!(xlrec->flags & GIN_INSERT_ISLEAF)) { BlockNumber leftChildBlkno; BlockNumber rightChildBlkno; leftChildBlkno = BlockIdGetBlockNumber((BlockId) payload); payload += sizeof(BlockIdData); rightChildBlkno = BlockIdGetBlockNumber((BlockId) payload); payload += sizeof(BlockNumber); appendStringInfo(buf, " children: %u/%u", leftChildBlkno, rightChildBlkno); } if (!(xlrec->flags & GIN_INSERT_ISDATA)) appendStringInfo(buf, " isdelete: %c", (((ginxlogInsertEntry *) payload)->isDelete) ? 'T' : 'F'); else if (xlrec->flags & GIN_INSERT_ISLEAF) { ginxlogRecompressDataLeaf *insertData = (ginxlogRecompressDataLeaf *) payload; appendStringInfo(buf, " unmodified: %u length: %u (compressed)", insertData->unmodifiedsize, insertData->length); } else { ginxlogInsertDataInternal *insertData = (ginxlogInsertDataInternal *) payload; appendStringInfo(buf, " pitem: %u-%u/%u", PostingItemGetBlockNumber(&insertData->newitem), ItemPointerGetBlockNumber(&insertData->newitem.key), ItemPointerGetOffsetNumber(&insertData->newitem.key)); } } break; case XLOG_GIN_SPLIT: { ginxlogSplit *xlrec = (ginxlogSplit *) rec; appendStringInfoString(buf, "Page split, "); desc_node(buf, ((ginxlogSplit *) rec)->node, ((ginxlogSplit *) rec)->lblkno); appendStringInfo(buf, " isrootsplit: %c", (((ginxlogSplit *) rec)->flags & GIN_SPLIT_ROOT) ? 'T' : 'F'); appendStringInfo(buf, " isdata: %c isleaf: %c", (xlrec->flags & GIN_INSERT_ISDATA) ? 'T' : 'F', (xlrec->flags & GIN_INSERT_ISLEAF) ? 'T' : 'F'); } break; case XLOG_GIN_VACUUM_PAGE: appendStringInfoString(buf, "Vacuum page, "); desc_node(buf, ((ginxlogVacuumPage *) rec)->node, ((ginxlogVacuumPage *) rec)->blkno); break; case XLOG_GIN_VACUUM_DATA_LEAF_PAGE: { ginxlogVacuumDataLeafPage *xlrec = (ginxlogVacuumDataLeafPage *) rec; appendStringInfoString(buf, "Vacuum data leaf page, "); desc_node(buf, xlrec->node, xlrec->blkno); appendStringInfo(buf, " unmodified: %u length: %u", xlrec->data.unmodifiedsize, xlrec->data.length); } break; case XLOG_GIN_DELETE_PAGE: appendStringInfoString(buf, "Delete page, "); desc_node(buf, ((ginxlogDeletePage *) rec)->node, ((ginxlogDeletePage *) rec)->blkno); break; case XLOG_GIN_UPDATE_META_PAGE: appendStringInfoString(buf, "Update metapage, "); desc_node(buf, ((ginxlogUpdateMeta *) rec)->node, GIN_METAPAGE_BLKNO); break; case XLOG_GIN_INSERT_LISTPAGE: appendStringInfoString(buf, "Insert new list page, "); desc_node(buf, ((ginxlogInsertListPage *) rec)->node, ((ginxlogInsertListPage *) rec)->blkno); break; case XLOG_GIN_DELETE_LISTPAGE: appendStringInfo(buf, "Delete list pages (%d), ", ((ginxlogDeleteListPages *) rec)->ndeleted); desc_node(buf, ((ginxlogDeleteListPages *) rec)->node, GIN_METAPAGE_BLKNO); break; default: appendStringInfo(buf, "unknown gin op code %u", info); break; } }
/* * pgstattuple_real * * The real work occurs here */ static Datum pgstattuple_real(Relation rel) { HeapScanDesc scan; HeapTuple tuple; BlockNumber nblocks; BlockNumber block = 0; /* next block to count free space in */ BlockNumber tupblock; Buffer buffer; uint64 table_len; uint64 tuple_len = 0; uint64 dead_tuple_len = 0; uint64 tuple_count = 0; uint64 dead_tuple_count = 0; double tuple_percent; double dead_tuple_percent; uint64 free_space = 0; /* free/reusable space in bytes */ double free_percent; /* free/reusable space in % */ TupleDesc tupdesc; TupleTableSlot *slot; AttInMetadata *attinmeta; char **values; int i; Datum result; /* * Build a tuple description for a pgstattupe_type tuple */ tupdesc = RelationNameGetTupleDesc(DUMMY_TUPLE); /* allocate a slot for a tuple with this tupdesc */ slot = TupleDescGetSlot(tupdesc); /* * Generate attribute metadata needed later to produce tuples from raw * C strings */ attinmeta = TupleDescGetAttInMetadata(tupdesc); nblocks = RelationGetNumberOfBlocks(rel); scan = heap_beginscan(rel, SnapshotAny, 0, NULL); /* scan the relation */ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { uint16 sv_infomask; sv_infomask = tuple->t_data->t_infomask; if (HeapTupleSatisfiesNow(tuple->t_data)) { tuple_len += tuple->t_len; tuple_count++; } else { dead_tuple_len += tuple->t_len; dead_tuple_count++; } if (sv_infomask != tuple->t_data->t_infomask) SetBufferCommitInfoNeedsSave(scan->rs_cbuf); /* * To avoid physically reading the table twice, try to do the * free-space scan in parallel with the heap scan. However, * heap_getnext may find no tuples on a given page, so we cannot * simply examine the pages returned by the heap scan. */ tupblock = BlockIdGetBlockNumber(&tuple->t_self.ip_blkid); while (block <= tupblock) { buffer = ReadBuffer(rel, block); free_space += PageGetFreeSpace((Page) BufferGetPage(buffer)); ReleaseBuffer(buffer); block++; } } heap_endscan(scan); while (block < nblocks) { buffer = ReadBuffer(rel, block); free_space += PageGetFreeSpace((Page) BufferGetPage(buffer)); ReleaseBuffer(buffer); block++; } heap_close(rel, AccessShareLock); table_len = (uint64) nblocks *BLCKSZ; if (nblocks == 0) { tuple_percent = 0.0; dead_tuple_percent = 0.0; free_percent = 0.0; } else { tuple_percent = (double) tuple_len *100.0 / table_len; dead_tuple_percent = (double) dead_tuple_len *100.0 / table_len; free_percent = (double) free_space *100.0 / table_len; } /* * Prepare a values array for storage in our slot. This should be an * array of C strings which will be processed later by the appropriate * "in" functions. */ values = (char **) palloc(NCOLUMNS * sizeof(char *)); for (i = 0; i < NCOLUMNS; i++) values[i] = (char *) palloc(NCHARS * sizeof(char)); i = 0; snprintf(values[i++], NCHARS, INT64_FORMAT, table_len); snprintf(values[i++], NCHARS, INT64_FORMAT, tuple_count); snprintf(values[i++], NCHARS, INT64_FORMAT, tuple_len); snprintf(values[i++], NCHARS, "%.2f", tuple_percent); snprintf(values[i++], NCHARS, INT64_FORMAT, dead_tuple_count); snprintf(values[i++], NCHARS, INT64_FORMAT, dead_tuple_len); snprintf(values[i++], NCHARS, "%.2f", dead_tuple_percent); snprintf(values[i++], NCHARS, INT64_FORMAT, free_space); snprintf(values[i++], NCHARS, "%.2f", free_percent); /* build a tuple */ tuple = BuildTupleFromCStrings(attinmeta, values); /* make the tuple into a datum */ result = TupleGetDatum(slot, tuple); /* Clean up */ for (i = 0; i < NCOLUMNS; i++) pfree(values[i]); pfree(values); return (result); }
static void PersistentStore_DoInsertTuple( PersistentStoreData *storeData, PersistentStoreSharedData *storeSharedData, Relation persistentRel, /* The persistent table relation. */ Datum *values, bool flushToXLog, /* When true, the XLOG record for this change will be flushed to disk. */ ItemPointer persistentTid) /* TID of the stored tuple. */ { bool *nulls; HeapTuple persistentTuple = NULL; XLogRecPtr xlogInsertEndLoc; /* * In order to keep the tuples the exact same size to enable direct reuse of * free tuples, we do not use NULLs. */ nulls = (bool*)palloc0(storeData->numAttributes * sizeof(bool)); /* * Form the tuple. */ persistentTuple = heap_form_tuple(persistentRel->rd_att, values, nulls); if (!HeapTupleIsValid(persistentTuple)) elog(ERROR, "Failed to build persistent tuple ('%s')", storeData->tableName); /* * (We have an exclusive lock (higher up) here so we can direct the insert to the last page.) */ { // Do not assert valid ItemPointer -- it is ok if it is (0,0)... BlockNumber blockNumber = BlockIdGetBlockNumber( &storeSharedData->maxTid.ip_blkid); frozen_heap_insert_directed( persistentRel, persistentTuple, blockNumber); } if (Debug_persistent_store_print) elog(PersistentStore_DebugPrintLevel(), "PersistentStore_DoInsertTuple: old maximum known TID %s, new insert TID %s ('%s')", ItemPointerToString(&storeSharedData->maxTid), ItemPointerToString2(&persistentTuple->t_self), storeData->tableName); if (ItemPointerCompare( &storeSharedData->maxTid, &persistentTuple->t_self) == -1) { // Current max is Less-Than. storeSharedData->maxTid = persistentTuple->t_self; } /* * Return the TID of the INSERT tuple. * Return the XLOG location of the INSERT tuple's XLOG record. */ *persistentTid = persistentTuple->t_self; xlogInsertEndLoc = XLogLastInsertEndLoc(); heap_freetuple(persistentTuple); if (flushToXLog) { XLogFlush(xlogInsertEndLoc); XLogRecPtr_Zero(&nowaitXLogEndLoc); } else nowaitXLogEndLoc = xlogInsertEndLoc; pfree(nulls); }
/* * pgstattuple_real * * The real work occurs here */ static Datum pgstattuple_real(Relation rel, FunctionCallInfo fcinfo) { HeapScanDesc scan; HeapTuple tuple; BlockNumber nblocks; BlockNumber block = 0; /* next block to count free space in */ BlockNumber tupblock; Buffer buffer; uint64 table_len; uint64 tuple_len = 0; uint64 dead_tuple_len = 0; uint64 tuple_count = 0; uint64 dead_tuple_count = 0; double tuple_percent; double dead_tuple_percent; uint64 free_space = 0; /* free/reusable space in bytes */ double free_percent; /* free/reusable space in % */ TupleDesc tupdesc; AttInMetadata *attinmeta; char **values; int i; Datum result; /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); /* make sure we have a persistent copy of the tupdesc */ tupdesc = CreateTupleDescCopy(tupdesc); /* * Generate attribute metadata needed later to produce tuples from raw C * strings */ attinmeta = TupleDescGetAttInMetadata(tupdesc); scan = heap_beginscan(rel, SnapshotAny, 0, NULL); nblocks = scan->rs_nblocks; /* # blocks to be scanned */ /* scan the relation */ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { /* must hold a buffer lock to call HeapTupleSatisfiesNow */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); if (HeapTupleSatisfiesNow(tuple->t_data, scan->rs_cbuf)) { tuple_len += tuple->t_len; tuple_count++; } else { dead_tuple_len += tuple->t_len; dead_tuple_count++; } LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); /* * To avoid physically reading the table twice, try to do the * free-space scan in parallel with the heap scan. However, * heap_getnext may find no tuples on a given page, so we cannot * simply examine the pages returned by the heap scan. */ tupblock = BlockIdGetBlockNumber(&tuple->t_self.ip_blkid); while (block <= tupblock) { buffer = ReadBuffer(rel, block); LockBuffer(buffer, BUFFER_LOCK_SHARE); free_space += PageGetFreeSpace((Page) BufferGetPage(buffer)); LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); block++; } } heap_endscan(scan); while (block < nblocks) { buffer = ReadBuffer(rel, block); free_space += PageGetFreeSpace((Page) BufferGetPage(buffer)); ReleaseBuffer(buffer); block++; } heap_close(rel, AccessShareLock); table_len = (uint64) nblocks *BLCKSZ; if (nblocks == 0) { tuple_percent = 0.0; dead_tuple_percent = 0.0; free_percent = 0.0; } else { tuple_percent = (double) tuple_len *100.0 / table_len; dead_tuple_percent = (double) dead_tuple_len *100.0 / table_len; free_percent = (double) free_space *100.0 / table_len; } /* * Prepare a values array for constructing the tuple. This should be an * array of C strings which will be processed later by the appropriate * "in" functions. */ values = (char **) palloc(NCOLUMNS * sizeof(char *)); for (i = 0; i < NCOLUMNS; i++) values[i] = (char *) palloc(NCHARS * sizeof(char)); i = 0; snprintf(values[i++], NCHARS, INT64_FORMAT, table_len); snprintf(values[i++], NCHARS, INT64_FORMAT, tuple_count); snprintf(values[i++], NCHARS, INT64_FORMAT, tuple_len); snprintf(values[i++], NCHARS, "%.2f", tuple_percent); snprintf(values[i++], NCHARS, INT64_FORMAT, dead_tuple_count); snprintf(values[i++], NCHARS, INT64_FORMAT, dead_tuple_len); snprintf(values[i++], NCHARS, "%.2f", dead_tuple_percent); snprintf(values[i++], NCHARS, INT64_FORMAT, free_space); snprintf(values[i++], NCHARS, "%.2f", free_percent); /* build a tuple */ tuple = BuildTupleFromCStrings(attinmeta, values); /* make the tuple into a datum */ result = HeapTupleGetDatum(tuple); /* Clean up */ for (i = 0; i < NCOLUMNS; i++) pfree(values[i]); pfree(values); return (result); }
void ChangeTracking_GetRelationChangeInfoFromXlog( RmgrId xl_rmid, uint8 xl_info, void *data, RelationChangeInfo *relationChangeInfoArray, int *relationChangeInfoArrayCount, int relationChangeInfoMaxSize) { uint8 info = xl_info & ~XLR_INFO_MASK; uint8 op = 0; MemSet(relationChangeInfoArray, 0, sizeof(RelationChangeInfo) * relationChangeInfoMaxSize); *relationChangeInfoArrayCount = 0; /* * Find the RM for this xlog record and see whether we are * interested in logging it as a buffer pool change or not. */ switch (xl_rmid) { /* * The following changes aren't interesting to the change log */ case RM_XLOG_ID: case RM_CLOG_ID: case RM_MULTIXACT_ID: case RM_XACT_ID: case RM_SMGR_ID: case RM_DBASE_ID: case RM_TBLSPC_ID: case RM_MMXLOG_ID: break; /* * These aren't supported in GPDB */ case RM_HASH_ID: elog(ERROR, "internal error: unsupported RM ID (%d) in ChangeTracking_GetRelationChangeInfoFromXlog", xl_rmid); break; case RM_GIN_ID: /* keep LOG severity till crash recovery or GIN is implemented in order to avoid double failures during cdbfast */ elog(LOG, "internal error: unsupported RM ID (%d) in ChangeTracking_GetRelationChangeInfoFromXlog", xl_rmid); break; /* * The following changes must be logged in the change log. */ case RM_HEAP2_ID: switch (info) { case XLOG_HEAP2_FREEZE: { xl_heap_freeze *xlrec = (xl_heap_freeze *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->heapnode.node), xlrec->block, &xlrec->heapnode.persistentTid, xlrec->heapnode.persistentSerialNum); break; } default: elog(ERROR, "internal error: unsupported RM_HEAP2_ID op (%u) in ChangeTracking_GetRelationChangeInfoFromXlog", info); } break; case RM_HEAP_ID: op = info & XLOG_HEAP_OPMASK; switch (op) { case XLOG_HEAP_INSERT: { xl_heap_insert *xlrec = (xl_heap_insert *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), ItemPointerGetBlockNumber(&(xlrec->target.tid)), &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); break; } case XLOG_HEAP_DELETE: { xl_heap_delete *xlrec = (xl_heap_delete *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), ItemPointerGetBlockNumber(&(xlrec->target.tid)), &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); break; } case XLOG_HEAP_UPDATE: case XLOG_HEAP_MOVE: { xl_heap_update *xlrec = (xl_heap_update *) data; BlockNumber oldblock = ItemPointerGetBlockNumber(&(xlrec->target.tid)); BlockNumber newblock = ItemPointerGetBlockNumber(&(xlrec->newtid)); bool samepage = (oldblock == newblock); ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), newblock, &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); if(!samepage) ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), oldblock, &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); break; } case XLOG_HEAP_CLEAN: { xl_heap_clean *xlrec = (xl_heap_clean *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->heapnode.node), xlrec->block, &xlrec->heapnode.persistentTid, xlrec->heapnode.persistentSerialNum); break; } case XLOG_HEAP_NEWPAGE: { xl_heap_newpage *xlrec = (xl_heap_newpage *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->heapnode.node), xlrec->blkno, &xlrec->heapnode.persistentTid, xlrec->heapnode.persistentSerialNum); break; } case XLOG_HEAP_LOCK: { xl_heap_lock *xlrec = (xl_heap_lock *) data; BlockNumber block = ItemPointerGetBlockNumber(&(xlrec->target.tid)); ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), block, &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); break; } case XLOG_HEAP_INPLACE: { xl_heap_inplace *xlrec = (xl_heap_inplace *) data; BlockNumber block = ItemPointerGetBlockNumber(&(xlrec->target.tid)); ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), block, &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); break; } default: elog(ERROR, "internal error: unsupported RM_HEAP_ID op (%u) in ChangeTracking_GetRelationChangeInfoFromXlog", op); } break; case RM_BTREE_ID: switch (info) { case XLOG_BTREE_INSERT_LEAF: case XLOG_BTREE_INSERT_UPPER: case XLOG_BTREE_INSERT_META: { xl_btree_insert *xlrec = (xl_btree_insert *) data; BlockIdData blkid = xlrec->target.tid.ip_blkid; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), BlockIdGetBlockNumber(&blkid), &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); if(info == XLOG_BTREE_INSERT_META) ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), BTREE_METAPAGE, &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); break; } case XLOG_BTREE_SPLIT_L: case XLOG_BTREE_SPLIT_R: case XLOG_BTREE_SPLIT_L_ROOT: case XLOG_BTREE_SPLIT_R_ROOT: { xl_btree_split *xlrec = (xl_btree_split *) data; BlockIdData blkid = xlrec->target.tid.ip_blkid; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), BlockIdGetBlockNumber(&blkid), &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), xlrec->otherblk, &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); if (xlrec->rightblk != P_NONE) { ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), xlrec->rightblk, &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); } break; } case XLOG_BTREE_DELETE: { xl_btree_delete *xlrec = (xl_btree_delete *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->btreenode.node), xlrec->block, &xlrec->btreenode.persistentTid, xlrec->btreenode.persistentSerialNum); break; } case XLOG_BTREE_DELETE_PAGE: case XLOG_BTREE_DELETE_PAGE_HALF: case XLOG_BTREE_DELETE_PAGE_META: { xl_btree_delete_page *xlrec = (xl_btree_delete_page *) data; BlockIdData blkid = xlrec->target.tid.ip_blkid; BlockNumber block = BlockIdGetBlockNumber(&blkid); if (block != P_NONE) ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), block, &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); if (xlrec->rightblk != P_NONE) ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), xlrec->rightblk, &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); if (xlrec->leftblk != P_NONE) ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), xlrec->leftblk, &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); if (xlrec->deadblk != P_NONE) ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), xlrec->deadblk, &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); if (info == XLOG_BTREE_DELETE_PAGE_META) ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->target.node), BTREE_METAPAGE, &xlrec->target.persistentTid, xlrec->target.persistentSerialNum); break; } case XLOG_BTREE_NEWROOT: { xl_btree_newroot *xlrec = (xl_btree_newroot *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->btreenode.node), xlrec->rootblk, &xlrec->btreenode.persistentTid, xlrec->btreenode.persistentSerialNum); /* newroot always updates the meta page */ ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->btreenode.node), BTREE_METAPAGE, &xlrec->btreenode.persistentTid, xlrec->btreenode.persistentSerialNum); break; } default: elog(ERROR, "internal error: unsupported RM_BTREE_ID op (%u) in ChangeTracking_GetRelationChangeInfoFromXlog", info); } break; case RM_BITMAP_ID: switch (info) { case XLOG_BITMAP_INSERT_NEWLOV: { xl_bm_newpage *xlrec = (xl_bm_newpage *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->bm_node), xlrec->bm_new_blkno, &xlrec->bm_persistentTid, xlrec->bm_persistentSerialNum); break; } case XLOG_BITMAP_INSERT_LOVITEM: { xl_bm_lovitem *xlrec = (xl_bm_lovitem *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->bm_node), xlrec->bm_lov_blkno, &xlrec->bm_persistentTid, xlrec->bm_persistentSerialNum); if (xlrec->bm_is_new_lov_blkno) ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->bm_node), BM_METAPAGE, &xlrec->bm_persistentTid, xlrec->bm_persistentSerialNum); break; } case XLOG_BITMAP_INSERT_META: { xl_bm_metapage *xlrec = (xl_bm_metapage *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->bm_node), BM_METAPAGE, &xlrec->bm_persistentTid, xlrec->bm_persistentSerialNum); break; } case XLOG_BITMAP_INSERT_BITMAP_LASTWORDS: { xl_bm_bitmap_lastwords *xlrec = (xl_bm_bitmap_lastwords *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->bm_node), xlrec->bm_lov_blkno, &xlrec->bm_persistentTid, xlrec->bm_persistentSerialNum); break; } case XLOG_BITMAP_INSERT_WORDS: { xl_bm_bitmapwords *xlrec = (xl_bm_bitmapwords *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->bm_node), xlrec->bm_lov_blkno, &xlrec->bm_persistentTid, xlrec->bm_persistentSerialNum); ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->bm_node), xlrec->bm_blkno, &xlrec->bm_persistentTid, xlrec->bm_persistentSerialNum); if (!xlrec->bm_is_last) ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->bm_node), xlrec->bm_next_blkno, &xlrec->bm_persistentTid, xlrec->bm_persistentSerialNum); break; } case XLOG_BITMAP_UPDATEWORD: { xl_bm_updateword *xlrec = (xl_bm_updateword *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->bm_node), xlrec->bm_blkno, &xlrec->bm_persistentTid, xlrec->bm_persistentSerialNum); break; } case XLOG_BITMAP_UPDATEWORDS: { xl_bm_updatewords *xlrec = (xl_bm_updatewords *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->bm_node), xlrec->bm_first_blkno, &xlrec->bm_persistentTid, xlrec->bm_persistentSerialNum); if (xlrec->bm_two_pages) ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->bm_node), xlrec->bm_second_blkno, &xlrec->bm_persistentTid, xlrec->bm_persistentSerialNum); if (xlrec->bm_new_lastpage) ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->bm_node), xlrec->bm_lov_blkno, &xlrec->bm_persistentTid, xlrec->bm_persistentSerialNum); break; } default: elog(ERROR, "internal error: unsupported RM_BITMAP_ID op (%u) in ChangeTracking_GetRelationChangeInfoFromXlog", info); } break; case RM_SEQ_ID: switch (info) { case XLOG_SEQ_LOG: { xl_seq_rec *xlrec = (xl_seq_rec *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xlrec->node), 0, /* seq_redo touches block 0 only */ &xlrec->persistentTid, xlrec->persistentSerialNum); break; } default: elog(ERROR, "internal error: unsupported RM_SEQ_ID op (%u) in ChangeTracking_GetRelationChangeInfoFromXlog", info); } break; case RM_GIST_ID: switch (info) { case XLOG_GIST_PAGE_UPDATE: case XLOG_GIST_NEW_ROOT: { gistxlogPageUpdate *xldata = (gistxlogPageUpdate *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xldata->node), xldata->blkno, &xldata->persistentTid, xldata->persistentSerialNum); break; } case XLOG_GIST_PAGE_DELETE: { gistxlogPageDelete *xldata = (gistxlogPageDelete *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xldata->node), xldata->blkno, &xldata->persistentTid, xldata->persistentSerialNum); break; } case XLOG_GIST_PAGE_SPLIT: { gistxlogPageSplit* xldata = (gistxlogPageSplit *) data; char* ptr; int j, i = 0; /* first, log the splitted page */ ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xldata->node), xldata->origblkno, &xldata->persistentTid, xldata->persistentSerialNum); /* now log all the pages that we split into */ ptr = (char *)data + sizeof(gistxlogPageSplit); for (i = 0; i < xldata->npage; i++) { gistxlogPage* gistp; gistp = (gistxlogPage *) ptr; ptr += sizeof(gistxlogPage); //elog(LOG, "CHANGETRACKING GIST SPLIT: block [%d/%d]:%d", i+1,xldata->npage, gistp->blkno); ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xldata->node), gistp->blkno, &xldata->persistentTid, xldata->persistentSerialNum); /* skip over all index tuples. we only care about block numbers */ j = 0; while (j < gistp->num) { ptr += IndexTupleSize((IndexTuple) ptr); j++; } } break; } case XLOG_GIST_CREATE_INDEX: { gistxlogCreateIndex* xldata = (gistxlogCreateIndex *) data; ChangeTracking_AddRelationChangeInfo( relationChangeInfoArray, relationChangeInfoArrayCount, relationChangeInfoMaxSize, &(xldata->node), GIST_ROOT_BLKNO, &xldata->persistentTid, xldata->persistentSerialNum); break; } case XLOG_GIST_INSERT_COMPLETE: { /* nothing to be done here */ break; } default: elog(ERROR, "internal error: unsupported RM_GIST_ID op (%u) in ChangeTracking_GetRelationChangeInfoFromXlog", info); } break; default: elog(ERROR, "internal error: unsupported resource manager type (%d) in ChangeTracking_GetRelationChangeInfoFromXlog", xl_rmid); } }