diff options
Diffstat (limited to 'src/backend/executor/nodeIndexscan.c')
-rw-r--r-- | src/backend/executor/nodeIndexscan.c | 476 |
1 files changed, 330 insertions, 146 deletions
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 4f6fadfde49..f34a14c8596 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.106 2005/11/25 04:24:48 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.107 2005/11/25 19:47:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,6 +32,8 @@ #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "parser/parsetree.h" +#include "utils/array.h" +#include "utils/lsyscache.h" #include "utils/memutils.h" @@ -138,7 +140,7 @@ ExecIndexScan(IndexScanState *node) /* * If we have runtime keys and they've not already been set up, do it now. */ - if (node->iss_RuntimeKeyInfo && !node->iss_RuntimeKeysReady) + if (node->iss_NumRuntimeKeys != 0 && !node->iss_RuntimeKeysReady) ExecReScan((PlanState *) node, NULL); /* @@ -162,16 +164,10 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt) { EState *estate; ExprContext *econtext; - ScanKey scanKeys; - ExprState **runtimeKeyInfo; - int numScanKeys; Index scanrelid; estate = node->ss.ps.state; econtext = node->iss_RuntimeContext; /* context for runtime keys */ - scanKeys = node->iss_ScanKeys; - runtimeKeyInfo = node->iss_RuntimeKeyInfo; - numScanKeys = node->iss_NumScanKeys; scanrelid = ((IndexScan *) node->ss.ps.plan)->scan.scanrelid; if (econtext) @@ -202,14 +198,11 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt) * 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) - { + if (node->iss_NumRuntimeKeys != 0) ExecIndexEvalRuntimeKeys(econtext, - runtimeKeyInfo, - scanKeys, - numScanKeys); - node->iss_RuntimeKeysReady = true; - } + node->iss_RuntimeKeys, + node->iss_NumRuntimeKeys); + node->iss_RuntimeKeysReady = true; /* If this is re-scanning of PlanQual ... */ if (estate->es_evTuple != NULL && @@ -220,7 +213,7 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt) } /* reset index scan */ - index_rescan(node->iss_ScanDesc, scanKeys); + index_rescan(node->iss_ScanDesc, node->iss_ScanKeys); } @@ -230,18 +223,21 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt) */ void ExecIndexEvalRuntimeKeys(ExprContext *econtext, - ExprState **run_keys, - ScanKey scan_keys, - int n_keys) + IndexRuntimeKeyInfo *runtimeKeys, int numRuntimeKeys) { int j; - for (j = 0; j < n_keys; j++) + for (j = 0; j < numRuntimeKeys; j++) { + ScanKey scan_key = runtimeKeys[j].scan_key; + ExprState *key_expr = runtimeKeys[j].key_expr; + Datum scanvalue; + bool isNull; + /* - * If we have a run-time key, then extract the run-time expression and + * For each run-time key, extract the run-time expression and * evaluate it with respect to the current outer tuple. We then stick - * the result into the scan key. + * the result into the proper 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 @@ -250,24 +246,140 @@ ExecIndexEvalRuntimeKeys(ExprContext *econtext, * the result into our context explicitly, but I think that's not * necessary... */ - if (run_keys[j] != NULL) + scanvalue = ExecEvalExprSwitchContext(key_expr, + econtext, + &isNull, + NULL); + scan_key->sk_argument = scanvalue; + if (isNull) + scan_key->sk_flags |= SK_ISNULL; + else + scan_key->sk_flags &= ~SK_ISNULL; + } +} + +/* + * ExecIndexEvalArrayKeys + * Evaluate any array key values, and set up to iterate through arrays. + * + * Returns TRUE if there are array elements to consider; FALSE means there + * is at least one null or empty array, so no match is possible. On TRUE + * result, the scankeys are initialized with the first elements of the arrays. + */ +bool +ExecIndexEvalArrayKeys(ExprContext *econtext, + IndexArrayKeyInfo *arrayKeys, int numArrayKeys) +{ + 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) { - 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; + 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; +} + +/* + * ExecIndexAdvanceArrayKeys + * Advance to the next set of array key values, if any. + * + * Returns TRUE if there is another set of values to consider, FALSE if not. + * On TRUE result, the scankeys are initialized with the next set of values. + */ +bool +ExecIndexAdvanceArrayKeys(IndexArrayKeyInfo *arrayKeys, int numArrayKeys) +{ + bool found = false; + int j; + + for (j = 0; j < numArrayKeys; 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; } + /* ---------------------------------------------------------------- * ExecEndIndexScan * ---------------------------------------------------------------- @@ -352,10 +464,6 @@ IndexScanState * ExecInitIndexScan(IndexScan *node, EState *estate) { IndexScanState *indexstate; - ScanKey scanKeys; - int numScanKeys; - ExprState **runtimeKeyInfo; - bool have_runtime_keys; RangeTblEntry *rtentry; Index relid; Oid reloid; @@ -412,18 +520,16 @@ ExecInitIndexScan(IndexScan *node, EState *estate) /* * build the index scan keys from the index qualification */ - have_runtime_keys = - ExecIndexBuildScanKeys((PlanState *) indexstate, - node->indexqual, - node->indexstrategy, - node->indexsubtype, - &runtimeKeyInfo, - &scanKeys, - &numScanKeys); - - indexstate->iss_RuntimeKeyInfo = runtimeKeyInfo; - indexstate->iss_ScanKeys = scanKeys; - indexstate->iss_NumScanKeys = numScanKeys; + ExecIndexBuildScanKeys((PlanState *) indexstate, + node->indexqual, + node->indexstrategy, + node->indexsubtype, + &indexstate->iss_ScanKeys, + &indexstate->iss_NumScanKeys, + &indexstate->iss_RuntimeKeys, + &indexstate->iss_NumRuntimeKeys, + NULL, /* no ArrayKeys */ + NULL); /* * If we have runtime keys, we need an ExprContext to evaluate them. The @@ -431,7 +537,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate) * for every tuple. So, build another context just like the other one... * -tgl 7/11/00 */ - if (have_runtime_keys) + if (indexstate->iss_NumRuntimeKeys != 0) { ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext; @@ -471,8 +577,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate) indexstate->iss_ScanDesc = index_beginscan(currentRelation, indexstate->iss_RelationDesc, estate->es_snapshot, - numScanKeys, - scanKeys); + indexstate->iss_NumScanKeys, + indexstate->iss_ScanKeys); /* * Initialize result tuple type and projection info. @@ -489,7 +595,26 @@ ExecInitIndexScan(IndexScan *node, EState *estate) /* * ExecIndexBuildScanKeys - * Build the index scan keys from the index qualification + * Build the index scan keys from the index qualification expressions + * + * The index quals are passed to the index AM in the form of a ScanKey array. + * This routine sets up the ScanKeys, fills in all constant fields of the + * ScanKeys, and prepares information about the keys that have non-constant + * comparison values. We divide index qual expressions into three types: + * + * 1. Simple operator with constant comparison value ("indexkey op constant"). + * For these, we just fill in a ScanKey containing the constant value. + * + * 2. Simple operator with non-constant value ("indexkey op expression"). + * For these, we create a ScanKey with everything filled in except the + * expression value, and set up an IndexRuntimeKeyInfo struct to drive + * evaluation of the expression at the right times. + * + * 3. ScalarArrayOpExpr ("indexkey op ANY (array-expression)"). For these, + * we create a ScanKey with everything filled in except the comparison value, + * and set up an IndexArrayKeyInfo struct to drive processing of the qual. + * (Note that we treat all array-expressions as requiring runtime evaluation, + * even if they happen to be constants.) * * Input params are: * @@ -500,33 +625,43 @@ ExecInitIndexScan(IndexScan *node, EState *estate) * * Output params are: * - * *runtimeKeyInfo: receives ptr to array of runtime key exprstates - * (NULL if no runtime keys) * *scanKeys: receives ptr to array of ScanKeys - * *numScanKeys: receives number of scankeys/runtime keys + * *numScanKeys: receives number of scankeys + * *runtimeKeys: receives ptr to array of IndexRuntimeKeyInfos, or NULL if none + * *numRuntimeKeys: receives number of runtime keys + * *arrayKeys: receives ptr to array of IndexArrayKeyInfos, or NULL if none + * *numArrayKeys: receives number of array keys * - * Return value is TRUE if any runtime key expressions were found, else FALSE. + * Caller may pass NULL for arrayKeys and numArrayKeys to indicate that + * ScalarArrayOpExpr quals are not supported. */ -bool +void ExecIndexBuildScanKeys(PlanState *planstate, List *quals, List *strategies, List *subtypes, - ExprState ***runtimeKeyInfo, - ScanKey *scanKeys, int *numScanKeys) + ScanKey *scanKeys, int *numScanKeys, + IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys, + IndexArrayKeyInfo **arrayKeys, int *numArrayKeys) { - bool have_runtime_keys = false; ListCell *qual_cell; ListCell *strategy_cell; ListCell *subtype_cell; - int n_keys; ScanKey scan_keys; - ExprState **run_keys; + IndexRuntimeKeyInfo *runtime_keys; + IndexArrayKeyInfo *array_keys; + int n_scan_keys; + int n_runtime_keys; + int n_array_keys; int j; - 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 *)); + n_scan_keys = list_length(quals); + scan_keys = (ScanKey) palloc(n_scan_keys * sizeof(ScanKeyData)); + /* Allocate these arrays as large as they could possibly need to be */ + runtime_keys = (IndexRuntimeKeyInfo *) + palloc(n_scan_keys * sizeof(IndexRuntimeKeyInfo)); + array_keys = (IndexArrayKeyInfo *) + palloc0(n_scan_keys * sizeof(IndexArrayKeyInfo)); + n_runtime_keys = 0; + n_array_keys = 0; /* * for each opclause in the given qual, convert each qual's opclause into @@ -536,122 +671,171 @@ ExecIndexBuildScanKeys(PlanState *planstate, List *quals, strategy_cell = list_head(strategies); subtype_cell = list_head(subtypes); - for (j = 0; j < n_keys; j++) + for (j = 0; j < n_scan_keys; j++) { - OpExpr *clause; /* one clause of index qual */ + ScanKey this_scan_key = &scan_keys[j]; + Expr *clause; /* one clause of index qual */ + RegProcedure opfuncid; /* operator proc id used in scan */ + StrategyNumber strategy; /* op's strategy number */ + Oid subtype; /* op's strategy subtype */ 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); + clause = (Expr *) 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, "indexqual is not an OpExpr"); + if (IsA(clause, OpExpr)) + { + /* indexkey op const or indexkey op expression */ + int flags = 0; + Datum scanvalue; - opfuncid = clause->opfuncid; + opfuncid = ((OpExpr *) 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; + /* + * leftop should be the index key Var, possibly relabeled + */ + leftop = (Expr *) get_leftop(clause); - /* - * determine information in leftop - */ - leftop = (Expr *) get_leftop((Expr *) clause); + if (leftop && IsA(leftop, RelabelType)) + leftop = ((RelabelType *) leftop)->arg; - if (leftop && IsA(leftop, RelabelType)) - leftop = ((RelabelType *) leftop)->arg; + Assert(leftop != NULL); - Assert(leftop != NULL); + if (!(IsA(leftop, Var) && + var_is_rel((Var *) leftop))) + elog(ERROR, "indexqual doesn't have key on left side"); - if (!(IsA(leftop, Var) && - var_is_rel((Var *) leftop))) - elog(ERROR, "indexqual doesn't have key on left side"); + varattno = ((Var *) leftop)->varattno; - varattno = ((Var *) leftop)->varattno; + /* + * rightop is the constant or variable comparison value + */ + rightop = (Expr *) get_rightop(clause); - /* - * now determine information in rightop - */ - rightop = (Expr *) get_rightop((Expr *) clause); + if (rightop && IsA(rightop, RelabelType)) + rightop = ((RelabelType *) rightop)->arg; - if (rightop && IsA(rightop, RelabelType)) - rightop = ((RelabelType *) rightop)->arg; + Assert(rightop != NULL); - 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 */ + runtime_keys[n_runtime_keys].scan_key = this_scan_key; + runtime_keys[n_runtime_keys].key_expr = + ExecInitExpr(rightop, planstate); + n_runtime_keys++; + scanvalue = (Datum) 0; + } - if (IsA(rightop, Const)) - { /* - * if the rightop is a const node then it means it identifies the - * value to place in our scan key. + * initialize the scan key's fields appropriately */ - scanvalue = ((Const *) rightop)->constvalue; - if (((Const *) rightop)->constisnull) - flags |= SK_ISNULL; + ScanKeyEntryInitialize(this_scan_key, + flags, + varattno, /* attribute number to scan */ + strategy, /* op's strategy */ + subtype, /* strategy subtype */ + opfuncid, /* reg proc to use */ + scanvalue); /* constant */ } - else + else if (IsA(clause, ScalarArrayOpExpr)) { + /* indexkey op ANY (array-expression) */ + ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; + + Assert(saop->useOr); + opfuncid = saop->opfuncid; + /* - * otherwise, the rightop contains an expression evaluable at - * runtime to figure out the value to place in our scan key. + * leftop should be the index key Var, possibly relabeled */ - have_runtime_keys = true; - run_keys[j] = ExecInitExpr(rightop, planstate); - scanvalue = (Datum) 0; - } + leftop = (Expr *) linitial(saop->args); - /* - * 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 */ + if (leftop && IsA(leftop, RelabelType)) + leftop = ((RelabelType *) leftop)->arg; + + Assert(leftop != NULL); + + if (!(IsA(leftop, Var) && + var_is_rel((Var *) leftop))) + elog(ERROR, "indexqual doesn't have key on left side"); + + varattno = ((Var *) leftop)->varattno; + + /* + * 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); + + 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++; + + /* + * initialize the scan key's fields appropriately + */ + ScanKeyEntryInitialize(this_scan_key, + 0, /* flags */ + varattno, /* attribute number to scan */ + strategy, /* op's strategy */ + subtype, /* strategy subtype */ + opfuncid, /* reg proc to use */ + (Datum) 0); /* constant */ + } + else + elog(ERROR, "unsupported indexqual type: %d", + (int) nodeTag(clause)); } - /* If no runtime keys, get rid of speculatively-allocated array */ - if (run_keys && !have_runtime_keys) + /* Get rid of any unused arrays */ + if (n_runtime_keys == 0) + { + pfree(runtime_keys); + runtime_keys = NULL; + } + if (n_array_keys == 0) { - pfree(run_keys); - run_keys = NULL; + pfree(array_keys); + array_keys = NULL; } /* - * Return the info to our caller. + * Return info to our caller. */ - *numScanKeys = n_keys; *scanKeys = scan_keys; - *runtimeKeyInfo = run_keys; - - return have_runtime_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"); } int |