PostgreSQL Source Code git master
gist.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * gist.c
4 * interface routines for the postgres GiST index access method.
5 *
6 *
7 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
9 *
10 * IDENTIFICATION
11 * src/backend/access/gist/gist.c
12 *
13 *-------------------------------------------------------------------------
14 */
15#include "postgres.h"
16
17#include "access/gist_private.h"
18#include "access/gistscan.h"
19#include "access/xloginsert.h"
21#include "commands/vacuum.h"
22#include "miscadmin.h"
23#include "nodes/execnodes.h"
24#include "storage/predicate.h"
25#include "utils/fmgrprotos.h"
27#include "utils/memutils.h"
28#include "utils/rel.h"
29
30/* non-export function prototypes */
31static void gistfixsplit(GISTInsertState *state, GISTSTATE *giststate);
33 GISTSTATE *giststate, IndexTuple tuple, OffsetNumber oldoffnum);
35 GISTSTATE *giststate,
36 IndexTuple *tuples, int ntup, OffsetNumber oldoffnum,
38 bool unlockbuf, bool unlockleftchild);
40 GISTSTATE *giststate, List *splitinfo, bool unlockbuf);
41static void gistprunepage(Relation rel, Page page, Buffer buffer,
42 Relation heapRel);
43
44
45#define ROTATEDIST(d) do { \
46 SplitPageLayout *tmp = palloc0_object(SplitPageLayout); \
47 tmp->block.blkno = InvalidBlockNumber; \
48 tmp->buffer = InvalidBuffer; \
49 tmp->next = (d); \
50 (d)=tmp; \
51} while(0)
52
53
54/*
55 * GiST handler function: return IndexAmRoutine with access method parameters
56 * and callbacks.
57 */
60{
61 static const IndexAmRoutine amroutine = {
62 .type = T_IndexAmRoutine,
63 .amstrategies = 0,
64 .amsupport = GISTNProcs,
65 .amoptsprocnum = GIST_OPTIONS_PROC,
66 .amcanorder = false,
67 .amcanorderbyop = true,
68 .amcanhash = false,
69 .amconsistentequality = false,
70 .amconsistentordering = false,
71 .amcanbackward = false,
72 .amcanunique = false,
73 .amcanmulticol = true,
74 .amoptionalkey = true,
75 .amsearcharray = false,
76 .amsearchnulls = true,
77 .amstorage = true,
78 .amclusterable = true,
79 .ampredlocks = true,
80 .amcanparallel = false,
81 .amcanbuildparallel = false,
82 .amcaninclude = true,
83 .amusemaintenanceworkmem = false,
84 .amsummarizing = false,
85 .amparallelvacuumoptions =
87 .amkeytype = InvalidOid,
88
89 .ambuild = gistbuild,
90 .ambuildempty = gistbuildempty,
91 .aminsert = gistinsert,
92 .aminsertcleanup = NULL,
93 .ambulkdelete = gistbulkdelete,
94 .amvacuumcleanup = gistvacuumcleanup,
95 .amcanreturn = gistcanreturn,
96 .amcostestimate = gistcostestimate,
97 .amgettreeheight = NULL,
98 .amoptions = gistoptions,
99 .amproperty = gistproperty,
100 .ambuildphasename = NULL,
101 .amvalidate = gistvalidate,
102 .amadjustmembers = gistadjustmembers,
103 .ambeginscan = gistbeginscan,
104 .amrescan = gistrescan,
105 .amgettuple = gistgettuple,
106 .amgetbitmap = gistgetbitmap,
107 .amendscan = gistendscan,
108 .ammarkpos = NULL,
109 .amrestrpos = NULL,
110 .amestimateparallelscan = NULL,
111 .aminitparallelscan = NULL,
112 .amparallelrescan = NULL,
113 .amtranslatestrategy = NULL,
114 .amtranslatecmptype = gisttranslatecmptype,
115 };
116
117 PG_RETURN_POINTER(&amroutine);
118}
119
120/*
121 * Create and return a temporary memory context for use by GiST. We
122 * _always_ invoke user-provided methods in a temporary memory
123 * context, so that memory leaks in those functions cannot cause
124 * problems. Also, we use some additional temporary contexts in the
125 * GiST code itself, to avoid the need to do some awkward manual
126 * memory management.
127 */
130{
132 "GiST temporary context",
134}
135
136/*
137 * gistbuildempty() -- build an empty gist index in the initialization fork
138 */
139void
141{
142 Buffer buffer;
143
144 /* Initialize the root page */
147
148 /* Initialize and xlog buffer */
150 GISTInitBuffer(buffer, F_LEAF);
151 MarkBufferDirty(buffer);
152 log_newpage_buffer(buffer, true);
154
155 /* Unlock and release the buffer */
156 UnlockReleaseBuffer(buffer);
157}
158
159/*
160 * gistinsert -- wrapper for GiST tuple insertion.
161 *
162 * This is the public interface routine for tuple insertion in GiSTs.
163 * It doesn't do any work; just locks the relation and passes the buck.
164 */
165bool
166gistinsert(Relation r, Datum *values, bool *isnull,
167 ItemPointer ht_ctid, Relation heapRel,
168 IndexUniqueCheck checkUnique,
169 bool indexUnchanged,
170 IndexInfo *indexInfo)
171{
172 GISTSTATE *giststate = (GISTSTATE *) indexInfo->ii_AmCache;
173 IndexTuple itup;
174 MemoryContext oldCxt;
175
176 /* Initialize GISTSTATE cache if first call in this statement */
177 if (giststate == NULL)
178 {
179 oldCxt = MemoryContextSwitchTo(indexInfo->ii_Context);
180 giststate = initGISTstate(r);
181 giststate->tempCxt = createTempGistContext();
182 indexInfo->ii_AmCache = giststate;
183 MemoryContextSwitchTo(oldCxt);
184 }
185
186 oldCxt = MemoryContextSwitchTo(giststate->tempCxt);
187
188 itup = gistFormTuple(giststate, r, values, isnull, true);
189 itup->t_tid = *ht_ctid;
190
191 gistdoinsert(r, itup, 0, giststate, heapRel, false);
192
193 /* cleanup */
194 MemoryContextSwitchTo(oldCxt);
195 MemoryContextReset(giststate->tempCxt);
196
197 return false;
198}
199
200
201/*
202 * Place tuples from 'itup' to 'buffer'. If 'oldoffnum' is valid, the tuple
203 * at that offset is atomically removed along with inserting the new tuples.
204 * This is used to replace a tuple with a new one.
205 *
206 * If 'leftchildbuf' is valid, we're inserting the downlink for the page
207 * to the right of 'leftchildbuf', or updating the downlink for 'leftchildbuf'.
208 * F_FOLLOW_RIGHT flag on 'leftchildbuf' is cleared and NSN is set.
209 *
210 * If 'markfollowright' is true and the page is split, the left child is
211 * marked with F_FOLLOW_RIGHT flag. That is the normal case. During buffered
212 * index build, however, there is no concurrent access and the page splitting
213 * is done in a slightly simpler fashion, and false is passed.
214 *
215 * If there is not enough room on the page, it is split. All the split
216 * pages are kept pinned and locked and returned in *splitinfo, the caller
217 * is responsible for inserting the downlinks for them. However, if
218 * 'buffer' is the root page and it needs to be split, gistplacetopage()
219 * performs the split as one atomic operation, and *splitinfo is set to NIL.
220 * In that case, we continue to hold the root page locked, and the child
221 * pages are released; note that new tuple(s) are *not* on the root page
222 * but in one of the new child pages.
223 *
224 * If 'newblkno' is not NULL, returns the block number of page the first
225 * new/updated tuple was inserted to. Usually it's the given page, but could
226 * be its right sibling if the page was split.
227 *
228 * Returns 'true' if the page was split, 'false' otherwise.
229 */
230bool
231gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
232 Buffer buffer,
233 IndexTuple *itup, int ntup, OffsetNumber oldoffnum,
234 BlockNumber *newblkno,
235 Buffer leftchildbuf,
236 List **splitinfo,
237 bool markfollowright,
238 Relation heapRel,
239 bool is_build)
240{
241 BlockNumber blkno = BufferGetBlockNumber(buffer);
242 Page page = BufferGetPage(buffer);
243 bool is_leaf = (GistPageIsLeaf(page)) ? true : false;
244 XLogRecPtr recptr;
245 bool is_split;
246
247 /*
248 * Refuse to modify a page that's incompletely split. This should not
249 * happen because we finish any incomplete splits while we walk down the
250 * tree. However, it's remotely possible that another concurrent inserter
251 * splits a parent page, and errors out before completing the split. We
252 * will just throw an error in that case, and leave any split we had in
253 * progress unfinished too. The next insert that comes along will clean up
254 * the mess.
255 */
256 if (GistFollowRight(page))
257 elog(ERROR, "concurrent GiST page split was incomplete");
258
259 /* should never try to insert to a deleted page */
261
262 *splitinfo = NIL;
263
264 /*
265 * if isupdate, remove old key: This node's key has been modified, either
266 * because a child split occurred or because we needed to adjust our key
267 * for an insert in a child node. Therefore, remove the old version of
268 * this node's key.
269 *
270 * for WAL replay, in the non-split case we handle this by setting up a
271 * one-element todelete array; in the split case, it's handled implicitly
272 * because the tuple vector passed to gistSplit won't include this tuple.
273 */
274 is_split = gistnospace(page, itup, ntup, oldoffnum, freespace);
275
276 /*
277 * If leaf page is full, try at first to delete dead tuples. And then
278 * check again.
279 */
280 if (is_split && GistPageIsLeaf(page) && GistPageHasGarbage(page))
281 {
282 gistprunepage(rel, page, buffer, heapRel);
283 is_split = gistnospace(page, itup, ntup, oldoffnum, freespace);
284 }
285
286 if (is_split)
287 {
288 /* no space for insertion */
289 IndexTuple *itvec;
290 int tlen;
291 SplitPageLayout *dist = NULL,
292 *ptr;
294 GistNSN oldnsn = 0;
295 SplitPageLayout rootpg;
296 bool is_rootsplit;
297 int npage;
298
299 is_rootsplit = (blkno == GIST_ROOT_BLKNO);
300
301 /*
302 * Form index tuples vector to split. If we're replacing an old tuple,
303 * remove the old version from the vector.
304 */
305 itvec = gistextractpage(page, &tlen);
306 if (OffsetNumberIsValid(oldoffnum))
307 {
308 /* on inner page we should remove old tuple */
309 int pos = oldoffnum - FirstOffsetNumber;
310
311 tlen--;
312 if (pos != tlen)
313 memmove(itvec + pos, itvec + pos + 1, sizeof(IndexTuple) * (tlen - pos));
314 }
315 itvec = gistjoinvector(itvec, &tlen, itup, ntup);
316 dist = gistSplit(rel, page, itvec, tlen, giststate);
317
318 /*
319 * Check that split didn't produce too many pages.
320 */
321 npage = 0;
322 for (ptr = dist; ptr; ptr = ptr->next)
323 npage++;
324 /* in a root split, we'll add one more page to the list below */
325 if (is_rootsplit)
326 npage++;
327 if (npage > GIST_MAX_SPLIT_PAGES)
328 elog(ERROR, "GiST page split into too many halves (%d, maximum %d)",
329 npage, GIST_MAX_SPLIT_PAGES);
330
331 /*
332 * Set up pages to work with. Allocate new buffers for all but the
333 * leftmost page. The original page becomes the new leftmost page, and
334 * is just replaced with the new contents.
335 *
336 * For a root-split, allocate new buffers for all child pages, the
337 * original page is overwritten with new root page containing
338 * downlinks to the new child pages.
339 */
340 ptr = dist;
341 if (!is_rootsplit)
342 {
343 /* save old rightlink and NSN */
344 oldrlink = GistPageGetOpaque(page)->rightlink;
345 oldnsn = GistPageGetNSN(page);
346
347 dist->buffer = buffer;
348 dist->block.blkno = BufferGetBlockNumber(buffer);
350
351 /* clean all flags except F_LEAF */
352 GistPageGetOpaque(dist->page)->flags = (is_leaf) ? F_LEAF : 0;
353
354 ptr = ptr->next;
355 }
356 for (; ptr; ptr = ptr->next)
357 {
358 /* Allocate new page */
359 ptr->buffer = gistNewBuffer(rel, heapRel);
360 GISTInitBuffer(ptr->buffer, (is_leaf) ? F_LEAF : 0);
361 ptr->page = BufferGetPage(ptr->buffer);
362 ptr->block.blkno = BufferGetBlockNumber(ptr->buffer);
364 BufferGetBlockNumber(buffer),
365 BufferGetBlockNumber(ptr->buffer));
366 }
367
368 /*
369 * Now that we know which blocks the new pages go to, set up downlink
370 * tuples to point to them.
371 */
372 for (ptr = dist; ptr; ptr = ptr->next)
373 {
374 ItemPointerSetBlockNumber(&(ptr->itup->t_tid), ptr->block.blkno);
375 GistTupleSetValid(ptr->itup);
376 }
377
378 /*
379 * If this is a root split, we construct the new root page with the
380 * downlinks here directly, instead of requiring the caller to insert
381 * them. Add the new root page to the list along with the child pages.
382 */
383 if (is_rootsplit)
384 {
385 IndexTuple *downlinks;
386 int ndownlinks = 0;
387 int i;
388
389 rootpg.buffer = buffer;
391 GistPageGetOpaque(rootpg.page)->flags = 0;
392
393 /* Prepare a vector of all the downlinks */
394 for (ptr = dist; ptr; ptr = ptr->next)
395 ndownlinks++;
396 downlinks = palloc_array(IndexTuple, ndownlinks);
397 for (i = 0, ptr = dist; ptr; ptr = ptr->next)
398 downlinks[i++] = ptr->itup;
399
400 rootpg.block.blkno = GIST_ROOT_BLKNO;
401 rootpg.block.num = ndownlinks;
402 rootpg.list = gistfillitupvec(downlinks, ndownlinks,
403 &(rootpg.lenlist));
404 rootpg.itup = NULL;
405
406 rootpg.next = dist;
407 dist = &rootpg;
408 }
409 else
410 {
411 /* Prepare split-info to be returned to caller */
412 for (ptr = dist; ptr; ptr = ptr->next)
413 {
415
416 si->buf = ptr->buffer;
417 si->downlink = ptr->itup;
418 *splitinfo = lappend(*splitinfo, si);
419 }
420 }
421
422 /*
423 * Fill all pages. All the pages are new, ie. freshly allocated empty
424 * pages, or a temporary copy of the old page.
425 */
426 for (ptr = dist; ptr; ptr = ptr->next)
427 {
428 char *data = (char *) (ptr->list);
429
430 for (int i = 0; i < ptr->block.num; i++)
431 {
432 IndexTuple thistup = (IndexTuple) data;
433
434 if (PageAddItem(ptr->page, data, IndexTupleSize(thistup), i + FirstOffsetNumber, false, false) == InvalidOffsetNumber)
435 elog(ERROR, "failed to add item to index page in \"%s\"", RelationGetRelationName(rel));
436
437 /*
438 * If this is the first inserted/updated tuple, let the caller
439 * know which page it landed on.
440 */
441 if (newblkno && ItemPointerEquals(&thistup->t_tid, &(*itup)->t_tid))
442 *newblkno = ptr->block.blkno;
443
444 data += IndexTupleSize(thistup);
445 }
446
447 /* Set up rightlinks */
448 if (ptr->next && ptr->block.blkno != GIST_ROOT_BLKNO)
449 GistPageGetOpaque(ptr->page)->rightlink =
450 ptr->next->block.blkno;
451 else
452 GistPageGetOpaque(ptr->page)->rightlink = oldrlink;
453
454 /*
455 * Mark the all but the right-most page with the follow-right
456 * flag. It will be cleared as soon as the downlink is inserted
457 * into the parent, but this ensures that if we error out before
458 * that, the index is still consistent. (in buffering build mode,
459 * any error will abort the index build anyway, so this is not
460 * needed.)
461 */
462 if (ptr->next && !is_rootsplit && markfollowright)
463 GistMarkFollowRight(ptr->page);
464 else
465 GistClearFollowRight(ptr->page);
466
467 /*
468 * Copy the NSN of the original page to all pages. The
469 * F_FOLLOW_RIGHT flags ensure that scans will follow the
470 * rightlinks until the downlinks are inserted.
471 */
472 GistPageSetNSN(ptr->page, oldnsn);
473 }
474
475 /*
476 * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
477 * insertion for that. NB: The number of pages and data segments
478 * specified here must match the calculations in gistXLogSplit()!
479 */
480 if (!is_build && RelationNeedsWAL(rel))
481 XLogEnsureRecordSpace(npage, 1 + npage * 2);
482
484
485 /*
486 * Must mark buffers dirty before XLogInsert, even though we'll still
487 * be changing their opaque fields below.
488 */
489 for (ptr = dist; ptr; ptr = ptr->next)
490 MarkBufferDirty(ptr->buffer);
491 if (BufferIsValid(leftchildbuf))
492 MarkBufferDirty(leftchildbuf);
493
494 /*
495 * The first page in the chain was a temporary working copy meant to
496 * replace the old page. Copy it over the old page.
497 */
499 dist->page = BufferGetPage(dist->buffer);
500
501 /*
502 * Write the WAL record.
503 *
504 * If we're building a new index, however, we don't WAL-log changes
505 * yet. The LSN-NSN interlock between parent and child requires that
506 * LSNs never move backwards, so set the LSNs to a value that's
507 * smaller than any real or fake unlogged LSN that might be generated
508 * later. (There can't be any concurrent scans during index build, so
509 * we don't need to be able to detect concurrent splits yet.)
510 */
511 if (is_build)
512 recptr = GistBuildLSN;
513 else
514 {
515 if (RelationNeedsWAL(rel))
516 recptr = gistXLogSplit(is_leaf,
517 dist, oldrlink, oldnsn, leftchildbuf,
518 markfollowright);
519 else
520 recptr = gistGetFakeLSN(rel);
521 }
522
523 for (ptr = dist; ptr; ptr = ptr->next)
524 PageSetLSN(ptr->page, recptr);
525
526 /*
527 * Return the new child buffers to the caller.
528 *
529 * If this was a root split, we've already inserted the downlink
530 * pointers, in the form of a new root page. Therefore we can release
531 * all the new buffers, and keep just the root page locked.
532 */
533 if (is_rootsplit)
534 {
535 for (ptr = dist->next; ptr; ptr = ptr->next)
536 UnlockReleaseBuffer(ptr->buffer);
537 }
538 }
539 else
540 {
541 /*
542 * Enough space. We always get here if ntup==0.
543 */
545
546 /*
547 * Delete old tuple if any, then insert new tuple(s) if any. If
548 * possible, use the fast path of PageIndexTupleOverwrite.
549 */
550 if (OffsetNumberIsValid(oldoffnum))
551 {
552 if (ntup == 1)
553 {
554 /* One-for-one replacement, so use PageIndexTupleOverwrite */
555 if (!PageIndexTupleOverwrite(page, oldoffnum, *itup, IndexTupleSize(*itup)))
556 elog(ERROR, "failed to add item to index page in \"%s\"",
558 }
559 else
560 {
561 /* Delete old, then append new tuple(s) to page */
562 PageIndexTupleDelete(page, oldoffnum);
563 gistfillbuffer(page, itup, ntup, InvalidOffsetNumber);
564 }
565 }
566 else
567 {
568 /* Just append new tuples at the end of the page */
569 gistfillbuffer(page, itup, ntup, InvalidOffsetNumber);
570 }
571
572 MarkBufferDirty(buffer);
573
574 if (BufferIsValid(leftchildbuf))
575 MarkBufferDirty(leftchildbuf);
576
577 if (is_build)
578 recptr = GistBuildLSN;
579 else
580 {
581 if (RelationNeedsWAL(rel))
582 {
583 OffsetNumber ndeloffs = 0,
584 deloffs[1];
585
586 if (OffsetNumberIsValid(oldoffnum))
587 {
588 deloffs[0] = oldoffnum;
589 ndeloffs = 1;
590 }
591
592 recptr = gistXLogUpdate(buffer,
593 deloffs, ndeloffs, itup, ntup,
594 leftchildbuf);
595 }
596 else
597 recptr = gistGetFakeLSN(rel);
598 }
599 PageSetLSN(page, recptr);
600
601 if (newblkno)
602 *newblkno = blkno;
603 }
604
605 /*
606 * If we inserted the downlink for a child page, set NSN and clear
607 * F_FOLLOW_RIGHT flag on the left child, so that concurrent scans know to
608 * follow the rightlink if and only if they looked at the parent page
609 * before we inserted the downlink.
610 *
611 * Note that we do this *after* writing the WAL record. That means that
612 * the possible full page image in the WAL record does not include these
613 * changes, and they must be replayed even if the page is restored from
614 * the full page image. There's a chicken-and-egg problem: if we updated
615 * the child pages first, we wouldn't know the recptr of the WAL record
616 * we're about to write.
617 */
618 if (BufferIsValid(leftchildbuf))
619 {
620 Page leftpg = BufferGetPage(leftchildbuf);
621
622 GistPageSetNSN(leftpg, recptr);
623 GistClearFollowRight(leftpg);
624
625 PageSetLSN(leftpg, recptr);
626 }
627
629
630 return is_split;
631}
632
633/*
634 * Workhorse routine for doing insertion into a GiST index. Note that
635 * this routine assumes it is invoked in a short-lived memory context,
636 * so it does not bother releasing palloc'd allocations.
637 */
638void
640 GISTSTATE *giststate, Relation heapRel, bool is_build)
641{
642 ItemId iid;
643 IndexTuple idxtuple;
644 GISTInsertStack firststack;
645 GISTInsertStack *stack;
647 bool xlocked = false;
648
649 memset(&state, 0, sizeof(GISTInsertState));
650 state.freespace = freespace;
651 state.r = r;
652 state.heapRel = heapRel;
653 state.is_build = is_build;
654
655 /* Start from the root */
656 firststack.blkno = GIST_ROOT_BLKNO;
657 firststack.lsn = 0;
658 firststack.retry_from_parent = false;
659 firststack.parent = NULL;
661 state.stack = stack = &firststack;
662
663 /*
664 * Walk down along the path of smallest penalty, updating the parent
665 * pointers with the key we're inserting as we go. If we crash in the
666 * middle, the tree is consistent, although the possible parent updates
667 * were a waste.
668 */
669 for (;;)
670 {
671 /*
672 * If we split an internal page while descending the tree, we have to
673 * retry at the parent. (Normally, the LSN-NSN interlock below would
674 * also catch this and cause us to retry. But LSNs are not updated
675 * during index build.)
676 */
677 while (stack->retry_from_parent)
678 {
679 if (xlocked)
681 xlocked = false;
682 ReleaseBuffer(stack->buffer);
683 state.stack = stack = stack->parent;
684 }
685
686 if (!XLogRecPtrIsValid(stack->lsn))
687 stack->buffer = ReadBuffer(state.r, stack->blkno);
688
689 /*
690 * Be optimistic and grab shared lock first. Swap it for an exclusive
691 * lock later if we need to update the page.
692 */
693 if (!xlocked)
694 {
696 gistcheckpage(state.r, stack->buffer);
697 }
698
699 stack->page = BufferGetPage(stack->buffer);
700 stack->lsn = xlocked ?
701 PageGetLSN(stack->page) : BufferGetLSNAtomic(stack->buffer);
703
704 /*
705 * If this page was split but the downlink was never inserted to the
706 * parent because the inserting backend crashed before doing that, fix
707 * that now.
708 */
709 if (GistFollowRight(stack->page))
710 {
711 if (!xlocked)
712 {
715 xlocked = true;
716 /* someone might've completed the split when we unlocked */
717 if (!GistFollowRight(stack->page))
718 continue;
719 }
720 gistfixsplit(&state, giststate);
721
723 xlocked = false;
724 state.stack = stack = stack->parent;
725 continue;
726 }
727
728 if ((stack->blkno != GIST_ROOT_BLKNO &&
729 stack->parent->lsn < GistPageGetNSN(stack->page)) ||
730 GistPageIsDeleted(stack->page))
731 {
732 /*
733 * Concurrent split or page deletion detected. There's no
734 * guarantee that the downlink for this page is consistent with
735 * the tuple we're inserting anymore, so go back to parent and
736 * rechoose the best child.
737 */
739 xlocked = false;
740 state.stack = stack = stack->parent;
741 continue;
742 }
743
744 if (!GistPageIsLeaf(stack->page))
745 {
746 /*
747 * This is an internal page so continue to walk down the tree.
748 * Find the child node that has the minimum insertion penalty.
749 */
750 BlockNumber childblkno;
751 IndexTuple newtup;
752 GISTInsertStack *item;
753 OffsetNumber downlinkoffnum;
754
755 downlinkoffnum = gistchoose(state.r, stack->page, itup, giststate);
756 iid = PageGetItemId(stack->page, downlinkoffnum);
757 idxtuple = (IndexTuple) PageGetItem(stack->page, iid);
758 childblkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
759
760 /*
761 * Check that it's not a leftover invalid tuple from pre-9.1
762 */
763 if (GistTupleIsInvalid(idxtuple))
765 (errmsg("index \"%s\" contains an inner tuple marked as invalid",
767 errdetail("This is caused by an incomplete page split at crash recovery before upgrading to PostgreSQL 9.1."),
768 errhint("Please REINDEX it.")));
769
770 /*
771 * Check that the key representing the target child node is
772 * consistent with the key we're inserting. Update it if it's not.
773 */
774 newtup = gistgetadjusted(state.r, idxtuple, itup, giststate);
775 if (newtup)
776 {
777 /*
778 * Swap shared lock for an exclusive one. Beware, the page may
779 * change while we unlock/lock the page...
780 */
781 if (!xlocked)
782 {
785 xlocked = true;
786 stack->page = BufferGetPage(stack->buffer);
787
788 if (PageGetLSN(stack->page) != stack->lsn)
789 {
790 /* the page was changed while we unlocked it, retry */
791 continue;
792 }
793 }
794
795 /*
796 * Update the tuple.
797 *
798 * We still hold the lock after gistinserttuple(), but it
799 * might have to split the page to make the updated tuple fit.
800 * In that case the updated tuple might migrate to the other
801 * half of the split, so we have to go back to the parent and
802 * descend back to the half that's a better fit for the new
803 * tuple.
804 */
805 if (gistinserttuple(&state, stack, giststate, newtup,
806 downlinkoffnum))
807 {
808 /*
809 * If this was a root split, the root page continues to be
810 * the parent and the updated tuple went to one of the
811 * child pages, so we just need to retry from the root
812 * page.
813 */
814 if (stack->blkno != GIST_ROOT_BLKNO)
815 {
817 xlocked = false;
818 state.stack = stack = stack->parent;
819 }
820 continue;
821 }
822 }
824 xlocked = false;
825
826 /* descend to the chosen child */
828 item->blkno = childblkno;
829 item->parent = stack;
830 item->downlinkoffnum = downlinkoffnum;
831 state.stack = stack = item;
832 }
833 else
834 {
835 /*
836 * Leaf page. Insert the new key. We've already updated all the
837 * parents on the way down, but we might have to split the page if
838 * it doesn't fit. gistinserttuple() will take care of that.
839 */
840
841 /*
842 * Swap shared lock for an exclusive one. Be careful, the page may
843 * change while we unlock/lock the page...
844 */
845 if (!xlocked)
846 {
849 xlocked = true;
850 stack->page = BufferGetPage(stack->buffer);
851 stack->lsn = PageGetLSN(stack->page);
852
853 if (stack->blkno == GIST_ROOT_BLKNO)
854 {
855 /*
856 * the only page that can become inner instead of leaf is
857 * the root page, so for root we should recheck it
858 */
859 if (!GistPageIsLeaf(stack->page))
860 {
861 /*
862 * very rare situation: during unlock/lock index with
863 * number of pages = 1 was increased
864 */
866 xlocked = false;
867 continue;
868 }
869
870 /*
871 * we don't need to check root split, because checking
872 * leaf/inner is enough to recognize split for root
873 */
874 }
875 else if ((GistFollowRight(stack->page) ||
876 stack->parent->lsn < GistPageGetNSN(stack->page)) ||
877 GistPageIsDeleted(stack->page))
878 {
879 /*
880 * The page was split or deleted while we momentarily
881 * unlocked the page. Go back to parent.
882 */
884 xlocked = false;
885 state.stack = stack = stack->parent;
886 continue;
887 }
888 }
889
890 /* now state.stack->(page, buffer and blkno) points to leaf page */
891
892 gistinserttuple(&state, stack, giststate, itup,
895
896 /* Release any pins we might still hold before exiting */
897 for (; stack; stack = stack->parent)
898 ReleaseBuffer(stack->buffer);
899 break;
900 }
901 }
902}
903
904/*
905 * Traverse the tree to find path from root page to specified "child" block.
906 *
907 * returns a new insertion stack, starting from the parent of "child", up
908 * to the root. *downlinkoffnum is set to the offset of the downlink in the
909 * direct parent of child.
910 *
911 * To prevent deadlocks, this should lock only one page at a time.
912 */
913static GISTInsertStack *
915{
916 Page page;
917 Buffer buffer;
919 maxoff;
920 ItemId iid;
921 IndexTuple idxtuple;
922 List *fifo;
923 GISTInsertStack *top,
924 *ptr;
925 BlockNumber blkno;
926
928 top->blkno = GIST_ROOT_BLKNO;
930
931 fifo = list_make1(top);
932 while (fifo != NIL)
933 {
934 /* Get next page to visit */
935 top = linitial(fifo);
936 fifo = list_delete_first(fifo);
937
938 buffer = ReadBuffer(r, top->blkno);
939 LockBuffer(buffer, GIST_SHARE);
940 gistcheckpage(r, buffer);
941 page = BufferGetPage(buffer);
942
943 if (GistPageIsLeaf(page))
944 {
945 /*
946 * Because we scan the index top-down, all the rest of the pages
947 * in the queue must be leaf pages as well.
948 */
949 UnlockReleaseBuffer(buffer);
950 break;
951 }
952
953 /* currently, internal pages are never deleted */
955
956 top->lsn = BufferGetLSNAtomic(buffer);
957
958 /*
959 * If F_FOLLOW_RIGHT is set, the page to the right doesn't have a
960 * downlink. This should not normally happen..
961 */
962 if (GistFollowRight(page))
963 elog(ERROR, "concurrent GiST page split was incomplete");
964
965 if (top->parent && top->parent->lsn < GistPageGetNSN(page) &&
966 GistPageGetOpaque(page)->rightlink != InvalidBlockNumber /* sanity check */ )
967 {
968 /*
969 * Page was split while we looked elsewhere. We didn't see the
970 * downlink to the right page when we scanned the parent, so add
971 * it to the queue now.
972 *
973 * Put the right page ahead of the queue, so that we visit it
974 * next. That's important, because if this is the lowest internal
975 * level, just above leaves, we might already have queued up some
976 * leaf pages, and we assume that there can't be any non-leaf
977 * pages behind leaf pages.
978 */
980 ptr->blkno = GistPageGetOpaque(page)->rightlink;
982 ptr->parent = top->parent;
983
984 fifo = lcons(ptr, fifo);
985 }
986
987 maxoff = PageGetMaxOffsetNumber(page);
988
989 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
990 {
991 iid = PageGetItemId(page, i);
992 idxtuple = (IndexTuple) PageGetItem(page, iid);
993 blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
994 if (blkno == child)
995 {
996 /* Found it! */
997 UnlockReleaseBuffer(buffer);
998 *downlinkoffnum = i;
999 return top;
1000 }
1001 else
1002 {
1003 /* Append this child to the list of pages to visit later */
1005 ptr->blkno = blkno;
1006 ptr->downlinkoffnum = i;
1007 ptr->parent = top;
1008
1009 fifo = lappend(fifo, ptr);
1010 }
1011 }
1012
1013 UnlockReleaseBuffer(buffer);
1014 }
1015
1016 elog(ERROR, "failed to re-find parent of a page in index \"%s\", block %u",
1017 RelationGetRelationName(r), child);
1018 return NULL; /* keep compiler quiet */
1019}
1020
1021/*
1022 * Updates the stack so that child->parent is the correct parent of the
1023 * child. child->parent must be exclusively locked on entry, and will
1024 * remain so at exit, but it might not be the same page anymore.
1025 */
1026static void
1028{
1029 GISTInsertStack *parent = child->parent;
1030 ItemId iid;
1031 IndexTuple idxtuple;
1032 OffsetNumber maxoff;
1033 GISTInsertStack *ptr;
1034
1035 gistcheckpage(r, parent->buffer);
1036 parent->page = BufferGetPage(parent->buffer);
1037 maxoff = PageGetMaxOffsetNumber(parent->page);
1038
1039 /* Check if the downlink is still where it was before */
1040 if (child->downlinkoffnum != InvalidOffsetNumber && child->downlinkoffnum <= maxoff)
1041 {
1042 iid = PageGetItemId(parent->page, child->downlinkoffnum);
1043 idxtuple = (IndexTuple) PageGetItem(parent->page, iid);
1044 if (ItemPointerGetBlockNumber(&(idxtuple->t_tid)) == child->blkno)
1045 return; /* still there */
1046 }
1047
1048 /*
1049 * The page has changed since we looked. During normal operation, every
1050 * update of a page changes its LSN, so the LSN we memorized should have
1051 * changed too.
1052 *
1053 * During index build, however, we don't WAL-log the changes until we have
1054 * built the index, so the LSN doesn't change. There is no concurrent
1055 * activity during index build, but we might have changed the parent
1056 * ourselves.
1057 *
1058 * We will also get here if child->downlinkoffnum is invalid. That happens
1059 * if 'parent' had been updated by an earlier call to this function on its
1060 * grandchild, which had to move right.
1061 */
1062 Assert(parent->lsn != PageGetLSN(parent->page) || is_build ||
1064
1065 /*
1066 * Scan the page to re-find the downlink. If the page was split, it might
1067 * have moved to a different page, so follow the right links until we find
1068 * it.
1069 */
1070 while (true)
1071 {
1073
1074 maxoff = PageGetMaxOffsetNumber(parent->page);
1075 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1076 {
1077 iid = PageGetItemId(parent->page, i);
1078 idxtuple = (IndexTuple) PageGetItem(parent->page, iid);
1079 if (ItemPointerGetBlockNumber(&(idxtuple->t_tid)) == child->blkno)
1080 {
1081 /* yes!!, found */
1082 child->downlinkoffnum = i;
1083 return;
1084 }
1085 }
1086
1087 parent->blkno = GistPageGetOpaque(parent->page)->rightlink;
1089 UnlockReleaseBuffer(parent->buffer);
1090 if (parent->blkno == InvalidBlockNumber)
1091 {
1092 /*
1093 * End of chain and still didn't find parent. It's a very-very
1094 * rare situation when the root was split.
1095 */
1096 break;
1097 }
1098 parent->buffer = ReadBuffer(r, parent->blkno);
1100 gistcheckpage(r, parent->buffer);
1101 parent->page = BufferGetPage(parent->buffer);
1102 }
1103
1104 /*
1105 * awful!!, we need search tree to find parent ... , but before we should
1106 * release all old parent
1107 */
1108
1109 ptr = child->parent->parent; /* child->parent already released above */
1110 while (ptr)
1111 {
1112 ReleaseBuffer(ptr->buffer);
1113 ptr = ptr->parent;
1114 }
1115
1116 /* ok, find new path */
1117 ptr = parent = gistFindPath(r, child->blkno, &child->downlinkoffnum);
1118
1119 /* read all buffers as expected by caller */
1120 /* note we don't lock them or gistcheckpage them here! */
1121 while (ptr)
1122 {
1123 ptr->buffer = ReadBuffer(r, ptr->blkno);
1124 ptr->page = BufferGetPage(ptr->buffer);
1125 ptr = ptr->parent;
1126 }
1127
1128 /* install new chain of parents to stack */
1129 child->parent = parent;
1130
1131 /* make recursive call to normal processing */
1133 gistFindCorrectParent(r, child, is_build);
1134}
1135
1136/*
1137 * Form a downlink pointer for the page in 'buf'.
1138 */
1139static IndexTuple
1141 GISTInsertStack *stack, bool is_build)
1142{
1143 Page page = BufferGetPage(buf);
1144 OffsetNumber maxoff;
1145 OffsetNumber offset;
1146 IndexTuple downlink = NULL;
1147
1148 maxoff = PageGetMaxOffsetNumber(page);
1149 for (offset = FirstOffsetNumber; offset <= maxoff; offset = OffsetNumberNext(offset))
1150 {
1151 IndexTuple ituple = (IndexTuple)
1152 PageGetItem(page, PageGetItemId(page, offset));
1153
1154 if (downlink == NULL)
1155 downlink = CopyIndexTuple(ituple);
1156 else
1157 {
1158 IndexTuple newdownlink;
1159
1160 newdownlink = gistgetadjusted(rel, downlink, ituple,
1161 giststate);
1162 if (newdownlink)
1163 downlink = newdownlink;
1164 }
1165 }
1166
1167 /*
1168 * If the page is completely empty, we can't form a meaningful downlink
1169 * for it. But we have to insert a downlink for the page. Any key will do,
1170 * as long as its consistent with the downlink of parent page, so that we
1171 * can legally insert it to the parent. A minimal one that matches as few
1172 * scans as possible would be best, to keep scans from doing useless work,
1173 * but we don't know how to construct that. So we just use the downlink of
1174 * the original page that was split - that's as far from optimal as it can
1175 * get but will do..
1176 */
1177 if (!downlink)
1178 {
1179 ItemId iid;
1180
1182 gistFindCorrectParent(rel, stack, is_build);
1183 iid = PageGetItemId(stack->parent->page, stack->downlinkoffnum);
1184 downlink = (IndexTuple) PageGetItem(stack->parent->page, iid);
1185 downlink = CopyIndexTuple(downlink);
1187 }
1188
1190 GistTupleSetValid(downlink);
1191
1192 return downlink;
1193}
1194
1195
1196/*
1197 * Complete the incomplete split of state->stack->page.
1198 */
1199static void
1201{
1202 GISTInsertStack *stack = state->stack;
1203 Buffer buf;
1204 Page page;
1205 List *splitinfo = NIL;
1206
1207 ereport(LOG,
1208 (errmsg("fixing incomplete split in index \"%s\", block %u",
1209 RelationGetRelationName(state->r), stack->blkno)));
1210
1211 Assert(GistFollowRight(stack->page));
1213
1214 buf = stack->buffer;
1215
1216 /*
1217 * Read the chain of split pages, following the rightlinks. Construct a
1218 * downlink tuple for each page.
1219 */
1220 for (;;)
1221 {
1223 IndexTuple downlink;
1224
1225 page = BufferGetPage(buf);
1226
1227 /* Form the new downlink tuples to insert to parent */
1228 downlink = gistformdownlink(state->r, buf, giststate, stack, state->is_build);
1229
1230 si->buf = buf;
1231 si->downlink = downlink;
1232
1233 splitinfo = lappend(splitinfo, si);
1234
1235 if (GistFollowRight(page))
1236 {
1237 /* lock next page */
1238 buf = ReadBuffer(state->r, GistPageGetOpaque(page)->rightlink);
1240 }
1241 else
1242 break;
1243 }
1244
1245 /* Insert the downlinks */
1246 gistfinishsplit(state, stack, giststate, splitinfo, false);
1247}
1248
1249/*
1250 * Insert or replace a tuple in stack->buffer. If 'oldoffnum' is valid, the
1251 * tuple at 'oldoffnum' is replaced, otherwise the tuple is inserted as new.
1252 * 'stack' represents the path from the root to the page being updated.
1253 *
1254 * The caller must hold an exclusive lock on stack->buffer. The lock is still
1255 * held on return, but the page might not contain the inserted tuple if the
1256 * page was split. The function returns true if the page was split, false
1257 * otherwise.
1258 */
1259static bool
1261 GISTSTATE *giststate, IndexTuple tuple, OffsetNumber oldoffnum)
1262{
1263 return gistinserttuples(state, stack, giststate, &tuple, 1, oldoffnum,
1264 InvalidBuffer, InvalidBuffer, false, false);
1265}
1266
1267/* ----------------
1268 * An extended workhorse version of gistinserttuple(). This version allows
1269 * inserting multiple tuples, or replacing a single tuple with multiple tuples.
1270 * This is used to recursively update the downlinks in the parent when a page
1271 * is split.
1272 *
1273 * If leftchild and rightchild are valid, we're inserting/replacing the
1274 * downlink for rightchild, and leftchild is its left sibling. We clear the
1275 * F_FOLLOW_RIGHT flag and update NSN on leftchild, atomically with the
1276 * insertion of the downlink.
1277 *
1278 * To avoid holding locks for longer than necessary, when recursing up the
1279 * tree to update the parents, the locking is a bit peculiar here. On entry,
1280 * the caller must hold an exclusive lock on stack->buffer, as well as
1281 * leftchild and rightchild if given. On return:
1282 *
1283 * - Lock on stack->buffer is released, if 'unlockbuf' is true. The page is
1284 * always kept pinned, however.
1285 * - Lock on 'leftchild' is released, if 'unlockleftchild' is true. The page
1286 * is kept pinned.
1287 * - Lock and pin on 'rightchild' are always released.
1288 *
1289 * Returns 'true' if the page had to be split. Note that if the page was
1290 * split, the inserted/updated tuples might've been inserted to a right
1291 * sibling of stack->buffer instead of stack->buffer itself.
1292 */
1293static bool
1295 GISTSTATE *giststate,
1296 IndexTuple *tuples, int ntup, OffsetNumber oldoffnum,
1298 bool unlockbuf, bool unlockleftchild)
1299{
1300 List *splitinfo;
1301 bool is_split;
1302
1303 /*
1304 * Check for any rw conflicts (in serializable isolation level) just
1305 * before we intend to modify the page
1306 */
1308
1309 /* Insert the tuple(s) to the page, splitting the page if necessary */
1310 is_split = gistplacetopage(state->r, state->freespace, giststate,
1311 stack->buffer,
1312 tuples, ntup,
1313 oldoffnum, NULL,
1314 leftchild,
1315 &splitinfo,
1316 true,
1317 state->heapRel,
1318 state->is_build);
1319
1320 /*
1321 * Before recursing up in case the page was split, release locks on the
1322 * child pages. We don't need to keep them locked when updating the
1323 * parent.
1324 */
1327 if (BufferIsValid(leftchild) && unlockleftchild)
1329
1330 /*
1331 * If we had to split, insert/update the downlinks in the parent. If the
1332 * caller requested us to release the lock on stack->buffer, tell
1333 * gistfinishsplit() to do that as soon as it's safe to do so. If we
1334 * didn't have to split, release it ourselves.
1335 */
1336 if (splitinfo)
1337 gistfinishsplit(state, stack, giststate, splitinfo, unlockbuf);
1338 else if (unlockbuf)
1339 LockBuffer(stack->buffer, GIST_UNLOCK);
1340
1341 return is_split;
1342}
1343
1344/*
1345 * Finish an incomplete split by inserting/updating the downlinks in parent
1346 * page. 'splitinfo' contains all the child pages involved in the split,
1347 * from left-to-right.
1348 *
1349 * On entry, the caller must hold a lock on stack->buffer and all the child
1350 * pages in 'splitinfo'. If 'unlockbuf' is true, the lock on stack->buffer is
1351 * released on return. The child pages are always unlocked and unpinned.
1352 */
1353static void
1355 GISTSTATE *giststate, List *splitinfo, bool unlockbuf)
1356{
1357 GISTPageSplitInfo *right;
1358 GISTPageSplitInfo *left;
1359 IndexTuple tuples[2];
1360
1361 /* A split always contains at least two halves */
1362 Assert(list_length(splitinfo) >= 2);
1363
1364 /*
1365 * We need to insert downlinks for each new page, and update the downlink
1366 * for the original (leftmost) page in the split. Begin at the rightmost
1367 * page, inserting one downlink at a time until there's only two pages
1368 * left. Finally insert the downlink for the last new page and update the
1369 * downlink for the original page as one operation.
1370 */
1372
1373 /*
1374 * Insert downlinks for the siblings from right to left, until there are
1375 * only two siblings left.
1376 */
1377 for (int pos = list_length(splitinfo) - 1; pos > 1; pos--)
1378 {
1379 right = (GISTPageSplitInfo *) list_nth(splitinfo, pos);
1380 left = (GISTPageSplitInfo *) list_nth(splitinfo, pos - 1);
1381
1382 gistFindCorrectParent(state->r, stack, state->is_build);
1383 if (gistinserttuples(state, stack->parent, giststate,
1384 &right->downlink, 1,
1386 left->buf, right->buf, false, false))
1387 {
1388 /*
1389 * If the parent page was split, the existing downlink might have
1390 * moved.
1391 */
1393 }
1394 /* gistinserttuples() released the lock on right->buf. */
1395 }
1396
1397 right = (GISTPageSplitInfo *) lsecond(splitinfo);
1398 left = (GISTPageSplitInfo *) linitial(splitinfo);
1399
1400 /*
1401 * Finally insert downlink for the remaining right page and update the
1402 * downlink for the original page to not contain the tuples that were
1403 * moved to the new pages.
1404 */
1405 tuples[0] = left->downlink;
1406 tuples[1] = right->downlink;
1407 gistFindCorrectParent(state->r, stack, state->is_build);
1408 (void) gistinserttuples(state, stack->parent, giststate,
1409 tuples, 2,
1410 stack->downlinkoffnum,
1411 left->buf, right->buf,
1412 true, /* Unlock parent */
1413 unlockbuf /* Unlock stack->buffer if caller
1414 * wants that */
1415 );
1416
1417 /*
1418 * The downlink might have moved when we updated it. Even if the page
1419 * wasn't split, because gistinserttuples() implements updating the old
1420 * tuple by removing and re-inserting it!
1421 */
1423
1424 Assert(left->buf == stack->buffer);
1425
1426 /*
1427 * If we split the page because we had to adjust the downlink on an
1428 * internal page, while descending the tree for inserting a new tuple,
1429 * then this might no longer be the correct page for the new tuple. The
1430 * downlink to this page might not cover the new tuple anymore, it might
1431 * need to go to the newly-created right sibling instead. Tell the caller
1432 * to walk back up the stack, to re-check at the parent which page to
1433 * insert to.
1434 *
1435 * Normally, the LSN-NSN interlock during the tree descend would also
1436 * detect that a concurrent split happened (by ourselves), and cause us to
1437 * retry at the parent. But that mechanism doesn't work during index
1438 * build, because we don't do WAL-logging, and don't update LSNs, during
1439 * index build.
1440 */
1441 stack->retry_from_parent = true;
1442}
1443
1444/*
1445 * gistSplit -- split a page in the tree and fill struct
1446 * used for XLOG and real writes buffers. Function is recursive, ie
1447 * it will split page until keys will fit in every page.
1448 */
1451 Page page,
1452 IndexTuple *itup, /* contains compressed entry */
1453 int len,
1454 GISTSTATE *giststate)
1455{
1456 IndexTuple *lvectup,
1457 *rvectup;
1459 int i;
1460 SplitPageLayout *res = NULL;
1461
1462 /* this should never recurse very deeply, but better safe than sorry */
1464
1465 /* there's no point in splitting an empty page */
1466 Assert(len > 0);
1467
1468 /*
1469 * If a single tuple doesn't fit on a page, no amount of splitting will
1470 * help.
1471 */
1472 if (len == 1)
1473 ereport(ERROR,
1474 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1475 errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
1476 IndexTupleSize(itup[0]), GiSTPageSize,
1478
1479 memset(v.spl_lisnull, true,
1480 sizeof(bool) * giststate->nonLeafTupdesc->natts);
1481 memset(v.spl_risnull, true,
1482 sizeof(bool) * giststate->nonLeafTupdesc->natts);
1483 gistSplitByKey(r, page, itup, len, giststate, &v, 0);
1484
1485 /* form left and right vector */
1486 lvectup = palloc_array(IndexTuple, len + 1);
1487 rvectup = palloc_array(IndexTuple, len + 1);
1488
1489 for (i = 0; i < v.splitVector.spl_nleft; i++)
1490 lvectup[i] = itup[v.splitVector.spl_left[i] - 1];
1491
1492 for (i = 0; i < v.splitVector.spl_nright; i++)
1493 rvectup[i] = itup[v.splitVector.spl_right[i] - 1];
1494
1495 /* finalize splitting (may need another split) */
1496 if (!gistfitpage(rvectup, v.splitVector.spl_nright))
1497 {
1498 res = gistSplit(r, page, rvectup, v.splitVector.spl_nright, giststate);
1499 }
1500 else
1501 {
1502 ROTATEDIST(res);
1504 res->list = gistfillitupvec(rvectup, v.splitVector.spl_nright, &(res->lenlist));
1505 res->itup = gistFormTuple(giststate, r, v.spl_rattr, v.spl_risnull, false);
1506 }
1507
1508 if (!gistfitpage(lvectup, v.splitVector.spl_nleft))
1509 {
1510 SplitPageLayout *resptr,
1511 *subres;
1512
1513 resptr = subres = gistSplit(r, page, lvectup, v.splitVector.spl_nleft, giststate);
1514
1515 /* install on list's tail */
1516 while (resptr->next)
1517 resptr = resptr->next;
1518
1519 resptr->next = res;
1520 res = subres;
1521 }
1522 else
1523 {
1524 ROTATEDIST(res);
1525 res->block.num = v.splitVector.spl_nleft;
1526 res->list = gistfillitupvec(lvectup, v.splitVector.spl_nleft, &(res->lenlist));
1527 res->itup = gistFormTuple(giststate, r, v.spl_lattr, v.spl_lisnull, false);
1528 }
1529
1530 return res;
1531}
1532
1533/*
1534 * Create a GISTSTATE and fill it with information about the index
1535 */
1536GISTSTATE *
1538{
1539 GISTSTATE *giststate;
1540 MemoryContext scanCxt;
1541 MemoryContext oldCxt;
1542 int i;
1543
1544 /* safety check to protect fixed-size arrays in GISTSTATE */
1545 if (index->rd_att->natts > INDEX_MAX_KEYS)
1546 elog(ERROR, "numberOfAttributes %d > %d",
1547 index->rd_att->natts, INDEX_MAX_KEYS);
1548
1549 /* Create the memory context that will hold the GISTSTATE */
1551 "GiST scan context",
1553 oldCxt = MemoryContextSwitchTo(scanCxt);
1554
1555 /* Create and fill in the GISTSTATE */
1556 giststate = palloc_object(GISTSTATE);
1557
1558 giststate->scanCxt = scanCxt;
1559 giststate->tempCxt = scanCxt; /* caller must change this if needed */
1560 giststate->leafTupdesc = index->rd_att;
1561
1562 /*
1563 * The truncated tupdesc for non-leaf index tuples, which doesn't contain
1564 * the INCLUDE attributes.
1565 *
1566 * It is used to form tuples during tuple adjustment and page split.
1567 * B-tree creates shortened tuple descriptor for every truncated tuple,
1568 * because it is doing this less often: it does not have to form truncated
1569 * tuples during page split. Also, B-tree is not adjusting tuples on
1570 * internal pages the way GiST does.
1571 */
1574
1576 {
1577 fmgr_info_copy(&(giststate->consistentFn[i]),
1579 scanCxt);
1580 fmgr_info_copy(&(giststate->unionFn[i]),
1582 scanCxt);
1583
1584 /* opclasses are not required to provide a Compress method */
1586 fmgr_info_copy(&(giststate->compressFn[i]),
1588 scanCxt);
1589 else
1590 giststate->compressFn[i].fn_oid = InvalidOid;
1591
1592 /* opclasses are not required to provide a Decompress method */
1594 fmgr_info_copy(&(giststate->decompressFn[i]),
1596 scanCxt);
1597 else
1598 giststate->decompressFn[i].fn_oid = InvalidOid;
1599
1600 fmgr_info_copy(&(giststate->penaltyFn[i]),
1602 scanCxt);
1603 fmgr_info_copy(&(giststate->picksplitFn[i]),
1605 scanCxt);
1606 fmgr_info_copy(&(giststate->equalFn[i]),
1608 scanCxt);
1609
1610 /* opclasses are not required to provide a Distance method */
1612 fmgr_info_copy(&(giststate->distanceFn[i]),
1614 scanCxt);
1615 else
1616 giststate->distanceFn[i].fn_oid = InvalidOid;
1617
1618 /* opclasses are not required to provide a Fetch method */
1620 fmgr_info_copy(&(giststate->fetchFn[i]),
1622 scanCxt);
1623 else
1624 giststate->fetchFn[i].fn_oid = InvalidOid;
1625
1626 /*
1627 * If the index column has a specified collation, we should honor that
1628 * while doing comparisons. However, we may have a collatable storage
1629 * type for a noncollatable indexed data type. If there's no index
1630 * collation then specify default collation in case the support
1631 * functions need collation. This is harmless if the support
1632 * functions don't care about collation, so we just do it
1633 * unconditionally. (We could alternatively call get_typcollation,
1634 * but that seems like expensive overkill --- there aren't going to be
1635 * any cases where a GiST storage type has a nondefault collation.)
1636 */
1637 if (OidIsValid(index->rd_indcollation[i]))
1638 giststate->supportCollation[i] = index->rd_indcollation[i];
1639 else
1640 giststate->supportCollation[i] = DEFAULT_COLLATION_OID;
1641 }
1642
1643 /* No opclass information for INCLUDE attributes */
1644 for (; i < index->rd_att->natts; i++)
1645 {
1646 giststate->consistentFn[i].fn_oid = InvalidOid;
1647 giststate->unionFn[i].fn_oid = InvalidOid;
1648 giststate->compressFn[i].fn_oid = InvalidOid;
1649 giststate->decompressFn[i].fn_oid = InvalidOid;
1650 giststate->penaltyFn[i].fn_oid = InvalidOid;
1651 giststate->picksplitFn[i].fn_oid = InvalidOid;
1652 giststate->equalFn[i].fn_oid = InvalidOid;
1653 giststate->distanceFn[i].fn_oid = InvalidOid;
1654 giststate->fetchFn[i].fn_oid = InvalidOid;
1655 giststate->supportCollation[i] = InvalidOid;
1656 }
1657
1658 MemoryContextSwitchTo(oldCxt);
1659
1660 return giststate;
1661}
1662
1663void
1665{
1666 /* It's sufficient to delete the scanCxt */
1667 MemoryContextDelete(giststate->scanCxt);
1668}
1669
1670/*
1671 * gistprunepage() -- try to remove LP_DEAD items from the given page.
1672 * Function assumes that buffer is exclusively locked.
1673 */
1674static void
1675gistprunepage(Relation rel, Page page, Buffer buffer, Relation heapRel)
1676{
1678 int ndeletable = 0;
1679 OffsetNumber offnum,
1680 maxoff;
1681
1682 Assert(GistPageIsLeaf(page));
1683
1684 /*
1685 * Scan over all items to see which ones need to be deleted according to
1686 * LP_DEAD flags.
1687 */
1688 maxoff = PageGetMaxOffsetNumber(page);
1689 for (offnum = FirstOffsetNumber;
1690 offnum <= maxoff;
1691 offnum = OffsetNumberNext(offnum))
1692 {
1693 ItemId itemId = PageGetItemId(page, offnum);
1694
1695 if (ItemIdIsDead(itemId))
1696 deletable[ndeletable++] = offnum;
1697 }
1698
1699 if (ndeletable > 0)
1700 {
1701 TransactionId snapshotConflictHorizon = InvalidTransactionId;
1702
1704 snapshotConflictHorizon =
1705 index_compute_xid_horizon_for_tuples(rel, heapRel, buffer,
1706 deletable, ndeletable);
1707
1709
1710 PageIndexMultiDelete(page, deletable, ndeletable);
1711
1712 /*
1713 * Mark the page as not containing any LP_DEAD items. This is not
1714 * certainly true (there might be some that have recently been marked,
1715 * but weren't included in our target-item list), but it will almost
1716 * always be true and it doesn't seem worth an additional page scan to
1717 * check it. Remember that F_HAS_GARBAGE is only a hint anyway.
1718 */
1720
1721 MarkBufferDirty(buffer);
1722
1723 /* XLOG stuff */
1724 if (RelationNeedsWAL(rel))
1725 {
1726 XLogRecPtr recptr;
1727
1728 recptr = gistXLogDelete(buffer,
1729 deletable, ndeletable,
1730 snapshotConflictHorizon,
1731 heapRel);
1732
1733 PageSetLSN(page, recptr);
1734 }
1735 else
1736 PageSetLSN(page, gistGetFakeLSN(rel));
1737
1739 }
1740
1741 /*
1742 * Note: if we didn't find any LP_DEAD items, then the page's
1743 * F_HAS_GARBAGE hint bit is falsely set. We do not bother expending a
1744 * separate write to clear it, however. We will clear it when we split
1745 * the page.
1746 */
1747}
uint32 BlockNumber
Definition: block.h:31
#define InvalidBlockNumber
Definition: block.h:33
static Datum values[MAXATTR]
Definition: bootstrap.c:153
int Buffer
Definition: buf.h:23
#define InvalidBuffer
Definition: buf.h:25
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:4318
void LockBuffer(Buffer buffer, BufferLockMode mode)
Definition: bufmgr.c:5699
Buffer ExtendBufferedRel(BufferManagerRelation bmr, ForkNumber forkNum, BufferAccessStrategy strategy, uint32 flags)
Definition: bufmgr.c:939
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:5461
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition: bufmgr.c:4594
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:5478
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:3037
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:839
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:436
@ EB_SKIP_EXTENSION_LOCK
Definition: bufmgr.h:75
@ EB_LOCK_FIRST
Definition: bufmgr.h:87
#define BMR_REL(p_rel)
Definition: bufmgr.h:114
static bool BufferIsValid(Buffer bufnum)
Definition: bufmgr.h:387
void PageRestoreTempPage(Page tempPage, Page oldPage)
Definition: bufpage.c:423
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:1160
bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, const void *newtup, Size newsize)
Definition: bufpage.c:1404
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:1051
Page PageGetTempPageCopySpecial(const PageData *page)
Definition: bufpage.c:401
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:243
static void * PageGetItem(PageData *page, const ItemIdData *itemId)
Definition: bufpage.h:353
static void PageSetLSN(Page page, XLogRecPtr lsn)
Definition: bufpage.h:390
PageData * Page
Definition: bufpage.h:81
static XLogRecPtr PageGetLSN(const PageData *page)
Definition: bufpage.h:385
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:471
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition: bufpage.h:371
uint32 TransactionId
Definition: c.h:671
#define OidIsValid(objectId)
Definition: c.h:788
size_t Size
Definition: c.h:624
int errdetail(const char *fmt,...)
Definition: elog.c:1216
int errhint(const char *fmt,...)
Definition: elog.c:1330
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#define LOG
Definition: elog.h:31
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define ereport(elevel,...)
Definition: elog.h:150
#define palloc_object(type)
Definition: fe_memutils.h:74
#define palloc_array(type, count)
Definition: fe_memutils.h:76
#define palloc0_object(type)
Definition: fe_memutils.h:75
void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt)
Definition: fmgr.c:581
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define rightchild(x)
Definition: fsmpage.c:30
#define leftchild(x)
Definition: fsmpage.c:29
TransactionId index_compute_xid_horizon_for_tuples(Relation irel, Relation hrel, Buffer ibuf, OffsetNumber *itemnos, int nitems)
Definition: genam.c:295
IndexUniqueCheck
Definition: genam.h:143
SplitPageLayout * gistSplit(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
Definition: gist.c:1450
GISTSTATE * initGISTstate(Relation index)
Definition: gist.c:1537
static GISTInsertStack * gistFindPath(Relation r, BlockNumber child, OffsetNumber *downlinkoffnum)
Definition: gist.c:914
void gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate, Relation heapRel, bool is_build)
Definition: gist.c:639
static void gistfixsplit(GISTInsertState *state, GISTSTATE *giststate)
Definition: gist.c:1200
static void gistprunepage(Relation rel, Page page, Buffer buffer, Relation heapRel)
Definition: gist.c:1675
bool gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate, Buffer buffer, IndexTuple *itup, int ntup, OffsetNumber oldoffnum, BlockNumber *newblkno, Buffer leftchildbuf, List **splitinfo, bool markfollowright, Relation heapRel, bool is_build)
Definition: gist.c:231
bool gistinsert(Relation r, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
Definition: gist.c:166
void gistbuildempty(Relation index)
Definition: gist.c:140
static bool gistinserttuples(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, IndexTuple *tuples, int ntup, OffsetNumber oldoffnum, Buffer leftchild, Buffer rightchild, bool unlockbuf, bool unlockleftchild)
Definition: gist.c:1294
MemoryContext createTempGistContext(void)
Definition: gist.c:129
void freeGISTstate(GISTSTATE *giststate)
Definition: gist.c:1664
static bool gistinserttuple(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, IndexTuple tuple, OffsetNumber oldoffnum)
Definition: gist.c:1260
#define ROTATEDIST(d)
Definition: gist.c:45
static void gistfinishsplit(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, List *splitinfo, bool unlockbuf)
Definition: gist.c:1354
static void gistFindCorrectParent(Relation r, GISTInsertStack *child, bool is_build)
Definition: gist.c:1027
static IndexTuple gistformdownlink(Relation rel, Buffer buf, GISTSTATE *giststate, GISTInsertStack *stack, bool is_build)
Definition: gist.c:1140
Datum gisthandler(PG_FUNCTION_ARGS)
Definition: gist.c:59
#define GIST_DECOMPRESS_PROC
Definition: gist.h:35
#define GIST_PICKSPLIT_PROC
Definition: gist.h:37
#define GistMarkFollowRight(page)
Definition: gist.h:184
#define F_LEAF
Definition: gist.h:49
#define GIST_CONSISTENT_PROC
Definition: gist.h:32
#define GistClearFollowRight(page)
Definition: gist.h:185
#define GIST_UNION_PROC
Definition: gist.h:33
#define GIST_FETCH_PROC
Definition: gist.h:40
#define GIST_COMPRESS_PROC
Definition: gist.h:34
#define GISTNProcs
Definition: gist.h:44
#define GistClearPageHasGarbage(page)
Definition: gist.h:181
#define GIST_PENALTY_PROC
Definition: gist.h:36
#define GistPageIsLeaf(page)
Definition: gist.h:170
#define GistFollowRight(page)
Definition: gist.h:183
#define GIST_OPTIONS_PROC
Definition: gist.h:41
#define GIST_DISTANCE_PROC
Definition: gist.h:39
#define GistPageSetNSN(page, val)
Definition: gist.h:188
#define GistPageIsDeleted(page)
Definition: gist.h:173
#define GistPageGetOpaque(page)
Definition: gist.h:168
#define GIST_EQUAL_PROC
Definition: gist.h:38
#define GistPageHasGarbage(page)
Definition: gist.h:179
#define GistPageGetNSN(page)
Definition: gist.h:187
XLogRecPtr GistNSN
Definition: gist.h:63
#define GistBuildLSN
Definition: gist.h:70
#define GIST_MAX_SPLIT_PAGES
Definition: gist_private.h:39
#define GistTupleSetValid(itup)
Definition: gist_private.h:289
#define GIST_UNLOCK
Definition: gist_private.h:44
#define GIST_ROOT_BLKNO
Definition: gist_private.h:262
#define GIST_EXCLUSIVE
Definition: gist_private.h:43
#define GiSTPageSize
Definition: gist_private.h:476
#define GistTupleIsInvalid(itup)
Definition: gist_private.h:288
#define GIST_SHARE
Definition: gist_private.h:42
IndexBuildResult * gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
Definition: gistbuild.c:179
bool gistgettuple(IndexScanDesc scan, ScanDirection dir)
Definition: gistget.c:612
int64 gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
Definition: gistget.c:745
bool gistcanreturn(Relation index, int attno)
Definition: gistget.c:797
IndexScanDesc gistbeginscan(Relation r, int nkeys, int norderbys)
Definition: gistscan.c:74
void gistendscan(IndexScanDesc scan)
Definition: gistscan.c:347
void gistrescan(IndexScanDesc scan, ScanKey key, int nkeys, ScanKey orderbys, int norderbys)
Definition: gistscan.c:127
void gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, int attno)
Definition: gistsplit.c:623
bytea * gistoptions(Datum reloptions, bool validate)
Definition: gistutil.c:912
Buffer gistNewBuffer(Relation r, Relation heaprel)
Definition: gistutil.c:824
bool gistproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
Definition: gistutil.c:933
bool gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
Definition: gistutil.c:59
void gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off)
Definition: gistutil.c:34
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, const Datum *attdata, const bool *isnull, bool isleaf)
Definition: gistutil.c:575
IndexTuple * gistextractpage(Page page, int *len)
Definition: gistutil.c:95
bool gistfitpage(IndexTuple *itvec, int len)
Definition: gistutil.c:79
IndexTuple gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
Definition: gistutil.c:316
OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, GISTSTATE *giststate)
Definition: gistutil.c:374
XLogRecPtr gistGetFakeLSN(Relation rel)
Definition: gistutil.c:1016
IndexTuple * gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen)
Definition: gistutil.c:114
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:773
StrategyNumber gisttranslatecmptype(CompareType cmptype, Oid opfamily)
Definition: gistutil.c:1098
void gistcheckpage(Relation rel, Buffer buf)
Definition: gistutil.c:785
IndexTupleData * gistfillitupvec(IndexTuple *vec, int veclen, int *memlen)
Definition: gistutil.c:127
IndexBulkDeleteResult * gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
Definition: gistvacuum.c:75
IndexBulkDeleteResult * gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: gistvacuum.c:59
void gistadjustmembers(Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
Definition: gistvalidate.c:288
bool gistvalidate(Oid opclassoid)
Definition: gistvalidate.c:32
XLogRecPtr gistXLogSplit(bool page_is_leaf, SplitPageLayout *dist, BlockNumber origrlink, GistNSN orignsn, Buffer leftchildbuf, bool markfollowright)
Definition: gistxlog.c:493
XLogRecPtr gistXLogDelete(Buffer buffer, OffsetNumber *todelete, int ntodelete, TransactionId snapshotConflictHorizon, Relation heaprel)
Definition: gistxlog.c:668
XLogRecPtr gistXLogUpdate(Buffer buffer, OffsetNumber *todelete, int ntodelete, IndexTuple *itup, int ituplen, Buffer leftchildbuf)
Definition: gistxlog.c:627
Assert(PointerIsAligned(start, uint64))
for(;;)
FmgrInfo * index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:917
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:883
IndexTuple CopyIndexTuple(IndexTuple source)
Definition: indextuple.c:547
int i
Definition: isn.c:77
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
bool ItemPointerEquals(const ItemPointerData *pointer1, const ItemPointerData *pointer2)
Definition: itemptr.c:35
static void ItemPointerSetBlockNumber(ItemPointerData *pointer, BlockNumber blockNumber)
Definition: itemptr.h:147
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition: itemptr.h:103
IndexTupleData * IndexTuple
Definition: itup.h:53
static Size IndexTupleSize(const IndexTupleData *itup)
Definition: itup.h:71
#define MaxIndexTuplesPerPage
Definition: itup.h:181
List * lappend(List *list, void *datum)
Definition: list.c:339
List * list_delete_first(List *list)
Definition: list.c:943
List * lcons(void *datum, List *list)
Definition: list.c:495
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:403
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:472
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
#define START_CRIT_SECTION()
Definition: miscadmin.h:150
#define END_CRIT_SECTION()
Definition: miscadmin.h:152
#define InvalidOffsetNumber
Definition: off.h:26
#define OffsetNumberIsValid(offsetNumber)
Definition: off.h:39
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
#define INDEX_MAX_KEYS
const void size_t len
const void * data
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define list_make1(x1)
Definition: pg_list.h:212
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define linitial(l)
Definition: pg_list.h:178
#define lsecond(l)
Definition: pg_list.h:183
static char buf[DEFAULT_XLOG_SEG_SIZE]
Definition: pg_test_fsync.c:71
uint64_t Datum
Definition: postgres.h:70
#define InvalidOid
Definition: postgres_ext.h:37
void PredicateLockPageSplit(Relation relation, BlockNumber oldblkno, BlockNumber newblkno)
Definition: predicate.c:3142
void CheckForSerializableConflictIn(Relation relation, const ItemPointerData *tid, BlockNumber blkno)
Definition: predicate.c:4334
#define RelationGetRelationName(relation)
Definition: rel.h:549
#define RelationNeedsWAL(relation)
Definition: rel.h:638
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:534
@ INIT_FORKNUM
Definition: relpath.h:61
void gistcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
Definition: selfuncs.c:8191
void check_stack_depth(void)
Definition: stack_depth.c:95
Oid fn_oid
Definition: fmgr.h:59
BlockNumber blkno
Definition: gist_private.h:210
OffsetNumber downlinkoffnum
Definition: gist_private.h:228
struct GISTInsertStack * parent
Definition: gist_private.h:231
IndexTuple downlink
Definition: gist_private.h:422
FmgrInfo fetchFn[INDEX_MAX_KEYS]
Definition: gist_private.h:94
TupleDesc leafTupdesc
Definition: gist_private.h:80
TupleDesc nonLeafTupdesc
Definition: gist_private.h:81
FmgrInfo penaltyFn[INDEX_MAX_KEYS]
Definition: gist_private.h:90
MemoryContext tempCxt
Definition: gist_private.h:78
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:97
FmgrInfo distanceFn[INDEX_MAX_KEYS]
Definition: gist_private.h:93
FmgrInfo consistentFn[INDEX_MAX_KEYS]
Definition: gist_private.h:86
MemoryContext scanCxt
Definition: gist_private.h:77
FmgrInfo decompressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:89
FmgrInfo compressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:88
FmgrInfo equalFn[INDEX_MAX_KEYS]
Definition: gist_private.h:92
FmgrInfo unionFn[INDEX_MAX_KEYS]
Definition: gist_private.h:87
FmgrInfo picksplitFn[INDEX_MAX_KEYS]
Definition: gist_private.h:91
int spl_nleft
Definition: gist.h:144
OffsetNumber * spl_right
Definition: gist.h:148
int spl_nright
Definition: gist.h:149
OffsetNumber * spl_left
Definition: gist.h:143
GIST_SPLITVEC splitVector
Definition: gist_private.h:237
Datum spl_lattr[INDEX_MAX_KEYS]
Definition: gist_private.h:239
bool spl_lisnull[INDEX_MAX_KEYS]
Definition: gist_private.h:241
Datum spl_rattr[INDEX_MAX_KEYS]
Definition: gist_private.h:243
bool spl_risnull[INDEX_MAX_KEYS]
Definition: gist_private.h:245
NodeTag type
Definition: amapi.h:234
void * ii_AmCache
Definition: execnodes.h:223
MemoryContext ii_Context
Definition: execnodes.h:226
ItemPointerData t_tid
Definition: itup.h:37
Definition: pg_list.h:54
gistxlogPage block
Definition: gist_private.h:193
struct SplitPageLayout * next
Definition: gist_private.h:200
IndexTuple itup
Definition: gist_private.h:196
IndexTupleData * list
Definition: gist_private.h:194
BlockNumber blkno
Definition: gist_private.h:186
Definition: type.h:96
Definition: regguts.h:323
#define InvalidTransactionId
Definition: transam.h:31
TupleDesc CreateTupleDescTruncatedCopy(TupleDesc tupdesc, int natts)
Definition: tupdesc.c:296
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition: vacuum.h:48
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition: vacuum.h:55
#define XLogStandbyInfoActive()
Definition: xlog.h:125
#define XLogRecPtrIsValid(r)
Definition: xlogdefs.h:29
uint64 XLogRecPtr
Definition: xlogdefs.h:21
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1259
void XLogEnsureRecordSpace(int max_block_id, int ndatas)
Definition: xloginsert.c:178