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