#include "postgres.h"
#include "access/nbtree.h"
#include "access/relscan.h"
#include "executor/execdebug.h"
#include "executor/nodeIndexscan.h"
#include "optimizer/clauses.h"
#include "utils/array.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
Go to the source code of this file.
Functions | |
static TupleTableSlot * | IndexNext (IndexScanState *node) |
static bool | IndexRecheck (IndexScanState *node, TupleTableSlot *slot) |
TupleTableSlot * | ExecIndexScan (IndexScanState *node) |
void | ExecReScanIndexScan (IndexScanState *node) |
void | ExecIndexEvalRuntimeKeys (ExprContext *econtext, IndexRuntimeKeyInfo *runtimeKeys, int numRuntimeKeys) |
bool | ExecIndexEvalArrayKeys (ExprContext *econtext, IndexArrayKeyInfo *arrayKeys, int numArrayKeys) |
bool | ExecIndexAdvanceArrayKeys (IndexArrayKeyInfo *arrayKeys, int numArrayKeys) |
void | ExecEndIndexScan (IndexScanState *node) |
void | ExecIndexMarkPos (IndexScanState *node) |
void | ExecIndexRestrPos (IndexScanState *node) |
IndexScanState * | ExecInitIndexScan (IndexScan *node, EState *estate, int eflags) |
void | ExecIndexBuildScanKeys (PlanState *planstate, Relation index, List *quals, bool isorderby, ScanKey *scanKeys, int *numScanKeys, IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys, IndexArrayKeyInfo **arrayKeys, int *numArrayKeys) |
void ExecEndIndexScan | ( | IndexScanState * | node | ) |
Definition at line 389 of file nodeIndexscan.c.
References ExecClearTuple(), ExecCloseScanRelation(), ExecFreeExprContext(), FreeExprContext(), index_close(), index_endscan(), IndexScanState::iss_RelationDesc, IndexScanState::iss_RuntimeContext, IndexScanState::iss_ScanDesc, NoLock, ScanState::ps, PlanState::ps_ResultTupleSlot, IndexScanState::ss, ScanState::ss_currentRelation, and ScanState::ss_ScanTupleSlot.
Referenced by ExecEndNode().
{ Relation indexRelationDesc; IndexScanDesc indexScanDesc; Relation relation; /* * extract information from the node */ indexRelationDesc = node->iss_RelationDesc; indexScanDesc = node->iss_ScanDesc; relation = node->ss.ss_currentRelation; /* * Free the exprcontext(s) ... now dead code, see ExecFreeExprContext */ #ifdef NOT_USED ExecFreeExprContext(&node->ss.ps); if (node->iss_RuntimeContext) FreeExprContext(node->iss_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); }
bool ExecIndexAdvanceArrayKeys | ( | IndexArrayKeyInfo * | arrayKeys, | |
int | numArrayKeys | |||
) |
Definition at line 344 of file nodeIndexscan.c.
References IndexArrayKeyInfo::elem_nulls, IndexArrayKeyInfo::elem_values, IndexArrayKeyInfo::next_elem, IndexArrayKeyInfo::num_elems, IndexArrayKeyInfo::scan_key, ScanKeyData::sk_argument, and ScanKeyData::sk_flags.
Referenced by MultiExecBitmapIndexScan().
{ bool found = false; int j; /* * Note we advance the rightmost array key most quickly, since it will * correspond to the lowest-order index column among the available * qualifications. This is hypothesized to result in better locality of * access in the index. */ for (j = numArrayKeys - 1; j >= 0; j--) { ScanKey scan_key = arrayKeys[j].scan_key; int next_elem = arrayKeys[j].next_elem; int num_elems = arrayKeys[j].num_elems; Datum *elem_values = arrayKeys[j].elem_values; bool *elem_nulls = arrayKeys[j].elem_nulls; if (next_elem >= num_elems) { next_elem = 0; found = false; /* need to advance next array key */ } else found = true; scan_key->sk_argument = elem_values[next_elem]; if (elem_nulls[next_elem]) scan_key->sk_flags |= SK_ISNULL; else scan_key->sk_flags &= ~SK_ISNULL; arrayKeys[j].next_elem = next_elem + 1; if (found) break; } return found; }
void ExecIndexBuildScanKeys | ( | PlanState * | planstate, | |
Relation | index, | |||
List * | quals, | |||
bool | isorderby, | |||
ScanKey * | scanKeys, | |||
int * | numScanKeys, | |||
IndexRuntimeKeyInfo ** | runtimeKeys, | |||
int * | numRuntimeKeys, | |||
IndexArrayKeyInfo ** | arrayKeys, | |||
int * | numArrayKeys | |||
) |
Definition at line 688 of file nodeIndexscan.c.
References NullTest::arg, arg, ScalarArrayOpExpr::args, IndexArrayKeyInfo::array_expr, Assert, BTORDER_PROC, BTREE_AM_OID, elog, ERROR, ExecInitExpr(), get_leftop(), get_op_opfamily_properties(), get_opfamily_proc(), get_rightop(), INDEX_VAR, ScalarArrayOpExpr::inputcollid, RowCompareExpr::inputcollids, InvalidOid, InvalidStrategy, IS_NOT_NULL, IS_NULL, IsA, IndexRuntimeKeyInfo::key_expr, IndexRuntimeKeyInfo::key_toastable, RowCompareExpr::largs, lfirst, lfirst_oid, linitial, list_head(), list_length(), lnext, lsecond, MemSet, nodeTag, NULL, NullTest::nulltesttype, ScalarArrayOpExpr::opfuncid, ScalarArrayOpExpr::opno, RowCompareExpr::opnos, palloc(), palloc0(), pfree(), PointerGetDatum, RowCompareExpr::rargs, RowCompareExpr::rctype, RelationData::rd_am, RelationData::rd_index, RelationData::rd_opfamily, RelationData::rd_rel, repalloc(), IndexArrayKeyInfo::scan_key, IndexRuntimeKeyInfo::scan_key, ScanKeyEntryInitialize(), ScanKeyData::sk_argument, ScanKeyData::sk_attno, ScanKeyData::sk_flags, SK_ISNULL, ScanKeyData::sk_strategy, TypeIsToastable, and ScalarArrayOpExpr::useOr.
Referenced by ExecInitBitmapIndexScan(), ExecInitIndexOnlyScan(), and ExecInitIndexScan().
{ ListCell *qual_cell; ScanKey scan_keys; IndexRuntimeKeyInfo *runtime_keys; IndexArrayKeyInfo *array_keys; int n_scan_keys; int n_runtime_keys; int max_runtime_keys; int n_array_keys; int j; /* Allocate array for ScanKey structs: one per qual */ n_scan_keys = list_length(quals); scan_keys = (ScanKey) palloc(n_scan_keys * sizeof(ScanKeyData)); /* * runtime_keys array is dynamically resized as needed. We handle it this * way so that the same runtime keys array can be shared between * indexquals and indexorderbys, which will be processed in separate calls * of this function. Caller must be sure to pass in NULL/0 for first * call. */ runtime_keys = *runtimeKeys; n_runtime_keys = max_runtime_keys = *numRuntimeKeys; /* Allocate array_keys as large as it could possibly need to be */ array_keys = (IndexArrayKeyInfo *) palloc0(n_scan_keys * sizeof(IndexArrayKeyInfo)); n_array_keys = 0; /* * for each opclause in the given qual, convert the opclause into a single * scan key */ j = 0; foreach(qual_cell, quals) { Expr *clause = (Expr *) lfirst(qual_cell); ScanKey this_scan_key = &scan_keys[j++]; Oid opno; /* operator's OID */ RegProcedure opfuncid; /* operator proc id used in scan */ Oid opfamily; /* opfamily of index column */ int op_strategy; /* operator's strategy number */ Oid op_lefttype; /* operator's declared input types */ Oid op_righttype; Expr *leftop; /* expr on lhs of operator */ Expr *rightop; /* expr on rhs ... */ AttrNumber varattno; /* att number used in scan */ if (IsA(clause, OpExpr)) { /* indexkey op const or indexkey op expression */ int flags = 0; Datum scanvalue; opno = ((OpExpr *) clause)->opno; opfuncid = ((OpExpr *) clause)->opfuncid; /* * leftop should be the index key Var, possibly relabeled */ leftop = (Expr *) get_leftop(clause); if (leftop && IsA(leftop, RelabelType)) leftop = ((RelabelType *) leftop)->arg; Assert(leftop != NULL); if (!(IsA(leftop, Var) && ((Var *) leftop)->varno == INDEX_VAR)) elog(ERROR, "indexqual doesn't have key on left side"); varattno = ((Var *) leftop)->varattno; if (varattno < 1 || varattno > index->rd_index->indnatts) elog(ERROR, "bogus index qualification"); /* * We have to look up the operator's strategy number. This * provides a cross-check that the operator does match the index. */ opfamily = index->rd_opfamily[varattno - 1]; get_op_opfamily_properties(opno, opfamily, isorderby, &op_strategy, &op_lefttype, &op_righttype); if (isorderby) flags |= SK_ORDER_BY; /* * rightop is the constant or variable comparison value */ rightop = (Expr *) get_rightop(clause); if (rightop && IsA(rightop, RelabelType)) rightop = ((RelabelType *) rightop)->arg; Assert(rightop != NULL); if (IsA(rightop, Const)) { /* OK, simple constant comparison value */ scanvalue = ((Const *) rightop)->constvalue; if (((Const *) rightop)->constisnull) flags |= SK_ISNULL; } else { /* Need to treat this one as a runtime key */ if (n_runtime_keys >= max_runtime_keys) { if (max_runtime_keys == 0) { max_runtime_keys = 8; runtime_keys = (IndexRuntimeKeyInfo *) palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo)); } else { max_runtime_keys *= 2; runtime_keys = (IndexRuntimeKeyInfo *) repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo)); } } runtime_keys[n_runtime_keys].scan_key = this_scan_key; runtime_keys[n_runtime_keys].key_expr = ExecInitExpr(rightop, planstate); runtime_keys[n_runtime_keys].key_toastable = TypeIsToastable(op_righttype); n_runtime_keys++; scanvalue = (Datum) 0; } /* * initialize the scan key's fields appropriately */ ScanKeyEntryInitialize(this_scan_key, flags, varattno, /* attribute number to scan */ op_strategy, /* op's strategy */ op_righttype, /* strategy subtype */ ((OpExpr *) clause)->inputcollid, /* collation */ opfuncid, /* reg proc to use */ scanvalue); /* constant */ } else if (IsA(clause, RowCompareExpr)) { /* (indexkey, indexkey, ...) op (expression, expression, ...) */ RowCompareExpr *rc = (RowCompareExpr *) clause; ListCell *largs_cell = list_head(rc->largs); ListCell *rargs_cell = list_head(rc->rargs); ListCell *opnos_cell = list_head(rc->opnos); ListCell *collids_cell = list_head(rc->inputcollids); ScanKey first_sub_key; int n_sub_key; Assert(!isorderby); first_sub_key = (ScanKey) palloc(list_length(rc->opnos) * sizeof(ScanKeyData)); n_sub_key = 0; /* Scan RowCompare columns and generate subsidiary ScanKey items */ while (opnos_cell != NULL) { ScanKey this_sub_key = &first_sub_key[n_sub_key]; int flags = SK_ROW_MEMBER; Datum scanvalue; Oid inputcollation; /* * leftop should be the index key Var, possibly relabeled */ leftop = (Expr *) lfirst(largs_cell); largs_cell = lnext(largs_cell); if (leftop && IsA(leftop, RelabelType)) leftop = ((RelabelType *) leftop)->arg; Assert(leftop != NULL); if (!(IsA(leftop, Var) && ((Var *) leftop)->varno == INDEX_VAR)) elog(ERROR, "indexqual doesn't have key on left side"); varattno = ((Var *) leftop)->varattno; /* * We have to look up the operator's associated btree support * function */ opno = lfirst_oid(opnos_cell); opnos_cell = lnext(opnos_cell); if (index->rd_rel->relam != BTREE_AM_OID || varattno < 1 || varattno > index->rd_index->indnatts) elog(ERROR, "bogus RowCompare index qualification"); opfamily = index->rd_opfamily[varattno - 1]; get_op_opfamily_properties(opno, opfamily, isorderby, &op_strategy, &op_lefttype, &op_righttype); if (op_strategy != rc->rctype) elog(ERROR, "RowCompare index qualification contains wrong operator"); opfuncid = get_opfamily_proc(opfamily, op_lefttype, op_righttype, BTORDER_PROC); inputcollation = lfirst_oid(collids_cell); collids_cell = lnext(collids_cell); /* * rightop is the constant or variable comparison value */ rightop = (Expr *) lfirst(rargs_cell); rargs_cell = lnext(rargs_cell); if (rightop && IsA(rightop, RelabelType)) rightop = ((RelabelType *) rightop)->arg; Assert(rightop != NULL); if (IsA(rightop, Const)) { /* OK, simple constant comparison value */ scanvalue = ((Const *) rightop)->constvalue; if (((Const *) rightop)->constisnull) flags |= SK_ISNULL; } else { /* Need to treat this one as a runtime key */ if (n_runtime_keys >= max_runtime_keys) { if (max_runtime_keys == 0) { max_runtime_keys = 8; runtime_keys = (IndexRuntimeKeyInfo *) palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo)); } else { max_runtime_keys *= 2; runtime_keys = (IndexRuntimeKeyInfo *) repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo)); } } runtime_keys[n_runtime_keys].scan_key = this_sub_key; runtime_keys[n_runtime_keys].key_expr = ExecInitExpr(rightop, planstate); runtime_keys[n_runtime_keys].key_toastable = TypeIsToastable(op_righttype); n_runtime_keys++; scanvalue = (Datum) 0; } /* * initialize the subsidiary scan key's fields appropriately */ ScanKeyEntryInitialize(this_sub_key, flags, varattno, /* attribute number */ op_strategy, /* op's strategy */ op_righttype, /* strategy subtype */ inputcollation, /* collation */ opfuncid, /* reg proc to use */ scanvalue); /* constant */ n_sub_key++; } /* Mark the last subsidiary scankey correctly */ first_sub_key[n_sub_key - 1].sk_flags |= SK_ROW_END; /* * We don't use ScanKeyEntryInitialize for the header because it * isn't going to contain a valid sk_func pointer. */ MemSet(this_scan_key, 0, sizeof(ScanKeyData)); this_scan_key->sk_flags = SK_ROW_HEADER; this_scan_key->sk_attno = first_sub_key->sk_attno; this_scan_key->sk_strategy = rc->rctype; /* sk_subtype, sk_collation, sk_func not used in a header */ this_scan_key->sk_argument = PointerGetDatum(first_sub_key); } else if (IsA(clause, ScalarArrayOpExpr)) { /* indexkey op ANY (array-expression) */ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; int flags = 0; Datum scanvalue; Assert(!isorderby); Assert(saop->useOr); opno = saop->opno; opfuncid = saop->opfuncid; /* * leftop should be the index key Var, possibly relabeled */ leftop = (Expr *) linitial(saop->args); if (leftop && IsA(leftop, RelabelType)) leftop = ((RelabelType *) leftop)->arg; Assert(leftop != NULL); if (!(IsA(leftop, Var) && ((Var *) leftop)->varno == INDEX_VAR)) elog(ERROR, "indexqual doesn't have key on left side"); varattno = ((Var *) leftop)->varattno; if (varattno < 1 || varattno > index->rd_index->indnatts) elog(ERROR, "bogus index qualification"); /* * We have to look up the operator's strategy number. This * provides a cross-check that the operator does match the index. */ opfamily = index->rd_opfamily[varattno - 1]; get_op_opfamily_properties(opno, opfamily, isorderby, &op_strategy, &op_lefttype, &op_righttype); /* * rightop is the constant or variable array value */ rightop = (Expr *) lsecond(saop->args); if (rightop && IsA(rightop, RelabelType)) rightop = ((RelabelType *) rightop)->arg; Assert(rightop != NULL); if (index->rd_am->amsearcharray) { /* Index AM will handle this like a simple operator */ flags |= SK_SEARCHARRAY; if (IsA(rightop, Const)) { /* OK, simple constant comparison value */ scanvalue = ((Const *) rightop)->constvalue; if (((Const *) rightop)->constisnull) flags |= SK_ISNULL; } else { /* Need to treat this one as a runtime key */ if (n_runtime_keys >= max_runtime_keys) { if (max_runtime_keys == 0) { max_runtime_keys = 8; runtime_keys = (IndexRuntimeKeyInfo *) palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo)); } else { max_runtime_keys *= 2; runtime_keys = (IndexRuntimeKeyInfo *) repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo)); } } runtime_keys[n_runtime_keys].scan_key = this_scan_key; runtime_keys[n_runtime_keys].key_expr = ExecInitExpr(rightop, planstate); /* * Careful here: the runtime expression is not of * op_righttype, but rather is an array of same; so * TypeIsToastable() isn't helpful. However, we can * assume that all array types are toastable. */ runtime_keys[n_runtime_keys].key_toastable = true; n_runtime_keys++; scanvalue = (Datum) 0; } } else { /* Executor has to expand the array value */ array_keys[n_array_keys].scan_key = this_scan_key; array_keys[n_array_keys].array_expr = ExecInitExpr(rightop, planstate); /* the remaining fields were zeroed by palloc0 */ n_array_keys++; scanvalue = (Datum) 0; } /* * initialize the scan key's fields appropriately */ ScanKeyEntryInitialize(this_scan_key, flags, varattno, /* attribute number to scan */ op_strategy, /* op's strategy */ op_righttype, /* strategy subtype */ saop->inputcollid, /* collation */ opfuncid, /* reg proc to use */ scanvalue); /* constant */ } else if (IsA(clause, NullTest)) { /* indexkey IS NULL or indexkey IS NOT NULL */ NullTest *ntest = (NullTest *) clause; int flags; Assert(!isorderby); /* * argument should be the index key Var, possibly relabeled */ leftop = ntest->arg; if (leftop && IsA(leftop, RelabelType)) leftop = ((RelabelType *) leftop)->arg; Assert(leftop != NULL); if (!(IsA(leftop, Var) && ((Var *) leftop)->varno == INDEX_VAR)) elog(ERROR, "NullTest indexqual has wrong key"); varattno = ((Var *) leftop)->varattno; /* * initialize the scan key's fields appropriately */ switch (ntest->nulltesttype) { case IS_NULL: flags = SK_ISNULL | SK_SEARCHNULL; break; case IS_NOT_NULL: flags = SK_ISNULL | SK_SEARCHNOTNULL; break; default: elog(ERROR, "unrecognized nulltesttype: %d", (int) ntest->nulltesttype); flags = 0; /* keep compiler quiet */ break; } ScanKeyEntryInitialize(this_scan_key, flags, varattno, /* attribute number to scan */ InvalidStrategy, /* no strategy */ InvalidOid, /* no strategy subtype */ InvalidOid, /* no collation */ InvalidOid, /* no reg proc for this */ (Datum) 0); /* constant */ } else elog(ERROR, "unsupported indexqual type: %d", (int) nodeTag(clause)); } Assert(n_runtime_keys <= max_runtime_keys); /* Get rid of any unused arrays */ if (n_array_keys == 0) { pfree(array_keys); array_keys = NULL; } /* * Return info to our caller. */ *scanKeys = scan_keys; *numScanKeys = n_scan_keys; *runtimeKeys = runtime_keys; *numRuntimeKeys = n_runtime_keys; if (arrayKeys) { *arrayKeys = array_keys; *numArrayKeys = n_array_keys; } else if (n_array_keys != 0) elog(ERROR, "ScalarArrayOpExpr index qual found where not allowed"); }
bool ExecIndexEvalArrayKeys | ( | ExprContext * | econtext, | |
IndexArrayKeyInfo * | arrayKeys, | |||
int | numArrayKeys | |||
) |
Definition at line 264 of file nodeIndexscan.c.
References ARR_ELEMTYPE, IndexArrayKeyInfo::array_expr, DatumGetArrayTypeP, deconstruct_array(), ExprContext::ecxt_per_tuple_memory, IndexArrayKeyInfo::elem_nulls, IndexArrayKeyInfo::elem_values, ExecEvalExpr, get_typlenbyvalalign(), MemoryContextSwitchTo(), IndexArrayKeyInfo::next_elem, NULL, IndexArrayKeyInfo::num_elems, IndexArrayKeyInfo::scan_key, ScanKeyData::sk_argument, and ScanKeyData::sk_flags.
Referenced by ExecReScanBitmapIndexScan().
{ bool result = true; int j; MemoryContext oldContext; /* We want to keep the arrays in per-tuple memory */ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); for (j = 0; j < numArrayKeys; j++) { ScanKey scan_key = arrayKeys[j].scan_key; ExprState *array_expr = arrayKeys[j].array_expr; Datum arraydatum; bool isNull; ArrayType *arrayval; int16 elmlen; bool elmbyval; char elmalign; int num_elems; Datum *elem_values; bool *elem_nulls; /* * Compute and deconstruct the array expression. (Notes in * ExecIndexEvalRuntimeKeys() apply here too.) */ arraydatum = ExecEvalExpr(array_expr, econtext, &isNull, NULL); if (isNull) { result = false; break; /* no point in evaluating more */ } arrayval = DatumGetArrayTypeP(arraydatum); /* We could cache this data, but not clear it's worth it */ get_typlenbyvalalign(ARR_ELEMTYPE(arrayval), &elmlen, &elmbyval, &elmalign); deconstruct_array(arrayval, ARR_ELEMTYPE(arrayval), elmlen, elmbyval, elmalign, &elem_values, &elem_nulls, &num_elems); if (num_elems <= 0) { result = false; break; /* no point in evaluating more */ } /* * Note: we expect the previous array data, if any, to be * automatically freed by resetting the per-tuple context; hence no * pfree's here. */ arrayKeys[j].elem_values = elem_values; arrayKeys[j].elem_nulls = elem_nulls; arrayKeys[j].num_elems = num_elems; scan_key->sk_argument = elem_values[0]; if (elem_nulls[0]) scan_key->sk_flags |= SK_ISNULL; else scan_key->sk_flags &= ~SK_ISNULL; arrayKeys[j].next_elem = 1; } MemoryContextSwitchTo(oldContext); return result; }
void ExecIndexEvalRuntimeKeys | ( | ExprContext * | econtext, | |
IndexRuntimeKeyInfo * | runtimeKeys, | |||
int | numRuntimeKeys | |||
) |
Definition at line 201 of file nodeIndexscan.c.
References ExprContext::ecxt_per_tuple_memory, ExecEvalExpr, IndexRuntimeKeyInfo::key_expr, MemoryContextSwitchTo(), NULL, PG_DETOAST_DATUM, PointerGetDatum, IndexRuntimeKeyInfo::scan_key, ScanKeyData::sk_argument, and ScanKeyData::sk_flags.
Referenced by ExecReScanBitmapIndexScan(), ExecReScanIndexOnlyScan(), and ExecReScanIndexScan().
{ int j; MemoryContext oldContext; /* We want to keep the key values in per-tuple memory */ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); for (j = 0; j < numRuntimeKeys; j++) { ScanKey scan_key = runtimeKeys[j].scan_key; ExprState *key_expr = runtimeKeys[j].key_expr; Datum scanvalue; bool isNull; /* * For each run-time key, extract the run-time expression and evaluate * it with respect to the current context. We then stick the result * into the proper scan key. * * Note: the result of the eval could be a pass-by-ref value that's * stored in some 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. * * It's also entirely possible that the result of the eval is a * toasted value. In this case we should forcibly detoast it, to * avoid repeat detoastings each time the value is examined by an * index support function. */ scanvalue = ExecEvalExpr(key_expr, econtext, &isNull, NULL); if (isNull) { scan_key->sk_argument = scanvalue; scan_key->sk_flags |= SK_ISNULL; } else { if (runtimeKeys[j].key_toastable) scanvalue = PointerGetDatum(PG_DETOAST_DATUM(scanvalue)); scan_key->sk_argument = scanvalue; scan_key->sk_flags &= ~SK_ISNULL; } } MemoryContextSwitchTo(oldContext); }
void ExecIndexMarkPos | ( | IndexScanState * | node | ) |
Definition at line 436 of file nodeIndexscan.c.
References index_markpos(), and IndexScanState::iss_ScanDesc.
Referenced by ExecMarkPos().
{ index_markpos(node->iss_ScanDesc); }
void ExecIndexRestrPos | ( | IndexScanState * | node | ) |
Definition at line 446 of file nodeIndexscan.c.
References index_restrpos(), and IndexScanState::iss_ScanDesc.
Referenced by ExecRestrPos().
{ index_restrpos(node->iss_ScanDesc); }
TupleTableSlot* ExecIndexScan | ( | IndexScanState * | node | ) |
Definition at line 142 of file nodeIndexscan.c.
References ExecReScan(), ExecScan(), IndexNext(), IndexRecheck(), IndexScanState::iss_NumRuntimeKeys, and IndexScanState::iss_RuntimeKeysReady.
Referenced by ExecProcNode().
{ /* * If we have runtime keys and they've not already been set up, do it now. */ if (node->iss_NumRuntimeKeys != 0 && !node->iss_RuntimeKeysReady) ExecReScan((PlanState *) node); return ExecScan(&node->ss, (ExecScanAccessMtd) IndexNext, (ExecScanRecheckMtd) IndexRecheck); }
IndexScanState* ExecInitIndexScan | ( | IndexScan * | node, | |
EState * | estate, | |||
int | eflags | |||
) |
Definition at line 463 of file nodeIndexscan.c.
References AccessShareLock, EState::es_snapshot, EXEC_FLAG_EXPLAIN_ONLY, ExecAssignExprContext(), ExecAssignResultTypeFromTL(), ExecAssignScanProjectionInfo(), ExecAssignScanType(), ExecIndexBuildScanKeys(), ExecInitExpr(), ExecInitResultTupleSlot(), ExecInitScanTupleSlot(), ExecOpenScanRelation(), ExecRelationIsTargetRelation(), index_beginscan(), index_open(), index_rescan(), IndexScan::indexid, IndexScan::indexorderby, IndexScan::indexqual, IndexScan::indexqualorig, IndexScanState::indexqualorig, IndexScanState::iss_NumRuntimeKeys, IndexScanState::iss_RelationDesc, IndexScanState::iss_RuntimeKeys, IndexScanState::iss_RuntimeKeysReady, makeNode, NoLock, NULL, Scan::plan, PlanState::plan, ScanState::ps, PlanState::ps_ExprContext, PlanState::ps_TupFromTlist, Plan::qual, PlanState::qual, RelationGetDescr, IndexScan::scan, Scan::scanrelid, IndexScanState::ss, ScanState::ss_currentRelation, ScanState::ss_currentScanDesc, PlanState::state, Plan::targetlist, and PlanState::targetlist.
Referenced by ExecInitNode().
{ IndexScanState *indexstate; Relation currentRelation; bool relistarget; /* * create state structure */ indexstate = makeNode(IndexScanState); indexstate->ss.ps.plan = (Plan *) node; indexstate->ss.ps.state = estate; /* * 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 indexqual expression, only the * sub-parts corresponding to runtime keys (see below). Likewise for * indexorderby, if any. But the indexqualorig expression is always * initialized even though it will only be used in some uncommon cases --- * would be nice to improve that. (Problem is that any SubPlans present * in the expression must be found now...) */ 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->indexqualorig = (List *) ExecInitExpr((Expr *) node->indexqualorig, (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 */ /* * get the scan type from the relation descriptor. */ ExecAssignScanType(&indexstate->ss, RelationGetDescr(currentRelation)); /* * 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->iss_RelationDesc = index_open(node->indexid, relistarget ? NoLock : AccessShareLock); /* * Initialize index-specific scan state */ indexstate->iss_RuntimeKeysReady = false; indexstate->iss_RuntimeKeys = NULL; indexstate->iss_NumRuntimeKeys = 0; /* * build the index scan keys from the index qualification */ ExecIndexBuildScanKeys((PlanState *) indexstate, indexstate->iss_RelationDesc, node->indexqual, false, &indexstate->iss_ScanKeys, &indexstate->iss_NumScanKeys, &indexstate->iss_RuntimeKeys, &indexstate->iss_NumRuntimeKeys, NULL, /* no ArrayKeys */ NULL); /* * any ORDER BY exprs have to be turned into scankeys in the same way */ ExecIndexBuildScanKeys((PlanState *) indexstate, indexstate->iss_RelationDesc, node->indexorderby, true, &indexstate->iss_OrderByKeys, &indexstate->iss_NumOrderByKeys, &indexstate->iss_RuntimeKeys, &indexstate->iss_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->iss_NumRuntimeKeys != 0) { ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext; ExecAssignExprContext(estate, &indexstate->ss.ps); indexstate->iss_RuntimeContext = indexstate->ss.ps.ps_ExprContext; indexstate->ss.ps.ps_ExprContext = stdecontext; } else { indexstate->iss_RuntimeContext = NULL; } /* * Initialize scan descriptor. */ indexstate->iss_ScanDesc = index_beginscan(currentRelation, indexstate->iss_RelationDesc, estate->es_snapshot, indexstate->iss_NumScanKeys, indexstate->iss_NumOrderByKeys); /* * If no run-time keys to calculate, go ahead and pass the scankeys to the * index AM. */ if (indexstate->iss_NumRuntimeKeys == 0) index_rescan(indexstate->iss_ScanDesc, indexstate->iss_ScanKeys, indexstate->iss_NumScanKeys, indexstate->iss_OrderByKeys, indexstate->iss_NumOrderByKeys); /* * all done. */ return indexstate; }
void ExecReScanIndexScan | ( | IndexScanState * | node | ) |
Definition at line 167 of file nodeIndexscan.c.
References ExecIndexEvalRuntimeKeys(), ExecScanReScan(), index_rescan(), IndexScanState::iss_NumOrderByKeys, IndexScanState::iss_NumRuntimeKeys, IndexScanState::iss_NumScanKeys, IndexScanState::iss_OrderByKeys, IndexScanState::iss_RuntimeContext, IndexScanState::iss_RuntimeKeys, IndexScanState::iss_RuntimeKeysReady, IndexScanState::iss_ScanDesc, IndexScanState::iss_ScanKeys, ResetExprContext, and IndexScanState::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->iss_NumRuntimeKeys != 0) { ExprContext *econtext = node->iss_RuntimeContext; ResetExprContext(econtext); ExecIndexEvalRuntimeKeys(econtext, node->iss_RuntimeKeys, node->iss_NumRuntimeKeys); } node->iss_RuntimeKeysReady = true; /* reset index scan */ index_rescan(node->iss_ScanDesc, node->iss_ScanKeys, node->iss_NumScanKeys, node->iss_OrderByKeys, node->iss_NumOrderByKeys); ExecScanReScan(&node->ss); }
static TupleTableSlot * IndexNext | ( | IndexScanState * | node | ) | [static] |
Definition at line 49 of file nodeIndexscan.c.
References ExprContext::ecxt_scantuple, EState::es_direction, ExecClearTuple(), ExecQual(), ExecStoreTuple(), index_getnext(), IndexScanState::indexqualorig, InstrCountFiltered2, IndexScanState::iss_ScanDesc, NULL, PlanState::plan, ScanState::ps, PlanState::ps_ExprContext, ResetExprContext, ScanDirectionIsBackward, ScanDirectionIsForward, IndexScanState::ss, ScanState::ss_ScanTupleSlot, PlanState::state, IndexScanDescData::xs_cbuf, and IndexScanDescData::xs_recheck.
Referenced by ExecIndexScan().
{ EState *estate; ExprContext *econtext; ScanDirection direction; IndexScanDesc scandesc; HeapTuple tuple; TupleTableSlot *slot; /* * 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(((IndexScan *) node->ss.ps.plan)->indexorderdir)) { if (ScanDirectionIsForward(direction)) direction = BackwardScanDirection; else if (ScanDirectionIsBackward(direction)) direction = ForwardScanDirection; } scandesc = node->iss_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 ((tuple = index_getnext(scandesc, direction)) != NULL) { /* * Store the scanned tuple in the scan tuple slot of the scan state. * Note: we pass 'false' because tuples returned by amgetnext are * pointers onto disk pages and must not be pfree()'d. */ ExecStoreTuple(tuple, /* tuple to store */ slot, /* slot to store in */ scandesc->xs_cbuf, /* buffer containing tuple */ false); /* don't pfree */ /* * If the index was lossy, we have to recheck the index quals using * the fetched tuple. */ if (scandesc->xs_recheck) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); if (!ExecQual(node->indexqualorig, econtext, false)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); continue; } } 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 IndexRecheck | ( | IndexScanState * | node, | |
TupleTableSlot * | slot | |||
) | [static] |
Definition at line 120 of file nodeIndexscan.c.
References ExprContext::ecxt_scantuple, ExecQual(), IndexScanState::indexqualorig, ScanState::ps, PlanState::ps_ExprContext, ResetExprContext, and IndexScanState::ss.
Referenced by ExecIndexScan().
{ ExprContext *econtext; /* * extract necessary information from index scan node */ econtext = node->ss.ps.ps_ExprContext; /* Does the tuple meet the indexqual condition? */ econtext->ecxt_scantuple = slot; ResetExprContext(econtext); return ExecQual(node->indexqualorig, econtext, false); }