Header And Logo

PostgreSQL
| The world's most advanced open source database.

gist_private.h

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * gist_private.h
00004  *    private declarations for GiST -- declarations related to the
00005  *    internal implementation of GiST, not the public API
00006  *
00007  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00008  * Portions Copyright (c) 1994, Regents of the University of California
00009  *
00010  * src/include/access/gist_private.h
00011  *
00012  *-------------------------------------------------------------------------
00013  */
00014 #ifndef GIST_PRIVATE_H
00015 #define GIST_PRIVATE_H
00016 
00017 #include "access/gist.h"
00018 #include "access/itup.h"
00019 #include "fmgr.h"
00020 #include "storage/bufmgr.h"
00021 #include "storage/buffile.h"
00022 #include "utils/rbtree.h"
00023 #include "utils/hsearch.h"
00024 
00025 /* Buffer lock modes */
00026 #define GIST_SHARE  BUFFER_LOCK_SHARE
00027 #define GIST_EXCLUSIVE  BUFFER_LOCK_EXCLUSIVE
00028 #define GIST_UNLOCK BUFFER_LOCK_UNLOCK
00029 
00030 typedef struct
00031 {
00032     BlockNumber prev;
00033     uint32      freespace;
00034     char        tupledata[1];
00035 } GISTNodeBufferPage;
00036 
00037 #define BUFFER_PAGE_DATA_OFFSET MAXALIGN(offsetof(GISTNodeBufferPage, tupledata))
00038 /* Returns free space in node buffer page */
00039 #define PAGE_FREE_SPACE(nbp) (nbp->freespace)
00040 /* Checks if node buffer page is empty */
00041 #define PAGE_IS_EMPTY(nbp) (nbp->freespace == BLCKSZ - BUFFER_PAGE_DATA_OFFSET)
00042 /* Checks if node buffers page don't contain sufficient space for index tuple */
00043 #define PAGE_NO_SPACE(nbp, itup) (PAGE_FREE_SPACE(nbp) < \
00044                                         MAXALIGN(IndexTupleSize(itup)))
00045 
00046 /*
00047  * GISTSTATE: information needed for any GiST index operation
00048  *
00049  * This struct retains call info for the index's opclass-specific support
00050  * functions (per index column), plus the index's tuple descriptor.
00051  *
00052  * scanCxt holds the GISTSTATE itself as well as any data that lives for the
00053  * lifetime of the index operation.  We pass this to the support functions
00054  * via fn_mcxt, so that they can store scan-lifespan data in it.  The
00055  * functions are invoked in tempCxt, which is typically short-lifespan
00056  * (that is, it's reset after each tuple).  However, tempCxt can be the same
00057  * as scanCxt if we're not bothering with per-tuple context resets.
00058  */
00059 typedef struct GISTSTATE
00060 {
00061     MemoryContext scanCxt;      /* context for scan-lifespan data */
00062     MemoryContext tempCxt;      /* short-term context for calling functions */
00063 
00064     TupleDesc   tupdesc;        /* index's tuple descriptor */
00065 
00066     FmgrInfo    consistentFn[INDEX_MAX_KEYS];
00067     FmgrInfo    unionFn[INDEX_MAX_KEYS];
00068     FmgrInfo    compressFn[INDEX_MAX_KEYS];
00069     FmgrInfo    decompressFn[INDEX_MAX_KEYS];
00070     FmgrInfo    penaltyFn[INDEX_MAX_KEYS];
00071     FmgrInfo    picksplitFn[INDEX_MAX_KEYS];
00072     FmgrInfo    equalFn[INDEX_MAX_KEYS];
00073     FmgrInfo    distanceFn[INDEX_MAX_KEYS];
00074 
00075     /* Collations to pass to the support functions */
00076     Oid         supportCollation[INDEX_MAX_KEYS];
00077 } GISTSTATE;
00078 
00079 
00080 /*
00081  * During a GiST index search, we must maintain a queue of unvisited items,
00082  * which can be either individual heap tuples or whole index pages.  If it
00083  * is an ordered search, the unvisited items should be visited in distance
00084  * order.  Unvisited items at the same distance should be visited in
00085  * depth-first order, that is heap items first, then lower index pages, then
00086  * upper index pages; this rule avoids doing extra work during a search that
00087  * ends early due to LIMIT.
00088  *
00089  * To perform an ordered search, we use an RBTree to manage the distance-order
00090  * queue.  Each GISTSearchTreeItem stores all unvisited items of the same
00091  * distance; they are GISTSearchItems chained together via their next fields.
00092  *
00093  * In a non-ordered search (no order-by operators), the RBTree degenerates
00094  * to a single item, which we use as a queue of unvisited index pages only.
00095  * In this case matched heap items from the current index leaf page are
00096  * remembered in GISTScanOpaqueData.pageData[] and returned directly from
00097  * there, instead of building a separate GISTSearchItem for each one.
00098  */
00099 
00100 /* Individual heap tuple to be visited */
00101 typedef struct GISTSearchHeapItem
00102 {
00103     ItemPointerData heapPtr;
00104     bool        recheck;        /* T if quals must be rechecked */
00105 } GISTSearchHeapItem;
00106 
00107 /* Unvisited item, either index page or heap tuple */
00108 typedef struct GISTSearchItem
00109 {
00110     struct GISTSearchItem *next;    /* list link */
00111     BlockNumber blkno;          /* index page number, or InvalidBlockNumber */
00112     union
00113     {
00114         GistNSN     parentlsn;  /* parent page's LSN, if index page */
00115         /* we must store parentlsn to detect whether a split occurred */
00116         GISTSearchHeapItem heap;    /* heap info, if heap tuple */
00117     }           data;
00118 } GISTSearchItem;
00119 
00120 #define GISTSearchItemIsHeap(item)  ((item).blkno == InvalidBlockNumber)
00121 
00122 /*
00123  * Within a GISTSearchTreeItem's chain, heap items always appear before
00124  * index-page items, since we want to visit heap items first.  lastHeap points
00125  * to the last heap item in the chain, or is NULL if there are none.
00126  */
00127 typedef struct GISTSearchTreeItem
00128 {
00129     RBNode      rbnode;         /* this is an RBTree item */
00130     GISTSearchItem *head;       /* first chain member */
00131     GISTSearchItem *lastHeap;   /* last heap-tuple member, if any */
00132     double      distances[1];   /* array with numberOfOrderBys entries */
00133 } GISTSearchTreeItem;
00134 
00135 #define GSTIHDRSZ offsetof(GISTSearchTreeItem, distances)
00136 
00137 /*
00138  * GISTScanOpaqueData: private state for a scan of a GiST index
00139  */
00140 typedef struct GISTScanOpaqueData
00141 {
00142     GISTSTATE  *giststate;      /* index information, see above */
00143     RBTree     *queue;          /* queue of unvisited items */
00144     MemoryContext queueCxt;     /* context holding the queue */
00145     bool        qual_ok;        /* false if qual can never be satisfied */
00146     bool        firstCall;      /* true until first gistgettuple call */
00147 
00148     GISTSearchTreeItem *curTreeItem;    /* current queue item, if any */
00149 
00150     /* pre-allocated workspace arrays */
00151     GISTSearchTreeItem *tmpTreeItem;    /* workspace to pass to rb_insert */
00152     double     *distances;      /* output area for gistindex_keytest */
00153 
00154     /* In a non-ordered search, returnable heap items are stored here: */
00155     GISTSearchHeapItem pageData[BLCKSZ / sizeof(IndexTupleData)];
00156     OffsetNumber nPageData;     /* number of valid items in array */
00157     OffsetNumber curPageData;   /* next item to return */
00158 } GISTScanOpaqueData;
00159 
00160 typedef GISTScanOpaqueData *GISTScanOpaque;
00161 
00162 
00163 /* XLog stuff */
00164 
00165 #define XLOG_GIST_PAGE_UPDATE       0x00
00166  /* #define XLOG_GIST_NEW_ROOT           0x20 */    /* not used anymore */
00167 #define XLOG_GIST_PAGE_SPLIT        0x30
00168  /* #define XLOG_GIST_INSERT_COMPLETE    0x40 */    /* not used anymore */
00169 #define XLOG_GIST_CREATE_INDEX      0x50
00170  /* #define XLOG_GIST_PAGE_DELETE        0x60 */    /* not used anymore */
00171 
00172 typedef struct gistxlogPageUpdate
00173 {
00174     RelFileNode node;
00175     BlockNumber blkno;
00176 
00177     /*
00178      * If this operation completes a page split, by inserting a downlink for
00179      * the split page, leftchild points to the left half of the split.
00180      */
00181     BlockNumber leftchild;
00182 
00183     /* number of deleted offsets */
00184     uint16      ntodelete;
00185 
00186     /*
00187      * follow: 1. todelete OffsetNumbers 2. tuples to insert
00188      */
00189 } gistxlogPageUpdate;
00190 
00191 typedef struct gistxlogPageSplit
00192 {
00193     RelFileNode node;
00194     BlockNumber origblkno;      /* splitted page */
00195     BlockNumber origrlink;      /* rightlink of the page before split */
00196     GistNSN     orignsn;        /* NSN of the page before split */
00197     bool        origleaf;       /* was splitted page a leaf page? */
00198 
00199     BlockNumber leftchild;      /* like in gistxlogPageUpdate */
00200     uint16      npage;          /* # of pages in the split */
00201     bool        markfollowright;    /* set F_FOLLOW_RIGHT flags */
00202 
00203     /*
00204      * follow: 1. gistxlogPage and array of IndexTupleData per page
00205      */
00206 } gistxlogPageSplit;
00207 
00208 typedef struct gistxlogPage
00209 {
00210     BlockNumber blkno;
00211     int         num;            /* number of index tuples following */
00212 } gistxlogPage;
00213 
00214 /* SplitedPageLayout - gistSplit function result */
00215 typedef struct SplitedPageLayout
00216 {
00217     gistxlogPage block;
00218     IndexTupleData *list;
00219     int         lenlist;
00220     IndexTuple  itup;           /* union key for page */
00221     Page        page;           /* to operate */
00222     Buffer      buffer;         /* to write after all proceed */
00223 
00224     struct SplitedPageLayout *next;
00225 } SplitedPageLayout;
00226 
00227 /*
00228  * GISTInsertStack used for locking buffers and transfer arguments during
00229  * insertion
00230  */
00231 typedef struct GISTInsertStack
00232 {
00233     /* current page */
00234     BlockNumber blkno;
00235     Buffer      buffer;
00236     Page        page;
00237 
00238     /*
00239      * log sequence number from page->lsn to recognize page update and compare
00240      * it with page's nsn to recognize page split
00241      */
00242     GistNSN     lsn;
00243 
00244     /* offset of the downlink in the parent page, that points to this page */
00245     OffsetNumber downlinkoffnum;
00246 
00247     /* pointer to parent */
00248     struct GISTInsertStack *parent;
00249 } GISTInsertStack;
00250 
00251 /* Working state and results for multi-column split logic in gistsplit.c */
00252 typedef struct GistSplitVector
00253 {
00254     GIST_SPLITVEC splitVector;  /* passed to/from user PickSplit method */
00255 
00256     Datum       spl_lattr[INDEX_MAX_KEYS];      /* Union of subkeys in
00257                                                  * splitVector.spl_left */
00258     bool        spl_lisnull[INDEX_MAX_KEYS];
00259 
00260     Datum       spl_rattr[INDEX_MAX_KEYS];      /* Union of subkeys in
00261                                                  * splitVector.spl_right */
00262     bool        spl_risnull[INDEX_MAX_KEYS];
00263 
00264     bool       *spl_dontcare;   /* flags tuples which could go to either side
00265                                  * of the split for zero penalty */
00266 } GistSplitVector;
00267 
00268 typedef struct
00269 {
00270     Relation    r;
00271     Size        freespace;      /* free space to be left */
00272 
00273     GISTInsertStack *stack;
00274 } GISTInsertState;
00275 
00276 /* root page of a gist index */
00277 #define GIST_ROOT_BLKNO             0
00278 
00279 /*
00280  * Before PostgreSQL 9.1, we used rely on so-called "invalid tuples" on inner
00281  * pages to finish crash recovery of incomplete page splits. If a crash
00282  * happened in the middle of a page split, so that the downlink pointers were
00283  * not yet inserted, crash recovery inserted a special downlink pointer. The
00284  * semantics of an invalid tuple was that it if you encounter one in a scan,
00285  * it must always be followed, because we don't know if the tuples on the
00286  * child page match or not.
00287  *
00288  * We no longer create such invalid tuples, we now mark the left-half of such
00289  * an incomplete split with the F_FOLLOW_RIGHT flag instead, and finish the
00290  * split properly the next time we need to insert on that page. To retain
00291  * on-disk compatibility for the sake of pg_upgrade, we still store 0xffff as
00292  * the offset number of all inner tuples. If we encounter any invalid tuples
00293  * with 0xfffe during insertion, we throw an error, though scans still handle
00294  * them. You should only encounter invalid tuples if you pg_upgrade a pre-9.1
00295  * gist index which already has invalid tuples in it because of a crash. That
00296  * should be rare, and you are recommended to REINDEX anyway if you have any
00297  * invalid tuples in an index, so throwing an error is as far as we go with
00298  * supporting that.
00299  */
00300 #define TUPLE_IS_VALID      0xffff
00301 #define TUPLE_IS_INVALID    0xfffe
00302 
00303 #define  GistTupleIsInvalid(itup)   ( ItemPointerGetOffsetNumber( &((itup)->t_tid) ) == TUPLE_IS_INVALID )
00304 #define  GistTupleSetValid(itup)    ItemPointerSetOffsetNumber( &((itup)->t_tid), TUPLE_IS_VALID )
00305 
00306 
00307 
00308 
00309 /*
00310  * A buffer attached to an internal node, used when building an index in
00311  * buffering mode.
00312  */
00313 typedef struct
00314 {
00315     BlockNumber nodeBlocknum;   /* index block # this buffer is for */
00316     int32       blocksCount;    /* current # of blocks occupied by buffer */
00317 
00318     BlockNumber pageBlocknum;   /* temporary file block # */
00319     GISTNodeBufferPage *pageBuffer;     /* in-memory buffer page */
00320 
00321     /* is this buffer queued for emptying? */
00322     bool        queuedForEmptying;
00323 
00324     /* is this a temporary copy, not in the hash table? */
00325     bool        isTemp;
00326 
00327     int         level;          /* 0 == leaf */
00328 } GISTNodeBuffer;
00329 
00330 /*
00331  * Does specified level have buffers? (Beware of multiple evaluation of
00332  * arguments.)
00333  */
00334 #define LEVEL_HAS_BUFFERS(nlevel, gfbb) \
00335     ((nlevel) != 0 && (nlevel) % (gfbb)->levelStep == 0 && \
00336      (nlevel) != (gfbb)->rootlevel)
00337 
00338 /* Is specified buffer at least half-filled (should be queued for emptying)? */
00339 #define BUFFER_HALF_FILLED(nodeBuffer, gfbb) \
00340     ((nodeBuffer)->blocksCount > (gfbb)->pagesPerBuffer / 2)
00341 
00342 /*
00343  * Is specified buffer full? Our buffers can actually grow indefinitely,
00344  * beyond the "maximum" size, so this just means whether the buffer has grown
00345  * beyond the nominal maximum size.
00346  */
00347 #define BUFFER_OVERFLOWED(nodeBuffer, gfbb) \
00348     ((nodeBuffer)->blocksCount > (gfbb)->pagesPerBuffer)
00349 
00350 /*
00351  * Data structure with general information about build buffers.
00352  */
00353 typedef struct GISTBuildBuffers
00354 {
00355     /* Persistent memory context for the buffers and metadata. */
00356     MemoryContext context;
00357 
00358     BufFile    *pfile;          /* Temporary file to store buffers in */
00359     long        nFileBlocks;    /* Current size of the temporary file */
00360 
00361     /*
00362      * resizable array of free blocks.
00363      */
00364     long       *freeBlocks;
00365     int         nFreeBlocks;    /* # of currently free blocks in the array */
00366     int         freeBlocksLen;  /* current allocated length of the array */
00367 
00368     /* Hash for buffers by block number */
00369     HTAB       *nodeBuffersTab;
00370 
00371     /* List of buffers scheduled for emptying */
00372     List       *bufferEmptyingQueue;
00373 
00374     /*
00375      * Parameters to the buffering build algorithm. levelStep determines which
00376      * levels in the tree have buffers, and pagesPerBuffer determines how
00377      * large each buffer is.
00378      */
00379     int         levelStep;
00380     int         pagesPerBuffer;
00381 
00382     /* Array of lists of buffers on each level, for final emptying */
00383     List      **buffersOnLevels;
00384     int         buffersOnLevelsLen;
00385 
00386     /*
00387      * Dynamically-sized array of buffers that currently have their last page
00388      * loaded in main memory.
00389      */
00390     GISTNodeBuffer **loadedBuffers;
00391     int         loadedBuffersCount;     /* # of entries in loadedBuffers */
00392     int         loadedBuffersLen;       /* allocated size of loadedBuffers */
00393 
00394     /* Level of the current root node (= height of the index tree - 1) */
00395     int         rootlevel;
00396 } GISTBuildBuffers;
00397 
00398 /*
00399  * Storage type for GiST's reloptions
00400  */
00401 typedef struct GiSTOptions
00402 {
00403     int32       vl_len_;        /* varlena header (do not touch directly!) */
00404     int         fillfactor;     /* page fill factor in percent (0..100) */
00405     int         bufferingModeOffset;    /* use buffering build? */
00406 } GiSTOptions;
00407 
00408 /* gist.c */
00409 extern Datum gistbuildempty(PG_FUNCTION_ARGS);
00410 extern Datum gistinsert(PG_FUNCTION_ARGS);
00411 extern MemoryContext createTempGistContext(void);
00412 extern GISTSTATE *initGISTstate(Relation index);
00413 extern void freeGISTstate(GISTSTATE *giststate);
00414 extern void gistdoinsert(Relation r,
00415              IndexTuple itup,
00416              Size freespace,
00417              GISTSTATE *GISTstate);
00418 
00419 /* A List of these is returned from gistplacetopage() in *splitinfo */
00420 typedef struct
00421 {
00422     Buffer      buf;            /* the split page "half" */
00423     IndexTuple  downlink;       /* downlink for this half. */
00424 } GISTPageSplitInfo;
00425 
00426 extern bool gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
00427                 Buffer buffer,
00428                 IndexTuple *itup, int ntup,
00429                 OffsetNumber oldoffnum, BlockNumber *newblkno,
00430                 Buffer leftchildbuf,
00431                 List **splitinfo,
00432                 bool markleftchild);
00433 
00434 extern SplitedPageLayout *gistSplit(Relation r, Page page, IndexTuple *itup,
00435           int len, GISTSTATE *giststate);
00436 
00437 /* gistxlog.c */
00438 extern void gist_redo(XLogRecPtr lsn, XLogRecord *record);
00439 extern void gist_desc(StringInfo buf, uint8 xl_info, char *rec);
00440 extern void gist_xlog_startup(void);
00441 extern void gist_xlog_cleanup(void);
00442 
00443 extern XLogRecPtr gistXLogUpdate(RelFileNode node, Buffer buffer,
00444                OffsetNumber *todelete, int ntodelete,
00445                IndexTuple *itup, int ntup,
00446                Buffer leftchild);
00447 
00448 extern XLogRecPtr gistXLogSplit(RelFileNode node,
00449               BlockNumber blkno, bool page_is_leaf,
00450               SplitedPageLayout *dist,
00451               BlockNumber origrlink, GistNSN oldnsn,
00452               Buffer leftchild, bool markfollowright);
00453 
00454 /* gistget.c */
00455 extern Datum gistgettuple(PG_FUNCTION_ARGS);
00456 extern Datum gistgetbitmap(PG_FUNCTION_ARGS);
00457 
00458 /* gistutil.c */
00459 
00460 #define GiSTPageSize   \
00461     ( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GISTPageOpaqueData)) )
00462 
00463 #define GIST_MIN_FILLFACTOR         10
00464 #define GIST_DEFAULT_FILLFACTOR     90
00465 
00466 extern Datum gistoptions(PG_FUNCTION_ARGS);
00467 extern bool gistfitpage(IndexTuple *itvec, int len);
00468 extern bool gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace);
00469 extern void gistcheckpage(Relation rel, Buffer buf);
00470 extern Buffer gistNewBuffer(Relation r);
00471 extern void gistfillbuffer(Page page, IndexTuple *itup, int len,
00472                OffsetNumber off);
00473 extern IndexTuple *gistextractpage(Page page, int *len /* out */ );
00474 extern IndexTuple *gistjoinvector(
00475                IndexTuple *itvec, int *len,
00476                IndexTuple *additvec, int addlen);
00477 extern IndexTupleData *gistfillitupvec(IndexTuple *vec, int veclen, int *memlen);
00478 
00479 extern IndexTuple gistunion(Relation r, IndexTuple *itvec,
00480           int len, GISTSTATE *giststate);
00481 extern IndexTuple gistgetadjusted(Relation r,
00482                 IndexTuple oldtup,
00483                 IndexTuple addtup,
00484                 GISTSTATE *giststate);
00485 extern IndexTuple gistFormTuple(GISTSTATE *giststate,
00486               Relation r, Datum *attdata, bool *isnull, bool newValues);
00487 
00488 extern OffsetNumber gistchoose(Relation r, Page p,
00489            IndexTuple it,
00490            GISTSTATE *giststate);
00491 extern void gistcentryinit(GISTSTATE *giststate, int nkey,
00492                GISTENTRY *e, Datum k,
00493                Relation r, Page pg,
00494                OffsetNumber o, bool l, bool isNull);
00495 
00496 extern void GISTInitBuffer(Buffer b, uint32 f);
00497 extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
00498                Datum k, Relation r, Page pg, OffsetNumber o,
00499                bool l, bool isNull);
00500 
00501 extern float gistpenalty(GISTSTATE *giststate, int attno,
00502             GISTENTRY *key1, bool isNull1,
00503             GISTENTRY *key2, bool isNull2);
00504 extern void gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
00505                    Datum *attr, bool *isnull);
00506 extern bool gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b);
00507 extern void gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p,
00508                   OffsetNumber o, GISTENTRY *attdata, bool *isnull);
00509 
00510 extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
00511                  GISTENTRY *entry1, bool isnull1,
00512                  GISTENTRY *entry2, bool isnull2,
00513                  Datum *dst, bool *dstisnull);
00514 
00515 extern XLogRecPtr gistGetFakeLSN(Relation rel);
00516 
00517 /* gistvacuum.c */
00518 extern Datum gistbulkdelete(PG_FUNCTION_ARGS);
00519 extern Datum gistvacuumcleanup(PG_FUNCTION_ARGS);
00520 
00521 /* gistsplit.c */
00522 extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup,
00523                int len, GISTSTATE *giststate,
00524                GistSplitVector *v,
00525                int attno);
00526 
00527 /* gistbuild.c */
00528 extern Datum gistbuild(PG_FUNCTION_ARGS);
00529 extern void gistValidateBufferingOption(char *value);
00530 
00531 /* gistbuildbuffers.c */
00532 extern GISTBuildBuffers *gistInitBuildBuffers(int pagesPerBuffer, int levelStep,
00533                      int maxLevel);
00534 extern GISTNodeBuffer *gistGetNodeBuffer(GISTBuildBuffers *gfbb,
00535                   GISTSTATE *giststate,
00536                   BlockNumber blkno, int level);
00537 extern void gistPushItupToNodeBuffer(GISTBuildBuffers *gfbb,
00538                          GISTNodeBuffer *nodeBuffer, IndexTuple item);
00539 extern bool gistPopItupFromNodeBuffer(GISTBuildBuffers *gfbb,
00540                           GISTNodeBuffer *nodeBuffer, IndexTuple *item);
00541 extern void gistFreeBuildBuffers(GISTBuildBuffers *gfbb);
00542 extern void gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb,
00543                                 GISTSTATE *giststate, Relation r,
00544                                 int level, Buffer buffer,
00545                                 List *splitinfo);
00546 extern void gistUnloadNodeBuffers(GISTBuildBuffers *gfbb);
00547 
00548 #endif   /* GIST_PRIVATE_H */