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