/* ---------------- * ExecInitNullTupleSlot * * Build a slot containing an all-nulls tuple of the given type. * This is used as a substitute for an input tuple when performing an * outer join. * ---------------- */ TupleTableSlot * ExecInitNullTupleSlot(EState *estate, TupleDesc tupType) { TupleTableSlot *slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupType); return ExecStoreAllNullTuple(slot); }
/* * Use the supplied ResultRelInfo to create an appropriately restructured * version of the tuple in the supplied slot, if necessary. * * slot -- slot containing the input tuple * resultRelInfo -- info pertaining to the target part of an insert * * If no restructuring is required, the result is the argument slot, else * it is the slot from the argument result info updated to hold the * restructured tuple. */ TupleTableSlot * reconstructMatchingTupleSlot(TupleTableSlot *slot, ResultRelInfo *resultRelInfo) { int natts; Datum *values; bool *isnull; AttrMap *map; TupleTableSlot *partslot; Datum *partvalues; bool *partisnull; map = resultRelInfo->ri_partInsertMap; TupleDesc inputTupDesc = slot->tts_tupleDescriptor; TupleDesc resultTupDesc = resultRelInfo->ri_RelationDesc->rd_att; bool tupleDescMatch = (resultRelInfo->tupdesc_match == 1); if (resultRelInfo->tupdesc_match == 0) { tupleDescMatch = equalTupleDescs(inputTupDesc, resultTupDesc, false); if (tupleDescMatch) { resultRelInfo->tupdesc_match = 1; } else { resultRelInfo->tupdesc_match = -1; } } /* No map and matching tuple descriptor means no restructuring needed. */ if (map == NULL && tupleDescMatch) return slot; /* Put the given tuple into attribute arrays. */ natts = slot->tts_tupleDescriptor->natts; slot_getallattrs(slot); values = slot_get_values(slot); isnull = slot_get_isnull(slot); /* * Get the target slot ready. If this is a child partition table, * set target slot to ri_partSlot. Otherwise, use ri_resultSlot. */ if (map != NULL) { Assert(resultRelInfo->ri_partSlot != NULL); partslot = resultRelInfo->ri_partSlot; } else { if (resultRelInfo->ri_resultSlot == NULL) { resultRelInfo->ri_resultSlot = MakeSingleTupleTableSlot(resultTupDesc); } partslot = resultRelInfo->ri_resultSlot; } partslot = ExecStoreAllNullTuple(partslot); partvalues = slot_get_values(partslot); partisnull = slot_get_isnull(partslot); /* Restructure the input tuple. Non-zero map entries are attribute * numbers in the target tuple, however, not every attribute * number of the input tuple need be present. In particular, * attribute numbers corresponding to dropped attributes will be * missing. */ reconstructTupleValues(map, values, isnull, natts, partvalues, partisnull, partslot->tts_tupleDescriptor->natts); partslot = ExecStoreVirtualTuple(partslot); return partslot; }
/* ---------------------------------------------------------------- * BitmapHeapNext * * Retrieve next tuple from the BitmapHeapScan node's currentRelation * ---------------------------------------------------------------- */ static TupleTableSlot * BitmapHeapNext(BitmapHeapScanState *node) { ExprContext *econtext; HeapScanDesc scan; TIDBitmap *tbm; TBMIterator *tbmiterator = NULL; TBMSharedIterator *shared_tbmiterator = NULL; TBMIterateResult *tbmres; OffsetNumber targoffset; TupleTableSlot *slot; ParallelBitmapHeapState *pstate = node->pstate; dsa_area *dsa = node->ss.ps.state->es_query_dsa; /* * extract necessary information from index scan node */ econtext = node->ss.ps.ps_ExprContext; slot = node->ss.ss_ScanTupleSlot; scan = node->ss.ss_currentScanDesc; tbm = node->tbm; if (pstate == NULL) tbmiterator = node->tbmiterator; else shared_tbmiterator = node->shared_tbmiterator; tbmres = node->tbmres; /* * If we haven't yet performed the underlying index scan, do it, and begin * the iteration over the bitmap. * * For prefetching, we use *two* iterators, one for the pages we are * actually scanning and another that runs ahead of the first for * prefetching. node->prefetch_pages tracks exactly how many pages ahead * the prefetch iterator is. Also, node->prefetch_target tracks the * desired prefetch distance, which starts small and increases up to the * node->prefetch_maximum. This is to avoid doing a lot of prefetching in * a scan that stops after a few tuples because of a LIMIT. */ if (!node->initialized) { if (!pstate) { tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(node)); if (!tbm || !IsA(tbm, TIDBitmap)) elog(ERROR, "unrecognized result from subplan"); node->tbm = tbm; node->tbmiterator = tbmiterator = tbm_begin_iterate(tbm); node->tbmres = tbmres = NULL; #ifdef USE_PREFETCH if (node->prefetch_maximum > 0) { node->prefetch_iterator = tbm_begin_iterate(tbm); node->prefetch_pages = 0; node->prefetch_target = -1; } #endif /* USE_PREFETCH */ } else { /* * The leader will immediately come out of the function, but * others will be blocked until leader populates the TBM and wakes * them up. */ if (BitmapShouldInitializeSharedState(pstate)) { tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(node)); if (!tbm || !IsA(tbm, TIDBitmap)) elog(ERROR, "unrecognized result from subplan"); node->tbm = tbm; /* * Prepare to iterate over the TBM. This will return the * dsa_pointer of the iterator state which will be used by * multiple processes to iterate jointly. */ pstate->tbmiterator = tbm_prepare_shared_iterate(tbm); #ifdef USE_PREFETCH if (node->prefetch_maximum > 0) { pstate->prefetch_iterator = tbm_prepare_shared_iterate(tbm); /* * We don't need the mutex here as we haven't yet woke up * others. */ pstate->prefetch_pages = 0; pstate->prefetch_target = -1; } #endif /* We have initialized the shared state so wake up others. */ BitmapDoneInitializingSharedState(pstate); } /* Allocate a private iterator and attach the shared state to it */ node->shared_tbmiterator = shared_tbmiterator = tbm_attach_shared_iterate(dsa, pstate->tbmiterator); node->tbmres = tbmres = NULL; #ifdef USE_PREFETCH if (node->prefetch_maximum > 0) { node->shared_prefetch_iterator = tbm_attach_shared_iterate(dsa, pstate->prefetch_iterator); } #endif /* USE_PREFETCH */ } node->initialized = true; } for (;;) { Page dp; ItemId lp; CHECK_FOR_INTERRUPTS(); /* * Get next page of results if needed */ if (tbmres == NULL) { if (!pstate) node->tbmres = tbmres = tbm_iterate(tbmiterator); else node->tbmres = tbmres = tbm_shared_iterate(shared_tbmiterator); if (tbmres == NULL) { /* no more entries in the bitmap */ break; } BitmapAdjustPrefetchIterator(node, tbmres); /* * Ignore any claimed entries past what we think is the end of the * relation. (This is probably not necessary given that we got at * least AccessShareLock on the table before performing any of the * indexscans, but let's be safe.) */ if (tbmres->blockno >= scan->rs_nblocks) { node->tbmres = tbmres = NULL; continue; } /* * We can skip fetching the heap page if we don't need any fields * from the heap, and the bitmap entries don't need rechecking, * and all tuples on the page are visible to our transaction. */ node->skip_fetch = (node->can_skip_fetch && !tbmres->recheck && VM_ALL_VISIBLE(node->ss.ss_currentRelation, tbmres->blockno, &node->vmbuffer)); if (node->skip_fetch) { /* * The number of tuples on this page is put into * scan->rs_ntuples; note we don't fill scan->rs_vistuples. */ scan->rs_ntuples = tbmres->ntuples; } else { /* * Fetch the current heap page and identify candidate tuples. */ bitgetpage(scan, tbmres); } if (tbmres->ntuples >= 0) node->exact_pages++; else node->lossy_pages++; /* * Set rs_cindex to first slot to examine */ scan->rs_cindex = 0; /* Adjust the prefetch target */ BitmapAdjustPrefetchTarget(node); } else { /* * Continuing in previously obtained page; advance rs_cindex */ scan->rs_cindex++; #ifdef USE_PREFETCH /* * Try to prefetch at least a few pages even before we get to the * second page if we don't stop reading after the first tuple. */ if (!pstate) { if (node->prefetch_target < node->prefetch_maximum) node->prefetch_target++; } else if (pstate->prefetch_target < node->prefetch_maximum) { /* take spinlock while updating shared state */ SpinLockAcquire(&pstate->mutex); if (pstate->prefetch_target < node->prefetch_maximum) pstate->prefetch_target++; SpinLockRelease(&pstate->mutex); } #endif /* USE_PREFETCH */ } /* * Out of range? If so, nothing more to look at on this page */ if (scan->rs_cindex < 0 || scan->rs_cindex >= scan->rs_ntuples) { node->tbmres = tbmres = NULL; continue; } /* * We issue prefetch requests *after* fetching the current page to try * to avoid having prefetching interfere with the main I/O. Also, this * should happen only when we have determined there is still something * to do on the current page, else we may uselessly prefetch the same * page we are just about to request for real. */ BitmapPrefetch(node, scan); if (node->skip_fetch) { /* * If we don't have to fetch the tuple, just return nulls. */ ExecStoreAllNullTuple(slot); } else { /* * Okay to fetch the tuple. */ targoffset = scan->rs_vistuples[scan->rs_cindex]; dp = (Page) BufferGetPage(scan->rs_cbuf); lp = PageGetItemId(dp, targoffset); Assert(ItemIdIsNormal(lp)); scan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp); scan->rs_ctup.t_len = ItemIdGetLength(lp); scan->rs_ctup.t_tableOid = scan->rs_rd->rd_id; ItemPointerSet(&scan->rs_ctup.t_self, tbmres->blockno, targoffset); pgstat_count_heap_fetch(scan->rs_rd); /* * Set up the result slot to point to this tuple. Note that the * slot acquires a pin on the buffer. */ ExecStoreBufferHeapTuple(&scan->rs_ctup, slot, scan->rs_cbuf); /* * If we are using lossy info, we have to recheck the qual * conditions at every tuple. */ if (tbmres->recheck) { econtext->ecxt_scantuple = slot; if (!ExecQualAndReset(node->bitmapqualorig, econtext)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); ExecClearTuple(slot); continue; } } } /* OK to return this tuple */ return slot; } /* * if we get here it means we are at the end of the scan.. */ return ExecClearTuple(slot); }