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