Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * ginentrypage.c
4 : * routines for handling GIN entry tree pages.
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * IDENTIFICATION
11 : * src/backend/access/gin/ginentrypage.c
12 : *-------------------------------------------------------------------------
13 : */
14 :
15 : #include "postgres.h"
16 :
17 : #include "access/gin_private.h"
18 : #include "access/ginxlog.h"
19 : #include "access/xloginsert.h"
20 : #include "utils/rel.h"
21 :
22 : static void entrySplitPage(GinBtree btree, Buffer origbuf,
23 : GinBtreeStack *stack,
24 : GinBtreeEntryInsertData *insertData,
25 : BlockNumber updateblkno,
26 : Page *newlpage, Page *newrpage);
27 :
28 : /*
29 : * Form a tuple for entry tree.
30 : *
31 : * If the tuple would be too big to be stored, function throws a suitable
32 : * error if errorTooBig is true, or returns NULL if errorTooBig is false.
33 : *
34 : * See src/backend/access/gin/README for a description of the index tuple
35 : * format that is being built here. We build on the assumption that we
36 : * are making a leaf-level key entry containing a posting list of nipd items.
37 : * If the caller is actually trying to make a posting-tree entry, non-leaf
38 : * entry, or pending-list entry, it should pass dataSize = 0 and then overwrite
39 : * the t_tid fields as necessary. In any case, 'data' can be NULL to skip
40 : * filling in the posting list; the caller is responsible for filling it
41 : * afterwards if data = NULL and nipd > 0.
42 : */
43 : IndexTuple
44 2118342 : GinFormTuple(GinState *ginstate,
45 : OffsetNumber attnum, Datum key, GinNullCategory category,
46 : Pointer data, Size dataSize, int nipd,
47 : bool errorTooBig)
48 : {
49 : Datum datums[2];
50 : bool isnull[2];
51 : IndexTuple itup;
52 : uint32 newsize;
53 :
54 : /* Build the basic tuple: optional column number, plus key datum */
55 2118342 : if (ginstate->oneCol)
56 : {
57 916476 : datums[0] = key;
58 916476 : isnull[0] = (category != GIN_CAT_NORM_KEY);
59 : }
60 : else
61 : {
62 1201866 : datums[0] = UInt16GetDatum(attnum);
63 1201866 : isnull[0] = false;
64 1201866 : datums[1] = key;
65 1201866 : isnull[1] = (category != GIN_CAT_NORM_KEY);
66 : }
67 :
68 2118342 : itup = index_form_tuple(ginstate->tupdesc[attnum - 1], datums, isnull);
69 :
70 : /*
71 : * Determine and store offset to the posting list, making sure there is
72 : * room for the category byte if needed.
73 : *
74 : * Note: because index_form_tuple MAXALIGNs the tuple size, there may well
75 : * be some wasted pad space. Is it worth recomputing the data length to
76 : * prevent that? That would also allow us to Assert that the real data
77 : * doesn't overlap the GinNullCategory byte, which this code currently
78 : * takes on faith.
79 : */
80 2118342 : newsize = IndexTupleSize(itup);
81 :
82 2118342 : if (IndexTupleHasNulls(itup))
83 : {
84 : uint32 minsize;
85 :
86 : Assert(category != GIN_CAT_NORM_KEY);
87 236 : minsize = GinCategoryOffset(itup, ginstate) + sizeof(GinNullCategory);
88 236 : newsize = Max(newsize, minsize);
89 : }
90 :
91 2118342 : newsize = SHORTALIGN(newsize);
92 :
93 2118342 : GinSetPostingOffset(itup, newsize);
94 2118342 : GinSetNPosting(itup, nipd);
95 :
96 : /*
97 : * Add space needed for posting list, if any. Then check that the tuple
98 : * won't be too big to store.
99 : */
100 2118342 : newsize += dataSize;
101 :
102 2118342 : newsize = MAXALIGN(newsize);
103 :
104 2118342 : if (newsize > GinMaxItemSize)
105 : {
106 116 : if (errorTooBig)
107 0 : ereport(ERROR,
108 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
109 : errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
110 : (Size) newsize, (Size) GinMaxItemSize,
111 : RelationGetRelationName(ginstate->index))));
112 116 : pfree(itup);
113 116 : return NULL;
114 : }
115 :
116 : /*
117 : * Resize tuple if needed
118 : */
119 2118226 : if (newsize != IndexTupleSize(itup))
120 : {
121 722016 : itup = repalloc(itup, newsize);
122 :
123 : /*
124 : * PostgreSQL 9.3 and earlier did not clear this new space, so we
125 : * might find uninitialized padding when reading tuples from disk.
126 : */
127 722016 : memset((char *) itup + IndexTupleSize(itup),
128 722016 : 0, newsize - IndexTupleSize(itup));
129 : /* set new size in tuple header */
130 722016 : itup->t_info &= ~INDEX_SIZE_MASK;
131 722016 : itup->t_info |= newsize;
132 : }
133 :
134 : /*
135 : * Copy in the posting list, if provided
136 : */
137 2118226 : if (data)
138 : {
139 722010 : char *ptr = GinGetPosting(itup);
140 :
141 722010 : memcpy(ptr, data, dataSize);
142 : }
143 :
144 : /*
145 : * Insert category byte, if needed
146 : */
147 2118226 : if (category != GIN_CAT_NORM_KEY)
148 : {
149 : Assert(IndexTupleHasNulls(itup));
150 236 : GinSetNullCategory(itup, ginstate, category);
151 : }
152 2118226 : return itup;
153 : }
154 :
155 : /*
156 : * Read item pointers from leaf entry tuple.
157 : *
158 : * Returns a palloc'd array of ItemPointers. The number of items is returned
159 : * in *nitems.
160 : */
161 : ItemPointer
162 444054 : ginReadTuple(GinState *ginstate, OffsetNumber attnum, IndexTuple itup,
163 : int *nitems)
164 : {
165 444054 : Pointer ptr = GinGetPosting(itup);
166 444054 : int nipd = GinGetNPosting(itup);
167 : ItemPointer ipd;
168 : int ndecoded;
169 :
170 444054 : if (GinItupIsCompressed(itup))
171 : {
172 444054 : if (nipd > 0)
173 : {
174 324060 : ipd = ginPostingListDecode((GinPostingList *) ptr, &ndecoded);
175 324060 : if (nipd != ndecoded)
176 0 : elog(ERROR, "number of items mismatch in GIN entry tuple, %d in tuple header, %d decoded",
177 : nipd, ndecoded);
178 : }
179 : else
180 : {
181 119994 : ipd = palloc(0);
182 : }
183 : }
184 : else
185 : {
186 0 : ipd = (ItemPointer) palloc(sizeof(ItemPointerData) * nipd);
187 0 : memcpy(ipd, ptr, sizeof(ItemPointerData) * nipd);
188 : }
189 444054 : *nitems = nipd;
190 444054 : return ipd;
191 : }
192 :
193 : /*
194 : * Form a non-leaf entry tuple by copying the key data from the given tuple,
195 : * which can be either a leaf or non-leaf entry tuple.
196 : *
197 : * Any posting list in the source tuple is not copied. The specified child
198 : * block number is inserted into t_tid.
199 : */
200 : static IndexTuple
201 4900 : GinFormInteriorTuple(IndexTuple itup, Page page, BlockNumber childblk)
202 : {
203 : IndexTuple nitup;
204 :
205 4900 : if (GinPageIsLeaf(page) && !GinIsPostingTree(itup))
206 4894 : {
207 : /* Tuple contains a posting list, just copy stuff before that */
208 4894 : uint32 origsize = GinGetPostingOffset(itup);
209 :
210 4894 : origsize = MAXALIGN(origsize);
211 4894 : nitup = (IndexTuple) palloc(origsize);
212 4894 : memcpy(nitup, itup, origsize);
213 : /* ... be sure to fix the size header field ... */
214 4894 : nitup->t_info &= ~INDEX_SIZE_MASK;
215 4894 : nitup->t_info |= origsize;
216 : }
217 : else
218 : {
219 : /* Copy the tuple as-is */
220 6 : nitup = (IndexTuple) palloc(IndexTupleSize(itup));
221 6 : memcpy(nitup, itup, IndexTupleSize(itup));
222 : }
223 :
224 : /* Now insert the correct downlink */
225 4900 : GinSetDownlink(nitup, childblk);
226 :
227 4900 : return nitup;
228 : }
229 :
230 : /*
231 : * Entry tree is a "static", ie tuple never deletes from it,
232 : * so we don't use right bound, we use rightmost key instead.
233 : */
234 : static IndexTuple
235 50418 : getRightMostTuple(Page page)
236 : {
237 50418 : OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
238 :
239 50418 : return (IndexTuple) PageGetItem(page, PageGetItemId(page, maxoff));
240 : }
241 :
242 : static bool
243 769314 : entryIsMoveRight(GinBtree btree, Page page)
244 : {
245 : IndexTuple itup;
246 : OffsetNumber attnum;
247 : Datum key;
248 : GinNullCategory category;
249 :
250 769314 : if (GinPageRightMost(page))
251 723796 : return false;
252 :
253 45518 : itup = getRightMostTuple(page);
254 45518 : attnum = gintuple_get_attrnum(btree->ginstate, itup);
255 45518 : key = gintuple_get_key(btree->ginstate, itup, &category);
256 :
257 45518 : if (ginCompareAttEntries(btree->ginstate,
258 45518 : btree->entryAttnum, btree->entryKey, btree->entryCategory,
259 : attnum, key, category) > 0)
260 1092 : return true;
261 :
262 44426 : return false;
263 : }
264 :
265 : /*
266 : * Find correct tuple in non-leaf page. It supposed that
267 : * page correctly chosen and searching value SHOULD be on page
268 : */
269 : static BlockNumber
270 768222 : entryLocateEntry(GinBtree btree, GinBtreeStack *stack)
271 : {
272 : OffsetNumber low,
273 : high,
274 : maxoff;
275 768222 : IndexTuple itup = NULL;
276 : int result;
277 768222 : Page page = BufferGetPage(stack->buffer);
278 :
279 : Assert(!GinPageIsLeaf(page));
280 : Assert(!GinPageIsData(page));
281 :
282 768222 : if (btree->fullScan)
283 : {
284 0 : stack->off = FirstOffsetNumber;
285 0 : stack->predictNumber *= PageGetMaxOffsetNumber(page);
286 0 : return btree->getLeftMostChild(btree, page);
287 : }
288 :
289 768222 : low = FirstOffsetNumber;
290 768222 : maxoff = high = PageGetMaxOffsetNumber(page);
291 : Assert(high >= low);
292 :
293 768222 : high++;
294 :
295 5344872 : while (high > low)
296 : {
297 4576840 : OffsetNumber mid = low + ((high - low) / 2);
298 :
299 4576840 : if (mid == maxoff && GinPageRightMost(page))
300 : {
301 : /* Right infinity */
302 724486 : result = -1;
303 : }
304 : else
305 : {
306 : OffsetNumber attnum;
307 : Datum key;
308 : GinNullCategory category;
309 :
310 3852354 : itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
311 3852354 : attnum = gintuple_get_attrnum(btree->ginstate, itup);
312 3852354 : key = gintuple_get_key(btree->ginstate, itup, &category);
313 3852354 : result = ginCompareAttEntries(btree->ginstate,
314 3852354 : btree->entryAttnum,
315 : btree->entryKey,
316 3852354 : btree->entryCategory,
317 : attnum, key, category);
318 : }
319 :
320 4576840 : if (result == 0)
321 : {
322 190 : stack->off = mid;
323 : Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
324 190 : return GinGetDownlink(itup);
325 : }
326 4576650 : else if (result > 0)
327 3551940 : low = mid + 1;
328 : else
329 1024710 : high = mid;
330 : }
331 :
332 : Assert(high >= FirstOffsetNumber && high <= maxoff);
333 :
334 768032 : stack->off = high;
335 768032 : itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, high));
336 : Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
337 768032 : return GinGetDownlink(itup);
338 : }
339 :
340 : /*
341 : * Searches correct position for value on leaf page.
342 : * Page should be correctly chosen.
343 : * Returns true if value found on page.
344 : */
345 : static bool
346 778186 : entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack)
347 : {
348 778186 : Page page = BufferGetPage(stack->buffer);
349 : OffsetNumber low,
350 : high;
351 :
352 : Assert(GinPageIsLeaf(page));
353 : Assert(!GinPageIsData(page));
354 :
355 778186 : if (btree->fullScan)
356 : {
357 0 : stack->off = FirstOffsetNumber;
358 0 : return true;
359 : }
360 :
361 778186 : low = FirstOffsetNumber;
362 778186 : high = PageGetMaxOffsetNumber(page);
363 :
364 778186 : if (high < low)
365 : {
366 510 : stack->off = FirstOffsetNumber;
367 510 : return false;
368 : }
369 :
370 777676 : high++;
371 :
372 6007072 : while (high > low)
373 : {
374 5316054 : OffsetNumber mid = low + ((high - low) / 2);
375 : IndexTuple itup;
376 : OffsetNumber attnum;
377 : Datum key;
378 : GinNullCategory category;
379 : int result;
380 :
381 5316054 : itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
382 5316054 : attnum = gintuple_get_attrnum(btree->ginstate, itup);
383 5316054 : key = gintuple_get_key(btree->ginstate, itup, &category);
384 5316054 : result = ginCompareAttEntries(btree->ginstate,
385 5316054 : btree->entryAttnum,
386 : btree->entryKey,
387 5316054 : btree->entryCategory,
388 : attnum, key, category);
389 5316054 : if (result == 0)
390 : {
391 86658 : stack->off = mid;
392 86658 : return true;
393 : }
394 5229396 : else if (result > 0)
395 4981034 : low = mid + 1;
396 : else
397 248362 : high = mid;
398 : }
399 :
400 691018 : stack->off = high;
401 691018 : return false;
402 : }
403 :
404 : static OffsetNumber
405 4652 : entryFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff)
406 : {
407 : OffsetNumber i,
408 4652 : maxoff = PageGetMaxOffsetNumber(page);
409 : IndexTuple itup;
410 :
411 : Assert(!GinPageIsLeaf(page));
412 : Assert(!GinPageIsData(page));
413 :
414 : /* if page isn't changed, we returns storedOff */
415 4652 : if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
416 : {
417 4652 : itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, storedOff));
418 4652 : if (GinGetDownlink(itup) == blkno)
419 4652 : return storedOff;
420 :
421 : /*
422 : * we hope, that needed pointer goes to right. It's true if there
423 : * wasn't a deletion
424 : */
425 0 : for (i = storedOff + 1; i <= maxoff; i++)
426 : {
427 0 : itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
428 0 : if (GinGetDownlink(itup) == blkno)
429 0 : return i;
430 : }
431 0 : maxoff = storedOff - 1;
432 : }
433 :
434 : /* last chance */
435 0 : for (i = FirstOffsetNumber; i <= maxoff; i++)
436 : {
437 0 : itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
438 0 : if (GinGetDownlink(itup) == blkno)
439 0 : return i;
440 : }
441 :
442 0 : return InvalidOffsetNumber;
443 : }
444 :
445 : static BlockNumber
446 0 : entryGetLeftMostPage(GinBtree btree, Page page)
447 : {
448 : IndexTuple itup;
449 :
450 : Assert(!GinPageIsLeaf(page));
451 : Assert(!GinPageIsData(page));
452 : Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);
453 :
454 0 : itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
455 0 : return GinGetDownlink(itup);
456 : }
457 :
458 : static bool
459 726742 : entryIsEnoughSpace(GinBtree btree, Buffer buf, OffsetNumber off,
460 : GinBtreeEntryInsertData *insertData)
461 : {
462 726742 : Size releasedsz = 0;
463 : Size addedsz;
464 726742 : Page page = BufferGetPage(buf);
465 :
466 : Assert(insertData->entry);
467 : Assert(!GinPageIsData(page));
468 :
469 726742 : if (insertData->isDelete)
470 : {
471 32674 : IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
472 :
473 32674 : releasedsz = MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
474 : }
475 :
476 726742 : addedsz = MAXALIGN(IndexTupleSize(insertData->entry)) + sizeof(ItemIdData);
477 :
478 726742 : if (PageGetFreeSpace(page) + releasedsz >= addedsz)
479 721966 : return true;
480 :
481 4776 : return false;
482 : }
483 :
484 : /*
485 : * Delete tuple on leaf page if tuples existed and we
486 : * should update it, update old child blkno to new right page
487 : * if child split occurred
488 : */
489 : static void
490 726742 : entryPreparePage(GinBtree btree, Page page, OffsetNumber off,
491 : GinBtreeEntryInsertData *insertData, BlockNumber updateblkno)
492 : {
493 : Assert(insertData->entry);
494 : Assert(!GinPageIsData(page));
495 :
496 726742 : if (insertData->isDelete)
497 : {
498 : Assert(GinPageIsLeaf(page));
499 32674 : PageIndexTupleDelete(page, off);
500 : }
501 :
502 726742 : if (!GinPageIsLeaf(page) && updateblkno != InvalidBlockNumber)
503 : {
504 4652 : IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
505 :
506 4652 : GinSetDownlink(itup, updateblkno);
507 : }
508 726742 : }
509 :
510 : /*
511 : * Prepare to insert data on an entry page.
512 : *
513 : * If it will fit, return GPTP_INSERT after doing whatever setup is needed
514 : * before we enter the insertion critical section. *ptp_workspace can be
515 : * set to pass information along to the execPlaceToPage function.
516 : *
517 : * If it won't fit, perform a page split and return two temporary page
518 : * images into *newlpage and *newrpage, with result GPTP_SPLIT.
519 : *
520 : * In neither case should the given page buffer be modified here.
521 : *
522 : * Note: on insertion to an internal node, in addition to inserting the given
523 : * item, the downlink of the existing item at stack->off will be updated to
524 : * point to updateblkno.
525 : */
526 : static GinPlaceToPageRC
527 726742 : entryBeginPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
528 : void *insertPayload, BlockNumber updateblkno,
529 : void **ptp_workspace,
530 : Page *newlpage, Page *newrpage)
531 : {
532 726742 : GinBtreeEntryInsertData *insertData = insertPayload;
533 726742 : OffsetNumber off = stack->off;
534 :
535 : /* If it doesn't fit, deal with split case */
536 726742 : if (!entryIsEnoughSpace(btree, buf, off, insertData))
537 : {
538 4776 : entrySplitPage(btree, buf, stack, insertData, updateblkno,
539 : newlpage, newrpage);
540 4776 : return GPTP_SPLIT;
541 : }
542 :
543 : /* Else, we're ready to proceed with insertion */
544 721966 : return GPTP_INSERT;
545 : }
546 :
547 : /*
548 : * Perform data insertion after beginPlaceToPage has decided it will fit.
549 : *
550 : * This is invoked within a critical section, and XLOG record creation (if
551 : * needed) is already started. The target buffer is registered in slot 0.
552 : */
553 : static void
554 721966 : entryExecPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
555 : void *insertPayload, BlockNumber updateblkno,
556 : void *ptp_workspace)
557 : {
558 721966 : GinBtreeEntryInsertData *insertData = insertPayload;
559 721966 : Page page = BufferGetPage(buf);
560 721966 : OffsetNumber off = stack->off;
561 : OffsetNumber placed;
562 :
563 721966 : entryPreparePage(btree, page, off, insertData, updateblkno);
564 :
565 721966 : placed = PageAddItem(page,
566 : (Item) insertData->entry,
567 : IndexTupleSize(insertData->entry),
568 : off, false, false);
569 721966 : if (placed != off)
570 0 : elog(ERROR, "failed to add item to index page in \"%s\"",
571 : RelationGetRelationName(btree->index));
572 :
573 721966 : MarkBufferDirty(buf);
574 :
575 721966 : if (RelationNeedsWAL(btree->index) && !btree->isBuild)
576 : {
577 : /*
578 : * This must be static, because it has to survive until XLogInsert,
579 : * and we can't palloc here. Ugly, but the XLogInsert infrastructure
580 : * isn't reentrant anyway.
581 : */
582 : static ginxlogInsertEntry data;
583 :
584 332898 : data.isDelete = insertData->isDelete;
585 332898 : data.offset = off;
586 :
587 332898 : XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
588 332898 : XLogRegisterBufData(0, (char *) &data,
589 : offsetof(ginxlogInsertEntry, tuple));
590 332898 : XLogRegisterBufData(0, (char *) insertData->entry,
591 332898 : IndexTupleSize(insertData->entry));
592 : }
593 721966 : }
594 :
595 : /*
596 : * Split entry page and insert new data.
597 : *
598 : * Returns new temp pages to *newlpage and *newrpage.
599 : * The original buffer is left untouched.
600 : */
601 : static void
602 4776 : entrySplitPage(GinBtree btree, Buffer origbuf,
603 : GinBtreeStack *stack,
604 : GinBtreeEntryInsertData *insertData,
605 : BlockNumber updateblkno,
606 : Page *newlpage, Page *newrpage)
607 : {
608 4776 : OffsetNumber off = stack->off;
609 : OffsetNumber i,
610 : maxoff,
611 4776 : separator = InvalidOffsetNumber;
612 4776 : Size totalsize = 0;
613 4776 : Size lsize = 0,
614 : size;
615 : char *ptr;
616 : IndexTuple itup;
617 : Page page;
618 4776 : Page lpage = PageGetTempPageCopy(BufferGetPage(origbuf));
619 4776 : Page rpage = PageGetTempPageCopy(BufferGetPage(origbuf));
620 4776 : Size pageSize = PageGetPageSize(lpage);
621 : PGAlignedBlock tupstore[2]; /* could need 2 pages' worth of tuples */
622 :
623 4776 : entryPreparePage(btree, lpage, off, insertData, updateblkno);
624 :
625 : /*
626 : * First, append all the existing tuples and the new tuple we're inserting
627 : * one after another in a temporary workspace.
628 : */
629 4776 : maxoff = PageGetMaxOffsetNumber(lpage);
630 4776 : ptr = tupstore[0].data;
631 1326484 : for (i = FirstOffsetNumber; i <= maxoff; i++)
632 : {
633 1321708 : if (i == off)
634 : {
635 4 : size = MAXALIGN(IndexTupleSize(insertData->entry));
636 4 : memcpy(ptr, insertData->entry, size);
637 4 : ptr += size;
638 4 : totalsize += size + sizeof(ItemIdData);
639 : }
640 :
641 1321708 : itup = (IndexTuple) PageGetItem(lpage, PageGetItemId(lpage, i));
642 1321708 : size = MAXALIGN(IndexTupleSize(itup));
643 1321708 : memcpy(ptr, itup, size);
644 1321708 : ptr += size;
645 1321708 : totalsize += size + sizeof(ItemIdData);
646 : }
647 :
648 4776 : if (off == maxoff + 1)
649 : {
650 4772 : size = MAXALIGN(IndexTupleSize(insertData->entry));
651 4772 : memcpy(ptr, insertData->entry, size);
652 4772 : ptr += size;
653 4772 : totalsize += size + sizeof(ItemIdData);
654 : }
655 :
656 : /*
657 : * Initialize the left and right pages, and copy all the tuples back to
658 : * them.
659 : */
660 4776 : GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize);
661 4776 : GinInitPage(lpage, GinPageGetOpaque(rpage)->flags, pageSize);
662 :
663 4776 : ptr = tupstore[0].data;
664 4776 : maxoff++;
665 4776 : lsize = 0;
666 :
667 4776 : page = lpage;
668 1331260 : for (i = FirstOffsetNumber; i <= maxoff; i++)
669 : {
670 1326484 : itup = (IndexTuple) ptr;
671 :
672 : /*
673 : * Decide where to split. We try to equalize the pages' total data
674 : * size, not number of tuples.
675 : */
676 1326484 : if (lsize > totalsize / 2)
677 : {
678 659340 : if (separator == InvalidOffsetNumber)
679 4776 : separator = i - 1;
680 659340 : page = rpage;
681 : }
682 : else
683 : {
684 667144 : lsize += MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
685 : }
686 :
687 1326484 : if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
688 0 : elog(ERROR, "failed to add item to index page in \"%s\"",
689 : RelationGetRelationName(btree->index));
690 1326484 : ptr += MAXALIGN(IndexTupleSize(itup));
691 : }
692 :
693 : /* return temp pages to caller */
694 4776 : *newlpage = lpage;
695 4776 : *newrpage = rpage;
696 4776 : }
697 :
698 : /*
699 : * Construct insertion payload for inserting the downlink for given buffer.
700 : */
701 : static void *
702 4652 : entryPrepareDownlink(GinBtree btree, Buffer lbuf)
703 : {
704 : GinBtreeEntryInsertData *insertData;
705 4652 : Page lpage = BufferGetPage(lbuf);
706 4652 : BlockNumber lblkno = BufferGetBlockNumber(lbuf);
707 : IndexTuple itup;
708 :
709 4652 : itup = getRightMostTuple(lpage);
710 :
711 4652 : insertData = palloc(sizeof(GinBtreeEntryInsertData));
712 4652 : insertData->entry = GinFormInteriorTuple(itup, lpage, lblkno);
713 4652 : insertData->isDelete = false;
714 :
715 4652 : return insertData;
716 : }
717 :
718 : /*
719 : * Fills new root by rightest values from child.
720 : * Also called from ginxlog, should not use btree
721 : */
722 : void
723 124 : ginEntryFillRoot(GinBtree btree, Page root,
724 : BlockNumber lblkno, Page lpage,
725 : BlockNumber rblkno, Page rpage)
726 : {
727 : IndexTuple itup;
728 :
729 124 : itup = GinFormInteriorTuple(getRightMostTuple(lpage), lpage, lblkno);
730 124 : if (PageAddItem(root, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
731 0 : elog(ERROR, "failed to add item to index root page");
732 124 : pfree(itup);
733 :
734 124 : itup = GinFormInteriorTuple(getRightMostTuple(rpage), rpage, rblkno);
735 124 : if (PageAddItem(root, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
736 0 : elog(ERROR, "failed to add item to index root page");
737 124 : pfree(itup);
738 124 : }
739 :
740 : /*
741 : * Set up GinBtree for entry page access
742 : *
743 : * Note: during WAL recovery, there may be no valid data in ginstate
744 : * other than a faked-up Relation pointer; the key datum is bogus too.
745 : */
746 : void
747 778186 : ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum,
748 : Datum key, GinNullCategory category,
749 : GinState *ginstate)
750 : {
751 778186 : memset(btree, 0, sizeof(GinBtreeData));
752 :
753 778186 : btree->index = ginstate->index;
754 778186 : btree->rootBlkno = GIN_ROOT_BLKNO;
755 778186 : btree->ginstate = ginstate;
756 :
757 778186 : btree->findChildPage = entryLocateEntry;
758 778186 : btree->getLeftMostChild = entryGetLeftMostPage;
759 778186 : btree->isMoveRight = entryIsMoveRight;
760 778186 : btree->findItem = entryLocateLeafEntry;
761 778186 : btree->findChildPtr = entryFindChildPtr;
762 778186 : btree->beginPlaceToPage = entryBeginPlaceToPage;
763 778186 : btree->execPlaceToPage = entryExecPlaceToPage;
764 778186 : btree->fillRoot = ginEntryFillRoot;
765 778186 : btree->prepareDownlink = entryPrepareDownlink;
766 :
767 778186 : btree->isData = false;
768 778186 : btree->fullScan = false;
769 778186 : btree->isBuild = false;
770 :
771 778186 : btree->entryAttnum = attnum;
772 778186 : btree->entryKey = key;
773 778186 : btree->entryCategory = category;
774 778186 : }
|