1 /*-------------------------------------------------------------------------
2  *
3  * gist_private.h
4  *	  private declarations for GiST -- declarations related to the
5  *	  internal implementation of GiST, not the public API
6  *
7  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  * src/include/access/gist_private.h
11  *
12  *-------------------------------------------------------------------------
13  */
14 #ifndef GIST_PRIVATE_H
15 #define GIST_PRIVATE_H
16 
17 #include "access/amapi.h"
18 #include "access/gist.h"
19 #include "access/itup.h"
20 #include "access/xlogreader.h"
21 #include "fmgr.h"
22 #include "lib/pairingheap.h"
23 #include "storage/bufmgr.h"
24 #include "storage/buffile.h"
25 #include "utils/hsearch.h"
26 #include "access/genam.h"
27 
28 /*
29  * Maximum number of "halves" a page can be split into in one operation.
30  * Typically a split produces 2 halves, but can be more if keys have very
31  * different lengths, or when inserting multiple keys in one operation (as
32  * when inserting downlinks to an internal node).  There is no theoretical
33  * limit on this, but in practice if you get more than a handful page halves
34  * in one split, there's something wrong with the opclass implementation.
35  * GIST_MAX_SPLIT_PAGES is an arbitrary limit on that, used to size some
36  * local arrays used during split.  Note that there is also a limit on the
37  * number of buffers that can be held locked at a time, MAX_SIMUL_LWLOCKS,
38  * so if you raise this higher than that limit, you'll just get a different
39  * error.
40  */
41 #define GIST_MAX_SPLIT_PAGES		75
42 
43 /* Buffer lock modes */
44 #define GIST_SHARE	BUFFER_LOCK_SHARE
45 #define GIST_EXCLUSIVE	BUFFER_LOCK_EXCLUSIVE
46 #define GIST_UNLOCK BUFFER_LOCK_UNLOCK
47 
48 typedef struct
49 {
50 	BlockNumber prev;
51 	uint32		freespace;
52 	char		tupledata[FLEXIBLE_ARRAY_MEMBER];
53 } GISTNodeBufferPage;
54 
55 #define BUFFER_PAGE_DATA_OFFSET MAXALIGN(offsetof(GISTNodeBufferPage, tupledata))
56 /* Returns free space in node buffer page */
57 #define PAGE_FREE_SPACE(nbp) (nbp->freespace)
58 /* Checks if node buffer page is empty */
59 #define PAGE_IS_EMPTY(nbp) (nbp->freespace == BLCKSZ - BUFFER_PAGE_DATA_OFFSET)
60 /* Checks if node buffers page don't contain sufficient space for index tuple */
61 #define PAGE_NO_SPACE(nbp, itup) (PAGE_FREE_SPACE(nbp) < \
62 										MAXALIGN(IndexTupleSize(itup)))
63 
64 /*
65  * GISTSTATE: information needed for any GiST index operation
66  *
67  * This struct retains call info for the index's opclass-specific support
68  * functions (per index column), plus the index's tuple descriptor.
69  *
70  * scanCxt holds the GISTSTATE itself as well as any data that lives for the
71  * lifetime of the index operation.  We pass this to the support functions
72  * via fn_mcxt, so that they can store scan-lifespan data in it.  The
73  * functions are invoked in tempCxt, which is typically short-lifespan
74  * (that is, it's reset after each tuple).  However, tempCxt can be the same
75  * as scanCxt if we're not bothering with per-tuple context resets.
76  */
77 typedef struct GISTSTATE
78 {
79 	MemoryContext scanCxt;		/* context for scan-lifespan data */
80 	MemoryContext tempCxt;		/* short-term context for calling functions */
81 
82 	TupleDesc	tupdesc;		/* index's tuple descriptor */
83 	TupleDesc	fetchTupdesc;	/* tuple descriptor for tuples returned in an
84 								 * index-only scan */
85 
86 	FmgrInfo	consistentFn[INDEX_MAX_KEYS];
87 	FmgrInfo	unionFn[INDEX_MAX_KEYS];
88 	FmgrInfo	compressFn[INDEX_MAX_KEYS];
89 	FmgrInfo	decompressFn[INDEX_MAX_KEYS];
90 	FmgrInfo	penaltyFn[INDEX_MAX_KEYS];
91 	FmgrInfo	picksplitFn[INDEX_MAX_KEYS];
92 	FmgrInfo	equalFn[INDEX_MAX_KEYS];
93 	FmgrInfo	distanceFn[INDEX_MAX_KEYS];
94 	FmgrInfo	fetchFn[INDEX_MAX_KEYS];
95 
96 	/* Collations to pass to the support functions */
97 	Oid			supportCollation[INDEX_MAX_KEYS];
98 } GISTSTATE;
99 
100 
101 /*
102  * During a GiST index search, we must maintain a queue of unvisited items,
103  * which can be either individual heap tuples or whole index pages.  If it
104  * is an ordered search, the unvisited items should be visited in distance
105  * order.  Unvisited items at the same distance should be visited in
106  * depth-first order, that is heap items first, then lower index pages, then
107  * upper index pages; this rule avoids doing extra work during a search that
108  * ends early due to LIMIT.
109  *
110  * To perform an ordered search, we use a pairing heap to manage the
111  * distance-order queue.  In a non-ordered search (no order-by operators),
112  * we use it to return heap tuples before unvisited index pages, to
113  * ensure depth-first order, but all entries are otherwise considered
114  * equal.
115  */
116 
117 /* Individual heap tuple to be visited */
118 typedef struct GISTSearchHeapItem
119 {
120 	ItemPointerData heapPtr;
121 	bool		recheck;		/* T if quals must be rechecked */
122 	bool		recheckDistances;		/* T if distances must be rechecked */
123 	IndexTuple	ftup;			/* data fetched back from the index, used in
124 								 * index-only scans */
125 	OffsetNumber offnum;		/* track offset in page to mark tuple as
126 								 * LP_DEAD */
127 } GISTSearchHeapItem;
128 
129 /* Unvisited item, either index page or heap tuple */
130 typedef struct GISTSearchItem
131 {
132 	pairingheap_node phNode;
133 	BlockNumber blkno;			/* index page number, or InvalidBlockNumber */
134 	union
135 	{
136 		GistNSN		parentlsn;	/* parent page's LSN, if index page */
137 		/* we must store parentlsn to detect whether a split occurred */
138 		GISTSearchHeapItem heap;	/* heap info, if heap tuple */
139 	}			data;
140 
141 	/* numberOfOrderBys entries */
142 	IndexOrderByDistance distances[FLEXIBLE_ARRAY_MEMBER];
143 } GISTSearchItem;
144 
145 #define GISTSearchItemIsHeap(item)	((item).blkno == InvalidBlockNumber)
146 
147 #define SizeOfGISTSearchItem(n_distances) \
148 	(offsetof(GISTSearchItem, distances) + \
149 	 sizeof(IndexOrderByDistance) * (n_distances))
150 
151 /*
152  * GISTScanOpaqueData: private state for a scan of a GiST index
153  */
154 typedef struct GISTScanOpaqueData
155 {
156 	GISTSTATE  *giststate;		/* index information, see above */
157 	Oid		   *orderByTypes;	/* datatypes of ORDER BY expressions */
158 
159 	pairingheap *queue;			/* queue of unvisited items */
160 	MemoryContext queueCxt;		/* context holding the queue */
161 	bool		qual_ok;		/* false if qual can never be satisfied */
162 	bool		firstCall;		/* true until first gistgettuple call */
163 
164 	/* pre-allocated workspace arrays */
165 	IndexOrderByDistance *distances;	/* output area for gistindex_keytest */
166 
167 	/* info about killed items if any (killedItems is NULL if never used) */
168 	OffsetNumber *killedItems;	/* offset numbers of killed items */
169 	int			numKilled;		/* number of currently stored items */
170 	BlockNumber curBlkno;		/* current number of block */
171 	GistNSN		curPageLSN;		/* pos in the WAL stream when page was read */
172 
173 	/* In a non-ordered search, returnable heap items are stored here: */
174 	GISTSearchHeapItem pageData[BLCKSZ / sizeof(IndexTupleData)];
175 	OffsetNumber nPageData;		/* number of valid items in array */
176 	OffsetNumber curPageData;	/* next item to return */
177 	MemoryContext pageDataCxt;	/* context holding the fetched tuples, for
178 								 * index-only scans */
179 } GISTScanOpaqueData;
180 
181 typedef GISTScanOpaqueData *GISTScanOpaque;
182 
183 
184 /* XLog stuff */
185 
186 #define XLOG_GIST_PAGE_UPDATE		0x00
187  /* #define XLOG_GIST_NEW_ROOT			 0x20 */	/* not used anymore */
188 #define XLOG_GIST_PAGE_SPLIT		0x30
189  /* #define XLOG_GIST_INSERT_COMPLETE	 0x40 */	/* not used anymore */
190 #define XLOG_GIST_CREATE_INDEX		0x50
191  /* #define XLOG_GIST_PAGE_DELETE		 0x60 */	/* not used anymore */
192 
193 /*
194  * Backup Blk 0: updated page.
195  * Backup Blk 1: If this operation completes a page split, by inserting a
196  *				 downlink for the split page, the left half of the split
197  */
198 typedef struct gistxlogPageUpdate
199 {
200 	/* number of deleted offsets */
201 	uint16		ntodelete;
202 	uint16		ntoinsert;
203 
204 	/*
205 	 * In payload of blk 0 : 1. todelete OffsetNumbers 2. tuples to insert
206 	 */
207 } gistxlogPageUpdate;
208 
209 /*
210  * Backup Blk 0: If this operation completes a page split, by inserting a
211  *				 downlink for the split page, the left half of the split
212  * Backup Blk 1 - npage: split pages (1 is the original page)
213  */
214 typedef struct gistxlogPageSplit
215 {
216 	BlockNumber origrlink;		/* rightlink of the page before split */
217 	GistNSN		orignsn;		/* NSN of the page before split */
218 	bool		origleaf;		/* was splitted page a leaf page? */
219 
220 	uint16		npage;			/* # of pages in the split */
221 	bool		markfollowright;	/* set F_FOLLOW_RIGHT flags */
222 
223 	/*
224 	 * follow: 1. gistxlogPage and array of IndexTupleData per page
225 	 */
226 } gistxlogPageSplit;
227 
228 typedef struct gistxlogPage
229 {
230 	BlockNumber blkno;
231 	int			num;			/* number of index tuples following */
232 } gistxlogPage;
233 
234 /* SplitedPageLayout - gistSplit function result */
235 typedef struct SplitedPageLayout
236 {
237 	gistxlogPage block;
238 	IndexTupleData *list;
239 	int			lenlist;
240 	IndexTuple	itup;			/* union key for page */
241 	Page		page;			/* to operate */
242 	Buffer		buffer;			/* to write after all proceed */
243 
244 	struct SplitedPageLayout *next;
245 } SplitedPageLayout;
246 
247 /*
248  * GISTInsertStack used for locking buffers and transfer arguments during
249  * insertion
250  */
251 typedef struct GISTInsertStack
252 {
253 	/* current page */
254 	BlockNumber blkno;
255 	Buffer		buffer;
256 	Page		page;
257 
258 	/*
259 	 * log sequence number from page->lsn to recognize page update and compare
260 	 * it with page's nsn to recognize page split
261 	 */
262 	GistNSN		lsn;
263 
264 	/* offset of the downlink in the parent page, that points to this page */
265 	OffsetNumber downlinkoffnum;
266 
267 	/* pointer to parent */
268 	struct GISTInsertStack *parent;
269 } GISTInsertStack;
270 
271 /* Working state and results for multi-column split logic in gistsplit.c */
272 typedef struct GistSplitVector
273 {
274 	GIST_SPLITVEC splitVector;	/* passed to/from user PickSplit method */
275 
276 	Datum		spl_lattr[INDEX_MAX_KEYS];		/* Union of subkeys in
277 												 * splitVector.spl_left */
278 	bool		spl_lisnull[INDEX_MAX_KEYS];
279 
280 	Datum		spl_rattr[INDEX_MAX_KEYS];		/* Union of subkeys in
281 												 * splitVector.spl_right */
282 	bool		spl_risnull[INDEX_MAX_KEYS];
283 
284 	bool	   *spl_dontcare;	/* flags tuples which could go to either side
285 								 * of the split for zero penalty */
286 } GistSplitVector;
287 
288 typedef struct
289 {
290 	Relation	r;
291 	Relation	heapRel;
292 	Size		freespace;		/* free space to be left */
293 
294 	GISTInsertStack *stack;
295 } GISTInsertState;
296 
297 /* root page of a gist index */
298 #define GIST_ROOT_BLKNO				0
299 
300 /*
301  * Before PostgreSQL 9.1, we used to rely on so-called "invalid tuples" on
302  * inner pages to finish crash recovery of incomplete page splits. If a crash
303  * happened in the middle of a page split, so that the downlink pointers were
304  * not yet inserted, crash recovery inserted a special downlink pointer. The
305  * semantics of an invalid tuple was that it if you encounter one in a scan,
306  * it must always be followed, because we don't know if the tuples on the
307  * child page match or not.
308  *
309  * We no longer create such invalid tuples, we now mark the left-half of such
310  * an incomplete split with the F_FOLLOW_RIGHT flag instead, and finish the
311  * split properly the next time we need to insert on that page. To retain
312  * on-disk compatibility for the sake of pg_upgrade, we still store 0xffff as
313  * the offset number of all inner tuples. If we encounter any invalid tuples
314  * with 0xfffe during insertion, we throw an error, though scans still handle
315  * them. You should only encounter invalid tuples if you pg_upgrade a pre-9.1
316  * gist index which already has invalid tuples in it because of a crash. That
317  * should be rare, and you are recommended to REINDEX anyway if you have any
318  * invalid tuples in an index, so throwing an error is as far as we go with
319  * supporting that.
320  */
321 #define TUPLE_IS_VALID		0xffff
322 #define TUPLE_IS_INVALID	0xfffe
323 
324 #define  GistTupleIsInvalid(itup)	( ItemPointerGetOffsetNumber( &((itup)->t_tid) ) == TUPLE_IS_INVALID )
325 #define  GistTupleSetValid(itup)	ItemPointerSetOffsetNumber( &((itup)->t_tid), TUPLE_IS_VALID )
326 
327 
328 
329 
330 /*
331  * A buffer attached to an internal node, used when building an index in
332  * buffering mode.
333  */
334 typedef struct
335 {
336 	BlockNumber nodeBlocknum;	/* index block # this buffer is for */
337 	int32		blocksCount;	/* current # of blocks occupied by buffer */
338 
339 	BlockNumber pageBlocknum;	/* temporary file block # */
340 	GISTNodeBufferPage *pageBuffer;		/* in-memory buffer page */
341 
342 	/* is this buffer queued for emptying? */
343 	bool		queuedForEmptying;
344 
345 	/* is this a temporary copy, not in the hash table? */
346 	bool		isTemp;
347 
348 	int			level;			/* 0 == leaf */
349 } GISTNodeBuffer;
350 
351 /*
352  * Does specified level have buffers? (Beware of multiple evaluation of
353  * arguments.)
354  */
355 #define LEVEL_HAS_BUFFERS(nlevel, gfbb) \
356 	((nlevel) != 0 && (nlevel) % (gfbb)->levelStep == 0 && \
357 	 (nlevel) != (gfbb)->rootlevel)
358 
359 /* Is specified buffer at least half-filled (should be queued for emptying)? */
360 #define BUFFER_HALF_FILLED(nodeBuffer, gfbb) \
361 	((nodeBuffer)->blocksCount > (gfbb)->pagesPerBuffer / 2)
362 
363 /*
364  * Is specified buffer full? Our buffers can actually grow indefinitely,
365  * beyond the "maximum" size, so this just means whether the buffer has grown
366  * beyond the nominal maximum size.
367  */
368 #define BUFFER_OVERFLOWED(nodeBuffer, gfbb) \
369 	((nodeBuffer)->blocksCount > (gfbb)->pagesPerBuffer)
370 
371 /*
372  * Data structure with general information about build buffers.
373  */
374 typedef struct GISTBuildBuffers
375 {
376 	/* Persistent memory context for the buffers and metadata. */
377 	MemoryContext context;
378 
379 	BufFile    *pfile;			/* Temporary file to store buffers in */
380 	long		nFileBlocks;	/* Current size of the temporary file */
381 
382 	/*
383 	 * resizable array of free blocks.
384 	 */
385 	long	   *freeBlocks;
386 	int			nFreeBlocks;	/* # of currently free blocks in the array */
387 	int			freeBlocksLen;	/* current allocated length of the array */
388 
389 	/* Hash for buffers by block number */
390 	HTAB	   *nodeBuffersTab;
391 
392 	/* List of buffers scheduled for emptying */
393 	List	   *bufferEmptyingQueue;
394 
395 	/*
396 	 * Parameters to the buffering build algorithm. levelStep determines which
397 	 * levels in the tree have buffers, and pagesPerBuffer determines how
398 	 * large each buffer is.
399 	 */
400 	int			levelStep;
401 	int			pagesPerBuffer;
402 
403 	/* Array of lists of buffers on each level, for final emptying */
404 	List	  **buffersOnLevels;
405 	int			buffersOnLevelsLen;
406 
407 	/*
408 	 * Dynamically-sized array of buffers that currently have their last page
409 	 * loaded in main memory.
410 	 */
411 	GISTNodeBuffer **loadedBuffers;
412 	int			loadedBuffersCount;		/* # of entries in loadedBuffers */
413 	int			loadedBuffersLen;		/* allocated size of loadedBuffers */
414 
415 	/* Level of the current root node (= height of the index tree - 1) */
416 	int			rootlevel;
417 } GISTBuildBuffers;
418 
419 /*
420  * Storage type for GiST's reloptions
421  */
422 typedef struct GiSTOptions
423 {
424 	int32		vl_len_;		/* varlena header (do not touch directly!) */
425 	int			fillfactor;		/* page fill factor in percent (0..100) */
426 	int			bufferingModeOffset;	/* use buffering build? */
427 } GiSTOptions;
428 
429 /* gist.c */
430 extern Datum gisthandler(PG_FUNCTION_ARGS);
431 extern void gistbuildempty(Relation index);
432 extern bool gistinsert(Relation r, Datum *values, bool *isnull,
433 		   ItemPointer ht_ctid, Relation heapRel,
434 		   IndexUniqueCheck checkUnique);
435 extern MemoryContext createTempGistContext(void);
436 extern GISTSTATE *initGISTstate(Relation index);
437 extern void freeGISTstate(GISTSTATE *giststate);
438 extern void gistdoinsert(Relation r,
439 			 IndexTuple itup,
440 			 Size freespace,
441 			 GISTSTATE *GISTstate,
442 			 Relation heapRel);
443 
444 /* A List of these is returned from gistplacetopage() in *splitinfo */
445 typedef struct
446 {
447 	Buffer		buf;			/* the split page "half" */
448 	IndexTuple	downlink;		/* downlink for this half. */
449 } GISTPageSplitInfo;
450 
451 extern bool gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
452 				Buffer buffer,
453 				IndexTuple *itup, int ntup,
454 				OffsetNumber oldoffnum, BlockNumber *newblkno,
455 				Buffer leftchildbuf,
456 				List **splitinfo,
457 				bool markleftchild,
458 				Relation heapRel);
459 
460 extern SplitedPageLayout *gistSplit(Relation r, Page page, IndexTuple *itup,
461 		  int len, GISTSTATE *giststate);
462 
463 /* gistxlog.c */
464 extern void gist_redo(XLogReaderState *record);
465 extern void gist_desc(StringInfo buf, XLogReaderState *record);
466 extern const char *gist_identify(uint8 info);
467 extern void gist_xlog_startup(void);
468 extern void gist_xlog_cleanup(void);
469 
470 extern XLogRecPtr gistXLogUpdate(Buffer buffer,
471 			   OffsetNumber *todelete, int ntodelete,
472 			   IndexTuple *itup, int ntup,
473 			   Buffer leftchild, RelFileNode *hnode);
474 
475 extern XLogRecPtr gistXLogSplit(bool page_is_leaf,
476 			  SplitedPageLayout *dist,
477 			  BlockNumber origrlink, GistNSN oldnsn,
478 			  Buffer leftchild, bool markfollowright);
479 
480 /* gistget.c */
481 extern bool gistgettuple(IndexScanDesc scan, ScanDirection dir);
482 extern int64 gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
483 extern bool gistcanreturn(Relation index, int attno);
484 
485 /* gistvalidate.c */
486 extern bool gistvalidate(Oid opclassoid);
487 
488 /* gistutil.c */
489 
490 #define GiSTPageSize   \
491 	( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GISTPageOpaqueData)) )
492 
493 #define GIST_MIN_FILLFACTOR			10
494 #define GIST_DEFAULT_FILLFACTOR		90
495 
496 extern bytea *gistoptions(Datum reloptions, bool validate);
497 extern bool gistproperty(Oid index_oid, int attno,
498 			 IndexAMProperty prop, const char *propname,
499 			 bool *res, bool *isnull);
500 extern bool gistfitpage(IndexTuple *itvec, int len);
501 extern bool gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace);
502 extern void gistcheckpage(Relation rel, Buffer buf);
503 extern Buffer gistNewBuffer(Relation r);
504 extern void gistfillbuffer(Page page, IndexTuple *itup, int len,
505 			   OffsetNumber off);
506 extern IndexTuple *gistextractpage(Page page, int *len /* out */ );
507 extern IndexTuple *gistjoinvector(
508 			   IndexTuple *itvec, int *len,
509 			   IndexTuple *additvec, int addlen);
510 extern IndexTupleData *gistfillitupvec(IndexTuple *vec, int veclen, int *memlen);
511 
512 extern IndexTuple gistunion(Relation r, IndexTuple *itvec,
513 		  int len, GISTSTATE *giststate);
514 extern IndexTuple gistgetadjusted(Relation r,
515 				IndexTuple oldtup,
516 				IndexTuple addtup,
517 				GISTSTATE *giststate);
518 extern IndexTuple gistFormTuple(GISTSTATE *giststate,
519 			  Relation r, Datum *attdata, bool *isnull, bool isleaf);
520 
521 extern OffsetNumber gistchoose(Relation r, Page p,
522 		   IndexTuple it,
523 		   GISTSTATE *giststate);
524 
525 extern void GISTInitBuffer(Buffer b, uint32 f);
526 extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
527 			   Datum k, Relation r, Page pg, OffsetNumber o,
528 			   bool l, bool isNull);
529 
530 extern float gistpenalty(GISTSTATE *giststate, int attno,
531 			GISTENTRY *key1, bool isNull1,
532 			GISTENTRY *key2, bool isNull2);
533 extern void gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
534 				   Datum *attr, bool *isnull);
535 extern bool gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b);
536 extern void gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p,
537 				  OffsetNumber o, GISTENTRY *attdata, bool *isnull);
538 extern IndexTuple gistFetchTuple(GISTSTATE *giststate, Relation r,
539 			   IndexTuple tuple);
540 extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
541 				 GISTENTRY *entry1, bool isnull1,
542 				 GISTENTRY *entry2, bool isnull2,
543 				 Datum *dst, bool *dstisnull);
544 
545 extern XLogRecPtr gistGetFakeLSN(Relation rel);
546 
547 /* gistvacuum.c */
548 extern IndexBulkDeleteResult *gistbulkdelete(IndexVacuumInfo *info,
549 			   IndexBulkDeleteResult *stats,
550 			   IndexBulkDeleteCallback callback,
551 			   void *callback_state);
552 extern IndexBulkDeleteResult *gistvacuumcleanup(IndexVacuumInfo *info,
553 				  IndexBulkDeleteResult *stats);
554 
555 /* gistsplit.c */
556 extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup,
557 			   int len, GISTSTATE *giststate,
558 			   GistSplitVector *v,
559 			   int attno);
560 
561 /* gistbuild.c */
562 extern IndexBuildResult *gistbuild(Relation heap, Relation index,
563 		  struct IndexInfo *indexInfo);
564 extern void gistValidateBufferingOption(char *value);
565 
566 /* gistbuildbuffers.c */
567 extern GISTBuildBuffers *gistInitBuildBuffers(int pagesPerBuffer, int levelStep,
568 					 int maxLevel);
569 extern GISTNodeBuffer *gistGetNodeBuffer(GISTBuildBuffers *gfbb,
570 				  GISTSTATE *giststate,
571 				  BlockNumber blkno, int level);
572 extern void gistPushItupToNodeBuffer(GISTBuildBuffers *gfbb,
573 						 GISTNodeBuffer *nodeBuffer, IndexTuple item);
574 extern bool gistPopItupFromNodeBuffer(GISTBuildBuffers *gfbb,
575 						  GISTNodeBuffer *nodeBuffer, IndexTuple *item);
576 extern void gistFreeBuildBuffers(GISTBuildBuffers *gfbb);
577 extern void gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb,
578 								GISTSTATE *giststate, Relation r,
579 								int level, Buffer buffer,
580 								List *splitinfo);
581 extern void gistUnloadNodeBuffers(GISTBuildBuffers *gfbb);
582 
583 #endif   /* GIST_PRIVATE_H */
584