Header And Logo

PostgreSQL
| The world's most advanced open source database.

Functions

nodeIndexonlyscan.c File Reference

#include "postgres.h"
#include "access/relscan.h"
#include "access/visibilitymap.h"
#include "executor/execdebug.h"
#include "executor/nodeIndexonlyscan.h"
#include "executor/nodeIndexscan.h"
#include "storage/bufmgr.h"
#include "storage/predicate.h"
#include "utils/memutils.h"
#include "utils/rel.h"
Include dependency graph for nodeIndexonlyscan.c:

Go to the source code of this file.

Functions

static TupleTableSlotIndexOnlyNext (IndexOnlyScanState *node)
static void StoreIndexTuple (TupleTableSlot *slot, IndexTuple itup, TupleDesc itupdesc)
static bool IndexOnlyRecheck (IndexOnlyScanState *node, TupleTableSlot *slot)
TupleTableSlotExecIndexOnlyScan (IndexOnlyScanState *node)
void ExecReScanIndexOnlyScan (IndexOnlyScanState *node)
void ExecEndIndexOnlyScan (IndexOnlyScanState *node)
void ExecIndexOnlyMarkPos (IndexOnlyScanState *node)
void ExecIndexOnlyRestrPos (IndexOnlyScanState *node)
IndexOnlyScanStateExecInitIndexOnlyScan (IndexOnlyScan *node, EState *estate, int eflags)

Function Documentation

void ExecEndIndexOnlyScan ( IndexOnlyScanState node  ) 

Definition at line 283 of file nodeIndexonlyscan.c.

References ExecClearTuple(), ExecCloseScanRelation(), ExecFreeExprContext(), FreeExprContext(), index_close(), index_endscan(), InvalidBuffer, IndexOnlyScanState::ioss_RelationDesc, IndexOnlyScanState::ioss_RuntimeContext, IndexOnlyScanState::ioss_ScanDesc, IndexOnlyScanState::ioss_VMBuffer, NoLock, ScanState::ps, PlanState::ps_ResultTupleSlot, ReleaseBuffer(), IndexOnlyScanState::ss, ScanState::ss_currentRelation, and ScanState::ss_ScanTupleSlot.

Referenced by ExecEndNode().

{
    Relation    indexRelationDesc;
    IndexScanDesc indexScanDesc;
    Relation    relation;

    /*
     * extract information from the node
     */
    indexRelationDesc = node->ioss_RelationDesc;
    indexScanDesc = node->ioss_ScanDesc;
    relation = node->ss.ss_currentRelation;

    /* Release VM buffer pin, if any. */
    if (node->ioss_VMBuffer != InvalidBuffer)
    {
        ReleaseBuffer(node->ioss_VMBuffer);
        node->ioss_VMBuffer = InvalidBuffer;
    }

    /*
     * Free the exprcontext(s) ... now dead code, see ExecFreeExprContext
     */
#ifdef NOT_USED
    ExecFreeExprContext(&node->ss.ps);
    if (node->ioss_RuntimeContext)
        FreeExprContext(node->ioss_RuntimeContext, true);
#endif

    /*
     * clear out tuple table slots
     */
    ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
    ExecClearTuple(node->ss.ss_ScanTupleSlot);

    /*
     * close the index relation (no-op if we didn't open it)
     */
    if (indexScanDesc)
        index_endscan(indexScanDesc);
    if (indexRelationDesc)
        index_close(indexRelationDesc, NoLock);

    /*
     * close the heap relation.
     */
    ExecCloseScanRelation(relation);
}

void ExecIndexOnlyMarkPos ( IndexOnlyScanState node  ) 

Definition at line 337 of file nodeIndexonlyscan.c.

References index_markpos(), and IndexOnlyScanState::ioss_ScanDesc.

Referenced by ExecMarkPos().

void ExecIndexOnlyRestrPos ( IndexOnlyScanState node  ) 

Definition at line 347 of file nodeIndexonlyscan.c.

References index_restrpos(), and IndexOnlyScanState::ioss_ScanDesc.

Referenced by ExecRestrPos().

TupleTableSlot* ExecIndexOnlyScan ( IndexOnlyScanState node  ) 

Definition at line 224 of file nodeIndexonlyscan.c.

