Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * ginentrypage.c
4 : * routines for handling GIN entry tree pages.
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2017, 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 124156 : 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 124156 : if (ginstate->oneCol)
57 : {
58 123861 : datums[0] = key;
59 123861 : isnull[0] = (category != GIN_CAT_NORM_KEY);
60 : }
61 : else
62 : {
63 295 : datums[0] = UInt16GetDatum(attnum);
64 295 : isnull[0] = false;
65 295 : datums[1] = key;
66 295 : isnull[1] = (category != GIN_CAT_NORM_KEY);
67 : }
68 :
69 124156 : 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 124156 : newsize = IndexTupleSize(itup);
82 :
83 124156 : if (IndexTupleHasNulls(itup))
84 : {
85 : uint32 minsize;
86 :
87 20 : Assert(category != GIN_CAT_NORM_KEY);
88 20 : minsize = GinCategoryOffset(itup, ginstate) + sizeof(GinNullCategory);
89 20 : newsize = Max(newsize, minsize);
90 : }
91 :
92 124156 : newsize = SHORTALIGN(newsize);
93 :
94 124156 : GinSetPostingOffset(itup, newsize);
95 124156 : 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 124156 : newsize += dataSize;
102 :
103 124156 : newsize = MAXALIGN(newsize);
104 :
105 124156 : if (newsize > GinMaxItemSize)
106 : {
107 4 : 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 4 : pfree(itup);
114 4 : return NULL;
115 : }
116 :
117 : /*
118 : * Resize tuple if needed
119 : */
120 124152 : if (newsize != IndexTupleSize(itup))
121 : {
122 39144 : 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 39144 : memset((char *) itup + IndexTupleSize(itup),
129 39144 : 0, newsize - IndexTupleSize(itup));
130 : /* set new size in tuple header */
131 39144 : itup->t_info &= ~INDEX_SIZE_MASK;
132 39144 : itup->t_info |= newsize;
133 : }
134 :
135 : /*
136 : * Copy in the posting list, if provided
137 : */
138 124152 : if (data)
139 : {
140 39143 : char *ptr = GinGetPosting(itup);
141 :
142 39143 : memcpy(ptr, data, dataSize);
143 : }
144 :
145 : /*
146 : * Insert category byte, if needed
147 : */
148 124152 : if (category != GIN_CAT_NORM_KEY)
149 : {
150 20 : Assert(IndexTupleHasNulls(itup));
151 20 : GinSetNullCategory(itup, ginstate, category);
152 : }
153 124152 : 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 7899 : ginReadTuple(GinState *ginstate, OffsetNumber attnum, IndexTuple itup,
164 : int *nitems)
165 : {
166 7899 : Pointer ptr = GinGetPosting(itup);
167 7899 : int nipd = GinGetNPosting(itup);
168 : ItemPointer ipd;
169 : int ndecoded;
170 :
171 7899 : if (GinItupIsCompressed(itup))
172 : {
173 7899 : if (nipd > 0)
174 : {
175 7899 : ipd = ginPostingListDecode((GinPostingList *) ptr, &ndecoded);
176 7899 : 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 0 : 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 7899 : *nitems = nipd;
191 7899 : 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 216 : GinFormInteriorTuple(IndexTuple itup, Page page, BlockNumber childblk)
203 : {
204 : IndexTuple nitup;
205 :
206 216 : if (GinPageIsLeaf(page) && !GinIsPostingTree(itup))
207 216 : {
208 : /* Tuple contains a posting list, just copy stuff before that */
209 216 : uint32 origsize = GinGetPostingOffset(itup);
210 :
211 216 : origsize = MAXALIGN(origsize);
212 216 : nitup = (IndexTuple) palloc(origsize);
213 216 : memcpy(nitup, itup, origsize);
214 : /* ... be sure to fix the size header field ... */
215 216 : nitup->t_info &= ~INDEX_SIZE_MASK;
216 216 : 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 216 : GinSetDownlink(nitup, childblk);
227 :
228 216 : 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 7274 : getRightMostTuple(Page page)
237 : {
238 7274 : OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
239 :
240 7274 : return (IndexTuple) PageGetItem(page, PageGetItemId(page, maxoff));
241 : }
242 :
243 : static bool
244 40038 : entryIsMoveRight(GinBtree btree, Page page)
245 : {
246 : IndexTuple itup;
247 : OffsetNumber attnum;
248 : Datum key;
249 : GinNullCategory category;
250 :
251 40038 : if (GinPageRightMost(page))
252 32980 : return FALSE;
253 :
254 7058 : itup = getRightMostTuple(page);
255 7058 : attnum = gintuple_get_attrnum(btree->ginstate, itup);
256 7058 : key = gintuple_get_key(btree->ginstate, itup, &category);
257 :
258 21174 : if (ginCompareAttEntries(btree->ginstate,
259 14116 : btree->entryAttnum, btree->entryKey, btree->entryCategory,
260 : attnum, key, category) > 0)
261 0 : return TRUE;
262 :
263 7058 : 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 40038 : entryLocateEntry(GinBtree btree, GinBtreeStack *stack)
272 : {
273 : OffsetNumber low,
274 : high,
275 : maxoff;
276 40038 : IndexTuple itup = NULL;
277 : int result;
278 40038 : Page page = BufferGetPage(stack->buffer);
279 :
280 40038 : Assert(!GinPageIsLeaf(page));
281 40038 : Assert(!GinPageIsData(page));
282 :
283 40038 : 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 40038 : low = FirstOffsetNumber;
291 40038 : maxoff = high = PageGetMaxOffsetNumber(page);
292 40038 : Assert(high >= low);
293 :
294 40038 : high++;
295 :
296 294393 : while (high > low)
297 : {
298 214339 : OffsetNumber mid = low + ((high - low) / 2);
299 :
300 214339 : if (mid == maxoff && GinPageRightMost(page))
301 : {
302 : /* Right infinity */
303 32987 : result = -1;
304 : }
305 : else
306 : {
307 : OffsetNumber attnum;
308 : Datum key;
309 : GinNullCategory category;
310 :
311 181352 : itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
312 181352 : attnum = gintuple_get_attrnum(btree->ginstate, itup);
313 181352 : key = gintuple_get_key(btree->ginstate, itup, &category);
314 544056 : result = ginCompareAttEntries(btree->ginstate,
315 181352 : btree->entryAttnum,
316 : btree->entryKey,
317 181352 : btree->entryCategory,
318 : attnum, key, category);
319 : }
320 :
321 214339 : if (result == 0)
322 : {
323 22 : stack->off = mid;
324 22 : Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
325 22 : return GinGetDownlink(itup);
326 : }
327 214317 : else if (result > 0)
328 136231 : low = mid + 1;
329 : else
330 78086 : high = mid;
331 : }
332 :
333 40016 : Assert(high >= FirstOffsetNumber && high <= maxoff);
334 :
335 40016 : stack->off = high;
336 40016 : itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, high));
337 40016 : Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
338 40016 : 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 41600 : entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack)
348 : {
349 41600 : Page page = BufferGetPage(stack->buffer);
350 : OffsetNumber low,
351 : high;
352 :
353 41600 : Assert(GinPageIsLeaf(page));
354 41600 : Assert(!GinPageIsData(page));
355 :
356 41600 : if (btree->fullScan)
357 : {
358 0 : stack->off = FirstOffsetNumber;
359 0 : return TRUE;
360 : }
361 :
362 41600 : low = FirstOffsetNumber;
363 41600 : high = PageGetMaxOffsetNumber(page);
364 :
365 41600 : if (high < low)
366 : {
367 12 : stack->off = FirstOffsetNumber;
368 12 : return false;
369 : }
370 :
371 41588 : high++;
372 :
373 375637 : while (high > low)
374 : {
375 299558 : OffsetNumber mid = low + ((high - low) / 2);
376 : IndexTuple itup;
377 : OffsetNumber attnum;
378 : Datum key;
379 : GinNullCategory category;
380 : int result;
381 :
382 299558 : itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
383 299558 : attnum = gintuple_get_attrnum(btree->ginstate, itup);
384 299558 : key = gintuple_get_key(btree->ginstate, itup, &category);
385 898674 : result = ginCompareAttEntries(btree->ginstate,
386 299558 : btree->entryAttnum,
387 : btree->entryKey,
388 299558 : btree->entryCategory,
389 : attnum, key, category);
390 299558 : if (result == 0)
391 : {
392 7097 : stack->off = mid;
393 7097 : return true;
394 : }
395 292461 : else if (result > 0)
396 261591 : low = mid + 1;
397 : else
398 30870 : high = mid;
399 : }
400 :
401 34491 : stack->off = high;
402 34491 : return false;
403 : }
404 :
405 : static OffsetNumber
406 204 : entryFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff)
407 : {
408 : OffsetNumber i,
409 204 : maxoff = PageGetMaxOffsetNumber(page);
410 : IndexTuple itup;
411 :
412 204 : Assert(!GinPageIsLeaf(page));
413 204 : Assert(!GinPageIsData(page));
414 :
415 : /* if page isn't changed, we returns storedOff */
416 204 : if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
417 : {
418 204 : itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, storedOff));
419 204 : if (GinGetDownlink(itup) == blkno)
420 204 : 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 0 : Assert(!GinPageIsLeaf(page));
452 0 : Assert(!GinPageIsData(page));
453 0 : 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 38354 : entryIsEnoughSpace(GinBtree btree, Buffer buf, OffsetNumber off,
461 : GinBtreeEntryInsertData *insertData)
462 : {
463 38354 : Size releasedsz = 0;
464 : Size addedsz;
465 38354 : Page page = BufferGetPage(buf);
466 :
467 38354 : Assert(insertData->entry);
468 38354 : Assert(!GinPageIsData(page));
469 :
470 38354 : if (insertData->isDelete)
471 : {
472 3665 : IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
473 :
474 3665 : releasedsz = MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
475 : }
476 :
477 38354 : addedsz = MAXALIGN(IndexTupleSize(insertData->entry)) + sizeof(ItemIdData);
478 :
479 38354 : if (PageGetFreeSpace(page) + releasedsz >= addedsz)
480 38144 : return true;
481 :
482 210 : 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 38354 : entryPreparePage(GinBtree btree, Page page, OffsetNumber off,
492 : GinBtreeEntryInsertData *insertData, BlockNumber updateblkno)
493 : {
494 38354 : Assert(insertData->entry);
495 38354 : Assert(!GinPageIsData(page));
496 :
497 38354 : if (insertData->isDelete)
498 : {
499 3665 : Assert(GinPageIsLeaf(page));
500 3665 : PageIndexTupleDelete(page, off);
501 : }
502 :
503 38354 : if (!GinPageIsLeaf(page) && updateblkno != InvalidBlockNumber)
504 : {
505 204 : IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
506 :
507 204 : GinSetDownlink(itup, updateblkno);
508 : }
509 38354 : }
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 38354 : entryBeginPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
529 : void *insertPayload, BlockNumber updateblkno,
530 : void **ptp_workspace,
531 : Page *newlpage, Page *newrpage)
532 : {
533 38354 : GinBtreeEntryInsertData *insertData = insertPayload;
534 38354 : OffsetNumber off = stack->off;
535 :
536 : /* If it doesn't fit, deal with split case */
537 38354 : if (!entryIsEnoughSpace(btree, buf, off, insertData))
538 : {
539 210 : entrySplitPage(btree, buf, stack, insertData, updateblkno,
540 : newlpage, newrpage);
541 210 : return GPTP_SPLIT;
542 : }
543 :
544 : /* Else, we're ready to proceed with insertion */
545 38144 : 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 38144 : entryExecPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
556 : void *insertPayload, BlockNumber updateblkno,
557 : void *ptp_workspace)
558 : {
559 38144 : GinBtreeEntryInsertData *insertData = insertPayload;
560 38144 : Page page = BufferGetPage(buf);
561 38144 : OffsetNumber off = stack->off;
562 : OffsetNumber placed;
563 :
564 38144 : entryPreparePage(btree, page, off, insertData, updateblkno);
565 :
566 38144 : placed = PageAddItem(page,
567 : (Item) insertData->entry,
568 : IndexTupleSize(insertData->entry),
569 : off, false, false);
570 38144 : if (placed != off)
571 0 : elog(ERROR, "failed to add item to index page in \"%s\"",
572 : RelationGetRelationName(btree->index));
573 :
574 38144 : if (RelationNeedsWAL(btree->index))
575 : {
576 : /*
577 : * This must be static, because it has to survive until XLogInsert,
578 : * and we can't palloc here. Ugly, but the XLogInsert infrastructure
579 : * isn't reentrant anyway.
580 : */
581 : static ginxlogInsertEntry data;
582 :
583 38127 : data.isDelete = insertData->isDelete;
584 38127 : data.offset = off;
585 :
586 38127 : XLogRegisterBufData(0, (char *) &data,
587 : offsetof(ginxlogInsertEntry, tuple));
588 38127 : XLogRegisterBufData(0, (char *) insertData->entry,
589 38127 : IndexTupleSize(insertData->entry));
590 : }
591 38144 : }
592 :
593 : /*
594 : * Split entry page and insert new data.
595 : *
596 : * Returns new temp pages to *newlpage and *newrpage.
597 : * The original buffer is left untouched.
598 : */
599 : static void
600 210 : entrySplitPage(GinBtree btree, Buffer origbuf,
601 : GinBtreeStack *stack,
602 : GinBtreeEntryInsertData *insertData,
603 : BlockNumber updateblkno,
604 : Page *newlpage, Page *newrpage)
605 : {
606 210 : OffsetNumber off = stack->off;
607 : OffsetNumber i,
608 : maxoff,
609 210 : separator = InvalidOffsetNumber;
610 210 : Size totalsize = 0;
611 210 : Size lsize = 0,
612 : size;
613 : char *ptr;
614 : IndexTuple itup;
615 : Page page;
616 210 : Page lpage = PageGetTempPageCopy(BufferGetPage(origbuf));
617 210 : Page rpage = PageGetTempPageCopy(BufferGetPage(origbuf));
618 210 : Size pageSize = PageGetPageSize(lpage);
619 : char tupstore[2 * BLCKSZ];
620 :
621 210 : entryPreparePage(btree, lpage, off, insertData, updateblkno);
622 :
623 : /*
624 : * First, append all the existing tuples and the new tuple we're inserting
625 : * one after another in a temporary workspace.
626 : */
627 210 : maxoff = PageGetMaxOffsetNumber(lpage);
628 210 : ptr = tupstore;
629 65872 : for (i = FirstOffsetNumber; i <= maxoff; i++)
630 : {
631 65662 : if (i == off)
632 : {
633 0 : size = MAXALIGN(IndexTupleSize(insertData->entry));
634 0 : memcpy(ptr, insertData->entry, size);
635 0 : ptr += size;
636 0 : totalsize += size + sizeof(ItemIdData);
637 : }
638 :
639 65662 : itup = (IndexTuple) PageGetItem(lpage, PageGetItemId(lpage, i));
640 65662 : size = MAXALIGN(IndexTupleSize(itup));
641 65662 : memcpy(ptr, itup, size);
642 65662 : ptr += size;
643 65662 : totalsize += size + sizeof(ItemIdData);
644 : }
645 :
646 210 : if (off == maxoff + 1)
647 : {
648 210 : size = MAXALIGN(IndexTupleSize(insertData->entry));
649 210 : memcpy(ptr, insertData->entry, size);
650 210 : ptr += size;
651 210 : totalsize += size + sizeof(ItemIdData);
652 : }
653 :
654 : /*
655 : * Initialize the left and right pages, and copy all the tuples back to
656 : * them.
657 : */
658 210 : GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize);
659 210 : GinInitPage(lpage, GinPageGetOpaque(rpage)->flags, pageSize);
660 :
661 210 : ptr = tupstore;
662 210 : maxoff++;
663 210 : lsize = 0;
664 :
665 210 : page = lpage;
666 66082 : for (i = FirstOffsetNumber; i <= maxoff; i++)
667 : {
668 65872 : itup = (IndexTuple) ptr;
669 :
670 : /*
671 : * Decide where to split. We try to equalize the pages' total data
672 : * size, not number of tuples.
673 : */
674 65872 : if (lsize > totalsize / 2)
675 : {
676 32864 : if (separator == InvalidOffsetNumber)
677 210 : separator = i - 1;
678 32864 : page = rpage;
679 : }
680 : else
681 : {
682 33008 : lsize += MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
683 : }
684 :
685 65872 : if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
686 0 : elog(ERROR, "failed to add item to index page in \"%s\"",
687 : RelationGetRelationName(btree->index));
688 65872 : ptr += MAXALIGN(IndexTupleSize(itup));
689 : }
690 :
691 : /* return temp pages to caller */
692 210 : *newlpage = lpage;
693 210 : *newrpage = rpage;
694 210 : }
695 :
696 : /*
697 : * Construct insertion payload for inserting the downlink for given buffer.
698 : */
699 : static void *
700 204 : entryPrepareDownlink(GinBtree btree, Buffer lbuf)
701 : {
702 : GinBtreeEntryInsertData *insertData;
703 204 : Page lpage = BufferGetPage(lbuf);
704 204 : BlockNumber lblkno = BufferGetBlockNumber(lbuf);
705 : IndexTuple itup;
706 :
707 204 : itup = getRightMostTuple(lpage);
708 :
709 204 : insertData = palloc(sizeof(GinBtreeEntryInsertData));
710 204 : insertData->entry = GinFormInteriorTuple(itup, lpage, lblkno);
711 204 : insertData->isDelete = false;
712 :
713 204 : return insertData;
714 : }
715 :
716 : /*
717 : * Fills new root by rightest values from child.
718 : * Also called from ginxlog, should not use btree
719 : */
720 : void
721 6 : ginEntryFillRoot(GinBtree btree, Page root,
722 : BlockNumber lblkno, Page lpage,
723 : BlockNumber rblkno, Page rpage)
724 : {
725 : IndexTuple itup;
726 :
727 6 : itup = GinFormInteriorTuple(getRightMostTuple(lpage), lpage, lblkno);
728 6 : if (PageAddItem(root, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
729 0 : elog(ERROR, "failed to add item to index root page");
730 6 : pfree(itup);
731 :
732 6 : itup = GinFormInteriorTuple(getRightMostTuple(rpage), rpage, rblkno);
733 6 : if (PageAddItem(root, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
734 0 : elog(ERROR, "failed to add item to index root page");
735 6 : pfree(itup);
736 6 : }
737 :
738 : /*
739 : * Set up GinBtree for entry page access
740 : *
741 : * Note: during WAL recovery, there may be no valid data in ginstate
742 : * other than a faked-up Relation pointer; the key datum is bogus too.
743 : */
744 : void
745 41600 : ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum,
746 : Datum key, GinNullCategory category,
747 : GinState *ginstate)
748 : {
749 41600 : memset(btree, 0, sizeof(GinBtreeData));
750 :
751 41600 : btree->index = ginstate->index;
752 41600 : btree->rootBlkno = GIN_ROOT_BLKNO;
753 41600 : btree->ginstate = ginstate;
754 :
755 41600 : btree->findChildPage = entryLocateEntry;
756 41600 : btree->getLeftMostChild = entryGetLeftMostPage;
757 41600 : btree->isMoveRight = entryIsMoveRight;
758 41600 : btree->findItem = entryLocateLeafEntry;
759 41600 : btree->findChildPtr = entryFindChildPtr;
760 41600 : btree->beginPlaceToPage = entryBeginPlaceToPage;
761 41600 : btree->execPlaceToPage = entryExecPlaceToPage;
762 41600 : btree->fillRoot = ginEntryFillRoot;
763 41600 : btree->prepareDownlink = entryPrepareDownlink;
764 :
765 41600 : btree->isData = FALSE;
766 41600 : btree->fullScan = FALSE;
767 41600 : btree->isBuild = FALSE;
768 :
769 41600 : btree->entryAttnum = attnum;
770 41600 : btree->entryKey = key;
771 41600 : btree->entryCategory = category;
772 41600 : }
|