add usedInnerSpace/usedLeafSpace for spgist
[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                           usedLeafSpace = 0.0,
896                           usedInnerSpace = 0.0;
897         char            res[1024];
898         int              bufferSize = -1;
899         int64      innerTuples = 0,
900                                 leafTuples = 0,
901                                 nAllTheSame = 0,
902                                 nLeafPlaceholder = 0,
903                                 nInnerPlaceholder = 0,
904                                 nLeafRedirect = 0,
905                                 nInnerRedirect = 0;
906
907 #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
908 #define IS_SPGIST(r) ((r)->rd_rel->relam == SPGIST_AM_OID)
909
910         relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
911         index = relation_openrv(relvar, AccessExclusiveLock);
912
913         if (!IS_INDEX(index) || !IS_SPGIST(index))
914                 elog(ERROR, "relation \"%s\" is not an SPGiST index",
915                          RelationGetRelationName(index));
916
917         totalPages = RelationGetNumberOfBlocks(index);
918
919         for (blkno = SPGIST_ROOT_BLKNO; blkno < totalPages; blkno++)
920         {
921                 Buffer    buffer;
922                 Page            page;
923                 int              pageFree;
924
925                 buffer = ReadBuffer(index, blkno);
926                 LockBuffer(buffer, BUFFER_LOCK_SHARE);
927
928                 page = BufferGetPage(buffer);
929
930                 if (PageIsNew(page) || SpGistPageIsDeleted(page))
931                 {
932                         deletedPages++;
933                         UnlockReleaseBuffer(buffer);
934                         continue;
935                 }
936
937                 if (SpGistPageIsLeaf(page))
938                 {
939                         leafPages++;
940                         leafTuples += PageGetMaxOffsetNumber(page);
941                         nLeafPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
942                         nLeafRedirect += SpGistPageGetOpaque(page)->nRedirection;
943                 }
944                 else
945                 {
946                         int      i,
947                                         max;
948
949                         innerPages++;
950                         max = PageGetMaxOffsetNumber(page);
951                         innerTuples += max;
952                         nInnerPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
953                         nInnerRedirect += SpGistPageGetOpaque(page)->nRedirection;
954                         for (i = FirstOffsetNumber; i <= max; i++)
955                         {
956                                 SpGistInnerTuple it;
957
958                                 it = (SpGistInnerTuple) PageGetItem(page,
959                                                                                                         PageGetItemId(page, i));
960                                 if (it->allTheSame)
961                                         nAllTheSame++;
962                         }
963                 }
964
965                 if (bufferSize < 0)
966                         bufferSize = BufferGetPageSize(buffer)
967                                 - MAXALIGN(sizeof(SpGistPageOpaqueData))
968                                 - SizeOfPageHeaderData;
969
970                 pageFree = PageGetExactFreeSpace(page);
971
972                 usedSpace += bufferSize - pageFree;
973                 if (SpGistPageIsLeaf(page))
974                         usedLeafSpace += bufferSize - pageFree;
975                 else
976                         usedInnerSpace += bufferSize - pageFree;
977
978                 if (pageFree == bufferSize)
979                         emptyPages++;
980
981                 UnlockReleaseBuffer(buffer);
982         }
983
984         index_close(index, AccessExclusiveLock);
985
986         totalPages--;                      /* discount metapage */
987
988         snprintf(res, sizeof(res),
989                          "totalPages:        %u\n"
990                          "deletedPages:      %u\n"
991                          "innerPages:        %u\n"
992                          "leafPages:         %u\n"
993                          "emptyPages:        %u\n"
994                          "usedSpace:         %.2f kbytes\n"
995                          "usedInnerSpace:    %.2f kbytes\n"
996                          "usedLeafSpace:     %.2f kbytes\n"
997                          "freeSpace:         %.2f kbytes\n"
998                          "fillRatio:         %.2f%%\n"
999                          "leafTuples:        " INT64_FORMAT "\n"
1000                          "innerTuples:       " INT64_FORMAT "\n"
1001                          "innerAllTheSame:   " INT64_FORMAT "\n"
1002                          "leafPlaceholders:  " INT64_FORMAT "\n"
1003                          "innerPlaceholders: " INT64_FORMAT "\n"
1004                          "leafRedirects:     " INT64_FORMAT "\n"
1005                          "innerRedirects:    " INT64_FORMAT,
1006                          totalPages, deletedPages, innerPages, leafPages, emptyPages,
1007                          usedSpace / 1024.0,
1008                          usedInnerSpace / 1024.0,
1009                          usedLeafSpace / 1024.0,
1010                          (((double) bufferSize) * ((double) totalPages) - usedSpace) / 1024,
1011                          100.0 * (usedSpace / (((double) bufferSize) * ((double) totalPages))),
1012                          leafTuples, innerTuples, nAllTheSame,
1013                          nLeafPlaceholder, nInnerPlaceholder,
1014                          nLeafRedirect, nInnerRedirect);
1015
1016         PG_RETURN_TEXT_P(CStringGetTextDatum(res));
1017 #endif
1018 }
1019
1020 #if PG_VERSION_NUM >= 90200
1021
1022 typedef struct SPGistPrintStackElem {
1023         ItemPointerData         iptr;
1024         int16                           nlabel;
1025         int                                     level;
1026 } SPGistPrintStackElem;
1027
1028 typedef struct SPGistPrint {
1029         SpGistState     state;
1030         Relation        index;
1031         Datum           dvalues[7 /* see CreateTemplateTupleDesc call */];
1032         char            nulls[7 /* see CreateTemplateTupleDesc call */];
1033         List            *stack;
1034 } SPGistPrint;
1035
1036 static void
1037 pushSPGistPrint(FuncCallContext *funcctx, SPGistPrint *prst, ItemPointer ip, int level) {
1038         MemoryContext   oldcontext;
1039         SPGistPrintStackElem    *e;
1040
1041         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1042
1043         e = palloc(sizeof(*e));
1044         e->iptr = *ip;
1045         e->nlabel = 0;
1046         e->level = level;
1047         prst->stack = lcons(e, prst->stack); 
1048
1049         MemoryContextSwitchTo(oldcontext);
1050 }
1051
1052 static void
1053 close_spgist_print(SPGistPrint *prst) {
1054         index_close(prst->index, AccessExclusiveLock);
1055 }
1056 #endif
1057
1058 PG_FUNCTION_INFO_V1(spgist_print);
1059 Datum spgist_print(PG_FUNCTION_ARGS);
1060 Datum
1061 spgist_print(PG_FUNCTION_ARGS)
1062 {
1063 #if PG_VERSION_NUM < 90200
1064         elog(NOTICE, "Function is not working under PgSQL < 9.2");
1065
1066         PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1067 #else
1068         FuncCallContext                 *funcctx;
1069         SPGistPrint                             *prst;
1070         SPGistPrintStackElem    *s;
1071         HeapTuple                               htuple;
1072         Datum                                   result;
1073         MemoryContext                   oldcontext;
1074
1075         if (SRF_IS_FIRSTCALL()) {
1076                 text                    *name=PG_GETARG_TEXT_P(0);
1077                 RangeVar                *relvar;
1078                 Relation                index;
1079                 ItemPointerData ipd;
1080                 TupleDesc               tupdesc;
1081
1082                 funcctx = SRF_FIRSTCALL_INIT();
1083                 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1084                 index = relation_openrv(relvar, AccessExclusiveLock);
1085
1086                 if (!IS_INDEX(index) || !IS_SPGIST(index))
1087                         elog(ERROR, "relation \"%s\" is not an SPGiST index",
1088                                  RelationGetRelationName(index));
1089
1090                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1091
1092                 prst = palloc(sizeof(*prst));
1093
1094                 prst->index = index;
1095                 initSpGistState(&prst->state, index);
1096
1097                 tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ +  2 /* tids */, false);
1098                 TupleDescInitEntry(tupdesc, 1, "tid", TIDOID, -1, 0);
1099                 TupleDescInitEntry(tupdesc, 2, "node", INT4OID, -1, 0);
1100                 TupleDescInitEntry(tupdesc, 3, "level", INT4OID, -1, 0);
1101                 TupleDescInitEntry(tupdesc, 4, "tid_pointer", TIDOID, -1, 0);
1102                 TupleDescInitEntry(tupdesc, 5, "prefix", 
1103                                 (prst->state.attPrefixType.type == VOIDOID) ? INT4OID : prst->state.attPrefixType.type, -1, 0);
1104                 TupleDescInitEntry(tupdesc, 6, "label", 
1105                                 (prst->state.attLabelType.type == VOIDOID) ? INT4OID : prst->state.attLabelType.type, -1, 0);
1106                 TupleDescInitEntry(tupdesc, 7, "leaf", 
1107                                 (prst->state.attType.type == VOIDOID) ? INT4OID : prst->state.attType.type, -1, 0);
1108
1109                 funcctx->slot = TupleDescGetSlot(tupdesc);
1110                 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1111
1112                 funcctx->user_fctx = (void*)prst;
1113
1114                 MemoryContextSwitchTo(oldcontext);
1115
1116                 ItemPointerSet(&ipd, SPGIST_ROOT_BLKNO, FirstOffsetNumber);
1117                 prst->stack = NIL;
1118                 pushSPGistPrint(funcctx, prst, &ipd, 1);
1119
1120                 PG_FREE_IF_COPY(name,0);
1121         }
1122
1123         funcctx = SRF_PERCALL_SETUP();  
1124         prst = (SPGistPrint*)(funcctx->user_fctx);
1125
1126 next:
1127         for(;;) {
1128                 if ( prst->stack == NIL ) {
1129                         close_spgist_print(prst);
1130                         SRF_RETURN_DONE(funcctx);
1131                 }
1132
1133                 CHECK_FOR_INTERRUPTS();
1134
1135                 s = (SPGistPrintStackElem*)linitial(prst->stack);
1136                 prst->stack = list_delete_first(prst->stack);
1137
1138                 if (ItemPointerIsValid(&s->iptr))
1139                         break;
1140                 free(s);
1141         }
1142
1143         do {
1144                 Buffer                          buffer;
1145                 Page                            page;
1146                 SpGistDeadTuple         dtuple;
1147                 ItemPointer                     tid;
1148
1149                 buffer = ReadBuffer(prst->index, ItemPointerGetBlockNumber(&s->iptr));
1150                 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1151
1152                 page = BufferGetPage(buffer);
1153                 if (ItemPointerGetOffsetNumber(&s->iptr) > PageGetMaxOffsetNumber(page)) {
1154                         UnlockReleaseBuffer(buffer);
1155                         pfree(s);
1156                         goto next;
1157                 }
1158
1159                 dtuple = (SpGistDeadTuple)PageGetItem(page, PageGetItemId(page, ItemPointerGetOffsetNumber(&s->iptr)));
1160
1161                 if (dtuple->tupstate != SPGIST_LIVE)  {
1162                         UnlockReleaseBuffer(buffer);
1163                         pfree(s);
1164                         goto next;
1165                 }
1166
1167                 if (SpGistPageIsLeaf(page)) {
1168                                 SpGistLeafTuple leafTuple = (SpGistLeafTuple)dtuple;
1169
1170                                 tid = palloc(sizeof(ItemPointerData));
1171                                 *tid = s->iptr;
1172                                 prst->dvalues[0] = PointerGetDatum(tid);
1173                                 prst->nulls[0] = ' ';
1174                                 prst->nulls[1] = 'n';
1175                                 prst->dvalues[2]  = s->level; 
1176                                 prst->nulls[2] = ' ';
1177                                 prst->nulls[3] = 'n';
1178                                 prst->nulls[4] = 'n';
1179                                 prst->nulls[5] = 'n';
1180                                 prst->dvalues[6]  = datumCopy(SGLTDATUM(leafTuple, &prst->state), 
1181                                                                                         prst->state.attType.attbyval, prst->state.attType.attlen); 
1182                                 prst->nulls[6] = ' ';
1183                 } else {
1184                         SpGistInnerTuple        innerTuple = (SpGistInnerTuple)dtuple;
1185                         int                             i;
1186                         SpGistNodeTuple         node;
1187
1188                         SGITITERATE(innerTuple, i, node) {
1189                                 if (ItemPointerIsValid(&node->t_tid)) {
1190                                         if (i >= s->nlabel)
1191                                                 break;
1192                                 }
1193                         }
1194
1195                         if (i >= innerTuple->nNodes) {
1196                                 UnlockReleaseBuffer(buffer);
1197                                 pfree(s);
1198                                 goto next;
1199                         }
1200
1201                         tid = palloc(sizeof(ItemPointerData));
1202                         *tid = s->iptr;
1203                         prst->dvalues[0] = PointerGetDatum(tid);
1204                         prst->nulls[0] = ' ';
1205                         prst->dvalues[1] = Int32GetDatum(s->nlabel);
1206                         prst->nulls[1] = ' ';
1207                         prst->dvalues[2]  = s->level; 
1208                         prst->nulls[2] = ' ';
1209                         tid = palloc(sizeof(ItemPointerData));
1210                         *tid = node->t_tid;
1211                         prst->dvalues[3] = PointerGetDatum(tid);
1212                         prst->nulls[3] = ' ';
1213                         if (prst->state.attPrefixType.attbyval == false && innerTuple->prefixSize > 0) {
1214                                 prst->dvalues[4]  = datumCopy(SGITDATUM(innerTuple, &prst->state), 
1215                                                                                         prst->state.attPrefixType.attbyval, prst->state.attPrefixType.attlen); 
1216                                 prst->nulls[4] = ' ';
1217                         } else
1218                                 prst->nulls[4] = 'n';
1219                         if (prst->state.attLabelType.attbyval == false && !IndexTupleHasNulls(node)) {
1220                                 prst->dvalues[5]  = datumCopy(SGNTDATUM(node, &prst->state), 
1221                                                                                         prst->state.attLabelType.attbyval, prst->state.attLabelType.attlen); 
1222                                 prst->nulls[5] = ' ';
1223                         } else
1224                                 prst->nulls[5] = 'n';
1225                         prst->nulls[6] = 'n';
1226
1227                         pushSPGistPrint(funcctx, prst, &node->t_tid, s->level + 1);
1228                         s->nlabel = i + 1;
1229                         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1230                         prst->stack = lcons(s, prst->stack);
1231                         MemoryContextSwitchTo(oldcontext);
1232                         s = NULL;
1233                 }
1234
1235                 UnlockReleaseBuffer(buffer);
1236                 if (s) pfree(s);
1237         } while(0);
1238
1239         htuple = heap_formtuple(funcctx->attinmeta->tupdesc, prst->dvalues, prst->nulls);
1240         result = TupleGetDatum(funcctx->slot, htuple);
1241
1242         SRF_RETURN_NEXT(funcctx, result);
1243 #endif
1244 }
1245
1246
1247 PG_FUNCTION_INFO_V1(gin_statpage);
1248 Datum gin_statpage(PG_FUNCTION_ARGS);
1249 Datum
1250 gin_statpage(PG_FUNCTION_ARGS)
1251 {
1252 #if PG_VERSION_NUM < 90400
1253         elog(NOTICE, "Function is not working under PgSQL < 9.4");
1254
1255         PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1256 #else
1257         text       *name = PG_GETARG_TEXT_P(0);
1258         RangeVar   *relvar;
1259         Relation        index;
1260         BlockNumber blkno;
1261         char            res[1024];
1262         uint32          totalPages,
1263                                 entryPages = 0,
1264                                 dataPages = 0,
1265                                 dataInnerPages = 0,
1266                                 dataLeafPages = 0,
1267                                 entryInnerPages = 0,
1268                                 entryLeafPages = 0
1269                                 ;
1270         uint64          dataInnerFreeSpace = 0,
1271                                 dataLeafFreeSpace = 0,
1272                                 dataInnerTuplesCount = 0,
1273                                 dataLeafIptrsCount = 0,
1274                                 entryInnerFreeSpace = 0,
1275                                 entryLeafFreeSpace = 0,
1276                                 entryInnerTuplesCount = 0,
1277                                 entryLeafTuplesCount = 0,
1278                                 entryPostingSize = 0,
1279                                 entryPostingCount = 0,
1280                                 entryAttrSize = 0
1281                                 ;
1282
1283         relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1284         index = relation_openrv(relvar, AccessExclusiveLock);
1285
1286         if (index->rd_rel->relkind != RELKIND_INDEX ||
1287                         index->rd_rel->relam != GIN_AM_OID)
1288                 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1289                          RelationGetRelationName(index));
1290
1291         totalPages = RelationGetNumberOfBlocks(index);
1292
1293         for (blkno = GIN_ROOT_BLKNO; blkno < totalPages; blkno++)
1294         {
1295                 Buffer          buffer;
1296                 Page            page;
1297                 PageHeader      header;
1298
1299                 buffer = ReadBuffer(index, blkno);
1300                 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1301
1302                 page = BufferGetPage(buffer);
1303                 header = (PageHeader)page;
1304
1305                 if (GinPageIsData(page))
1306                 {
1307                         dataPages++;
1308                         if (GinPageIsLeaf(page))
1309                         {
1310                                 ItemPointerData minItem;
1311                                 int nlist;
1312
1313                                 dataLeafPages++;
1314                                 dataLeafFreeSpace += header->pd_upper - header->pd_lower;
1315                                 ItemPointerSetMin(&minItem);
1316                                 pfree(GinDataLeafPageGetItems(page, &nlist, minItem));
1317                                 dataLeafIptrsCount += nlist;
1318                         }
1319                         else
1320                         {
1321                                 dataInnerPages++;
1322                                 dataInnerFreeSpace += header->pd_upper - header->pd_lower;
1323                                 dataInnerTuplesCount += GinPageGetOpaque(page)->maxoff;
1324                         }
1325                 }
1326                 else
1327                 {
1328                         IndexTuple itup;
1329                         OffsetNumber i, maxoff;
1330
1331                         maxoff = PageGetMaxOffsetNumber(page);
1332
1333                         entryPages++;
1334                         if (GinPageIsLeaf(page))
1335                         {
1336                                 entryLeafPages++;
1337                                 entryLeafFreeSpace += header->pd_upper - header->pd_lower;
1338                                 entryLeafTuplesCount += maxoff;
1339                         }
1340                         else
1341                         {
1342                                 entryInnerPages++;
1343                                 entryInnerFreeSpace += header->pd_upper - header->pd_lower;
1344                                 entryInnerTuplesCount += maxoff;
1345                         }
1346
1347                         for (i = 1; i <= maxoff; i++)
1348                         {
1349                                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
1350
1351                                 if (GinPageIsLeaf(page))
1352                                 {
1353                                         GinPostingList *list = (GinPostingList *)GinGetPosting(itup);
1354                                         entryPostingCount += GinGetNPosting(itup);
1355                                         entryPostingSize += SizeOfGinPostingList(list);
1356                                         entryAttrSize += GinGetPostingOffset(itup) - IndexInfoFindDataOffset((itup)->t_info);
1357                                 }
1358                                 else
1359                                 {
1360                                         entryAttrSize += IndexTupleSize(itup) - IndexInfoFindDataOffset((itup)->t_info);
1361                                 }
1362                         }
1363                 }
1364
1365                 UnlockReleaseBuffer(buffer);
1366         }
1367
1368         index_close(index, AccessExclusiveLock);
1369         totalPages--;
1370
1371         snprintf(res, sizeof(res),
1372                          "totalPages:            %u\n"
1373                          "dataPages:             %u\n"
1374                          "dataInnerPages:        %u\n"
1375                          "dataLeafPages:         %u\n"
1376                          "dataInnerFreeSpace:    " INT64_FORMAT "\n"
1377                          "dataLeafFreeSpace:     " INT64_FORMAT "\n"
1378                          "dataInnerTuplesCount:  " INT64_FORMAT "\n"
1379                          "dataLeafIptrsCount:    " INT64_FORMAT "\n"
1380                          "entryPages:            %u\n"
1381                          "entryInnerPages:       %u\n"
1382                          "entryLeafPages:        %u\n"
1383                          "entryInnerFreeSpace:   " INT64_FORMAT "\n"
1384                          "entryLeafFreeSpace:    " INT64_FORMAT "\n"
1385                          "entryInnerTuplesCount: " INT64_FORMAT "\n"
1386                          "entryLeafTuplesCount:  " INT64_FORMAT "\n"
1387                          "entryPostingSize:      " INT64_FORMAT "\n"
1388                          "entryPostingCount:     " INT64_FORMAT "\n"
1389                          "entryAttrSize:         " INT64_FORMAT "\n"
1390                          ,
1391                          totalPages,
1392                          dataPages,
1393                          dataInnerPages,
1394                          dataLeafPages,
1395                          dataInnerFreeSpace,
1396                          dataLeafFreeSpace,
1397                          dataInnerTuplesCount,
1398                          dataLeafIptrsCount,
1399                          entryPages,
1400                          entryInnerPages,
1401                          entryLeafPages,
1402                          entryInnerFreeSpace,
1403                          entryLeafFreeSpace,
1404                          entryInnerTuplesCount,
1405                          entryLeafTuplesCount,
1406                          entryPostingSize,
1407                          entryPostingCount,
1408                          entryAttrSize
1409                          );
1410
1411         PG_RETURN_TEXT_P(CStringGetTextDatum(res));
1412 #endif
1413 }
1414