References ExecReScan(), ExecScan(), IndexOnlyNext(), IndexOnlyRecheck(), IndexOnlyScanState::ioss_NumRuntimeKeys, and IndexOnlyScanState::ioss_RuntimeKeysReady.

Referenced by ExecProcNode().

{
    /*
     * If we have runtime keys and they've not already been set up, do it now.
     */
    if (node->ioss_NumRuntimeKeys != 0 && !node->ioss_RuntimeKeysReady)
        ExecReScan((PlanState *) node);

    return ExecScan(&node->ss,
                    (ExecScanAccessMtd) IndexOnlyNext,
                    (ExecScanRecheckMtd) IndexOnlyRecheck);
}

IndexOnlyScanState* ExecInitIndexOnlyScan ( IndexOnlyScan node,
EState estate,
int  eflags 
)

Definition at line 364 of file nodeIndexonlyscan.c.

References AccessShareLock, EState::es_snapshot, EXEC_FLAG_EXPLAIN_ONLY, ExecAssignExprContext(), ExecAssignResultTypeFromTL(), ExecAssignScanProjectionInfo(), ExecAssignScanType(), ExecIndexBuildScanKeys(), ExecInitExpr(), ExecInitResultTupleSlot(), ExecInitScanTupleSlot(), ExecOpenScanRelation(), ExecRelationIsTargetRelation(), ExecTypeFromTL(), index_beginscan(), index_open(), index_rescan(), IndexOnlyScan::indexid, IndexOnlyScan::indexorderby, IndexOnlyScan::indexqual, IndexOnlyScanState::indexqual, IndexOnlyScan::indextlist, IndexOnlyScanState::ioss_HeapFetches, IndexOnlyScanState::ioss_NumRuntimeKeys, IndexOnlyScanState::ioss_RelationDesc, IndexOnlyScanState::ioss_RuntimeKeys, IndexOnlyScanState::ioss_RuntimeKeysReady, makeNode, NoLock, NULL, Scan::plan, PlanState::plan, ScanState::ps, PlanState::ps_ExprContext, PlanState::ps_TupFromTlist, Plan::qual, PlanState::qual, IndexOnlyScan::scan, Scan::scanrelid, IndexOnlyScanState::ss, ScanState::ss_currentRelation, ScanState::ss_currentScanDesc, PlanState::state, Plan::targetlist, and PlanState::targetlist.

Referenced by ExecInitNode().

