PostgreSQL Source Code git master
nbtreadpage.c File Reference
#include "postgres.h"
#include "access/nbtree.h"
#include "access/relscan.h"
#include "storage/predicate.h"
#include "utils/datum.h"
#include "utils/rel.h"
Include dependency graph for nbtreadpage.c:

Go to the source code of this file.

Data Structures

struct  BTReadPageState
 

Macros

#define LOOK_AHEAD_REQUIRED_RECHECKS   3
 
#define LOOK_AHEAD_DEFAULT_DISTANCE   5
 
#define NSKIPADVANCES_THRESHOLD   3
 

Typedefs

typedef struct BTReadPageState BTReadPageState
 

Functions

static void _bt_set_startikey (IndexScanDesc scan, BTReadPageState *pstate)
 
static bool _bt_scanbehind_checkkeys (IndexScanDesc scan, ScanDirection dir, IndexTuple finaltup)
 
static bool _bt_oppodir_checkkeys (IndexScanDesc scan, ScanDirection dir, IndexTuple finaltup)
 
static void _bt_saveitem (BTScanOpaque so, int itemIndex, OffsetNumber offnum, IndexTuple itup)
 
static int _bt_setuppostingitems (BTScanOpaque so, int itemIndex, OffsetNumber offnum, const ItemPointerData *heapTid, IndexTuple itup)
 
static void _bt_savepostingitem (BTScanOpaque so, int itemIndex, OffsetNumber offnum, ItemPointer heapTid, int tupleOffset)
 
static bool _bt_checkkeys (IndexScanDesc scan, BTReadPageState *pstate, bool arrayKeys, IndexTuple tuple, int tupnatts)
 
static bool _bt_check_compare (IndexScanDesc scan, ScanDirection dir, IndexTuple tuple, int tupnatts, TupleDesc tupdesc, bool advancenonrequired, bool forcenonrequired, bool *continuescan, int *ikey)
 
static bool _bt_check_rowcompare (ScanKey header, IndexTuple tuple, int tupnatts, TupleDesc tupdesc, ScanDirection dir, bool forcenonrequired, bool *continuescan)
 
static bool _bt_rowcompare_cmpresult (ScanKey subkey, int cmpresult)
 
static bool _bt_tuple_before_array_skeys (IndexScanDesc scan, ScanDirection dir, IndexTuple tuple, TupleDesc tupdesc, int tupnatts, bool readpagetup, int sktrig, bool *scanBehind)
 
static void _bt_checkkeys_look_ahead (IndexScanDesc scan, BTReadPageState *pstate, int tupnatts, TupleDesc tupdesc)
 
static bool _bt_advance_array_keys (IndexScanDesc scan, BTReadPageState *pstate, IndexTuple tuple, int tupnatts, TupleDesc tupdesc, int sktrig, bool sktrig_required)
 
static bool _bt_advance_array_keys_increment (IndexScanDesc scan, ScanDirection dir, bool *skip_array_set)
 
static bool _bt_array_increment (Relation rel, ScanKey skey, BTArrayKeyInfo *array)
 
static bool _bt_array_decrement (Relation rel, ScanKey skey, BTArrayKeyInfo *array)
 
static void _bt_array_set_low_or_high (Relation rel, ScanKey skey, BTArrayKeyInfo *array, bool low_not_high)
 
static void _bt_skiparray_set_element (Relation rel, ScanKey skey, BTArrayKeyInfo *array, int32 set_elem_result, Datum tupdatum, bool tupnull)
 
static void _bt_skiparray_set_isnull (Relation rel, ScanKey skey, BTArrayKeyInfo *array)
 
static int32 _bt_compare_array_skey (FmgrInfo *orderproc, Datum tupdatum, bool tupnull, Datum arrdatum, ScanKey cur)
 
static void _bt_binsrch_skiparray_skey (bool cur_elem_trig, ScanDirection dir, Datum tupdatum, bool tupnull, BTArrayKeyInfo *array, ScanKey cur, int32 *set_elem_result)
 
bool _bt_readpage (IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum, bool firstpage)
 
void _bt_start_array_keys (IndexScanDesc scan, ScanDirection dir)
 
int _bt_binsrch_array_skey (FmgrInfo *orderproc, bool cur_elem_trig, ScanDirection dir, Datum tupdatum, bool tupnull, BTArrayKeyInfo *array, ScanKey cur, int32 *set_elem_result)
 

Macro Definition Documentation

◆ LOOK_AHEAD_DEFAULT_DISTANCE

#define LOOK_AHEAD_DEFAULT_DISTANCE   5

Definition at line 1117 of file nbtreadpage.c.

◆ LOOK_AHEAD_REQUIRED_RECHECKS

#define LOOK_AHEAD_REQUIRED_RECHECKS   3

Definition at line 1116 of file nbtreadpage.c.

◆ NSKIPADVANCES_THRESHOLD

#define NSKIPADVANCES_THRESHOLD   3

Definition at line 1118 of file nbtreadpage.c.

Typedef Documentation

◆ BTReadPageState

Function Documentation

◆ _bt_advance_array_keys()

static bool _bt_advance_array_keys ( IndexScanDesc  scan,
BTReadPageState pstate,
IndexTuple  tuple,
int  tupnatts,
TupleDesc  tupdesc,
int  sktrig,
bool  sktrig_required 
)
static

Definition at line 2182 of file nbtreadpage.c.

