OSDN Git Service

Change SearchSysCache coding conventions so that a reference count is
[pg-rex/syncrep.git] / src / backend / access / gist / gist.c
1 /*-------------------------------------------------------------------------
2  *
3  * gist.c
4  *        interface routines for the postgres GiST index access method.
5  *
6  *
7  *
8  * IDENTIFICATION
9  *        $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.65 2000/11/16 22:30:15 tgl Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13
14 #include "postgres.h"
15
16 #include "access/genam.h"
17 #include "access/gist.h"
18 #include "access/gistscan.h"
19 #include "access/heapam.h"
20 #include "catalog/index.h"
21 #include "catalog/pg_index.h"
22 #include "executor/executor.h"
23 #include "miscadmin.h"
24 #include "utils/syscache.h"
25
26 #ifdef XLOG
27 #include "access/xlogutils.h"
28 void gist_redo(XLogRecPtr lsn, XLogRecord *record);
29 void gist_undo(XLogRecPtr lsn, XLogRecord *record);
30 void gist_desc(char *buf, uint8 xl_info, char* rec);
31 #endif
32
33 /* non-export function prototypes */
34 static InsertIndexResult gistdoinsert(Relation r, IndexTuple itup,
35                          GISTSTATE *GISTstate);
36 static InsertIndexResult gistentryinsert(Relation r, GISTSTACK *stk,
37                                 IndexTuple tup,
38                                 GISTSTATE *giststate);
39 static void gistentryinserttwo(Relation r, GISTSTACK *stk, IndexTuple ltup,
40                                    IndexTuple rtup, GISTSTATE *giststate);
41 static void gistAdjustKeys(Relation r, GISTSTACK *stk, BlockNumber blk,
42                            char *datum, int att_size, GISTSTATE *giststate);
43 static void gistintinsert(Relation r, GISTSTACK *stk, IndexTuple ltup,
44                           IndexTuple rtup, GISTSTATE *giststate);
45 static InsertIndexResult gistSplit(Relation r, Buffer buffer,
46                   GISTSTACK *stack, IndexTuple itup,
47                   GISTSTATE *giststate);
48 static void gistnewroot(GISTSTATE *giststate, Relation r, IndexTuple lt,
49                         IndexTuple rt);
50 static void GISTInitBuffer(Buffer b, uint32 f);
51 static BlockNumber gistChooseSubtree(Relation r, IndexTuple itup, int level,
52                                   GISTSTATE *giststate,
53                                   GISTSTACK **retstack, Buffer *leafbuf);
54 static OffsetNumber gistchoose(Relation r, Page p, IndexTuple it,
55                    GISTSTATE *giststate);
56 static int      gistnospace(Page p, IndexTuple it);
57 static IndexTuple gist_tuple_replacekey(Relation r, GISTENTRY entry, IndexTuple t);
58 static void gistcentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr,
59                            Relation r, Page pg, OffsetNumber o, int b, bool l);
60
61 #ifdef GISTDEBUG
62 static char *int_range_out(INTRANGE *r);
63
64 #endif
65
66 /*
67 ** routine to build an index.  Basically calls insert over and over
68 */
69 Datum
70 gistbuild(PG_FUNCTION_ARGS)
71 {
72         Relation                heap = (Relation) PG_GETARG_POINTER(0);
73         Relation                index = (Relation) PG_GETARG_POINTER(1);
74         IndexInfo          *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
75         Node               *oldPred = (Node *) PG_GETARG_POINTER(3);
76 #ifdef NOT_USED
77         IndexStrategy   istrat = (IndexStrategy) PG_GETARG_POINTER(4);
78 #endif
79         HeapScanDesc hscan;
80         HeapTuple       htup;
81         IndexTuple      itup;
82         TupleDesc       htupdesc,
83                                 itupdesc;
84         Datum           attdata[INDEX_MAX_KEYS];
85         char            nulls[INDEX_MAX_KEYS];
86         int                     nhtups,
87                                 nitups;
88         Node       *pred = indexInfo->ii_Predicate;
89 #ifndef OMIT_PARTIAL_INDEX
90         TupleTable      tupleTable;
91         TupleTableSlot *slot;
92 #endif
93         ExprContext *econtext;
94         InsertIndexResult res = NULL;
95         GISTSTATE       giststate;
96         GISTENTRY       tmpcentry;
97         Buffer          buffer = InvalidBuffer;
98         bool       *compvec;
99         int                     i;
100
101         /* no locking is needed */
102
103         initGISTstate(&giststate, index);
104
105         /*
106          * We expect to be called exactly once for any index relation. If
107          * that's not the case, big trouble's what we have.
108          */
109         if (oldPred == NULL && RelationGetNumberOfBlocks(index) != 0)
110                 elog(ERROR, "%s already contains data", RelationGetRelationName(index));
111
112         /* initialize the root page (if this is a new index) */
113         if (oldPred == NULL)
114         {
115                 buffer = ReadBuffer(index, P_NEW);
116                 GISTInitBuffer(buffer, F_LEAF);
117                 WriteBuffer(buffer);
118         }
119
120         /* get tuple descriptors for heap and index relations */
121         htupdesc = RelationGetDescr(heap);
122         itupdesc = RelationGetDescr(index);
123
124         /*
125          * If this is a predicate (partial) index, we will need to evaluate
126          * the predicate using ExecQual, which requires the current tuple to
127          * be in a slot of a TupleTable.  In addition, ExecQual must have an
128          * ExprContext referring to that slot.  Here, we initialize dummy
129          * TupleTable and ExprContext objects for this purpose. --Nels, Feb 92
130          *
131          * We construct the ExprContext anyway since we need a per-tuple
132          * temporary memory context for function evaluation -- tgl July 00
133          */
134 #ifndef OMIT_PARTIAL_INDEX
135         if (pred != NULL || oldPred != NULL)
136         {
137                 tupleTable = ExecCreateTupleTable(1);
138                 slot = ExecAllocTableSlot(tupleTable);
139                 ExecSetSlotDescriptor(slot, htupdesc);
140         }
141         else
142         {
143                 tupleTable = NULL;
144                 slot = NULL;
145         }
146         econtext = MakeExprContext(slot, TransactionCommandContext);
147 #else
148         econtext = MakeExprContext(NULL, TransactionCommandContext);
149 #endif   /* OMIT_PARTIAL_INDEX */
150
151         /* build the index */
152         nhtups = nitups = 0;
153
154         compvec = (bool *) palloc(sizeof(bool) * indexInfo->ii_NumIndexAttrs);
155
156         /* start a heap scan */
157         hscan = heap_beginscan(heap, 0, SnapshotNow, 0, (ScanKey) NULL);
158
159         while (HeapTupleIsValid(htup = heap_getnext(hscan, 0)))
160         {
161                 MemoryContextReset(econtext->ecxt_per_tuple_memory);
162
163                 nhtups++;
164
165 #ifndef OMIT_PARTIAL_INDEX
166                 /*
167                  * If oldPred != NULL, this is an EXTEND INDEX command, so skip
168                  * this tuple if it was already in the existing partial index
169                  */
170                 if (oldPred != NULL)
171                 {
172                         slot->val = htup;
173                         if (ExecQual((List *) oldPred, econtext, false))
174                         {
175                                 nitups++;
176                                 continue;
177                         }
178                 }
179
180                 /*
181                  * Skip this tuple if it doesn't satisfy the partial-index
182                  * predicate
183                  */
184                 if (pred != NULL)
185                 {
186                         slot->val = htup;
187                         if (!ExecQual((List *) pred, econtext, false))
188                                 continue;
189                 }
190 #endif   /* OMIT_PARTIAL_INDEX */
191
192                 nitups++;
193
194                 /*
195                  * For the current heap tuple, extract all the attributes we use
196                  * in this index, and note which are null.
197                  */
198                 FormIndexDatum(indexInfo,
199                                            htup,
200                                            htupdesc,
201                                            econtext->ecxt_per_tuple_memory,
202                                            attdata,
203                                            nulls);
204
205                 /* immediately compress keys to normalize */
206                 for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
207                 {
208                         gistcentryinit(&giststate, &tmpcentry, (char *) attdata[i],
209                                                    (Relation) NULL, (Page) NULL, (OffsetNumber) 0,
210                                                    -1 /* size is currently bogus */ , TRUE);
211                         if (attdata[i] != (Datum) tmpcentry.pred &&
212                                 !(giststate.keytypbyval))
213                                 compvec[i] = TRUE;
214                         else
215                                 compvec[i] = FALSE;
216                         attdata[i] = (Datum) tmpcentry.pred;
217                 }
218
219                 /* form an index tuple and point it at the heap tuple */
220                 itup = index_formtuple(itupdesc, attdata, nulls);
221                 itup->t_tid = htup->t_self;
222
223                 /*
224                  * Since we already have the index relation locked, we call
225                  * gistdoinsert directly.  Normal access method calls dispatch
226                  * through gistinsert, which locks the relation for write.      This
227                  * is the right thing to do if you're inserting single tups, but
228                  * not when you're initializing the whole index at once.
229                  */
230
231                 res = gistdoinsert(index, itup, &giststate);
232
233                 for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
234                         if (compvec[i])
235                                 pfree(DatumGetPointer(attdata[i]));
236
237                 pfree(itup);
238                 pfree(res);
239         }
240
241         /* okay, all heap tuples are indexed */
242         heap_endscan(hscan);
243
244         pfree(compvec);
245
246 #ifndef OMIT_PARTIAL_INDEX
247         if (pred != NULL || oldPred != NULL)
248         {
249                 ExecDropTupleTable(tupleTable, true);
250         }
251 #endif   /* OMIT_PARTIAL_INDEX */
252         FreeExprContext(econtext);
253
254         /*
255          * Since we just counted the tuples in the heap, we update its stats
256          * in pg_class to guarantee that the planner takes advantage of the
257          * index we just created.  But, only update statistics during normal
258          * index definitions, not for indices on system catalogs created
259          * during bootstrap processing.  We must close the relations before
260          * updating statistics to guarantee that the relcache entries are
261          * flushed when we increment the command counter in UpdateStats(). But
262          * we do not release any locks on the relations; those will be held
263          * until end of transaction.
264          */
265         if (IsNormalProcessingMode())
266         {
267                 Oid                     hrelid = RelationGetRelid(heap);
268                 Oid                     irelid = RelationGetRelid(index);
269
270                 heap_close(heap, NoLock);
271                 index_close(index);
272                 UpdateStats(hrelid, nhtups);
273                 UpdateStats(irelid, nitups);
274                 if (oldPred != NULL)
275                 {
276                         if (nitups == nhtups)
277                                 pred = NULL;
278                         UpdateIndexPredicate(irelid, oldPred, pred);
279                 }
280         }
281
282         PG_RETURN_VOID();
283 }
284
285 /*
286  *      gistinsert -- wrapper for GiST tuple insertion.
287  *
288  *        This is the public interface routine for tuple insertion in GiSTs.
289  *        It doesn't do any work; just locks the relation and passes the buck.
290  */
291 Datum
292 gistinsert(PG_FUNCTION_ARGS)
293 {
294         Relation                r = (Relation) PG_GETARG_POINTER(0);
295         Datum              *datum = (Datum *) PG_GETARG_POINTER(1);
296         char               *nulls = (char *) PG_GETARG_POINTER(2);
297         ItemPointer             ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
298 #ifdef NOT_USED
299         Relation                heapRel = (Relation) PG_GETARG_POINTER(4);
300 #endif
301         InsertIndexResult res;
302         IndexTuple      itup;
303         GISTSTATE       giststate;
304         GISTENTRY       tmpentry;
305         int                     i;
306         bool       *compvec;
307
308         initGISTstate(&giststate, r);
309
310         /* immediately compress keys to normalize */
311         compvec = (bool *) palloc(sizeof(bool) * r->rd_att->natts);
312         for (i = 0; i < r->rd_att->natts; i++)
313         {
314                 gistcentryinit(&giststate, &tmpentry, (char *) datum[i],
315                                            (Relation) NULL, (Page) NULL, (OffsetNumber) 0,
316                                            -1 /* size is currently bogus */ , TRUE);
317                 if (datum[i] != (Datum) tmpentry.pred && !(giststate.keytypbyval))
318                         compvec[i] = TRUE;
319                 else
320                         compvec[i] = FALSE;
321                 datum[i] = (Datum) tmpentry.pred;
322         }
323         itup = index_formtuple(RelationGetDescr(r), datum, nulls);
324         itup->t_tid = *ht_ctid;
325
326         /*
327          * Notes in ExecUtils:ExecOpenIndices()
328          *
329          * RelationSetLockForWrite(r);
330          */
331
332         res = gistdoinsert(r, itup, &giststate);
333         for (i = 0; i < r->rd_att->natts; i++)
334                 if (compvec[i] == TRUE)
335                         pfree((char *) datum[i]);
336         pfree(itup);
337         pfree(compvec);
338
339         PG_RETURN_POINTER(res);
340 }
341
342 /*
343 ** Take a compressed entry, and install it on a page.  Since we now know
344 ** where the entry will live, we decompress it and recompress it using
345 ** that knowledge (some compression routines may want to fish around
346 ** on the page, for example, or do something special for leaf nodes.)
347 */
348 static OffsetNumber
349 gistPageAddItem(GISTSTATE *giststate,
350                                 Relation r,
351                                 Page page,
352                                 Item item,
353                                 Size size,
354                                 OffsetNumber offsetNumber,
355                                 ItemIdFlags flags,
356                                 GISTENTRY *dentry,
357                                 IndexTuple *newtup)
358 {
359         GISTENTRY       tmpcentry;
360         IndexTuple      itup = (IndexTuple) item;
361
362         /*
363          * recompress the item given that we now know the exact page and
364          * offset for insertion
365          */
366         gistdentryinit(giststate, dentry,
367                                    (((char *) itup) + sizeof(IndexTupleData)),
368                           (Relation) 0, (Page) 0, (OffsetNumber) InvalidOffsetNumber,
369                                    IndexTupleSize(itup) - sizeof(IndexTupleData), FALSE);
370         gistcentryinit(giststate, &tmpcentry, dentry->pred, r, page,
371                                    offsetNumber, dentry->bytes, FALSE);
372         *newtup = gist_tuple_replacekey(r, *dentry, itup);
373         /* be tidy */
374         if (tmpcentry.pred != dentry->pred
375                 && tmpcentry.pred != (((char *) itup) + sizeof(IndexTupleData)))
376                 pfree(tmpcentry.pred);
377
378         return (PageAddItem(page, (Item) *newtup, IndexTupleSize(*newtup),
379                                                 offsetNumber, flags));
380 }
381
382
383 static InsertIndexResult
384 gistdoinsert(Relation r,
385                          IndexTuple itup,       /* itup contains compressed entry */
386                          GISTSTATE *giststate)
387 {
388         GISTENTRY       tmpdentry;
389         InsertIndexResult res;
390         OffsetNumber l;
391         GISTSTACK  *stack;
392         Buffer          buffer;
393         BlockNumber blk;
394         Page            page;
395         OffsetNumber off;
396         IndexTuple      newtup;
397
398         /* 3rd arg is ignored for now */
399         blk = gistChooseSubtree(r, itup, 0, giststate, &stack, &buffer);
400         page = (Page) BufferGetPage(buffer);
401
402         if (gistnospace(page, itup))
403         {
404                 /* need to do a split */
405                 res = gistSplit(r, buffer, stack, itup, giststate);
406                 gistfreestack(stack);
407                 WriteBuffer(buffer);    /* don't forget to release buffer! */
408                 return res;
409         }
410
411         if (PageIsEmpty(page))
412                 off = FirstOffsetNumber;
413         else
414                 off = OffsetNumberNext(PageGetMaxOffsetNumber(page));
415
416         /* add the item and write the buffer */
417         l = gistPageAddItem(giststate, r, page, (Item) itup, IndexTupleSize(itup),
418                                                 off, LP_USED, &tmpdentry, &newtup);
419         WriteBuffer(buffer);
420
421         /* now expand the page boundary in the parent to include the new child */
422         gistAdjustKeys(r, stack, blk, tmpdentry.pred, tmpdentry.bytes, giststate);
423         gistfreestack(stack);
424
425         /* be tidy */
426         if (itup != newtup)
427                 pfree(newtup);
428         if (tmpdentry.pred != (((char *) itup) + sizeof(IndexTupleData)))
429                 pfree(tmpdentry.pred);
430
431         /* build and return an InsertIndexResult for this insertion */
432         res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
433         ItemPointerSet(&(res->pointerData), blk, l);
434
435         return res;
436 }
437
438
439 static BlockNumber
440 gistChooseSubtree(Relation r, IndexTuple itup,  /* itup has compressed
441                                                                                                  * entry */
442                                   int level,
443                                   GISTSTATE *giststate,
444                                   GISTSTACK **retstack /* out */ ,
445                                   Buffer *leafbuf /* out */ )
446 {
447         Buffer          buffer;
448         BlockNumber blk;
449         GISTSTACK  *stack;
450         Page            page;
451         GISTPageOpaque opaque;
452         IndexTuple      which;
453
454         blk = GISTP_ROOT;
455         buffer = InvalidBuffer;
456         stack = (GISTSTACK *) NULL;
457
458         do
459         {
460                 /* let go of current buffer before getting next */
461                 if (buffer != InvalidBuffer)
462                         ReleaseBuffer(buffer);
463
464                 /* get next buffer */
465                 buffer = ReadBuffer(r, blk);
466                 page = (Page) BufferGetPage(buffer);
467
468                 opaque = (GISTPageOpaque) PageGetSpecialPointer(page);
469                 if (!(opaque->flags & F_LEAF))
470                 {
471                         GISTSTACK  *n;
472                         ItemId          iid;
473
474                         n = (GISTSTACK *) palloc(sizeof(GISTSTACK));
475                         n->gs_parent = stack;
476                         n->gs_blk = blk;
477                         n->gs_child = gistchoose(r, page, itup, giststate);
478                         stack = n;
479
480                         iid = PageGetItemId(page, n->gs_child);
481                         which = (IndexTuple) PageGetItem(page, iid);
482                         blk = ItemPointerGetBlockNumber(&(which->t_tid));
483                 }
484         } while (!(opaque->flags & F_LEAF));
485
486         *retstack = stack;
487         *leafbuf = buffer;
488
489         return blk;
490 }
491
492
493 static void
494 gistAdjustKeys(Relation r,
495                            GISTSTACK *stk,
496                            BlockNumber blk,
497                            char *datum,         /* datum is uncompressed */
498                            int att_size,
499                            GISTSTATE *giststate)
500 {
501         char       *oldud;
502         Page            p;
503         Buffer          b;
504         bool            result;
505         bytea      *evec;
506         GISTENTRY       centry,
507                            *ev0p,
508                            *ev1p;
509         int                     size,
510                                 datumsize;
511         IndexTuple      tid;
512
513         if (stk == (GISTSTACK *) NULL)
514                 return;
515
516         b = ReadBuffer(r, stk->gs_blk);
517         p = BufferGetPage(b);
518
519         oldud = (char *) PageGetItem(p, PageGetItemId(p, stk->gs_child));
520         tid = (IndexTuple) oldud;
521         size = IndexTupleSize((IndexTuple) oldud) - sizeof(IndexTupleData);
522         oldud += sizeof(IndexTupleData);
523
524         evec = (bytea *) palloc(2 * sizeof(GISTENTRY) + VARHDRSZ);
525         VARATT_SIZEP(evec) = 2 * sizeof(GISTENTRY) + VARHDRSZ;
526
527         /* insert decompressed oldud into entry vector */
528         gistdentryinit(giststate, &((GISTENTRY *) VARDATA(evec))[0],
529                                    oldud, r, p, stk->gs_child,
530                                    size, FALSE);
531         ev0p = &((GISTENTRY *) VARDATA(evec))[0];
532
533         /* insert datum entry into entry vector */
534         gistentryinit(((GISTENTRY *) VARDATA(evec))[1], datum,
535                 (Relation) NULL, (Page) NULL, (OffsetNumber) 0, att_size, FALSE);
536         ev1p = &((GISTENTRY *) VARDATA(evec))[1];
537
538         /* form union of decompressed entries */
539         datum = (char *)
540                 DatumGetPointer(FunctionCall2(&giststate->unionFn,
541                                                                           PointerGetDatum(evec),
542                                                                           PointerGetDatum(&datumsize)));
543
544         /* did union leave decompressed version of oldud unchanged? */
545         FunctionCall3(&giststate->equalFn,
546                                   PointerGetDatum(ev0p->pred),
547                                   PointerGetDatum(datum),
548                                   PointerGetDatum(&result));
549         if (!result)
550         {
551                 TupleDesc       td = RelationGetDescr(r);
552
553                 /* compress datum for storage on page */
554                 gistcentryinit(giststate, &centry, datum, ev0p->rel, ev0p->page,
555                                            ev0p->offset, datumsize, FALSE);
556                 if (td->attrs[0]->attlen >= 0)
557                 {
558                         memmove(oldud, centry.pred, att_size);
559                         gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, datum, att_size,
560                                                    giststate);
561                 }
562                 else if (VARSIZE(centry.pred) == VARSIZE(oldud))
563                 {
564                         memmove(oldud, centry.pred, VARSIZE(centry.pred));
565                         gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, datum, att_size,
566                                                    giststate);
567                 }
568                 else
569                 {
570
571                         /*
572                          * * new datum is not the same size as the old. * We have to
573                          * delete the old entry and insert the new * one.  Note that
574                          * this may cause a split here!
575                          */
576                         IndexTuple      newtup;
577                         ItemPointerData oldtid;
578                         char       *isnull;
579                         TupleDesc       tupDesc;
580                         InsertIndexResult res;
581
582                         /* delete old tuple */
583                         ItemPointerSet(&oldtid, stk->gs_blk, stk->gs_child);
584                         DirectFunctionCall2(gistdelete,
585                                                                 PointerGetDatum(r),
586                                                                 PointerGetDatum(&oldtid));
587
588                         /* generate and insert new tuple */
589                         tupDesc = r->rd_att;
590                         isnull = (char *) palloc(r->rd_rel->relnatts);
591                         MemSet(isnull, ' ', r->rd_rel->relnatts);
592                         newtup = (IndexTuple) index_formtuple(tupDesc,
593                                                                                  (Datum *) &centry.pred, isnull);
594                         pfree(isnull);
595                         /* set pointer in new tuple to point to current child */
596                         ItemPointerSet(&oldtid, blk, 1);
597                         newtup->t_tid = oldtid;
598
599                         /* inserting the new entry also adjust keys above */
600                         res = gistentryinsert(r, stk, newtup, giststate);
601
602                         /* in stack, set info to point to new tuple */
603                         stk->gs_blk = ItemPointerGetBlockNumber(&(res->pointerData));
604                         stk->gs_child = ItemPointerGetOffsetNumber(&(res->pointerData));
605
606                         pfree(res);
607                 }
608                 WriteBuffer(b);
609
610                 if (centry.pred != datum)
611                         pfree(datum);
612         }
613         else
614                 ReleaseBuffer(b);
615         pfree(evec);
616 }
617
618 /*
619  *      gistSplit -- split a page in the tree.
620  *
621  */
622 static InsertIndexResult
623 gistSplit(Relation r,
624                   Buffer buffer,
625                   GISTSTACK *stack,
626                   IndexTuple itup,              /* contains compressed entry */
627                   GISTSTATE *giststate)
628 {
629         Page            p;
630         Buffer          leftbuf,
631                                 rightbuf;
632         Page            left,
633                                 right;
634         ItemId          itemid;
635         IndexTuple      item;
636         IndexTuple      ltup,
637                                 rtup,
638                                 newtup;
639         OffsetNumber maxoff;
640         OffsetNumber i;
641         OffsetNumber leftoff,
642                                 rightoff;
643         BlockNumber lbknum,
644                                 rbknum;
645         BlockNumber bufblock;
646         GISTPageOpaque opaque;
647         int                     blank;
648         InsertIndexResult res;
649         char       *isnull;
650         GIST_SPLITVEC v;
651         TupleDesc       tupDesc;
652         bytea      *entryvec;
653         bool       *decompvec;
654         IndexTuple      item_1;
655         GISTENTRY       tmpdentry,
656                                 tmpentry;
657
658         isnull = (char *) palloc(r->rd_rel->relnatts);
659         for (blank = 0; blank < r->rd_rel->relnatts; blank++)
660                 isnull[blank] = ' ';
661         p = (Page) BufferGetPage(buffer);
662         opaque = (GISTPageOpaque) PageGetSpecialPointer(p);
663
664
665         /*
666          * The root of the tree is the first block in the relation.  If we're
667          * about to split the root, we need to do some hocus-pocus to enforce
668          * this guarantee.
669          */
670
671         if (BufferGetBlockNumber(buffer) == GISTP_ROOT)
672         {
673                 leftbuf = ReadBuffer(r, P_NEW);
674                 GISTInitBuffer(leftbuf, opaque->flags);
675                 lbknum = BufferGetBlockNumber(leftbuf);
676                 left = (Page) BufferGetPage(leftbuf);
677         }
678         else
679         {
680                 leftbuf = buffer;
681                 IncrBufferRefCount(buffer);
682                 lbknum = BufferGetBlockNumber(buffer);
683                 left = (Page) PageGetTempPage(p, sizeof(GISTPageOpaqueData));
684         }
685
686         rightbuf = ReadBuffer(r, P_NEW);
687         GISTInitBuffer(rightbuf, opaque->flags);
688         rbknum = BufferGetBlockNumber(rightbuf);
689         right = (Page) BufferGetPage(rightbuf);
690
691         /* generate the item array */
692         maxoff = PageGetMaxOffsetNumber(p);
693         entryvec = (bytea *) palloc(VARHDRSZ + (maxoff + 2) * sizeof(GISTENTRY));
694         decompvec = (bool *) palloc(VARHDRSZ + (maxoff + 2) * sizeof(bool));
695         for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
696         {
697                 item_1 = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
698                 gistdentryinit(giststate, &((GISTENTRY *) VARDATA(entryvec))[i],
699                                            (((char *) item_1) + sizeof(IndexTupleData)),
700                                            r, p, i,
701                                  IndexTupleSize(item_1) - sizeof(IndexTupleData), FALSE);
702                 if ((char *) (((GISTENTRY *) VARDATA(entryvec))[i].pred)
703                         == (((char *) item_1) + sizeof(IndexTupleData)))
704                         decompvec[i] = FALSE;
705                 else
706                         decompvec[i] = TRUE;
707         }
708
709         /* add the new datum as the last entry */
710         gistdentryinit(giststate, &(((GISTENTRY *) VARDATA(entryvec))[maxoff + 1]),
711                                    (((char *) itup) + sizeof(IndexTupleData)),
712                                    (Relation) NULL, (Page) NULL,
713                                    (OffsetNumber) 0, tmpentry.bytes, FALSE);
714         if ((char *) (((GISTENTRY *) VARDATA(entryvec))[maxoff + 1]).pred !=
715                 (((char *) itup) + sizeof(IndexTupleData)))
716                 decompvec[maxoff + 1] = TRUE;
717         else
718                 decompvec[maxoff + 1] = FALSE;
719
720         VARATT_SIZEP(entryvec) = (maxoff + 2) * sizeof(GISTENTRY) + VARHDRSZ;
721
722         /* now let the user-defined picksplit function set up the split vector */
723         FunctionCall2(&giststate->picksplitFn,
724                                   PointerGetDatum(entryvec),
725                                   PointerGetDatum(&v));
726
727         /* compress ldatum and rdatum */
728         gistcentryinit(giststate, &tmpentry, v.spl_ldatum, (Relation) NULL,
729                                    (Page) NULL, (OffsetNumber) 0,
730                                    ((GISTENTRY *) VARDATA(entryvec))[i].bytes, FALSE);
731         if (v.spl_ldatum != tmpentry.pred)
732                 pfree(v.spl_ldatum);
733         v.spl_ldatum = tmpentry.pred;
734
735         gistcentryinit(giststate, &tmpentry, v.spl_rdatum, (Relation) NULL,
736                                    (Page) NULL, (OffsetNumber) 0,
737                                    ((GISTENTRY *) VARDATA(entryvec))[i].bytes, FALSE);
738         if (v.spl_rdatum != tmpentry.pred)
739                 pfree(v.spl_rdatum);
740         v.spl_rdatum = tmpentry.pred;
741
742         /* clean up the entry vector: its preds need to be deleted, too */
743         for (i = FirstOffsetNumber; i <= maxoff + 1; i = OffsetNumberNext(i))
744                 if (decompvec[i])
745                         pfree(((GISTENTRY *) VARDATA(entryvec))[i].pred);
746         pfree(entryvec);
747         pfree(decompvec);
748
749         leftoff = rightoff = FirstOffsetNumber;
750         maxoff = PageGetMaxOffsetNumber(p);
751         for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
752         {
753                 itemid = PageGetItemId(p, i);
754                 item = (IndexTuple) PageGetItem(p, itemid);
755
756                 if (i == *(v.spl_left))
757                 {
758                         gistPageAddItem(giststate, r, left, (Item) item,
759                                                         IndexTupleSize(item),
760                                                         leftoff, LP_USED, &tmpdentry, &newtup);
761                         leftoff = OffsetNumberNext(leftoff);
762                         v.spl_left++;           /* advance in left split vector */
763                         /* be tidy */
764                         if (tmpdentry.pred != (((char *) item) + sizeof(IndexTupleData)))
765                                 pfree(tmpdentry.pred);
766                         if ((IndexTuple) item != newtup)
767                                 pfree(newtup);
768                 }
769                 else
770                 {
771                         gistPageAddItem(giststate, r, right, (Item) item,
772                                                         IndexTupleSize(item),
773                                                         rightoff, LP_USED, &tmpdentry, &newtup);
774                         rightoff = OffsetNumberNext(rightoff);
775                         v.spl_right++;          /* advance in right split vector */
776                         /* be tidy */
777                         if (tmpdentry.pred != (((char *) item) + sizeof(IndexTupleData)))
778                                 pfree(tmpdentry.pred);
779                         if (item != newtup)
780                                 pfree(newtup);
781                 }
782         }
783
784         /* build an InsertIndexResult for this insertion */
785         res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
786
787         /* now insert the new index tuple */
788         if (*(v.spl_left) != FirstOffsetNumber)
789         {
790                 gistPageAddItem(giststate, r, left, (Item) itup,
791                                                 IndexTupleSize(itup),
792                                                 leftoff, LP_USED, &tmpdentry, &newtup);
793                 leftoff = OffsetNumberNext(leftoff);
794                 ItemPointerSet(&(res->pointerData), lbknum, leftoff);
795                 /* be tidy */
796                 if (tmpdentry.pred != (((char *) itup) + sizeof(IndexTupleData)))
797                         pfree(tmpdentry.pred);
798                 if (itup != newtup)
799                         pfree(newtup);
800         }
801         else
802         {
803                 gistPageAddItem(giststate, r, right, (Item) itup,
804                                                 IndexTupleSize(itup),
805                                                 rightoff, LP_USED, &tmpdentry, &newtup);
806                 rightoff = OffsetNumberNext(rightoff);
807                 ItemPointerSet(&(res->pointerData), rbknum, rightoff);
808                 /* be tidy */
809                 if (tmpdentry.pred != (((char *) itup) + sizeof(IndexTupleData)))
810                         pfree(tmpdentry.pred);
811                 if (itup != newtup)
812                         pfree(newtup);
813         }
814
815         if ((bufblock = BufferGetBlockNumber(buffer)) != GISTP_ROOT)
816                 PageRestoreTempPage(left, p);
817         WriteBuffer(leftbuf);
818         WriteBuffer(rightbuf);
819
820         /*
821          * Okay, the page is split.  We have three things left to do:
822          *
823          * 1)  Adjust any active scans on this index to cope with changes we
824          * introduced in its structure by splitting this page.
825          *
826          * 2)  "Tighten" the bounding box of the pointer to the left page in the
827          * parent node in the tree, if any.  Since we moved a bunch of stuff
828          * off the left page, we expect it to get smaller.      This happens in
829          * the internal insertion routine.
830          *
831          * 3)  Insert a pointer to the right page in the parent.  This may cause
832          * the parent to split.  If it does, we need to repeat steps one and
833          * two for each split node in the tree.
834          */
835
836         /* adjust active scans */
837         gistadjscans(r, GISTOP_SPLIT, bufblock, FirstOffsetNumber);
838
839         tupDesc = r->rd_att;
840
841         ltup = (IndexTuple) index_formtuple(tupDesc,
842                                                                           (Datum *) &(v.spl_ldatum), isnull);
843         rtup = (IndexTuple) index_formtuple(tupDesc,
844                                                                           (Datum *) &(v.spl_rdatum), isnull);
845         pfree(isnull);
846
847         /* set pointers to new child pages in the internal index tuples */
848         ItemPointerSet(&(ltup->t_tid), lbknum, 1);
849         ItemPointerSet(&(rtup->t_tid), rbknum, 1);
850
851         gistintinsert(r, stack, ltup, rtup, giststate);
852
853         pfree(ltup);
854         pfree(rtup);
855
856         return res;
857 }
858
859 /*
860 ** After a split, we need to overwrite the old entry's key in the parent,
861 ** and install install an entry for the new key into the parent.
862 */
863 static void
864 gistintinsert(Relation r,
865                           GISTSTACK *stk,
866                           IndexTuple ltup,      /* new version of entry for old page */
867                           IndexTuple rtup,      /* entry for new page */
868                           GISTSTATE *giststate)
869 {
870         ItemPointerData ltid;
871
872         if (stk == (GISTSTACK *) NULL)
873         {
874                 gistnewroot(giststate, r, ltup, rtup);
875                 return;
876         }
877
878         /* remove old left pointer, insert the 2 new entries */
879         ItemPointerSet(&ltid, stk->gs_blk, stk->gs_child);
880         DirectFunctionCall2(gistdelete,
881                                                 PointerGetDatum(r),
882                                                 PointerGetDatum(&ltid));
883         gistentryinserttwo(r, stk, ltup, rtup, giststate);
884 }
885
886
887 /*
888 ** Insert two entries onto one page, handling a split for either one!
889 */
890 static void
891 gistentryinserttwo(Relation r, GISTSTACK *stk, IndexTuple ltup,
892                                    IndexTuple rtup, GISTSTATE *giststate)
893 {
894         Buffer          b;
895         Page            p;
896         InsertIndexResult res;
897         GISTENTRY       tmpentry;
898         IndexTuple      newtup;
899
900         b = ReadBuffer(r, stk->gs_blk);
901         p = BufferGetPage(b);
902
903         if (gistnospace(p, ltup))
904         {
905                 res = gistSplit(r, b, stk->gs_parent, ltup, giststate);
906                 WriteBuffer(b);                 /* don't forget to release buffer!  -
907                                                                  * 01/31/94 */
908                 pfree(res);
909                 gistdoinsert(r, rtup, giststate);
910         }
911         else
912         {
913                 gistPageAddItem(giststate, r, p, (Item) ltup,
914                                                 IndexTupleSize(ltup), InvalidOffsetNumber,
915                                                 LP_USED, &tmpentry, &newtup);
916                 WriteBuffer(b);
917                 gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, tmpentry.pred,
918                                            tmpentry.bytes, giststate);
919                 /* be tidy */
920                 if (tmpentry.pred != (((char *) ltup) + sizeof(IndexTupleData)))
921                         pfree(tmpentry.pred);
922                 if (ltup != newtup)
923                         pfree(newtup);
924                 gistentryinsert(r, stk, rtup, giststate);
925         }
926 }
927
928
929 /*
930 ** Insert an entry onto a page
931 */
932 static InsertIndexResult
933 gistentryinsert(Relation r, GISTSTACK *stk, IndexTuple tup,
934                                 GISTSTATE *giststate)
935 {
936         Buffer          b;
937         Page            p;
938         InsertIndexResult res;
939         OffsetNumber off;
940         GISTENTRY       tmpentry;
941         IndexTuple      newtup;
942
943         b = ReadBuffer(r, stk->gs_blk);
944         p = BufferGetPage(b);
945
946         if (gistnospace(p, tup))
947         {
948                 res = gistSplit(r, b, stk->gs_parent, tup, giststate);
949                 WriteBuffer(b);                 /* don't forget to release buffer!  -
950                                                                  * 01/31/94 */
951                 return res;
952         }
953         else
954         {
955                 res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
956                 off = gistPageAddItem(giststate, r, p, (Item) tup, IndexTupleSize(tup),
957                                            InvalidOffsetNumber, LP_USED, &tmpentry, &newtup);
958                 WriteBuffer(b);
959                 ItemPointerSet(&(res->pointerData), stk->gs_blk, off);
960                 gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, tmpentry.pred,
961                                            tmpentry.bytes, giststate);
962                 /* be tidy */
963                 if (tmpentry.pred != (((char *) tup) + sizeof(IndexTupleData)))
964                         pfree(tmpentry.pred);
965                 if (tup != newtup)
966                         pfree(newtup);
967                 return res;
968         }
969 }
970
971
972 static void
973 gistnewroot(GISTSTATE *giststate, Relation r, IndexTuple lt, IndexTuple rt)
974 {
975         Buffer          b;
976         Page            p;
977         GISTENTRY       tmpentry;
978         IndexTuple      newtup;
979
980         b = ReadBuffer(r, GISTP_ROOT);
981         GISTInitBuffer(b, 0);
982         p = BufferGetPage(b);
983         gistPageAddItem(giststate, r, p, (Item) lt, IndexTupleSize(lt),
984                                         FirstOffsetNumber,
985                                         LP_USED, &tmpentry, &newtup);
986         /* be tidy */
987         if (tmpentry.pred != (((char *) lt) + sizeof(IndexTupleData)))
988                 pfree(tmpentry.pred);
989         if (lt != newtup)
990                 pfree(newtup);
991         gistPageAddItem(giststate, r, p, (Item) rt, IndexTupleSize(rt),
992                                         OffsetNumberNext(FirstOffsetNumber), LP_USED,
993                                         &tmpentry, &newtup);
994         /* be tidy */
995         if (tmpentry.pred != (((char *) rt) + sizeof(IndexTupleData)))
996                 pfree(tmpentry.pred);
997         if (rt != newtup)
998                 pfree(newtup);
999         WriteBuffer(b);
1000 }
1001
1002 static void
1003 GISTInitBuffer(Buffer b, uint32 f)
1004 {
1005         GISTPageOpaque opaque;
1006         Page            page;
1007         Size            pageSize;
1008
1009         pageSize = BufferGetPageSize(b);
1010
1011         page = BufferGetPage(b);
1012         MemSet(page, 0, (int) pageSize);
1013         PageInit(page, pageSize, sizeof(GISTPageOpaqueData));
1014
1015         opaque = (GISTPageOpaque) PageGetSpecialPointer(page);
1016         opaque->flags = f;
1017 }
1018
1019
1020 /*
1021 ** find entry with lowest penalty
1022 */
1023 static OffsetNumber
1024 gistchoose(Relation r, Page p, IndexTuple it,   /* it has compressed entry */
1025                    GISTSTATE *giststate)
1026 {
1027         OffsetNumber maxoff;
1028         OffsetNumber i;
1029         char       *id;
1030         char       *datum;
1031         float           usize;
1032         OffsetNumber which;
1033         float           which_grow;
1034         GISTENTRY       entry,
1035                                 identry;
1036         int                     size,
1037                                 idsize;
1038
1039         idsize = IndexTupleSize(it) - sizeof(IndexTupleData);
1040         id = ((char *) it) + sizeof(IndexTupleData);
1041         maxoff = PageGetMaxOffsetNumber(p);
1042         which_grow = -1.0;
1043         which = -1;
1044
1045         gistdentryinit(giststate, &identry, id, (Relation) NULL, (Page) NULL,
1046                                    (OffsetNumber) 0, idsize, FALSE);
1047
1048         for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1049         {
1050                 datum = (char *) PageGetItem(p, PageGetItemId(p, i));
1051                 size = IndexTupleSize(datum) - sizeof(IndexTupleData);
1052                 datum += sizeof(IndexTupleData);
1053                 gistdentryinit(giststate, &entry, datum, r, p, i, size, FALSE);
1054                 FunctionCall3(&giststate->penaltyFn,
1055                                           PointerGetDatum(&entry),
1056                                           PointerGetDatum(&identry),
1057                                           PointerGetDatum(&usize));
1058                 if (which_grow < 0 || usize < which_grow)
1059                 {
1060                         which = i;
1061                         which_grow = usize;
1062                         if (which_grow == 0)
1063                                 break;
1064                 }
1065                 if (entry.pred != datum)
1066                         pfree(entry.pred);
1067         }
1068         if (identry.pred != id)
1069                 pfree(identry.pred);
1070
1071         return which;
1072 }
1073
1074 static int
1075 gistnospace(Page p, IndexTuple it)
1076 {
1077         return PageGetFreeSpace(p) < IndexTupleSize(it);
1078 }
1079
1080 void
1081 gistfreestack(GISTSTACK *s)
1082 {
1083         GISTSTACK  *p;
1084
1085         while (s != (GISTSTACK *) NULL)
1086         {
1087                 p = s->gs_parent;
1088                 pfree(s);
1089                 s = p;
1090         }
1091 }
1092
1093
1094 /*
1095 ** remove an entry from a page
1096 */
1097 Datum
1098 gistdelete(PG_FUNCTION_ARGS)
1099 {
1100         Relation                r = (Relation) PG_GETARG_POINTER(0);
1101         ItemPointer             tid = (ItemPointer) PG_GETARG_POINTER(1);
1102         BlockNumber blkno;
1103         OffsetNumber offnum;
1104         Buffer          buf;
1105         Page            page;
1106
1107         /*
1108          * Notes in ExecUtils:ExecOpenIndices() Also note that only vacuum
1109          * deletes index tuples now...
1110          *
1111          * RelationSetLockForWrite(r);
1112          */
1113
1114         blkno = ItemPointerGetBlockNumber(tid);
1115         offnum = ItemPointerGetOffsetNumber(tid);
1116
1117         /* adjust any scans that will be affected by this deletion */
1118         gistadjscans(r, GISTOP_DEL, blkno, offnum);
1119
1120         /* delete the index tuple */
1121         buf = ReadBuffer(r, blkno);
1122         page = BufferGetPage(buf);
1123
1124         PageIndexTupleDelete(page, offnum);
1125
1126         WriteBuffer(buf);
1127
1128         PG_RETURN_VOID();
1129 }
1130
1131 void
1132 initGISTstate(GISTSTATE *giststate, Relation index)
1133 {
1134         RegProcedure consistent_proc,
1135                                 union_proc,
1136                                 compress_proc,
1137                                 decompress_proc;
1138         RegProcedure penalty_proc,
1139                                 picksplit_proc,
1140                                 equal_proc;
1141         HeapTuple       htup;
1142         Form_pg_index itupform;
1143         Oid                     indexrelid;
1144
1145         consistent_proc = index_getprocid(index, 1, GIST_CONSISTENT_PROC);
1146         union_proc = index_getprocid(index, 1, GIST_UNION_PROC);
1147         compress_proc = index_getprocid(index, 1, GIST_COMPRESS_PROC);
1148         decompress_proc = index_getprocid(index, 1, GIST_DECOMPRESS_PROC);
1149         penalty_proc = index_getprocid(index, 1, GIST_PENALTY_PROC);
1150         picksplit_proc = index_getprocid(index, 1, GIST_PICKSPLIT_PROC);
1151         equal_proc = index_getprocid(index, 1, GIST_EQUAL_PROC);
1152         fmgr_info(consistent_proc, &giststate->consistentFn);
1153         fmgr_info(union_proc, &giststate->unionFn);
1154         fmgr_info(compress_proc, &giststate->compressFn);
1155         fmgr_info(decompress_proc, &giststate->decompressFn);
1156         fmgr_info(penalty_proc, &giststate->penaltyFn);
1157         fmgr_info(picksplit_proc, &giststate->picksplitFn);
1158         fmgr_info(equal_proc, &giststate->equalFn);
1159
1160         /* see if key type is different from type of attribute being indexed */
1161         htup = SearchSysCache(INDEXRELID,
1162                                                   ObjectIdGetDatum(RelationGetRelid(index)),
1163                                                   0, 0, 0);
1164         if (!HeapTupleIsValid(htup))
1165                 elog(ERROR, "initGISTstate: index %u not found",
1166                          RelationGetRelid(index));
1167         itupform = (Form_pg_index) GETSTRUCT(htup);
1168         giststate->haskeytype = itupform->indhaskeytype;
1169         indexrelid = itupform->indexrelid;
1170         ReleaseSysCache(htup);
1171
1172         if (giststate->haskeytype)
1173         {
1174                 /* key type is different -- is it byval? */
1175                 htup = SearchSysCache(ATTNUM,
1176                                                           ObjectIdGetDatum(indexrelid),
1177                                                           UInt16GetDatum(FirstOffsetNumber),
1178                                                           0, 0);
1179                 if (!HeapTupleIsValid(htup))
1180                         elog(ERROR, "initGISTstate: no attribute tuple %u %d",
1181                                  indexrelid, FirstOffsetNumber);
1182                 giststate->keytypbyval = (((Form_pg_attribute) htup)->attbyval);
1183                 ReleaseSysCache(htup);
1184         }
1185         else
1186                 giststate->keytypbyval = FALSE;
1187 }
1188
1189
1190 /*
1191 ** Given an IndexTuple to be inserted on a page, this routine replaces
1192 ** the key with another key, which may involve generating a new IndexTuple
1193 ** if the sizes don't match
1194 */
1195 static IndexTuple
1196 gist_tuple_replacekey(Relation r, GISTENTRY entry, IndexTuple t)
1197 {
1198         char       *datum = (((char *) t) + sizeof(IndexTupleData));
1199
1200         /* if new entry fits in index tuple, copy it in */
1201         if ((Size) entry.bytes < IndexTupleSize(t) - sizeof(IndexTupleData))
1202         {
1203                 memcpy(datum, entry.pred, entry.bytes);
1204                 /* clear out old size */
1205                 t->t_info &= 0xe000;
1206                 /* or in new size */
1207                 t->t_info |= MAXALIGN(entry.bytes + sizeof(IndexTupleData));
1208
1209                 return t;
1210         }
1211         else
1212         {
1213                 /* generate a new index tuple for the compressed entry */
1214                 TupleDesc       tupDesc = r->rd_att;
1215                 IndexTuple      newtup;
1216                 char       *isnull;
1217                 int                     blank;
1218
1219                 isnull = (char *) palloc(r->rd_rel->relnatts);
1220                 for (blank = 0; blank < r->rd_rel->relnatts; blank++)
1221                         isnull[blank] = ' ';
1222                 newtup = (IndexTuple) index_formtuple(tupDesc,
1223                                                                                           (Datum *) &(entry.pred),
1224                                                                                           isnull);
1225                 newtup->t_tid = t->t_tid;
1226                 pfree(isnull);
1227                 return newtup;
1228         }
1229 }
1230
1231
1232 /*
1233 ** initialize a GiST entry with a decompressed version of pred
1234 */
1235 void
1236 gistdentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr, Relation r,
1237                            Page pg, OffsetNumber o, int b, bool l)
1238 {
1239         GISTENTRY  *dep;
1240
1241         gistentryinit(*e, pr, r, pg, o, b, l);
1242         if (giststate->haskeytype)
1243         {
1244                 dep = (GISTENTRY *)
1245                         DatumGetPointer(FunctionCall1(&giststate->decompressFn,
1246                                                                                   PointerGetDatum(e)));
1247                 gistentryinit(*e, dep->pred, dep->rel, dep->page, dep->offset, dep->bytes,
1248                                           dep->leafkey);
1249                 if (dep != e)
1250                         pfree(dep);
1251         }
1252 }
1253
1254
1255 /*
1256 ** initialize a GiST entry with a compressed version of pred
1257 */
1258 static void
1259 gistcentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr, Relation r,
1260                            Page pg, OffsetNumber o, int b, bool l)
1261 {
1262         GISTENTRY  *cep;
1263
1264         gistentryinit(*e, pr, r, pg, o, b, l);
1265         if (giststate->haskeytype)
1266         {
1267                 cep = (GISTENTRY *)
1268                         DatumGetPointer(FunctionCall1(&giststate->compressFn,
1269                                                                                   PointerGetDatum(e)));
1270                 gistentryinit(*e, cep->pred, cep->rel, cep->page, cep->offset, cep->bytes,
1271                                           cep->leafkey);
1272                 if (cep != e)
1273                         pfree(cep);
1274         }
1275 }
1276
1277
1278
1279 #ifdef GISTDEBUG
1280
1281 /*
1282 ** sloppy debugging support routine, requires recompilation with appropriate
1283 ** "out" method for the index keys.  Could be fixed to find that info
1284 ** in the catalogs...
1285 */
1286 void
1287 _gistdump(Relation r)
1288 {
1289         Buffer          buf;
1290         Page            page;
1291         OffsetNumber offnum,
1292                                 maxoff;
1293         BlockNumber blkno;
1294         BlockNumber nblocks;
1295         GISTPageOpaque po;
1296         IndexTuple      itup;
1297         BlockNumber itblkno;
1298         OffsetNumber itoffno;
1299         char       *datum;
1300         char       *itkey;
1301
1302         nblocks = RelationGetNumberOfBlocks(r);
1303         for (blkno = 0; blkno < nblocks; blkno++)
1304         {
1305                 buf = ReadBuffer(r, blkno);
1306                 page = BufferGetPage(buf);
1307                 po = (GISTPageOpaque) PageGetSpecialPointer(page);
1308                 maxoff = PageGetMaxOffsetNumber(page);
1309                 printf("Page %d maxoff %d <%s>\n", blkno, maxoff,
1310                            (po->flags & F_LEAF ? "LEAF" : "INTERNAL"));
1311
1312                 if (PageIsEmpty(page))
1313                 {
1314                         ReleaseBuffer(buf);
1315                         continue;
1316                 }
1317
1318                 for (offnum = FirstOffsetNumber;
1319                          offnum <= maxoff;
1320                          offnum = OffsetNumberNext(offnum))
1321                 {
1322                         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
1323                         itblkno = ItemPointerGetBlockNumber(&(itup->t_tid));
1324                         itoffno = ItemPointerGetOffsetNumber(&(itup->t_tid));
1325                         datum = ((char *) itup);
1326                         datum += sizeof(IndexTupleData);
1327                         /* get out function for type of key, and out it! */
1328                         itkey = (char *) int_range_out((INTRANGE *) datum);
1329                         /* itkey = " unable to print"; */
1330                         printf("\t[%d] size %d heap <%d,%d> key:%s\n",
1331                                    offnum, IndexTupleSize(itup), itblkno, itoffno, itkey);
1332                         pfree(itkey);
1333                 }
1334
1335                 ReleaseBuffer(buf);
1336         }
1337 }
1338
1339 static char *
1340 int_range_out(INTRANGE *r)
1341 {
1342         char       *result;
1343
1344         if (r == NULL)
1345                 return NULL;
1346         result = (char *) palloc(80);
1347         snprintf(result, 80, "[%d,%d): %d", r->lower, r->upper, r->flag);
1348
1349         return result;
1350 }
1351
1352 #endif   /* defined GISTDEBUG */
1353
1354 #ifdef XLOG
1355 void
1356 gist_redo(XLogRecPtr lsn, XLogRecord *record)
1357 {
1358         elog(STOP, "gist_redo: unimplemented");
1359 }
1360  
1361 void
1362 gist_undo(XLogRecPtr lsn, XLogRecord *record)
1363 {
1364         elog(STOP, "gist_undo: unimplemented");
1365 }
1366  
1367 void
1368 gist_desc(char *buf, uint8 xl_info, char* rec)
1369 {
1370 }
1371 #endif