{
    IndexOnlyScanState *indexstate;
    Relation    currentRelation;
    bool        relistarget;
    TupleDesc   tupDesc;

    /*
     * create state structure
     */
    indexstate = makeNode(IndexOnlyScanState);
    indexstate->ss.ps.plan = (Plan *) node;
    indexstate->ss.ps.state = estate;
    indexstate->ioss_HeapFetches = 0;

    /*
     * Miscellaneous initialization
     *
     * create expression context for node
     */
    ExecAssignExprContext(estate, &indexstate->ss.ps);

    indexstate->ss.ps.ps_TupFromTlist = false;

    /*
     * initialize child expressions
     *
     * Note: we don't initialize all of the indexorderby expression, only the
     * sub-parts corresponding to runtime keys (see below).
     */
    indexstate->ss.ps.targetlist = (List *)
        ExecInitExpr((Expr *) node->scan.plan.targetlist,
                     (PlanState *) indexstate);
    indexstate->ss.ps.qual = (List *)
        ExecInitExpr((Expr *) node->scan.plan.qual,
                     (PlanState *) indexstate);
    indexstate->indexqual = (List *)
        ExecInitExpr((Expr *) node->indexqual,
                     (PlanState *) indexstate);

    /*
     * tuple table initialization
     */
    ExecInitResultTupleSlot(estate, &indexstate->ss.ps);
    ExecInitScanTupleSlot(estate, &indexstate->ss);

    /*
     * open the base relation and acquire appropriate lock on it.
     */
    currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);

    indexstate->ss.ss_currentRelation = currentRelation;
    indexstate->ss.ss_currentScanDesc = NULL;   /* no heap scan here */

    /*
     * Build the scan tuple type using the indextlist generated by the
     * planner.  We use this, rather than the index's physical tuple
     * descriptor, because the latter contains storage column types not the
     * types of the original datums.  (It's the AM's responsibility to return
     * suitable data anyway.)
     */
    tupDesc = ExecTypeFromTL(node->indextlist, false);
    ExecAssignScanType(&indexstate->ss, tupDesc);

    /*
     * Initialize result tuple type and projection info.
     */
    ExecAssignResultTypeFromTL(&indexstate->ss.ps);
    ExecAssignScanProjectionInfo(&indexstate->ss);

    /*
     * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
     * here.  This allows an index-advisor plugin to EXPLAIN a plan containing
     * references to nonexistent indexes.
     */
    if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
        return indexstate;

    /*
     * Open the index relation.
     *
     * If the parent table is one of the target relations of the query, then
     * InitPlan already opened and write-locked the index, so we can avoid
     * taking another lock here.  Otherwise we need a normal reader's lock.
     */
    relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid);
    indexstate->ioss_RelationDesc = index_open(node->indexid,
                                     relistarget ? NoLock : AccessShareLock);

    /*
     * Initialize index-specific scan state
     */
    indexstate->ioss_RuntimeKeysReady = false;
    indexstate->ioss_RuntimeKeys = NULL;
    indexstate->ioss_NumRuntimeKeys = 0;

    /*
     * build the index scan keys from the index qualification
     */
    ExecIndexBuildScanKeys((PlanState *) indexstate,
                           indexstate->ioss_RelationDesc,
                           node->indexqual,
                           false,
                           &indexstate->ioss_ScanKeys,
                           &indexstate->ioss_NumScanKeys,
                           &indexstate->ioss_RuntimeKeys,
                           &indexstate->ioss_NumRuntimeKeys,
                           NULL,    /* no ArrayKeys */
                           NULL);

    /*
     * any ORDER BY exprs have to be turned into scankeys in the same way
     */
    ExecIndexBuildScanKeys((PlanState *) indexstate,
                           indexstate->ioss_RelationDesc,
                           node->indexorderby,
                           true,
                           &indexstate->ioss_OrderByKeys,
                           &indexstate->ioss_NumOrderByKeys,
                           &indexstate->ioss_RuntimeKeys,
                           &indexstate->ioss_NumRuntimeKeys,
                           NULL,    /* no ArrayKeys */
                           NULL);

    /*
     * If we have runtime keys, we need an ExprContext to evaluate them. The
     * node's standard context won't do because we want to reset that context
     * for every tuple.  So, build another context just like the other one...
     * -tgl 7/11/00
     */
    if (indexstate->ioss_NumRuntimeKeys != 0)
    {
        ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;

        ExecAssignExprContext(estate, &indexstate->ss.ps);
        indexstate->ioss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
        indexstate->ss.ps.ps_ExprContext = stdecontext;
    }
    else
    {
        indexstate->ioss_RuntimeContext = NULL;
    }

    /*
     * Initialize scan descriptor.
     */
    indexstate->ioss_ScanDesc = index_beginscan(currentRelation,
                                                indexstate->ioss_RelationDesc,
                                                estate->es_snapshot,
                                                indexstate->ioss_NumScanKeys,
                                            indexstate->ioss_NumOrderByKeys);

    /* Set it up for index-only scan */
    indexstate->ioss_ScanDesc->xs_want_itup = true;
    indexstate->ioss_VMBuffer = InvalidBuffer;

    /*
     * If no run-time keys to calculate, go ahead and pass the scankeys to the
     * index AM.
     */
    if (indexstate->ioss_NumRuntimeKeys == 0)
        index_rescan(indexstate->ioss_ScanDesc,
                     indexstate->ioss_ScanKeys,
                     indexstate->ioss_NumScanKeys,
                     indexstate->ioss_OrderByKeys,
                     indexstate->ioss_NumOrderByKeys);

    /*
     * all done.
     */
    return indexstate;
}

void ExecReScanIndexOnlyScan ( IndexOnlyScanState node  ) 

Definition at line 249 of file nodeIndexonlyscan.c.