2185{
2186 BTScanOpaque so = (BTScanOpaque) scan->opaque;
2187 Relation rel = scan->indexRelation;
2188 ScanDirection dir = pstate ? pstate->dir : ForwardScanDirection;
2189 int arrayidx = 0;
2190 bool beyond_end_advance = false,
2191 skip_array_advanced = false,
2192 has_required_opposite_direction_only = false,
2193 all_required_satisfied = true,
2194 all_satisfied = true;
2195
2196 Assert(!so->needPrimScan && !so->scanBehind && !so->oppositeDirCheck);
2197 Assert(_bt_verify_keys_with_arraykeys(scan));
2198
2199 if (sktrig_required)
2200 {
2201 /*
2202 * Precondition array state assertion
2203 */
2204 Assert(!_bt_tuple_before_array_skeys(scan, dir, tuple, tupdesc,
2205 tupnatts, false, 0, NULL));
2206
2207 /*
2208 * Once we return we'll have a new set of required array keys, so
2209 * reset state used by "look ahead" optimization
2210 */
2211 pstate->rechecks = 0;
2212 pstate->targetdistance = 0;
2213 }
2214 else if (sktrig < so->numberOfKeys - 1 &&
2215 !(so->keyData[so->numberOfKeys - 1].sk_flags & SK_SEARCHARRAY))
2216 {
2217 int least_sign_ikey = so->numberOfKeys - 1;
2218 bool continuescan;
2219
2220 /*
2221 * Optimization: perform a precheck of the least significant key
2222 * during !sktrig_required calls when it isn't already our sktrig
2223 * (provided the precheck key is not itself an array).
2224 *
2225 * When the precheck works out we'll avoid an expensive binary search
2226 * of sktrig's array (plus any other arrays before least_sign_ikey).
2227 */
2228 Assert(so->keyData[sktrig].sk_flags & SK_SEARCHARRAY);
2229 if (!_bt_check_compare(scan, dir, tuple, tupnatts, tupdesc, false,
2230 false, &continuescan,
2231 &least_sign_ikey))
2232 return false;
2233 }
2234
2235 for (int ikey = 0; ikey < so->numberOfKeys; ikey++)
2236 {
2237 ScanKey cur = so->keyData + ikey;
2238 BTArrayKeyInfo *array = NULL;
2239 Datum tupdatum;
2240 bool required = false,
2241 tupnull;
2242 int32 result;
2243 int set_elem = 0;
2244
2245 if (cur->sk_strategy == BTEqualStrategyNumber)
2246 {
2247 /* Manage array state */
2248 if (cur->sk_flags & SK_SEARCHARRAY)
2249 {
2250 array = &so->arrayKeys[arrayidx++];
2251 Assert(array->scan_key == ikey);
2252 }
2253 }
2254 else
2255 {
2256 /*
2257 * Are any inequalities required in the opposite direction only
2258 * present here?
2259 */
2260 if (((ScanDirectionIsForward(dir) &&
2261 (cur->sk_flags & (SK_BT_REQBKWD))) ||
2263 (cur->sk_flags & (SK_BT_REQFWD)))))
2264 has_required_opposite_direction_only = true;
2265 }
2266
2267 /* Optimization: skip over known-satisfied scan keys */
2268 if (ikey < sktrig)
2269 continue;
2270
2271 if (cur->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD))
2272 {
2273 required = true;
2274
2275 if (cur->sk_attno > tupnatts)
2276 {
2277 /* Set this just like _bt_tuple_before_array_skeys */
2278 Assert(sktrig < ikey);
2279 so->scanBehind = true;
2280 }
2281 }
2282
2283 /*
2284 * Handle a required non-array scan key that the initial call to
2285 * _bt_check_compare indicated triggered array advancement, if any.
2286 *
2287 * The non-array scan key's strategy will be <, <=, or = during a
2288 * forwards scan (or any one of =, >=, or > during a backwards scan).
2289 * It follows that the corresponding tuple attribute's value must now
2290 * be either > or >= the scan key value (for backwards scans it must
2291 * be either < or <= that value).
2292 *
2293 * If this is a required equality strategy scan key, this is just an
2294 * optimization; _bt_tuple_before_array_skeys already confirmed that
2295 * this scan key places us ahead of caller's tuple. There's no need
2296 * to repeat that work now. (The same underlying principle also gets
2297 * applied by the cur_elem_trig optimization used to speed up searches
2298 * for the next array element.)
2299 *
2300 * If this is a required inequality strategy scan key, we _must_ rely
2301 * on _bt_check_compare like this; we aren't capable of directly
2302 * evaluating required inequality strategy scan keys here, on our own.
2303 */
2304 if (ikey == sktrig && !array)
2305 {
2306 Assert(sktrig_required && required && all_required_satisfied);
2307
2308 /* Use "beyond end" advancement. See below for an explanation. */
2309 beyond_end_advance = true;
2310 all_satisfied = all_required_satisfied = false;
2311
2312 continue;
2313 }
2314
2315 /*
2316 * Nothing more for us to do with an inequality strategy scan key that
2317 * wasn't the one that _bt_check_compare stopped on, though.
2318 *
2319 * Note: if our later call to _bt_check_compare (to recheck caller's
2320 * tuple) sets continuescan=false due to finding this same inequality
2321 * unsatisfied (possible when it's required in the scan direction),
2322 * we'll deal with it via a recursive "second pass" call.
2323 */
2324 else if (cur->sk_strategy != BTEqualStrategyNumber)
2325 continue;
2326
2327 /*
2328 * Nothing for us to do with an equality strategy scan key that isn't
2329 * marked required, either -- unless it's a non-required array
2330 */
2331 else if (!required && !array)
2332 continue;
2333
2334 /*
2335 * Here we perform steps for all array scan keys after a required
2336 * array scan key whose binary search triggered "beyond end of array
2337 * element" array advancement due to encountering a tuple attribute
2338 * value > the closest matching array key (or < for backwards scans).
2339 */
2340 if (beyond_end_advance)
2341 {
2342 if (array)
2343 _bt_array_set_low_or_high(rel, cur, array,
2345
2346 continue;
2347 }
2348
2349 /*
2350 * Here we perform steps for all array scan keys after a required
2351 * array scan key whose tuple attribute was < the closest matching
2352 * array key when we dealt with it (or > for backwards scans).
2353 *
2354 * This earlier required array key already puts us ahead of caller's
2355 * tuple in the key space (for the current scan direction). We must
2356 * make sure that subsequent lower-order array keys do not put us too
2357 * far ahead (ahead of tuples that have yet to be seen by our caller).
2358 * For example, when a tuple "(a, b) = (42, 5)" advances the array
2359 * keys on "a" from 40 to 45, we must also set "b" to whatever the
2360 * first array element for "b" is. It would be wrong to allow "b" to
2361 * be set based on the tuple value.
2362 *
2363 * Perform the same steps with truncated high key attributes. You can
2364 * think of this as a "binary search" for the element closest to the
2365 * value -inf. Again, the arrays must never get ahead of the scan.
2366 */
2367 if (!all_required_satisfied || cur->sk_attno > tupnatts)
2368 {
2369 if (array)
2370 _bt_array_set_low_or_high(rel, cur, array,
2372
2373 continue;
2374 }
2375
2376 /*
2377 * Search in scankey's array for the corresponding tuple attribute
2378 * value from caller's tuple
2379 */
2380 tupdatum = index_getattr(tuple, cur->sk_attno, tupdesc, &tupnull);
2381
2382 if (array)
2383 {
2384 bool cur_elem_trig = (sktrig_required && ikey == sktrig);
2385
2386 /*
2387 * "Binary search" by checking if tupdatum/tupnull are within the
2388 * range of the skip array
2389 */
2390 if (array->num_elems == -1)
2391 _bt_binsrch_skiparray_skey(cur_elem_trig, dir,
2392 tupdatum, tupnull, array, cur,
2393 &result);
2394
2395 /*
2396 * Binary search for the closest match from the SAOP array
2397 */
2398 else
2399 set_elem = _bt_binsrch_array_skey(&so->orderProcs[ikey],
2400 cur_elem_trig, dir,
2401 tupdatum, tupnull, array, cur,
2402 &result);
2403 }
2404 else
2405 {
2407
2408 /*
2409 * This is a required non-array equality strategy scan key, which
2410 * we'll treat as a degenerate single element array.
2411 *
2412 * This scan key's imaginary "array" can't really advance, but it
2413 * can still roll over like any other array. (Actually, this is
2414 * no different to real single value arrays, which never advance
2415 * without rolling over -- they can never truly advance, either.)
2416 */
2417 result = _bt_compare_array_skey(&so->orderProcs[ikey],
2418 tupdatum, tupnull,
2419 cur->sk_argument, cur);
2420 }
2421
2422 /*
2423 * Consider "beyond end of array element" array advancement.
2424 *
2425 * When the tuple attribute value is > the closest matching array key
2426 * (or < in the backwards scan case), we need to ratchet this array
2427 * forward (backward) by one increment, so that caller's tuple ends up
2428 * being < final array value instead (or > final array value instead).
2429 * This process has to work for all of the arrays, not just this one:
2430 * it must "carry" to higher-order arrays when the set_elem that we
2431 * just found happens to be the final one for the scan's direction.
2432 * Incrementing (decrementing) set_elem itself isn't good enough.
2433 *
2434 * Our approach is to provisionally use set_elem as if it was an exact
2435 * match now, then set each later/less significant array to whatever
2436 * its final element is. Once outside the loop we'll then "increment
2437 * this array's set_elem" by calling _bt_advance_array_keys_increment.
2438 * That way the process rolls over to higher order arrays as needed.
2439 *
2440 * Under this scheme any required arrays only ever ratchet forwards
2441 * (or backwards), and always do so to the maximum possible extent
2442 * that we can know will be safe without seeing the scan's next tuple.
2443 * We don't need any special handling for required scan keys that lack
2444 * a real array to advance, nor for redundant scan keys that couldn't
2445 * be eliminated by _bt_preprocess_keys. It won't matter if some of
2446 * our "true" array scan keys (or even all of them) are non-required.
2447 */
2448 if (sktrig_required && required &&
2449 ((ScanDirectionIsForward(dir) && result > 0) ||
2450 (ScanDirectionIsBackward(dir) && result < 0)))
2451 beyond_end_advance = true;
2452
2453 Assert(all_required_satisfied && all_satisfied);
2454 if (result != 0)
2455 {
2456 /*
2457 * Track whether caller's tuple satisfies our new post-advancement
2458 * qual, for required scan keys, as well as for the entire set of
2459 * interesting scan keys (all required scan keys plus non-required
2460 * array scan keys are considered interesting.)
2461 */
2462 all_satisfied = false;
2463 if (sktrig_required && required)
2464 all_required_satisfied = false;
2465 else
2466 {
2467 /*
2468 * There's no need to advance the arrays using the best
2469 * available match for a non-required array. Give up now.
2470 * (Though note that sktrig_required calls still have to do
2471 * all the usual post-advancement steps, including the recheck
2472 * call to _bt_check_compare.)
2473 */
2474 break;
2475 }
2476 }
2477
2478 /* Advance array keys, even when we don't have an exact match */
2479 if (array)
2480 {
2481 if (array->num_elems == -1)
2482 {
2483 /* Skip array's new element is tupdatum (or MINVAL/MAXVAL) */
2484 _bt_skiparray_set_element(rel, cur, array, result,
2485 tupdatum, tupnull);
2486 skip_array_advanced = true;
2487 }
2488 else if (array->cur_elem != set_elem)
2489 {
2490 /* SAOP array's new element is set_elem datum */
2491 array->cur_elem = set_elem;
2492 cur->sk_argument = array->elem_values[set_elem];
2493 }
2494 }
2495 }
2496
2497 /*
2498 * Advance the array keys incrementally whenever "beyond end of array
2499 * element" array advancement happens, so that advancement will carry to
2500 * higher-order arrays (might exhaust all the scan's arrays instead, which
2501 * ends the top-level scan).
2502 */
2503 if (beyond_end_advance &&
2504 !_bt_advance_array_keys_increment(scan, dir, &skip_array_advanced))
2505 goto end_toplevel_scan;
2506
2507 Assert(_bt_verify_keys_with_arraykeys(scan));
2508
2509 /*
2510 * Maintain a page-level count of the number of times the scan's array
2511 * keys advanced in a way that affected at least one skip array
2512 */
2513 if (sktrig_required && skip_array_advanced)
2514 pstate->nskipadvances++;
2515
2516 /*
2517 * Does tuple now satisfy our new qual? Recheck with _bt_check_compare.
2518 *
2519 * Calls triggered by an unsatisfied required scan key, whose tuple now
2520 * satisfies all required scan keys, but not all nonrequired array keys,
2521 * will still require a recheck call to _bt_check_compare. They'll still
2522 * need its "second pass" handling of required inequality scan keys.
2523 * (Might have missed a still-unsatisfied required inequality scan key
2524 * that caller didn't detect as the sktrig scan key during its initial
2525 * _bt_check_compare call that used the old/original qual.)
2526 *
2527 * Calls triggered by an unsatisfied nonrequired array scan key never need
2528 * "second pass" handling of required inequalities (nor any other handling
2529 * of any required scan key). All that matters is whether caller's tuple
2530 * satisfies the new qual, so it's safe to just skip the _bt_check_compare
2531 * recheck when we've already determined that it can only return 'false'.
2532 *
2533 * Note: In practice most scan keys are marked required by preprocessing,
2534 * if necessary by generating a preceding skip array. We nevertheless
2535 * often handle array keys marked required as if they were nonrequired.
2536 * This behavior is requested by our _bt_check_compare caller, though only
2537 * when it is passed "forcenonrequired=true" by _bt_checkkeys.
2538 */
2539 if ((sktrig_required && all_required_satisfied) ||
2540 (!sktrig_required && all_satisfied))
2541 {
2542 int nsktrig = sktrig + 1;
2543 bool continuescan;
2544
2545 Assert(all_required_satisfied);
2546
2547 /* Recheck _bt_check_compare on behalf of caller */
2548 if (_bt_check_compare(scan, dir, tuple, tupnatts, tupdesc, false,
2549 !sktrig_required, &continuescan,
2550 &nsktrig) &&
2551 !so->scanBehind)
2552 {
2553 /* This tuple satisfies the new qual */
2554 Assert(all_satisfied && continuescan);
2555
2556 if (pstate)
2557 pstate->continuescan = true;
2558
2559 return true;
2560 }
2561
2562 /*
2563 * Consider "second pass" handling of required inequalities.
2564 *
2565 * It's possible that our _bt_check_compare call indicated that the
2566 * scan should end due to some unsatisfied inequality that wasn't
2567 * initially recognized as such by us. Handle this by calling
2568 * ourselves recursively, this time indicating that the trigger is the
2569 * inequality that we missed first time around (and using a set of
2570 * required array/equality keys that are now exact matches for tuple).
2571 *
2572 * We make a strong, general guarantee that every _bt_checkkeys call
2573 * here will advance the array keys to the maximum possible extent
2574 * that we can know to be safe based on caller's tuple alone. If we
2575 * didn't perform this step, then that guarantee wouldn't quite hold.
2576 */
2577 if (unlikely(!continuescan))
2578 {
2579 bool satisfied PG_USED_FOR_ASSERTS_ONLY;
2580
2581 Assert(sktrig_required);
2583
2584 /*
2585 * The tuple must use "beyond end" advancement during the
2586 * recursive call, so we cannot possibly end up back here when
2587 * recursing. We'll consume a small, fixed amount of stack space.
2588 */
2589 Assert(!beyond_end_advance);
2590
2591 /* Advance the array keys a second time using same tuple */
2592 satisfied = _bt_advance_array_keys(scan, pstate, tuple, tupnatts,
2593 tupdesc, nsktrig, true);
2594
2595 /* This tuple doesn't satisfy the inequality */
2596 Assert(!satisfied);
2597 return false;
2598 }
2599
2600 /*
2601 * Some non-required scan key (from new qual) still not satisfied.
2602 *
2603 * All scan keys required in the current scan direction must still be
2604 * satisfied, though, so we can trust all_required_satisfied below.
2605 */
2606 }
2607
2608 /*
2609 * When we were called just to deal with "advancing" non-required arrays,
2610 * this is as far as we can go (cannot stop the scan for these callers)
2611 */
2612 if (!sktrig_required)
2613 {
2614 /* Caller's tuple doesn't match any qual */
2615 return false;
2616 }
2617
2618 /*
2619 * Postcondition array state assertion (for still-unsatisfied tuples).
2620 *
2621 * By here we have established that the scan's required arrays (scan must
2622 * have at least one required array) advanced, without becoming exhausted.
2623 *
2624 * Caller's tuple is now < the newly advanced array keys (or > when this
2625 * is a backwards scan), except in the case where we only got this far due
2626 * to an unsatisfied non-required scan key. Verify that with an assert.
2627 *
2628 * Note: we don't just quit at this point when all required scan keys were
2629 * found to be satisfied because we need to consider edge-cases involving
2630 * scan keys required in the opposite direction only; those aren't tracked
2631 * by all_required_satisfied.
2632 */
2633 Assert(_bt_tuple_before_array_skeys(scan, dir, tuple, tupdesc, tupnatts,
2634 false, 0, NULL) ==
2635 !all_required_satisfied);
2636
2637 /*
2638 * We generally permit primitive index scans to continue onto the next
2639 * sibling page when the page's finaltup satisfies all required scan keys
2640 * at the point where we're between pages.
2641 *
2642 * If caller's tuple is also the page's finaltup, and we see that required
2643 * scan keys still aren't satisfied, start a new primitive index scan.
2644 */
2645 if (!all_required_satisfied && pstate->finaltup == tuple)
2646 goto new_prim_scan;
2647
2648 /*
2649 * Proactively check finaltup (don't wait until finaltup is reached by the
2650 * scan) when it might well turn out to not be satisfied later on.
2651 *
2652 * Note: if so->scanBehind hasn't already been set for finaltup by us,
2653 * it'll be set during this call to _bt_tuple_before_array_skeys. Either
2654 * way, it'll be set correctly (for the whole page) after this point.
2655 */
2656 if (!all_required_satisfied && pstate->finaltup &&
2657 _bt_tuple_before_array_skeys(scan, dir, pstate->finaltup, tupdesc,
2658 BTreeTupleGetNAtts(pstate->finaltup, rel),
2659 false, 0, &so->scanBehind))
2660 goto new_prim_scan;
2661
2662 /*
2663 * When we encounter a truncated finaltup high key attribute, we're
2664 * optimistic about the chances of its corresponding required scan key
2665 * being satisfied when we go on to recheck it against tuples from this
2666 * page's right sibling leaf page. We consider truncated attributes to be
2667 * satisfied by required scan keys, which allows the primitive index scan
2668 * to continue to the next leaf page. We must set so->scanBehind to true
2669 * to remember that the last page's finaltup had "satisfied" required scan
2670 * keys for one or more truncated attribute values (scan keys required in
2671 * _either_ scan direction).
2672 *
2673 * There is a chance that _bt_readpage (which checks so->scanBehind) will
2674 * find that even the sibling leaf page's finaltup is < the new array
2675 * keys. When that happens, our optimistic policy will have incurred a
2676 * single extra leaf page access that could have been avoided.
2677 *
2678 * A pessimistic policy would give backward scans a gratuitous advantage
2679 * over forward scans. We'd punish forward scans for applying more
2680 * accurate information from the high key, rather than just using the
2681 * final non-pivot tuple as finaltup, in the style of backward scans.
2682 * Being pessimistic would also give some scans with non-required arrays a
2683 * perverse advantage over similar scans that use required arrays instead.
2684 *
2685 * This is similar to our scan-level heuristics, below. They also set
2686 * scanBehind to speculatively continue the primscan onto the next page.
2687 */
2688 if (so->scanBehind)
2689 {
2690 /* Truncated high key -- _bt_scanbehind_checkkeys recheck scheduled */
2691 }
2692
2693 /*
2694 * Handle inequalities marked required in the opposite scan direction.
2695 * They can also signal that we should start a new primitive index scan.
2696 *
2697 * It's possible that the scan is now positioned where "matching" tuples
2698 * begin, and that caller's tuple satisfies all scan keys required in the
2699 * current scan direction. But if caller's tuple still doesn't satisfy
2700 * other scan keys that are required in the opposite scan direction only
2701 * (e.g., a required >= strategy scan key when scan direction is forward),
2702 * it's still possible that there are many leaf pages before the page that
2703 * _bt_first could skip straight to. Groveling through all those pages
2704 * will always give correct answers, but it can be very inefficient. We
2705 * must avoid needlessly scanning extra pages.
2706 *
2707 * Separately, it's possible that _bt_check_compare set continuescan=false
2708 * for a scan key that's required in the opposite direction only. This is
2709 * a special case, that happens only when _bt_check_compare sees that the
2710 * inequality encountered a NULL value. This signals the end of non-NULL
2711 * values in the current scan direction, which is reason enough to end the
2712 * (primitive) scan. If this happens at the start of a large group of
2713 * NULL values, then we shouldn't expect to be called again until after
2714 * the scan has already read indefinitely-many leaf pages full of tuples
2715 * with NULL suffix values. (_bt_first is expected to skip over the group
2716 * of NULLs by applying a similar "deduce NOT NULL" rule of its own, which
2717 * involves consing up an explicit SK_SEARCHNOTNULL key.)
2718 *
2719 * Apply a test against finaltup to detect and recover from the problem:
2720 * if even finaltup doesn't satisfy such an inequality, we just skip by
2721 * starting a new primitive index scan. When we skip, we know for sure
2722 * that all of the tuples on the current page following caller's tuple are
2723 * also before the _bt_first-wise start of tuples for our new qual. That
2724 * at least suggests many more skippable pages beyond the current page.
2725 * (when so->scanBehind and so->oppositeDirCheck are set, this'll happen
2726 * when we test the next page's finaltup/high key instead.)
2727 */
2728 else if (has_required_opposite_direction_only && pstate->finaltup &&
2729 unlikely(!_bt_oppodir_checkkeys(scan, dir, pstate->finaltup)))
2730 goto new_prim_scan;
2731
2732continue_scan:
2733
2734 /*
2735 * Stick with the ongoing primitive index scan for now.
2736 *
2737 * It's possible that later tuples will also turn out to have values that
2738 * are still < the now-current array keys (or > the current array keys).
2739 * Our caller will handle this by performing what amounts to a linear
2740 * search of the page, implemented by calling _bt_check_compare and then
2741 * _bt_tuple_before_array_skeys for each tuple.
2742 *
2743 * This approach has various advantages over a binary search of the page.
2744 * Repeated binary searches of the page (one binary search for every array
2745 * advancement) won't outperform a continuous linear search. While there
2746 * are workloads that a naive linear search won't handle well, our caller
2747 * has a "look ahead" fallback mechanism to deal with that problem.
2748 */
2749 pstate->continuescan = true; /* Override _bt_check_compare */
2750 so->needPrimScan = false; /* _bt_readpage has more tuples to check */
2751
2752 if (so->scanBehind)
2753 {
2754 /*
2755 * Remember if recheck needs to call _bt_oppodir_checkkeys for next
2756 * page's finaltup (see above comments about "Handle inequalities
2757 * marked required in the opposite scan direction" for why).
2758 */
2759 so->oppositeDirCheck = has_required_opposite_direction_only;
2760
2761 /*
2762 * skip by setting "look ahead" mechanism's offnum for forwards scans
2763 * (backwards scans check scanBehind flag directly instead)
2764 */
2765 if (ScanDirectionIsForward(dir))
2766 pstate->skip = pstate->maxoff + 1;
2767 }
2768
2769 /* Caller's tuple doesn't match the new qual */
2770 return false;
2771
2772new_prim_scan:
2773
2774 Assert(pstate->finaltup); /* not on rightmost/leftmost page */
2775
2776 /*
2777 * Looks like another primitive index scan is required. But consider
2778 * continuing the current primscan based on scan-level heuristics.
2779 *
2780 * Continue the ongoing primitive scan (and schedule a recheck for when
2781 * the scan arrives on the next sibling leaf page) when it has already
2782 * read at least one leaf page before the one we're reading now. This
2783 * makes primscan scheduling more efficient when scanning subsets of an
2784 * index with many distinct attribute values matching many array elements.
2785 * It encourages fewer, larger primitive scans where that makes sense.
2786 * This will in turn encourage _bt_readpage to apply the pstate.startikey
2787 * optimization more often.
2788 *
2789 * Also continue the ongoing primitive index scan when it is still on the
2790 * first page if there have been more than NSKIPADVANCES_THRESHOLD calls
2791 * here that each advanced at least one of the scan's skip arrays
2792 * (deliberately ignore advancements that only affected SAOP arrays here).
2793 * A page that cycles through this many skip array elements is quite
2794 * likely to neighbor similar pages, that we'll also need to read.
2795 *
2796 * Note: These heuristics aren't as aggressive as you might think. We're
2797 * conservative about allowing a primitive scan to step from the first
2798 * leaf page it reads to the page's sibling page (we only allow it on
2799 * first pages whose finaltup strongly suggests that it'll work out, as
2800 * well as first pages that have a large number of skip array advances).
2801 * Clearing this first page finaltup hurdle is a strong signal in itself.
2802 *
2803 * Note: The NSKIPADVANCES_THRESHOLD heuristic exists only to avoid
2804 * pathological cases. Specifically, cases where a skip scan should just
2805 * behave like a traditional full index scan, but ends up "skipping" again
2806 * and again, descending to the prior leaf page's direct sibling leaf page
2807 * each time. This misbehavior would otherwise be possible during scans
2808 * that never quite manage to "clear the first page finaltup hurdle".
2809 */
2810 if (!pstate->firstpage || pstate->nskipadvances > NSKIPADVANCES_THRESHOLD)
2811 {
2812 /* Schedule a recheck once on the next (or previous) page */
2813 so->scanBehind = true;
2814
2815 /* Continue the current primitive scan after all */
2816 goto continue_scan;
2817 }
2818
2819 /*
2820 * End this primitive index scan, but schedule another.
2821 *
2822 * Note: We make a soft assumption that the current scan direction will
2823 * also be used within _bt_next, when it is asked to step off this page.
2824 * It is up to _bt_next to cancel this scheduled primitive index scan
2825 * whenever it steps to a page in the direction opposite currPos.dir.
2826 */
2827 pstate->continuescan = false; /* Tell _bt_readpage we're done... */
2828 so->needPrimScan = true; /* ...but call _bt_first again */
2829
2830 if (scan->parallel_scan)
2832
2833 /* Caller's tuple doesn't match the new qual */
2834 return false;
2835
2836end_toplevel_scan:
2837
2838 /*
2839 * End the current primitive index scan, but don't schedule another.
2840 *
2841 * This ends the entire top-level scan in the current scan direction.
2842 *
2843 * Note: The scan's arrays (including any non-required arrays) are now in
2844 * their final positions for the current scan direction. If the scan
2845 * direction happens to change, then the arrays will already be in their
2846 * first positions for what will then be the current scan direction.
2847 */
2848 pstate->continuescan = false; /* Tell _bt_readpage we're done... */
2849 so->needPrimScan = false; /* ...and don't call _bt_first again */
2850
2851 /* Caller's tuple doesn't match any qual */
2852 return false;
2853}
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:229
int32_t int32
Definition: c.h:548
#define unlikely(x)
Definition: c.h:418
struct cursor * cur
Definition: ecpg.c:29
Assert(PointerIsAligned(start, uint64))
static Datum index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: itup.h:131
static void _bt_binsrch_skiparray_skey(bool cur_elem_trig, ScanDirection dir, Datum tupdatum, bool tupnull, BTArrayKeyInfo *array, ScanKey cur, int32 *set_elem_result)
Definition: nbtreadpage.c:3571
static void _bt_array_set_low_or_high(Relation rel, ScanKey skey, BTArrayKeyInfo *array, bool low_not_high)
Definition: nbtreadpage.c:3203
static void _bt_skiparray_set_element(Relation rel, ScanKey skey, BTArrayKeyInfo *array, int32 set_elem_result, Datum tupdatum, bool tupnull)
Definition: nbtreadpage.c:3273
#define NSKIPADVANCES_THRESHOLD
Definition: nbtreadpage.c:1118
int _bt_binsrch_array_skey(FmgrInfo *orderproc, bool cur_elem_trig, ScanDirection dir, Datum tupdatum, bool tupnull, BTArrayKeyInfo *array, ScanKey cur, int32 *set_elem_result)
Definition: nbtreadpage.c:3415
static bool _bt_advance_array_keys(IndexScanDesc scan, BTReadPageState *pstate, IndexTuple tuple, int tupnatts, TupleDesc tupdesc, int sktrig, bool sktrig_required)
Definition: nbtreadpage.c:2182
static bool _bt_oppodir_checkkeys(IndexScanDesc scan, ScanDirection dir, IndexTuple finaltup)
Definition: nbtreadpage.c:1007
static bool _bt_check_compare(IndexScanDesc scan, ScanDirection dir, IndexTuple tuple, int tupnatts, TupleDesc tupdesc, bool advancenonrequired, bool forcenonrequired, bool *continuescan, int *ikey)
Definition: nbtreadpage.c:1312
static bool _bt_tuple_before_array_skeys(IndexScanDesc scan, ScanDirection dir, IndexTuple tuple, TupleDesc tupdesc, int tupnatts, bool readpagetup, int sktrig, bool *scanBehind)
Definition: nbtreadpage.c:1854
static bool _bt_advance_array_keys_increment(IndexScanDesc scan, ScanDirection dir, bool *skip_array_set)
Definition: nbtreadpage.c:2868
static int32 _bt_compare_array_skey(FmgrInfo *orderproc, Datum tupdatum, bool tupnull, Datum arrdatum, ScanKey cur)
Definition: nbtreadpage.c:3344
void _bt_parallel_primscan_schedule(IndexScanDesc scan, BlockNumber curr_page)
Definition: nbtree.c:1100
#define SK_BT_REQBKWD
Definition: nbtree.h:1105
#define SK_BT_REQFWD
Definition: nbtree.h:1104
#define BTreeTupleGetNAtts(itup, rel)
Definition: nbtree.h:578
BTScanOpaqueData * BTScanOpaque
Definition: nbtree.h:1097
uint64_t Datum
Definition: postgres.h:70
#define ScanDirectionIsForward(direction)
Definition: sdir.h:64
#define ScanDirectionIsBackward(direction)
Definition: sdir.h:50
ScanDirection
Definition: sdir.h:25
@ ForwardScanDirection
Definition: sdir.h:28
#define SK_SEARCHARRAY
Definition: skey.h:120
#define BTEqualStrategyNumber
Definition: stratnum.h:31
Datum * elem_values
Definition: nbtree.h:1041
IndexTuple finaltup
Definition: nbtreadpage.c:37
ScanDirection dir
Definition: nbtreadpage.c:34
int16 targetdistance
Definition: nbtreadpage.c:55
int16 nskipadvances
Definition: nbtreadpage.c:56
OffsetNumber skip
Definition: nbtreadpage.c:47
OffsetNumber maxoff
Definition: nbtreadpage.c:36
bool needPrimScan
Definition: nbtree.h:1063
BTArrayKeyInfo * arrayKeys
Definition: nbtree.h:1066
FmgrInfo * orderProcs
Definition: nbtree.h:1067
BTScanPosData currPos
Definition: nbtree.h:1093
bool oppositeDirCheck
Definition: nbtree.h:1065
ScanKey keyData
Definition: nbtree.h:1058
BlockNumber currPage
Definition: nbtree.h:967
struct ParallelIndexScanDescData * parallel_scan
Definition: relscan.h:193
Relation indexRelation
Definition: relscan.h:139
int sk_flags
Definition: skey.h:66
StrategyNumber sk_strategy
Definition: skey.h:68

