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