References ExecIndexEvalRuntimeKeys(), ExecScanReScan(), index_rescan(), IndexOnlyScanState::ioss_NumOrderByKeys, IndexOnlyScanState::ioss_NumRuntimeKeys, IndexOnlyScanState::ioss_NumScanKeys, IndexOnlyScanState::ioss_OrderByKeys, IndexOnlyScanState::ioss_RuntimeContext, IndexOnlyScanState::ioss_RuntimeKeys, IndexOnlyScanState::ioss_RuntimeKeysReady, IndexOnlyScanState::ioss_ScanDesc, IndexOnlyScanState::ioss_ScanKeys, ResetExprContext, and IndexOnlyScanState::ss.

Referenced by ExecReScan().

{
    /*
     * If we are doing runtime key calculations (ie, any of the index key
     * values weren't simple Consts), compute the new key values.  But first,
     * reset the context so we don't leak memory as each outer tuple is
     * scanned.  Note this assumes that we will recalculate *all* runtime keys
     * on each call.
     */
    if (node->ioss_NumRuntimeKeys != 0)
    {
        ExprContext *econtext = node->ioss_RuntimeContext;

        ResetExprContext(econtext);
        ExecIndexEvalRuntimeKeys(econtext,
                                 node->ioss_RuntimeKeys,
                                 node->ioss_NumRuntimeKeys);
    }
    node->ioss_RuntimeKeysReady = true;

    /* reset index scan */
    index_rescan(node->ioss_ScanDesc,
                 node->ioss_ScanKeys, node->ioss_NumScanKeys,
                 node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);

    ExecScanReScan(&node->ss);
}

static TupleTableSlot * IndexOnlyNext ( IndexOnlyScanState node  )  [static]

Definition at line 50 of file nodeIndexonlyscan.c.

References ExprContext::ecxt_scantuple, elog, ERROR, EState::es_direction, EState::es_snapshot, ExecClearTuple(), ExecQual(), IndexScanDescData::heapRelation, index_fetch_heap(), index_getnext_tid(), IndexOnlyScanState::indexqual, InstrCountFiltered2, IndexOnlyScanState::ioss_HeapFetches, IndexOnlyScanState::ioss_ScanDesc, IndexOnlyScanState::ioss_VMBuffer, ItemPointerGetBlockNumber, NULL, PlanState::plan, PredicateLockPage(), ScanState::ps, PlanState::ps_ExprContext, ResetExprContext, ScanDirectionIsBackward, ScanDirectionIsForward, IndexOnlyScanState::ss, ScanState::ss_ScanTupleSlot, PlanState::state, StoreIndexTuple(), visibilitymap_test(), IndexScanDescData::xs_continue_hot, IndexScanDescData::xs_itup, IndexScanDescData::xs_itupdesc, and IndexScanDescData::xs_recheck.

Referenced by ExecIndexOnlyScan().