References _bt_advance_array_keys(), _bt_advance_array_keys_increment(), _bt_array_set_low_or_high(), _bt_binsrch_array_skey(), _bt_binsrch_skiparray_skey(), _bt_check_compare(), _bt_compare_array_skey(), _bt_oppodir_checkkeys(), _bt_parallel_primscan_schedule(), _bt_skiparray_set_element(), _bt_tuple_before_array_skeys(), BTScanOpaqueData::arrayKeys, Assert(), BTEqualStrategyNumber, BTreeTupleGetNAtts, BTReadPageState::continuescan, cur, BTArrayKeyInfo::cur_elem, BTScanPosData::currPage, BTScanOpaqueData::currPos, BTReadPageState::dir, BTArrayKeyInfo::elem_values, BTReadPageState::finaltup, BTReadPageState::firstpage, ForwardScanDirection, index_getattr(), IndexScanDescData::indexRelation, BTScanOpaqueData::keyData, BTReadPageState::maxoff, BTScanOpaqueData::needPrimScan, BTReadPageState::nskipadvances, NSKIPADVANCES_THRESHOLD, BTArrayKeyInfo::num_elems, BTScanOpaqueData::numberOfKeys, IndexScanDescData::opaque, BTScanOpaqueData::oppositeDirCheck, BTScanOpaqueData::orderProcs, IndexScanDescData::parallel_scan, PG_USED_FOR_ASSERTS_ONLY, BTReadPageState::rechecks, generate_unaccent_rules::required, BTArrayKeyInfo::scan_key, BTScanOpaqueData::scanBehind, ScanDirectionIsBackward, ScanDirectionIsForward, SK_BT_REQBKWD, SK_BT_REQFWD, ScanKeyData::sk_flags, SK_SEARCHARRAY, ScanKeyData::sk_strategy, BTReadPageState::skip, BTReadPageState::targetdistance, and unlikely.

Referenced by _bt_advance_array_keys(), _bt_check_compare(), and _bt_checkkeys().

◆ _bt_advance_array_keys_increment()

static bool _bt_advance_array_keys_increment ( IndexScanDesc  scan,
ScanDirection  dir,
bool *  skip_array_set 
)
static

Definition at line 2868 of file nbtreadpage.c.

2870{
2871 Relation rel = scan->indexRelation;
2872 BTScanOpaque so = (BTScanOpaque) scan->opaque;
2873
2874 /*
2875 * We must advance the last array key most quickly, since it will
2876 * correspond to the lowest-order index column among the available
2877 * qualifications
2878 */
2879 for (int i = so->numArrayKeys - 1; i >= 0; i--)
2880 {
2881 BTArrayKeyInfo *array = &so->arrayKeys[i];
2882 ScanKey skey = &so->keyData[array->scan_key];
2883
2884 if (array->num_elems == -1)
2885 *skip_array_set = true;
2886
2887 if (ScanDirectionIsForward(dir))
2888 {
2889 if (_bt_array_increment(rel, skey, array))
2890 return true;
2891 }
2892 else
2893 {
2894 if (_bt_array_decrement(rel, skey, array))
2895 return true;
2896 }
2897
2898 /*
2899 * Couldn't increment (or decrement) array. Handle array roll over.
2900 *
2901 * Start over at the array's lowest sorting value (or its highest
2902 * value, for backward scans)...
2903 */
2904 _bt_array_set_low_or_high(rel, skey, array,
2906
2907 /* ...then increment (or decrement) next most significant array */
2908 }
2909
2910 /*
2911 * The array keys are now exhausted.
2912 *
2913 * Restore the array keys to the state they were in immediately before we
2914 * were called. This ensures that the arrays only ever ratchet in the
2915 * current scan direction.
2916 *
2917 * Without this, scans could overlook matching tuples when the scan
2918 * direction gets reversed just before btgettuple runs out of items to
2919 * return, but just after _bt_readpage prepares all the items from the
2920 * scan's final page in so->currPos. When we're on the final page it is
2921 * typical for so->currPos to get invalidated once btgettuple finally
2922 * returns false, which'll effectively invalidate the scan's array keys.
2923 * That hasn't happened yet, though -- and in general it may never happen.
2924 */
2925 _bt_start_array_keys(scan, -dir);
2926
2927 return false;
2928}
for(;;)
int i
Definition: isn.c:77
static bool _bt_array_decrement(Relation rel, ScanKey skey, BTArrayKeyInfo *array)
Definition: nbtreadpage.c:3070
void _bt_start_array_keys(IndexScanDesc scan, ScanDirection dir)
Definition: nbtreadpage.c:537
static bool _bt_array_increment(Relation rel, ScanKey skey, BTArrayKeyInfo *array)
Definition: nbtreadpage.c:2937

References _bt_array_decrement(), _bt_array_increment(), _bt_array_set_low_or_high(), _bt_start_array_keys(), BTScanOpaqueData::arrayKeys, for(), i, IndexScanDescData::indexRelation, BTScanOpaqueData::keyData, BTArrayKeyInfo::num_elems, BTScanOpaqueData::numArrayKeys, IndexScanDescData::opaque, BTArrayKeyInfo::scan_key, and ScanDirectionIsForward.

Referenced by _bt_advance_array_keys().

◆ _bt_array_decrement()

static bool _bt_array_decrement ( Relation  rel,
ScanKey  skey,
BTArrayKeyInfo array 
)
static

Definition at line 3070 of file nbtreadpage.c.

3071{
3072 bool uflow = false;
3073 Datum dec_sk_argument;
3074
3077
3078 /* SAOP array? */
3079 if (array->num_elems != -1)
3080 {
3082 if (array->cur_elem > 0)
3083 {
3084 /*
3085 * Just decrement current element, and assign its datum to skey
3086 * (only skip arrays need us to free existing sk_argument memory)
3087 */
3088 array->cur_elem--;
3089 skey->sk_argument = array->elem_values[array->cur_elem];
3090
3091 /* Successfully decremented array */
3092 return true;
3093 }
3094
3095 /* Cannot decrement to before first array element */
3096 return false;
3097 }
3098
3099 /* Nope, this is a skip array */
3100 Assert(skey->sk_flags & SK_BT_SKIP);
3101
3102 /*
3103 * The sentinel value that represents the minimum value within the range
3104 * of a skip array (often just -inf) is never decrementable
3105 */
3106 if (skey->sk_flags & SK_BT_MINVAL)
3107 return false;
3108
3109 /*
3110 * When the current array element is NULL, and the lowest sorting value in
3111 * the index is also NULL, we cannot decrement before first array element
3112 */
3113 if ((skey->sk_flags & SK_ISNULL) && (skey->sk_flags & SK_BT_NULLS_FIRST))
3114 return false;
3115
3116 /*
3117 * Opclasses without skip support "decrement" the scan key's current
3118 * element by setting the PRIOR flag. The true prior value is determined
3119 * by repositioning to the last index tuple < existing sk_argument/current
3120 * array element. Note that this works in the usual way when the scan key
3121 * is already marked ISNULL (i.e. when the current element is NULL).
3122 */
3123 if (!array->sksup)
3124 {
3125 /* Successfully "decremented" array */
3126 skey->sk_flags |= SK_BT_PRIOR;
3127 return true;
3128 }
3129
3130 /*
3131 * Opclasses with skip support directly decrement sk_argument
3132 */
3133 if (skey->sk_flags & SK_ISNULL)
3134 {
3135 Assert(!(skey->sk_flags & SK_BT_NULLS_FIRST));
3136
3137 /*
3138 * Existing sk_argument/array element is NULL (for an IS NULL qual).
3139 *
3140 * "Decrement" from NULL to the high_elem value provided by opclass
3141 * skip support routine.
3142 */
3143 skey->sk_flags &= ~(SK_SEARCHNULL | SK_ISNULL);
3144 skey->sk_argument = datumCopy(array->sksup->high_elem,
3145 array->attbyval, array->attlen);
3146 return true;
3147 }
3148
3149 /*
3150 * Ask opclass support routine to provide decremented copy of existing
3151 * non-NULL sk_argument
3152 */
3153 dec_sk_argument = array->sksup->decrement(rel, skey->sk_argument, &uflow);
3154 if (unlikely(uflow))
3155 {
3156 /* dec_sk_argument has undefined value (so no pfree) */
3157 if (array->null_elem && (skey->sk_flags & SK_BT_NULLS_FIRST))
3158 {
3159 _bt_skiparray_set_isnull(rel, skey, array);
3160
3161 /* Successfully "decremented" array to NULL */
3162 return true;
3163 }
3164
3165 /* Cannot decrement to before first array element */
3166 return false;
3167 }
3168
3169 /*
3170 * Successfully decremented sk_argument to a non-NULL value. Make sure
3171 * that the decremented value is still within the range of the array.
3172 */
3173 if (array->low_compare &&
3175 array->low_compare->sk_collation,
3176 dec_sk_argument,
3177 array->low_compare->sk_argument)))
3178 {
3179 /* Keep existing sk_argument after all */
3180 if (!array->attbyval)
3181 pfree(DatumGetPointer(dec_sk_argument));
3182
3183 /* Cannot decrement to before first array element */
3184 return false;
3185 }
3186
3187 /* Accept value returned by opclass decrement callback */
3188 if (!array->attbyval && skey->sk_argument)
3190 skey->sk_argument = dec_sk_argument;
3191
3192 /* Successfully decremented array */
3193 return true;
3194}
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1150
void pfree(void *pointer)
Definition: mcxt.c:1594
static void _bt_skiparray_set_isnull(Relation rel, ScanKey skey, BTArrayKeyInfo *array)
Definition: nbtreadpage.c:3310
#define SK_BT_SKIP
Definition: nbtree.h:1106
#define SK_BT_PRIOR
Definition: nbtree.h:1112
#define SK_BT_NEXT
Definition: nbtree.h:1111
#define SK_BT_NULLS_FIRST
Definition: nbtree.h:1117
#define SK_BT_MAXVAL
Definition: nbtree.h:1110
#define SK_BT_MINVAL
Definition: nbtree.h:1109
static bool DatumGetBool(Datum X)
Definition: postgres.h:100
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:322
#define SK_SEARCHNULL
Definition: skey.h:121
#define SK_ISNULL
Definition: skey.h:115
bool attbyval
Definition: nbtree.h:1046
ScanKey low_compare
Definition: nbtree.h:1049
SkipSupport sksup
Definition: nbtree.h:1048
int16 attlen
Definition: nbtree.h:1045
bool null_elem
Definition: nbtree.h:1047
Datum sk_argument
Definition: skey.h:72
FmgrInfo sk_func
Definition: skey.h:71
Oid sk_collation
Definition: skey.h:70
SkipSupportIncDec decrement
Definition: skipsupport.h:91

References _bt_skiparray_set_isnull(), Assert(), BTArrayKeyInfo::attbyval, BTArrayKeyInfo::attlen, BTArrayKeyInfo::cur_elem, datumCopy(), DatumGetBool(), DatumGetPointer(), SkipSupportData::decrement, BTArrayKeyInfo::elem_values, FunctionCall2Coll(), SkipSupportData::high_elem, BTArrayKeyInfo::low_compare, BTArrayKeyInfo::null_elem, BTArrayKeyInfo::num_elems, pfree(), ScanKeyData::sk_argument, SK_BT_MAXVAL, SK_BT_MINVAL, SK_BT_NEXT, SK_BT_NULLS_FIRST, SK_BT_PRIOR, SK_BT_SKIP, ScanKeyData::sk_collation, ScanKeyData::sk_flags, ScanKeyData::sk_func, SK_ISNULL, SK_SEARCHARRAY, SK_SEARCHNULL, BTArrayKeyInfo::sksup, and unlikely.

Referenced by _bt_advance_array_keys_increment().

◆ _bt_array_increment()

static bool _bt_array_increment ( Relation  rel,
ScanKey  skey,
BTArrayKeyInfo array 
)
static

Definition at line 2937 of file nbtreadpage.c.

2938{
2939 bool oflow = false;
2940 Datum inc_sk_argument;
2941
2944
2945 /* SAOP array? */
2946 if (array->num_elems != -1)
2947 {
2949 if (array->cur_elem < array->num_elems - 1)
2950 {
2951 /*
2952 * Just increment current element, and assign its datum to skey
2953 * (only skip arrays need us to free existing sk_argument memory)
2954 */
2955 array->cur_elem++;
2956 skey->sk_argument = array->elem_values[array->cur_elem];
2957
2958 /* Successfully incremented array */
2959 return true;
2960 }
2961
2962 /* Cannot increment past final array element */
2963 return false;
2964 }
2965
2966 /* Nope, this is a skip array */
2967 Assert(skey->sk_flags & SK_BT_SKIP);
2968
2969 /*
2970 * The sentinel value that represents the maximum value within the range
2971 * of a skip array (often just +inf) is never incrementable
2972 */
2973 if (skey->sk_flags & SK_BT_MAXVAL)
2974 return false;
2975
2976 /*
2977 * When the current array element is NULL, and the highest sorting value
2978 * in the index is also NULL, we cannot increment past the final element
2979 */
2980 if ((skey->sk_flags & SK_ISNULL) && !(skey->sk_flags & SK_BT_NULLS_FIRST))
2981 return false;
2982
2983 /*
2984 * Opclasses without skip support "increment" the scan key's current
2985 * element by setting the NEXT flag. The true next value is determined by
2986 * repositioning to the first index tuple > existing sk_argument/current
2987 * array element. Note that this works in the usual way when the scan key
2988 * is already marked ISNULL (i.e. when the current element is NULL).
2989 */
2990 if (!array->sksup)
2991 {
2992 /* Successfully "incremented" array */
2993 skey->sk_flags |= SK_BT_NEXT;
2994 return true;
2995 }
2996
2997 /*
2998 * Opclasses with skip support directly increment sk_argument
2999 */
3000 if (skey->sk_flags & SK_ISNULL)
3001 {
3003
3004 /*
3005 * Existing sk_argument/array element is NULL (for an IS NULL qual).
3006 *
3007 * "Increment" from NULL to the low_elem value provided by opclass
3008 * skip support routine.
3009 */
3010 skey->sk_flags &= ~(SK_SEARCHNULL | SK_ISNULL);
3011 skey->sk_argument = datumCopy(array->sksup->low_elem,
3012 array->attbyval, array->attlen);
3013 return true;
3014 }
3015
3016 /*
3017 * Ask opclass support routine to provide incremented copy of existing
3018 * non-NULL sk_argument
3019 */
3020 inc_sk_argument = array->sksup->increment(rel, skey->sk_argument, &oflow);
3021 if (unlikely(oflow))
3022 {
3023 /* inc_sk_argument has undefined value (so no pfree) */
3024 if (array->null_elem && !(skey->sk_flags & SK_BT_NULLS_FIRST))
3025 {
3026 _bt_skiparray_set_isnull(rel, skey, array);
3027
3028 /* Successfully "incremented" array to NULL */
3029 return true;
3030 }
3031
3032 /* Cannot increment past final array element */
3033 return false;
3034 }
3035
3036 /*
3037 * Successfully incremented sk_argument to a non-NULL value. Make sure
3038 * that the incremented value is still within the range of the array.
3039 */
3040 if (array->high_compare &&
3042 array->high_compare->sk_collation,
3043 inc_sk_argument,
3044 array->high_compare->sk_argument)))
3045 {
3046 /* Keep existing sk_argument after all */
3047 if (!array->attbyval)
3048 pfree(DatumGetPointer(inc_sk_argument));
3049
3050 /* Cannot increment past final array element */
3051 return false;
3052 }
3053
3054 /* Accept value returned by opclass increment callback */
3055 if (!array->attbyval && skey->sk_argument)
3057 skey->sk_argument = inc_sk_argument;
3058
3059 /* Successfully incremented array */
3060 return true;
3061}
ScanKey high_compare
Definition: nbtree.h:1050
SkipSupportIncDec increment
Definition: skipsupport.h:92

References _bt_skiparray_set_isnull(), Assert(), BTArrayKeyInfo::attbyval, BTArrayKeyInfo::attlen, BTArrayKeyInfo::cur_elem, datumCopy(), DatumGetBool(), DatumGetPointer(), BTArrayKeyInfo::elem_values, FunctionCall2Coll(), BTArrayKeyInfo::high_compare, SkipSupportData::increment, SkipSupportData::low_elem, BTArrayKeyInfo::null_elem, BTArrayKeyInfo::num_elems, pfree(), ScanKeyData::sk_argument, SK_BT_MAXVAL, SK_BT_MINVAL, SK_BT_NEXT, SK_BT_NULLS_FIRST, SK_BT_PRIOR, SK_BT_SKIP, ScanKeyData::sk_collation, ScanKeyData::sk_flags, ScanKeyData::sk_func, SK_ISNULL, SK_SEARCHARRAY, SK_SEARCHNULL, BTArrayKeyInfo::sksup, and unlikely.

Referenced by _bt_advance_array_keys_increment().

◆ _bt_array_set_low_or_high()

static void _bt_array_set_low_or_high ( Relation  rel,
ScanKey  skey,
BTArrayKeyInfo array,
bool  low_not_high 
)
static

Definition at line 3203 of file nbtreadpage.c.

