9.4 + gin_statpage. gin_statpage author is Alexander Korotkov <aekorotkov@gmail.com>
[gevel.git] / gevel.c
1 #include "postgres.h"
2
3 #include "access/genam.h"
4 #include "access/gin.h"
5 #if PG_VERSION_NUM >= 90400
6 #include "utils/snapmgr.h"
7 #endif
8 #if PG_VERSION_NUM >= 90100
9 #include "access/gin_private.h"
10 #endif
11 #include "access/gist.h"
12 #include "access/gist_private.h"
13 #include "access/gistscan.h"
14 #if PG_VERSION_NUM >= 90200
15 #include "access/spgist_private.h"
16 #include "access/spgist.h"
17 #include "utils/datum.h"
18 #endif
19 #include "access/heapam.h"
20 #include "catalog/index.h"
21 #include "miscadmin.h"
22 #include "storage/lmgr.h"
23 #include "catalog/namespace.h"
24 #if PG_VERSION_NUM >= 80300
25 #include <tsearch/ts_utils.h>
26 #endif
27 #include <utils/tqual.h>
28 #include "utils/builtins.h"
29 #include "utils/lsyscache.h"
30 #include "utils/datum.h"
31 #include "utils/fmgroids.h"
32 #include <fmgr.h>
33 #include <funcapi.h>
34 #include <access/heapam.h>
35 #include <catalog/pg_type.h>
36 #include <access/relscan.h>
37
38
39 #define PAGESIZE        (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData)))
40
41 #ifndef PG_NARGS
42 #define PG_NARGS() (fcinfo->nargs)
43 #endif
44
45 static char
46 *t2c(text* in) {
47         char *out=palloc( VARSIZE(in) );
48         memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ);
49         out[ VARSIZE(in)-VARHDRSZ ] ='\0';
50         return out;
51 }
52
53 typedef struct {
54         int maxlevel;
55         text    *txt;
56         char    *ptr;
57         int     len;    
58 } IdxInfo;
59
60 static Relation checkOpenedRelation(Relation r, Oid PgAmOid);
61
62 #ifdef PG_MODULE_MAGIC
63 /* >= 8.2 */ 
64
65 PG_MODULE_MAGIC;
66
67 static Relation
68 gist_index_open(RangeVar *relvar) {
69 #if PG_VERSION_NUM < 90200 
70         Oid relOid = RangeVarGetRelid(relvar, false);
71 #else
72         Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
73 #endif
74         return checkOpenedRelation(
75                                 index_open(relOid, AccessExclusiveLock), GIST_AM_OID);
76 }
77
78 #define gist_index_close(r)     index_close((r), AccessExclusiveLock)
79
80 static Relation
81 gin_index_open(RangeVar *relvar) {
82 #if PG_VERSION_NUM < 90200 
83         Oid relOid = RangeVarGetRelid(relvar, false);
84 #else
85         Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
86 #endif
87         return checkOpenedRelation(
88                                 index_open(relOid, AccessShareLock), GIN_AM_OID);
89 }
90
91 #define gin_index_close(r) index_close((r), AccessShareLock)
92
93 #else /* <8.2 */
94
95 static Relation
96 gist_index_open(RangeVar *relvar) {
97         Relation rel = index_openrv(relvar);
98
99         LockRelation(rel, AccessExclusiveLock);
100         return checkOpenedRelation(rel, GIST_AM_OID);
101 }
102
103 static void
104 gist_index_close(Relation rel) {
105         UnlockRelation(rel, AccessExclusiveLock);
106         index_close(rel);
107 }
108
109 static Relation
110 gin_index_open(RangeVar *relvar) {
111         Relation rel = index_openrv(relvar);
112
113         LockRelation(rel, AccessShareLock);
114         return checkOpenedRelation(rel, GIN_AM_OID);
115 }
116
117 static void
118 gin_index_close(Relation rel) {
119         UnlockRelation(rel, AccessShareLock);
120         index_close(rel);
121 }
122
123 #endif
124
125 #if PG_VERSION_NUM >= 80300
126 #define stringToQualifiedNameList(x,y)  stringToQualifiedNameList(x)
127 #endif
128
129 #if PG_VERSION_NUM < 80300
130 #define SET_VARSIZE(p,l)        VARATT_SIZEP(p)=(l)
131 #endif
132
133 static Relation 
134 checkOpenedRelation(Relation r, Oid PgAmOid) {
135         if ( r->rd_am == NULL )
136                 elog(ERROR, "Relation %s.%s is not an index",
137                                         get_namespace_name(RelationGetNamespace(r)),
138                                         RelationGetRelationName(r)
139                         );
140
141         if ( r->rd_rel->relam != PgAmOid )
142                 elog(ERROR, "Index %s.%s has wrong type",
143                                         get_namespace_name(RelationGetNamespace(r)),
144                                         RelationGetRelationName(r)
145                         );
146         
147         return r;
148 }
149
150 static void
151 gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo *info) {
152         Buffer          buffer;
153         Page            page;
154         IndexTuple      which;
155         ItemId          iid;
156         OffsetNumber i,
157                                 maxoff;
158         BlockNumber cblk;
159         char       *pred;
160
161         pred = (char *) palloc(sizeof(char) * level * 4 + 1);
162         MemSet(pred, ' ', level*4);
163         pred[level*4] = '\0';
164
165         buffer = ReadBuffer(r, blk);
166         page = (Page) BufferGetPage(buffer);
167
168         maxoff = PageGetMaxOffsetNumber(page);
169
170
171         while ( (info->ptr-((char*)info->txt)) + level*4 + 128 >= info->len ) {
172                 int dist=info->ptr-((char*)info->txt);
173                 info->len *= 2;
174                 info->txt=(text*)repalloc(info->txt, info->len);
175                 info->ptr = ((char*)info->txt)+dist;
176         }
177
178         sprintf(info->ptr, "%s%d(l:%d) blk: %u numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n", 
179                 pred,
180                 coff, 
181                 level, 
182                 blk,
183                 (int) maxoff, 
184                 (int) PageGetFreeSpace(page),  
185                 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE),
186                 GistPageGetOpaque(page)->rightlink,
187                 ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" );
188         info->ptr=strchr(info->ptr,'\0');
189
190         if (!GistPageIsLeaf(page) && ( info->maxlevel<0 || level<info->maxlevel ) )
191                 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
192                         iid = PageGetItemId(page, i);
193                         which = (IndexTuple) PageGetItem(page, iid);
194                         cblk = ItemPointerGetBlockNumber(&(which->t_tid));
195                         gist_dumptree(r, level + 1, cblk, i, info);
196                 }
197         ReleaseBuffer(buffer);
198         pfree(pred);
199 }
200
201 PG_FUNCTION_INFO_V1(gist_tree);
202 Datum   gist_tree(PG_FUNCTION_ARGS);
203 Datum
204 gist_tree(PG_FUNCTION_ARGS) {
205         text    *name=PG_GETARG_TEXT_P(0);
206         char *relname=t2c(name);
207         RangeVar   *relvar;
208         Relation        index;
209         List       *relname_list;
210         IdxInfo info;
211
212         relname_list = stringToQualifiedNameList(relname, "gist_tree");
213         relvar = makeRangeVarFromNameList(relname_list);
214         index = gist_index_open(relvar);
215         PG_FREE_IF_COPY(name,0);
216
217         info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1; 
218         info.len=1024;
219         info.txt=(text*)palloc( info.len );
220         info.ptr=((char*)info.txt)+VARHDRSZ;
221
222         gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0, &info);
223
224         gist_index_close(index);
225         pfree(relname);
226
227         SET_VARSIZE(info.txt, info.ptr-((char*)info.txt));
228         PG_RETURN_POINTER(info.txt);
229 }
230
231 typedef struct {
232         int     level;
233         int     numpages;
234         int     numleafpages;
235         int     numtuple;
236         int     numinvalidtuple;
237         int     numleaftuple;
238         uint64  tuplesize;
239         uint64  leaftuplesize;
240         uint64  totalsize;
241 } IdxStat;
242
243 static void
244 gist_stattree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxStat *info) {
245         Buffer          buffer;
246         Page            page;
247         IndexTuple      which;
248         ItemId          iid;
249         OffsetNumber i,
250                                 maxoff;
251         BlockNumber cblk;
252         char       *pred;
253
254         pred = (char *) palloc(sizeof(char) * level * 4 + 1);
255         MemSet(pred, ' ', level*4);
256         pred[level*4] = '\0';
257
258         buffer = ReadBuffer(r, blk);
259         page = (Page) BufferGetPage(buffer);
260
261         maxoff = PageGetMaxOffsetNumber(page);
262
263         info->numpages++;
264         info->tuplesize+=PAGESIZE-PageGetFreeSpace(page);
265         info->totalsize+=BLCKSZ;
266         info->numtuple+=maxoff;
267         if ( info->level < level )
268                 info->level = level;
269
270         if (GistPageIsLeaf(page)) {
271                 info->numleafpages++;
272                 info->leaftuplesize+=PAGESIZE-PageGetFreeSpace(page);
273                 info->numleaftuple+=maxoff;
274         } else {
275                 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
276                         iid = PageGetItemId(page, i);
277                         which = (IndexTuple) PageGetItem(page, iid);
278                         if ( GistTupleIsInvalid(which) )
279                                 info->numinvalidtuple++;
280                         cblk = ItemPointerGetBlockNumber(&(which->t_tid));
281                                 gist_stattree(r, level + 1, cblk, i, info);
282                 }
283         }
284
285         ReleaseBuffer(buffer);
286         pfree(pred);
287 }
288
289 PG_FUNCTION_INFO_V1(gist_stat);
290 Datum   gist_stat(PG_FUNCTION_ARGS);
291 Datum
292 gist_stat(PG_FUNCTION_ARGS) {
293         text    *name=PG_GETARG_TEXT_P(0);
294         char *relname=t2c(name);
295         RangeVar   *relvar;
296         Relation        index;
297         List       *relname_list;
298         IdxStat info;
299         text *out=(text*)palloc(1024);
300         char *ptr=((char*)out)+VARHDRSZ;
301
302
303         relname_list = stringToQualifiedNameList(relname, "gist_tree");
304         relvar = makeRangeVarFromNameList(relname_list);
305         index = gist_index_open(relvar);
306         PG_FREE_IF_COPY(name,0);
307
308         memset(&info, 0, sizeof(IdxStat));
309
310         gist_stattree(index, 0, GIST_ROOT_BLKNO, 0, &info);
311
312         gist_index_close(index);
313         pfree(relname);
314
315         sprintf(ptr, 
316                 "Number of levels:          %d\n"
317                 "Number of pages:           %d\n"
318                 "Number of leaf pages:      %d\n"
319                 "Number of tuples:          %d\n"
320                 "Number of invalid tuples:  %d\n"
321                 "Number of leaf tuples:     %d\n"
322                 "Total size of tuples:      "INT64_FORMAT" bytes\n"
323                 "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
324                 "Total size of index:       "INT64_FORMAT" bytes\n",
325                 info.level+1,
326                 info.numpages,
327                 info.numleafpages,
328                 info.numtuple,
329                 info.numinvalidtuple,
330                 info.numleaftuple,
331                 info.tuplesize,
332                 info.leaftuplesize,
333                 info.totalsize);
334
335         ptr=strchr(ptr,'\0');
336                  
337         SET_VARSIZE(out, ptr-((char*)out));
338         PG_RETURN_POINTER(out);
339 }
340
341 typedef struct GPItem {
342         Buffer  buffer;
343         Page    page;
344         OffsetNumber    offset;
345         int     level;
346         struct GPItem *next;
347 } GPItem;
348
349 typedef struct {
350         List    *relname_list;
351         RangeVar   *relvar;
352         Relation        index;
353         Datum   *dvalues;
354         char    *nulls;
355         GPItem  *item;
356 } TypeStorage;
357
358 static GPItem*
359 openGPPage( FuncCallContext *funcctx, BlockNumber blk ) {
360         GPItem  *nitem;
361         MemoryContext     oldcontext;
362         Relation index = ( (TypeStorage*)(funcctx->user_fctx) )->index;
363         
364         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
365         nitem = (GPItem*)palloc( sizeof(GPItem) );
366         memset(nitem,0,sizeof(GPItem));
367
368         nitem->buffer = ReadBuffer(index, blk);
369         nitem->page = (Page) BufferGetPage(nitem->buffer);
370         nitem->offset=FirstOffsetNumber;
371         nitem->next = ( (TypeStorage*)(funcctx->user_fctx) )->item;
372         nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1; 
373         ( (TypeStorage*)(funcctx->user_fctx) )->item = nitem;
374
375         MemoryContextSwitchTo(oldcontext);
376         return nitem;
377
378
379 static GPItem*
380 closeGPPage( FuncCallContext *funcctx ) {
381         GPItem  *oitem = ( (TypeStorage*)(funcctx->user_fctx) )->item;
382
383         ( (TypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
384         
385         ReleaseBuffer(oitem->buffer);
386         pfree( oitem );
387         return ( (TypeStorage*)(funcctx->user_fctx) )->item; 
388 }
389
390 static void
391 setup_firstcall(FuncCallContext  *funcctx, text *name) {
392         MemoryContext     oldcontext;
393         TypeStorage     *st;
394         char *relname=t2c(name);
395         TupleDesc            tupdesc;
396         char            attname[NAMEDATALEN];
397         int i;
398
399         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
400
401         st=(TypeStorage*)palloc( sizeof(TypeStorage) );
402         memset(st,0,sizeof(TypeStorage));
403         st->relname_list = stringToQualifiedNameList(relname, "gist_tree");
404         st->relvar = makeRangeVarFromNameList(st->relname_list);
405         st->index = gist_index_open(st->relvar);
406         funcctx->user_fctx = (void*)st;
407
408         tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2, false);
409         TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
410         TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
411         for (i = 0; i < st->index->rd_att->natts; i++) {
412                 sprintf(attname, "z%d", i+2);
413                 TupleDescInitEntry(
414                         tupdesc,
415                         i+3,
416                         attname,
417                         st->index->rd_att->attrs[i]->atttypid,
418                         st->index->rd_att->attrs[i]->atttypmod,
419                         st->index->rd_att->attrs[i]->attndims
420                 );
421         }
422
423         st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
424         st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(char));
425
426         funcctx->slot = TupleDescGetSlot(tupdesc);
427         funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
428
429         MemoryContextSwitchTo(oldcontext);
430         pfree(relname);
431
432         st->item=openGPPage(funcctx, GIST_ROOT_BLKNO);
433
434
435 static void 
436 close_call( FuncCallContext  *funcctx ) {
437         TypeStorage *st = (TypeStorage*)(funcctx->user_fctx);
438         
439         while( st->item && closeGPPage(funcctx) );
440         
441         pfree( st->dvalues );
442         pfree( st->nulls );
443
444         gist_index_close(st->index);
445 }
446
447 PG_FUNCTION_INFO_V1(gist_print);
448 Datum   gist_print(PG_FUNCTION_ARGS);
449 Datum
450 gist_print(PG_FUNCTION_ARGS) {
451         FuncCallContext  *funcctx;
452         TypeStorage     *st;
453         Datum result=(Datum)0;
454         ItemId          iid;
455         IndexTuple      ituple;
456         HeapTuple       htuple;
457         int i;
458         bool isnull;
459
460         if (SRF_IS_FIRSTCALL()) {
461                 text    *name=PG_GETARG_TEXT_P(0);
462                 funcctx = SRF_FIRSTCALL_INIT();
463                 setup_firstcall(funcctx, name);
464                 PG_FREE_IF_COPY(name,0);
465         }
466
467         funcctx = SRF_PERCALL_SETUP();  
468         st = (TypeStorage*)(funcctx->user_fctx);
469
470         if ( !st->item ) {
471                 close_call(funcctx);
472                 SRF_RETURN_DONE(funcctx);
473         }
474
475         while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) ) {
476                 if ( ! closeGPPage(funcctx) ) {
477                         close_call(funcctx);
478                         SRF_RETURN_DONE(funcctx);
479                 } 
480         }
481
482         iid = PageGetItemId( st->item->page, st->item->offset );
483         ituple = (IndexTuple) PageGetItem(st->item->page, iid);
484
485         st->dvalues[0] = Int32GetDatum( st->item->level );
486         st->nulls[0] = ' ';
487         st->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true );
488         st->nulls[1] = ' ';
489         for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++) {
490                 if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) ) {
491                         st->dvalues[i] = (Datum)0;
492                         st->nulls[i] = 'n';
493                 } else {
494                         st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull); 
495                         st->nulls[i] = ( isnull ) ? 'n' : ' ';
496                 }
497         }
498
499         htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
500         result = TupleGetDatum(funcctx->slot, htuple);
501         st->item->offset = OffsetNumberNext(st->item->offset);
502         if ( !GistPageIsLeaf(st->item->page) )
503                 openGPPage(funcctx, ItemPointerGetBlockNumber(&(ituple->t_tid)) );
504
505         SRF_RETURN_NEXT(funcctx, result);
506 }
507
508 typedef struct GinStatState {
509         Relation                index;
510         GinState                ginstate;
511         OffsetNumber    attnum;
512
513         Buffer                  buffer;
514         OffsetNumber    offset;
515         Datum                   curval;
516 #if PG_VERSION_NUM >= 90100
517         GinNullCategory category;
518 #endif
519         Datum                   dvalues[2];
520         char                    nulls[2];
521 } GinStatState;
522
523 static bool
524 moveRightIfItNeeded( GinStatState *st )
525 {
526         Page page = BufferGetPage(st->buffer);
527
528         if ( st->offset > PageGetMaxOffsetNumber(page) ) {
529                 /*
530                 * We scaned the whole page, so we should take right page
531                 */
532                 BlockNumber blkno = GinPageGetOpaque(page)->rightlink;               
533
534                 if ( GinPageRightMost(page) )
535                         return false;  /* no more page */
536
537                 LockBuffer(st->buffer, GIN_UNLOCK);
538                 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
539                 LockBuffer(st->buffer, GIN_SHARE);
540                 st->offset = FirstOffsetNumber;
541         }
542
543         return true;
544 }
545
546 /*
547  * Refinds a previois position, at returns it has correctly 
548  * set offset and buffer is locked
549  */
550 static bool
551 refindPosition(GinStatState *st)
552 {
553         Page    page;        
554
555         /* find left if needed (it causes only for first search) */
556         for (;;) {
557                 IndexTuple  itup;
558                 BlockNumber blkno;
559
560                 LockBuffer(st->buffer, GIN_SHARE);
561
562                 page = BufferGetPage(st->buffer);
563                 if (GinPageIsLeaf(page))
564                         break;
565
566                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
567                 blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid);
568
569                 LockBuffer(st->buffer,GIN_UNLOCK);
570                 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
571         }
572
573         if (st->offset == InvalidOffsetNumber) {
574                 return (PageGetMaxOffsetNumber(page) >= FirstOffsetNumber ) ? true : false; /* first one */
575         }
576
577         for(;;) {
578                 int                     cmp;
579 #if PG_VERSION_NUM >= 90100
580                 GinNullCategory category;
581 #elif PG_VERSION_NUM < 80400
582                 bool                    isnull = false;
583 #endif
584                 Datum                   datum;
585                 IndexTuple              itup;
586
587                 if (moveRightIfItNeeded(st)==false)
588                         return false;
589
590                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
591 #if PG_VERSION_NUM >= 90100
592                 datum = gintuple_get_key(&st->ginstate, itup, &category); 
593                 cmp = ginCompareAttEntries(&st->ginstate,
594                                                                         st->attnum + 1, st->curval, st->category,
595                                                                         gintuple_get_attrnum(&st->ginstate, itup), datum, category);
596 #elif PG_VERSION_NUM >= 80400
597                 datum = gin_index_getattr(&st->ginstate, itup);
598
599                 cmp = compareAttEntries(&st->ginstate,
600                                                                         st->attnum + 1, st->curval,
601                                                                         gintuple_get_attrnum(&st->ginstate, itup), datum);
602 #else
603                 datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull);
604
605                 cmp = DatumGetInt32(
606                                 FunctionCall2(
607                                                 &st->ginstate.compareFn,
608                                                 st->curval,
609                                                 datum
610                                         ));
611 #endif
612                 if ( cmp == 0 )
613                 {
614                         if ( !st->index->rd_att->attrs[st->attnum]->attbyval )
615                                 pfree( (void*) st->curval );
616                         return true;
617                 }
618
619                 st->offset++;
620         }
621
622         return false;
623 }
624
625 static void
626 gin_setup_firstcall(FuncCallContext  *funcctx, text *name, int attnum) {
627         MemoryContext     oldcontext;
628         GinStatState     *st;
629         char *relname=t2c(name);
630         TupleDesc            tupdesc;
631
632         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
633
634         st=(GinStatState*)palloc( sizeof(GinStatState) );
635         memset(st,0,sizeof(GinStatState));
636         st->index = gin_index_open(
637                  makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_stat")));
638         initGinState( &st->ginstate, st->index );
639
640 #if PG_VERSION_NUM >= 80400
641         if (attnum < 0 || attnum >= st->index->rd_att->natts)
642                 elog(ERROR,"Wrong column's number");
643         st->attnum = attnum;
644 #else
645         st->attnum = 0;
646 #endif
647
648         funcctx->user_fctx = (void*)st;
649
650         tupdesc = CreateTemplateTupleDesc(2, false);
651         TupleDescInitEntry(tupdesc, 1, "value", 
652                         st->index->rd_att->attrs[st->attnum]->atttypid, 
653                         st->index->rd_att->attrs[st->attnum]->atttypmod,
654                         st->index->rd_att->attrs[st->attnum]->attndims);
655         TupleDescInitEntry(tupdesc, 2, "nrow", INT4OID, -1, 0);
656
657         memset( st->nulls, ' ', 2*sizeof(char) );
658
659         funcctx->slot = TupleDescGetSlot(tupdesc);
660         funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
661
662         MemoryContextSwitchTo(oldcontext);
663         pfree(relname);
664
665         st->offset = InvalidOffsetNumber;
666         st->buffer = ReadBuffer( st->index, GIN_ROOT_BLKNO );
667 }
668
669 static void
670 processTuple( FuncCallContext  *funcctx,  GinStatState *st, IndexTuple itup ) {
671         MemoryContext           oldcontext;
672 #if PG_VERSION_NUM < 80400
673         bool                            isnull;
674 #endif
675
676         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
677         st->curval = datumCopy(
678 #if PG_VERSION_NUM >= 90100
679                                         gintuple_get_key(&st->ginstate, itup, &st->category),
680 #elif PG_VERSION_NUM >= 80400
681                                         gin_index_getattr(&st->ginstate, itup),
682 #else
683                                         index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull),
684 #endif
685                                         st->index->rd_att->attrs[st->attnum]->attbyval,
686                                         st->index->rd_att->attrs[st->attnum]->attlen );
687         MemoryContextSwitchTo(oldcontext);
688
689         st->dvalues[0] = st->curval;
690 #if PG_VERSION_NUM >= 90100
691         /* do no distiguish various null category */
692         st->nulls[0] = (st->category == GIN_CAT_NORM_KEY) ? ' ' : 'n';
693 #endif
694                 
695         if ( GinIsPostingTree(itup) ) {
696                 BlockNumber     rootblkno = GinGetPostingTree(itup);
697 #if PG_VERSION_NUM >= 90400
698                 GinBtreeData    btree;
699                 GinBtreeStack   *stack;
700                 ItemPointerData minItem;
701                 int                             nlist;
702                 ItemPointer             list;
703 #else
704                 GinPostingTreeScan *gdi;
705                 Buffer          entrybuffer;              
706 #endif
707                 Page        page;
708                 uint32          predictNumber;
709
710                 LockBuffer(st->buffer, GIN_UNLOCK);
711 #if PG_VERSION_NUM >= 90400
712                 stack = ginScanBeginPostingTree(&btree, st->index, rootblkno);
713                 page = BufferGetPage(stack->buffer);
714                 ItemPointerSetMin(&minItem);
715                 list = GinDataLeafPageGetItems(page, &nlist, minItem);
716                 pfree(list);
717                 predictNumber = stack->predictNumber;
718 #elif PG_VERSION_NUM >= 90100
719                 gdi = ginPrepareScanPostingTree(st->index, rootblkno, TRUE);
720                 entrybuffer = ginScanBeginPostingTree(gdi);
721                 page = BufferGetPage(entrybuffer);
722                 predictNumber = gdi->stack->predictNumber;
723 #else
724                 gdi = prepareScanPostingTree(st->index, rootblkno, TRUE);
725                 entrybuffer = scanBeginPostingTree(gdi);
726                 page = BufferGetPage(entrybuffer);
727                 predictNumber = gdi->stack->predictNumber;
728 #endif
729
730                 st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
731
732 #if PG_VERSION_NUM < 90400
733                 LockBuffer(entrybuffer, GIN_UNLOCK);
734                 freeGinBtreeStack(gdi->stack);
735                 pfree(gdi);
736 #else
737                 LockBuffer(stack->buffer, GIN_UNLOCK);
738                 freeGinBtreeStack(stack);
739 #endif
740         } else {
741                 st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) );
742                 LockBuffer(st->buffer, GIN_UNLOCK);
743         }
744 }
745
746 PG_FUNCTION_INFO_V1(gin_stat);
747 Datum   gin_stat(PG_FUNCTION_ARGS);
748 Datum
749 gin_stat(PG_FUNCTION_ARGS) {
750         FuncCallContext  *funcctx;
751         GinStatState     *st;
752         Datum result=(Datum)0;
753         IndexTuple      ituple;
754         HeapTuple       htuple;
755         Page page;
756
757         if (SRF_IS_FIRSTCALL()) {
758                 text    *name=PG_GETARG_TEXT_P(0);
759                 funcctx = SRF_FIRSTCALL_INIT();
760                 gin_setup_firstcall(funcctx, name, (PG_NARGS()==2) ? PG_GETARG_INT32(1) : 0 );
761                 PG_FREE_IF_COPY(name,0);
762         }
763
764         funcctx = SRF_PERCALL_SETUP();  
765         st = (GinStatState*)(funcctx->user_fctx);
766
767         if ( refindPosition(st) == false ) {
768                 UnlockReleaseBuffer( st->buffer );
769                 gin_index_close(st->index);
770
771                 SRF_RETURN_DONE(funcctx);
772         }
773
774         for(;;) {
775                 st->offset++;
776         
777                 if (moveRightIfItNeeded(st)==false) { 
778                         UnlockReleaseBuffer( st->buffer );
779                         gin_index_close(st->index);
780
781                         SRF_RETURN_DONE(funcctx);
782                 }
783
784                 page = BufferGetPage(st->buffer);
785                 ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset)); 
786
787 #if PG_VERSION_NUM >= 80400
788                 if (st->attnum + 1 == gintuple_get_attrnum(&st->ginstate, ituple))
789 #endif
790                         break;
791         }
792
793         processTuple( funcctx,  st, ituple );
794         
795         htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
796         result = TupleGetDatum(funcctx->slot, htuple);
797
798         SRF_RETURN_NEXT(funcctx, result);
799 }
800
801 PG_FUNCTION_INFO_V1(gin_count_estimate);
802 Datum gin_count_estimate(PG_FUNCTION_ARGS);
803 #if PG_VERSION_NUM >= 80300
804 Datum
805 gin_count_estimate(PG_FUNCTION_ARGS) {
806         text                    *name=PG_GETARG_TEXT_P(0);
807         Relation                index;
808         IndexScanDesc   scan;
809         int64                   count = 0;
810         char                    *relname=t2c(name);
811         ScanKeyData             key;
812 #if PG_VERSION_NUM >= 80400
813         TIDBitmap               *bitmap = tbm_create(work_mem * 1024L);
814 #else
815 #define MAXTIDS         1024
816         ItemPointerData tids[MAXTIDS];
817         int32                   returned_tids;
818         bool                    more;
819 #endif
820
821         index = gin_index_open(
822                  makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_count_estimate")));
823
824         if ( index->rd_opcintype[0] != TSVECTOROID ) {
825                 gin_index_close(index);
826                 elog(ERROR, "Column type is not a tsvector");
827         }
828
829         key.sk_flags    = 0;
830         key.sk_attno    = 1;
831         key.sk_strategy = TSearchStrategyNumber;
832         key.sk_subtype  = 0;
833         key.sk_argument = PG_GETARG_DATUM(1);
834
835         fmgr_info( F_TS_MATCH_VQ , &key.sk_func );
836
837 #if PG_VERSION_NUM >= 90100
838 #if PG_VERSION_NUM >= 90400
839         scan = index_beginscan_bitmap(index, SnapshotSelf, 1);
840 #else
841         scan = index_beginscan_bitmap(index, SnapshotNow, 1);
842 #endif
843         index_rescan(scan, &key, 1, NULL, 0);
844
845         count = index_getbitmap(scan, bitmap);
846         tbm_free(bitmap);
847 #elif PG_VERSION_NUM >= 80400
848         scan = index_beginscan_bitmap(index, SnapshotNow, 1, &key);
849
850         count = index_getbitmap(scan, bitmap);
851         tbm_free(bitmap);
852 #else
853         scan = index_beginscan_multi(index, SnapshotNow, 1, &key);
854
855         do {
856                 more = index_getmulti(scan, tids, MAXTIDS, &returned_tids);
857                 count += returned_tids;
858         } while(more);
859 #endif
860
861         index_endscan( scan );
862         gin_index_close(index);
863
864         PG_RETURN_INT64(count);
865 }
866 #else
867 Datum
868 gin_count_estimate(PG_FUNCTION_ARGS) {
869         elog(NOTICE, "Function is not working under PgSQL < 8.3");
870
871         PG_RETURN_INT64(0);
872 }
873 #endif
874
875 PG_FUNCTION_INFO_V1(spgist_stat);
876 Datum spgist_stat(PG_FUNCTION_ARGS);
877 Datum
878 spgist_stat(PG_FUNCTION_ARGS)
879 {
880 #if PG_VERSION_NUM < 90200
881         elog(NOTICE, "Function is not working under PgSQL < 9.2");
882
883         PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
884 #else
885         text       *name = PG_GETARG_TEXT_P(0);
886         RangeVar   *relvar;
887         Relation        index;
888         BlockNumber blkno;
889         BlockNumber totalPages = 0,
890                                 innerPages = 0,
891                                 leafPages = 0,
892                                 emptyPages = 0,
893                                 deletedPages = 0;
894         double    usedSpace = 0.0;
895         char            res[1024];
896         int              bufferSize = -1;
897         int64      innerTuples = 0,
898                                 leafTuples = 0,
899                                 nAllTheSame = 0,
900                                 nLeafPlaceholder = 0,
901                                 nInnerPlaceholder = 0,
902                                 nLeafRedirect = 0,
903                                 nInnerRedirect = 0;
904
905 #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
906 #define IS_SPGIST(r) ((r)->rd_rel->relam == SPGIST_AM_OID)
907
908         relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
909         index = relation_openrv(relvar, AccessExclusiveLock);
910
911         if (!IS_INDEX(index) || !IS_SPGIST(index))
912                 elog(ERROR, "relation \"%s\" is not an SPGiST index",
913                          RelationGetRelationName(index));
914
915         totalPages = RelationGetNumberOfBlocks(index);
916
917         for (blkno = SPGIST_ROOT_BLKNO; blkno < totalPages; blkno++)
918         {
919                 Buffer    buffer;
920                 Page            page;
921                 int              pageFree;
922
923                 buffer = ReadBuffer(index, blkno);
924                 LockBuffer(buffer, BUFFER_LOCK_SHARE);
925
926                 page = BufferGetPage(buffer);
927
928                 if (PageIsNew(page) || SpGistPageIsDeleted(page))
929                 {
930                         deletedPages++;
931                         UnlockReleaseBuffer(buffer);
932                         continue;
933                 }
934
935                 if (SpGistPageIsLeaf(page))
936                 {
937                         leafPages++;
938                         leafTuples += PageGetMaxOffsetNumber(page);
939                         nLeafPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
940                         nLeafRedirect += SpGistPageGetOpaque(page)->nRedirection;
941                 }
942                 else
943                 {
944                         int      i,
945                                         max;
946
947                         innerPages++;
948                         max = PageGetMaxOffsetNumber(page);
949                         innerTuples += max;
950                         nInnerPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
951                         nInnerRedirect += SpGistPageGetOpaque(page)->nRedirection;
952                         for (i = FirstOffsetNumber; i <= max; i++)
953                         {
954                                 SpGistInnerTuple it;
955
956                                 it = (SpGistInnerTuple) PageGetItem(page,
957                                                                                                         PageGetItemId(page, i));
958                                 if (it->allTheSame)
959                                         nAllTheSame++;
960                         }
961                 }
962
963                 if (bufferSize < 0)
964                         bufferSize = BufferGetPageSize(buffer)
965                                 - MAXALIGN(sizeof(SpGistPageOpaqueData))
966                                 - SizeOfPageHeaderData;
967
968                 pageFree = PageGetExactFreeSpace(page);
969
970                 usedSpace += bufferSize - pageFree;
971
972                 if (pageFree == bufferSize)
973                         emptyPages++;
974
975                 UnlockReleaseBuffer(buffer);
976         }
977
978         index_close(index, AccessExclusiveLock);
979
980         totalPages--;                      /* discount metapage */
981
982         snprintf(res, sizeof(res),
983                          "totalPages:        %u\n"
984                          "deletedPages:      %u\n"
985                          "innerPages:        %u\n"
986                          "leafPages:         %u\n"
987                          "emptyPages:        %u\n"
988                          "usedSpace:         %.2f kbytes\n"
989                          "freeSpace:         %.2f kbytes\n"
990                          "fillRatio:         %.2f%%\n"
991                          "leafTuples:        " INT64_FORMAT "\n"
992                          "innerTuples:       " INT64_FORMAT "\n"
993                          "innerAllTheSame:   " INT64_FORMAT "\n"
994                          "leafPlaceholders:  " INT64_FORMAT "\n"
995                          "innerPlaceholders: " INT64_FORMAT "\n"
996                          "leafRedirects:     " INT64_FORMAT "\n"
997                          "innerRedirects:    " INT64_FORMAT,
998                          totalPages, deletedPages, innerPages, leafPages, emptyPages,
999                          usedSpace / 1024.0,
1000                          (((double) bufferSize) * ((double) totalPages) - usedSpace) / 1024,
1001                          100.0 * (usedSpace / (((double) bufferSize) * ((double) totalPages))),
1002                          leafTuples, innerTuples, nAllTheSame,
1003                          nLeafPlaceholder, nInnerPlaceholder,
1004                          nLeafRedirect, nInnerRedirect);
1005
1006         PG_RETURN_TEXT_P(CStringGetTextDatum(res));
1007 #endif
1008 }
1009
1010 #if PG_VERSION_NUM >= 90200
1011
1012 typedef struct SPGistPrintStackElem {
1013         ItemPointerData         iptr;
1014         int16                           nlabel;
1015         int                                     level;
1016 } SPGistPrintStackElem;
1017
1018 typedef struct SPGistPrint {
1019         SpGistState     state;
1020         Relation        index;
1021         Datum           dvalues[7 /* see CreateTemplateTupleDesc call */];
1022         char            nulls[7 /* see CreateTemplateTupleDesc call */];
1023         List            *stack;
1024 } SPGistPrint;
1025
1026 static void
1027 pushSPGistPrint(FuncCallContext *funcctx, SPGistPrint *prst, ItemPointer ip, int level) {
1028         MemoryContext   oldcontext;
1029         SPGistPrintStackElem    *e;
1030
1031         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1032
1033         e = palloc(sizeof(*e));
1034         e->iptr = *ip;
1035         e->nlabel = 0;
1036         e->level = level;
1037         prst->stack = lcons(e, prst->stack); 
1038
1039         MemoryContextSwitchTo(oldcontext);
1040 }
1041
1042 static void
1043 close_spgist_print(SPGistPrint *prst) {
1044         index_close(prst->index, AccessExclusiveLock);
1045 }
1046 #endif
1047
1048 PG_FUNCTION_INFO_V1(spgist_print);
1049 Datum spgist_print(PG_FUNCTION_ARGS);
1050 Datum
1051 spgist_print(PG_FUNCTION_ARGS)
1052 {
1053 #if PG_VERSION_NUM < 90200
1054         elog(NOTICE, "Function is not working under PgSQL < 9.2");
1055
1056         PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1057 #else
1058         FuncCallContext                 *funcctx;
1059         SPGistPrint                             *prst;
1060         SPGistPrintStackElem    *s;
1061         HeapTuple                               htuple;
1062         Datum                                   result;
1063         MemoryContext                   oldcontext;
1064
1065         if (SRF_IS_FIRSTCALL()) {
1066                 text                    *name=PG_GETARG_TEXT_P(0);
1067                 RangeVar                *relvar;
1068                 Relation                index;
1069                 ItemPointerData ipd;
1070                 TupleDesc               tupdesc;
1071
1072                 funcctx = SRF_FIRSTCALL_INIT();
1073                 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1074                 index = relation_openrv(relvar, AccessExclusiveLock);
1075
1076                 if (!IS_INDEX(index) || !IS_SPGIST(index))
1077                         elog(ERROR, "relation \"%s\" is not an SPGiST index",
1078                                  RelationGetRelationName(index));
1079
1080                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1081
1082                 prst = palloc(sizeof(*prst));
1083
1084                 prst->index = index;
1085                 initSpGistState(&prst->state, index);
1086
1087                 tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ +  2 /* tids */, false);
1088                 TupleDescInitEntry(tupdesc, 1, "tid", TIDOID, -1, 0);
1089                 TupleDescInitEntry(tupdesc, 2, "node", INT4OID, -1, 0);
1090                 TupleDescInitEntry(tupdesc, 3, "level", INT4OID, -1, 0);
1091                 TupleDescInitEntry(tupdesc, 4, "tid_pointer", TIDOID, -1, 0);
1092                 TupleDescInitEntry(tupdesc, 5, "prefix", 
1093                                 (prst->state.attPrefixType.type == VOIDOID) ? INT4OID : prst->state.attPrefixType.type, -1, 0);
1094                 TupleDescInitEntry(tupdesc, 6, "label", 
1095                                 (prst->state.attLabelType.type == VOIDOID) ? INT4OID : prst->state.attLabelType.type, -1, 0);
1096                 TupleDescInitEntry(tupdesc, 7, "leaf", 
1097                                 (prst->state.attType.type == VOIDOID) ? INT4OID : prst->state.attType.type, -1, 0);
1098
1099                 funcctx->slot = TupleDescGetSlot(tupdesc);
1100                 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1101
1102                 funcctx->user_fctx = (void*)prst;
1103
1104                 MemoryContextSwitchTo(oldcontext);
1105
1106                 ItemPointerSet(&ipd, SPGIST_ROOT_BLKNO, FirstOffsetNumber);
1107                 prst->stack = NIL;
1108                 pushSPGistPrint(funcctx, prst, &ipd, 1);
1109
1110                 PG_FREE_IF_COPY(name,0);
1111         }
1112
1113         funcctx = SRF_PERCALL_SETUP();  
1114         prst = (SPGistPrint*)(funcctx->user_fctx);
1115
1116 next:
1117         for(;;) {
1118                 if ( prst->stack == NIL ) {
1119                         close_spgist_print(prst);
1120                         SRF_RETURN_DONE(funcctx);
1121                 }
1122
1123                 CHECK_FOR_INTERRUPTS();
1124
1125                 s = (SPGistPrintStackElem*)linitial(prst->stack);
1126                 prst->stack = list_delete_first(prst->stack);
1127
1128                 if (ItemPointerIsValid(&s->iptr))
1129                         break;
1130                 free(s);
1131         }
1132
1133         do {
1134                 Buffer                          buffer;
1135                 Page                            page;
1136                 SpGistDeadTuple         dtuple;
1137                 ItemPointer                     tid;
1138
1139                 buffer = ReadBuffer(prst->index, ItemPointerGetBlockNumber(&s->iptr));
1140                 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1141
1142                 page = BufferGetPage(buffer);
1143                 if (ItemPointerGetOffsetNumber(&s->iptr) > PageGetMaxOffsetNumber(page)) {
1144                         UnlockReleaseBuffer(buffer);
1145                         pfree(s);
1146                         goto next;
1147                 }
1148
1149                 dtuple = (SpGistDeadTuple)PageGetItem(page, PageGetItemId(page, ItemPointerGetOffsetNumber(&s->iptr)));
1150
1151                 if (dtuple->tupstate != SPGIST_LIVE)  {
1152                         UnlockReleaseBuffer(buffer);
1153                         pfree(s);
1154                         goto next;
1155                 }
1156
1157                 if (SpGistPageIsLeaf(page)) {
1158                                 SpGistLeafTuple leafTuple = (SpGistLeafTuple)dtuple;
1159
1160                                 tid = palloc(sizeof(ItemPointerData));
1161                                 *tid = s->iptr;
1162                                 prst->dvalues[0] = PointerGetDatum(tid);
1163                                 prst->nulls[0] = ' ';
1164                                 prst->nulls[1] = 'n';
1165                                 prst->dvalues[2]  = s->level; 
1166                                 prst->nulls[2] = ' ';
1167                                 prst->nulls[3] = 'n';
1168                                 prst->nulls[4] = 'n';
1169                                 prst->nulls[5] = 'n';
1170                                 prst->dvalues[6]  = datumCopy(SGLTDATUM(leafTuple, &prst->state), 
1171                                                                                         prst->state.attType.attbyval, prst->state.attType.attlen); 
1172                                 prst->nulls[6] = ' ';
1173                 } else {
1174                         SpGistInnerTuple        innerTuple = (SpGistInnerTuple)dtuple;
1175                         int                             i;
1176                         SpGistNodeTuple         node;
1177
1178                         SGITITERATE(innerTuple, i, node) {
1179                                 if (ItemPointerIsValid(&node->t_tid)) {
1180                                         if (i >= s->nlabel)
1181                                                 break;
1182                                 }
1183                         }
1184
1185                         if (i >= innerTuple->nNodes) {
1186                                 UnlockReleaseBuffer(buffer);
1187                                 pfree(s);
1188                                 goto next;
1189                         }
1190
1191                         tid = palloc(sizeof(ItemPointerData));
1192                         *tid = s->iptr;
1193                         prst->dvalues[0] = PointerGetDatum(tid);
1194                         prst->nulls[0] = ' ';
1195                         prst->dvalues[1] = Int32GetDatum(s->nlabel);
1196                         prst->nulls[1] = ' ';
1197                         prst->dvalues[2]  = s->level; 
1198                         prst->nulls[2] = ' ';
1199                         tid = palloc(sizeof(ItemPointerData));
1200                         *tid = node->t_tid;
1201                         prst->dvalues[3] = PointerGetDatum(tid);
1202                         prst->nulls[3] = ' ';
1203                         if (prst->state.attPrefixType.attbyval == false && innerTuple->prefixSize > 0) {
1204                                 prst->dvalues[4]  = datumCopy(SGITDATUM(innerTuple, &prst->state), 
1205                                                                                         prst->state.attPrefixType.attbyval, prst->state.attPrefixType.attlen); 
1206                                 prst->nulls[4] = ' ';
1207                         } else
1208                                 prst->nulls[4] = 'n';
1209                         if (prst->state.attLabelType.attbyval == false && !IndexTupleHasNulls(node)) {
1210                                 prst->dvalues[5]  = datumCopy(SGNTDATUM(node, &prst->state), 
1211                                                                                         prst->state.attLabelType.attbyval, prst->state.attLabelType.attlen); 
1212                                 prst->nulls[5] = ' ';
1213                         } else
1214                                 prst->nulls[5] = 'n';
1215                         prst->nulls[6] = 'n';
1216
1217                         pushSPGistPrint(funcctx, prst, &node->t_tid, s->level + 1);
1218                         s->nlabel = i + 1;
1219                         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1220                         prst->stack = lcons(s, prst->stack);
1221                         MemoryContextSwitchTo(oldcontext);
1222                         s = NULL;
1223                 }
1224
1225                 UnlockReleaseBuffer(buffer);
1226                 if (s) pfree(s);
1227         } while(0);
1228
1229         htuple = heap_formtuple(funcctx->attinmeta->tupdesc, prst->dvalues, prst->nulls);
1230         result = TupleGetDatum(funcctx->slot, htuple);
1231
1232         SRF_RETURN_NEXT(funcctx, result);
1233 #endif
1234 }
1235
1236
1237 PG_FUNCTION_INFO_V1(gin_statpage);
1238 Datum gin_statpage(PG_FUNCTION_ARGS);
1239 Datum
1240 gin_statpage(PG_FUNCTION_ARGS)
1241 {
1242 #if PG_VERSION_NUM < 90400
1243         elog(NOTICE, "Function is not working under PgSQL < 9.4");
1244
1245         PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1246 #else
1247         text       *name = PG_GETARG_TEXT_P(0);
1248         RangeVar   *relvar;
1249         Relation        index;
1250         BlockNumber blkno;
1251         char            res[1024];
1252         uint32          totalPages,
1253                                 entryPages = 0,
1254                                 dataPages = 0,
1255                                 dataInnerPages = 0,
1256                                 dataLeafPages = 0,
1257                                 entryInnerPages = 0,
1258                                 entryLeafPages = 0
1259                                 ;
1260         uint64          dataInnerFreeSpace = 0,
1261                                 dataLeafFreeSpace = 0,
1262                                 dataInnerTuplesCount = 0,
1263                                 dataLeafIptrsCount = 0,
1264                                 entryInnerFreeSpace = 0,
1265                                 entryLeafFreeSpace = 0,
1266                                 entryInnerTuplesCount = 0,
1267                                 entryLeafTuplesCount = 0,
1268                                 entryPostingSize = 0,
1269                                 entryPostingCount = 0,
1270                                 entryAttrSize = 0
1271                                 ;
1272
1273         relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1274         index = relation_openrv(relvar, AccessExclusiveLock);
1275
1276         if (index->rd_rel->relkind != RELKIND_INDEX ||
1277                         index->rd_rel->relam != GIN_AM_OID)
1278                 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1279                          RelationGetRelationName(index));
1280
1281         totalPages = RelationGetNumberOfBlocks(index);
1282
1283         for (blkno = GIN_ROOT_BLKNO; blkno < totalPages; blkno++)
1284         {
1285                 Buffer          buffer;
1286                 Page            page;
1287                 PageHeader      header;
1288
1289                 buffer = ReadBuffer(index, blkno);
1290                 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1291
1292                 page = BufferGetPage(buffer);
1293                 header = (PageHeader)page;
1294
1295                 if (GinPageIsData(page))
1296                 {
1297                         dataPages++;
1298                         if (GinPageIsLeaf(page))
1299                         {
1300                                 ItemPointerData minItem;
1301                                 int nlist;
1302
1303                                 dataLeafPages++;
1304                                 dataLeafFreeSpace += header->pd_upper - header->pd_lower;
1305                                 ItemPointerSetMin(&minItem);
1306                                 pfree(GinDataLeafPageGetItems(page, &nlist, minItem));
1307                                 dataLeafIptrsCount += nlist;
1308                         }
1309                         else
1310                         {
1311                                 dataInnerPages++;
1312                                 dataInnerFreeSpace += header->pd_upper - header->pd_lower;
1313                                 dataInnerTuplesCount += GinPageGetOpaque(page)->maxoff;
1314                         }
1315                 }
1316                 else
1317                 {
1318                         IndexTuple itup;
1319                         OffsetNumber i, maxoff;
1320
1321                         maxoff = PageGetMaxOffsetNumber(page);
1322
1323                         entryPages++;
1324                         if (GinPageIsLeaf(page))
1325                         {
1326                                 entryLeafPages++;
1327                                 entryLeafFreeSpace += header->pd_upper - header->pd_lower;
1328                                 entryLeafTuplesCount += maxoff;
1329                         }
1330                         else
1331                         {
1332                                 entryInnerPages++;
1333                                 entryInnerFreeSpace += header->pd_upper - header->pd_lower;
1334                                 entryInnerTuplesCount += maxoff;
1335                         }
1336
1337                         for (i = 1; i <= maxoff; i++)
1338                         {
1339                                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
1340
1341                                 if (GinPageIsLeaf(page))
1342                                 {
1343                                         GinPostingList *list = (GinPostingList *)GinGetPosting(itup);
1344                                         entryPostingCount += GinGetNPosting(itup);
1345                                         entryPostingSize += SizeOfGinPostingList(list);
1346                                         entryAttrSize += GinGetPostingOffset(itup) - IndexInfoFindDataOffset((itup)->t_info);
1347                                 }
1348                                 else
1349                                 {
1350                                         entryAttrSize += IndexTupleSize(itup) - IndexInfoFindDataOffset((itup)->t_info);
1351                                 }
1352                         }
1353                 }
1354
1355                 UnlockReleaseBuffer(buffer);
1356         }
1357
1358         index_close(index, AccessExclusiveLock);
1359         totalPages--;
1360
1361         snprintf(res, sizeof(res),
1362                          "totalPages:            %u\n"
1363                          "dataPages:             %u\n"
1364                          "dataInnerPages:        %u\n"
1365                          "dataLeafPages:         %u\n"
1366                          "dataInnerFreeSpace:    " INT64_FORMAT "\n"
1367                          "dataLeafFreeSpace:     " INT64_FORMAT "\n"
1368                          "dataInnerTuplesCount:  " INT64_FORMAT "\n"
1369                          "dataLeafIptrsCount:    " INT64_FORMAT "\n"
1370                          "entryPages:            %u\n"
1371                          "entryInnerPages:       %u\n"
1372                          "entryLeafPages:        %u\n"
1373                          "entryInnerFreeSpace:   " INT64_FORMAT "\n"
1374                          "entryLeafFreeSpace:    " INT64_FORMAT "\n"
1375                          "entryInnerTuplesCount: " INT64_FORMAT "\n"
1376                          "entryLeafTuplesCount:  " INT64_FORMAT "\n"
1377                          "entryPostingSize:      " INT64_FORMAT "\n"
1378                          "entryPostingCount:     " INT64_FORMAT "\n"
1379                          "entryAttrSize:         " INT64_FORMAT "\n"
1380                          ,
1381                          totalPages,
1382                          dataPages,
1383                          dataInnerPages,
1384                          dataLeafPages,
1385                          dataInnerFreeSpace,
1386                          dataLeafFreeSpace,
1387                          dataInnerTuplesCount,
1388                          dataLeafIptrsCount,
1389                          entryPages,
1390                          entryInnerPages,
1391                          entryLeafPages,
1392                          entryInnerFreeSpace,
1393                          entryLeafFreeSpace,
1394                          entryInnerTuplesCount,
1395                          entryLeafTuplesCount,
1396                          entryPostingSize,
1397                          entryPostingCount,
1398                          entryAttrSize
1399                          );
1400
1401         PG_RETURN_TEXT_P(CStringGetTextDatum(res));
1402 #endif
1403 }
1404