]> granicus.if.org Git - postgresql/blob - src/backend/access/gin/ginentrypage.c
Make sure that GIN fast-insert and regular code paths enforce the same
[postgresql] / src / backend / access / gin / ginentrypage.c
1 /*-------------------------------------------------------------------------
2  *
3  * ginentrypage.c
4  *        page utilities routines for the postgres inverted index access method.
5  *
6  *
7  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  * IDENTIFICATION
11  *                      $PostgreSQL: pgsql/src/backend/access/gin/ginentrypage.c,v 1.22 2009/10/02 21:14:04 tgl Exp $
12  *-------------------------------------------------------------------------
13  */
14
15 #include "postgres.h"
16
17 #include "access/gin.h"
18 #include "storage/bufmgr.h"
19 #include "utils/rel.h"
20
21 /*
22  * Form a tuple for entry tree.
23  *
24  * If the tuple would be too big to be stored, function throws a suitable
25  * error if errorTooBig is TRUE, or returns NULL if errorTooBig is FALSE.
26  *
27  * On leaf pages, Index tuple has non-traditional layout. Tuple may contain
28  * posting list or root blocknumber of posting tree.
29  * Macros: GinIsPostingTree(itup) / GinSetPostingTree(itup, blkno)
30  * 1) Posting list
31  *              - itup->t_info & INDEX_SIZE_MASK contains total size of tuple as usual
32  *              - ItemPointerGetBlockNumber(&itup->t_tid) contains original
33  *                size of tuple (without posting list).
34  *                Macros: GinGetOrigSizePosting(itup) / GinSetOrigSizePosting(itup,n)
35  *              - ItemPointerGetOffsetNumber(&itup->t_tid) contains number
36  *                of elements in posting list (number of heap itempointers)
37  *                Macros: GinGetNPosting(itup) / GinSetNPosting(itup,n)
38  *              - After standard part of tuple there is a posting list, ie, array
39  *                of heap itempointers
40  *                Macros: GinGetPosting(itup)
41  * 2) Posting tree
42  *              - itup->t_info & INDEX_SIZE_MASK contains size of tuple as usual
43  *              - ItemPointerGetBlockNumber(&itup->t_tid) contains block number of
44  *                root of posting tree
45  *              - ItemPointerGetOffsetNumber(&itup->t_tid) contains magic number
46  *                GIN_TREE_POSTING, which distinguishes this from posting-list case
47  *
48  * Attributes of an index tuple are different for single and multicolumn index.
49  * For single-column case, index tuple stores only value to be indexed.
50  * For multicolumn case, it stores two attributes: column number of value
51  * and value.
52  */
53 IndexTuple
54 GinFormTuple(Relation index, GinState *ginstate,
55                          OffsetNumber attnum, Datum key,
56                          ItemPointerData *ipd, uint32 nipd, bool errorTooBig)
57 {
58         bool            isnull[2] = {FALSE, FALSE};
59         IndexTuple      itup;
60         uint32          newsize;
61
62         if (ginstate->oneCol)
63                 itup = index_form_tuple(ginstate->origTupdesc, &key, isnull);
64         else
65         {
66                 Datum           datums[2];
67
68                 datums[0] = UInt16GetDatum(attnum);
69                 datums[1] = key;
70                 itup = index_form_tuple(ginstate->tupdesc[attnum - 1], datums, isnull);
71         }
72
73         GinSetOrigSizePosting(itup, IndexTupleSize(itup));
74
75         if (nipd > 0)
76         {
77                 newsize = MAXALIGN(SHORTALIGN(IndexTupleSize(itup)) + sizeof(ItemPointerData) * nipd);
78                 if (newsize > Min(INDEX_SIZE_MASK, GinMaxItemSize))
79                 {
80                         if (errorTooBig)
81                                 ereport(ERROR,
82                                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
83                                                  errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
84                                                                 (unsigned long) newsize,
85                                                                 (unsigned long) Min(INDEX_SIZE_MASK,
86                                                                                                         GinMaxItemSize),
87                                                                 RelationGetRelationName(index))));
88                         return NULL;
89                 }
90
91                 itup = repalloc(itup, newsize);
92
93                 /* set new size */
94                 itup->t_info &= ~INDEX_SIZE_MASK;
95                 itup->t_info |= newsize;
96
97                 if (ipd)
98                         memcpy(GinGetPosting(itup), ipd, sizeof(ItemPointerData) * nipd);
99                 GinSetNPosting(itup, nipd);
100         }
101         else
102         {
103                 /*
104                  * Gin tuple without any ItemPointers should be large enough to keep
105                  * one ItemPointer, to prevent inconsistency between
106                  * ginHeapTupleFastCollect and ginEntryInsert called by
107                  * ginHeapTupleInsert.  ginHeapTupleFastCollect forms tuple without
108                  * extra pointer to heap, but ginEntryInsert (called for pending list
109                  * cleanup during vacuum) will form the same tuple with one
110                  * ItemPointer.
111                  */
112                 newsize = MAXALIGN(SHORTALIGN(IndexTupleSize(itup)) + sizeof(ItemPointerData));
113                 if (newsize > Min(INDEX_SIZE_MASK, GinMaxItemSize))
114                 {
115                         if (errorTooBig)
116                                 ereport(ERROR,
117                                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
118                                                  errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
119                                                                 (unsigned long) newsize,
120                                                                 (unsigned long) Min(INDEX_SIZE_MASK,
121                                                                                                         GinMaxItemSize),
122                                                                 RelationGetRelationName(index))));
123                         return NULL;
124                 }
125
126                 GinSetNPosting(itup, 0);
127         }
128         return itup;
129 }
130
131 /*
132  * Sometimes we reduce the number of posting list items in a tuple after
133  * having built it with GinFormTuple.  This function adjusts the size
134  * fields to match.
135  */
136 void
137 GinShortenTuple(IndexTuple itup, uint32 nipd)
138 {
139         uint32          newsize;
140
141         Assert(nipd <= GinGetNPosting(itup));
142
143         newsize = MAXALIGN(SHORTALIGN(GinGetOrigSizePosting(itup)) + sizeof(ItemPointerData) * nipd);
144
145         Assert(newsize <= (itup->t_info & INDEX_SIZE_MASK));
146
147         itup->t_info &= ~INDEX_SIZE_MASK;
148         itup->t_info |= newsize;
149
150         GinSetNPosting(itup, nipd);
151 }
152
153 /*
154  * Entry tree is a "static", ie tuple never deletes from it,
155  * so we don't use right bound, we use rightest key instead.
156  */
157 static IndexTuple
158 getRightMostTuple(Page page)
159 {
160         OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
161
162         return (IndexTuple) PageGetItem(page, PageGetItemId(page, maxoff));
163 }
164
165 static bool
166 entryIsMoveRight(GinBtree btree, Page page)
167 {
168         IndexTuple      itup;
169
170         if (GinPageRightMost(page))
171                 return FALSE;
172
173         itup = getRightMostTuple(page);
174
175         if (compareAttEntries(btree->ginstate,
176                                                   btree->entryAttnum, btree->entryValue,
177                                                   gintuple_get_attrnum(btree->ginstate, itup),
178                                                   gin_index_getattr(btree->ginstate, itup)) > 0)
179                 return TRUE;
180
181         return FALSE;
182 }
183
184 /*
185  * Find correct tuple in non-leaf page. It supposed that
186  * page correctly choosen and searching value SHOULD be on page
187  */
188 static BlockNumber
189 entryLocateEntry(GinBtree btree, GinBtreeStack *stack)
190 {
191         OffsetNumber low,
192                                 high,
193                                 maxoff;
194         IndexTuple      itup = NULL;
195         int                     result;
196         Page            page = BufferGetPage(stack->buffer);
197
198         Assert(!GinPageIsLeaf(page));
199         Assert(!GinPageIsData(page));
200
201         if (btree->fullScan)
202         {
203                 stack->off = FirstOffsetNumber;
204                 stack->predictNumber *= PageGetMaxOffsetNumber(page);
205                 return btree->getLeftMostPage(btree, page);
206         }
207
208         low = FirstOffsetNumber;
209         maxoff = high = PageGetMaxOffsetNumber(page);
210         Assert(high >= low);
211
212         high++;
213
214         while (high > low)
215         {
216                 OffsetNumber mid = low + ((high - low) / 2);
217
218                 if (mid == maxoff && GinPageRightMost(page))
219                         /* Right infinity */
220                         result = -1;
221                 else
222                 {
223                         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
224                         result = compareAttEntries(btree->ginstate,
225                                                                            btree->entryAttnum, btree->entryValue,
226                                                                  gintuple_get_attrnum(btree->ginstate, itup),
227                                                                    gin_index_getattr(btree->ginstate, itup));
228                 }
229
230                 if (result == 0)
231                 {
232                         stack->off = mid;
233                         Assert(GinItemPointerGetBlockNumber(&(itup)->t_tid) != GIN_ROOT_BLKNO);
234                         return GinItemPointerGetBlockNumber(&(itup)->t_tid);
235                 }
236                 else if (result > 0)
237                         low = mid + 1;
238                 else
239                         high = mid;
240         }
241
242         Assert(high >= FirstOffsetNumber && high <= maxoff);
243
244         stack->off = high;
245         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, high));
246         Assert(GinItemPointerGetBlockNumber(&(itup)->t_tid) != GIN_ROOT_BLKNO);
247         return GinItemPointerGetBlockNumber(&(itup)->t_tid);
248 }
249
250 /*
251  * Searches correct position for value on leaf page.
252  * Page should be corrrectly choosen.
253  * Returns true if value found on page.
254  */
255 static bool
256 entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack)
257 {
258         Page            page = BufferGetPage(stack->buffer);
259         OffsetNumber low,
260                                 high;
261         IndexTuple      itup;
262
263         Assert(GinPageIsLeaf(page));
264         Assert(!GinPageIsData(page));
265
266         if (btree->fullScan)
267         {
268                 stack->off = FirstOffsetNumber;
269                 return TRUE;
270         }
271
272         low = FirstOffsetNumber;
273         high = PageGetMaxOffsetNumber(page);
274
275         if (high < low)
276         {
277                 stack->off = FirstOffsetNumber;
278                 return false;
279         }
280
281         high++;
282
283         while (high > low)
284         {
285                 OffsetNumber mid = low + ((high - low) / 2);
286                 int                     result;
287
288                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
289                 result = compareAttEntries(btree->ginstate,
290                                                                    btree->entryAttnum, btree->entryValue,
291                                                                  gintuple_get_attrnum(btree->ginstate, itup),
292                                                                    gin_index_getattr(btree->ginstate, itup));
293                 if (result == 0)
294                 {
295                         stack->off = mid;
296                         return true;
297                 }
298                 else if (result > 0)
299                         low = mid + 1;
300                 else
301                         high = mid;
302         }
303
304         stack->off = high;
305         return false;
306 }
307
308 static OffsetNumber
309 entryFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff)
310 {
311         OffsetNumber i,
312                                 maxoff = PageGetMaxOffsetNumber(page);
313         IndexTuple      itup;
314
315         Assert(!GinPageIsLeaf(page));
316         Assert(!GinPageIsData(page));
317
318         /* if page isn't changed, we returns storedOff */
319         if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
320         {
321                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, storedOff));
322                 if (GinItemPointerGetBlockNumber(&(itup)->t_tid) == blkno)
323                         return storedOff;
324
325                 /*
326                  * we hope, that needed pointer goes to right. It's true if there
327                  * wasn't a deletion
328                  */
329                 for (i = storedOff + 1; i <= maxoff; i++)
330                 {
331                         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
332                         if (GinItemPointerGetBlockNumber(&(itup)->t_tid) == blkno)
333                                 return i;
334                 }
335                 maxoff = storedOff - 1;
336         }
337
338         /* last chance */
339         for (i = FirstOffsetNumber; i <= maxoff; i++)
340         {
341                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
342                 if (GinItemPointerGetBlockNumber(&(itup)->t_tid) == blkno)
343                         return i;
344         }
345
346         return InvalidOffsetNumber;
347 }
348
349 static BlockNumber
350 entryGetLeftMostPage(GinBtree btree, Page page)
351 {
352         IndexTuple      itup;
353
354         Assert(!GinPageIsLeaf(page));
355         Assert(!GinPageIsData(page));
356         Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);
357
358         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
359         return GinItemPointerGetBlockNumber(&(itup)->t_tid);
360 }
361
362 static bool
363 entryIsEnoughSpace(GinBtree btree, Buffer buf, OffsetNumber off)
364 {
365         Size            itupsz = 0;
366         Page            page = BufferGetPage(buf);
367
368         Assert(btree->entry);
369         Assert(!GinPageIsData(page));
370
371         if (btree->isDelete)
372         {
373                 IndexTuple      itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
374
375                 itupsz = MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
376         }
377
378         if (PageGetFreeSpace(page) + itupsz >= MAXALIGN(IndexTupleSize(btree->entry)) + sizeof(ItemIdData))
379                 return true;
380
381         return false;
382 }
383
384 /*
385  * Delete tuple on leaf page if tuples was existed and we
386  * should update it, update old child blkno to new right page
387  * if child split is occured
388  */
389 static BlockNumber
390 entryPreparePage(GinBtree btree, Page page, OffsetNumber off)
391 {
392         BlockNumber ret = InvalidBlockNumber;
393
394         Assert(btree->entry);
395         Assert(!GinPageIsData(page));
396
397         if (btree->isDelete)
398         {
399                 Assert(GinPageIsLeaf(page));
400                 PageIndexTupleDelete(page, off);
401         }
402
403         if (!GinPageIsLeaf(page) && btree->rightblkno != InvalidBlockNumber)
404         {
405                 IndexTuple      itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
406
407                 ItemPointerSet(&itup->t_tid, btree->rightblkno, InvalidOffsetNumber);
408                 ret = btree->rightblkno;
409         }
410
411         btree->rightblkno = InvalidBlockNumber;
412
413         return ret;
414 }
415
416 /*
417  * Place tuple on page and fills WAL record
418  */
419 static void
420 entryPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prdata)
421 {
422         Page            page = BufferGetPage(buf);
423         static XLogRecData rdata[3];
424         OffsetNumber placed;
425         static ginxlogInsert data;
426         int                     cnt = 0;
427
428         *prdata = rdata;
429         data.updateBlkno = entryPreparePage(btree, page, off);
430
431         placed = PageAddItem(page, (Item) btree->entry, IndexTupleSize(btree->entry), off, false, false);
432         if (placed != off)
433                 elog(ERROR, "failed to add item to index page in \"%s\"",
434                          RelationGetRelationName(btree->index));
435
436         data.node = btree->index->rd_node;
437         data.blkno = BufferGetBlockNumber(buf);
438         data.offset = off;
439         data.nitem = 1;
440         data.isDelete = btree->isDelete;
441         data.isData = false;
442         data.isLeaf = GinPageIsLeaf(page) ? TRUE : FALSE;
443
444         /*
445          * Prevent full page write if child's split occurs. That is needed to
446          * remove incomplete splits while replaying WAL
447          *
448          * data.updateBlkno contains new block number (of newly created right
449          * page) for recently splited page.
450          */
451         if (data.updateBlkno == InvalidBlockNumber)
452         {
453                 rdata[0].buffer = buf;
454                 rdata[0].buffer_std = TRUE;
455                 rdata[0].data = NULL;
456                 rdata[0].len = 0;
457                 rdata[0].next = &rdata[1];
458                 cnt++;
459         }
460
461         rdata[cnt].buffer = InvalidBuffer;
462         rdata[cnt].data = (char *) &data;
463         rdata[cnt].len = sizeof(ginxlogInsert);
464         rdata[cnt].next = &rdata[cnt + 1];
465         cnt++;
466
467         rdata[cnt].buffer = InvalidBuffer;
468         rdata[cnt].data = (char *) btree->entry;
469         rdata[cnt].len = IndexTupleSize(btree->entry);
470         rdata[cnt].next = NULL;
471
472         btree->entry = NULL;
473 }
474
475 /*
476  * Returns new tuple with copied value from source tuple.
477  * New tuple will not store posting list
478  */
479 static IndexTuple
480 copyIndexTuple(IndexTuple itup, Page page)
481 {
482         IndexTuple      nitup;
483
484         if (GinPageIsLeaf(page) && !GinIsPostingTree(itup))
485         {
486                 nitup = (IndexTuple) palloc(MAXALIGN(GinGetOrigSizePosting(itup)));
487                 memcpy(nitup, itup, GinGetOrigSizePosting(itup));
488                 nitup->t_info &= ~INDEX_SIZE_MASK;
489                 nitup->t_info |= GinGetOrigSizePosting(itup);
490         }
491         else
492         {
493                 nitup = (IndexTuple) palloc(MAXALIGN(IndexTupleSize(itup)));
494                 memcpy(nitup, itup, IndexTupleSize(itup));
495         }
496
497         return nitup;
498 }
499
500 /*
501  * Place tuple and split page, original buffer(lbuf) leaves untouched,
502  * returns shadow page of lbuf filled new data.
503  * Tuples are distributed between pages by equal size on its, not
504  * an equal number!
505  */
506 static Page
507 entrySplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRecData **prdata)
508 {
509         static XLogRecData rdata[2];
510         OffsetNumber i,
511                                 maxoff,
512                                 separator = InvalidOffsetNumber;
513         Size            totalsize = 0;
514         Size            lsize = 0,
515                                 size;
516         static char tupstore[2 * BLCKSZ];
517         char       *ptr;
518         IndexTuple      itup,
519                                 leftrightmost = NULL;
520         static ginxlogSplit data;
521         Page            page;
522         Page            lpage = PageGetTempPageCopy(BufferGetPage(lbuf));
523         Page            rpage = BufferGetPage(rbuf);
524         Size            pageSize = PageGetPageSize(lpage);
525
526         *prdata = rdata;
527         data.leftChildBlkno = (GinPageIsLeaf(lpage)) ?
528                 InvalidOffsetNumber : GinItemPointerGetBlockNumber(&(btree->entry->t_tid));
529         data.updateBlkno = entryPreparePage(btree, lpage, off);
530
531         maxoff = PageGetMaxOffsetNumber(lpage);
532         ptr = tupstore;
533
534         for (i = FirstOffsetNumber; i <= maxoff; i++)
535         {
536                 if (i == off)
537                 {
538                         size = MAXALIGN(IndexTupleSize(btree->entry));
539                         memcpy(ptr, btree->entry, size);
540                         ptr += size;
541                         totalsize += size + sizeof(ItemIdData);
542                 }
543
544                 itup = (IndexTuple) PageGetItem(lpage, PageGetItemId(lpage, i));
545                 size = MAXALIGN(IndexTupleSize(itup));
546                 memcpy(ptr, itup, size);
547                 ptr += size;
548                 totalsize += size + sizeof(ItemIdData);
549         }
550
551         if (off == maxoff + 1)
552         {
553                 size = MAXALIGN(IndexTupleSize(btree->entry));
554                 memcpy(ptr, btree->entry, size);
555                 ptr += size;
556                 totalsize += size + sizeof(ItemIdData);
557         }
558
559         GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize);
560         GinInitPage(lpage, GinPageGetOpaque(rpage)->flags, pageSize);
561
562         ptr = tupstore;
563         maxoff++;
564         lsize = 0;
565
566         page = lpage;
567         for (i = FirstOffsetNumber; i <= maxoff; i++)
568         {
569                 itup = (IndexTuple) ptr;
570
571                 if (lsize > totalsize / 2)
572                 {
573                         if (separator == InvalidOffsetNumber)
574                                 separator = i - 1;
575                         page = rpage;
576                 }
577                 else
578                 {
579                         leftrightmost = itup;
580                         lsize += MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
581                 }
582
583                 if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
584                         elog(ERROR, "failed to add item to index page in \"%s\"",
585                                  RelationGetRelationName(btree->index));
586                 ptr += MAXALIGN(IndexTupleSize(itup));
587         }
588
589         btree->entry = copyIndexTuple(leftrightmost, lpage);
590         ItemPointerSet(&(btree->entry)->t_tid, BufferGetBlockNumber(lbuf), InvalidOffsetNumber);
591
592         btree->rightblkno = BufferGetBlockNumber(rbuf);
593
594         data.node = btree->index->rd_node;
595         data.rootBlkno = InvalidBlockNumber;
596         data.lblkno = BufferGetBlockNumber(lbuf);
597         data.rblkno = BufferGetBlockNumber(rbuf);
598         data.separator = separator;
599         data.nitem = maxoff;
600         data.isData = FALSE;
601         data.isLeaf = GinPageIsLeaf(lpage) ? TRUE : FALSE;
602         data.isRootSplit = FALSE;
603
604         rdata[0].buffer = InvalidBuffer;
605         rdata[0].data = (char *) &data;
606         rdata[0].len = sizeof(ginxlogSplit);
607         rdata[0].next = &rdata[1];
608
609         rdata[1].buffer = InvalidBuffer;
610         rdata[1].data = tupstore;
611         rdata[1].len = MAXALIGN(totalsize);
612         rdata[1].next = NULL;
613
614         return lpage;
615 }
616
617 /*
618  * return newly allocate rightmost tuple
619  */
620 IndexTuple
621 ginPageGetLinkItup(Buffer buf)
622 {
623         IndexTuple      itup,
624                                 nitup;
625         Page            page = BufferGetPage(buf);
626
627         itup = getRightMostTuple(page);
628         nitup = copyIndexTuple(itup, page);
629         ItemPointerSet(&nitup->t_tid, BufferGetBlockNumber(buf), InvalidOffsetNumber);
630
631         return nitup;
632 }
633
634 /*
635  * Fills new root by rightest values from child.
636  * Also called from ginxlog, should not use btree
637  */
638 void
639 entryFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf)
640 {
641         Page            page;
642         IndexTuple      itup;
643
644         page = BufferGetPage(root);
645
646         itup = ginPageGetLinkItup(lbuf);
647         if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
648                 elog(ERROR, "failed to add item to index root page");
649
650         itup = ginPageGetLinkItup(rbuf);
651         if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
652                 elog(ERROR, "failed to add item to index root page");
653 }
654
655 void
656 prepareEntryScan(GinBtree btree, Relation index, OffsetNumber attnum, Datum value, GinState *ginstate)
657 {
658         memset(btree, 0, sizeof(GinBtreeData));
659
660         btree->isMoveRight = entryIsMoveRight;
661         btree->findChildPage = entryLocateEntry;
662         btree->findItem = entryLocateLeafEntry;
663         btree->findChildPtr = entryFindChildPtr;
664         btree->getLeftMostPage = entryGetLeftMostPage;
665         btree->isEnoughSpace = entryIsEnoughSpace;
666         btree->placeToPage = entryPlaceToPage;
667         btree->splitPage = entrySplitPage;
668         btree->fillRoot = entryFillRoot;
669
670         btree->index = index;
671         btree->ginstate = ginstate;
672         btree->entryAttnum = attnum;
673         btree->entryValue = value;
674
675         btree->isDelete = FALSE;
676         btree->searchMode = FALSE;
677         btree->fullScan = FALSE;
678         btree->isBuild = FALSE;
679 }