/*------------------------------------------------------------------------- * * 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.6 2005/04/24 18:16:38 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; Oid indxid; Relation indexRelation; 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); /* * We do not open or lock the base relation here. We assume that an * ancestor BitmapHeapScan node is holding AccessShareLock on the * heap relation throughout the execution of the plan tree. */ /* * 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. */ indxid = ((BitmapIndexScan *) node->ss.ps.plan)->indxid; indexRelation = index_open(indxid); scandesc = index_beginscan_multi(indexRelation, node->ss.ps.state->es_snapshot, node->biss_NumScanKeys, node->biss_ScanKeys); /* * 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(); } /* * close the index relation */ index_endscan(scandesc); index_close(indexRelation); /* 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. * ---------------------------------------------------------------- */ void ExecBitmapIndexReScan(BitmapIndexScanState *node, ExprContext *exprCtxt) { ExprContext *econtext; ExprState **runtimeKeyInfo; econtext = node->biss_RuntimeContext; /* context for runtime * keys */ runtimeKeyInfo = node->biss_RuntimeKeyInfo; if (econtext) { /* * If we are being passed an outer tuple, save it for runtime key * calc. */ if (exprCtxt != NULL) econtext->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; } } /* ---------------------------------------------------------------- * ExecEndBitmapIndexScan * ---------------------------------------------------------------- */ void ExecEndBitmapIndexScan(BitmapIndexScanState *node) { /* * Free the exprcontext ... now dead code, see ExecFreeExprContext */ #ifdef NOT_USED if (node->biss_RuntimeContext) FreeExprContext(node->biss_RuntimeContext); #endif } /* ---------------------------------------------------------------- * ExecInitBitmapIndexScan * * Initializes the index scan's state information. * ---------------------------------------------------------------- */ BitmapIndexScanState * ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate) { BitmapIndexScanState *indexstate; ExprState **runtimeKeyInfo; bool have_runtime_keys; /* * 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 * * We do not need a standard exprcontext for this node, though we may * decide below to create a runtime-key exprcontext */ /* * 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; 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 to runtime key * expressions. * * If we do have runtime keys, we need an ExprContext to evaluate them. * We could just create a "standard" plan node exprcontext, but to * keep the code looking similar to nodeIndexscan.c, it seems better * to stick with the approach of using a separate ExprContext. */ 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 */ if (runtimeKeyInfo) pfree(runtimeKeyInfo); } /* We don't keep the table or index open across calls */ indexstate->ss.ss_currentRelation = NULL; indexstate->ss.ss_currentScanDesc = NULL; /* * all done. */ return indexstate; } int ExecCountSlotsBitmapIndexScan(BitmapIndexScan *node) { return ExecCountSlotsNode(outerPlan((Plan *) node)) + ExecCountSlotsNode(innerPlan((Plan *) node)) + BITMAPINDEXSCAN_NSLOTS; }