3205{
3207
3208 if (array->num_elems != -1)
3209 {
3210 /* set low or high element for SAOP array */
3211 int set_elem = 0;
3212
3213 Assert(!(skey->sk_flags & SK_BT_SKIP));
3214
3215 if (!low_not_high)
3216 set_elem = array->num_elems - 1;
3217
3218 /*
3219 * Just copy over array datum (only skip arrays require freeing and
3220 * allocating memory for sk_argument)
3221 */
3222 array->cur_elem = set_elem;
3223 skey->sk_argument = array->elem_values[set_elem];
3224
3225 return;
3226 }
3227
3228 /* set low or high element for skip array */
3229 Assert(skey->sk_flags & SK_BT_SKIP);
3230 Assert(array->num_elems == -1);
3231
3232 /* Free memory previously allocated for sk_argument if needed */
3233 if (!array->attbyval && skey->sk_argument)
3235
3236 /* Reset flags */
3237 skey->sk_argument = (Datum) 0;
3238 skey->sk_flags &= ~(SK_SEARCHNULL | SK_ISNULL |
3241
3242 if (array->null_elem &&
3243 (low_not_high == ((skey->sk_flags & SK_BT_NULLS_FIRST) != 0)))
3244 {
3245 /* Requested element (either lowest or highest) has the value NULL */
3246 skey->sk_flags |= (SK_SEARCHNULL | SK_ISNULL);
3247 }
3248 else if (low_not_high)
3249 {
3250 /* Setting array to lowest element (according to low_compare) */
3251 skey->sk_flags |= SK_BT_MINVAL;
3252 }
3253 else
3254 {
3255 /* Setting array to highest element (according to high_compare) */
3256 skey->sk_flags |= SK_BT_MAXVAL;
3257 }
3258}

References Assert(), BTArrayKeyInfo::attbyval, BTArrayKeyInfo::cur_elem, DatumGetPointer(), BTArrayKeyInfo::elem_values, BTArrayKeyInfo::null_elem, BTArrayKeyInfo::num_elems, pfree(), ScanKeyData::sk_argument, SK_BT_MAXVAL, SK_BT_MINVAL, SK_BT_NEXT, SK_BT_NULLS_FIRST, SK_BT_PRIOR, SK_BT_SKIP, ScanKeyData::sk_flags, SK_ISNULL, SK_SEARCHARRAY, and SK_SEARCHNULL.

Referenced by _bt_advance_array_keys(), _bt_advance_array_keys_increment(), _bt_skiparray_set_element(), and _bt_start_array_keys().

◆ _bt_binsrch_array_skey()

int _bt_binsrch_array_skey ( FmgrInfo orderproc,
bool  cur_elem_trig,
ScanDirection  dir,
Datum  tupdatum,
bool  tupnull,
BTArrayKeyInfo array,
ScanKey  cur,
int32 set_elem_result 
)

Definition at line 3415 of file nbtreadpage.c.

3420{
3421 int low_elem = 0,
3422 mid_elem = -1,
3423 high_elem = array->num_elems - 1,
3424 result = 0;
3425 Datum arrdatum;
3426
3427 Assert(cur->sk_flags & SK_SEARCHARRAY);
3428 Assert(!(cur->sk_flags & SK_BT_SKIP));
3429 Assert(!(cur->sk_flags & SK_ISNULL)); /* SAOP arrays never have NULLs */
3430 Assert(cur->sk_strategy == BTEqualStrategyNumber);
3431
3432 if (cur_elem_trig)
3433 {
3435 Assert(cur->sk_flags & SK_BT_REQFWD);
3436
3437 /*
3438 * When the scan key that triggered array advancement is a required
3439 * array scan key, it is now certain that the current array element
3440 * (plus all prior elements relative to the current scan direction)
3441 * cannot possibly be at or ahead of the corresponding tuple value.
3442 * (_bt_checkkeys must have called _bt_tuple_before_array_skeys, which
3443 * makes sure this is true as a condition of advancing the arrays.)
3444 *
3445 * This makes it safe to exclude array elements up to and including
3446 * the former-current array element from our search.
3447 *
3448 * Separately, when array advancement was triggered by a required scan
3449 * key, the array element immediately after the former-current element
3450 * is often either an exact tupdatum match, or a "close by" near-match
3451 * (a near-match tupdatum is one whose key space falls _between_ the
3452 * former-current and new-current array elements). We'll detect both
3453 * cases via an optimistic comparison of the new search lower bound
3454 * (or new search upper bound in the case of backwards scans).
3455 */
3456 if (ScanDirectionIsForward(dir))
3457 {
3458 low_elem = array->cur_elem + 1; /* old cur_elem exhausted */
3459
3460 /* Compare prospective new cur_elem (also the new lower bound) */
3461 if (high_elem >= low_elem)
3462 {
3463 arrdatum = array->elem_values[low_elem];
3464 result = _bt_compare_array_skey(orderproc, tupdatum, tupnull,
3465 arrdatum, cur);
3466
3467 if (result <= 0)
3468 {
3469 /* Optimistic comparison optimization worked out */
3470 *set_elem_result = result;
3471 return low_elem;
3472 }
3473 mid_elem = low_elem;
3474 low_elem++; /* this cur_elem exhausted, too */
3475 }
3476
3477 if (high_elem < low_elem)
3478 {
3479 /* Caller needs to perform "beyond end" array advancement */
3480 *set_elem_result = 1;
3481 return high_elem;
3482 }
3483 }
3484 else
3485 {
3486 high_elem = array->cur_elem - 1; /* old cur_elem exhausted */
3487
3488 /* Compare prospective new cur_elem (also the new upper bound) */
3489 if (high_elem >= low_elem)
3490 {
3491 arrdatum = array->elem_values[high_elem];
3492 result = _bt_compare_array_skey(orderproc, tupdatum, tupnull,
3493 arrdatum, cur);
3494
3495 if (result >= 0)
3496 {
3497 /* Optimistic comparison optimization worked out */
3498 *set_elem_result = result;
3499 return high_elem;
3500 }
3501 mid_elem = high_elem;
3502 high_elem--; /* this cur_elem exhausted, too */
3503 }
3504
3505 if (high_elem < low_elem)
3506 {
3507 /* Caller needs to perform "beyond end" array advancement */
3508 *set_elem_result = -1;
3509 return low_elem;
3510 }
3511 }
3512 }
3513
3514 while (high_elem > low_elem)
3515 {
3516 mid_elem = low_elem + ((high_elem - low_elem) / 2);
3517 arrdatum = array->elem_values[mid_elem];
3518
3519 result = _bt_compare_array_skey(orderproc, tupdatum, tupnull,
3520 arrdatum, cur);
3521
3522 if (result == 0)
3523 {
3524 /*
3525 * It's safe to quit as soon as we see an equal array element.
3526 * This often saves an extra comparison or two...
3527 */
3528 low_elem = mid_elem;
3529 break;
3530 }
3531
3532 if (result > 0)
3533 low_elem = mid_elem + 1;
3534 else
3535 high_elem = mid_elem;
3536 }
3537
3538 /*
3539 * ...but our caller also cares about how its searched-for tuple datum
3540 * compares to the low_elem datum. Must always set *set_elem_result with
3541 * the result of that comparison specifically.
3542 */
3543 if (low_elem != mid_elem)
3544 result = _bt_compare_array_skey(orderproc, tupdatum, tupnull,
3545 array->elem_values[low_elem], cur);
3546
3547 *set_elem_result = result;
3548
3549 return low_elem;
3550}
#define ScanDirectionIsNoMovement(direction)
Definition: sdir.h:57

References _bt_compare_array_skey(), Assert(), BTEqualStrategyNumber, cur, BTArrayKeyInfo::cur_elem, BTArrayKeyInfo::elem_values, BTArrayKeyInfo::num_elems, ScanDirectionIsForward, ScanDirectionIsNoMovement, SK_BT_REQFWD, SK_BT_SKIP, SK_ISNULL, and SK_SEARCHARRAY.

Referenced by _bt_advance_array_keys(), _bt_saoparray_shrink(), and _bt_set_startikey().

◆ _bt_binsrch_skiparray_skey()

static void _bt_binsrch_skiparray_skey ( bool  cur_elem_trig,
ScanDirection  dir,
Datum  tupdatum,
bool  tupnull,
BTArrayKeyInfo array,
ScanKey  cur,
int32 set_elem_result 
)
static

Definition at line 3571 of file nbtreadpage.c.

3575{
3576 Assert(cur->sk_flags & SK_BT_SKIP);
3577 Assert(cur->sk_flags & SK_SEARCHARRAY);
3578 Assert(cur->sk_flags & SK_BT_REQFWD);
3579 Assert(array->num_elems == -1);
3581
3582 if (array->null_elem)
3583 {
3584 Assert(!array->low_compare && !array->high_compare);
3585
3586 *set_elem_result = 0;
3587 return;
3588 }
3589
3590 if (tupnull) /* NULL tupdatum */
3591 {
3592 if (cur->sk_flags & SK_BT_NULLS_FIRST)
3593 *set_elem_result = -1; /* NULL "<" NOT_NULL */
3594 else
3595 *set_elem_result = 1; /* NULL ">" NOT_NULL */
3596 return;
3597 }
3598
3599 /*
3600 * Array inequalities determine whether tupdatum is within the range of
3601 * caller's skip array
3602 */
3603 *set_elem_result = 0;
3604 if (ScanDirectionIsForward(dir))
3605 {
3606 /*
3607 * Evaluate low_compare first (unless cur_elem_trig tells us that it
3608 * cannot possibly fail to be satisfied), then evaluate high_compare
3609 */
3610 if (!cur_elem_trig && array->low_compare &&
3612 array->low_compare->sk_collation,
3613 tupdatum,
3614 array->low_compare->sk_argument)))
3615 *set_elem_result = -1;
3616 else if (array->high_compare &&
3618 array->high_compare->sk_collation,
3619 tupdatum,
3620 array->high_compare->sk_argument)))
3621 *set_elem_result = 1;
3622 }
3623 else
3624 {
3625 /*
3626 * Evaluate high_compare first (unless cur_elem_trig tells us that it
3627 * cannot possibly fail to be satisfied), then evaluate low_compare
3628 */
3629 if (!cur_elem_trig && array->high_compare &&
3631 array->high_compare->sk_collation,
3632 tupdatum,
3633 array->high_compare->sk_argument)))
3634 *set_elem_result = 1;
3635 else if (array->low_compare &&
3637 array->low_compare->sk_collation,
3638 tupdatum,
3639 array->low_compare->sk_argument)))
3640 *set_elem_result = -1;
3641 }
3642
3643 /*
3644 * Assert that any keys that were assumed to be satisfied already (due to
3645 * caller passing cur_elem_trig=true) really are satisfied as expected
3646 */
3647#ifdef USE_ASSERT_CHECKING
3648 if (cur_elem_trig)
3649 {
3650 if (ScanDirectionIsForward(dir) && array->low_compare)
3652 array->low_compare->sk_collation,
3653 tupdatum,
3654 array->low_compare->sk_argument)));
3655
3656 if (ScanDirectionIsBackward(dir) && array->high_compare)
3658 array->high_compare->sk_collation,
3659 tupdatum,
3660 array->high_compare->sk_argument)));
3661 }
3662#endif
3663}

References Assert(), cur, DatumGetBool(), FunctionCall2Coll(), BTArrayKeyInfo::high_compare, BTArrayKeyInfo::low_compare, BTArrayKeyInfo::null_elem, BTArrayKeyInfo::num_elems, ScanDirectionIsBackward, ScanDirectionIsForward, ScanDirectionIsNoMovement, ScanKeyData::sk_argument, SK_BT_NULLS_FIRST, SK_BT_REQFWD, SK_BT_SKIP, ScanKeyData::sk_collation, ScanKeyData::sk_func, and SK_SEARCHARRAY.

Referenced by _bt_advance_array_keys(), _bt_set_startikey(), and _bt_tuple_before_array_skeys().

◆ _bt_check_compare()

static bool _bt_check_compare ( IndexScanDesc  scan,
ScanDirection  dir,
IndexTuple  tuple,
int  tupnatts,
TupleDesc  tupdesc,
bool  advancenonrequired,
bool  forcenonrequired,
bool *  continuescan,
int *  ikey 
)
static

Definition at line 1312 of file nbtreadpage.c.