{
    EState     *estate;
    ExprContext *econtext;
    ScanDirection direction;
    IndexScanDesc scandesc;
    TupleTableSlot *slot;
    ItemPointer tid;

    /*
     * extract necessary information from index scan node
     */
    estate = node->ss.ps.state;
    direction = estate->es_direction;
    /* flip direction if this is an overall backward scan */
    if (ScanDirectionIsBackward(((IndexOnlyScan *) node->ss.ps.plan)->indexorderdir))
    {
        if (ScanDirectionIsForward(direction))
            direction = BackwardScanDirection;
        else if (ScanDirectionIsBackward(direction))
            direction = ForwardScanDirection;
    }
    scandesc = node->ioss_ScanDesc;
    econtext = node->ss.ps.ps_ExprContext;
    slot = node->ss.ss_ScanTupleSlot;

    /*
     * OK, now that we have what we need, fetch the next tuple.
     */
    while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
    {
        HeapTuple   tuple = NULL;

        /*
         * We can skip the heap fetch if the TID references a heap page on
         * which all tuples are known visible to everybody.  In any case,
         * we'll use the index tuple not the heap tuple as the data source.
         *
         * Note on Memory Ordering Effects: visibilitymap_test does not lock
         * the visibility map buffer, and therefore the result we read here
         * could be slightly stale.  However, it can't be stale enough to
         * matter.  It suffices to show that (1) there is a read barrier
         * between the time we read the index TID and the time we test the
         * visibility map; and (2) there is a write barrier between the time
         * some other concurrent process clears the visibility map bit and the
         * time it inserts the index TID.  Since acquiring or releasing a
         * LWLock interposes a full barrier, this is easy to show: (1) is
         * satisfied by the release of the index buffer content lock after
         * reading the TID; and (2) is satisfied by the acquisition of the
         * buffer content lock in order to insert the TID.
         */
        if (!visibilitymap_test(scandesc->heapRelation,
                                ItemPointerGetBlockNumber(tid),
                                &node->ioss_VMBuffer))
        {
            /*
             * Rats, we have to visit the heap to check visibility.
             */
            node->ioss_HeapFetches++;
            tuple = index_fetch_heap(scandesc);
            if (tuple == NULL)
                continue;       /* no visible tuple, try next index entry */

            /*
             * Only MVCC snapshots are supported here, so there should be no
             * need to keep following the HOT chain once a visible entry has
             * been found.  If we did want to allow that, we'd need to keep
             * more state to remember not to call index_getnext_tid next time.
             */
            if (scandesc->xs_continue_hot)
                elog(ERROR, "non-MVCC snapshots are not supported in index-only scans");

            /*
             * Note: at this point we are holding a pin on the heap page, as
             * recorded in scandesc->xs_cbuf.  We could release that pin now,
             * but it's not clear whether it's a win to do so.  The next index
             * entry might require a visit to the same heap page.
             */
        }

        /*
         * Fill the scan tuple slot with data from the index.
         */
        StoreIndexTuple(slot, scandesc->xs_itup, scandesc->xs_itupdesc);

        /*
         * If the index was lossy, we have to recheck the index quals.
         * (Currently, this can never happen, but we should support the case
         * for possible future use, eg with GiST indexes.)
         */
        if (scandesc->xs_recheck)
        {
            econtext->ecxt_scantuple = slot;
            ResetExprContext(econtext);
            if (!ExecQual(node->indexqual, econtext, false))
            {
                /* Fails recheck, so drop it and loop back for another */
                InstrCountFiltered2(node, 1);
                continue;
            }
        }

        /*
         * Predicate locks for index-only scans must be acquired at the page
         * level when the heap is not accessed, since tuple-level predicate
         * locks need the tuple's xmin value.  If we had to visit the tuple
         * anyway, then we already have the tuple-level lock and can skip the
         * page lock.
         */
        if (tuple == NULL)
            PredicateLockPage(scandesc->heapRelation,
                              ItemPointerGetBlockNumber(tid),
                              estate->es_snapshot);

        return slot;
    }

    /*
     * if we get here it means the index scan failed so we are at the end of
     * the scan..
     */
    return ExecClearTuple(slot);
}

static bool IndexOnlyRecheck ( IndexOnlyScanState node,
TupleTableSlot slot 
) [static]

Definition at line 213 of file nodeIndexonlyscan.c.

References elog, and ERROR.

Referenced by ExecIndexOnlyScan().

{
    elog(ERROR, "EvalPlanQual recheck is not supported in index-only scans");
    return false;               /* keep compiler quiet */
}

static void StoreIndexTuple ( TupleTableSlot slot,
IndexTuple  itup,
TupleDesc  itupdesc 
) [static]

Definition at line 182 of file nodeIndexonlyscan.c.

References Assert, ExecClearTuple(), ExecStoreVirtualTuple(), i, index_getattr, tupleDesc::natts, TupleTableSlot::tts_isnull, TupleTableSlot::tts_tupleDescriptor, TupleTableSlot::tts_values, and values.

Referenced by IndexOnlyNext().

{
    int         nindexatts = itupdesc->natts;
    Datum      *values = slot->tts_values;
    bool       *isnull = slot->tts_isnull;
    int         i;

    /*
     * Note: we must use the tupdesc supplied by the AM in index_getattr, not
     * the slot's tupdesc, in case the latter has different datatypes (this
     * happens for btree name_ops in particular).  They'd better have the same
     * number of columns though, as well as being datatype-compatible which is
     * something we can't so easily check.
     */
    Assert(slot->tts_tupleDescriptor->natts == nindexatts);

    ExecClearTuple(slot);
    for (i = 0; i < nindexatts; i++)
        values[i] = index_getattr(itup, i + 1, itupdesc, &isnull[i]);
    ExecStoreVirtualTuple(slot);
}