/src/postgres/src/backend/access/nbtree/nbtsearch.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * nbtsearch.c |
4 | | * Search code for postgres btrees. |
5 | | * |
6 | | * |
7 | | * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group |
8 | | * Portions Copyright (c) 1994, Regents of the University of California |
9 | | * |
10 | | * IDENTIFICATION |
11 | | * src/backend/access/nbtree/nbtsearch.c |
12 | | * |
13 | | *------------------------------------------------------------------------- |
14 | | */ |
15 | | |
16 | | #include "postgres.h" |
17 | | |
18 | | #include "access/nbtree.h" |
19 | | #include "access/relscan.h" |
20 | | #include "access/xact.h" |
21 | | #include "catalog/catalog.h" |
22 | | #include "executor/instrument_node.h" |
23 | | #include "miscadmin.h" |
24 | | #include "pgstat.h" |
25 | | #include "storage/predicate.h" |
26 | | #include "utils/injection_point.h" |
27 | | #include "utils/lsyscache.h" |
28 | | #include "utils/rel.h" |
29 | | |
30 | | |
31 | | static inline void _bt_drop_lock_and_maybe_pin(Relation rel, BTScanOpaque so); |
32 | | static Buffer _bt_moveright(Relation rel, Relation heaprel, BTScanInsert key, |
33 | | Buffer buf, bool forupdate, BTStack stack, |
34 | | int access); |
35 | | static OffsetNumber _bt_binsrch(Relation rel, BTScanInsert key, Buffer buf); |
36 | | static int _bt_binsrch_posting(BTScanInsert key, Page page, |
37 | | OffsetNumber offnum); |
38 | | static inline void _bt_returnitem(IndexScanDesc scan, BTScanOpaque so); |
39 | | static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir); |
40 | | static bool _bt_readfirstpage(IndexScanDesc scan, OffsetNumber offnum, |
41 | | ScanDirection dir); |
42 | | static bool _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, |
43 | | BlockNumber lastcurrblkno, ScanDirection dir, |
44 | | bool seized); |
45 | | static Buffer _bt_lock_and_validate_left(Relation rel, BlockNumber *blkno, |
46 | | BlockNumber lastcurrblkno); |
47 | | static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir); |
48 | | |
49 | | |
50 | | /* |
51 | | * _bt_drop_lock_and_maybe_pin() |
52 | | * |
53 | | * Unlock so->currPos.buf. If scan is so->dropPin, drop the pin, too. |
54 | | * Dropping the pin prevents VACUUM from blocking on acquiring a cleanup lock. |
55 | | */ |
56 | | static inline void |
57 | | _bt_drop_lock_and_maybe_pin(Relation rel, BTScanOpaque so) |
58 | 0 | { |
59 | 0 | if (!so->dropPin) |
60 | 0 | { |
61 | | /* Just drop the lock (not the pin) */ |
62 | 0 | _bt_unlockbuf(rel, so->currPos.buf); |
63 | 0 | return; |
64 | 0 | } |
65 | | |
66 | | /* |
67 | | * Drop both the lock and the pin. |
68 | | * |
69 | | * Have to set so->currPos.lsn so that _bt_killitems has a way to detect |
70 | | * when concurrent heap TID recycling by VACUUM might have taken place. |
71 | | */ |
72 | 0 | so->currPos.lsn = BufferGetLSNAtomic(so->currPos.buf); |
73 | 0 | _bt_relbuf(rel, so->currPos.buf); |
74 | 0 | so->currPos.buf = InvalidBuffer; |
75 | 0 | } |
76 | | |
77 | | /* |
78 | | * _bt_search() -- Search the tree for a particular scankey, |
79 | | * or more precisely for the first leaf page it could be on. |
80 | | * |
81 | | * The passed scankey is an insertion-type scankey (see nbtree/README), |
82 | | * but it can omit the rightmost column(s) of the index. |
83 | | * |
84 | | * If returnstack is true, return value is a stack of parent-page pointers |
85 | | * (i.e. there is no entry for the leaf level/page). If returnstack is false, |
86 | | * we just return NULL. This scheme allows callers that don't need a descent |
87 | | * stack to avoid palloc churn. |
88 | | * |
89 | | * When we return, *bufP is set to the address of the leaf-page buffer, which |
90 | | * is locked and pinned. No locks are held on the parent pages, however! |
91 | | * |
92 | | * The returned buffer is locked according to access parameter. Additionally, |
93 | | * access = BT_WRITE will allow an empty root page to be created and returned. |
94 | | * When access = BT_READ, an empty index will result in *bufP being set to |
95 | | * InvalidBuffer. Also, in BT_WRITE mode, any incomplete splits encountered |
96 | | * during the search will be finished. |
97 | | * |
98 | | * heaprel must be provided by callers that pass access = BT_WRITE, since we |
99 | | * might need to allocate a new root page for caller -- see _bt_allocbuf. |
100 | | */ |
101 | | BTStack |
102 | | _bt_search(Relation rel, Relation heaprel, BTScanInsert key, Buffer *bufP, |
103 | | int access, bool returnstack) |
104 | 0 | { |
105 | 0 | BTStack stack_in = NULL; |
106 | 0 | int page_access = BT_READ; |
107 | | |
108 | | /* heaprel must be set whenever _bt_allocbuf is reachable */ |
109 | 0 | Assert(access == BT_READ || access == BT_WRITE); |
110 | 0 | Assert(access == BT_READ || heaprel != NULL); |
111 | | |
112 | | /* Get the root page to start with */ |
113 | 0 | *bufP = _bt_getroot(rel, heaprel, access); |
114 | | |
115 | | /* If index is empty and access = BT_READ, no root page is created. */ |
116 | 0 | if (!BufferIsValid(*bufP)) |
117 | 0 | return (BTStack) NULL; |
118 | | |
119 | | /* Loop iterates once per level descended in the tree */ |
120 | 0 | for (;;) |
121 | 0 | { |
122 | 0 | Page page; |
123 | 0 | BTPageOpaque opaque; |
124 | 0 | OffsetNumber offnum; |
125 | 0 | ItemId itemid; |
126 | 0 | IndexTuple itup; |
127 | 0 | BlockNumber child; |
128 | 0 | BTStack new_stack; |
129 | | |
130 | | /* |
131 | | * Race -- the page we just grabbed may have split since we read its |
132 | | * downlink in its parent page (or the metapage). If it has, we may |
133 | | * need to move right to its new sibling. Do that. |
134 | | * |
135 | | * In write-mode, allow _bt_moveright to finish any incomplete splits |
136 | | * along the way. Strictly speaking, we'd only need to finish an |
137 | | * incomplete split on the leaf page we're about to insert to, not on |
138 | | * any of the upper levels (internal pages with incomplete splits are |
139 | | * also taken care of in _bt_getstackbuf). But this is a good |
140 | | * opportunity to finish splits of internal pages too. |
141 | | */ |
142 | 0 | *bufP = _bt_moveright(rel, heaprel, key, *bufP, (access == BT_WRITE), |
143 | 0 | stack_in, page_access); |
144 | | |
145 | | /* if this is a leaf page, we're done */ |
146 | 0 | page = BufferGetPage(*bufP); |
147 | 0 | opaque = BTPageGetOpaque(page); |
148 | 0 | if (P_ISLEAF(opaque)) |
149 | 0 | break; |
150 | | |
151 | | /* |
152 | | * Find the appropriate pivot tuple on this page. Its downlink points |
153 | | * to the child page that we're about to descend to. |
154 | | */ |
155 | 0 | offnum = _bt_binsrch(rel, key, *bufP); |
156 | 0 | itemid = PageGetItemId(page, offnum); |
157 | 0 | itup = (IndexTuple) PageGetItem(page, itemid); |
158 | 0 | Assert(BTreeTupleIsPivot(itup) || !key->heapkeyspace); |
159 | 0 | child = BTreeTupleGetDownLink(itup); |
160 | | |
161 | | /* |
162 | | * We need to save the location of the pivot tuple we chose in a new |
163 | | * stack entry for this page/level. If caller ends up splitting a |
164 | | * page one level down, it usually ends up inserting a new pivot |
165 | | * tuple/downlink immediately after the location recorded here. |
166 | | */ |
167 | 0 | if (returnstack) |
168 | 0 | { |
169 | 0 | new_stack = (BTStack) palloc_object(BTStackData); |
170 | 0 | new_stack->bts_blkno = BufferGetBlockNumber(*bufP); |
171 | 0 | new_stack->bts_offset = offnum; |
172 | 0 | new_stack->bts_parent = stack_in; |
173 | 0 | stack_in = new_stack; |
174 | 0 | } |
175 | | |
176 | | /* |
177 | | * Page level 1 is lowest non-leaf page level prior to leaves. So, if |
178 | | * we're on the level 1 and asked to lock leaf page in write mode, |
179 | | * then lock next page in write mode, because it must be a leaf. |
180 | | */ |
181 | 0 | if (opaque->btpo_level == 1 && access == BT_WRITE) |
182 | 0 | page_access = BT_WRITE; |
183 | | |
184 | | /* drop the read lock on the page, then acquire one on its child */ |
185 | 0 | *bufP = _bt_relandgetbuf(rel, *bufP, child, page_access); |
186 | | |
187 | | /* okay, all set to move down a level */ |
188 | 0 | } |
189 | | |
190 | | /* |
191 | | * If we're asked to lock leaf in write mode, but didn't manage to, then |
192 | | * relock. This should only happen when the root page is a leaf page (and |
193 | | * the only page in the index other than the metapage). |
194 | | */ |
195 | 0 | if (access == BT_WRITE && page_access == BT_READ) |
196 | 0 | { |
197 | | /* trade in our read lock for a write lock */ |
198 | 0 | _bt_unlockbuf(rel, *bufP); |
199 | 0 | _bt_lockbuf(rel, *bufP, BT_WRITE); |
200 | | |
201 | | /* |
202 | | * Race -- the leaf page may have split after we dropped the read lock |
203 | | * but before we acquired a write lock. If it has, we may need to |
204 | | * move right to its new sibling. Do that. |
205 | | */ |
206 | 0 | *bufP = _bt_moveright(rel, heaprel, key, *bufP, true, stack_in, BT_WRITE); |
207 | 0 | } |
208 | |
|
209 | 0 | return stack_in; |
210 | 0 | } |
211 | | |
212 | | /* |
213 | | * _bt_moveright() -- move right in the btree if necessary. |
214 | | * |
215 | | * When we follow a pointer to reach a page, it is possible that |
216 | | * the page has changed in the meanwhile. If this happens, we're |
217 | | * guaranteed that the page has "split right" -- that is, that any |
218 | | * data that appeared on the page originally is either on the page |
219 | | * or strictly to the right of it. |
220 | | * |
221 | | * This routine decides whether or not we need to move right in the |
222 | | * tree by examining the high key entry on the page. If that entry is |
223 | | * strictly less than the scankey, or <= the scankey in the |
224 | | * key.nextkey=true case, then we followed the wrong link and we need |
225 | | * to move right. |
226 | | * |
227 | | * The passed insertion-type scankey can omit the rightmost column(s) of the |
228 | | * index. (see nbtree/README) |
229 | | * |
230 | | * When key.nextkey is false (the usual case), we are looking for the first |
231 | | * item >= key. When key.nextkey is true, we are looking for the first item |
232 | | * strictly greater than key. |
233 | | * |
234 | | * If forupdate is true, we will attempt to finish any incomplete splits |
235 | | * that we encounter. This is required when locking a target page for an |
236 | | * insertion, because we don't allow inserting on a page before the split is |
237 | | * completed. 'heaprel' and 'stack' are only used if forupdate is true. |
238 | | * |
239 | | * On entry, we have the buffer pinned and a lock of the type specified by |
240 | | * 'access'. If we move right, we release the buffer and lock and acquire |
241 | | * the same on the right sibling. Return value is the buffer we stop at. |
242 | | */ |
243 | | static Buffer |
244 | | _bt_moveright(Relation rel, |
245 | | Relation heaprel, |
246 | | BTScanInsert key, |
247 | | Buffer buf, |
248 | | bool forupdate, |
249 | | BTStack stack, |
250 | | int access) |
251 | 0 | { |
252 | 0 | Page page; |
253 | 0 | BTPageOpaque opaque; |
254 | 0 | int32 cmpval; |
255 | |
|
256 | 0 | Assert(!forupdate || heaprel != NULL); |
257 | | |
258 | | /* |
259 | | * When nextkey = false (normal case): if the scan key that brought us to |
260 | | * this page is > the high key stored on the page, then the page has split |
261 | | * and we need to move right. (pg_upgrade'd !heapkeyspace indexes could |
262 | | * have some duplicates to the right as well as the left, but that's |
263 | | * something that's only ever dealt with on the leaf level, after |
264 | | * _bt_search has found an initial leaf page.) |
265 | | * |
266 | | * When nextkey = true: move right if the scan key is >= page's high key. |
267 | | * (Note that key.scantid cannot be set in this case.) |
268 | | * |
269 | | * The page could even have split more than once, so scan as far as |
270 | | * needed. |
271 | | * |
272 | | * We also have to move right if we followed a link that brought us to a |
273 | | * dead page. |
274 | | */ |
275 | 0 | cmpval = key->nextkey ? 0 : 1; |
276 | |
|
277 | 0 | for (;;) |
278 | 0 | { |
279 | 0 | page = BufferGetPage(buf); |
280 | 0 | opaque = BTPageGetOpaque(page); |
281 | |
|
282 | 0 | if (P_RIGHTMOST(opaque)) |
283 | 0 | break; |
284 | | |
285 | | /* |
286 | | * Finish any incomplete splits we encounter along the way. |
287 | | */ |
288 | 0 | if (forupdate && P_INCOMPLETE_SPLIT(opaque)) |
289 | 0 | { |
290 | 0 | BlockNumber blkno = BufferGetBlockNumber(buf); |
291 | | |
292 | | /* upgrade our lock if necessary */ |
293 | 0 | if (access == BT_READ) |
294 | 0 | { |
295 | 0 | _bt_unlockbuf(rel, buf); |
296 | 0 | _bt_lockbuf(rel, buf, BT_WRITE); |
297 | 0 | } |
298 | |
|
299 | 0 | if (P_INCOMPLETE_SPLIT(opaque)) |
300 | 0 | _bt_finish_split(rel, heaprel, buf, stack); |
301 | 0 | else |
302 | 0 | _bt_relbuf(rel, buf); |
303 | | |
304 | | /* re-acquire the lock in the right mode, and re-check */ |
305 | 0 | buf = _bt_getbuf(rel, blkno, access); |
306 | 0 | continue; |
307 | 0 | } |
308 | | |
309 | 0 | if (P_IGNORE(opaque) || _bt_compare(rel, key, page, P_HIKEY) >= cmpval) |
310 | 0 | { |
311 | | /* step right one page */ |
312 | 0 | buf = _bt_relandgetbuf(rel, buf, opaque->btpo_next, access); |
313 | 0 | continue; |
314 | 0 | } |
315 | 0 | else |
316 | 0 | break; |
317 | 0 | } |
318 | |
|
319 | 0 | if (P_IGNORE(opaque)) |
320 | 0 | elog(ERROR, "fell off the end of index \"%s\"", |
321 | 0 | RelationGetRelationName(rel)); |
322 | | |
323 | 0 | return buf; |
324 | 0 | } |
325 | | |
326 | | /* |
327 | | * _bt_binsrch() -- Do a binary search for a key on a particular page. |
328 | | * |
329 | | * On an internal (non-leaf) page, _bt_binsrch() returns the OffsetNumber |
330 | | * of the last key < given scankey, or last key <= given scankey if nextkey |
331 | | * is true. (Since _bt_compare treats the first data key of such a page as |
332 | | * minus infinity, there will be at least one key < scankey, so the result |
333 | | * always points at one of the keys on the page.) |
334 | | * |
335 | | * On a leaf page, _bt_binsrch() returns the final result of the initial |
336 | | * positioning process that started with _bt_first's call to _bt_search. |
337 | | * We're returning a non-pivot tuple offset, so things are a little different. |
338 | | * It is possible that we'll return an offset that's either past the last |
339 | | * non-pivot slot, or (in the case of a backward scan) before the first slot. |
340 | | * |
341 | | * This procedure is not responsible for walking right, it just examines |
342 | | * the given page. _bt_binsrch() has no lock or refcount side effects |
343 | | * on the buffer. |
344 | | */ |
345 | | static OffsetNumber |
346 | | _bt_binsrch(Relation rel, |
347 | | BTScanInsert key, |
348 | | Buffer buf) |
349 | 0 | { |
350 | 0 | Page page; |
351 | 0 | BTPageOpaque opaque; |
352 | 0 | OffsetNumber low, |
353 | 0 | high; |
354 | 0 | int32 result, |
355 | 0 | cmpval; |
356 | |
|
357 | 0 | page = BufferGetPage(buf); |
358 | 0 | opaque = BTPageGetOpaque(page); |
359 | | |
360 | | /* Requesting nextkey semantics while using scantid seems nonsensical */ |
361 | 0 | Assert(!key->nextkey || key->scantid == NULL); |
362 | | /* scantid-set callers must use _bt_binsrch_insert() on leaf pages */ |
363 | 0 | Assert(!P_ISLEAF(opaque) || key->scantid == NULL); |
364 | |
|
365 | 0 | low = P_FIRSTDATAKEY(opaque); |
366 | 0 | high = PageGetMaxOffsetNumber(page); |
367 | | |
368 | | /* |
369 | | * If there are no keys on the page, return the first available slot. Note |
370 | | * this covers two cases: the page is really empty (no keys), or it |
371 | | * contains only a high key. The latter case is possible after vacuuming. |
372 | | * This can never happen on an internal page, however, since they are |
373 | | * never empty (an internal page must have at least one child). |
374 | | */ |
375 | 0 | if (unlikely(high < low)) |
376 | 0 | return low; |
377 | | |
378 | | /* |
379 | | * Binary search to find the first key on the page >= scan key, or first |
380 | | * key > scankey when nextkey is true. |
381 | | * |
382 | | * For nextkey=false (cmpval=1), the loop invariant is: all slots before |
383 | | * 'low' are < scan key, all slots at or after 'high' are >= scan key. |
384 | | * |
385 | | * For nextkey=true (cmpval=0), the loop invariant is: all slots before |
386 | | * 'low' are <= scan key, all slots at or after 'high' are > scan key. |
387 | | * |
388 | | * We can fall out when high == low. |
389 | | */ |
390 | 0 | high++; /* establish the loop invariant for high */ |
391 | |
|
392 | 0 | cmpval = key->nextkey ? 0 : 1; /* select comparison value */ |
393 | |
|
394 | 0 | while (high > low) |
395 | 0 | { |
396 | 0 | OffsetNumber mid = low + ((high - low) / 2); |
397 | | |
398 | | /* We have low <= mid < high, so mid points at a real slot */ |
399 | |
|
400 | 0 | result = _bt_compare(rel, key, page, mid); |
401 | |
|
402 | 0 | if (result >= cmpval) |
403 | 0 | low = mid + 1; |
404 | 0 | else |
405 | 0 | high = mid; |
406 | 0 | } |
407 | | |
408 | | /* |
409 | | * At this point we have high == low. |
410 | | * |
411 | | * On a leaf page we always return the first non-pivot tuple >= scan key |
412 | | * (resp. > scan key) for forward scan callers. For backward scans, it's |
413 | | * always the _last_ non-pivot tuple < scan key (resp. <= scan key). |
414 | | */ |
415 | 0 | if (P_ISLEAF(opaque)) |
416 | 0 | { |
417 | | /* |
418 | | * In the backward scan case we're supposed to locate the last |
419 | | * matching tuple on the leaf level -- not the first matching tuple |
420 | | * (the last tuple will be the first one returned by the scan). |
421 | | * |
422 | | * At this point we've located the first non-pivot tuple immediately |
423 | | * after the last matching tuple (which might just be maxoff + 1). |
424 | | * Compensate by stepping back. |
425 | | */ |
426 | 0 | if (key->backward) |
427 | 0 | return OffsetNumberPrev(low); |
428 | | |
429 | 0 | return low; |
430 | 0 | } |
431 | | |
432 | | /* |
433 | | * On a non-leaf page, return the last key < scan key (resp. <= scan key). |
434 | | * There must be one if _bt_compare() is playing by the rules. |
435 | | * |
436 | | * _bt_compare() will seldom see any exactly-matching pivot tuples, since |
437 | | * a truncated -inf heap TID is usually enough to prevent it altogether. |
438 | | * Even omitted scan key entries are treated as > truncated attributes. |
439 | | * |
440 | | * However, during backward scans _bt_compare() interprets omitted scan |
441 | | * key attributes as == corresponding truncated -inf attributes instead. |
442 | | * This works just like < would work here. Under this scheme, < strategy |
443 | | * backward scans will always directly descend to the correct leaf page. |
444 | | * In particular, they will never incur an "extra" leaf page access with a |
445 | | * scan key that happens to contain the same prefix of values as some |
446 | | * pivot tuple's untruncated prefix. VACUUM relies on this guarantee when |
447 | | * it uses a leaf page high key to "re-find" a page undergoing deletion. |
448 | | */ |
449 | 0 | Assert(low > P_FIRSTDATAKEY(opaque)); |
450 | |
|
451 | 0 | return OffsetNumberPrev(low); |
452 | 0 | } |
453 | | |
454 | | /* |
455 | | * |
456 | | * _bt_binsrch_insert() -- Cacheable, incremental leaf page binary search. |
457 | | * |
458 | | * Like _bt_binsrch(), but with support for caching the binary search |
459 | | * bounds. Only used during insertion, and only on the leaf page that it |
460 | | * looks like caller will insert tuple on. Exclusive-locked and pinned |
461 | | * leaf page is contained within insertstate. |
462 | | * |
463 | | * Caches the bounds fields in insertstate so that a subsequent call can |
464 | | * reuse the low and strict high bounds of original binary search. Callers |
465 | | * that use these fields directly must be prepared for the case where low |
466 | | * and/or stricthigh are not on the same page (one or both exceed maxoff |
467 | | * for the page). The case where there are no items on the page (high < |
468 | | * low) makes bounds invalid. |
469 | | * |
470 | | * Caller is responsible for invalidating bounds when it modifies the page |
471 | | * before calling here a second time, and for dealing with posting list |
472 | | * tuple matches (callers can use insertstate's postingoff field to |
473 | | * determine which existing heap TID will need to be replaced by a posting |
474 | | * list split). |
475 | | */ |
476 | | OffsetNumber |
477 | | _bt_binsrch_insert(Relation rel, BTInsertState insertstate) |
478 | 0 | { |
479 | 0 | BTScanInsert key = insertstate->itup_key; |
480 | 0 | Page page; |
481 | 0 | BTPageOpaque opaque; |
482 | 0 | OffsetNumber low, |
483 | 0 | high, |
484 | 0 | stricthigh; |
485 | 0 | int32 result, |
486 | 0 | cmpval; |
487 | |
|
488 | 0 | page = BufferGetPage(insertstate->buf); |
489 | 0 | opaque = BTPageGetOpaque(page); |
490 | |
|
491 | 0 | Assert(P_ISLEAF(opaque)); |
492 | 0 | Assert(!key->nextkey); |
493 | 0 | Assert(insertstate->postingoff == 0); |
494 | |
|
495 | 0 | if (!insertstate->bounds_valid) |
496 | 0 | { |
497 | | /* Start new binary search */ |
498 | 0 | low = P_FIRSTDATAKEY(opaque); |
499 | 0 | high = PageGetMaxOffsetNumber(page); |
500 | 0 | } |
501 | 0 | else |
502 | 0 | { |
503 | | /* Restore result of previous binary search against same page */ |
504 | 0 | low = insertstate->low; |
505 | 0 | high = insertstate->stricthigh; |
506 | 0 | } |
507 | | |
508 | | /* If there are no keys on the page, return the first available slot */ |
509 | 0 | if (unlikely(high < low)) |
510 | 0 | { |
511 | | /* Caller can't reuse bounds */ |
512 | 0 | insertstate->low = InvalidOffsetNumber; |
513 | 0 | insertstate->stricthigh = InvalidOffsetNumber; |
514 | 0 | insertstate->bounds_valid = false; |
515 | 0 | return low; |
516 | 0 | } |
517 | | |
518 | | /* |
519 | | * Binary search to find the first key on the page >= scan key. (nextkey |
520 | | * is always false when inserting). |
521 | | * |
522 | | * The loop invariant is: all slots before 'low' are < scan key, all slots |
523 | | * at or after 'high' are >= scan key. 'stricthigh' is > scan key, and is |
524 | | * maintained to save additional search effort for caller. |
525 | | * |
526 | | * We can fall out when high == low. |
527 | | */ |
528 | 0 | if (!insertstate->bounds_valid) |
529 | 0 | high++; /* establish the loop invariant for high */ |
530 | 0 | stricthigh = high; /* high initially strictly higher */ |
531 | |
|
532 | 0 | cmpval = 1; /* !nextkey comparison value */ |
533 | |
|
534 | 0 | while (high > low) |
535 | 0 | { |
536 | 0 | OffsetNumber mid = low + ((high - low) / 2); |
537 | | |
538 | | /* We have low <= mid < high, so mid points at a real slot */ |
539 | |
|
540 | 0 | result = _bt_compare(rel, key, page, mid); |
541 | |
|
542 | 0 | if (result >= cmpval) |
543 | 0 | low = mid + 1; |
544 | 0 | else |
545 | 0 | { |
546 | 0 | high = mid; |
547 | 0 | if (result != 0) |
548 | 0 | stricthigh = high; |
549 | 0 | } |
550 | | |
551 | | /* |
552 | | * If tuple at offset located by binary search is a posting list whose |
553 | | * TID range overlaps with caller's scantid, perform posting list |
554 | | * binary search to set postingoff for caller. Caller must split the |
555 | | * posting list when postingoff is set. This should happen |
556 | | * infrequently. |
557 | | */ |
558 | 0 | if (unlikely(result == 0 && key->scantid != NULL)) |
559 | 0 | { |
560 | | /* |
561 | | * postingoff should never be set more than once per leaf page |
562 | | * binary search. That would mean that there are duplicate table |
563 | | * TIDs in the index, which is never okay. Check for that here. |
564 | | */ |
565 | 0 | if (insertstate->postingoff != 0) |
566 | 0 | ereport(ERROR, |
567 | 0 | (errcode(ERRCODE_INDEX_CORRUPTED), |
568 | 0 | errmsg_internal("table tid from new index tuple (%u,%u) cannot find insert offset between offsets %u and %u of block %u in index \"%s\"", |
569 | 0 | ItemPointerGetBlockNumber(key->scantid), |
570 | 0 | ItemPointerGetOffsetNumber(key->scantid), |
571 | 0 | low, stricthigh, |
572 | 0 | BufferGetBlockNumber(insertstate->buf), |
573 | 0 | RelationGetRelationName(rel)))); |
574 | | |
575 | 0 | insertstate->postingoff = _bt_binsrch_posting(key, page, mid); |
576 | 0 | } |
577 | 0 | } |
578 | | |
579 | | /* |
580 | | * On a leaf page, a binary search always returns the first key >= scan |
581 | | * key (at least in !nextkey case), which could be the last slot + 1. This |
582 | | * is also the lower bound of cached search. |
583 | | * |
584 | | * stricthigh may also be the last slot + 1, which prevents caller from |
585 | | * using bounds directly, but is still useful to us if we're called a |
586 | | * second time with cached bounds (cached low will be < stricthigh when |
587 | | * that happens). |
588 | | */ |
589 | 0 | insertstate->low = low; |
590 | 0 | insertstate->stricthigh = stricthigh; |
591 | 0 | insertstate->bounds_valid = true; |
592 | |
|
593 | 0 | return low; |
594 | 0 | } |
595 | | |
596 | | /*---------- |
597 | | * _bt_binsrch_posting() -- posting list binary search. |
598 | | * |
599 | | * Helper routine for _bt_binsrch_insert(). |
600 | | * |
601 | | * Returns offset into posting list where caller's scantid belongs. |
602 | | *---------- |
603 | | */ |
604 | | static int |
605 | | _bt_binsrch_posting(BTScanInsert key, Page page, OffsetNumber offnum) |
606 | 0 | { |
607 | 0 | IndexTuple itup; |
608 | 0 | ItemId itemid; |
609 | 0 | int low, |
610 | 0 | high, |
611 | 0 | mid, |
612 | 0 | res; |
613 | | |
614 | | /* |
615 | | * If this isn't a posting tuple, then the index must be corrupt (if it is |
616 | | * an ordinary non-pivot tuple then there must be an existing tuple with a |
617 | | * heap TID that equals inserter's new heap TID/scantid). Defensively |
618 | | * check that tuple is a posting list tuple whose posting list range |
619 | | * includes caller's scantid. |
620 | | * |
621 | | * (This is also needed because contrib/amcheck's rootdescend option needs |
622 | | * to be able to relocate a non-pivot tuple using _bt_binsrch_insert().) |
623 | | */ |
624 | 0 | itemid = PageGetItemId(page, offnum); |
625 | 0 | itup = (IndexTuple) PageGetItem(page, itemid); |
626 | 0 | if (!BTreeTupleIsPosting(itup)) |
627 | 0 | return 0; |
628 | | |
629 | 0 | Assert(key->heapkeyspace && key->allequalimage); |
630 | | |
631 | | /* |
632 | | * In the event that posting list tuple has LP_DEAD bit set, indicate this |
633 | | * to _bt_binsrch_insert() caller by returning -1, a sentinel value. A |
634 | | * second call to _bt_binsrch_insert() can take place when its caller has |
635 | | * removed the dead item. |
636 | | */ |
637 | 0 | if (ItemIdIsDead(itemid)) |
638 | 0 | return -1; |
639 | | |
640 | | /* "high" is past end of posting list for loop invariant */ |
641 | 0 | low = 0; |
642 | 0 | high = BTreeTupleGetNPosting(itup); |
643 | 0 | Assert(high >= 2); |
644 | |
|
645 | 0 | while (high > low) |
646 | 0 | { |
647 | 0 | mid = low + ((high - low) / 2); |
648 | 0 | res = ItemPointerCompare(key->scantid, |
649 | 0 | BTreeTupleGetPostingN(itup, mid)); |
650 | |
|
651 | 0 | if (res > 0) |
652 | 0 | low = mid + 1; |
653 | 0 | else if (res < 0) |
654 | 0 | high = mid; |
655 | 0 | else |
656 | 0 | return mid; |
657 | 0 | } |
658 | | |
659 | | /* Exact match not found */ |
660 | 0 | return low; |
661 | 0 | } |
662 | | |
663 | | /*---------- |
664 | | * _bt_compare() -- Compare insertion-type scankey to tuple on a page. |
665 | | * |
666 | | * page/offnum: location of btree item to be compared to. |
667 | | * |
668 | | * This routine returns: |
669 | | * <0 if scankey < tuple at offnum; |
670 | | * 0 if scankey == tuple at offnum; |
671 | | * >0 if scankey > tuple at offnum. |
672 | | * |
673 | | * NULLs in the keys are treated as sortable values. Therefore |
674 | | * "equality" does not necessarily mean that the item should be returned |
675 | | * to the caller as a matching key. Similarly, an insertion scankey |
676 | | * with its scantid set is treated as equal to a posting tuple whose TID |
677 | | * range overlaps with their scantid. There generally won't be a |
678 | | * matching TID in the posting tuple, which caller must handle |
679 | | * themselves (e.g., by splitting the posting list tuple). |
680 | | * |
681 | | * CRUCIAL NOTE: on a non-leaf page, the first data key is assumed to be |
682 | | * "minus infinity": this routine will always claim it is less than the |
683 | | * scankey. The actual key value stored is explicitly truncated to 0 |
684 | | * attributes (explicitly minus infinity) with version 3+ indexes, but |
685 | | * that isn't relied upon. This allows us to implement the Lehman and |
686 | | * Yao convention that the first down-link pointer is before the first |
687 | | * key. See backend/access/nbtree/README for details. |
688 | | *---------- |
689 | | */ |
690 | | int32 |
691 | | _bt_compare(Relation rel, |
692 | | BTScanInsert key, |
693 | | Page page, |
694 | | OffsetNumber offnum) |
695 | 0 | { |
696 | 0 | TupleDesc itupdesc = RelationGetDescr(rel); |
697 | 0 | BTPageOpaque opaque = BTPageGetOpaque(page); |
698 | 0 | IndexTuple itup; |
699 | 0 | ItemPointer heapTid; |
700 | 0 | ScanKey scankey; |
701 | 0 | int ncmpkey; |
702 | 0 | int ntupatts; |
703 | 0 | int32 result; |
704 | |
|
705 | 0 | Assert(_bt_check_natts(rel, key->heapkeyspace, page, offnum)); |
706 | 0 | Assert(key->keysz <= IndexRelationGetNumberOfKeyAttributes(rel)); |
707 | 0 | Assert(key->heapkeyspace || key->scantid == NULL); |
708 | | |
709 | | /* |
710 | | * Force result ">" if target item is first data item on an internal page |
711 | | * --- see NOTE above. |
712 | | */ |
713 | 0 | if (!P_ISLEAF(opaque) && offnum == P_FIRSTDATAKEY(opaque)) |
714 | 0 | return 1; |
715 | | |
716 | 0 | itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum)); |
717 | 0 | ntupatts = BTreeTupleGetNAtts(itup, rel); |
718 | | |
719 | | /* |
720 | | * The scan key is set up with the attribute number associated with each |
721 | | * term in the key. It is important that, if the index is multi-key, the |
722 | | * scan contain the first k key attributes, and that they be in order. If |
723 | | * you think about how multi-key ordering works, you'll understand why |
724 | | * this is. |
725 | | * |
726 | | * We don't test for violation of this condition here, however. The |
727 | | * initial setup for the index scan had better have gotten it right (see |
728 | | * _bt_first). |
729 | | */ |
730 | |
|
731 | 0 | ncmpkey = Min(ntupatts, key->keysz); |
732 | 0 | Assert(key->heapkeyspace || ncmpkey == key->keysz); |
733 | 0 | Assert(!BTreeTupleIsPosting(itup) || key->allequalimage); |
734 | 0 | scankey = key->scankeys; |
735 | 0 | for (int i = 1; i <= ncmpkey; i++) |
736 | 0 | { |
737 | 0 | Datum datum; |
738 | 0 | bool isNull; |
739 | |
|
740 | 0 | datum = index_getattr(itup, scankey->sk_attno, itupdesc, &isNull); |
741 | |
|
742 | 0 | if (scankey->sk_flags & SK_ISNULL) /* key is NULL */ |
743 | 0 | { |
744 | 0 | if (isNull) |
745 | 0 | result = 0; /* NULL "=" NULL */ |
746 | 0 | else if (scankey->sk_flags & SK_BT_NULLS_FIRST) |
747 | 0 | result = -1; /* NULL "<" NOT_NULL */ |
748 | 0 | else |
749 | 0 | result = 1; /* NULL ">" NOT_NULL */ |
750 | 0 | } |
751 | 0 | else if (isNull) /* key is NOT_NULL and item is NULL */ |
752 | 0 | { |
753 | 0 | if (scankey->sk_flags & SK_BT_NULLS_FIRST) |
754 | 0 | result = 1; /* NOT_NULL ">" NULL */ |
755 | 0 | else |
756 | 0 | result = -1; /* NOT_NULL "<" NULL */ |
757 | 0 | } |
758 | 0 | else |
759 | 0 | { |
760 | | /* |
761 | | * The sk_func needs to be passed the index value as left arg and |
762 | | * the sk_argument as right arg (they might be of different |
763 | | * types). Since it is convenient for callers to think of |
764 | | * _bt_compare as comparing the scankey to the index item, we have |
765 | | * to flip the sign of the comparison result. (Unless it's a DESC |
766 | | * column, in which case we *don't* flip the sign.) |
767 | | */ |
768 | 0 | result = DatumGetInt32(FunctionCall2Coll(&scankey->sk_func, |
769 | 0 | scankey->sk_collation, |
770 | 0 | datum, |
771 | 0 | scankey->sk_argument)); |
772 | |
|
773 | 0 | if (!(scankey->sk_flags & SK_BT_DESC)) |
774 | 0 | INVERT_COMPARE_RESULT(result); |
775 | 0 | } |
776 | | |
777 | | /* if the keys are unequal, return the difference */ |
778 | 0 | if (result != 0) |
779 | 0 | return result; |
780 | | |
781 | 0 | scankey++; |
782 | 0 | } |
783 | | |
784 | | /* |
785 | | * All non-truncated attributes (other than heap TID) were found to be |
786 | | * equal. Treat truncated attributes as minus infinity when scankey has a |
787 | | * key attribute value that would otherwise be compared directly. |
788 | | * |
789 | | * Note: it doesn't matter if ntupatts includes non-key attributes; |
790 | | * scankey won't, so explicitly excluding non-key attributes isn't |
791 | | * necessary. |
792 | | */ |
793 | 0 | if (key->keysz > ntupatts) |
794 | 0 | return 1; |
795 | | |
796 | | /* |
797 | | * Use the heap TID attribute and scantid to try to break the tie. The |
798 | | * rules are the same as any other key attribute -- only the |
799 | | * representation differs. |
800 | | */ |
801 | 0 | heapTid = BTreeTupleGetHeapTID(itup); |
802 | 0 | if (key->scantid == NULL) |
803 | 0 | { |
804 | | /* |
805 | | * Forward scans have a scankey that is considered greater than a |
806 | | * truncated pivot tuple if and when the scankey has equal values for |
807 | | * attributes up to and including the least significant untruncated |
808 | | * attribute in tuple. Even attributes that were omitted from the |
809 | | * scan key are considered greater than -inf truncated attributes. |
810 | | * (See _bt_binsrch for an explanation of our backward scan behavior.) |
811 | | * |
812 | | * For example, if an index has the minimum two attributes (single |
813 | | * user key attribute, plus heap TID attribute), and a page's high key |
814 | | * is ('foo', -inf), and scankey is ('foo', <omitted>), the search |
815 | | * will not descend to the page to the left. The search will descend |
816 | | * right instead. The truncated attribute in pivot tuple means that |
817 | | * all non-pivot tuples on the page to the left are strictly < 'foo', |
818 | | * so it isn't necessary to descend left. In other words, search |
819 | | * doesn't have to descend left because it isn't interested in a match |
820 | | * that has a heap TID value of -inf. |
821 | | * |
822 | | * Note: the heap TID part of the test ensures that scankey is being |
823 | | * compared to a pivot tuple with one or more truncated -inf key |
824 | | * attributes. The heap TID attribute is the last key attribute in |
825 | | * every index, of course, but other than that it isn't special. |
826 | | */ |
827 | 0 | if (!key->backward && key->keysz == ntupatts && heapTid == NULL && |
828 | 0 | key->heapkeyspace) |
829 | 0 | return 1; |
830 | | |
831 | | /* All provided scankey arguments found to be equal */ |
832 | 0 | return 0; |
833 | 0 | } |
834 | | |
835 | | /* |
836 | | * Treat truncated heap TID as minus infinity, since scankey has a key |
837 | | * attribute value (scantid) that would otherwise be compared directly |
838 | | */ |
839 | 0 | Assert(key->keysz == IndexRelationGetNumberOfKeyAttributes(rel)); |
840 | 0 | if (heapTid == NULL) |
841 | 0 | return 1; |
842 | | |
843 | | /* |
844 | | * Scankey must be treated as equal to a posting list tuple if its scantid |
845 | | * value falls within the range of the posting list. In all other cases |
846 | | * there can only be a single heap TID value, which is compared directly |
847 | | * with scantid. |
848 | | */ |
849 | 0 | Assert(ntupatts >= IndexRelationGetNumberOfKeyAttributes(rel)); |
850 | 0 | result = ItemPointerCompare(key->scantid, heapTid); |
851 | 0 | if (result <= 0 || !BTreeTupleIsPosting(itup)) |
852 | 0 | return result; |
853 | 0 | else |
854 | 0 | { |
855 | 0 | result = ItemPointerCompare(key->scantid, |
856 | 0 | BTreeTupleGetMaxHeapTID(itup)); |
857 | 0 | if (result > 0) |
858 | 0 | return 1; |
859 | 0 | } |
860 | | |
861 | 0 | return 0; |
862 | 0 | } |
863 | | |
864 | | /* |
865 | | * _bt_first() -- Find the first item in a scan. |
866 | | * |
867 | | * We need to be clever about the direction of scan, the search |
868 | | * conditions, and the tree ordering. We find the first item (or, |
869 | | * if backwards scan, the last item) in the tree that satisfies the |
870 | | * qualifications in the scan key. On success exit, data about the |
871 | | * matching tuple(s) on the page has been loaded into so->currPos. We'll |
872 | | * drop all locks and hold onto a pin on page's buffer, except during |
873 | | * so->dropPin scans, when we drop both the lock and the pin. |
874 | | * _bt_returnitem sets the next item to return to scan on success exit. |
875 | | * |
876 | | * If there are no matching items in the index, we return false, with no |
877 | | * pins or locks held. so->currPos will remain invalid. |
878 | | * |
879 | | * Note that scan->keyData[], and the so->keyData[] scankey built from it, |
880 | | * are both search-type scankeys (see nbtree/README for more about this). |
881 | | * Within this routine, we build a temporary insertion-type scankey to use |
882 | | * in locating the scan start position. |
883 | | */ |
884 | | bool |
885 | | _bt_first(IndexScanDesc scan, ScanDirection dir) |
886 | 0 | { |
887 | 0 | Relation rel = scan->indexRelation; |
888 | 0 | BTScanOpaque so = (BTScanOpaque) scan->opaque; |
889 | 0 | OffsetNumber offnum; |
890 | 0 | BTScanInsertData inskey; |
891 | 0 | ScanKey startKeys[INDEX_MAX_KEYS]; |
892 | 0 | ScanKeyData notnullkey; |
893 | 0 | int keysz = 0; |
894 | 0 | StrategyNumber strat_total = InvalidStrategy; |
895 | 0 | BlockNumber blkno = InvalidBlockNumber, |
896 | 0 | lastcurrblkno; |
897 | |
|
898 | 0 | Assert(!BTScanPosIsValid(so->currPos)); |
899 | | |
900 | | /* |
901 | | * Examine the scan keys and eliminate any redundant keys; also mark the |
902 | | * keys that must be matched to continue the scan. |
903 | | */ |
904 | 0 | _bt_preprocess_keys(scan); |
905 | | |
906 | | /* |
907 | | * Quit now if _bt_preprocess_keys() discovered that the scan keys can |
908 | | * never be satisfied (eg, x == 1 AND x > 2). |
909 | | */ |
910 | 0 | if (!so->qual_ok) |
911 | 0 | { |
912 | 0 | Assert(!so->needPrimScan); |
913 | 0 | _bt_parallel_done(scan); |
914 | 0 | return false; |
915 | 0 | } |
916 | | |
917 | | /* |
918 | | * If this is a parallel scan, we must seize the scan. _bt_readfirstpage |
919 | | * will likely release the parallel scan later on. |
920 | | */ |
921 | 0 | if (scan->parallel_scan != NULL && |
922 | 0 | !_bt_parallel_seize(scan, &blkno, &lastcurrblkno, true)) |
923 | 0 | return false; |
924 | | |
925 | | /* |
926 | | * Initialize the scan's arrays (if any) for the current scan direction |
927 | | * (except when they were already set to later values as part of |
928 | | * scheduling the primitive index scan that is now underway) |
929 | | */ |
930 | 0 | if (so->numArrayKeys && !so->needPrimScan) |
931 | 0 | _bt_start_array_keys(scan, dir); |
932 | |
|
933 | 0 | if (blkno != InvalidBlockNumber) |
934 | 0 | { |
935 | | /* |
936 | | * We anticipated calling _bt_search, but another worker bet us to it. |
937 | | * _bt_readnextpage releases the scan for us (not _bt_readfirstpage). |
938 | | */ |
939 | 0 | Assert(scan->parallel_scan != NULL); |
940 | 0 | Assert(!so->needPrimScan); |
941 | 0 | Assert(blkno != P_NONE); |
942 | |
|
943 | 0 | if (!_bt_readnextpage(scan, blkno, lastcurrblkno, dir, true)) |
944 | 0 | return false; |
945 | | |
946 | 0 | _bt_returnitem(scan, so); |
947 | 0 | return true; |
948 | 0 | } |
949 | | |
950 | | /* |
951 | | * Count an indexscan for stats, now that we know that we'll call |
952 | | * _bt_search/_bt_endpoint below |
953 | | */ |
954 | 0 | pgstat_count_index_scan(rel); |
955 | 0 | if (scan->instrument) |
956 | 0 | scan->instrument->nsearches++; |
957 | | |
958 | | /*---------- |
959 | | * Examine the scan keys to discover where we need to start the scan. |
960 | | * The selected scan keys (at most one per index column) are remembered by |
961 | | * storing their addresses into the local startKeys[] array. The final |
962 | | * startKeys[] entry's strategy is set in strat_total. (Actually, there |
963 | | * are a couple of cases where we force a less/more restrictive strategy.) |
964 | | * |
965 | | * We must use the key that was marked required (in the direction opposite |
966 | | * our own scan's) during preprocessing. Each index attribute can only |
967 | | * have one such required key. In general, the keys that we use to find |
968 | | * an initial position when scanning forwards are the same keys that end |
969 | | * the scan on the leaf level when scanning backwards (and vice-versa). |
970 | | * |
971 | | * When the scan keys include cross-type operators, _bt_preprocess_keys |
972 | | * may not be able to eliminate redundant keys; in such cases it will |
973 | | * arbitrarily pick a usable key for each attribute (and scan direction), |
974 | | * ensuring that there is no more than one key required in each direction. |
975 | | * We stop considering further keys once we reach the first nonrequired |
976 | | * key (which must come after all required keys), so this can't affect us. |
977 | | * |
978 | | * The required keys that we use as starting boundaries have to be =, >, |
979 | | * or >= keys for a forward scan or =, <, <= keys for a backwards scan. |
980 | | * We can use keys for multiple attributes so long as the prior attributes |
981 | | * had only =, >= (resp. =, <=) keys. These rules are very similar to the |
982 | | * rules that preprocessing used to determine which keys to mark required. |
983 | | * We cannot always use every required key as a positioning key, though. |
984 | | * Skip arrays necessitate independently applying our own rules here. |
985 | | * Skip arrays are always generally considered = array keys, but we'll |
986 | | * nevertheless treat them as inequalities at certain points of the scan. |
987 | | * When that happens, it _might_ have implications for the number of |
988 | | * required keys that we can safely use for initial positioning purposes. |
989 | | * |
990 | | * For example, a forward scan with a skip array on its leading attribute |
991 | | * (with no low_compare/high_compare) will have at least two required scan |
992 | | * keys, but we won't use any of them as boundary keys during the scan's |
993 | | * initial call here. Our positioning key during the first call here can |
994 | | * be thought of as representing "> -infinity". Similarly, if such a skip |
995 | | * array's low_compare is "a > 'foo'", then we position using "a > 'foo'" |
996 | | * during the scan's initial call here; a lower-order key such as "b = 42" |
997 | | * can't be used until the "a" array advances beyond MINVAL/low_compare. |
998 | | * |
999 | | * On the other hand, if such a skip array's low_compare was "a >= 'foo'", |
1000 | | * then we _can_ use "a >= 'foo' AND b = 42" during the initial call here. |
1001 | | * A subsequent call here might have us use "a = 'fop' AND b = 42". Note |
1002 | | * that we treat = and >= as equivalent when scanning forwards (just as we |
1003 | | * treat = and <= as equivalent when scanning backwards). We effectively |
1004 | | * do the same thing (though with a distinct "a" element/value) each time. |
1005 | | * |
1006 | | * All keys (with the exception of SK_SEARCHNULL keys and SK_BT_SKIP |
1007 | | * array keys whose array is "null_elem=true") imply a NOT NULL qualifier. |
1008 | | * If the index stores nulls at the end of the index we'll be starting |
1009 | | * from, and we have no boundary key for the column (which means the key |
1010 | | * we deduced NOT NULL from is an inequality key that constrains the other |
1011 | | * end of the index), then we cons up an explicit SK_SEARCHNOTNULL key to |
1012 | | * use as a boundary key. If we didn't do this, we might find ourselves |
1013 | | * traversing a lot of null entries at the start of the scan. |
1014 | | * |
1015 | | * In this loop, row-comparison keys are treated the same as keys on their |
1016 | | * first (leftmost) columns. We'll add all lower-order columns of the row |
1017 | | * comparison that were marked required during preprocessing below. |
1018 | | * |
1019 | | * _bt_advance_array_keys needs to know exactly how we'll reposition the |
1020 | | * scan (should it opt to schedule another primitive index scan). It is |
1021 | | * critical that primscans only be scheduled when they'll definitely make |
1022 | | * some useful progress. _bt_advance_array_keys does this by calling |
1023 | | * _bt_checkkeys routines that report whether a tuple is past the end of |
1024 | | * matches for the scan's keys (given the scan's current array elements). |
1025 | | * If the page's final tuple is "after the end of matches" for a scan that |
1026 | | * uses the *opposite* scan direction, then it must follow that it's also |
1027 | | * "before the start of matches" for the actual current scan direction. |
1028 | | * It is therefore essential that all of our initial positioning rules are |
1029 | | * symmetric with _bt_checkkeys's corresponding continuescan=false rule. |
1030 | | * If you update anything here, _bt_checkkeys/_bt_advance_array_keys might |
1031 | | * need to be kept in sync. |
1032 | | *---------- |
1033 | | */ |
1034 | 0 | if (so->numberOfKeys > 0) |
1035 | 0 | { |
1036 | 0 | AttrNumber curattr; |
1037 | 0 | ScanKey bkey; |
1038 | 0 | ScanKey impliesNN; |
1039 | 0 | ScanKey cur; |
1040 | | |
1041 | | /* |
1042 | | * bkey will be set to the key that preprocessing left behind as the |
1043 | | * boundary key for this attribute, in this scan direction (if any) |
1044 | | */ |
1045 | 0 | cur = so->keyData; |
1046 | 0 | curattr = 1; |
1047 | 0 | bkey = NULL; |
1048 | | /* Also remember any scankey that implies a NOT NULL constraint */ |
1049 | 0 | impliesNN = NULL; |
1050 | | |
1051 | | /* |
1052 | | * Loop iterates from 0 to numberOfKeys inclusive; we use the last |
1053 | | * pass to handle after-last-key processing. Actual exit from the |
1054 | | * loop is at one of the "break" statements below. |
1055 | | */ |
1056 | 0 | for (int i = 0;; cur++, i++) |
1057 | 0 | { |
1058 | 0 | if (i >= so->numberOfKeys || cur->sk_attno != curattr) |
1059 | 0 | { |
1060 | | /* Done looking for the curattr boundary key */ |
1061 | 0 | Assert(bkey == NULL || |
1062 | 0 | (bkey->sk_attno == curattr && |
1063 | 0 | (bkey->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)))); |
1064 | 0 | Assert(impliesNN == NULL || |
1065 | 0 | (impliesNN->sk_attno == curattr && |
1066 | 0 | (impliesNN->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)))); |
1067 | | |
1068 | | /* |
1069 | | * If this is a scan key for a skip array whose current |
1070 | | * element is MINVAL, choose low_compare (when scanning |
1071 | | * backwards it'll be MAXVAL, and we'll choose high_compare). |
1072 | | * |
1073 | | * Note: if the array's low_compare key makes 'bkey' NULL, |
1074 | | * then we behave as if the array's first element is -inf, |
1075 | | * except when !array->null_elem implies a usable NOT NULL |
1076 | | * constraint. |
1077 | | */ |
1078 | 0 | if (bkey != NULL && |
1079 | 0 | (bkey->sk_flags & (SK_BT_MINVAL | SK_BT_MAXVAL))) |
1080 | 0 | { |
1081 | 0 | int ikey = bkey - so->keyData; |
1082 | 0 | ScanKey skipequalitykey = bkey; |
1083 | 0 | BTArrayKeyInfo *array = NULL; |
1084 | |
|
1085 | 0 | for (int arridx = 0; arridx < so->numArrayKeys; arridx++) |
1086 | 0 | { |
1087 | 0 | array = &so->arrayKeys[arridx]; |
1088 | 0 | if (array->scan_key == ikey) |
1089 | 0 | break; |
1090 | 0 | } |
1091 | |
|
1092 | 0 | if (ScanDirectionIsForward(dir)) |
1093 | 0 | { |
1094 | 0 | Assert(!(skipequalitykey->sk_flags & SK_BT_MAXVAL)); |
1095 | 0 | bkey = array->low_compare; |
1096 | 0 | } |
1097 | 0 | else |
1098 | 0 | { |
1099 | 0 | Assert(!(skipequalitykey->sk_flags & SK_BT_MINVAL)); |
1100 | 0 | bkey = array->high_compare; |
1101 | 0 | } |
1102 | |
|
1103 | 0 | Assert(bkey == NULL || |
1104 | 0 | bkey->sk_attno == skipequalitykey->sk_attno); |
1105 | |
|
1106 | 0 | if (!array->null_elem) |
1107 | 0 | impliesNN = skipequalitykey; |
1108 | 0 | else |
1109 | 0 | Assert(bkey == NULL && impliesNN == NULL); |
1110 | 0 | } |
1111 | | |
1112 | | /* |
1113 | | * If we didn't find a usable boundary key, see if we can |
1114 | | * deduce a NOT NULL key |
1115 | | */ |
1116 | 0 | if (bkey == NULL && impliesNN != NULL && |
1117 | 0 | ((impliesNN->sk_flags & SK_BT_NULLS_FIRST) ? |
1118 | 0 | ScanDirectionIsForward(dir) : |
1119 | 0 | ScanDirectionIsBackward(dir))) |
1120 | 0 | { |
1121 | | /* Final startKeys[] entry will be deduced NOT NULL key */ |
1122 | 0 | bkey = ¬nullkey; |
1123 | 0 | ScanKeyEntryInitialize(bkey, |
1124 | 0 | (SK_SEARCHNOTNULL | SK_ISNULL | |
1125 | 0 | (impliesNN->sk_flags & |
1126 | 0 | (SK_BT_DESC | SK_BT_NULLS_FIRST))), |
1127 | 0 | curattr, |
1128 | 0 | ScanDirectionIsForward(dir) ? |
1129 | 0 | BTGreaterStrategyNumber : BTLessStrategyNumber, |
1130 | 0 | InvalidOid, |
1131 | 0 | InvalidOid, |
1132 | 0 | InvalidOid, |
1133 | 0 | (Datum) 0); |
1134 | 0 | } |
1135 | | |
1136 | | /* |
1137 | | * If preprocessing didn't leave a usable boundary key, quit; |
1138 | | * else save the boundary key pointer in startKeys[] |
1139 | | */ |
1140 | 0 | if (bkey == NULL) |
1141 | 0 | break; |
1142 | 0 | startKeys[keysz++] = bkey; |
1143 | | |
1144 | | /* |
1145 | | * We can only consider adding more boundary keys when the one |
1146 | | * that we just chose to add uses either the = or >= strategy |
1147 | | * (during backwards scans we can only do so when the key that |
1148 | | * we just added to startKeys[] uses the = or <= strategy) |
1149 | | */ |
1150 | 0 | strat_total = bkey->sk_strategy; |
1151 | 0 | if (strat_total == BTGreaterStrategyNumber || |
1152 | 0 | strat_total == BTLessStrategyNumber) |
1153 | 0 | break; |
1154 | | |
1155 | | /* |
1156 | | * If the key that we just added to startKeys[] is a skip |
1157 | | * array = key whose current element is marked NEXT or PRIOR, |
1158 | | * make strat_total > or < (and stop adding boundary keys). |
1159 | | * This can only happen with opclasses that lack skip support. |
1160 | | */ |
1161 | 0 | if (bkey->sk_flags & (SK_BT_NEXT | SK_BT_PRIOR)) |
1162 | 0 | { |
1163 | 0 | Assert(bkey->sk_flags & SK_BT_SKIP); |
1164 | 0 | Assert(strat_total == BTEqualStrategyNumber); |
1165 | |
|
1166 | 0 | if (ScanDirectionIsForward(dir)) |
1167 | 0 | { |
1168 | 0 | Assert(!(bkey->sk_flags & SK_BT_PRIOR)); |
1169 | 0 | strat_total = BTGreaterStrategyNumber; |
1170 | 0 | } |
1171 | 0 | else |
1172 | 0 | { |
1173 | 0 | Assert(!(bkey->sk_flags & SK_BT_NEXT)); |
1174 | 0 | strat_total = BTLessStrategyNumber; |
1175 | 0 | } |
1176 | | |
1177 | | /* |
1178 | | * We're done. We'll never find an exact = match for a |
1179 | | * NEXT or PRIOR sentinel sk_argument value. There's no |
1180 | | * sense in trying to add more keys to startKeys[]. |
1181 | | */ |
1182 | 0 | break; |
1183 | 0 | } |
1184 | | |
1185 | | /* |
1186 | | * Done if that was the last scan key output by preprocessing. |
1187 | | * Also done if we've now examined all keys marked required. |
1188 | | */ |
1189 | 0 | if (i >= so->numberOfKeys || |
1190 | 0 | !(cur->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD))) |
1191 | 0 | break; |
1192 | | |
1193 | | /* |
1194 | | * Reset for next attr. |
1195 | | */ |
1196 | 0 | Assert(cur->sk_attno == curattr + 1); |
1197 | 0 | curattr = cur->sk_attno; |
1198 | 0 | bkey = NULL; |
1199 | 0 | impliesNN = NULL; |
1200 | 0 | } |
1201 | | |
1202 | | /* |
1203 | | * If we've located the starting boundary key for curattr, we have |
1204 | | * no interest in curattr's other required key |
1205 | | */ |
1206 | 0 | if (bkey != NULL) |
1207 | 0 | continue; |
1208 | | |
1209 | | /* |
1210 | | * Is this key the starting boundary key for curattr? |
1211 | | * |
1212 | | * If not, does it imply a NOT NULL constraint? (Because |
1213 | | * SK_SEARCHNULL keys are always assigned BTEqualStrategyNumber, |
1214 | | * *any* inequality key works for that; we need not test.) |
1215 | | */ |
1216 | 0 | switch (cur->sk_strategy) |
1217 | 0 | { |
1218 | 0 | case BTLessStrategyNumber: |
1219 | 0 | case BTLessEqualStrategyNumber: |
1220 | 0 | if (ScanDirectionIsBackward(dir)) |
1221 | 0 | bkey = cur; |
1222 | 0 | else if (impliesNN == NULL) |
1223 | 0 | impliesNN = cur; |
1224 | 0 | break; |
1225 | 0 | case BTEqualStrategyNumber: |
1226 | 0 | bkey = cur; |
1227 | 0 | break; |
1228 | 0 | case BTGreaterEqualStrategyNumber: |
1229 | 0 | case BTGreaterStrategyNumber: |
1230 | 0 | if (ScanDirectionIsForward(dir)) |
1231 | 0 | bkey = cur; |
1232 | 0 | else if (impliesNN == NULL) |
1233 | 0 | impliesNN = cur; |
1234 | 0 | break; |
1235 | 0 | } |
1236 | 0 | } |
1237 | 0 | } |
1238 | | |
1239 | | /* |
1240 | | * If we found no usable boundary keys, we have to start from one end of |
1241 | | * the tree. Walk down that edge to the first or last key, and scan from |
1242 | | * there. |
1243 | | * |
1244 | | * Note: calls _bt_readfirstpage for us, which releases the parallel scan. |
1245 | | */ |
1246 | 0 | if (keysz == 0) |
1247 | 0 | return _bt_endpoint(scan, dir); |
1248 | | |
1249 | | /* |
1250 | | * We want to start the scan somewhere within the index. Set up an |
1251 | | * insertion scankey we can use to search for the boundary point we |
1252 | | * identified above. The insertion scankey is built using the keys |
1253 | | * identified by startKeys[]. (Remaining insertion scankey fields are |
1254 | | * initialized after initial-positioning scan keys are finalized.) |
1255 | | */ |
1256 | 0 | Assert(keysz <= INDEX_MAX_KEYS); |
1257 | 0 | for (int i = 0; i < keysz; i++) |
1258 | 0 | { |
1259 | 0 | ScanKey bkey = startKeys[i]; |
1260 | |
|
1261 | 0 | Assert(bkey->sk_attno == i + 1); |
1262 | |
|
1263 | 0 | if (bkey->sk_flags & SK_ROW_HEADER) |
1264 | 0 | { |
1265 | | /* |
1266 | | * Row comparison header: look to the first row member instead |
1267 | | */ |
1268 | 0 | ScanKey subkey = (ScanKey) DatumGetPointer(bkey->sk_argument); |
1269 | 0 | bool loosen_strat = false, |
1270 | 0 | tighten_strat = false; |
1271 | | |
1272 | | /* |
1273 | | * Cannot be a NULL in the first row member: _bt_preprocess_keys |
1274 | | * would've marked the qual as unsatisfiable, preventing us from |
1275 | | * ever getting this far |
1276 | | */ |
1277 | 0 | Assert(subkey->sk_flags & SK_ROW_MEMBER); |
1278 | 0 | Assert(subkey->sk_attno == bkey->sk_attno); |
1279 | 0 | Assert(!(subkey->sk_flags & SK_ISNULL)); |
1280 | | |
1281 | | /* |
1282 | | * This is either a > or >= key (during backwards scans it is |
1283 | | * either < or <=) that was marked required during preprocessing. |
1284 | | * Later so->keyData[] keys can't have been marked required, so |
1285 | | * our row compare header key must be the final startKeys[] entry. |
1286 | | */ |
1287 | 0 | Assert(subkey->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)); |
1288 | 0 | Assert(subkey->sk_strategy == bkey->sk_strategy); |
1289 | 0 | Assert(subkey->sk_strategy == strat_total); |
1290 | 0 | Assert(i == keysz - 1); |
1291 | | |
1292 | | /* |
1293 | | * The member scankeys are already in insertion format (ie, they |
1294 | | * have sk_func = 3-way-comparison function) |
1295 | | */ |
1296 | 0 | memcpy(inskey.scankeys + i, subkey, sizeof(ScanKeyData)); |
1297 | | |
1298 | | /* |
1299 | | * Now look to later row compare members. |
1300 | | * |
1301 | | * If there's an "index attribute gap" between two row compare |
1302 | | * members, the second member won't have been marked required, and |
1303 | | * so can't be used as a starting boundary key here. The part of |
1304 | | * the row comparison that we do still use has to be treated as a |
1305 | | * ">=" or "<=" condition. For example, a qual "(a, c) > (1, 42)" |
1306 | | * with an omitted intervening index attribute "b" will use an |
1307 | | * insertion scan key "a >= 1". Even the first "a = 1" tuple on |
1308 | | * the leaf level might satisfy the row compare qual. |
1309 | | * |
1310 | | * We're able to use a _more_ restrictive strategy when we reach a |
1311 | | * NULL row compare member, since they're always unsatisfiable. |
1312 | | * For example, a qual "(a, b, c) >= (1, NULL, 77)" will use an |
1313 | | * insertion scan key "a > 1". All tuples where "a = 1" cannot |
1314 | | * possibly satisfy the row compare qual, so this is safe. |
1315 | | */ |
1316 | 0 | Assert(!(subkey->sk_flags & SK_ROW_END)); |
1317 | 0 | for (;;) |
1318 | 0 | { |
1319 | 0 | subkey++; |
1320 | 0 | Assert(subkey->sk_flags & SK_ROW_MEMBER); |
1321 | |
|
1322 | 0 | if (subkey->sk_flags & SK_ISNULL) |
1323 | 0 | { |
1324 | | /* |
1325 | | * NULL member key, can only use earlier keys. |
1326 | | * |
1327 | | * We deliberately avoid checking if this key is marked |
1328 | | * required. All earlier keys are required, and this key |
1329 | | * is unsatisfiable either way, so we can't miss anything. |
1330 | | */ |
1331 | 0 | tighten_strat = true; |
1332 | 0 | break; |
1333 | 0 | } |
1334 | | |
1335 | 0 | if (!(subkey->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD))) |
1336 | 0 | { |
1337 | | /* nonrequired member key, can only use earlier keys */ |
1338 | 0 | loosen_strat = true; |
1339 | 0 | break; |
1340 | 0 | } |
1341 | | |
1342 | 0 | Assert(subkey->sk_attno == keysz + 1); |
1343 | 0 | Assert(subkey->sk_strategy == bkey->sk_strategy); |
1344 | 0 | Assert(keysz < INDEX_MAX_KEYS); |
1345 | |
|
1346 | 0 | memcpy(inskey.scankeys + keysz, subkey, sizeof(ScanKeyData)); |
1347 | 0 | keysz++; |
1348 | |
|
1349 | 0 | if (subkey->sk_flags & SK_ROW_END) |
1350 | 0 | break; |
1351 | 0 | } |
1352 | 0 | Assert(!(loosen_strat && tighten_strat)); |
1353 | 0 | if (loosen_strat) |
1354 | 0 | { |
1355 | | /* Use less restrictive strategy (and fewer member keys) */ |
1356 | 0 | switch (strat_total) |
1357 | 0 | { |
1358 | 0 | case BTLessStrategyNumber: |
1359 | 0 | strat_total = BTLessEqualStrategyNumber; |
1360 | 0 | break; |
1361 | 0 | case BTGreaterStrategyNumber: |
1362 | 0 | strat_total = BTGreaterEqualStrategyNumber; |
1363 | 0 | break; |
1364 | 0 | } |
1365 | 0 | } |
1366 | 0 | if (tighten_strat) |
1367 | 0 | { |
1368 | | /* Use more restrictive strategy (and fewer member keys) */ |
1369 | 0 | switch (strat_total) |
1370 | 0 | { |
1371 | 0 | case BTLessEqualStrategyNumber: |
1372 | 0 | strat_total = BTLessStrategyNumber; |
1373 | 0 | break; |
1374 | 0 | case BTGreaterEqualStrategyNumber: |
1375 | 0 | strat_total = BTGreaterStrategyNumber; |
1376 | 0 | break; |
1377 | 0 | } |
1378 | 0 | } |
1379 | | |
1380 | | /* Done (row compare header key is always last startKeys[] key) */ |
1381 | 0 | break; |
1382 | 0 | } |
1383 | | |
1384 | | /* |
1385 | | * Ordinary comparison key/search-style key. |
1386 | | * |
1387 | | * Transform the search-style scan key to an insertion scan key by |
1388 | | * replacing the sk_func with the appropriate btree 3-way-comparison |
1389 | | * function. |
1390 | | * |
1391 | | * If scankey operator is not a cross-type comparison, we can use the |
1392 | | * cached comparison function; otherwise gotta look it up in the |
1393 | | * catalogs. (That can't lead to infinite recursion, since no |
1394 | | * indexscan initiated by syscache lookup will use cross-data-type |
1395 | | * operators.) |
1396 | | * |
1397 | | * We support the convention that sk_subtype == InvalidOid means the |
1398 | | * opclass input type; this hack simplifies life for ScanKeyInit(). |
1399 | | */ |
1400 | 0 | if (bkey->sk_subtype == rel->rd_opcintype[i] || |
1401 | 0 | bkey->sk_subtype == InvalidOid) |
1402 | 0 | { |
1403 | 0 | FmgrInfo *procinfo; |
1404 | |
|
1405 | 0 | procinfo = index_getprocinfo(rel, bkey->sk_attno, BTORDER_PROC); |
1406 | 0 | ScanKeyEntryInitializeWithInfo(inskey.scankeys + i, |
1407 | 0 | bkey->sk_flags, |
1408 | 0 | bkey->sk_attno, |
1409 | 0 | InvalidStrategy, |
1410 | 0 | bkey->sk_subtype, |
1411 | 0 | bkey->sk_collation, |
1412 | 0 | procinfo, |
1413 | 0 | bkey->sk_argument); |
1414 | 0 | } |
1415 | 0 | else |
1416 | 0 | { |
1417 | 0 | RegProcedure cmp_proc; |
1418 | |
|
1419 | 0 | cmp_proc = get_opfamily_proc(rel->rd_opfamily[i], |
1420 | 0 | rel->rd_opcintype[i], |
1421 | 0 | bkey->sk_subtype, BTORDER_PROC); |
1422 | 0 | if (!RegProcedureIsValid(cmp_proc)) |
1423 | 0 | elog(ERROR, "missing support function %d(%u,%u) for attribute %d of index \"%s\"", |
1424 | 0 | BTORDER_PROC, rel->rd_opcintype[i], bkey->sk_subtype, |
1425 | 0 | bkey->sk_attno, RelationGetRelationName(rel)); |
1426 | 0 | ScanKeyEntryInitialize(inskey.scankeys + i, |
1427 | 0 | bkey->sk_flags, |
1428 | 0 | bkey->sk_attno, |
1429 | 0 | InvalidStrategy, |
1430 | 0 | bkey->sk_subtype, |
1431 | 0 | bkey->sk_collation, |
1432 | 0 | cmp_proc, |
1433 | 0 | bkey->sk_argument); |
1434 | 0 | } |
1435 | 0 | } |
1436 | | |
1437 | | /*---------- |
1438 | | * Examine the selected initial-positioning strategy to determine exactly |
1439 | | * where we need to start the scan, and set flag variables to control the |
1440 | | * initial descent by _bt_search (and our _bt_binsrch call for the leaf |
1441 | | * page _bt_search returns). |
1442 | | *---------- |
1443 | | */ |
1444 | 0 | _bt_metaversion(rel, &inskey.heapkeyspace, &inskey.allequalimage); |
1445 | 0 | inskey.anynullkeys = false; /* unused */ |
1446 | 0 | inskey.scantid = NULL; |
1447 | 0 | inskey.keysz = keysz; |
1448 | 0 | switch (strat_total) |
1449 | 0 | { |
1450 | 0 | case BTLessStrategyNumber: |
1451 | |
|
1452 | 0 | inskey.nextkey = false; |
1453 | 0 | inskey.backward = true; |
1454 | 0 | break; |
1455 | | |
1456 | 0 | case BTLessEqualStrategyNumber: |
1457 | |
|
1458 | 0 | inskey.nextkey = true; |
1459 | 0 | inskey.backward = true; |
1460 | 0 | break; |
1461 | | |
1462 | 0 | case BTEqualStrategyNumber: |
1463 | | |
1464 | | /* |
1465 | | * If a backward scan was specified, need to start with last equal |
1466 | | * item not first one. |
1467 | | */ |
1468 | 0 | if (ScanDirectionIsBackward(dir)) |
1469 | 0 | { |
1470 | | /* |
1471 | | * This is the same as the <= strategy |
1472 | | */ |
1473 | 0 | inskey.nextkey = true; |
1474 | 0 | inskey.backward = true; |
1475 | 0 | } |
1476 | 0 | else |
1477 | 0 | { |
1478 | | /* |
1479 | | * This is the same as the >= strategy |
1480 | | */ |
1481 | 0 | inskey.nextkey = false; |
1482 | 0 | inskey.backward = false; |
1483 | 0 | } |
1484 | 0 | break; |
1485 | | |
1486 | 0 | case BTGreaterEqualStrategyNumber: |
1487 | | |
1488 | | /* |
1489 | | * Find first item >= scankey |
1490 | | */ |
1491 | 0 | inskey.nextkey = false; |
1492 | 0 | inskey.backward = false; |
1493 | 0 | break; |
1494 | | |
1495 | 0 | case BTGreaterStrategyNumber: |
1496 | | |
1497 | | /* |
1498 | | * Find first item > scankey |
1499 | | */ |
1500 | 0 | inskey.nextkey = true; |
1501 | 0 | inskey.backward = false; |
1502 | 0 | break; |
1503 | | |
1504 | 0 | default: |
1505 | | /* can't get here, but keep compiler quiet */ |
1506 | 0 | elog(ERROR, "unrecognized strat_total: %d", (int) strat_total); |
1507 | 0 | return false; |
1508 | 0 | } |
1509 | | |
1510 | | /* |
1511 | | * Use the manufactured insertion scan key to descend the tree and |
1512 | | * position ourselves on the target leaf page. |
1513 | | */ |
1514 | 0 | Assert(ScanDirectionIsBackward(dir) == inskey.backward); |
1515 | 0 | _bt_search(rel, NULL, &inskey, &so->currPos.buf, BT_READ, false); |
1516 | |
|
1517 | 0 | if (!BufferIsValid(so->currPos.buf)) |
1518 | 0 | { |
1519 | 0 | Assert(!so->needPrimScan); |
1520 | |
|
1521 | | #ifdef USE_INJECTION_POINTS |
1522 | | if (!IsCatalogRelation(rel)) |
1523 | | INJECTION_POINT("nbtree-first-empty", NULL); |
1524 | | #endif |
1525 | | |
1526 | | /* |
1527 | | * We only get here if the index is completely empty. Lock relation |
1528 | | * because nothing finer to lock exists. Without a buffer lock, it's |
1529 | | * possible for another transaction to insert data between |
1530 | | * _bt_search() and PredicateLockRelation(). We have to try again |
1531 | | * after taking the relation-level predicate lock, to close a narrow |
1532 | | * window where we wouldn't scan concurrently inserted tuples, but the |
1533 | | * writer wouldn't see our predicate lock. |
1534 | | */ |
1535 | 0 | if (IsolationIsSerializable()) |
1536 | 0 | { |
1537 | 0 | PredicateLockRelation(rel, scan->xs_snapshot); |
1538 | 0 | _bt_search(rel, NULL, &inskey, &so->currPos.buf, BT_READ, false); |
1539 | 0 | } |
1540 | |
|
1541 | 0 | if (!BufferIsValid(so->currPos.buf)) |
1542 | 0 | { |
1543 | 0 | _bt_parallel_done(scan); |
1544 | 0 | return false; |
1545 | 0 | } |
1546 | 0 | } |
1547 | | |
1548 | | /* position to the precise item on the page */ |
1549 | 0 | offnum = _bt_binsrch(rel, &inskey, so->currPos.buf); |
1550 | | |
1551 | | /* |
1552 | | * Now load data from the first page of the scan (usually the page |
1553 | | * currently in so->currPos.buf). |
1554 | | * |
1555 | | * If inskey.nextkey = false and inskey.backward = false, offnum is |
1556 | | * positioned at the first non-pivot tuple >= inskey.scankeys. |
1557 | | * |
1558 | | * If inskey.nextkey = false and inskey.backward = true, offnum is |
1559 | | * positioned at the last non-pivot tuple < inskey.scankeys. |
1560 | | * |
1561 | | * If inskey.nextkey = true and inskey.backward = false, offnum is |
1562 | | * positioned at the first non-pivot tuple > inskey.scankeys. |
1563 | | * |
1564 | | * If inskey.nextkey = true and inskey.backward = true, offnum is |
1565 | | * positioned at the last non-pivot tuple <= inskey.scankeys. |
1566 | | * |
1567 | | * It's possible that _bt_binsrch returned an offnum that is out of bounds |
1568 | | * for the page. For example, when inskey is both < the leaf page's high |
1569 | | * key and > all of its non-pivot tuples, offnum will be "maxoff + 1". |
1570 | | */ |
1571 | 0 | if (!_bt_readfirstpage(scan, offnum, dir)) |
1572 | 0 | return false; |
1573 | | |
1574 | 0 | _bt_returnitem(scan, so); |
1575 | 0 | return true; |
1576 | 0 | } |
1577 | | |
1578 | | /* |
1579 | | * _bt_next() -- Get the next item in a scan. |
1580 | | * |
1581 | | * On entry, so->currPos describes the current page, which may be pinned |
1582 | | * but is not locked, and so->currPos.itemIndex identifies which item was |
1583 | | * previously returned. |
1584 | | * |
1585 | | * On success exit, so->currPos is updated as needed, and _bt_returnitem |
1586 | | * sets the next item to return to the scan. so->currPos remains valid. |
1587 | | * |
1588 | | * On failure exit (no more tuples), we invalidate so->currPos. It'll |
1589 | | * still be possible for the scan to return tuples by changing direction, |
1590 | | * though we'll need to call _bt_first anew in that other direction. |
1591 | | */ |
1592 | | bool |
1593 | | _bt_next(IndexScanDesc scan, ScanDirection dir) |
1594 | 0 | { |
1595 | 0 | BTScanOpaque so = (BTScanOpaque) scan->opaque; |
1596 | |
|
1597 | 0 | Assert(BTScanPosIsValid(so->currPos)); |
1598 | | |
1599 | | /* |
1600 | | * Advance to next tuple on current page; or if there's no more, try to |
1601 | | * step to the next page with data. |
1602 | | */ |
1603 | 0 | if (ScanDirectionIsForward(dir)) |
1604 | 0 | { |
1605 | 0 | if (++so->currPos.itemIndex > so->currPos.lastItem) |
1606 | 0 | { |
1607 | 0 | if (!_bt_steppage(scan, dir)) |
1608 | 0 | return false; |
1609 | 0 | } |
1610 | 0 | } |
1611 | 0 | else |
1612 | 0 | { |
1613 | 0 | if (--so->currPos.itemIndex < so->currPos.firstItem) |
1614 | 0 | { |
1615 | 0 | if (!_bt_steppage(scan, dir)) |
1616 | 0 | return false; |
1617 | 0 | } |
1618 | 0 | } |
1619 | | |
1620 | 0 | _bt_returnitem(scan, so); |
1621 | 0 | return true; |
1622 | 0 | } |
1623 | | |
1624 | | /* |
1625 | | * Return the index item from so->currPos.items[so->currPos.itemIndex] to the |
1626 | | * index scan by setting the relevant fields in caller's index scan descriptor |
1627 | | */ |
1628 | | static inline void |
1629 | | _bt_returnitem(IndexScanDesc scan, BTScanOpaque so) |
1630 | 0 | { |
1631 | 0 | BTScanPosItem *currItem = &so->currPos.items[so->currPos.itemIndex]; |
1632 | | |
1633 | | /* Most recent _bt_readpage must have succeeded */ |
1634 | 0 | Assert(BTScanPosIsValid(so->currPos)); |
1635 | 0 | Assert(so->currPos.itemIndex >= so->currPos.firstItem); |
1636 | 0 | Assert(so->currPos.itemIndex <= so->currPos.lastItem); |
1637 | | |
1638 | | /* Return next item, per amgettuple contract */ |
1639 | 0 | scan->xs_heaptid = currItem->heapTid; |
1640 | 0 | if (so->currTuples) |
1641 | 0 | scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset); |
1642 | 0 | } |
1643 | | |
1644 | | /* |
1645 | | * _bt_steppage() -- Step to next page containing valid data for scan |
1646 | | * |
1647 | | * Wrapper on _bt_readnextpage that performs final steps for the current page. |
1648 | | * |
1649 | | * On entry, so->currPos must be valid. Its buffer will be pinned, though |
1650 | | * never locked. (Actually, when so->dropPin there won't even be a pin held, |
1651 | | * though so->currPos.currPage must still be set to a valid block number.) |
1652 | | */ |
1653 | | static bool |
1654 | | _bt_steppage(IndexScanDesc scan, ScanDirection dir) |
1655 | 0 | { |
1656 | 0 | BTScanOpaque so = (BTScanOpaque) scan->opaque; |
1657 | 0 | BlockNumber blkno, |
1658 | 0 | lastcurrblkno; |
1659 | |
|
1660 | 0 | Assert(BTScanPosIsValid(so->currPos)); |
1661 | | |
1662 | | /* Before leaving current page, deal with any killed items */ |
1663 | 0 | if (so->numKilled > 0) |
1664 | 0 | _bt_killitems(scan); |
1665 | | |
1666 | | /* |
1667 | | * Before we modify currPos, make a copy of the page data if there was a |
1668 | | * mark position that needs it. |
1669 | | */ |
1670 | 0 | if (so->markItemIndex >= 0) |
1671 | 0 | { |
1672 | | /* bump pin on current buffer for assignment to mark buffer */ |
1673 | 0 | if (BTScanPosIsPinned(so->currPos)) |
1674 | 0 | IncrBufferRefCount(so->currPos.buf); |
1675 | 0 | memcpy(&so->markPos, &so->currPos, |
1676 | 0 | offsetof(BTScanPosData, items[1]) + |
1677 | 0 | so->currPos.lastItem * sizeof(BTScanPosItem)); |
1678 | 0 | if (so->markTuples) |
1679 | 0 | memcpy(so->markTuples, so->currTuples, |
1680 | 0 | so->currPos.nextTupleOffset); |
1681 | 0 | so->markPos.itemIndex = so->markItemIndex; |
1682 | 0 | so->markItemIndex = -1; |
1683 | | |
1684 | | /* |
1685 | | * If we're just about to start the next primitive index scan |
1686 | | * (possible with a scan that has arrays keys, and needs to skip to |
1687 | | * continue in the current scan direction), moreLeft/moreRight only |
1688 | | * indicate the end of the current primitive index scan. They must |
1689 | | * never be taken to indicate that the top-level index scan has ended |
1690 | | * (that would be wrong). |
1691 | | * |
1692 | | * We could handle this case by treating the current array keys as |
1693 | | * markPos state. But depending on the current array state like this |
1694 | | * would add complexity. Instead, we just unset markPos's copy of |
1695 | | * moreRight or moreLeft (whichever might be affected), while making |
1696 | | * btrestrpos reset the scan's arrays to their initial scan positions. |
1697 | | * In effect, btrestrpos leaves advancing the arrays up to the first |
1698 | | * _bt_readpage call (that takes place after it has restored markPos). |
1699 | | */ |
1700 | 0 | if (so->needPrimScan) |
1701 | 0 | { |
1702 | 0 | if (ScanDirectionIsForward(so->currPos.dir)) |
1703 | 0 | so->markPos.moreRight = true; |
1704 | 0 | else |
1705 | 0 | so->markPos.moreLeft = true; |
1706 | 0 | } |
1707 | | |
1708 | | /* mark/restore not supported by parallel scans */ |
1709 | 0 | Assert(!scan->parallel_scan); |
1710 | 0 | } |
1711 | |
|
1712 | 0 | BTScanPosUnpinIfPinned(so->currPos); |
1713 | | |
1714 | | /* Walk to the next page with data */ |
1715 | 0 | if (ScanDirectionIsForward(dir)) |
1716 | 0 | blkno = so->currPos.nextPage; |
1717 | 0 | else |
1718 | 0 | blkno = so->currPos.prevPage; |
1719 | 0 | lastcurrblkno = so->currPos.currPage; |
1720 | | |
1721 | | /* |
1722 | | * Cancel primitive index scans that were scheduled when the call to |
1723 | | * _bt_readpage for currPos happened to use the opposite direction to the |
1724 | | * one that we're stepping in now. (It's okay to leave the scan's array |
1725 | | * keys as-is, since the next _bt_readpage will advance them.) |
1726 | | */ |
1727 | 0 | if (so->currPos.dir != dir) |
1728 | 0 | so->needPrimScan = false; |
1729 | |
|
1730 | 0 | return _bt_readnextpage(scan, blkno, lastcurrblkno, dir, false); |
1731 | 0 | } |
1732 | | |
1733 | | /* |
1734 | | * _bt_readfirstpage() -- Read first page containing valid data for _bt_first |
1735 | | * |
1736 | | * _bt_first caller passes us an offnum returned by _bt_binsrch, which might |
1737 | | * be an out of bounds offnum such as "maxoff + 1" in certain corner cases. |
1738 | | * When we're passed an offnum past the end of the page, we might still manage |
1739 | | * to stop the scan on this page by calling _bt_checkkeys against the high |
1740 | | * key. See _bt_readpage for full details. |
1741 | | * |
1742 | | * On entry, so->currPos must be pinned and locked (so offnum stays valid). |
1743 | | * Parallel scan callers must have seized the scan before calling here. |
1744 | | * |
1745 | | * On exit, we'll have updated so->currPos and retained locks and pins |
1746 | | * according to the same rules as those laid out for _bt_readnextpage exit. |
1747 | | * Like _bt_readnextpage, our return value indicates if there are any matching |
1748 | | * records in the given direction. |
1749 | | * |
1750 | | * We always release the scan for a parallel scan caller, regardless of |
1751 | | * success or failure; we'll call _bt_parallel_release as soon as possible. |
1752 | | */ |
1753 | | static bool |
1754 | | _bt_readfirstpage(IndexScanDesc scan, OffsetNumber offnum, ScanDirection dir) |
1755 | 0 | { |
1756 | 0 | BTScanOpaque so = (BTScanOpaque) scan->opaque; |
1757 | |
|
1758 | 0 | so->numKilled = 0; /* just paranoia */ |
1759 | 0 | so->markItemIndex = -1; /* ditto */ |
1760 | | |
1761 | | /* Initialize so->currPos for the first page (page in so->currPos.buf) */ |
1762 | 0 | if (so->needPrimScan) |
1763 | 0 | { |
1764 | 0 | Assert(so->numArrayKeys); |
1765 | |
|
1766 | 0 | so->currPos.moreLeft = true; |
1767 | 0 | so->currPos.moreRight = true; |
1768 | 0 | so->needPrimScan = false; |
1769 | 0 | } |
1770 | 0 | else if (ScanDirectionIsForward(dir)) |
1771 | 0 | { |
1772 | 0 | so->currPos.moreLeft = false; |
1773 | 0 | so->currPos.moreRight = true; |
1774 | 0 | } |
1775 | 0 | else |
1776 | 0 | { |
1777 | 0 | so->currPos.moreLeft = true; |
1778 | 0 | so->currPos.moreRight = false; |
1779 | 0 | } |
1780 | | |
1781 | | /* |
1782 | | * Attempt to load matching tuples from the first page. |
1783 | | * |
1784 | | * Note that _bt_readpage will finish initializing the so->currPos fields. |
1785 | | * _bt_readpage also releases parallel scan (even when it returns false). |
1786 | | */ |
1787 | 0 | if (_bt_readpage(scan, dir, offnum, true)) |
1788 | 0 | { |
1789 | 0 | Relation rel = scan->indexRelation; |
1790 | | |
1791 | | /* |
1792 | | * _bt_readpage succeeded. Drop the lock (and maybe the pin) on |
1793 | | * so->currPos.buf in preparation for btgettuple returning tuples. |
1794 | | */ |
1795 | 0 | Assert(BTScanPosIsPinned(so->currPos)); |
1796 | 0 | _bt_drop_lock_and_maybe_pin(rel, so); |
1797 | 0 | return true; |
1798 | 0 | } |
1799 | | |
1800 | | /* There's no actually-matching data on the page in so->currPos.buf */ |
1801 | 0 | _bt_unlockbuf(scan->indexRelation, so->currPos.buf); |
1802 | | |
1803 | | /* Call _bt_readnextpage using its _bt_steppage wrapper function */ |
1804 | 0 | if (!_bt_steppage(scan, dir)) |
1805 | 0 | return false; |
1806 | | |
1807 | | /* _bt_readpage for a later page (now in so->currPos) succeeded */ |
1808 | 0 | return true; |
1809 | 0 | } |
1810 | | |
1811 | | /* |
1812 | | * _bt_readnextpage() -- Read next page containing valid data for _bt_next |
1813 | | * |
1814 | | * Caller's blkno is the next interesting page's link, taken from either the |
1815 | | * previously-saved right link or left link. lastcurrblkno is the page that |
1816 | | * was current at the point where the blkno link was saved, which we use to |
1817 | | * reason about concurrent page splits/page deletions during backwards scans. |
1818 | | * In the common case where seized=false, blkno is either so->currPos.nextPage |
1819 | | * or so->currPos.prevPage, and lastcurrblkno is so->currPos.currPage. |
1820 | | * |
1821 | | * On entry, so->currPos shouldn't be locked by caller. so->currPos.buf must |
1822 | | * be InvalidBuffer/unpinned as needed by caller (note that lastcurrblkno |
1823 | | * won't need to be read again in almost all cases). Parallel scan callers |
1824 | | * that seized the scan before calling here should pass seized=true; such a |
1825 | | * caller's blkno and lastcurrblkno arguments come from the seized scan. |
1826 | | * seized=false callers just pass us the blkno/lastcurrblkno taken from their |
1827 | | * so->currPos, which (along with so->currPos itself) can be used to end the |
1828 | | * scan. A seized=false caller's blkno can never be assumed to be the page |
1829 | | * that must be read next during a parallel scan, though. We must figure that |
1830 | | * part out for ourselves by seizing the scan (the correct page to read might |
1831 | | * already be beyond the seized=false caller's blkno during a parallel scan, |
1832 | | * unless blkno/so->currPos.nextPage/so->currPos.prevPage is already P_NONE, |
1833 | | * or unless so->currPos.moreRight/so->currPos.moreLeft is already unset). |
1834 | | * |
1835 | | * On success exit, so->currPos is updated to contain data from the next |
1836 | | * interesting page, and we return true. We hold a pin on the buffer on |
1837 | | * success exit (except during so->dropPin index scans, when we drop the pin |
1838 | | * eagerly to avoid blocking VACUUM). |
1839 | | * |
1840 | | * If there are no more matching records in the given direction, we invalidate |
1841 | | * so->currPos (while ensuring it retains no locks or pins), and return false. |
1842 | | * |
1843 | | * We always release the scan for a parallel scan caller, regardless of |
1844 | | * success or failure; we'll call _bt_parallel_release as soon as possible. |
1845 | | */ |
1846 | | static bool |
1847 | | _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, |
1848 | | BlockNumber lastcurrblkno, ScanDirection dir, bool seized) |
1849 | 0 | { |
1850 | 0 | Relation rel = scan->indexRelation; |
1851 | 0 | BTScanOpaque so = (BTScanOpaque) scan->opaque; |
1852 | |
|
1853 | 0 | Assert(so->currPos.currPage == lastcurrblkno || seized); |
1854 | 0 | Assert(!(blkno == P_NONE && seized)); |
1855 | 0 | Assert(!BTScanPosIsPinned(so->currPos)); |
1856 | | |
1857 | | /* |
1858 | | * Remember that the scan already read lastcurrblkno, a page to the left |
1859 | | * of blkno (or remember reading a page to the right, for backwards scans) |
1860 | | */ |
1861 | 0 | if (ScanDirectionIsForward(dir)) |
1862 | 0 | so->currPos.moreLeft = true; |
1863 | 0 | else |
1864 | 0 | so->currPos.moreRight = true; |
1865 | |
|
1866 | 0 | for (;;) |
1867 | 0 | { |
1868 | 0 | Page page; |
1869 | 0 | BTPageOpaque opaque; |
1870 | |
|
1871 | 0 | if (blkno == P_NONE || |
1872 | 0 | (ScanDirectionIsForward(dir) ? |
1873 | 0 | !so->currPos.moreRight : !so->currPos.moreLeft)) |
1874 | 0 | { |
1875 | | /* |
1876 | | * most recent _bt_readpage call (for lastcurrblkno) ended scan, |
1877 | | * or lastcurrblkno is a half-dead leftmost page (not read by |
1878 | | * _bt_readpage, so so->currPos doesn't describe it) |
1879 | | */ |
1880 | 0 | Assert(so->currPos.currPage == lastcurrblkno || blkno == P_NONE); |
1881 | 0 | Assert(!seized); |
1882 | 0 | BTScanPosInvalidate(so->currPos); |
1883 | 0 | _bt_parallel_done(scan); /* iff !so->needPrimScan */ |
1884 | 0 | return false; |
1885 | 0 | } |
1886 | | |
1887 | 0 | Assert(!so->needPrimScan); |
1888 | | |
1889 | | /* parallel scan must never actually visit so->currPos blkno */ |
1890 | 0 | if (!seized && scan->parallel_scan != NULL && |
1891 | 0 | !_bt_parallel_seize(scan, &blkno, &lastcurrblkno, false)) |
1892 | 0 | { |
1893 | | /* whole scan is now done (or another primitive scan required) */ |
1894 | 0 | BTScanPosInvalidate(so->currPos); |
1895 | 0 | return false; |
1896 | 0 | } |
1897 | | |
1898 | 0 | if (ScanDirectionIsForward(dir)) |
1899 | 0 | { |
1900 | | /* read blkno, but check for interrupts first */ |
1901 | 0 | CHECK_FOR_INTERRUPTS(); |
1902 | 0 | so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ); |
1903 | 0 | } |
1904 | 0 | else |
1905 | 0 | { |
1906 | | /* read blkno, avoiding race (also checks for interrupts) */ |
1907 | 0 | so->currPos.buf = _bt_lock_and_validate_left(rel, &blkno, |
1908 | 0 | lastcurrblkno); |
1909 | 0 | if (so->currPos.buf == InvalidBuffer) |
1910 | 0 | { |
1911 | | /* must have been a concurrent deletion of leftmost page */ |
1912 | 0 | BTScanPosInvalidate(so->currPos); |
1913 | 0 | _bt_parallel_done(scan); |
1914 | 0 | return false; |
1915 | 0 | } |
1916 | 0 | } |
1917 | | |
1918 | 0 | page = BufferGetPage(so->currPos.buf); |
1919 | 0 | opaque = BTPageGetOpaque(page); |
1920 | 0 | lastcurrblkno = blkno; |
1921 | 0 | if (likely(!P_IGNORE(opaque))) |
1922 | 0 | { |
1923 | | /* see if there are any matches on this page */ |
1924 | 0 | if (ScanDirectionIsForward(dir)) |
1925 | 0 | { |
1926 | | /* note that this will clear moreRight if we can stop */ |
1927 | 0 | if (_bt_readpage(scan, dir, P_FIRSTDATAKEY(opaque), seized)) |
1928 | 0 | break; |
1929 | 0 | blkno = so->currPos.nextPage; |
1930 | 0 | } |
1931 | 0 | else |
1932 | 0 | { |
1933 | | /* note that this will clear moreLeft if we can stop */ |
1934 | 0 | if (_bt_readpage(scan, dir, PageGetMaxOffsetNumber(page), seized)) |
1935 | 0 | break; |
1936 | 0 | blkno = so->currPos.prevPage; |
1937 | 0 | } |
1938 | 0 | } |
1939 | 0 | else |
1940 | 0 | { |
1941 | | /* _bt_readpage not called, so do all this for ourselves */ |
1942 | 0 | if (ScanDirectionIsForward(dir)) |
1943 | 0 | blkno = opaque->btpo_next; |
1944 | 0 | else |
1945 | 0 | blkno = opaque->btpo_prev; |
1946 | 0 | if (scan->parallel_scan != NULL) |
1947 | 0 | _bt_parallel_release(scan, blkno, lastcurrblkno); |
1948 | 0 | } |
1949 | | |
1950 | | /* no matching tuples on this page */ |
1951 | 0 | _bt_relbuf(rel, so->currPos.buf); |
1952 | 0 | seized = false; /* released by _bt_readpage (or by us) */ |
1953 | 0 | } |
1954 | | |
1955 | | /* |
1956 | | * _bt_readpage succeeded. Drop the lock (and maybe the pin) on |
1957 | | * so->currPos.buf in preparation for btgettuple returning tuples. |
1958 | | */ |
1959 | 0 | Assert(so->currPos.currPage == blkno); |
1960 | 0 | Assert(BTScanPosIsPinned(so->currPos)); |
1961 | 0 | _bt_drop_lock_and_maybe_pin(rel, so); |
1962 | |
|
1963 | 0 | return true; |
1964 | 0 | } |
1965 | | |
1966 | | /* |
1967 | | * _bt_lock_and_validate_left() -- lock caller's left sibling blkno, |
1968 | | * recovering from concurrent page splits/page deletions when necessary |
1969 | | * |
1970 | | * Called during backwards scans, to deal with their unique concurrency rules. |
1971 | | * |
1972 | | * blkno points to the block number of the page that we expect to move the |
1973 | | * scan to. We'll successfully move the scan there when we find that its |
1974 | | * right sibling link still points to lastcurrblkno (the page we just read). |
1975 | | * Otherwise, we have to figure out which page is the correct one for the scan |
1976 | | * to now read the hard way, reasoning about concurrent splits and deletions. |
1977 | | * See nbtree/README. |
1978 | | * |
1979 | | * On return, we have both a pin and a read lock on the returned page, whose |
1980 | | * block number will be set in *blkno. Returns InvalidBuffer if there is no |
1981 | | * page to the left (no lock or pin is held in that case). |
1982 | | * |
1983 | | * It is possible for the returned leaf page to be half-dead; caller must |
1984 | | * check that condition and step left again when required. |
1985 | | */ |
1986 | | static Buffer |
1987 | | _bt_lock_and_validate_left(Relation rel, BlockNumber *blkno, |
1988 | | BlockNumber lastcurrblkno) |
1989 | 0 | { |
1990 | 0 | BlockNumber origblkno = *blkno; /* detects circular links */ |
1991 | |
|
1992 | | #ifdef USE_INJECTION_POINTS |
1993 | | if (!IsCatalogRelation(rel)) |
1994 | | INJECTION_POINT("nbtree-walk-left", NULL); |
1995 | | #endif |
1996 | |
|
1997 | 0 | for (;;) |
1998 | 0 | { |
1999 | 0 | Buffer buf; |
2000 | 0 | Page page; |
2001 | 0 | BTPageOpaque opaque; |
2002 | 0 | int tries; |
2003 | | |
2004 | | /* check for interrupts while we're not holding any buffer lock */ |
2005 | 0 | CHECK_FOR_INTERRUPTS(); |
2006 | 0 | buf = _bt_getbuf(rel, *blkno, BT_READ); |
2007 | 0 | page = BufferGetPage(buf); |
2008 | 0 | opaque = BTPageGetOpaque(page); |
2009 | | |
2010 | | /* |
2011 | | * If this isn't the page we want, walk right till we find what we |
2012 | | * want --- but go no more than four hops (an arbitrary limit). If we |
2013 | | * don't find the correct page by then, the most likely bet is that |
2014 | | * lastcurrblkno got deleted and isn't in the sibling chain at all |
2015 | | * anymore, not that its left sibling got split more than four times. |
2016 | | * |
2017 | | * Note that it is correct to test P_ISDELETED not P_IGNORE here, |
2018 | | * because half-dead pages are still in the sibling chain. |
2019 | | */ |
2020 | 0 | tries = 0; |
2021 | 0 | for (;;) |
2022 | 0 | { |
2023 | 0 | if (likely(!P_ISDELETED(opaque) && |
2024 | 0 | opaque->btpo_next == lastcurrblkno)) |
2025 | 0 | { |
2026 | | /* Found desired page, return it */ |
2027 | 0 | return buf; |
2028 | 0 | } |
2029 | 0 | if (P_RIGHTMOST(opaque) || ++tries > 4) |
2030 | 0 | break; |
2031 | | |
2032 | | #ifdef USE_INJECTION_POINTS |
2033 | | if (!IsCatalogRelation(rel)) |
2034 | | INJECTION_POINT("nbtree-walk-left-step-right", NULL); |
2035 | | #endif |
2036 | | |
2037 | | /* step right */ |
2038 | 0 | *blkno = opaque->btpo_next; |
2039 | 0 | buf = _bt_relandgetbuf(rel, buf, *blkno, BT_READ); |
2040 | 0 | page = BufferGetPage(buf); |
2041 | 0 | opaque = BTPageGetOpaque(page); |
2042 | 0 | } |
2043 | | |
2044 | | /* |
2045 | | * Return to the original page (usually the page most recently read by |
2046 | | * _bt_readpage, which is passed by caller as lastcurrblkno) to see |
2047 | | * what's up with its prev sibling link |
2048 | | */ |
2049 | 0 | buf = _bt_relandgetbuf(rel, buf, lastcurrblkno, BT_READ); |
2050 | 0 | page = BufferGetPage(buf); |
2051 | 0 | opaque = BTPageGetOpaque(page); |
2052 | 0 | if (P_ISDELETED(opaque)) |
2053 | 0 | { |
2054 | | #ifdef USE_INJECTION_POINTS |
2055 | | if (!IsCatalogRelation(rel)) |
2056 | | INJECTION_POINT("nbtree-walk-left-deleted", NULL); |
2057 | | #endif |
2058 | | |
2059 | | /* |
2060 | | * It was deleted. Move right to first nondeleted page (there |
2061 | | * must be one); that is the page that has acquired the deleted |
2062 | | * one's keyspace, so stepping left from it will take us where we |
2063 | | * want to be. |
2064 | | */ |
2065 | 0 | for (;;) |
2066 | 0 | { |
2067 | 0 | if (P_RIGHTMOST(opaque)) |
2068 | 0 | elog(ERROR, "fell off the end of index \"%s\"", |
2069 | 0 | RelationGetRelationName(rel)); |
2070 | 0 | lastcurrblkno = opaque->btpo_next; |
2071 | 0 | buf = _bt_relandgetbuf(rel, buf, lastcurrblkno, BT_READ); |
2072 | 0 | page = BufferGetPage(buf); |
2073 | 0 | opaque = BTPageGetOpaque(page); |
2074 | 0 | if (!P_ISDELETED(opaque)) |
2075 | 0 | break; |
2076 | 0 | } |
2077 | 0 | } |
2078 | 0 | else |
2079 | 0 | { |
2080 | | /* |
2081 | | * Original lastcurrblkno wasn't deleted; the explanation had |
2082 | | * better be that the page to the left got split or deleted. |
2083 | | * Without this check, we risk going into an infinite loop. |
2084 | | */ |
2085 | 0 | if (opaque->btpo_prev == origblkno) |
2086 | 0 | elog(ERROR, "could not find left sibling of block %u in index \"%s\"", |
2087 | 0 | lastcurrblkno, RelationGetRelationName(rel)); |
2088 | | /* Okay to try again, since left sibling link changed */ |
2089 | 0 | } |
2090 | | |
2091 | | /* |
2092 | | * Original lastcurrblkno from caller was concurrently deleted (could |
2093 | | * also have been a great many concurrent left sibling page splits). |
2094 | | * Found a non-deleted page that should now act as our lastcurrblkno. |
2095 | | */ |
2096 | 0 | if (P_LEFTMOST(opaque)) |
2097 | 0 | { |
2098 | | /* New lastcurrblkno has no left sibling (concurrently deleted) */ |
2099 | 0 | _bt_relbuf(rel, buf); |
2100 | 0 | break; |
2101 | 0 | } |
2102 | | |
2103 | | /* Start from scratch with new lastcurrblkno's blkno/prev link */ |
2104 | 0 | *blkno = origblkno = opaque->btpo_prev; |
2105 | 0 | _bt_relbuf(rel, buf); |
2106 | |
|
2107 | | #ifdef USE_INJECTION_POINTS |
2108 | | if (!IsCatalogRelation(rel)) |
2109 | | INJECTION_POINT("nbtree-walk-left-restart", NULL); |
2110 | | #endif |
2111 | 0 | } |
2112 | | |
2113 | 0 | return InvalidBuffer; |
2114 | 0 | } |
2115 | | |
2116 | | /* |
2117 | | * _bt_get_endpoint() -- Find the first or last page on a given tree level |
2118 | | * |
2119 | | * If the index is empty, we will return InvalidBuffer; any other failure |
2120 | | * condition causes ereport(). We will not return a dead page. |
2121 | | * |
2122 | | * The returned buffer is pinned and read-locked. |
2123 | | */ |
2124 | | Buffer |
2125 | | _bt_get_endpoint(Relation rel, uint32 level, bool rightmost) |
2126 | 0 | { |
2127 | 0 | Buffer buf; |
2128 | 0 | Page page; |
2129 | 0 | BTPageOpaque opaque; |
2130 | 0 | OffsetNumber offnum; |
2131 | 0 | BlockNumber blkno; |
2132 | 0 | IndexTuple itup; |
2133 | | |
2134 | | /* |
2135 | | * If we are looking for a leaf page, okay to descend from fast root; |
2136 | | * otherwise better descend from true root. (There is no point in being |
2137 | | * smarter about intermediate levels.) |
2138 | | */ |
2139 | 0 | if (level == 0) |
2140 | 0 | buf = _bt_getroot(rel, NULL, BT_READ); |
2141 | 0 | else |
2142 | 0 | buf = _bt_gettrueroot(rel); |
2143 | |
|
2144 | 0 | if (!BufferIsValid(buf)) |
2145 | 0 | return InvalidBuffer; |
2146 | | |
2147 | 0 | page = BufferGetPage(buf); |
2148 | 0 | opaque = BTPageGetOpaque(page); |
2149 | |
|
2150 | 0 | for (;;) |
2151 | 0 | { |
2152 | | /* |
2153 | | * If we landed on a deleted page, step right to find a live page |
2154 | | * (there must be one). Also, if we want the rightmost page, step |
2155 | | * right if needed to get to it (this could happen if the page split |
2156 | | * since we obtained a pointer to it). |
2157 | | */ |
2158 | 0 | while (P_IGNORE(opaque) || |
2159 | 0 | (rightmost && !P_RIGHTMOST(opaque))) |
2160 | 0 | { |
2161 | 0 | blkno = opaque->btpo_next; |
2162 | 0 | if (blkno == P_NONE) |
2163 | 0 | elog(ERROR, "fell off the end of index \"%s\"", |
2164 | 0 | RelationGetRelationName(rel)); |
2165 | 0 | buf = _bt_relandgetbuf(rel, buf, blkno, BT_READ); |
2166 | 0 | page = BufferGetPage(buf); |
2167 | 0 | opaque = BTPageGetOpaque(page); |
2168 | 0 | } |
2169 | | |
2170 | | /* Done? */ |
2171 | 0 | if (opaque->btpo_level == level) |
2172 | 0 | break; |
2173 | 0 | if (opaque->btpo_level < level) |
2174 | 0 | ereport(ERROR, |
2175 | 0 | (errcode(ERRCODE_INDEX_CORRUPTED), |
2176 | 0 | errmsg_internal("btree level %u not found in index \"%s\"", |
2177 | 0 | level, RelationGetRelationName(rel)))); |
2178 | | |
2179 | | /* Descend to leftmost or rightmost child page */ |
2180 | 0 | if (rightmost) |
2181 | 0 | offnum = PageGetMaxOffsetNumber(page); |
2182 | 0 | else |
2183 | 0 | offnum = P_FIRSTDATAKEY(opaque); |
2184 | |
|
2185 | 0 | if (offnum < 1 || offnum > PageGetMaxOffsetNumber(page)) |
2186 | 0 | elog(PANIC, "offnum out of range"); |
2187 | | |
2188 | 0 | itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum)); |
2189 | 0 | blkno = BTreeTupleGetDownLink(itup); |
2190 | |
|
2191 | 0 | buf = _bt_relandgetbuf(rel, buf, blkno, BT_READ); |
2192 | 0 | page = BufferGetPage(buf); |
2193 | 0 | opaque = BTPageGetOpaque(page); |
2194 | 0 | } |
2195 | | |
2196 | 0 | return buf; |
2197 | 0 | } |
2198 | | |
2199 | | /* |
2200 | | * _bt_endpoint() -- Find the first or last page in the index, and scan |
2201 | | * from there to the first key satisfying all the quals. |
2202 | | * |
2203 | | * This is used by _bt_first() to set up a scan when we've determined |
2204 | | * that the scan must start at the beginning or end of the index (for |
2205 | | * a forward or backward scan respectively). |
2206 | | * |
2207 | | * Parallel scan callers must have seized the scan before calling here. |
2208 | | * Exit conditions are the same as for _bt_first(). |
2209 | | */ |
2210 | | static bool |
2211 | | _bt_endpoint(IndexScanDesc scan, ScanDirection dir) |
2212 | 0 | { |
2213 | 0 | Relation rel = scan->indexRelation; |
2214 | 0 | BTScanOpaque so = (BTScanOpaque) scan->opaque; |
2215 | 0 | Page page; |
2216 | 0 | BTPageOpaque opaque; |
2217 | 0 | OffsetNumber start; |
2218 | |
|
2219 | 0 | Assert(!BTScanPosIsValid(so->currPos)); |
2220 | 0 | Assert(!so->needPrimScan); |
2221 | | |
2222 | | /* |
2223 | | * Scan down to the leftmost or rightmost leaf page. This is a simplified |
2224 | | * version of _bt_search(). |
2225 | | */ |
2226 | 0 | so->currPos.buf = _bt_get_endpoint(rel, 0, ScanDirectionIsBackward(dir)); |
2227 | |
|
2228 | 0 | if (!BufferIsValid(so->currPos.buf)) |
2229 | 0 | { |
2230 | | #ifdef USE_INJECTION_POINTS |
2231 | | if (!IsCatalogRelation(rel)) |
2232 | | INJECTION_POINT("nbtree-endpoint-empty", NULL); |
2233 | | #endif |
2234 | | |
2235 | | /* |
2236 | | * Empty index. Lock the whole relation using the approach explained |
2237 | | * at the same point in the _bt_first path. |
2238 | | */ |
2239 | 0 | if (IsolationIsSerializable()) |
2240 | 0 | { |
2241 | 0 | PredicateLockRelation(rel, scan->xs_snapshot); |
2242 | 0 | so->currPos.buf = _bt_get_endpoint(rel, 0, |
2243 | 0 | ScanDirectionIsBackward(dir)); |
2244 | 0 | } |
2245 | |
|
2246 | 0 | if (!BufferIsValid(so->currPos.buf)) |
2247 | 0 | { |
2248 | 0 | _bt_parallel_done(scan); |
2249 | 0 | return false; |
2250 | 0 | } |
2251 | 0 | } |
2252 | | |
2253 | 0 | page = BufferGetPage(so->currPos.buf); |
2254 | 0 | opaque = BTPageGetOpaque(page); |
2255 | 0 | Assert(P_ISLEAF(opaque)); |
2256 | |
|
2257 | 0 | if (ScanDirectionIsForward(dir)) |
2258 | 0 | { |
2259 | | /* There could be dead pages to the left, so not this: */ |
2260 | | /* Assert(P_LEFTMOST(opaque)); */ |
2261 | |
|
2262 | 0 | start = P_FIRSTDATAKEY(opaque); |
2263 | 0 | } |
2264 | 0 | else if (ScanDirectionIsBackward(dir)) |
2265 | 0 | { |
2266 | 0 | Assert(P_RIGHTMOST(opaque)); |
2267 | |
|
2268 | 0 | start = PageGetMaxOffsetNumber(page); |
2269 | 0 | } |
2270 | 0 | else |
2271 | 0 | { |
2272 | 0 | elog(ERROR, "invalid scan direction: %d", (int) dir); |
2273 | 0 | start = 0; /* keep compiler quiet */ |
2274 | 0 | } |
2275 | | |
2276 | | /* |
2277 | | * Now load data from the first page of the scan. |
2278 | | */ |
2279 | 0 | if (!_bt_readfirstpage(scan, start, dir)) |
2280 | 0 | return false; |
2281 | | |
2282 | 0 | _bt_returnitem(scan, so); |
2283 | | return true; |
2284 | 0 | } |