1316{
1317 BTScanOpaque so = (BTScanOpaque) scan->opaque;
1318
1319 *continuescan = true; /* default assumption */
1320
1321 for (; *ikey < so->numberOfKeys; (*ikey)++)
1322 {
1323 ScanKey key = so->keyData + *ikey;
1324 Datum datum;
1325 bool isNull;
1326 bool requiredSameDir = false,
1327 requiredOppositeDirOnly = false;
1328
1329 /*
1330 * Check if the key is required in the current scan direction, in the
1331 * opposite scan direction _only_, or in neither direction (except
1332 * when we're forced to treat all scan keys as nonrequired)
1333 */
1334 if (forcenonrequired)
1335 {
1336 /* treating scan's keys as non-required */
1337 }
1338 else if (((key->sk_flags & SK_BT_REQFWD) && ScanDirectionIsForward(dir)) ||
1339 ((key->sk_flags & SK_BT_REQBKWD) && ScanDirectionIsBackward(dir)))
1340 requiredSameDir = true;
1341 else if (((key->sk_flags & SK_BT_REQFWD) && ScanDirectionIsBackward(dir)) ||
1342 ((key->sk_flags & SK_BT_REQBKWD) && ScanDirectionIsForward(dir)))
1343 requiredOppositeDirOnly = true;
1344
1345 if (key->sk_attno > tupnatts)
1346 {
1347 /*
1348 * This attribute is truncated (must be high key). The value for
1349 * this attribute in the first non-pivot tuple on the page to the
1350 * right could be any possible value. Assume that truncated
1351 * attribute passes the qual.
1352 */
1353 Assert(BTreeTupleIsPivot(tuple));
1354 continue;
1355 }
1356
1357 /*
1358 * A skip array scan key uses one of several sentinel values. We just
1359 * fall back on _bt_tuple_before_array_skeys when we see such a value.
1360 */
1361 if (key->sk_flags & (SK_BT_MINVAL | SK_BT_MAXVAL |
1363 {
1364 Assert(key->sk_flags & SK_SEARCHARRAY);
1365 Assert(key->sk_flags & SK_BT_SKIP);
1366 Assert(requiredSameDir || forcenonrequired);
1367
1368 /*
1369 * Cannot fall back on _bt_tuple_before_array_skeys when we're
1370 * treating the scan's keys as nonrequired, though. Just handle
1371 * this like any other non-required equality-type array key.
1372 */
1373 if (forcenonrequired)
1374 return _bt_advance_array_keys(scan, NULL, tuple, tupnatts,
1375 tupdesc, *ikey, false);
1376
1377 *continuescan = false;
1378 return false;
1379 }
1380
1381 /* row-comparison keys need special processing */
1382 if (key->sk_flags & SK_ROW_HEADER)
1383 {
1384 if (_bt_check_rowcompare(key, tuple, tupnatts, tupdesc, dir,
1385 forcenonrequired, continuescan))
1386 continue;
1387 return false;
1388 }
1389
1390 datum = index_getattr(tuple,
1391 key->sk_attno,
1392 tupdesc,
1393 &isNull);
1394
1395 if (key->sk_flags & SK_ISNULL)
1396 {
1397 /* Handle IS NULL/NOT NULL tests */
1398 if (key->sk_flags & SK_SEARCHNULL)
1399 {
1400 if (isNull)
1401 continue; /* tuple satisfies this qual */
1402 }
1403 else
1404 {
1405 Assert(key->sk_flags & SK_SEARCHNOTNULL);
1406 Assert(!(key->sk_flags & SK_BT_SKIP));
1407 if (!isNull)
1408 continue; /* tuple satisfies this qual */
1409 }
1410
1411 /*
1412 * Tuple fails this qual. If it's a required qual for the current
1413 * scan direction, then we can conclude no further tuples will
1414 * pass, either.
1415 */
1416 if (requiredSameDir)
1417 *continuescan = false;
1418 else if (unlikely(key->sk_flags & SK_BT_SKIP))
1419 {
1420 /*
1421 * If we're treating scan keys as nonrequired, and encounter a
1422 * skip array scan key whose current element is NULL, then it
1423 * must be a non-range skip array. It must be satisfied, so
1424 * there's no need to call _bt_advance_array_keys to check.
1425 */
1426 Assert(forcenonrequired && *ikey > 0);
1427 continue;
1428 }
1429
1430 /*
1431 * This indextuple doesn't match the qual.
1432 */
1433 return false;
1434 }
1435
1436 if (isNull)
1437 {
1438 /*
1439 * Scalar scan key isn't satisfied by NULL tuple value.
1440 *
1441 * If we're treating scan keys as nonrequired, and key is for a
1442 * skip array, then we must attempt to advance the array to NULL
1443 * (if we're successful then the tuple might match the qual).
1444 */
1445 if (unlikely(forcenonrequired && key->sk_flags & SK_BT_SKIP))
1446 return _bt_advance_array_keys(scan, NULL, tuple, tupnatts,
1447 tupdesc, *ikey, false);
1448
1449 if (key->sk_flags & SK_BT_NULLS_FIRST)
1450 {
1451 /*
1452 * Since NULLs are sorted before non-NULLs, we know we have
1453 * reached the lower limit of the range of values for this
1454 * index attr. On a backward scan, we can stop if this qual
1455 * is one of the "must match" subset. We can stop regardless
1456 * of whether the qual is > or <, so long as it's required,
1457 * because it's not possible for any future tuples to pass. On
1458 * a forward scan, however, we must keep going, because we may
1459 * have initially positioned to the start of the index.
1460 * (_bt_advance_array_keys also relies on this behavior during
1461 * forward scans.)
1462 */
1463 if ((requiredSameDir || requiredOppositeDirOnly) &&
1465 *continuescan = false;
1466 }
1467 else
1468 {
1469 /*
1470 * Since NULLs are sorted after non-NULLs, we know we have
1471 * reached the upper limit of the range of values for this
1472 * index attr. On a forward scan, we can stop if this qual is
1473 * one of the "must match" subset. We can stop regardless of
1474 * whether the qual is > or <, so long as it's required,
1475 * because it's not possible for any future tuples to pass. On
1476 * a backward scan, however, we must keep going, because we
1477 * may have initially positioned to the end of the index.
1478 * (_bt_advance_array_keys also relies on this behavior during
1479 * backward scans.)
1480 */
1481 if ((requiredSameDir || requiredOppositeDirOnly) &&
1483 *continuescan = false;
1484 }
1485
1486 /*
1487 * This indextuple doesn't match the qual.
1488 */
1489 return false;
1490 }
1491
1492 if (!DatumGetBool(FunctionCall2Coll(&key->sk_func, key->sk_collation,
1493 datum, key->sk_argument)))
1494 {
1495 /*
1496 * Tuple fails this qual. If it's a required qual for the current
1497 * scan direction, then we can conclude no further tuples will
1498 * pass, either.
1499 */
1500 if (requiredSameDir)
1501 *continuescan = false;
1502
1503 /*
1504 * If this is a non-required equality-type array key, the tuple
1505 * needs to be checked against every possible array key. Handle
1506 * this by "advancing" the scan key's array to a matching value
1507 * (if we're successful then the tuple might match the qual).
1508 */
1509 else if (advancenonrequired &&
1510 key->sk_strategy == BTEqualStrategyNumber &&
1511 (key->sk_flags & SK_SEARCHARRAY))
1512 return _bt_advance_array_keys(scan, NULL, tuple, tupnatts,
1513 tupdesc, *ikey, false);
1514
1515 /*
1516 * This indextuple doesn't match the qual.
1517 */
1518 return false;
1519 }
1520 }
1521
1522 /* If we get here, the tuple passes all index quals. */
1523 return true;
1524}
static bool _bt_check_rowcompare(ScanKey header, IndexTuple tuple, int tupnatts, TupleDesc tupdesc, ScanDirection dir, bool forcenonrequired, bool *continuescan)
Definition: nbtreadpage.c:1579
static bool BTreeTupleIsPivot(IndexTuple itup)
Definition: nbtree.h:481
#define SK_ROW_HEADER
Definition: skey.h:117
#define SK_SEARCHNOTNULL
Definition: skey.h:122

References _bt_advance_array_keys(), _bt_check_rowcompare(), Assert(), BTEqualStrategyNumber, BTreeTupleIsPivot(), DatumGetBool(), for(), FunctionCall2Coll(), index_getattr(), sort-test::key, BTScanOpaqueData::keyData, BTScanOpaqueData::numberOfKeys, IndexScanDescData::opaque, ScanDirectionIsBackward, ScanDirectionIsForward, SK_BT_MAXVAL, SK_BT_MINVAL, SK_BT_NEXT, SK_BT_NULLS_FIRST, SK_BT_PRIOR, SK_BT_REQBKWD, SK_BT_REQFWD, SK_BT_SKIP, SK_ISNULL, SK_ROW_HEADER, SK_SEARCHARRAY, SK_SEARCHNOTNULL, SK_SEARCHNULL, and unlikely.

Referenced by _bt_advance_array_keys(), _bt_checkkeys(), and _bt_oppodir_checkkeys().

◆ _bt_check_rowcompare()

static bool _bt_check_rowcompare ( ScanKey  header,
IndexTuple  tuple,
int  tupnatts,
TupleDesc  tupdesc,
ScanDirection  dir,
bool  forcenonrequired,
bool *  continuescan 
)
static

Definition at line 1579 of file nbtreadpage.c.

1582{
1583 ScanKey subkey = (ScanKey) DatumGetPointer(header->sk_argument);
1584 int32 cmpresult = 0;
1585 bool result;
1586
1587 /* First subkey should be same as the header says */
1588 Assert(header->sk_flags & SK_ROW_HEADER);
1589 Assert(subkey->sk_attno == header->sk_attno);
1590 Assert(subkey->sk_strategy == header->sk_strategy);
1591
1592 /* Loop over columns of the row condition */
1593 for (;;)
1594 {
1595 Datum datum;
1596 bool isNull;
1597
1598 Assert(subkey->sk_flags & SK_ROW_MEMBER);
1599
1600 /* When a NULL row member is compared, the row never matches */
1601 if (subkey->sk_flags & SK_ISNULL)
1602 {
1603 /*
1604 * Unlike the simple-scankey case, this isn't a disallowed case
1605 * (except when it's the first row element that has the NULL arg).
1606 * But it can never match. If all the earlier row comparison
1607 * columns are required for the scan direction, we can stop the
1608 * scan, because there can't be another tuple that will succeed.
1609 */
1610 Assert(subkey != (ScanKey) DatumGetPointer(header->sk_argument));
1611 subkey--;
1612 if (forcenonrequired)
1613 {
1614 /* treating scan's keys as non-required */
1615 }
1616 else if ((subkey->sk_flags & SK_BT_REQFWD) &&
1618 *continuescan = false;
1619 else if ((subkey->sk_flags & SK_BT_REQBKWD) &&
1621 *continuescan = false;
1622 return false;
1623 }
1624
1625 if (subkey->sk_attno > tupnatts)
1626 {
1627 /*
1628 * This attribute is truncated (must be high key). The value for
1629 * this attribute in the first non-pivot tuple on the page to the
1630 * right could be any possible value. Assume that truncated
1631 * attribute passes the qual.
1632 */
1633 Assert(BTreeTupleIsPivot(tuple));
1634 return true;
1635 }
1636
1637 datum = index_getattr(tuple,
1638 subkey->sk_attno,
1639 tupdesc,
1640 &isNull);
1641
1642 if (isNull)
1643 {
1644 int reqflags;
1645
1646 if (forcenonrequired)
1647 {
1648 /* treating scan's keys as non-required */
1649 }
1650 else if (subkey->sk_flags & SK_BT_NULLS_FIRST)
1651 {
1652 /*
1653 * Since NULLs are sorted before non-NULLs, we know we have
1654 * reached the lower limit of the range of values for this
1655 * index attr. On a backward scan, we can stop if this qual
1656 * is one of the "must match" subset. However, on a forwards
1657 * scan, we must keep going, because we may have initially
1658 * positioned to the start of the index.
1659 *
1660 * All required NULLS FIRST > row members can use NULL tuple
1661 * values to end backwards scans, just like with other values.
1662 * A qual "WHERE (a, b, c) > (9, 42, 'foo')" can terminate a
1663 * backwards scan upon reaching the index's rightmost "a = 9"
1664 * tuple whose "b" column contains a NULL (if not sooner).
1665 * Since "b" is NULLS FIRST, we can treat its NULLs as "<" 42.
1666 */
1667 reqflags = SK_BT_REQBKWD;
1668
1669 /*
1670 * When a most significant required NULLS FIRST < row compare
1671 * member sees NULL tuple values during a backwards scan, it
1672 * signals the end of matches for the whole row compare/scan.
1673 * A qual "WHERE (a, b, c) < (9, 42, 'foo')" will terminate a
1674 * backwards scan upon reaching the rightmost tuple whose "a"
1675 * column has a NULL. The "a" NULL value is "<" 9, and yet
1676 * our < row compare will still end the scan. (This isn't
1677 * safe with later/lower-order row members. Notice that it
1678 * can only happen with an "a" NULL some time after the scan
1679 * completely stops needing to use its "b" and "c" members.)
1680 */
1681 if (subkey == (ScanKey) DatumGetPointer(header->sk_argument))
1682 reqflags |= SK_BT_REQFWD; /* safe, first row member */
1683
1684 if ((subkey->sk_flags & reqflags) &&
1686 *continuescan = false;
1687 }
1688 else
1689 {
1690 /*
1691 * Since NULLs are sorted after non-NULLs, we know we have
1692 * reached the upper limit of the range of values for this
1693 * index attr. On a forward scan, we can stop if this qual is
1694 * one of the "must match" subset. However, on a backward
1695 * scan, we must keep going, because we may have initially
1696 * positioned to the end of the index.
1697 *
1698 * All required NULLS LAST < row members can use NULL tuple
1699 * values to end forwards scans, just like with other values.
1700 * A qual "WHERE (a, b, c) < (9, 42, 'foo')" can terminate a
1701 * forwards scan upon reaching the index's leftmost "a = 9"
1702 * tuple whose "b" column contains a NULL (if not sooner).
1703 * Since "b" is NULLS LAST, we can treat its NULLs as ">" 42.
1704 */
1705 reqflags = SK_BT_REQFWD;
1706
1707 /*
1708 * When a most significant required NULLS LAST > row compare
1709 * member sees NULL tuple values during a forwards scan, it
1710 * signals the end of matches for the whole row compare/scan.
1711 * A qual "WHERE (a, b, c) > (9, 42, 'foo')" will terminate a
1712 * forwards scan upon reaching the leftmost tuple whose "a"
1713 * column has a NULL. The "a" NULL value is ">" 9, and yet
1714 * our > row compare will end the scan. (This isn't safe with
1715 * later/lower-order row members. Notice that it can only
1716 * happen with an "a" NULL some time after the scan completely
1717 * stops needing to use its "b" and "c" members.)
1718 */
1719 if (subkey == (ScanKey) DatumGetPointer(header->sk_argument))
1720 reqflags |= SK_BT_REQBKWD; /* safe, first row member */
1721
1722 if ((subkey->sk_flags & reqflags) &&
1724 *continuescan = false;
1725 }
1726
1727 /*
1728 * In any case, this indextuple doesn't match the qual.
1729 */
1730 return false;
1731 }
1732
1733 /* Perform the test --- three-way comparison not bool operator */
1734 cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func,
1735 subkey->sk_collation,
1736 datum,
1737 subkey->sk_argument));
1738
1739 if (subkey->sk_flags & SK_BT_DESC)
1740 INVERT_COMPARE_RESULT(cmpresult);
1741
1742 /* Done comparing if unequal, else advance to next column */
1743 if (cmpresult != 0)
1744 break;
1745
1746 if (subkey->sk_flags & SK_ROW_END)
1747 break;
1748 subkey++;
1749 }
1750
1751 /* Final subkey/column determines if row compare is satisfied */
1752 result = _bt_rowcompare_cmpresult(subkey, cmpresult);
1753
1754 if (!result && !forcenonrequired)
1755 {
1756 /*
1757 * Tuple fails this qual. If it's a required qual for the current
1758 * scan direction, then we can conclude no further tuples will pass,
1759 * either. Note we have to look at the deciding column, not
1760 * necessarily the first or last column of the row condition.
1761 */
1762 if ((subkey->sk_flags & SK_BT_REQFWD) &&
1764 *continuescan = false;
1765 else if ((subkey->sk_flags & SK_BT_REQBKWD) &&
1767 *continuescan = false;
1768 }
1769
1770 return result;
1771}
#define INVERT_COMPARE_RESULT(var)
Definition: c.h:1118
static bool _bt_rowcompare_cmpresult(ScanKey subkey, int cmpresult)
Definition: nbtreadpage.c:1781
#define SK_BT_DESC
Definition: nbtree.h:1116
static int32 DatumGetInt32(Datum X)
Definition: postgres.h:212
#define SK_ROW_MEMBER
Definition: skey.h:118
#define SK_ROW_END
Definition: skey.h:119
ScanKeyData * ScanKey
Definition: skey.h:75
AttrNumber sk_attno
Definition: skey.h:67

References _bt_rowcompare_cmpresult(), Assert(), BTreeTupleIsPivot(), DatumGetInt32(), DatumGetPointer(), FunctionCall2Coll(), index_getattr(), INVERT_COMPARE_RESULT, ScanDirectionIsBackward, ScanDirectionIsForward, ScanKeyData::sk_argument, ScanKeyData::sk_attno, SK_BT_DESC, SK_BT_NULLS_FIRST, SK_BT_REQBKWD, SK_BT_REQFWD, ScanKeyData::sk_collation, ScanKeyData::sk_flags, ScanKeyData::sk_func, SK_ISNULL, SK_ROW_END, SK_ROW_HEADER, SK_ROW_MEMBER, and ScanKeyData::sk_strategy.

Referenced by _bt_check_compare().

◆ _bt_checkkeys()

static bool _bt_checkkeys ( IndexScanDesc  scan,
BTReadPageState pstate,
bool  arrayKeys,
IndexTuple  tuple,
int  tupnatts 
)
static

Definition at line 1149 of file nbtreadpage.c.

1151{
1152 TupleDesc tupdesc = RelationGetDescr(scan->indexRelation);
1154 ScanDirection dir = pstate->dir;
1155 int ikey = pstate->startikey;
1156 bool res;
1157
1158 Assert(BTreeTupleGetNAtts(tuple, scan->indexRelation) == tupnatts);
1159 Assert(!so->needPrimScan && !so->scanBehind && !so->oppositeDirCheck);
1160 Assert(arrayKeys || so->numArrayKeys == 0);
1161
1162 res = _bt_check_compare(scan, dir, tuple, tupnatts, tupdesc, arrayKeys,
1163 pstate->forcenonrequired, &pstate->continuescan,
1164 &ikey);
1165
1166 /*
1167 * If _bt_check_compare relied on the pstate.startikey optimization, call
1168 * again (in assert-enabled builds) to verify it didn't affect our answer.
1169 *
1170 * Note: we can't do this when !pstate.forcenonrequired, since any arrays
1171 * before pstate.startikey won't have advanced on this page at all.
1172 */
1173 Assert(!pstate->forcenonrequired || arrayKeys);
1174#ifdef USE_ASSERT_CHECKING
1175 if (pstate->startikey > 0 && !pstate->forcenonrequired)
1176 {
1177 bool dres,
1178 dcontinuescan;
1179 int dikey = 0;
1180
1181 /* Pass arrayKeys=false to avoid array side-effects */
1182 dres = _bt_check_compare(scan, dir, tuple, tupnatts, tupdesc, false,
1183 pstate->forcenonrequired, &dcontinuescan,
1184 &dikey);
1185 Assert(res == dres);
1186 Assert(pstate->continuescan == dcontinuescan);
1187
1188 /*
1189 * Should also get the same ikey result. We need a slightly weaker
1190 * assertion during arrayKeys calls, since they might be using an
1191 * array that couldn't be marked required during preprocessing.
1192 */
1193 Assert(arrayKeys || ikey == dikey);
1194 Assert(ikey <= dikey);
1195 }
1196#endif
1197
1198 /*
1199 * Only one _bt_check_compare call is required in the common case where
1200 * there are no equality strategy array scan keys. Otherwise we can only
1201 * accept _bt_check_compare's answer unreservedly when it didn't set
1202 * pstate.continuescan=false.
1203 */
1204 if (!arrayKeys || pstate->continuescan)
1205 return res;
1206
1207 /*
1208 * _bt_check_compare call set continuescan=false in the presence of
1209 * equality type array keys. This could mean that the tuple is just past
1210 * the end of matches for the current array keys.
1211 *
1212 * It's also possible that the scan is still _before_ the _start_ of
1213 * tuples matching the current set of array keys. Check for that first.
1214 */
1215 Assert(!pstate->forcenonrequired);
1216 if (_bt_tuple_before_array_skeys(scan, dir, tuple, tupdesc, tupnatts, true,
1217 ikey, NULL))
1218 {
1219 /* Override _bt_check_compare, continue primitive scan */
1220 pstate->continuescan = true;
1221
1222 /*
1223 * We will end up here repeatedly given a group of tuples > the
1224 * previous array keys and < the now-current keys (for a backwards
1225 * scan it's just the same, though the operators swap positions).
1226 *
1227 * We must avoid allowing this linear search process to scan very many
1228 * tuples from well before the start of tuples matching the current
1229 * array keys (or from well before the point where we'll once again
1230 * have to advance the scan's array keys).
1231 *
1232 * We keep the overhead under control by speculatively "looking ahead"
1233 * to later still-unscanned items from this same leaf page. We'll
1234 * only attempt this once the number of tuples that the linear search
1235 * process has examined starts to get out of hand.
1236 */
1237 pstate->rechecks++;
1239 {
1240 /* See if we should skip ahead within the current leaf page */
1241 _bt_checkkeys_look_ahead(scan, pstate, tupnatts, tupdesc);
1242
1243 /*
1244 * Might have set pstate.skip to a later page offset. When that
1245 * happens then _bt_readpage caller will inexpensively skip ahead
1246 * to a later tuple from the same page (the one just after the
1247 * tuple we successfully "looked ahead" to).
1248 */
1249 }
1250
1251 /* This indextuple doesn't match the current qual, in any case */
1252 return false;
1253 }
1254
1255 /*
1256 * Caller's tuple is >= the current set of array keys and other equality
1257 * constraint scan keys (or <= if this is a backwards scan). It's now
1258 * clear that we _must_ advance any required array keys in lockstep with
1259 * the scan.
1260 */
1261 return _bt_advance_array_keys(scan, pstate, tuple, tupnatts, tupdesc,
1262 ikey, true);
1263}
#define LOOK_AHEAD_REQUIRED_RECHECKS
Definition: nbtreadpage.c:1116
static void _bt_checkkeys_look_ahead(IndexScanDesc scan, BTReadPageState *pstate, int tupnatts, TupleDesc tupdesc)
Definition: nbtreadpage.c:2052
#define RelationGetDescr(relation)
Definition: rel.h:541
bool forcenonrequired
Definition: nbtreadpage.c:40

References _bt_advance_array_keys(), _bt_check_compare(), _bt_checkkeys_look_ahead(), _bt_tuple_before_array_skeys(), Assert(), BTreeTupleGetNAtts, BTReadPageState::continuescan, BTReadPageState::dir, BTReadPageState::forcenonrequired, IndexScanDescData::indexRelation, LOOK_AHEAD_REQUIRED_RECHECKS, IndexScanDescData::opaque, PG_USED_FOR_ASSERTS_ONLY, BTReadPageState::rechecks, RelationGetDescr, and BTReadPageState::startikey.

Referenced by _bt_readpage().

◆ _bt_checkkeys_look_ahead()

static void _bt_checkkeys_look_ahead ( IndexScanDesc  scan,
BTReadPageState pstate,
int  tupnatts,
TupleDesc  tupdesc 
)
static

Definition at line 2052 of file nbtreadpage.c.

2054{
2055 ScanDirection dir = pstate->dir;
2056 OffsetNumber aheadoffnum;
2057 IndexTuple ahead;
2058
2059 Assert(!pstate->forcenonrequired);
2060
2061 /* Avoid looking ahead when comparing the page high key */
2062 if (pstate->offnum < pstate->minoff)
2063 return;
2064
2065 /*
2066 * Don't look ahead when there aren't enough tuples remaining on the page
2067 * (in the current scan direction) for it to be worth our while
2068 */
2069 if (ScanDirectionIsForward(dir) &&
2070 pstate->offnum >= pstate->maxoff - LOOK_AHEAD_DEFAULT_DISTANCE)
2071 return;
2072 else if (ScanDirectionIsBackward(dir) &&
2073 pstate->offnum <= pstate->minoff + LOOK_AHEAD_DEFAULT_DISTANCE)
2074 return;
2075
2076 /*
2077 * The look ahead distance starts small, and ramps up as each call here
2078 * allows _bt_readpage to skip over more tuples
2079 */
2080 if (!pstate->targetdistance)
2082 else if (pstate->targetdistance < MaxIndexTuplesPerPage / 2)
2083 pstate->targetdistance *= 2;
2084
2085 /* Don't read past the end (or before the start) of the page, though */
2086 if (ScanDirectionIsForward(dir))
2087 aheadoffnum = Min((int) pstate->maxoff,
2088 (int) pstate->offnum + pstate->targetdistance);
2089 else
2090 aheadoffnum = Max((int) pstate->minoff,
2091 (int) pstate->offnum - pstate->targetdistance);
2092
2093 ahead = (IndexTuple) PageGetItem(pstate->page,
2094 PageGetItemId(pstate->page, aheadoffnum));
2095 if (_bt_tuple_before_array_skeys(scan, dir, ahead, tupdesc, tupnatts,
2096 false, 0, NULL))
2097 {
2098 /*
2099 * Success -- instruct _bt_readpage to skip ahead to very next tuple
2100 * after the one we determined was still before the current array keys
2101 */
2102 if (ScanDirectionIsForward(dir))
2103 pstate->skip = aheadoffnum + 1;
2104 else
2105 pstate->skip = aheadoffnum - 1;
2106 }
2107 else
2108 {
2109 /*
2110 * Failure -- "ahead" tuple is too far ahead (we were too aggressive).
2111 *
2112 * Reset the number of rechecks, and aggressively reduce the target
2113 * distance (we're much more aggressive here than we were when the
2114 * distance was initially ramped up).
2115 */
2116 pstate->rechecks = 0;
2117 pstate->targetdistance = Max(pstate->targetdistance / 8, 1);
2118 }
2119}
static void * PageGetItem(const PageData *page, const ItemIdData *itemId)
Definition: bufpage.h:353
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:243
#define Min(x, y)
Definition: c.h:1016
#define Max(x, y)
Definition: c.h:1010
IndexTupleData * IndexTuple
Definition: itup.h:53
#define MaxIndexTuplesPerPage
Definition: itup.h:181
#define LOOK_AHEAD_DEFAULT_DISTANCE
Definition: nbtreadpage.c:1117
uint16 OffsetNumber
Definition: off.h:24
OffsetNumber minoff
Definition: nbtreadpage.c:35
OffsetNumber offnum
Definition: nbtreadpage.c:44

References _bt_tuple_before_array_skeys(), Assert(), BTReadPageState::dir, BTReadPageState::forcenonrequired, LOOK_AHEAD_DEFAULT_DISTANCE, Max, MaxIndexTuplesPerPage, BTReadPageState::maxoff, Min, BTReadPageState::minoff, BTReadPageState::offnum, BTReadPageState::page, PageGetItem(), PageGetItemId(), BTReadPageState::rechecks, ScanDirectionIsBackward, ScanDirectionIsForward, BTReadPageState::skip, and BTReadPageState::targetdistance.

Referenced by _bt_checkkeys().

◆ _bt_compare_array_skey()

static int32 _bt_compare_array_skey ( FmgrInfo orderproc,
Datum  tupdatum,
bool  tupnull,
Datum  arrdatum,
ScanKey  cur 
)
inlinestatic

Definition at line 3344 of file nbtreadpage.c.

3347{
3348 int32 result = 0;
3349
3350 Assert(cur->sk_strategy == BTEqualStrategyNumber);
3351 Assert(!(cur->sk_flags & (SK_BT_MINVAL | SK_BT_MAXVAL)));
3352
3353 if (tupnull) /* NULL tupdatum */
3354 {
3355 if (cur->sk_flags & SK_ISNULL)
3356 result = 0; /* NULL "=" NULL */
3357 else if (cur->sk_flags & SK_BT_NULLS_FIRST)
3358 result = -1; /* NULL "<" NOT_NULL */
3359 else
3360 result = 1; /* NULL ">" NOT_NULL */
3361 }
3362 else if (cur->sk_flags & SK_ISNULL) /* NOT_NULL tupdatum, NULL arrdatum */
3363 {
3364 if (cur->sk_flags & SK_BT_NULLS_FIRST)
3365 result = 1; /* NOT_NULL ">" NULL */
3366 else
3367 result = -1; /* NOT_NULL "<" NULL */
3368 }
3369 else
3370 {
3371 /*
3372 * Like _bt_compare, we need to be careful of cross-type comparisons,
3373 * so the left value has to be the value that came from an index tuple
3374 */
3375 result = DatumGetInt32(FunctionCall2Coll(orderproc, cur->sk_collation,
3376 tupdatum, arrdatum));
3377
3378 /*
3379 * We flip the sign by following the obvious rule: flip whenever the
3380 * column is a DESC column.
3381 *
3382 * _bt_compare does it the wrong way around (flip when *ASC*) in order
3383 * to compensate for passing its orderproc arguments backwards. We
3384 * don't need to play these games because we find it natural to pass
3385 * tupdatum as the left value (and arrdatum as the right value).
3386 */
3387 if (cur->sk_flags & SK_BT_DESC)
3388 INVERT_COMPARE_RESULT(result);
3389 }
3390
3391 return result;
3392}

References Assert(), BTEqualStrategyNumber, cur, DatumGetInt32(), FunctionCall2Coll(), INVERT_COMPARE_RESULT, SK_BT_DESC, SK_BT_MAXVAL, SK_BT_MINVAL, SK_BT_NULLS_FIRST, and SK_ISNULL.

Referenced by _bt_advance_array_keys(), _bt_binsrch_array_skey(), and _bt_tuple_before_array_skeys().

◆ _bt_oppodir_checkkeys()

static bool _bt_oppodir_checkkeys ( IndexScanDesc  scan,
ScanDirection  dir,
IndexTuple  finaltup 
)
static

Definition at line 1007 of file nbtreadpage.c.

1009{
1010 Relation rel = scan->indexRelation;
1011 TupleDesc tupdesc = RelationGetDescr(rel);
1012 BTScanOpaque so = (BTScanOpaque) scan->opaque;
1013 int nfinaltupatts = BTreeTupleGetNAtts(finaltup, rel);
1014 bool continuescan;
1015 ScanDirection flipped = -dir;
1016 int ikey = 0;
1017
1018 Assert(so->numArrayKeys);
1019
1020 _bt_check_compare(scan, flipped, finaltup, nfinaltupatts, tupdesc, false,
1021 false, &continuescan,
1022 &ikey);
1023
1024 if (!continuescan && so->keyData[ikey].sk_strategy != BTEqualStrategyNumber)
1025 return false;
1026
1027 return true;
1028}

References _bt_check_compare(), Assert(), BTEqualStrategyNumber, BTreeTupleGetNAtts, IndexScanDescData::indexRelation, BTScanOpaqueData::keyData, BTScanOpaqueData::numArrayKeys, IndexScanDescData::opaque, RelationGetDescr, and ScanKeyData::sk_strategy.

Referenced by _bt_advance_array_keys(), and _bt_scanbehind_checkkeys().

◆ _bt_readpage()

bool _bt_readpage ( IndexScanDesc  scan,
ScanDirection  dir,
OffsetNumber  offnum,
bool  firstpage 
)

Definition at line 134 of file nbtreadpage.c.

136{
137 Relation rel = scan->indexRelation;
138 BTScanOpaque so = (BTScanOpaque) scan->opaque;
139 Page page;
140 BTPageOpaque opaque;
141 OffsetNumber minoff;
142 OffsetNumber maxoff;
143 BTReadPageState pstate;
144 bool arrayKeys,
145 ignore_killed_tuples = scan->ignore_killed_tuples;
146 int itemIndex,
147 indnatts;
148
149 /* save the page/buffer block number, along with its sibling links */
150 page = BufferGetPage(so->currPos.buf);
151 opaque = BTPageGetOpaque(page);
153 so->currPos.prevPage = opaque->btpo_prev;
154 so->currPos.nextPage = opaque->btpo_next;
155 /* delay setting so->currPos.lsn until _bt_drop_lock_and_maybe_pin */
156 pstate.dir = so->currPos.dir = dir;
157 so->currPos.nextTupleOffset = 0;
158
159 /* either moreRight or moreLeft should be set now (may be unset later) */
161 so->currPos.moreLeft);
162 Assert(!P_IGNORE(opaque));
164 Assert(!so->needPrimScan);
165
166 /* initialize local variables */
168 arrayKeys = so->numArrayKeys != 0;
169 minoff = P_FIRSTDATAKEY(opaque);
170 maxoff = PageGetMaxOffsetNumber(page);
171
172 /* initialize page-level state that we'll pass to _bt_checkkeys */
173 pstate.minoff = minoff;
174 pstate.maxoff = maxoff;
175 pstate.finaltup = NULL;
176 pstate.page = page;
177 pstate.firstpage = firstpage;
178 pstate.forcenonrequired = false;
179 pstate.startikey = 0;
180 pstate.offnum = InvalidOffsetNumber;
181 pstate.skip = InvalidOffsetNumber;
182 pstate.continuescan = true; /* default assumption */
183 pstate.rechecks = 0;
184 pstate.targetdistance = 0;
185 pstate.nskipadvances = 0;
186
187 if (scan->parallel_scan)
188 {
189 /* allow next/prev page to be read by other worker without delay */
190 if (ScanDirectionIsForward(dir))
192 so->currPos.currPage);
193 else
195 so->currPos.currPage);
196 }
197
199
200 if (ScanDirectionIsForward(dir))
201 {
202 /* SK_SEARCHARRAY forward scans must provide high key up front */
203 if (arrayKeys)
204 {
205 if (!P_RIGHTMOST(opaque))
206 {
207 ItemId iid = PageGetItemId(page, P_HIKEY);
208
209 pstate.finaltup = (IndexTuple) PageGetItem(page, iid);
210
211 if (unlikely(so->scanBehind) &&
212 !_bt_scanbehind_checkkeys(scan, dir, pstate.finaltup))
213 {
214 /* Schedule another primitive index scan after all */
215 so->currPos.moreRight = false;
216 so->needPrimScan = true;
217 if (scan->parallel_scan)
219 so->currPos.currPage);
220 return false;
221 }
222 }
223
224 so->scanBehind = so->oppositeDirCheck = false; /* reset */
225 }
226
227 /*
228 * Consider pstate.startikey optimization once the ongoing primitive
229 * index scan has already read at least one page
230 */
231 if (!pstate.firstpage && minoff < maxoff)
232 _bt_set_startikey(scan, &pstate);
233
234 /* load items[] in ascending order */
235 itemIndex = 0;
236
237 offnum = Max(offnum, minoff);
238
239 while (offnum <= maxoff)
240 {
241 ItemId iid = PageGetItemId(page, offnum);
242 IndexTuple itup;
243 bool passes_quals;
244
245 /*
246 * If the scan specifies not to return killed tuples, then we
247 * treat a killed tuple as not passing the qual
248 */
249 if (ignore_killed_tuples && ItemIdIsDead(iid))
250 {
251 offnum = OffsetNumberNext(offnum);
252 continue;
253 }
254
255 itup = (IndexTuple) PageGetItem(page, iid);
257
258 pstate.offnum = offnum;
259 passes_quals = _bt_checkkeys(scan, &pstate, arrayKeys,
260 itup, indnatts);
261
262 /*
263 * Check if we need to skip ahead to a later tuple (only possible
264 * when the scan uses array keys)
265 */
266 if (arrayKeys && OffsetNumberIsValid(pstate.skip))
267 {
268 Assert(!passes_quals && pstate.continuescan);
269 Assert(offnum < pstate.skip);
270 Assert(!pstate.forcenonrequired);
271
272 offnum = pstate.skip;
273 pstate.skip = InvalidOffsetNumber;
274 continue;
275 }
276
277 if (passes_quals)
278 {
279 /* tuple passes all scan key conditions */
280 if (!BTreeTupleIsPosting(itup))
281 {
282 /* Remember it */
283 _bt_saveitem(so, itemIndex, offnum, itup);
284 itemIndex++;
285 }
286 else
287 {
288 int tupleOffset;
289
290 /* Set up posting list state (and remember first TID) */
291 tupleOffset =
292 _bt_setuppostingitems(so, itemIndex, offnum,
293 BTreeTupleGetPostingN(itup, 0),
294 itup);
295 itemIndex++;
296
297 /* Remember all later TIDs (must be at least one) */
298 for (int i = 1; i < BTreeTupleGetNPosting(itup); i++)
299 {
300 _bt_savepostingitem(so, itemIndex, offnum,
302 tupleOffset);
303 itemIndex++;
304 }
305 }
306 }
307 /* When !continuescan, there can't be any more matches, so stop */
308 if (!pstate.continuescan)
309 break;
310
311 offnum = OffsetNumberNext(offnum);
312 }
313
314 /*
315 * We don't need to visit page to the right when the high key
316 * indicates that no more matches will be found there.
317 *
318 * Checking the high key like this works out more often than you might
319 * think. Leaf page splits pick a split point between the two most
320 * dissimilar tuples (this is weighed against the need to evenly share
321 * free space). Leaf pages with high key attribute values that can
322 * only appear on non-pivot tuples on the right sibling page are
323 * common.
324 */
325 if (pstate.continuescan && !so->scanBehind && !P_RIGHTMOST(opaque))
326 {
327 ItemId iid = PageGetItemId(page, P_HIKEY);
328 IndexTuple itup = (IndexTuple) PageGetItem(page, iid);
329 int truncatt;
330
331 /* Reset arrays, per _bt_set_startikey contract */
332 if (pstate.forcenonrequired)
333 _bt_start_array_keys(scan, dir);
334 pstate.forcenonrequired = false;
335 pstate.startikey = 0; /* _bt_set_startikey ignores P_HIKEY */
336
337 truncatt = BTreeTupleGetNAtts(itup, rel);
338 _bt_checkkeys(scan, &pstate, arrayKeys, itup, truncatt);
339 }
340
341 if (!pstate.continuescan)
342 so->currPos.moreRight = false;
343
344 Assert(itemIndex <= MaxTIDsPerBTreePage);
345 so->currPos.firstItem = 0;
346 so->currPos.lastItem = itemIndex - 1;
347 so->currPos.itemIndex = 0;
348 }
349 else
350 {
351 /* SK_SEARCHARRAY backward scans must provide final tuple up front */
352 if (arrayKeys)
353 {
354 if (minoff <= maxoff && !P_LEFTMOST(opaque))
355 {
356 ItemId iid = PageGetItemId(page, minoff);
357
358 pstate.finaltup = (IndexTuple) PageGetItem(page, iid);
359
360 if (unlikely(so->scanBehind) &&
361 !_bt_scanbehind_checkkeys(scan, dir, pstate.finaltup))
362 {
363 /* Schedule another primitive index scan after all */
364 so->currPos.moreLeft = false;
365 so->needPrimScan = true;
366 if (scan->parallel_scan)
368 so->currPos.currPage);
369 return false;
370 }
371 }
372
373 so->scanBehind = so->oppositeDirCheck = false; /* reset */
374 }
375
376 /*
377 * Consider pstate.startikey optimization once the ongoing primitive
378 * index scan has already read at least one page
379 */
380 if (!pstate.firstpage && minoff < maxoff)
381 _bt_set_startikey(scan, &pstate);
382
383 /* load items[] in descending order */
384 itemIndex = MaxTIDsPerBTreePage;
385
386 offnum = Min(offnum, maxoff);
387
388 while (offnum >= minoff)
389 {
390 ItemId iid = PageGetItemId(page, offnum);
391 IndexTuple itup;
392 bool tuple_alive;
393 bool passes_quals;
394
395 /*
396 * If the scan specifies not to return killed tuples, then we
397 * treat a killed tuple as not passing the qual. Most of the
398 * time, it's a win to not bother examining the tuple's index
399 * keys, but just skip to the next tuple (previous, actually,
400 * since we're scanning backwards). However, if this is the first
401 * tuple on the page, we do check the index keys, to prevent
402 * uselessly advancing to the page to the left. This is similar
403 * to the high key optimization used by forward scans.
404 */
405 if (ignore_killed_tuples && ItemIdIsDead(iid))
406 {
407 if (offnum > minoff)
408 {
409 offnum = OffsetNumberPrev(offnum);
410 continue;
411 }
412
413 tuple_alive = false;
414 }
415 else
416 tuple_alive = true;
417
418 itup = (IndexTuple) PageGetItem(page, iid);
420
421 pstate.offnum = offnum;
422 if (arrayKeys && offnum == minoff && pstate.forcenonrequired)
423 {
424 /* Reset arrays, per _bt_set_startikey contract */
425 pstate.forcenonrequired = false;
426 pstate.startikey = 0;
427 _bt_start_array_keys(scan, dir);
428 }
429 passes_quals = _bt_checkkeys(scan, &pstate, arrayKeys,
430 itup, indnatts);
431
432 if (arrayKeys && so->scanBehind)
433 {
434 /*
435 * Done scanning this page, but not done with the current
436 * primscan.
437 *
438 * Note: Forward scans don't check this explicitly, since they
439 * prefer to reuse pstate.skip for this instead.
440 */
441 Assert(!passes_quals && pstate.continuescan);
442 Assert(!pstate.forcenonrequired);
443
444 break;
445 }
446
447 /*
448 * Check if we need to skip ahead to a later tuple (only possible
449 * when the scan uses array keys)
450 */
451 if (arrayKeys && OffsetNumberIsValid(pstate.skip))
452 {
453 Assert(!passes_quals && pstate.continuescan);
454 Assert(offnum > pstate.skip);
455 Assert(!pstate.forcenonrequired);
456
457 offnum = pstate.skip;
458 pstate.skip = InvalidOffsetNumber;
459 continue;
460 }
461
462 if (passes_quals && tuple_alive)
463 {
464 /* tuple passes all scan key conditions */
465 if (!BTreeTupleIsPosting(itup))
466 {
467 /* Remember it */
468 itemIndex--;
469 _bt_saveitem(so, itemIndex, offnum, itup);
470 }
471 else
472 {
474 int tupleOffset;
475
476 /* Set up posting list state (and remember last TID) */
477 itemIndex--;
478 tupleOffset =
479 _bt_setuppostingitems(so, itemIndex, offnum,
480 BTreeTupleGetPostingN(itup, nitems - 1),
481 itup);
482
483 /* Remember all prior TIDs (must be at least one) */
484 for (int i = nitems - 2; i >= 0; i--)
485 {
486 itemIndex--;
487 _bt_savepostingitem(so, itemIndex, offnum,
489 tupleOffset);
490 }
491 }
492 }
493 /* When !continuescan, there can't be any more matches, so stop */
494 if (!pstate.continuescan)
495 break;
496
497 offnum = OffsetNumberPrev(offnum);
498 }
499
500 /*
501 * We don't need to visit page to the left when no more matches will
502 * be found there
503 */
504 if (!pstate.continuescan)
505 so->currPos.moreLeft = false;
506
507 Assert(itemIndex >= 0);
508 so->currPos.firstItem = itemIndex;
511 }
512
513 /*
514 * If _bt_set_startikey told us to temporarily treat the scan's keys as
515 * nonrequired (possible only during scans with array keys), there must be
516 * no lasting consequences for the scan's array keys. The scan's arrays
517 * should now have exactly the same elements as they would have had if the
518 * nonrequired behavior had never been used. (In general, a scan's arrays
519 * are expected to track its progress through the index's key space.)
520 *
521 * We are required (by _bt_set_startikey) to call _bt_checkkeys against
522 * pstate.finaltup with pstate.forcenonrequired=false to allow the scan's
523 * arrays to recover. Assert that that step hasn't been missed.
524 */
525 Assert(!pstate.forcenonrequired);
526
527 return (so->currPos.firstItem <= so->currPos.lastItem);
528}
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:4223
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:436
PageData * Page
Definition: bufpage.h:81
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition: bufpage.h:371
uint16_t uint16
Definition: c.h:551
#define nitems(x)
Definition: indent.h:31
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
static void _bt_saveitem(BTScanOpaque so, int itemIndex, OffsetNumber offnum, IndexTuple itup)
Definition: nbtreadpage.c:1032
static int _bt_setuppostingitems(BTScanOpaque so, int itemIndex, OffsetNumber offnum, const ItemPointerData *heapTid, IndexTuple itup)
Definition: nbtreadpage.c:1062
static bool _bt_scanbehind_checkkeys(IndexScanDesc scan, ScanDirection dir, IndexTuple finaltup)
Definition: nbtreadpage.c:952
static void _bt_savepostingitem(BTScanOpaque so, int itemIndex, OffsetNumber offnum, ItemPointer heapTid, int tupleOffset)
Definition: nbtreadpage.c:1100
static void _bt_set_startikey(IndexScanDesc scan, BTReadPageState *pstate)
Definition: nbtreadpage.c:593
static bool _bt_checkkeys(IndexScanDesc scan, BTReadPageState *pstate, bool arrayKeys, IndexTuple tuple, int tupnatts)
Definition: nbtreadpage.c:1149
void _bt_parallel_release(IndexScanDesc scan, BlockNumber next_scan_page, BlockNumber curr_page)
Definition: nbtree.c:1023
#define BTScanPosIsPinned(scanpos)
Definition: nbtree.h:1004
static uint16 BTreeTupleGetNPosting(IndexTuple posting)
Definition: nbtree.h:519
#define P_HIKEY
Definition: nbtree.h:368
#define P_LEFTMOST(opaque)
Definition: nbtree.h:219
#define BTPageGetOpaque(page)
Definition: nbtree.h:74
#define MaxTIDsPerBTreePage
Definition: nbtree.h:186
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:370
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:220
static ItemPointer BTreeTupleGetPostingN(IndexTuple posting, int n)
Definition: nbtree.h:545
#define P_IGNORE(opaque)
Definition: nbtree.h:226
static bool BTreeTupleIsPosting(IndexTuple itup)
Definition: nbtree.h:493
#define InvalidOffsetNumber
Definition: off.h:26
#define OffsetNumberIsValid(offsetNumber)
Definition: off.h:39
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
#define OffsetNumberPrev(offsetNumber)
Definition: off.h:54
void PredicateLockPage(Relation relation, BlockNumber blkno, Snapshot snapshot)
Definition: predicate.c:2597
#define IndexRelationGetNumberOfAttributes(relation)
Definition: rel.h:527
bool moreRight
Definition: nbtree.h:986
Buffer buf
Definition: nbtree.h:964
int firstItem
Definition: nbtree.h:995
int nextTupleOffset
Definition: nbtree.h:979
BlockNumber prevPage
Definition: nbtree.h:968
BlockNumber nextPage
Definition: nbtree.h:969
bool moreLeft
Definition: nbtree.h:985
int lastItem
Definition: nbtree.h:996
int itemIndex
Definition: nbtree.h:997
ScanDirection dir
Definition: nbtree.h:973
bool ignore_killed_tuples
Definition: relscan.h:150
struct SnapshotData * xs_snapshot
Definition: relscan.h:140

References _bt_checkkeys(), _bt_parallel_primscan_schedule(), _bt_parallel_release(), _bt_saveitem(), _bt_savepostingitem(), _bt_scanbehind_checkkeys(), _bt_set_startikey(), _bt_setuppostingitems(), _bt_start_array_keys(), Assert(), BTPageGetOpaque, BTreeTupleGetNAtts, BTreeTupleGetNPosting(), BTreeTupleGetPostingN(), BTreeTupleIsPivot(), BTreeTupleIsPosting(), BTScanPosIsPinned, BTScanPosData::buf, BufferGetBlockNumber(), BufferGetPage(), BTScanPosData::currPage, BTScanOpaqueData::currPos, BTScanPosData::dir, BTScanPosData::firstItem, i, IndexScanDescData::ignore_killed_tuples, IndexScanDescData::indexRelation, IndexRelationGetNumberOfAttributes, InvalidOffsetNumber, ItemIdIsDead, BTScanPosData::itemIndex, BTScanPosData::lastItem, Max, MaxTIDsPerBTreePage, Min, BTScanPosData::moreLeft, BTScanPosData::moreRight, BTScanOpaqueData::needPrimScan, BTScanPosData::nextPage, BTScanPosData::nextTupleOffset, nitems, BTScanOpaqueData::numArrayKeys, OffsetNumberIsValid, OffsetNumberNext, OffsetNumberPrev, IndexScanDescData::opaque, BTScanOpaqueData::oppositeDirCheck, P_FIRSTDATAKEY, P_HIKEY, P_IGNORE, P_LEFTMOST, P_RIGHTMOST, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), IndexScanDescData::parallel_scan, PredicateLockPage(), BTScanPosData::prevPage, BTScanOpaqueData::scanBehind, ScanDirectionIsForward, unlikely, and IndexScanDescData::xs_snapshot.

Referenced by _bt_readfirstpage(), and _bt_readnextpage().

◆ _bt_rowcompare_cmpresult()

static bool _bt_rowcompare_cmpresult ( ScanKey  subkey,
int  cmpresult 
)
static

Definition at line 1781 of file nbtreadpage.c.

1782{
1783 bool satisfied;
1784
1785 Assert(subkey->sk_flags & SK_ROW_MEMBER);
1786
1787 switch (subkey->sk_strategy)
1788 {
1790 satisfied = (cmpresult < 0);
1791 break;
1793 satisfied = (cmpresult <= 0);
1794 break;
1796 satisfied = (cmpresult >= 0);
1797 break;
1799 satisfied = (cmpresult > 0);
1800 break;
1801 default:
1802 /* EQ and NE cases aren't allowed here */
1803 elog(ERROR, "unexpected strategy number %d", subkey->sk_strategy);
1804 satisfied = false; /* keep compiler quiet */
1805 break;
1806 }
1807
1808 return satisfied;
1809}
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
#define BTLessStrategyNumber
Definition: stratnum.h:29
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32

References Assert(), BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, elog, ERROR, ScanKeyData::sk_flags, SK_ROW_MEMBER, and ScanKeyData::sk_strategy.

Referenced by _bt_check_rowcompare(), and _bt_set_startikey().

◆ _bt_saveitem()

static void _bt_saveitem ( BTScanOpaque  so,
int  itemIndex,
OffsetNumber  offnum,
IndexTuple  itup 
)
static

Definition at line 1032 of file nbtreadpage.c.

1034{
1035 BTScanPosItem *currItem = &so->currPos.items[itemIndex];
1036
1038
1039 currItem->heapTid = itup->t_tid;
1040 currItem->indexOffset = offnum;
1041 if (so->currTuples)
1042 {
1043 Size itupsz = IndexTupleSize(itup);
1044
1045 currItem->tupleOffset = so->currPos.nextTupleOffset;
1046 memcpy(so->currTuples + so->currPos.nextTupleOffset, itup, itupsz);
1047 so->currPos.nextTupleOffset += MAXALIGN(itupsz);
1048 }
1049}
#define MAXALIGN(LEN)
Definition: c.h:824
size_t Size
Definition: c.h:624
static Size IndexTupleSize(const IndexTupleData *itup)
Definition: itup.h:71
char * currTuples
Definition: nbtree.h:1080
BTScanPosItem items[MaxTIDsPerBTreePage]
Definition: nbtree.h:999
ItemPointerData heapTid
Definition: nbtree.h:957
LocationIndex tupleOffset
Definition: nbtree.h:959
OffsetNumber indexOffset
Definition: nbtree.h:958
ItemPointerData t_tid
Definition: itup.h:37

References Assert(), BTreeTupleIsPivot(), BTreeTupleIsPosting(), BTScanOpaqueData::currPos, BTScanOpaqueData::currTuples, BTScanPosItem::heapTid, BTScanPosItem::indexOffset, IndexTupleSize(), BTScanPosData::items, MAXALIGN, BTScanPosData::nextTupleOffset, IndexTupleData::t_tid, and BTScanPosItem::tupleOffset.

Referenced by _bt_readpage().

◆ _bt_savepostingitem()

static void _bt_savepostingitem ( BTScanOpaque  so,
int  itemIndex,
OffsetNumber  offnum,
ItemPointer  heapTid,
int  tupleOffset 
)
inlinestatic

Definition at line 1100 of file nbtreadpage.c.

1102{
1103 BTScanPosItem *currItem = &so->currPos.items[itemIndex];
1104
1105 currItem->heapTid = *heapTid;
1106 currItem->indexOffset = offnum;
1107
1108 /*
1109 * Have index-only scans return the same base IndexTuple for every TID
1110 * that originates from the same posting list
1111 */
1112 if (so->currTuples)
1113 currItem->tupleOffset = tupleOffset;
1114}

References BTScanOpaqueData::currPos, BTScanOpaqueData::currTuples, BTScanPosItem::heapTid, BTScanPosItem::indexOffset, BTScanPosData::items, and BTScanPosItem::tupleOffset.

Referenced by _bt_readpage().

◆ _bt_scanbehind_checkkeys()

static bool _bt_scanbehind_checkkeys ( IndexScanDesc  scan,
ScanDirection  dir,
IndexTuple  finaltup 
)
static

Definition at line 952 of file nbtreadpage.c.

954{
955 Relation rel = scan->indexRelation;
956 TupleDesc tupdesc = RelationGetDescr(rel);
957 BTScanOpaque so = (BTScanOpaque) scan->opaque;
958 int nfinaltupatts = BTreeTupleGetNAtts(finaltup, rel);
959 bool scanBehind;
960
961 Assert(so->numArrayKeys);
962
963 if (_bt_tuple_before_array_skeys(scan, dir, finaltup, tupdesc,
964 nfinaltupatts, false, 0, &scanBehind))
965 return false;
966
967 /*
968 * If scanBehind was set, all of the untruncated attribute values from
969 * finaltup that correspond to an array match the array's current element,
970 * but there are other keys associated with truncated suffix attributes.
971 * Array advancement must have incremented the scan's arrays on the
972 * previous page, resulting in a set of array keys that happen to be an
973 * exact match for the current page high key's untruncated prefix values.
974 *
975 * This page definitely doesn't contain tuples that the scan will need to
976 * return. The next page may or may not contain relevant tuples. Handle
977 * this by cutting our losses and starting a new primscan.
978 */
979 if (scanBehind)
980 return false;
981
982 if (!so->oppositeDirCheck)
983 return true;
984
985 return _bt_oppodir_checkkeys(scan, dir, finaltup);
986}

References _bt_oppodir_checkkeys(), _bt_tuple_before_array_skeys(), Assert(), BTreeTupleGetNAtts, IndexScanDescData::indexRelation, BTScanOpaqueData::numArrayKeys, IndexScanDescData::opaque, BTScanOpaqueData::oppositeDirCheck, and RelationGetDescr.

Referenced by _bt_readpage().

◆ _bt_set_startikey()

static void _bt_set_startikey ( IndexScanDesc  scan,
BTReadPageState pstate 
)
static

Definition at line 593 of file nbtreadpage.c.

594{
595 BTScanOpaque so = (BTScanOpaque) scan->opaque;
596 Relation rel = scan->indexRelation;
597 TupleDesc tupdesc = RelationGetDescr(rel);
598 ItemId iid;
599 IndexTuple firsttup,
600 lasttup;
601 int startikey = 0,
602 arrayidx = 0,
603 firstchangingattnum;
604 bool start_past_saop_eq = false;
605
606 Assert(!so->scanBehind);
607 Assert(pstate->minoff < pstate->maxoff);
608 Assert(!pstate->firstpage);
609 Assert(pstate->startikey == 0);
610 Assert(!so->numArrayKeys || pstate->finaltup ||
611 P_RIGHTMOST(BTPageGetOpaque(pstate->page)) ||
613
614 if (so->numberOfKeys == 0)
615 return;
616
617 /* minoff is an offset to the lowest non-pivot tuple on the page */
618 iid = PageGetItemId(pstate->page, pstate->minoff);
619 firsttup = (IndexTuple) PageGetItem(pstate->page, iid);
620
621 /* maxoff is an offset to the highest non-pivot tuple on the page */
622 iid = PageGetItemId(pstate->page, pstate->maxoff);
623 lasttup = (IndexTuple) PageGetItem(pstate->page, iid);
624
625 /* Determine the first attribute whose values change on caller's page */
626 firstchangingattnum = _bt_keep_natts_fast(rel, firsttup, lasttup);
627
628 for (; startikey < so->numberOfKeys; startikey++)
629 {
630 ScanKey key = so->keyData + startikey;
631 BTArrayKeyInfo *array;
632 Datum firstdatum,
633 lastdatum;
634 bool firstnull,
635 lastnull;
636 int32 result;
637
638 /*
639 * Determine if it's safe to set pstate.startikey to an offset to a
640 * key that comes after this key, by examining this key
641 */
642 if (key->sk_flags & SK_ROW_HEADER)
643 {
644 /* RowCompare inequality (header key) */
645 ScanKey subkey = (ScanKey) DatumGetPointer(key->sk_argument);
646 bool satisfied = false;
647
648 for (;;)
649 {
650 int cmpresult;
651 bool firstsatisfies = false;
652
653 if (subkey->sk_attno > firstchangingattnum) /* >, not >= */
654 break; /* unsafe, preceding attr has multiple
655 * distinct values */
656
657 if (subkey->sk_flags & SK_ISNULL)
658 break; /* unsafe, unsatisfiable NULL subkey arg */
659
660 firstdatum = index_getattr(firsttup, subkey->sk_attno,
661 tupdesc, &firstnull);
662 lastdatum = index_getattr(lasttup, subkey->sk_attno,
663 tupdesc, &lastnull);
664
665 if (firstnull || lastnull)
666 break; /* unsafe, NULL value won't satisfy subkey */
667
668 /*
669 * Compare the first tuple's datum for this row compare member
670 */
671 cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func,
672 subkey->sk_collation,
673 firstdatum,
674 subkey->sk_argument));
675 if (subkey->sk_flags & SK_BT_DESC)
676 INVERT_COMPARE_RESULT(cmpresult);
677
678 if (cmpresult != 0 || (subkey->sk_flags & SK_ROW_END))
679 {
680 firstsatisfies = _bt_rowcompare_cmpresult(subkey,
681 cmpresult);
682 if (!firstsatisfies)
683 {
684 /* Unsafe, firstdatum does not satisfy subkey */
685 break;
686 }
687 }
688
689 /*
690 * Compare the last tuple's datum for this row compare member
691 */
692 cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func,
693 subkey->sk_collation,
694 lastdatum,
695 subkey->sk_argument));
696 if (subkey->sk_flags & SK_BT_DESC)
697 INVERT_COMPARE_RESULT(cmpresult);
698
699 if (cmpresult != 0 || (subkey->sk_flags & SK_ROW_END))
700 {
701 if (!firstsatisfies)
702 {
703 /*
704 * It's only safe to set startikey beyond the row
705 * compare header key when both firsttup and lasttup
706 * satisfy the key as a whole based on the same
707 * deciding subkey/attribute. That can't happen now.
708 */
709 break; /* unsafe */
710 }
711
712 satisfied = _bt_rowcompare_cmpresult(subkey, cmpresult);
713 break; /* safe iff 'satisfied' is true */
714 }
715
716 /* Move on to next row member/subkey */
717 if (subkey->sk_flags & SK_ROW_END)
718 break; /* defensive */
719 subkey++;
720
721 /*
722 * We deliberately don't check if the next subkey has the same
723 * strategy as this iteration's subkey (which happens when
724 * subkeys for both ASC and DESC columns are used together),
725 * nor if any subkey is marked required. This is safe because
726 * in general all prior index attributes must have only one
727 * distinct value (across all of the tuples on the page) in
728 * order for us to even consider any subkey's attribute.
729 */
730 }
731
732 if (satisfied)
733 {
734 /* Safe, row compare satisfied by every tuple on page */
735 continue;
736 }
737
738 break; /* unsafe */
739 }
740 if (key->sk_strategy != BTEqualStrategyNumber)
741 {
742 /*
743 * Scalar inequality key.
744 *
745 * It's definitely safe for _bt_checkkeys to avoid assessing this
746 * inequality when the page's first and last non-pivot tuples both
747 * satisfy the inequality (since the same must also be true of all
748 * the tuples in between these two).
749 *
750 * Unlike the "=" case, it doesn't matter if this attribute has
751 * more than one distinct value (though it _is_ necessary for any
752 * and all _prior_ attributes to contain no more than one distinct
753 * value amongst all of the tuples from pstate.page).
754 */
755 if (key->sk_attno > firstchangingattnum) /* >, not >= */
756 break; /* unsafe, preceding attr has multiple
757 * distinct values */
758
759 firstdatum = index_getattr(firsttup, key->sk_attno, tupdesc, &firstnull);
760 lastdatum = index_getattr(lasttup, key->sk_attno, tupdesc, &lastnull);
761
762 if (key->sk_flags & SK_ISNULL)
763 {
764 /* IS NOT NULL key */
765 Assert(key->sk_flags & SK_SEARCHNOTNULL);
766
767 if (firstnull || lastnull)
768 break; /* unsafe */
769
770 /* Safe, IS NOT NULL key satisfied by every tuple */
771 continue;
772 }
773
774 /* Test firsttup */
775 if (firstnull ||
777 key->sk_collation, firstdatum,
778 key->sk_argument)))
779 break; /* unsafe */
780
781 /* Test lasttup */
782 if (lastnull ||
784 key->sk_collation, lastdatum,
785 key->sk_argument)))
786 break; /* unsafe */
787
788 /* Safe, scalar inequality satisfied by every tuple */
789 continue;
790 }
791
792 /* Some = key (could be a scalar = key, could be an array = key) */
793 Assert(key->sk_strategy == BTEqualStrategyNumber);
794
795 if (!(key->sk_flags & SK_SEARCHARRAY))
796 {
797 /*
798 * Scalar = key (possibly an IS NULL key).
799 *
800 * It is unsafe to set pstate.startikey to an ikey beyond this
801 * key, unless the = key is satisfied by every possible tuple on
802 * the page (possible only when attribute has just one distinct
803 * value among all tuples on the page).
804 */
805 if (key->sk_attno >= firstchangingattnum)
806 break; /* unsafe, multiple distinct attr values */
807
808 firstdatum = index_getattr(firsttup, key->sk_attno, tupdesc,
809 &firstnull);
810 if (key->sk_flags & SK_ISNULL)
811 {
812 /* IS NULL key */
813 Assert(key->sk_flags & SK_SEARCHNULL);
814
815 if (!firstnull)
816 break; /* unsafe */
817
818 /* Safe, IS NULL key satisfied by every tuple */
819 continue;
820 }
821 if (firstnull ||
823 key->sk_collation, firstdatum,
824 key->sk_argument)))
825 break; /* unsafe */
826
827 /* Safe, scalar = key satisfied by every tuple */
828 continue;
829 }
830
831 /* = array key (could be a SAOP array, could be a skip array) */
832 array = &so->arrayKeys[arrayidx++];
833 Assert(array->scan_key == startikey);
834 if (array->num_elems != -1)
835 {
836 /*
837 * SAOP array = key.
838 *
839 * Handle this like we handle scalar = keys (though binary search
840 * for a matching element, to avoid relying on key's sk_argument).
841 */
842 if (key->sk_attno >= firstchangingattnum)
843 break; /* unsafe, multiple distinct attr values */
844
845 firstdatum = index_getattr(firsttup, key->sk_attno, tupdesc,
846 &firstnull);
847 _bt_binsrch_array_skey(&so->orderProcs[startikey],
849 firstdatum, firstnull, array, key,
850 &result);
851 if (result != 0)
852 break; /* unsafe */
853
854 /* Safe, SAOP = key satisfied by every tuple */
855 start_past_saop_eq = true;
856 continue;
857 }
858
859 /*
860 * Skip array = key
861 */
862 Assert(key->sk_flags & SK_BT_SKIP);
863 if (array->null_elem)
864 {
865 /*
866 * Non-range skip array = key.
867 *
868 * Safe, non-range skip array "satisfied" by every tuple on page
869 * (safe even when "key->sk_attno > firstchangingattnum").
870 */
871 continue;
872 }
873
874 /*
875 * Range skip array = key.
876 *
877 * Handle this like we handle scalar inequality keys (but avoid using
878 * key's sk_argument directly, as in the SAOP array case).
879 */
880 if (key->sk_attno > firstchangingattnum) /* >, not >= */
881 break; /* unsafe, preceding attr has multiple
882 * distinct values */
883
884 firstdatum = index_getattr(firsttup, key->sk_attno, tupdesc, &firstnull);
885 lastdatum = index_getattr(lasttup, key->sk_attno, tupdesc, &lastnull);
886
887 /* Test firsttup */
889 firstdatum, firstnull, array, key,
890 &result);
891 if (result != 0)
892 break; /* unsafe */
893
894 /* Test lasttup */
896 lastdatum, lastnull, array, key,
897 &result);
898 if (result != 0)
899 break; /* unsafe */
900
901 /* Safe, range skip array satisfied by every tuple on page */
902 }
903
904 /*
905 * Use of forcenonrequired is typically undesirable, since it'll force
906 * _bt_readpage caller to read every tuple on the page -- even though, in
907 * general, it might well be possible to end the scan on an earlier tuple.
908 * However, caller must use forcenonrequired when start_past_saop_eq=true,
909 * since the usual required array behavior might fail to roll over to the
910 * SAOP array.
911 *
912 * We always prefer forcenonrequired=true during scans with skip arrays
913 * (except on the first page of each primitive index scan), though -- even
914 * when "startikey == 0". That way, _bt_advance_array_keys's low-order
915 * key precheck optimization can always be used (unless on the first page
916 * of the scan). It seems slightly preferable to check more tuples when
917 * that allows us to do significantly less skip array maintenance.
918 */
919 pstate->forcenonrequired = (start_past_saop_eq || so->skipScan);
920 pstate->startikey = startikey;
921
922 /*
923 * _bt_readpage caller is required to call _bt_checkkeys against page's
924 * finaltup with forcenonrequired=false whenever we initially set
925 * forcenonrequired=true. That way the scan's arrays will reliably track
926 * its progress through the index's key space.
927 *
928 * We don't expect this when _bt_readpage caller has no finaltup due to
929 * its page being the rightmost (or the leftmost, during backwards scans).
930 * When we see that _bt_readpage has no finaltup, back out of everything.
931 */
932 Assert(!pstate->forcenonrequired || so->numArrayKeys);
933 if (pstate->forcenonrequired && !pstate->finaltup)
934 {
935 pstate->forcenonrequired = false;
936 pstate->startikey = 0;
937 }
938}
int _bt_keep_natts_fast(Relation rel, IndexTuple lastleft, IndexTuple firstright)
Definition: nbtutils.c:917
@ NoMovementScanDirection
Definition: sdir.h:27

References _bt_binsrch_array_skey(), _bt_binsrch_skiparray_skey(), _bt_keep_natts_fast(), _bt_rowcompare_cmpresult(), BTScanOpaqueData::arrayKeys, Assert(), BTEqualStrategyNumber, BTPageGetOpaque, DatumGetBool(), DatumGetInt32(), DatumGetPointer(), BTReadPageState::finaltup, BTReadPageState::firstpage, BTReadPageState::forcenonrequired, ForwardScanDirection, FunctionCall2Coll(), index_getattr(), IndexScanDescData::indexRelation, INVERT_COMPARE_RESULT, sort-test::key, BTScanOpaqueData::keyData, BTReadPageState::maxoff, BTReadPageState::minoff, NoMovementScanDirection, BTArrayKeyInfo::null_elem, BTArrayKeyInfo::num_elems, BTScanOpaqueData::numArrayKeys, BTScanOpaqueData::numberOfKeys, IndexScanDescData::opaque, BTScanOpaqueData::orderProcs, P_LEFTMOST, P_RIGHTMOST, BTReadPageState::page, PageGetItem(), PageGetItemId(), RelationGetDescr, BTArrayKeyInfo::scan_key, BTScanOpaqueData::scanBehind, ScanKeyData::sk_argument, ScanKeyData::sk_attno, SK_BT_DESC, SK_BT_SKIP, ScanKeyData::sk_collation, ScanKeyData::sk_flags, ScanKeyData::sk_func, SK_ISNULL, SK_ROW_END, SK_ROW_HEADER, SK_SEARCHARRAY, SK_SEARCHNOTNULL, SK_SEARCHNULL, BTScanOpaqueData::skipScan, and BTReadPageState::startikey.

Referenced by _bt_readpage().

◆ _bt_setuppostingitems()

static int _bt_setuppostingitems ( BTScanOpaque  so,
int  itemIndex,
OffsetNumber  offnum,
const ItemPointerData heapTid,
IndexTuple  itup 
)
static

Definition at line 1062 of file nbtreadpage.c.

1064{
1065 BTScanPosItem *currItem = &so->currPos.items[itemIndex];
1066
1068
1069 currItem->heapTid = *heapTid;
1070 currItem->indexOffset = offnum;
1071 if (so->currTuples)
1072 {
1073 /* Save base IndexTuple (truncate posting list) */
1074 IndexTuple base;
1075 Size itupsz = BTreeTupleGetPostingOffset(itup);
1076
1077 itupsz = MAXALIGN(itupsz);
1078 currItem->tupleOffset = so->currPos.nextTupleOffset;
1079 base = (IndexTuple) (so->currTuples + so->currPos.nextTupleOffset);
1080 memcpy(base, itup, itupsz);
1081 /* Defensively reduce work area index tuple header size */
1082 base->t_info &= ~INDEX_SIZE_MASK;
1083 base->t_info |= itupsz;
1084 so->currPos.nextTupleOffset += itupsz;
1085
1086 return currItem->tupleOffset;
1087 }
1088
1089 return 0;
1090}
static uint32 BTreeTupleGetPostingOffset(IndexTuple posting)
Definition: nbtree.h:530
unsigned short t_info
Definition: itup.h:49

