static void AOCSMoveTuple(TupleTableSlot *slot, AOCSInsertDesc insertDesc, ResultRelInfo *resultRelInfo, EState *estate) { AOTupleId *oldAoTupleId; AOTupleId newAoTupleId; Assert(resultRelInfo); Assert(slot); Assert(estate); oldAoTupleId = (AOTupleId *) slot_get_ctid(slot); /* Extract all the values of the tuple */ slot_getallattrs(slot); (void) aocs_insert_values(insertDesc, slot_get_values(slot), slot_get_isnull(slot), &newAoTupleId); /* insert index' tuples if needed */ if (resultRelInfo->ri_NumIndices > 0) { ExecInsertIndexTuples(slot, (ItemPointer) &newAoTupleId, estate); ResetPerTupleExprContext(estate); } elogif(Debug_appendonly_print_compaction, DEBUG5, "Compaction: Moved tuple (%d," INT64_FORMAT ") -> (%d," INT64_FORMAT ")", AOTupleIdGet_segmentFileNum(oldAoTupleId), AOTupleIdGet_rowNum(oldAoTupleId), AOTupleIdGet_segmentFileNum(&newAoTupleId), AOTupleIdGet_rowNum(&newAoTupleId)); }
void AppendOnlyThrowAwayTuple( Relation rel, MemTuple tuple, TupleTableSlot *slot, MemTupleBinding *mt_bind) { AOTupleId *oldAoTupleId; Assert(slot); Assert(mt_bind); oldAoTupleId = (AOTupleId*)slot_get_ctid(slot); /* Extract all the values of the tuple */ slot_getallattrs(slot); if (MemTupleHasExternal(tuple, mt_bind)) { toast_delete(rel, (HeapTuple) tuple, mt_bind); } elogif(Debug_appendonly_print_compaction, DEBUG5, "Compaction: Throw away tuple (%d," INT64_FORMAT ")", AOTupleIdGet_segmentFileNum(oldAoTupleId), AOTupleIdGet_rowNum(oldAoTupleId)); }
static void AppendOnlyMoveTuple(MemTuple tuple, TupleTableSlot *slot, MemTupleBinding *mt_bind, AppendOnlyInsertDesc insertDesc, ResultRelInfo *resultRelInfo, EState *estate) { AOTupleId *oldAoTupleId; Oid tupleOid; AOTupleId newAoTupleId; Assert(resultRelInfo); Assert(slot); Assert(mt_bind); Assert(estate); oldAoTupleId = (AOTupleId*)slot_get_ctid(slot); /* Extract all the values of the tuple */ slot_getallattrs(slot); tupleOid = MemTupleGetOid(tuple, mt_bind); appendonly_insert(insertDesc, tuple, &tupleOid, &newAoTupleId); /* insert index' tuples if needed */ if (resultRelInfo->ri_NumIndices > 0) { ExecInsertIndexTuples(slot, (ItemPointer)&newAoTupleId, estate, true); ResetPerTupleExprContext(estate); } elogif(Debug_appendonly_print_compaction, DEBUG5, "Compaction: Moved tuple (%d," INT64_FORMAT ") -> (%d," INT64_FORMAT ")", AOTupleIdGet_segmentFileNum(oldAoTupleId), AOTupleIdGet_rowNum(oldAoTupleId), AOTupleIdGet_segmentFileNum(&newAoTupleId), AOTupleIdGet_rowNum(&newAoTupleId)); }
/* * AppendOnlyBlockDirectory_GetEntry * * Find a directory entry for the given AOTupleId in the block directory. * If such an entry is found, return true. Otherwise, return false. * * The range for directoryEntry is assigned accordingly in this function. * * The block directory for the appendonly table should exist before calling * this function. */ bool AppendOnlyBlockDirectory_GetEntry( AppendOnlyBlockDirectory *blockDirectory, AOTupleId *aoTupleId, int columnGroupNo, AppendOnlyBlockDirectoryEntry *directoryEntry) { int segmentFileNum = AOTupleIdGet_segmentFileNum(aoTupleId); int64 rowNum = AOTupleIdGet_rowNum(aoTupleId); int i; Relation blkdirRel = blockDirectory->blkdirRel; Relation blkdirIdx = blockDirectory->blkdirIdx; int numScanKeys = blockDirectory->numScanKeys; ScanKey scanKeys = blockDirectory->scanKeys; TupleDesc heapTupleDesc; FileSegInfo *fsInfo = NULL; IndexScanDesc idxScanDesc; HeapTuple tuple = NULL; MinipagePerColumnGroup *minipageInfo = &blockDirectory->minipages[columnGroupNo]; int entry_no = -1; int tmpGroupNo; if (blkdirRel == NULL || blkdirIdx == NULL) { Assert(RelationIsValid(blockDirectory->aoRel)); ereport(ERROR, (errcode(ERRCODE_GP_INTERNAL_ERROR), errmsg("Block directory for append-only relation '%s' does not exist", RelationGetRelationName(blockDirectory->aoRel)))); return false; } ereportif(Debug_appendonly_print_blockdirectory, LOG, (errmsg("Append-only block directory get entry: " "(columnGroupNo, segmentFileNum, rowNum) = " "(%d, %d, " INT64_FORMAT ")", columnGroupNo, segmentFileNum, rowNum))); /* * If the segment file number is the same as * blockDirectory->currentSegmentFileNum, the in-memory minipage * may contain such an entry. We search the in-memory minipage * first. If such an entry can not be found, we search for the * appropriate minipage by using the block directory btree index. */ if (segmentFileNum == blockDirectory->currentSegmentFileNum && minipageInfo->numMinipageEntries > 0) { Assert(blockDirectory->currentSegmentFileInfo != NULL); MinipageEntry *firstentry = &minipageInfo->minipage->entry[0]; if (rowNum >= firstentry->firstRowNum) { /* * Check if the existing minipage contains the requested * rowNum. If so, just get it. */ entry_no = find_minipage_entry(minipageInfo->minipage, minipageInfo->numMinipageEntries, rowNum); if (entry_no != -1) { return set_directoryentry_range(blockDirectory, columnGroupNo, entry_no, directoryEntry); } /* * The given rowNum may point to a tuple that does not exist * in the AO table any more, either because of cancellation of * an insert, or due to crashes during an insert. If this is * the case, rowNum is smaller than the highest entry in * the in-memory minipage entry. */ else { MinipageEntry *entry = &minipageInfo->minipage->entry[minipageInfo->numMinipageEntries - 1]; if (rowNum < entry->firstRowNum + entry->rowCount - 1) return false; } } } for (i = 0; i < blockDirectory->totalSegfiles; i++) { fsInfo = blockDirectory->segmentFileInfo[i]; if (!blockDirectory->isAOCol && segmentFileNum == fsInfo->segno) break; else if (blockDirectory->isAOCol && segmentFileNum == ((AOCSFileSegInfo*)fsInfo)->segno) break; } Assert(fsInfo != NULL); /* * Search the btree index to find the minipage that contains * the rowNum. We find the minipages for all column groups, since * currently we will need to access all columns at the same time. */ heapTupleDesc = RelationGetDescr(blkdirRel); Assert(numScanKeys == 3); for (tmpGroupNo = 0; tmpGroupNo < blockDirectory->numColumnGroups; tmpGroupNo++) { if (blockDirectory->proj && !blockDirectory->proj[tmpGroupNo]) { /* Ignore columns that are not projected. */ continue; } /* Setup the scan keys for the scan. */ Assert(scanKeys != NULL); scanKeys[0].sk_argument = Int32GetDatum(segmentFileNum); scanKeys[1].sk_argument = Int32GetDatum(tmpGroupNo); scanKeys[2].sk_argument = Int64GetDatum(rowNum); idxScanDesc = index_beginscan(blkdirRel, blkdirIdx, blockDirectory->appendOnlyMetaDataSnapshot, numScanKeys, scanKeys); tuple = index_getnext(idxScanDesc, BackwardScanDirection); if (tuple != NULL) { /* * MPP-17061: we need to update currentSegmentFileNum * & currentSegmentFileInfo at the same time when we * load the minipage for the block directory entry we * found, otherwise we would risk having inconsistency * between currentSegmentFileNum/currentSegmentFileInfo * and minipage contents, which would cause wrong block * header offset being returned in following block * directory entry look up. */ blockDirectory->currentSegmentFileNum = segmentFileNum; blockDirectory->currentSegmentFileInfo = fsInfo; extract_minipage(blockDirectory, tuple, heapTupleDesc, tmpGroupNo); } else { /* MPP-17061: index look up failed, row is invisible */ index_endscan(idxScanDesc); return false; } index_endscan(idxScanDesc); } { MinipagePerColumnGroup *minipageInfo; minipageInfo = &blockDirectory->minipages[columnGroupNo]; /* * Perform a binary search over the minipage to find * the entry about the AO block. */ entry_no = find_minipage_entry(minipageInfo->minipage, minipageInfo->numMinipageEntries, rowNum); /* If there are no entries, return false. */ if (entry_no == -1 && minipageInfo->numMinipageEntries == 0) return false; if (entry_no == -1) { /* * Since the last few blocks may not be logged in the block * directory, we always use the last entry. */ entry_no = minipageInfo->numMinipageEntries - 1; } return set_directoryentry_range(blockDirectory, columnGroupNo, entry_no, directoryEntry); } return false; }
static Datum gp_aovisimap_internal(PG_FUNCTION_ARGS, Oid aoRelOid) { Datum values[3]; bool nulls[3]; HeapTuple tuple; Datum result; typedef struct Context { Relation aorel; AppendOnlyVisimapScan visiMapScan; AOTupleId aoTupleId; } Context; FuncCallContext *funcctx; Context *context; if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; MemoryContext oldcontext; /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); /* * switch to memory context appropriate for multiple function * calls */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /* build tupdesc for result tuples */ tupdesc = CreateTemplateTupleDesc(3, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tid", TIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "segno", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "row_num", INT8OID, -1, 0); funcctx->tuple_desc = BlessTupleDesc(tupdesc); /* * Collect all the locking information that we will format and send * out as a result set. */ context = (Context *) palloc0(sizeof(Context)); context->aorel = heap_open(aoRelOid, AccessShareLock); if (!(RelationIsAoRows(context->aorel) || RelationIsAoCols(context->aorel))) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Function not supported on relation"))); } AppendOnlyVisimapScan_Init(&context->visiMapScan, context->aorel->rd_appendonly->visimaprelid, context->aorel->rd_appendonly->visimapidxid, AccessShareLock, SnapshotNow); AOTupleIdInit_Init(&context->aoTupleId); funcctx->user_fctx = (void *) context; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); context = (Context *) funcctx->user_fctx; while (true) { if (!AppendOnlyVisimapScan_GetNextInvisible( &context->visiMapScan, &context->aoTupleId)) { break; } MemSet(values, 0, sizeof(values)); MemSet(nulls, false, sizeof(nulls)); values[0] = ItemPointerGetDatum((ItemPointer)&context->aoTupleId); values[1] = Int32GetDatum(AOTupleIdGet_segmentFileNum(&context->aoTupleId)); values[2] = Int64GetDatum(AOTupleIdGet_rowNum(&context->aoTupleId)); tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } AppendOnlyVisimapScan_Finish(&context->visiMapScan, AccessShareLock); heap_close(context->aorel, AccessShareLock); pfree(context); funcctx->user_fctx = NULL; SRF_RETURN_DONE(funcctx); }