
bitmaps for multiple indexscans. Instead just let each indexscan add TIDs directly into the BitmapOr node's result bitmap.
535 lines
15 KiB
C
535 lines
15 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* nodeBitmapIndexscan.c
|
|
* Routines to support bitmapped index scans of relations
|
|
*
|
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL: pgsql/src/backend/executor/nodeBitmapIndexscan.c,v 1.2 2005/04/20 15:48:36 tgl Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
/*
|
|
* INTERFACE ROUTINES
|
|
* MultiExecBitmapIndexScan scans a relation using index.
|
|
* ExecInitBitmapIndexScan creates and initializes state info.
|
|
* ExecBitmapIndexReScan prepares to rescan the plan.
|
|
* ExecEndBitmapIndexScan releases all storage.
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/genam.h"
|
|
#include "access/heapam.h"
|
|
#include "executor/execdebug.h"
|
|
#include "executor/instrument.h"
|
|
#include "executor/nodeBitmapIndexscan.h"
|
|
#include "miscadmin.h"
|
|
#include "nodes/nodeFuncs.h"
|
|
#include "optimizer/clauses.h"
|
|
#include "parser/parsetree.h"
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
* MultiExecBitmapIndexScan(node)
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
Node *
|
|
MultiExecBitmapIndexScan(BitmapIndexScanState *node)
|
|
{
|
|
#define MAX_TIDS 1024
|
|
TIDBitmap *tbm;
|
|
IndexScanDesc scandesc;
|
|
ItemPointerData tids[MAX_TIDS];
|
|
int32 ntids;
|
|
double nTuples = 0;
|
|
|
|
/* must provide our own instrumentation support */
|
|
if (node->ss.ps.instrument)
|
|
InstrStartNode(node->ss.ps.instrument);
|
|
|
|
/*
|
|
* If we have runtime keys and they've not already been set up, do it
|
|
* now.
|
|
*/
|
|
if (node->biss_RuntimeKeyInfo && !node->biss_RuntimeKeysReady)
|
|
ExecReScan((PlanState *) node, NULL);
|
|
|
|
/*
|
|
* extract necessary information from index scan node
|
|
*/
|
|
scandesc = node->biss_ScanDesc;
|
|
|
|
/*
|
|
* Prepare the result bitmap. Normally we just create a new one to pass
|
|
* back; however, our parent node is allowed to store a pre-made one
|
|
* into node->biss_result, in which case we just OR our tuple IDs into
|
|
* the existing bitmap. (This saves needing explicit UNION steps.)
|
|
*/
|
|
if (node->biss_result)
|
|
{
|
|
tbm = node->biss_result;
|
|
node->biss_result = NULL; /* reset for next time */
|
|
}
|
|
else
|
|
{
|
|
/* XXX should we use less than work_mem for this? */
|
|
tbm = tbm_create(work_mem * 1024L);
|
|
}
|
|
|
|
/*
|
|
* Get TIDs from index and insert into bitmap
|
|
*/
|
|
for (;;)
|
|
{
|
|
bool more = index_getmulti(scandesc, tids, MAX_TIDS, &ntids);
|
|
|
|
if (ntids > 0)
|
|
{
|
|
tbm_add_tuples(tbm, tids, ntids);
|
|
nTuples += ntids;
|
|
}
|
|
|
|
if (!more)
|
|
break;
|
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
}
|
|
|
|
/* must provide our own instrumentation support */
|
|
if (node->ss.ps.instrument)
|
|
InstrStopNodeMulti(node->ss.ps.instrument, nTuples);
|
|
|
|
return (Node *) tbm;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecBitmapIndexReScan(node)
|
|
*
|
|
* Recalculates the value of the scan keys whose value depends on
|
|
* information known at runtime and rescans the indexed relation.
|
|
* Updating the scan key was formerly done separately in
|
|
* ExecUpdateIndexScanKeys. Integrating it into ReScan makes
|
|
* rescans of indices and relations/general streams more uniform.
|
|
*
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecBitmapIndexReScan(BitmapIndexScanState *node, ExprContext *exprCtxt)
|
|
{
|
|
ExprContext *econtext;
|
|
ExprState **runtimeKeyInfo;
|
|
Index scanrelid;
|
|
|
|
econtext = node->biss_RuntimeContext; /* context for runtime
|
|
* keys */
|
|
runtimeKeyInfo = node->biss_RuntimeKeyInfo;
|
|
scanrelid = ((BitmapIndexScan *) node->ss.ps.plan)->scan.scanrelid;
|
|
|
|
if (econtext)
|
|
{
|
|
/*
|
|
* If we are being passed an outer tuple, save it for runtime key
|
|
* calc. We also need to link it into the "regular" per-tuple
|
|
* econtext.
|
|
*/
|
|
if (exprCtxt != NULL)
|
|
{
|
|
ExprContext *stdecontext;
|
|
|
|
econtext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
|
|
stdecontext = node->ss.ps.ps_ExprContext;
|
|
stdecontext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
|
|
}
|
|
|
|
/*
|
|
* Reset the runtime-key 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.
|
|
*/
|
|
ResetExprContext(econtext);
|
|
}
|
|
|
|
/*
|
|
* If we are doing runtime key calculations (ie, the index keys depend
|
|
* on data from an outer scan), compute the new key values
|
|
*/
|
|
if (runtimeKeyInfo)
|
|
{
|
|
int n_keys;
|
|
ScanKey scan_keys;
|
|
ExprState **run_keys;
|
|
int j;
|
|
|
|
n_keys = node->biss_NumScanKeys;
|
|
scan_keys = node->biss_ScanKeys;
|
|
run_keys = runtimeKeyInfo;
|
|
|
|
for (j = 0; j < n_keys; j++)
|
|
{
|
|
/*
|
|
* If we have a run-time key, then extract the run-time
|
|
* expression and evaluate it with respect to the current
|
|
* outer tuple. We then stick the result into the scan
|
|
* key.
|
|
*
|
|
* Note: the result of the eval could be a pass-by-ref value
|
|
* that's stored in the outer scan's tuple, not in
|
|
* econtext->ecxt_per_tuple_memory. We assume that the
|
|
* outer tuple will stay put throughout our scan. If this
|
|
* is wrong, we could copy the result into our context
|
|
* explicitly, but I think that's not necessary...
|
|
*/
|
|
if (run_keys[j] != NULL)
|
|
{
|
|
Datum scanvalue;
|
|
bool isNull;
|
|
|
|
scanvalue = ExecEvalExprSwitchContext(run_keys[j],
|
|
econtext,
|
|
&isNull,
|
|
NULL);
|
|
scan_keys[j].sk_argument = scanvalue;
|
|
if (isNull)
|
|
scan_keys[j].sk_flags |= SK_ISNULL;
|
|
else
|
|
scan_keys[j].sk_flags &= ~SK_ISNULL;
|
|
}
|
|
}
|
|
|
|
node->biss_RuntimeKeysReady = true;
|
|
}
|
|
|
|
index_rescan(node->biss_ScanDesc, node->biss_ScanKeys);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecEndBitmapIndexScan
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecEndBitmapIndexScan(BitmapIndexScanState *node)
|
|
{
|
|
Relation relation;
|
|
|
|
/*
|
|
* extract information from the node
|
|
*/
|
|
relation = node->ss.ss_currentRelation;
|
|
|
|
/*
|
|
* Free the exprcontext(s)
|
|
*/
|
|
ExecFreeExprContext(&node->ss.ps);
|
|
if (node->biss_RuntimeContext)
|
|
FreeExprContext(node->biss_RuntimeContext);
|
|
|
|
/*
|
|
* close the index relation
|
|
*/
|
|
if (node->biss_ScanDesc != NULL)
|
|
index_endscan(node->biss_ScanDesc);
|
|
|
|
if (node->biss_RelationDesc != NULL)
|
|
index_close(node->biss_RelationDesc);
|
|
|
|
/*
|
|
* close the heap relation.
|
|
*
|
|
* Currently, we do not release the AccessShareLock acquired by
|
|
* ExecInitBitmapIndexScan. This lock should be held till end of
|
|
* transaction. (There is a faction that considers this too much
|
|
* locking, however.)
|
|
*/
|
|
heap_close(relation, NoLock);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecInitBitmapIndexScan
|
|
*
|
|
* Initializes the index scan's state information, creates
|
|
* scan keys, and opens the base and index relations.
|
|
*
|
|
* Note: index scans have 2 sets of state information because
|
|
* we have to keep track of the base relation and the
|
|
* index relations.
|
|
*
|
|
* old comments
|
|
* Creates the run-time state information for the node and
|
|
* sets the relation id to contain relevant descriptors.
|
|
*
|
|
* Parameters:
|
|
* node: BitmapIndexNode node produced by the planner.
|
|
* estate: the execution state initialized in InitPlan.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
BitmapIndexScanState *
|
|
ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate)
|
|
{
|
|
BitmapIndexScanState *indexstate;
|
|
ExprState **runtimeKeyInfo;
|
|
bool have_runtime_keys;
|
|
RangeTblEntry *rtentry;
|
|
Index relid;
|
|
Oid reloid;
|
|
Relation currentRelation;
|
|
|
|
/*
|
|
* create state structure
|
|
*/
|
|
indexstate = makeNode(BitmapIndexScanState);
|
|
indexstate->ss.ps.plan = (Plan *) node;
|
|
indexstate->ss.ps.state = estate;
|
|
|
|
/* normally we don't make the result bitmap till runtime */
|
|
indexstate->biss_result = NULL;
|
|
|
|
/*
|
|
* Miscellaneous initialization
|
|
*
|
|
* create expression context for node
|
|
*/
|
|
ExecAssignExprContext(estate, &indexstate->ss.ps);
|
|
|
|
/*
|
|
* initialize child expressions
|
|
*
|
|
* We don't need to initialize targetlist or qual since neither are used.
|
|
*
|
|
* Note: we don't initialize all of the indxqual expression, only the
|
|
* sub-parts corresponding to runtime keys (see below).
|
|
*/
|
|
|
|
#define BITMAPINDEXSCAN_NSLOTS 0
|
|
|
|
/*
|
|
* Initialize index-specific scan state
|
|
*/
|
|
indexstate->biss_ScanKeys = NULL;
|
|
indexstate->biss_NumScanKeys = 0;
|
|
indexstate->biss_RuntimeKeyInfo = NULL;
|
|
indexstate->biss_RuntimeContext = NULL;
|
|
indexstate->biss_RuntimeKeysReady = false;
|
|
indexstate->biss_RelationDesc = NULL;
|
|
indexstate->biss_ScanDesc = NULL;
|
|
|
|
CXT1_printf("ExecInitBitmapIndexScan: context is %d\n", CurrentMemoryContext);
|
|
|
|
/*
|
|
* initialize space for runtime key info (may not be needed)
|
|
*/
|
|
have_runtime_keys = false;
|
|
|
|
/*
|
|
* build the index scan keys from the index qualification
|
|
*/
|
|
{
|
|
List *quals;
|
|
List *strategies;
|
|
List *subtypes;
|
|
ListCell *qual_cell;
|
|
ListCell *strategy_cell;
|
|
ListCell *subtype_cell;
|
|
int n_keys;
|
|
ScanKey scan_keys;
|
|
ExprState **run_keys;
|
|
int j;
|
|
|
|
quals = node->indxqual;
|
|
strategies = node->indxstrategy;
|
|
subtypes = node->indxsubtype;
|
|
n_keys = list_length(quals);
|
|
scan_keys = (n_keys <= 0) ? NULL :
|
|
(ScanKey) palloc(n_keys * sizeof(ScanKeyData));
|
|
run_keys = (n_keys <= 0) ? NULL :
|
|
(ExprState **) palloc(n_keys * sizeof(ExprState *));
|
|
|
|
/*
|
|
* for each opclause in the given qual, convert each qual's
|
|
* opclause into a single scan key
|
|
*/
|
|
qual_cell = list_head(quals);
|
|
strategy_cell = list_head(strategies);
|
|
subtype_cell = list_head(subtypes);
|
|
for (j = 0; j < n_keys; j++)
|
|
{
|
|
OpExpr *clause; /* one clause of index qual */
|
|
Expr *leftop; /* expr on lhs of operator */
|
|
Expr *rightop; /* expr on rhs ... */
|
|
int flags = 0;
|
|
AttrNumber varattno; /* att number used in scan */
|
|
StrategyNumber strategy; /* op's strategy number */
|
|
Oid subtype; /* op's strategy subtype */
|
|
RegProcedure opfuncid; /* operator proc id used in scan */
|
|
Datum scanvalue; /* value used in scan (if const) */
|
|
|
|
/*
|
|
* extract clause information from the qualification
|
|
*/
|
|
clause = (OpExpr *) lfirst(qual_cell);
|
|
qual_cell = lnext(qual_cell);
|
|
strategy = lfirst_int(strategy_cell);
|
|
strategy_cell = lnext(strategy_cell);
|
|
subtype = lfirst_oid(subtype_cell);
|
|
subtype_cell = lnext(subtype_cell);
|
|
|
|
if (!IsA(clause, OpExpr))
|
|
elog(ERROR, "indxqual is not an OpExpr");
|
|
|
|
opfuncid = clause->opfuncid;
|
|
|
|
/*
|
|
* Here we figure out the contents of the index qual. The
|
|
* usual case is (var op const) which means we form a scan key
|
|
* for the attribute listed in the var node and use the value
|
|
* of the const as comparison data.
|
|
*
|
|
* If we don't have a const node, it means our scan key is a
|
|
* function of information obtained during the execution of
|
|
* the plan, in which case we need to recalculate the index
|
|
* scan key at run time. Hence, we set have_runtime_keys to
|
|
* true and place the appropriate subexpression in run_keys.
|
|
* The corresponding scan key values are recomputed at run
|
|
* time.
|
|
*/
|
|
run_keys[j] = NULL;
|
|
|
|
/*
|
|
* determine information in leftop
|
|
*/
|
|
leftop = (Expr *) get_leftop((Expr *) clause);
|
|
|
|
if (leftop && IsA(leftop, RelabelType))
|
|
leftop = ((RelabelType *) leftop)->arg;
|
|
|
|
Assert(leftop != NULL);
|
|
|
|
if (!(IsA(leftop, Var) &&
|
|
var_is_rel((Var *) leftop)))
|
|
elog(ERROR, "indxqual doesn't have key on left side");
|
|
|
|
varattno = ((Var *) leftop)->varattno;
|
|
|
|
/*
|
|
* now determine information in rightop
|
|
*/
|
|
rightop = (Expr *) get_rightop((Expr *) clause);
|
|
|
|
if (rightop && IsA(rightop, RelabelType))
|
|
rightop = ((RelabelType *) rightop)->arg;
|
|
|
|
Assert(rightop != NULL);
|
|
|
|
if (IsA(rightop, Const))
|
|
{
|
|
/*
|
|
* if the rightop is a const node then it means it
|
|
* identifies the value to place in our scan key.
|
|
*/
|
|
scanvalue = ((Const *) rightop)->constvalue;
|
|
if (((Const *) rightop)->constisnull)
|
|
flags |= SK_ISNULL;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* otherwise, the rightop contains an expression evaluable
|
|
* at runtime to figure out the value to place in our scan
|
|
* key.
|
|
*/
|
|
have_runtime_keys = true;
|
|
run_keys[j] = ExecInitExpr(rightop, (PlanState *) indexstate);
|
|
scanvalue = (Datum) 0;
|
|
}
|
|
|
|
/*
|
|
* initialize the scan key's fields appropriately
|
|
*/
|
|
ScanKeyEntryInitialize(&scan_keys[j],
|
|
flags,
|
|
varattno, /* attribute number to
|
|
* scan */
|
|
strategy, /* op's strategy */
|
|
subtype, /* strategy subtype */
|
|
opfuncid, /* reg proc to use */
|
|
scanvalue); /* constant */
|
|
}
|
|
|
|
/*
|
|
* store the key information into the node.
|
|
*/
|
|
indexstate->biss_NumScanKeys = n_keys;
|
|
indexstate->biss_ScanKeys = scan_keys;
|
|
runtimeKeyInfo = run_keys;
|
|
}
|
|
|
|
|
|
/*
|
|
* If all of our keys have the form (var op const), then we have no
|
|
* runtime keys so we store NULL in the runtime key info. Otherwise
|
|
* runtime key info contains an array of pointers (one for each index)
|
|
* to arrays of flags (one for each key) which indicate that the qual
|
|
* needs to be evaluated at runtime. -cim 10/24/89
|
|
*
|
|
* If we do 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 (have_runtime_keys)
|
|
{
|
|
ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
|
|
|
|
ExecAssignExprContext(estate, &indexstate->ss.ps);
|
|
indexstate->biss_RuntimeKeyInfo = runtimeKeyInfo;
|
|
indexstate->biss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
|
|
indexstate->ss.ps.ps_ExprContext = stdecontext;
|
|
}
|
|
else
|
|
{
|
|
indexstate->biss_RuntimeKeyInfo = NULL;
|
|
indexstate->biss_RuntimeContext = NULL;
|
|
/* Get rid of the speculatively-allocated flag array, too */
|
|
pfree(runtimeKeyInfo);
|
|
}
|
|
|
|
/*
|
|
* open the base relation and acquire AccessShareLock on it.
|
|
*/
|
|
relid = node->scan.scanrelid;
|
|
rtentry = rt_fetch(relid, estate->es_range_table);
|
|
reloid = rtentry->relid;
|
|
|
|
currentRelation = heap_open(reloid, AccessShareLock);
|
|
|
|
indexstate->ss.ss_currentRelation = currentRelation;
|
|
indexstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
|
|
|
|
/*
|
|
* open the index relation and initialize relation and scan
|
|
* descriptors. Note we acquire no locks here; the index machinery
|
|
* does its own locks and unlocks. (We rely on having AccessShareLock
|
|
* on the parent table to ensure the index won't go away!)
|
|
*/
|
|
indexstate->biss_RelationDesc = index_open(node->indxid);
|
|
indexstate->biss_ScanDesc =
|
|
index_beginscan_multi(indexstate->biss_RelationDesc,
|
|
estate->es_snapshot,
|
|
indexstate->biss_NumScanKeys,
|
|
indexstate->biss_ScanKeys);
|
|
|
|
/*
|
|
* all done.
|
|
*/
|
|
return indexstate;
|
|
}
|
|
|
|
int
|
|
ExecCountSlotsBitmapIndexScan(BitmapIndexScan *node)
|
|
{
|
|
return ExecCountSlotsNode(outerPlan((Plan *) node)) +
|
|
ExecCountSlotsNode(innerPlan((Plan *) node)) + BITMAPINDEXSCAN_NSLOTS;
|
|
}
|