1 : /*-------------------------------------------------------------------------
2 : *
3 : * gindatapage.c
4 : * page utilities routines for the postgres inverted index access method.
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * IDENTIFICATION
11 : * $PostgreSQL: pgsql/src/backend/access/gin/gindatapage.c,v 1.8 2007/11/15 21:14:31 momjian Exp $
12 : *-------------------------------------------------------------------------
13 : */
14 :
15 : #include "postgres.h"
16 : #include "access/gin.h"
17 :
18 : int
19 : compareItemPointers(ItemPointer a, ItemPointer b)
20 37005 : {
21 37005 : if (GinItemPointerGetBlockNumber(a) == GinItemPointerGetBlockNumber(b))
22 : {
23 18569 : if (GinItemPointerGetOffsetNumber(a) == GinItemPointerGetOffsetNumber(b))
24 2141 : return 0;
25 16428 : return (GinItemPointerGetOffsetNumber(a) > GinItemPointerGetOffsetNumber(b)) ? 1 : -1;
26 : }
27 :
28 18436 : return (GinItemPointerGetBlockNumber(a) > GinItemPointerGetBlockNumber(b)) ? 1 : -1;
29 : }
30 :
31 : /*
32 : * Merge two ordered array of itempointer
33 : */
34 : void
35 : MergeItemPointers(ItemPointerData *dst, ItemPointerData *a, uint32 na, ItemPointerData *b, uint32 nb)
36 1293 : {
37 1293 : ItemPointerData *dptr = dst;
38 1293 : ItemPointerData *aptr = a,
39 1293 : *bptr = b;
40 :
41 3879 : while (aptr - a < na && bptr - b < nb)
42 : {
43 1293 : if (compareItemPointers(aptr, bptr) > 0)
44 0 : *dptr++ = *bptr++;
45 : else
46 1293 : *dptr++ = *aptr++;
47 : }
48 :
49 1293 : while (aptr - a < na)
50 0 : *dptr++ = *aptr++;
51 :
52 28033 : while (bptr - b < nb)
53 26740 : *dptr++ = *bptr++;
54 1293 : }
55 :
56 : /*
57 : * Checks, should we move to right link...
58 : * Compares inserting itemp pointer with right bound of current page
59 : */
60 : static bool
61 : dataIsMoveRight(GinBtree btree, Page page)
62 0 : {
63 0 : ItemPointer iptr = GinDataPageGetRightBound(page);
64 :
65 0 : if (GinPageRightMost(page))
66 0 : return FALSE;
67 :
68 0 : return (compareItemPointers(btree->items + btree->curitem, iptr) > 0) ? TRUE : FALSE;
69 : }
70 :
71 : /*
72 : * Find correct PostingItem in non-leaf page. It supposed that page
73 : * correctly chosen and searching value SHOULD be on page
74 : */
75 : static BlockNumber
76 : dataLocateItem(GinBtree btree, GinBtreeStack *stack)
77 0 : {
78 : OffsetNumber low,
79 : high,
80 : maxoff;
81 0 : PostingItem *pitem = NULL;
82 : int result;
83 0 : Page page = BufferGetPage(stack->buffer);
84 :
85 : Assert(!GinPageIsLeaf(page));
86 : Assert(GinPageIsData(page));
87 :
88 0 : if (btree->fullScan)
89 : {
90 0 : stack->off = FirstOffsetNumber;
91 0 : stack->predictNumber *= GinPageGetOpaque(page)->maxoff;
92 0 : return btree->getLeftMostPage(btree, page);
93 : }
94 :
95 0 : low = FirstOffsetNumber;
96 0 : maxoff = high = GinPageGetOpaque(page)->maxoff;
97 : Assert(high >= low);
98 :
99 0 : high++;
100 :
101 0 : while (high > low)
102 : {
103 0 : OffsetNumber mid = low + ((high - low) / 2);
104 :
105 0 : pitem = (PostingItem *) GinDataPageGetItem(page, mid);
106 :
107 0 : if (mid == maxoff)
108 :
109 : /*
110 : * Right infinity, page already correctly chosen with a help of
111 : * dataIsMoveRight
112 : */
113 0 : result = -1;
114 : else
115 : {
116 0 : pitem = (PostingItem *) GinDataPageGetItem(page, mid);
117 0 : result = compareItemPointers(btree->items + btree->curitem, &(pitem->key));
118 : }
119 :
120 0 : if (result == 0)
121 : {
122 0 : stack->off = mid;
123 0 : return PostingItemGetBlockNumber(pitem);
124 : }
125 0 : else if (result > 0)
126 0 : low = mid + 1;
127 : else
128 0 : high = mid;
129 : }
130 :
131 : Assert(high >= FirstOffsetNumber && high <= maxoff);
132 :
133 0 : stack->off = high;
134 0 : pitem = (PostingItem *) GinDataPageGetItem(page, high);
135 0 : return PostingItemGetBlockNumber(pitem);
136 : }
137 :
138 : /*
139 : * Searches correct position for value on leaf page.
140 : * Page should be correctly chosen.
141 : * Returns true if value found on page.
142 : */
143 : static bool
144 : dataLocateLeafItem(GinBtree btree, GinBtreeStack *stack)
145 19 : {
146 19 : Page page = BufferGetPage(stack->buffer);
147 : OffsetNumber low,
148 : high;
149 : int result;
150 :
151 : Assert(GinPageIsLeaf(page));
152 : Assert(GinPageIsData(page));
153 :
154 19 : if (btree->fullScan)
155 : {
156 0 : stack->off = FirstOffsetNumber;
157 0 : return TRUE;
158 : }
159 :
160 19 : low = FirstOffsetNumber;
161 19 : high = GinPageGetOpaque(page)->maxoff;
162 :
163 19 : if (high < low)
164 : {
165 0 : stack->off = FirstOffsetNumber;
166 0 : return false;
167 : }
168 :
169 19 : high++;
170 :
171 57 : while (high > low)
172 : {
173 19 : OffsetNumber mid = low + ((high - low) / 2);
174 :
175 19 : result = compareItemPointers(btree->items + btree->curitem, (ItemPointer) GinDataPageGetItem(page, mid));
176 :
177 19 : if (result == 0)
178 : {
179 0 : stack->off = mid;
180 0 : return true;
181 : }
182 19 : else if (result > 0)
183 19 : low = mid + 1;
184 : else
185 0 : high = mid;
186 : }
187 :
188 19 : stack->off = high;
189 19 : return false;
190 : }
191 :
192 : /*
193 : * Finds links to blkno on non-leaf page, returns
194 : * offset of PostingItem
195 : */
196 : static OffsetNumber
197 : dataFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff)
198 0 : {
199 : OffsetNumber i,
200 0 : maxoff = GinPageGetOpaque(page)->maxoff;
201 : PostingItem *pitem;
202 :
203 : Assert(!GinPageIsLeaf(page));
204 : Assert(GinPageIsData(page));
205 :
206 : /* if page isn't changed, we returns storedOff */
207 0 : if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
208 : {
209 0 : pitem = (PostingItem *) GinDataPageGetItem(page, storedOff);
210 0 : if (PostingItemGetBlockNumber(pitem) == blkno)
211 0 : return storedOff;
212 :
213 : /*
214 : * we hope, that needed pointer goes to right. It's true if there
215 : * wasn't a deletion
216 : */
217 0 : for (i = storedOff + 1; i <= maxoff; i++)
218 : {
219 0 : pitem = (PostingItem *) GinDataPageGetItem(page, i);
220 0 : if (PostingItemGetBlockNumber(pitem) == blkno)
221 0 : return i;
222 : }
223 :
224 0 : maxoff = storedOff - 1;
225 : }
226 :
227 : /* last chance */
228 0 : for (i = FirstOffsetNumber; i <= maxoff; i++)
229 : {
230 0 : pitem = (PostingItem *) GinDataPageGetItem(page, i);
231 0 : if (PostingItemGetBlockNumber(pitem) == blkno)
232 0 : return i;
233 : }
234 :
235 0 : return InvalidOffsetNumber;
236 : }
237 :
238 : /*
239 : * returns blkno of leftmost child
240 : */
241 : static BlockNumber
242 : dataGetLeftMostPage(GinBtree btree, Page page)
243 0 : {
244 : PostingItem *pitem;
245 :
246 : Assert(!GinPageIsLeaf(page));
247 : Assert(GinPageIsData(page));
248 : Assert(GinPageGetOpaque(page)->maxoff >= FirstOffsetNumber);
249 :
250 0 : pitem = (PostingItem *) GinDataPageGetItem(page, FirstOffsetNumber);
251 0 : return PostingItemGetBlockNumber(pitem);
252 : }
253 :
254 : /*
255 : * add ItemPointer or PostingItem to page. data should point to
256 : * correct value! depending on leaf or non-leaf page
257 : */
258 : void
259 : GinDataPageAddItem(Page page, void *data, OffsetNumber offset)
260 1730 : {
261 1730 : OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
262 : char *ptr;
263 :
264 1730 : if (offset == InvalidOffsetNumber)
265 : {
266 0 : ptr = GinDataPageGetItem(page, maxoff + 1);
267 : }
268 : else
269 : {
270 1730 : ptr = GinDataPageGetItem(page, offset);
271 1730 : if (maxoff + 1 - offset != 0)
272 0 : memmove(ptr + GinSizeOfItem(page), ptr, (maxoff - offset + 1) * GinSizeOfItem(page));
273 : }
274 1730 : memcpy(ptr, data, GinSizeOfItem(page));
275 :
276 1730 : GinPageGetOpaque(page)->maxoff++;
277 1730 : }
278 :
279 : /*
280 : * Deletes posting item from non-leaf page
281 : */
282 : void
283 : PageDeletePostingItem(Page page, OffsetNumber offset)
284 0 : {
285 0 : OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
286 :
287 : Assert(!GinPageIsLeaf(page));
288 : Assert(offset >= FirstOffsetNumber && offset <= maxoff);
289 :
290 0 : if (offset != maxoff)
291 0 : memmove(GinDataPageGetItem(page, offset), GinDataPageGetItem(page, offset + 1),
292 : sizeof(PostingItem) * (maxoff - offset));
293 :
294 0 : GinPageGetOpaque(page)->maxoff--;
295 0 : }
296 :
297 : /*
298 : * checks space to install new value,
299 : * item pointer never deletes!
300 : */
301 : static bool
302 : dataIsEnoughSpace(GinBtree btree, Buffer buf, OffsetNumber off)
303 19 : {
304 19 : Page page = BufferGetPage(buf);
305 :
306 : Assert(GinPageIsData(page));
307 : Assert(!btree->isDelete);
308 :
309 19 : if (GinPageIsLeaf(page))
310 : {
311 19 : if (GinPageRightMost(page) && off > GinPageGetOpaque(page)->maxoff)
312 : {
313 19 : if ((btree->nitem - btree->curitem) * sizeof(ItemPointerData) <= GinDataPageGetFreeSpace(page))
314 19 : return true;
315 : }
316 0 : else if (sizeof(ItemPointerData) <= GinDataPageGetFreeSpace(page))
317 0 : return true;
318 : }
319 0 : else if (sizeof(PostingItem) <= GinDataPageGetFreeSpace(page))
320 0 : return true;
321 :
322 0 : return false;
323 : }
324 :
325 : /*
326 : * In case of previous split update old child blkno to
327 : * new right page
328 : * item pointer never deletes!
329 : */
330 : static BlockNumber
331 : dataPrepareData(GinBtree btree, Page page, OffsetNumber off)
332 19 : {
333 19 : BlockNumber ret = InvalidBlockNumber;
334 :
335 : Assert(GinPageIsData(page));
336 :
337 19 : if (!GinPageIsLeaf(page) && btree->rightblkno != InvalidBlockNumber)
338 : {
339 0 : PostingItem *pitem = (PostingItem *) GinDataPageGetItem(page, off);
340 :
341 0 : PostingItemSetBlockNumber(pitem, btree->rightblkno);
342 0 : ret = btree->rightblkno;
343 : }
344 :
345 19 : btree->rightblkno = InvalidBlockNumber;
346 :
347 19 : return ret;
348 : }
349 :
350 : /*
351 : * Places keys to page and fills WAL record. In case leaf page and
352 : * build mode puts all ItemPointers to page.
353 : */
354 : static void
355 : dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prdata)
356 19 : {
357 19 : Page page = BufferGetPage(buf);
358 : static XLogRecData rdata[3];
359 19 : int sizeofitem = GinSizeOfItem(page);
360 : static ginxlogInsert data;
361 19 : int cnt = 0;
362 :
363 19 : *prdata = rdata;
364 : Assert(GinPageIsData(page));
365 :
366 19 : data.updateBlkno = dataPrepareData(btree, page, off);
367 :
368 19 : data.node = btree->index->rd_node;
369 19 : data.blkno = BufferGetBlockNumber(buf);
370 19 : data.offset = off;
371 19 : data.nitem = 1;
372 19 : data.isDelete = FALSE;
373 19 : data.isData = TRUE;
374 19 : data.isLeaf = GinPageIsLeaf(page) ? TRUE : FALSE;
375 :
376 : /*
377 : * Prevent full page write if child's split occurs. That is needed to
378 : * remove incomplete splits while replaying WAL
379 : *
380 : * data.updateBlkno contains new block number (of newly created right
381 : * page) for recently splited page.
382 : */
383 19 : if (data.updateBlkno == InvalidBlockNumber)
384 : {
385 19 : rdata[0].buffer = buf;
386 19 : rdata[0].buffer_std = FALSE;
387 19 : rdata[0].data = NULL;
388 19 : rdata[0].len = 0;
389 19 : rdata[0].next = &rdata[1];
390 19 : cnt++;
391 : }
392 :
393 19 : rdata[cnt].buffer = InvalidBuffer;
394 19 : rdata[cnt].data = (char *) &data;
395 19 : rdata[cnt].len = sizeof(ginxlogInsert);
396 19 : rdata[cnt].next = &rdata[cnt + 1];
397 19 : cnt++;
398 :
399 19 : rdata[cnt].buffer = InvalidBuffer;
400 19 : rdata[cnt].data = (GinPageIsLeaf(page)) ? ((char *) (btree->items + btree->curitem)) : ((char *) &(btree->pitem));
401 19 : rdata[cnt].len = sizeofitem;
402 19 : rdata[cnt].next = NULL;
403 :
404 19 : if (GinPageIsLeaf(page))
405 : {
406 38 : if (GinPageRightMost(page) && off > GinPageGetOpaque(page)->maxoff)
407 : {
408 : /* usually, create index... */
409 19 : uint32 savedPos = btree->curitem;
410 :
411 1768 : while (btree->curitem < btree->nitem)
412 : {
413 1730 : GinDataPageAddItem(page, btree->items + btree->curitem, off);
414 1730 : off++;
415 1730 : btree->curitem++;
416 : }
417 19 : data.nitem = btree->curitem - savedPos;
418 19 : rdata[cnt].len = sizeofitem * data.nitem;
419 : }
420 : else
421 : {
422 0 : GinDataPageAddItem(page, btree->items + btree->curitem, off);
423 0 : btree->curitem++;
424 : }
425 : }
426 : else
427 0 : GinDataPageAddItem(page, &(btree->pitem), off);
428 19 : }
429 :
430 : /*
431 : * split page and fills WAL record. original buffer(lbuf) leaves untouched,
432 : * returns shadow page of lbuf filled new data. In leaf page and build mode puts all
433 : * ItemPointers to pages. Also, in build mode splits data by way to full fulled
434 : * left page
435 : */
436 : static Page
437 : dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRecData **prdata)
438 0 : {
439 : static ginxlogSplit data;
440 : static XLogRecData rdata[4];
441 : static char vector[2 * BLCKSZ];
442 : char *ptr;
443 : OffsetNumber separator;
444 : ItemPointer bound;
445 0 : Page lpage = GinPageGetCopyPage(BufferGetPage(lbuf));
446 0 : ItemPointerData oldbound = *GinDataPageGetRightBound(lpage);
447 0 : int sizeofitem = GinSizeOfItem(lpage);
448 0 : OffsetNumber maxoff = GinPageGetOpaque(lpage)->maxoff;
449 0 : Page rpage = BufferGetPage(rbuf);
450 0 : Size pageSize = PageGetPageSize(lpage);
451 : Size freeSpace;
452 0 : uint32 nCopied = 1;
453 :
454 0 : GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize);
455 0 : freeSpace = GinDataPageGetFreeSpace(rpage);
456 :
457 0 : *prdata = rdata;
458 0 : data.leftChildBlkno = (GinPageIsLeaf(lpage)) ?
459 : InvalidOffsetNumber : PostingItemGetBlockNumber(&(btree->pitem));
460 0 : data.updateBlkno = dataPrepareData(btree, lpage, off);
461 :
462 0 : memcpy(vector, GinDataPageGetItem(lpage, FirstOffsetNumber),
463 : maxoff * sizeofitem);
464 :
465 0 : if (GinPageIsLeaf(lpage) && GinPageRightMost(lpage) && off > GinPageGetOpaque(lpage)->maxoff)
466 : {
467 0 : nCopied = 0;
468 0 : while (btree->curitem < btree->nitem && maxoff * sizeof(ItemPointerData) < 2 * (freeSpace - sizeof(ItemPointerData)))
469 : {
470 0 : memcpy(vector + maxoff * sizeof(ItemPointerData), btree->items + btree->curitem,
471 : sizeof(ItemPointerData));
472 0 : maxoff++;
473 0 : nCopied++;
474 0 : btree->curitem++;
475 : }
476 : }
477 : else
478 : {
479 0 : ptr = vector + (off - 1) * sizeofitem;
480 0 : if (maxoff + 1 - off != 0)
481 0 : memmove(ptr + sizeofitem, ptr, (maxoff - off + 1) * sizeofitem);
482 0 : if (GinPageIsLeaf(lpage))
483 : {
484 0 : memcpy(ptr, btree->items + btree->curitem, sizeofitem);
485 0 : btree->curitem++;
486 : }
487 : else
488 0 : memcpy(ptr, &(btree->pitem), sizeofitem);
489 :
490 0 : maxoff++;
491 : }
492 :
493 : /*
494 : * we suppose that during index creation table scaned from begin to end,
495 : * so ItemPointers are monotonically increased..
496 : */
497 0 : if (btree->isBuild && GinPageRightMost(lpage))
498 0 : separator = freeSpace / sizeofitem;
499 : else
500 0 : separator = maxoff / 2;
501 :
502 0 : GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize);
503 0 : GinInitPage(lpage, GinPageGetOpaque(rpage)->flags, pageSize);
504 :
505 0 : memcpy(GinDataPageGetItem(lpage, FirstOffsetNumber), vector, separator * sizeofitem);
506 0 : GinPageGetOpaque(lpage)->maxoff = separator;
507 0 : memcpy(GinDataPageGetItem(rpage, FirstOffsetNumber),
508 : vector + separator * sizeofitem, (maxoff - separator) * sizeofitem);
509 0 : GinPageGetOpaque(rpage)->maxoff = maxoff - separator;
510 :
511 0 : PostingItemSetBlockNumber(&(btree->pitem), BufferGetBlockNumber(lbuf));
512 0 : if (GinPageIsLeaf(lpage))
513 0 : btree->pitem.key = *(ItemPointerData *) GinDataPageGetItem(lpage,
514 : GinPageGetOpaque(lpage)->maxoff);
515 : else
516 0 : btree->pitem.key = ((PostingItem *) GinDataPageGetItem(lpage,
517 : GinPageGetOpaque(lpage)->maxoff))->key;
518 0 : btree->rightblkno = BufferGetBlockNumber(rbuf);
519 :
520 : /* set up right bound for left page */
521 0 : bound = GinDataPageGetRightBound(lpage);
522 0 : *bound = btree->pitem.key;
523 :
524 : /* set up right bound for right page */
525 0 : bound = GinDataPageGetRightBound(rpage);
526 0 : *bound = oldbound;
527 :
528 0 : data.node = btree->index->rd_node;
529 0 : data.rootBlkno = InvalidBlockNumber;
530 0 : data.lblkno = BufferGetBlockNumber(lbuf);
531 0 : data.rblkno = BufferGetBlockNumber(rbuf);
532 0 : data.separator = separator;
533 0 : data.nitem = maxoff;
534 0 : data.isData = TRUE;
535 0 : data.isLeaf = GinPageIsLeaf(lpage) ? TRUE : FALSE;
536 0 : data.isRootSplit = FALSE;
537 0 : data.rightbound = oldbound;
538 :
539 0 : rdata[0].buffer = InvalidBuffer;
540 0 : rdata[0].data = (char *) &data;
541 0 : rdata[0].len = sizeof(ginxlogSplit);
542 0 : rdata[0].next = &rdata[1];
543 :
544 0 : rdata[1].buffer = InvalidBuffer;
545 0 : rdata[1].data = vector;
546 0 : rdata[1].len = MAXALIGN(maxoff * sizeofitem);
547 0 : rdata[1].next = NULL;
548 :
549 0 : return lpage;
550 : }
551 :
552 : /*
553 : * Fills new root by right bound values from child.
554 : * Also called from ginxlog, should not use btree
555 : */
556 : void
557 : dataFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf)
558 0 : {
559 0 : Page page = BufferGetPage(root),
560 0 : lpage = BufferGetPage(lbuf),
561 0 : rpage = BufferGetPage(rbuf);
562 : PostingItem li,
563 : ri;
564 :
565 0 : li.key = *GinDataPageGetRightBound(lpage);
566 0 : PostingItemSetBlockNumber(&li, BufferGetBlockNumber(lbuf));
567 0 : GinDataPageAddItem(page, &li, InvalidOffsetNumber);
568 :
569 0 : ri.key = *GinDataPageGetRightBound(rpage);
570 0 : PostingItemSetBlockNumber(&ri, BufferGetBlockNumber(rbuf));
571 0 : GinDataPageAddItem(page, &ri, InvalidOffsetNumber);
572 0 : }
573 :
574 : void
575 : prepareDataScan(GinBtree btree, Relation index)
576 23 : {
577 23 : memset(btree, 0, sizeof(GinBtreeData));
578 23 : btree->index = index;
579 23 : btree->isMoveRight = dataIsMoveRight;
580 23 : btree->findChildPage = dataLocateItem;
581 23 : btree->findItem = dataLocateLeafItem;
582 23 : btree->findChildPtr = dataFindChildPtr;
583 23 : btree->getLeftMostPage = dataGetLeftMostPage;
584 23 : btree->isEnoughSpace = dataIsEnoughSpace;
585 23 : btree->placeToPage = dataPlaceToPage;
586 23 : btree->splitPage = dataSplitPage;
587 23 : btree->fillRoot = dataFillRoot;
588 :
589 23 : btree->searchMode = FALSE;
590 23 : btree->isDelete = FALSE;
591 23 : btree->fullScan = FALSE;
592 23 : btree->isBuild = FALSE;
593 23 : }
594 :
595 : GinPostingTreeScan *
596 : prepareScanPostingTree(Relation index, BlockNumber rootBlkno, bool searchMode)
597 23 : {
598 23 : GinPostingTreeScan *gdi = (GinPostingTreeScan *) palloc0(sizeof(GinPostingTreeScan));
599 :
600 23 : prepareDataScan(&gdi->btree, index);
601 :
602 23 : gdi->btree.searchMode = searchMode;
603 23 : gdi->btree.fullScan = searchMode;
604 :
605 23 : gdi->stack = ginPrepareFindLeafPage(&gdi->btree, rootBlkno);
606 :
607 23 : return gdi;
608 : }
609 :
610 : /*
611 : * Inserts array of item pointers, may execute several tree scan (very rare)
612 : */
613 : void
614 : insertItemPointer(GinPostingTreeScan *gdi, ItemPointerData *items, uint32 nitem)
615 19 : {
616 19 : BlockNumber rootBlkno = gdi->stack->blkno;
617 :
618 19 : gdi->btree.items = items;
619 19 : gdi->btree.nitem = nitem;
620 19 : gdi->btree.curitem = 0;
621 :
622 57 : while (gdi->btree.curitem < gdi->btree.nitem)
623 : {
624 19 : if (!gdi->stack)
625 0 : gdi->stack = ginPrepareFindLeafPage(&gdi->btree, rootBlkno);
626 :
627 19 : gdi->stack = ginFindLeafPage(&gdi->btree, gdi->stack);
628 :
629 19 : if (gdi->btree.findItem(&(gdi->btree), gdi->stack))
630 0 : elog(ERROR, "item pointer (%u,%d) already exists",
631 : ItemPointerGetBlockNumber(gdi->btree.items + gdi->btree.curitem),
632 : ItemPointerGetOffsetNumber(gdi->btree.items + gdi->btree.curitem));
633 :
634 19 : ginInsertValue(&(gdi->btree), gdi->stack);
635 :
636 19 : gdi->stack = NULL;
637 : }
638 19 : }
639 :
640 : Buffer
641 : scanBeginPostingTree(GinPostingTreeScan *gdi)
642 4 : {
643 4 : gdi->stack = ginFindLeafPage(&gdi->btree, gdi->stack);
644 4 : return gdi->stack->buffer;
645 : }
|