| /*------------------------------------------------------------------------- |
| * |
| * nodeBitmapIndexscan.c |
| * Routines to support bitmapped index scans of relations |
| * |
| * Portions Copyright (c) 2007-2008, Greenplum inc |
| * Portions Copyright (c) 2012-Present VMware, Inc. or its affiliates. |
| * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * |
| * IDENTIFICATION |
| * src/backend/executor/nodeBitmapIndexscan.c |
| * |
| *------------------------------------------------------------------------- |
| */ |
| /* |
| * INTERFACE ROUTINES |
| * MultiExecBitmapIndexScan scans a relation using index. |
| * ExecInitBitmapIndexScan creates and initializes state info. |
| * ExecReScanBitmapIndexScan prepares to rescan the plan. |
| * ExecEndBitmapIndexScan releases all storage. |
| */ |
| #include "postgres.h" |
| |
| #include "access/genam.h" |
| #include "access/nbtree.h" |
| #include "access/relscan.h" |
| #include "executor/execdebug.h" |
| #include "executor/nodeBitmapIndexscan.h" |
| #include "executor/nodeIndexscan.h" |
| #include "miscadmin.h" |
| #include "utils/memutils.h" |
| #include "utils/lsyscache.h" |
| #include "nodes/tidbitmap.h" |
| |
| #include "cdb/cdbvars.h" |
| |
| |
| /* ---------------------------------------------------------------- |
| * ExecBitmapIndexScan |
| * |
| * stub for pro forma compliance |
| * ---------------------------------------------------------------- |
| */ |
| static TupleTableSlot * |
| ExecBitmapIndexScan(PlanState *pstate) |
| { |
| elog(ERROR, "BitmapIndexScan node does not support ExecProcNode call convention"); |
| return NULL; |
| } |
| |
| /* ---------------------------------------------------------------- |
| * MultiExecBitmapIndexScan(node) |
| * |
| * If IndexScanState's iss_NumArrayKeys = 0, then BitmapIndexScan |
| * node returns a StreamBitmap that stores all the interesting rows. |
| * The returning StreamBitmap will point to an IndexStream that contains |
| * pointers to functions for handling the output. |
| * |
| * If IndexScanState's iss_NumArrayKeys > 0 (e.g., WHERE clause contains |
| * `d in (0,1)` condition and a bitmap index has been created on column d), |
| * then iss_NumArrayKeys StreamBitmaps will be created, where every bitmap |
| * points to an individual IndexStream. BitmapIndexScan node |
| * returns a StreamBitmap that ORs all the above StreamBitmaps. Also, the |
| * returning StreamBitmap will point to an OpStream of type BMS_OR whose input |
| * streams are the IndexStreams created for every array key. |
| * ---------------------------------------------------------------- |
| */ |
| Node * |
| MultiExecBitmapIndexScan(BitmapIndexScanState *node) |
| { |
| Node *bitmap = NULL; |
| IndexScanDesc scandesc; |
| double nTuples = 0; |
| bool doscan; |
| |
| /* Make sure we are not leaking a previous bitmap */ |
| Assert(NULL == node->biss_result); |
| |
| /* must provide our own instrumentation support */ |
| if (node->ss.ps.instrument) |
| InstrStartNode(node->ss.ps.instrument); |
| |
| /* |
| * extract necessary information from index scan node |
| */ |
| scandesc = node->biss_ScanDesc; |
| |
| /* |
| * Actually the bitmap building logic is different to upstream. |
| * We build the bitmap inside function `index_getbitmap`, however, |
| * the upsteam will build the bitmap before calling `index_getbitmap`. |
| * |
| * Thus, if we want to build bitmap in cbdb, we have to pass the dsa_area |
| * into the `index_getbitmap`. |
| * |
| * I work around it by adding a field for dsa in struct IndexScanDesc, |
| * but we might need a code refaction here to align the code to upstream. |
| * |
| */ |
| if (node->ss.ps.state->es_query_dsa != NULL && ((BitmapIndexScan *)node->ss.ps.plan)->isshared) |
| { |
| scandesc->dsa = node->ss.ps.state->es_query_dsa; |
| } |
| |
| /* |
| * If we have runtime keys and they've not already been set up, do it now. |
| * Array keys are also treated as runtime keys; note that if ExecReScan |
| * returns with biss_RuntimeKeysReady still false, then there is an empty |
| * array key so we should do nothing. |
| */ |
| if (!node->biss_RuntimeKeysReady && |
| (node->biss_NumRuntimeKeys != 0 || node->biss_NumArrayKeys != 0)) |
| { |
| ExecReScan((PlanState *) node); |
| doscan = node->biss_RuntimeKeysReady; |
| |
| /* Return an empty bitmap if biss_RuntimeKeysReady still false.*/ |
| if (!doscan) |
| index_initbitmap(scandesc, &bitmap); |
| } |
| else |
| doscan = true; |
| |
| /* Get bitmap from index */ |
| while (doscan) |
| { |
| nTuples += (double) index_getbitmap(scandesc, &bitmap); |
| |
| if ((NULL != bitmap) && |
| !(IsA(bitmap, TIDBitmap) || IsA(bitmap, StreamBitmap))) |
| { |
| elog(ERROR, "unrecognized result from bitmap index scan"); |
| } |
| |
| CHECK_FOR_INTERRUPTS(); |
| |
| if (QueryFinishPending) |
| break; |
| |
| /* CDB: If EXPLAIN ANALYZE, let bitmap share our Instrumentation. */ |
| if (node->ss.ps.instrument && (node->ss.ps.instrument)->need_cdb) |
| tbm_generic_set_instrument(bitmap, node->ss.ps.instrument); |
| |
| doscan = ExecIndexAdvanceArrayKeys(node->biss_ArrayKeys, |
| node->biss_NumArrayKeys); |
| if (doscan) /* reset index scan */ |
| index_rescan(node->biss_ScanDesc, |
| node->biss_ScanKeys, node->biss_NumScanKeys, |
| NULL, 0); |
| } |
| |
| /* must provide our own instrumentation support */ |
| if (node->ss.ps.instrument) |
| InstrStopNode(node->ss.ps.instrument, nTuples); |
| |
| return (Node *) bitmap; |
| } |
| |
| /* ---------------------------------------------------------------- |
| * ExecReScanBitmapIndexScan(node) |
| * |
| * Recalculates the values of any scan keys whose value depends on |
| * information known at runtime, then rescans the indexed relation. |
| * ---------------------------------------------------------------- |
| */ |
| void |
| ExecReScanBitmapIndexScan(BitmapIndexScanState *node) |
| { |
| ExprContext *econtext = node->biss_RuntimeContext; |
| |
| /* |
| * 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. |
| */ |
| if (econtext) |
| ResetExprContext(econtext); |
| |
| /* |
| * If we are doing runtime key calculations (ie, any of the index key |
| * values weren't simple Consts), compute the new key values. |
| * |
| * Array keys are also treated as runtime keys; note that if we return |
| * with biss_RuntimeKeysReady still false, then there is an empty array |
| * key so no index scan is needed. |
| */ |
| if (node->biss_NumRuntimeKeys != 0) |
| ExecIndexEvalRuntimeKeys(econtext, |
| node->biss_RuntimeKeys, |
| node->biss_NumRuntimeKeys); |
| if (node->biss_NumArrayKeys != 0) |
| node->biss_RuntimeKeysReady = |
| ExecIndexEvalArrayKeys(econtext, |
| node->biss_ArrayKeys, |
| node->biss_NumArrayKeys); |
| else |
| node->biss_RuntimeKeysReady = true; |
| |
| /* reset index scan */ |
| if (node->biss_RuntimeKeysReady) |
| index_rescan(node->biss_ScanDesc, |
| node->biss_ScanKeys, node->biss_NumScanKeys, |
| NULL, 0); |
| } |
| |
| /* ---------------------------------------------------------------- |
| * ExecEndBitmapIndexScan |
| * ---------------------------------------------------------------- |
| */ |
| void |
| ExecEndBitmapIndexScan(BitmapIndexScanState *node) |
| { |
| Relation indexRelationDesc; |
| IndexScanDesc indexScanDesc; |
| |
| /* |
| * extract information from the node |
| */ |
| indexRelationDesc = node->biss_RelationDesc; |
| indexScanDesc = node->biss_ScanDesc; |
| |
| /* |
| * Free the exprcontext(s) ... now dead code, see ExecFreeExprContext |
| * |
| * GPDB: This is not dead code in GPDB, because we don't want to leak |
| * exprcontexts in a dynamic bitmap index scan. |
| */ |
| #if 1 |
| if (node->biss_RuntimeContext) |
| FreeExprContext(node->biss_RuntimeContext, true); |
| #endif |
| |
| /* |
| * close the index relation (no-op if we didn't open it) |
| */ |
| if (indexScanDesc) |
| index_endscan(indexScanDesc); |
| if (indexRelationDesc) |
| index_close(indexRelationDesc, NoLock); |
| } |
| |
| /* ---------------------------------------------------------------- |
| * ExecInitBitmapIndexScan |
| * |
| * Initializes the index scan's state information. |
| * ---------------------------------------------------------------- |
| */ |
| BitmapIndexScanState * |
| ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags) |
| { |
| BitmapIndexScanState *indexstate; |
| LOCKMODE lockmode; |
| |
| /* check for unsupported flags */ |
| Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); |
| |
| /* |
| * create state structure |
| */ |
| indexstate = makeNode(BitmapIndexScanState); |
| indexstate->ss.ps.plan = (Plan *) node; |
| indexstate->ss.ps.state = estate; |
| indexstate->ss.ps.ExecProcNode = ExecBitmapIndexScan; |
| |
| /* normally we don't make the result bitmap till runtime */ |
| indexstate->biss_result = NULL; |
| |
| /* |
| * We do not open or lock the base relation here. We assume that an |
| * ancestor BitmapHeapScan node is holding AccessShareLock (or better) on |
| * the heap relation throughout the execution of the plan tree. |
| */ |
| |
| indexstate->ss.ss_currentRelation = NULL; |
| indexstate->ss.ss_currentScanDesc = 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 indexqual expression, only the |
| * sub-parts corresponding to runtime keys (see below). |
| */ |
| |
| /* |
| * 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. */ |
| lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode; |
| indexstate->biss_RelationDesc = index_open(node->indexid, lockmode); |
| |
| /* |
| * Initialize index-specific scan state |
| */ |
| indexstate->biss_RuntimeKeysReady = false; |
| indexstate->biss_RuntimeKeys = NULL; |
| indexstate->biss_NumRuntimeKeys = 0; |
| |
| /* |
| * build the index scan keys from the index qualification |
| */ |
| ExecIndexBuildScanKeys((PlanState *) indexstate, |
| indexstate->biss_RelationDesc, |
| node->indexqual, |
| false, |
| &indexstate->biss_ScanKeys, |
| &indexstate->biss_NumScanKeys, |
| &indexstate->biss_RuntimeKeys, |
| &indexstate->biss_NumRuntimeKeys, |
| &indexstate->biss_ArrayKeys, |
| &indexstate->biss_NumArrayKeys); |
| |
| /* |
| * If we have runtime keys or array 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 (indexstate->biss_NumRuntimeKeys != 0 || |
| indexstate->biss_NumArrayKeys != 0) |
| { |
| ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext; |
| |
| ExecAssignExprContext(estate, &indexstate->ss.ps); |
| indexstate->biss_RuntimeContext = indexstate->ss.ps.ps_ExprContext; |
| indexstate->ss.ps.ps_ExprContext = stdecontext; |
| } |
| else |
| { |
| indexstate->biss_RuntimeContext = NULL; |
| } |
| |
| /* |
| * Initialize scan descriptor. |
| */ |
| indexstate->biss_ScanDesc = |
| index_beginscan_bitmap(indexstate->biss_RelationDesc, |
| estate->es_snapshot, |
| indexstate->biss_NumScanKeys); |
| |
| /* |
| * If no run-time keys to calculate, go ahead and pass the scankeys to the |
| * index AM. |
| */ |
| if (indexstate->biss_NumRuntimeKeys == 0 && |
| indexstate->biss_NumArrayKeys == 0) |
| index_rescan(indexstate->biss_ScanDesc, |
| indexstate->biss_ScanKeys, indexstate->biss_NumScanKeys, |
| NULL, 0); |
| |
| /* |
| * all done. |
| */ |
| return indexstate; |
| } |