References Assert(), BTreeTupleGetPostingOffset(), BTreeTupleIsPosting(), BTScanOpaqueData::currPos, BTScanOpaqueData::currTuples, BTScanPosItem::heapTid, BTScanPosItem::indexOffset, BTScanPosData::items, MAXALIGN, BTScanPosData::nextTupleOffset, IndexTupleData::t_info, and BTScanPosItem::tupleOffset.

Referenced by _bt_readpage().

◆ _bt_skiparray_set_element()

static void _bt_skiparray_set_element ( Relation  rel,
ScanKey  skey,
BTArrayKeyInfo array,
int32  set_elem_result,
Datum  tupdatum,
bool  tupnull 
)
static

Definition at line 3273 of file nbtreadpage.c.

3275{
3276 Assert(skey->sk_flags & SK_BT_SKIP);
3278
3279 if (set_elem_result)
3280 {
3281 /* tupdatum/tupnull is out of the range of the skip array */
3282 Assert(!array->null_elem);
3283
3284 _bt_array_set_low_or_high(rel, skey, array, set_elem_result < 0);
3285 return;
3286 }
3287
3288 /* Advance skip array to tupdatum (or tupnull) value */
3289 if (unlikely(tupnull))
3290 {
3291 _bt_skiparray_set_isnull(rel, skey, array);
3292 return;
3293 }
3294
3295 /* Free memory previously allocated for sk_argument if needed */
3296 if (!array->attbyval && skey->sk_argument)
3298
3299 /* tupdatum becomes new sk_argument/new current element */
3300 skey->sk_flags &= ~(SK_SEARCHNULL | SK_ISNULL |
3303 skey->sk_argument = datumCopy(tupdatum, array->attbyval, array->attlen);
3304}

References _bt_array_set_low_or_high(), _bt_skiparray_set_isnull(), Assert(), BTArrayKeyInfo::attbyval, BTArrayKeyInfo::attlen, datumCopy(), DatumGetPointer(), BTArrayKeyInfo::null_elem, pfree(), ScanKeyData::sk_argument, SK_BT_MAXVAL, SK_BT_MINVAL, SK_BT_NEXT, SK_BT_PRIOR, SK_BT_SKIP, ScanKeyData::sk_flags, SK_ISNULL, SK_SEARCHARRAY, SK_SEARCHNULL, and unlikely.

Referenced by _bt_advance_array_keys().

◆ _bt_skiparray_set_isnull()

static void _bt_skiparray_set_isnull ( Relation  rel,
ScanKey  skey,
BTArrayKeyInfo array 
)
static

Definition at line 3310 of file nbtreadpage.c.

3311{
3312 Assert(skey->sk_flags & SK_BT_SKIP);
3314 Assert(array->null_elem && !array->low_compare && !array->high_compare);
3315
3316 /* Free memory previously allocated for sk_argument if needed */
3317 if (!array->attbyval && skey->sk_argument)
3319
3320 /* NULL becomes new sk_argument/new current element */
3321 skey->sk_argument = (Datum) 0;
3322 skey->sk_flags &= ~(SK_BT_MINVAL | SK_BT_MAXVAL |
3324 skey->sk_flags |= (SK_SEARCHNULL | SK_ISNULL);
3325}

References Assert(), BTArrayKeyInfo::attbyval, DatumGetPointer(), BTArrayKeyInfo::high_compare, BTArrayKeyInfo::low_compare, BTArrayKeyInfo::null_elem, pfree(), ScanKeyData::sk_argument, SK_BT_MAXVAL, SK_BT_MINVAL, SK_BT_NEXT, SK_BT_PRIOR, SK_BT_SKIP, ScanKeyData::sk_flags, SK_ISNULL, SK_SEARCHARRAY, and SK_SEARCHNULL.

Referenced by _bt_array_decrement(), _bt_array_increment(), and _bt_skiparray_set_element().

◆ _bt_start_array_keys()

◆ _bt_tuple_before_array_skeys()

static bool _bt_tuple_before_array_skeys ( IndexScanDesc  scan,
ScanDirection  dir,
IndexTuple  tuple,
TupleDesc  tupdesc,
int  tupnatts,
bool  readpagetup,
int  sktrig,
bool *  scanBehind 
)
static

Definition at line 1854 of file nbtreadpage.c.

1857{
1858 BTScanOpaque so = (BTScanOpaque) scan->opaque;
1859
1860 Assert(so->numArrayKeys);
1861 Assert(so->numberOfKeys);
1862 Assert(sktrig == 0 || readpagetup);
1863 Assert(!readpagetup || scanBehind == NULL);
1864
1865 if (scanBehind)
1866 *scanBehind = false;
1867
1868 for (int ikey = sktrig; ikey < so->numberOfKeys; ikey++)
1869 {
1870 ScanKey cur = so->keyData + ikey;
1871 Datum tupdatum;
1872 bool tupnull;
1873 int32 result;
1874
1875 /* readpagetup calls require one ORDER proc comparison (at most) */
1876 Assert(!readpagetup || ikey == sktrig);
1877
1878 /*
1879 * Once we reach a non-required scan key, we're completely done.
1880 *
1881 * Note: we deliberately don't consider the scan direction here.
1882 * _bt_advance_array_keys caller requires that we track *scanBehind
1883 * without concern for scan direction.
1884 */
1885 if ((cur->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)) == 0)
1886 {
1887 Assert(!readpagetup);
1888 Assert(ikey > sktrig || ikey == 0);
1889 return false;
1890 }
1891
1892 if (cur->sk_attno > tupnatts)
1893 {
1894 Assert(!readpagetup);
1895
1896 /*
1897 * When we reach a high key's truncated attribute, assume that the
1898 * tuple attribute's value is >= the scan's equality constraint
1899 * scan keys (but set *scanBehind to let interested callers know
1900 * that a truncated attribute might have affected our answer).
1901 */
1902 if (scanBehind)
1903 *scanBehind = true;
1904
1905 return false;
1906 }
1907
1908 /*
1909 * Deal with inequality strategy scan keys that _bt_check_compare set
1910 * continuescan=false for
1911 */
1912 if (cur->sk_strategy != BTEqualStrategyNumber)
1913 {
1914 /*
1915 * When _bt_check_compare indicated that a required inequality
1916 * scan key wasn't satisfied, there's no need to verify anything;
1917 * caller always calls _bt_advance_array_keys with this sktrig.
1918 */
1919 if (readpagetup)
1920 return false;
1921
1922 /*
1923 * Otherwise we can't give up, since we must check all required
1924 * scan keys (required in either direction) in order to correctly
1925 * track *scanBehind for caller
1926 */
1927 continue;
1928 }
1929
1930 tupdatum = index_getattr(tuple, cur->sk_attno, tupdesc, &tupnull);
1931
1932 if (likely(!(cur->sk_flags & (SK_BT_MINVAL | SK_BT_MAXVAL))))
1933 {
1934 /* Scankey has a valid/comparable sk_argument value */
1935 result = _bt_compare_array_skey(&so->orderProcs[ikey],
1936 tupdatum, tupnull,
1937 cur->sk_argument, cur);
1938
1939 if (result == 0)
1940 {
1941 /*
1942 * Interpret result in a way that takes NEXT/PRIOR into
1943 * account
1944 */
1945 if (cur->sk_flags & SK_BT_NEXT)
1946 result = -1;
1947 else if (cur->sk_flags & SK_BT_PRIOR)
1948 result = 1;
1949
1950 Assert(result == 0 || (cur->sk_flags & SK_BT_SKIP));
1951 }
1952 }
1953 else
1954 {
1955 BTArrayKeyInfo *array = NULL;
1956
1957 /*
1958 * Current array element/array = scan key value is a sentinel
1959 * value that represents the lowest (or highest) possible value
1960 * that's still within the range of the array.
1961 *
1962 * Like _bt_first, we only see MINVAL keys during forwards scans
1963 * (and similarly only see MAXVAL keys during backwards scans).
1964 * Even if the scan's direction changes, we'll stop at some higher
1965 * order key before we can ever reach any MAXVAL (or MINVAL) keys.
1966 * (However, unlike _bt_first we _can_ get to keys marked either
1967 * NEXT or PRIOR, regardless of the scan's current direction.)
1968 */
1970 !(cur->sk_flags & SK_BT_MAXVAL) :
1971 !(cur->sk_flags & SK_BT_MINVAL));
1972
1973 /*
1974 * There are no valid sk_argument values in MINVAL/MAXVAL keys.
1975 * Check if tupdatum is within the range of skip array instead.
1976 */
1977 for (int arrayidx = 0; arrayidx < so->numArrayKeys; arrayidx++)
1978 {
1979 array = &so->arrayKeys[arrayidx];
1980 if (array->scan_key == ikey)
1981 break;
1982 }
1983
1984 _bt_binsrch_skiparray_skey(false, dir, tupdatum, tupnull,
1985 array, cur, &result);
1986
1987 if (result == 0)
1988 {
1989 /*
1990 * tupdatum satisfies both low_compare and high_compare, so
1991 * it's time to advance the array keys.
1992 *
1993 * Note: It's possible that the skip array will "advance" from
1994 * its MINVAL (or MAXVAL) representation to an alternative,
1995 * logically equivalent representation of the same value: a
1996 * representation where the = key gets a valid datum in its
1997 * sk_argument. This is only possible when low_compare uses
1998 * the >= strategy (or high_compare uses the <= strategy).
1999 */
2000 return false;
2001 }
2002 }
2003
2004 /*
2005 * Does this comparison indicate that caller must _not_ advance the
2006 * scan's arrays just yet?
2007 */
2008 if ((ScanDirectionIsForward(dir) && result < 0) ||
2009 (ScanDirectionIsBackward(dir) && result > 0))
2010 return true;
2011
2012 /*
2013 * Does this comparison indicate that caller should now advance the
2014 * scan's arrays? (Must be if we get here during a readpagetup call.)
2015 */
2016 if (readpagetup || result != 0)
2017 {
2018 Assert(result != 0);
2019 return false;
2020 }
2021
2022 /*
2023 * Inconclusive -- need to check later scan keys, too.
2024 *
2025 * This must be a finaltup precheck, or a call made from an assertion.
2026 */
2027 Assert(result == 0);
2028 }
2029
2030 Assert(!readpagetup);
2031
2032 return false;
2033}
#define likely(x)
Definition: c.h:417

References _bt_binsrch_skiparray_skey(), _bt_compare_array_skey(), BTScanOpaqueData::arrayKeys, Assert(), BTEqualStrategyNumber, cur, index_getattr(), BTScanOpaqueData::keyData, likely, BTScanOpaqueData::numArrayKeys, BTScanOpaqueData::numberOfKeys, IndexScanDescData::opaque, BTScanOpaqueData::orderProcs, BTArrayKeyInfo::scan_key, ScanDirectionIsBackward, ScanDirectionIsForward, SK_BT_MAXVAL, SK_BT_MINVAL, SK_BT_NEXT, SK_BT_PRIOR, SK_BT_REQBKWD, SK_BT_REQFWD, and SK_BT_SKIP.

Referenced by _bt_advance_array_keys(), _bt_checkkeys(), _bt_checkkeys_look_ahead(), and _bt_scanbehind_checkkeys().