📄 nbtutils.c
字号:
*/ Assert(j != (BTEqualStrategyNumber - 1)); continue; } /* have we seen one of these before? */ if (xform[j] == NULL) { /* nope, so remember this scankey */ xform[j] = cur; } else { /* yup, keep only the more restrictive key */ /* if either arg is NULL, don't try to compare */ if ((cur->sk_flags | xform[j]->sk_flags) & SK_ISNULL) { /* at least one of them must be an IS NULL clause */ Assert(j == (BTEqualStrategyNumber - 1)); Assert((cur->sk_flags | xform[j]->sk_flags) & SK_SEARCHNULL); /* if one is and one isn't, the search must fail */ if ((cur->sk_flags ^ xform[j]->sk_flags) & SK_SEARCHNULL) { so->qual_ok = false; return; } /* we have duplicate IS NULL clauses, ignore the newer one */ continue; } if (_bt_compare_scankey_args(scan, cur, cur, xform[j], &test_result)) { if (test_result) xform[j] = cur; else if (j == (BTEqualStrategyNumber - 1)) { /* key == a && key == b, but a != b */ so->qual_ok = false; return; } /* else old key is more restrictive, keep it */ } else { /* * We can't determine which key is more restrictive. Keep the * previous one in xform[j] and push this one directly to the * output array. */ ScanKey outkey = &outkeys[new_numberOfKeys++]; memcpy(outkey, cur, sizeof(ScanKeyData)); if (numberOfEqualCols == attno - 1) _bt_mark_scankey_required(outkey); } } } so->numberOfKeys = new_numberOfKeys;}/* * Compare two scankey values using a specified operator. Both values * must be already known non-NULL. * * The test we want to perform is logically "leftarg op rightarg", where * leftarg and rightarg are the sk_argument values in those ScanKeys, and * the comparison operator is the one in the op ScanKey. However, in * cross-data-type situations we may need to look up the correct operator in * the index's opfamily: it is the one having amopstrategy = op->sk_strategy * and amoplefttype/amoprighttype equal to the two argument datatypes. * * If the opfamily doesn't supply a complete set of cross-type operators we * may not be able to make the comparison. If we can make the comparison * we store the operator result in *result and return TRUE. We return FALSE * if the comparison could not be made. * * Note: op always points at the same ScanKey as either leftarg or rightarg. * Since we don't scribble on the scankeys, this aliasing should cause no * trouble. * * Note: this routine needs to be insensitive to any DESC option applied * to the index column. For example, "x < 4" is a tighter constraint than * "x < 5" regardless of which way the index is sorted. We don't worry about * NULLS FIRST/LAST either, since the given values are never nulls. */static bool_bt_compare_scankey_args(IndexScanDesc scan, ScanKey op, ScanKey leftarg, ScanKey rightarg, bool *result){ Relation rel = scan->indexRelation; Oid lefttype, righttype, optype, opcintype, cmp_op; StrategyNumber strat; /* * The opfamily we need to worry about is identified by the index column. */ Assert(leftarg->sk_attno == rightarg->sk_attno); opcintype = rel->rd_opcintype[leftarg->sk_attno - 1]; /* * Determine the actual datatypes of the ScanKey arguments. We have to * support the convention that sk_subtype == InvalidOid means the opclass * input type; this is a hack to simplify life for ScanKeyInit(). */ lefttype = leftarg->sk_subtype; if (lefttype == InvalidOid) lefttype = opcintype; righttype = rightarg->sk_subtype; if (righttype == InvalidOid) righttype = opcintype; optype = op->sk_subtype; if (optype == InvalidOid) optype = opcintype; /* * If leftarg and rightarg match the types expected for the "op" scankey, * we can use its already-looked-up comparison function. */ if (lefttype == opcintype && righttype == optype) { *result = DatumGetBool(FunctionCall2(&op->sk_func, leftarg->sk_argument, rightarg->sk_argument)); return true; } /* * Otherwise, we need to go to the syscache to find the appropriate * operator. (This cannot result in infinite recursion, since no * indexscan initiated by syscache lookup will use cross-data-type * operators.) * * If the sk_strategy was flipped by _bt_mark_scankey_with_indoption, we * have to un-flip it to get the correct opfamily member. */ strat = op->sk_strategy; if (op->sk_flags & SK_BT_DESC) strat = BTCommuteStrategyNumber(strat); cmp_op = get_opfamily_member(rel->rd_opfamily[leftarg->sk_attno - 1], lefttype, righttype, strat); if (OidIsValid(cmp_op)) { RegProcedure cmp_proc = get_opcode(cmp_op); if (RegProcedureIsValid(cmp_proc)) { *result = DatumGetBool(OidFunctionCall2(cmp_proc, leftarg->sk_argument, rightarg->sk_argument)); return true; } } /* Can't make the comparison */ *result = false; /* suppress compiler warnings */ return false;}/* * Mark a scankey with info from the index's indoption array. * * We copy the appropriate indoption value into the scankey sk_flags * (shifting to avoid clobbering system-defined flag bits). Also, if * the DESC option is set, commute (flip) the operator strategy number. * * This function is applied to the *input* scankey structure; therefore * on a rescan we will be looking at already-processed scankeys. Hence * we have to be careful not to re-commute the strategy if we already did it. * It's a bit ugly to modify the caller's copy of the scankey but in practice * there shouldn't be any problem, since the index's indoptions are certainly * not going to change while the scankey survives. */static void_bt_mark_scankey_with_indoption(ScanKey skey, int16 *indoption){ int addflags; addflags = indoption[skey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT; if ((addflags & SK_BT_DESC) && !(skey->sk_flags & SK_BT_DESC)) skey->sk_strategy = BTCommuteStrategyNumber(skey->sk_strategy); skey->sk_flags |= addflags; if (skey->sk_flags & SK_ROW_HEADER) { ScanKey subkey = (ScanKey) DatumGetPointer(skey->sk_argument); for (;;) { Assert(subkey->sk_flags & SK_ROW_MEMBER); addflags = indoption[subkey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT; if ((addflags & SK_BT_DESC) && !(subkey->sk_flags & SK_BT_DESC)) subkey->sk_strategy = BTCommuteStrategyNumber(subkey->sk_strategy); subkey->sk_flags |= addflags; if (subkey->sk_flags & SK_ROW_END) break; subkey++; } }}/* * Mark a scankey as "required to continue the scan". * * Depending on the operator type, the key may be required for both scan * directions or just one. Also, if the key is a row comparison header, * we have to mark the appropriate subsidiary ScanKeys as required. In * such cases, the first subsidiary key is required, but subsequent ones * are required only as long as they correspond to successive index columns * and match the leading column as to sort direction. * Otherwise the row comparison ordering is different from the index ordering * and so we can't stop the scan on the basis of those lower-order columns. * * Note: when we set required-key flag bits in a subsidiary scankey, we are * scribbling on a data structure belonging to the index AM's caller, not on * our private copy. This should be OK because the marking will not change * from scan to scan within a query, and so we'd just re-mark the same way * anyway on a rescan. Something to keep an eye on though. */static void_bt_mark_scankey_required(ScanKey skey){ int addflags; switch (skey->sk_strategy) { case BTLessStrategyNumber: case BTLessEqualStrategyNumber: addflags = SK_BT_REQFWD; break; case BTEqualStrategyNumber: addflags = SK_BT_REQFWD | SK_BT_REQBKWD; break; case BTGreaterEqualStrategyNumber: case BTGreaterStrategyNumber: addflags = SK_BT_REQBKWD; break; default: elog(ERROR, "unrecognized StrategyNumber: %d", (int) skey->sk_strategy); addflags = 0; /* keep compiler quiet */ break; } skey->sk_flags |= addflags; if (skey->sk_flags & SK_ROW_HEADER) { ScanKey subkey = (ScanKey) DatumGetPointer(skey->sk_argument); AttrNumber attno = skey->sk_attno; /* First subkey should be same as the header says */ Assert(subkey->sk_attno == attno); for (;;) { Assert(subkey->sk_flags & SK_ROW_MEMBER); if (subkey->sk_attno != attno) break; /* non-adjacent key, so not required */ if (subkey->sk_strategy != skey->sk_strategy) break; /* wrong direction, so not required */ subkey->sk_flags |= addflags; if (subkey->sk_flags & SK_ROW_END) break; subkey++; attno++; } }}/* * Test whether an indextuple satisfies all the scankey conditions. * * If so, copy its TID into scan->xs_ctup.t_self, and return TRUE. * If not, return FALSE (xs_ctup is not changed). * * If the tuple fails to pass the qual, we also determine whether there's * any need to continue the scan beyond this tuple, and set *continuescan * accordingly. See comments for _bt_preprocess_keys(), above, about how * this is done. * * scan: index scan descriptor (containing a search-type scankey) * page: buffer page containing index tuple * offnum: offset number of index tuple (must be a valid item!) * dir: direction we are scanning in * continuescan: output parameter (will be set correctly in all cases) */bool_bt_checkkeys(IndexScanDesc scan, Page page, OffsetNumber offnum, ScanDirection dir, bool *continuescan){ ItemId iid = PageGetItemId(page, offnum); bool tuple_valid; IndexTuple tuple; TupleDesc tupdesc; BTScanOpaque so; int keysz; int ikey; ScanKey key; *continuescan = true; /* default assumption */ /* * If the scan specifies not to return killed tuples, then we treat a * killed tuple as not passing the qual. Most of the time, it's a win to * not bother examining the tuple's index keys, but just return * immediately with continuescan = true to proceed to the next tuple. * However, if this is the last tuple on the page, we should check the * index keys to prevent uselessly advancing to the next page. */ if (scan->ignore_killed_tuples && ItemIdIsDead(iid)) { /* return immediately if there are more tuples on the page */ if (ScanDirectionIsForward(dir)) { if (offnum < PageGetMaxOffsetNumber(page)) return false; } else { BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page); if (offnum > P_FIRSTDATAKEY(opaque)) return false; } /* * OK, we want to check the keys, but we'll return FALSE even if the * tuple passes the key tests. */ tuple_valid = false; } else tuple_valid = true; tuple = (IndexTuple) PageGetItem(page, iid); IncrIndexProcessed(); tupdesc = RelationGetDescr(scan->indexRelation); so = (BTScanOpaque) scan->opaque; keysz = so->numberOfKeys; for (key = so->keyData, ikey = 0; ikey < keysz; key++, ikey++) { Datum datum; bool isNull; Datum test; /* row-comparison keys need special processing */ if (key->sk_flags & SK_ROW_HEADER) { if (_bt_check_rowcompare(key, tuple, tupdesc, dir, continuescan)) continue; return false; } datum = index_getattr(tuple, key->sk_attno, tupdesc, &isNull); if (key->sk_flags & SK_ISNULL) { /* Handle IS NULL tests */ Assert(key->sk_flags & SK_SEARCHNULL); if (isNull) continue; /* tuple satisfies this qual */ /* * Tuple fails this qual. If it's a required qual for the current * scan direction, then we can conclude no further tuples will * pass, either. */ if ((key->sk_flags & SK_BT_REQFWD) && ScanDirectionIsForward(dir)) *continuescan = false; else if ((key->sk_flags & SK_BT_REQBKWD) && ScanDirectionIsBackward(dir)) *continuescan = false; /* * In any case, this indextuple doesn't match the qual. */ return false; } if (isNull) { if (key->sk_flags & SK_BT_NULLS_FIRST) { /* * Since NULLs are sorted before non-NULLs, we know we have * reached the lower limit of the range of values for this * index attr. On a backward scan, we can stop if this qual * is one of the "must match" subset. On a forward scan, * however, we should keep going. */ if ((key->sk_flags & SK_BT_REQBKWD) && ScanDirectionIsBackward(dir)) *continuescan = false; } else { /* * Since NULLs are sorted after non-NULLs, we know we have * reached the upper limit of the range of values for this * index attr. On a forward scan, we can stop if this qual is * one of the "must match" subset. On a backward scan, * however, we should keep going. */ if ((key->sk_flags & SK_BT_REQFWD) && ScanDirectionIsForward(dir)) *continuescan = false; } /* * In any case, this indextuple doesn't match the qual. */ return false; } test = FunctionCall2(&key->sk_func, datum, key->sk_argument); if (!DatumGetBool(test)) { /* * Tuple fails this qual. If it's a required qual for the current * scan direction, then we can conclude no further tuples will * pass, either. * * Note: because we stop the scan as soon as any required equality * qual fails, it is critical that equality quals be used for the * initial positioning in _bt_first() when they are available. See * comments in _bt_first(). */ if ((key->sk_flags & SK_BT_REQFWD) && ScanDirectionIsForward(dir)) *continuescan = false; else if ((key->sk_flags & SK_BT_REQBKWD) && ScanDirectionIsBackward(dir)) *continuescan = false; /* * In any case, this indextuple doesn't match the qual. */ return false; } } /* If we get here, the tuple passes all index quals. */ if (tuple_valid) scan->xs_ctup.t_self = tuple->t_tid; return tuple_valid;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -