support 9.5 & 9.6
[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->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 < 80400
687         bool                            isnull;
688 #endif
689
690         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
691         st->curval = datumCopy(
692 #if PG_VERSION_NUM >= 90100
693                                         gintuple_get_key(&st->ginstate, itup, &st->category),
694 #elif PG_VERSION_NUM >= 80400
695                                         gin_index_getattr(&st->ginstate, itup),
696 #else
697                                         index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull),
698 #endif
699                                         st->index->rd_att->attrs[st->attnum]->attbyval,
700                                         st->index->rd_att->attrs[st->attnum]->attlen );
701         MemoryContextSwitchTo(oldcontext);
702
703         st->dvalues[0] = st->curval;
704 #if PG_VERSION_NUM >= 90100
705         /* do no distiguish various null category */
706         st->nulls[0] = (st->category == GIN_CAT_NORM_KEY) ? ISNOTNULL : ISNULL;
707 #endif
708
709         if ( GinIsPostingTree(itup) ) {
710                 BlockNumber     rootblkno = GinGetPostingTree(itup);
711 #if PG_VERSION_NUM >= 90400
712                 GinBtreeData    btree;
713                 GinBtreeStack   *stack;
714                 ItemPointerData minItem;
715                 int                             nlist;
716                 ItemPointer             list;
717 #else
718                 GinPostingTreeScan *gdi;
719                 Buffer          entrybuffer;
720 #endif
721                 Page        page;
722                 uint32          predictNumber;
723
724                 LockBuffer(st->buffer, GIN_UNLOCK);
725 #if PG_VERSION_NUM >= 90400
726                 stack = ginScanBeginPostingTree(&btree, st->index, rootblkno);
727                 page = BufferGetPage(stack->buffer);
728                 ItemPointerSetMin(&minItem);
729                 list = GinDataLeafPageGetItems(page, &nlist, minItem);
730                 pfree(list);
731                 predictNumber = stack->predictNumber;
732 #elif PG_VERSION_NUM >= 90100
733                 gdi = ginPrepareScanPostingTree(st->index, rootblkno, TRUE);
734                 entrybuffer = ginScanBeginPostingTree(gdi);
735                 page = BufferGetPage(entrybuffer);
736                 predictNumber = gdi->stack->predictNumber;
737 #else
738                 gdi = prepareScanPostingTree(st->index, rootblkno, TRUE);
739                 entrybuffer = scanBeginPostingTree(gdi);
740                 page = BufferGetPage(entrybuffer);
741                 predictNumber = gdi->stack->predictNumber;
742 #endif
743
744                 st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
745
746 #if PG_VERSION_NUM < 90400
747                 LockBuffer(entrybuffer, GIN_UNLOCK);
748                 freeGinBtreeStack(gdi->stack);
749                 pfree(gdi);
750 #else
751                 LockBuffer(stack->buffer, GIN_UNLOCK);
752                 freeGinBtreeStack(stack);
753 #endif
754         } else {
755                 st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) );
756                 LockBuffer(st->buffer, GIN_UNLOCK);
757         }
758 }
759
760 PG_FUNCTION_INFO_V1(gin_stat);
761 Datum   gin_stat(PG_FUNCTION_ARGS);
762 Datum
763 gin_stat(PG_FUNCTION_ARGS) {
764         FuncCallContext  *funcctx;
765         GinStatState     *st;
766         Datum result=(Datum)0;
767         IndexTuple      ituple;
768         HeapTuple       htuple;
769         Page page;
770
771         if (SRF_IS_FIRSTCALL()) {
772                 text    *name=PG_GETARG_TEXT_P(0);
773                 funcctx = SRF_FIRSTCALL_INIT();
774                 gin_setup_firstcall(funcctx, name, (PG_NARGS()==2) ? PG_GETARG_INT32(1) : 0 );
775                 PG_FREE_IF_COPY(name,0);
776         }
777
778         funcctx = SRF_PERCALL_SETUP();
779         st = (GinStatState*)(funcctx->user_fctx);
780
781         if ( refindPosition(st) == false ) {
782                 UnlockReleaseBuffer( st->buffer );
783                 gin_index_close(st->index);
784
785                 SRF_RETURN_DONE(funcctx);
786         }
787
788         for(;;) {
789                 st->offset++;
790
791                 if (moveRightIfItNeeded(st)==false) {
792                         UnlockReleaseBuffer( st->buffer );
793                         gin_index_close(st->index);
794
795                         SRF_RETURN_DONE(funcctx);
796                 }
797
798                 page = BufferGetPage(st->buffer);
799                 ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
800
801 #if PG_VERSION_NUM >= 80400
802                 if (st->attnum + 1 == gintuple_get_attrnum(&st->ginstate, ituple))
803 #endif
804                         break;
805         }
806
807         processTuple( funcctx,  st, ituple );
808
809         htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
810         result = TupleGetDatum(funcctx->slot, htuple);
811
812         SRF_RETURN_NEXT(funcctx, result);
813 }
814
815 PG_FUNCTION_INFO_V1(gin_count_estimate);
816 Datum gin_count_estimate(PG_FUNCTION_ARGS);
817 #if PG_VERSION_NUM >= 80300
818 Datum
819 gin_count_estimate(PG_FUNCTION_ARGS) {
820         text                    *name=PG_GETARG_TEXT_P(0);
821         Relation                index;
822         IndexScanDesc   scan;
823         int64                   count = 0;
824         char                    *relname=t2c(name);
825         ScanKeyData             key;
826 #if PG_VERSION_NUM >= 80400
827         TIDBitmap               *bitmap = tbm_create(work_mem * 1024L);
828 #else
829 #define MAXTIDS         1024
830         ItemPointerData tids[MAXTIDS];
831         int32                   returned_tids;
832         bool                    more;
833 #endif
834
835         index = gin_index_open(
836                  makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_count_estimate")));
837
838         if ( index->rd_opcintype[0] != TSVECTOROID ) {
839                 gin_index_close(index);
840                 elog(ERROR, "Column type is not a tsvector");
841         }
842
843         key.sk_flags    = 0;
844         key.sk_attno    = 1;
845         key.sk_strategy = TSearchStrategyNumber;
846         key.sk_subtype  = 0;
847         key.sk_argument = PG_GETARG_DATUM(1);
848
849         fmgr_info( F_TS_MATCH_VQ , &key.sk_func );
850
851 #if PG_VERSION_NUM >= 90100
852 #if PG_VERSION_NUM >= 90400
853         scan = index_beginscan_bitmap(index, SnapshotSelf, 1);
854 #else
855         scan = index_beginscan_bitmap(index, SnapshotNow, 1);
856 #endif
857         index_rescan(scan, &key, 1, NULL, 0);
858
859         count = index_getbitmap(scan, bitmap);
860         tbm_free(bitmap);
861 #elif PG_VERSION_NUM >= 80400
862         scan = index_beginscan_bitmap(index, SnapshotNow, 1, &key);
863
864         count = index_getbitmap(scan, bitmap);
865         tbm_free(bitmap);
866 #else
867         scan = index_beginscan_multi(index, SnapshotNow, 1, &key);
868
869         do {
870                 more = index_getmulti(scan, tids, MAXTIDS, &returned_tids);
871                 count += returned_tids;
872         } while(more);
873 #endif
874
875         index_endscan( scan );
876         gin_index_close(index);
877
878         PG_RETURN_INT64(count);
879 }
880 #else
881 Datum
882 gin_count_estimate(PG_FUNCTION_ARGS) {
883         elog(NOTICE, "Function is not working under PgSQL < 8.3");
884
885         PG_RETURN_INT64(0);
886 }
887 #endif
888
889 PG_FUNCTION_INFO_V1(spgist_stat);
890 Datum spgist_stat(PG_FUNCTION_ARGS);
891 Datum
892 spgist_stat(PG_FUNCTION_ARGS)
893 {
894 #if PG_VERSION_NUM < 90200
895         elog(NOTICE, "Function is not working under PgSQL < 9.2");
896
897         PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
898 #else
899         text       *name = PG_GETARG_TEXT_P(0);
900         RangeVar   *relvar;
901         Relation        index;
902         BlockNumber blkno;
903         BlockNumber totalPages = 0,
904                                 innerPages = 0,
905                                 leafPages = 0,
906                                 emptyPages = 0,
907                                 deletedPages = 0;
908         double    usedSpace = 0.0,
909                           usedLeafSpace = 0.0,
910                           usedInnerSpace = 0.0;
911         char            res[1024];
912         int              bufferSize = -1;
913         int64      innerTuples = 0,
914                                 leafTuples = 0,
915                                 nAllTheSame = 0,
916                                 nLeafPlaceholder = 0,
917                                 nInnerPlaceholder = 0,
918                                 nLeafRedirect = 0,
919                                 nInnerRedirect = 0;
920
921 #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
922 #define IS_SPGIST(r) ((r)->rd_rel->relam == SPGIST_AM_OID)
923
924         relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
925         index = relation_openrv(relvar, AccessExclusiveLock);
926
927         if (!IS_INDEX(index) || !IS_SPGIST(index))
928                 elog(ERROR, "relation \"%s\" is not an SPGiST index",
929                          RelationGetRelationName(index));
930
931         totalPages = RelationGetNumberOfBlocks(index);
932
933         for (blkno = SPGIST_ROOT_BLKNO; blkno < totalPages; blkno++)
934         {
935                 Buffer    buffer;
936                 Page            page;
937                 int              pageFree;
938
939                 buffer = ReadBuffer(index, blkno);
940                 LockBuffer(buffer, BUFFER_LOCK_SHARE);
941
942                 page = BufferGetPage(buffer);
943
944                 if (PageIsNew(page) || SpGistPageIsDeleted(page))
945                 {
946                         deletedPages++;
947                         UnlockReleaseBuffer(buffer);
948                         continue;
949                 }
950
951                 if (SpGistPageIsLeaf(page))
952                 {
953                         leafPages++;
954                         leafTuples += PageGetMaxOffsetNumber(page);
955                         nLeafPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
956                         nLeafRedirect += SpGistPageGetOpaque(page)->nRedirection;
957                 }
958                 else
959                 {
960                         int      i,
961                                         max;
962
963                         innerPages++;
964                         max = PageGetMaxOffsetNumber(page);
965                         innerTuples += max;
966                         nInnerPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
967                         nInnerRedirect += SpGistPageGetOpaque(page)->nRedirection;
968                         for (i = FirstOffsetNumber; i <= max; i++)
969                         {
970                                 SpGistInnerTuple it;
971
972                                 it = (SpGistInnerTuple) PageGetItem(page,
973                                                                                                         PageGetItemId(page, i));
974                                 if (it->allTheSame)
975                                         nAllTheSame++;
976                         }
977                 }
978
979                 if (bufferSize < 0)
980                         bufferSize = BufferGetPageSize(buffer)
981                                 - MAXALIGN(sizeof(SpGistPageOpaqueData))
982                                 - SizeOfPageHeaderData;
983
984                 pageFree = PageGetExactFreeSpace(page);
985
986                 usedSpace += bufferSize - pageFree;
987                 if (SpGistPageIsLeaf(page))
988                         usedLeafSpace += bufferSize - pageFree;
989                 else
990                         usedInnerSpace += bufferSize - pageFree;
991
992                 if (pageFree == bufferSize)
993                         emptyPages++;
994
995                 UnlockReleaseBuffer(buffer);
996         }
997
998         index_close(index, AccessExclusiveLock);
999
1000         totalPages--;                      /* discount metapage */
1001
1002         snprintf(res, sizeof(res),
1003                          "totalPages:        %u\n"
1004                          "deletedPages:      %u\n"
1005                          "innerPages:        %u\n"
1006                          "leafPages:         %u\n"
1007                          "emptyPages:        %u\n"
1008                          "usedSpace:         %.2f kbytes\n"
1009                          "usedInnerSpace:    %.2f kbytes\n"
1010                          "usedLeafSpace:     %.2f kbytes\n"
1011                          "freeSpace:         %.2f kbytes\n"
1012                          "fillRatio:         %.2f%%\n"
1013                          "leafTuples:        " INT64_FORMAT "\n"
1014                          "innerTuples:       " INT64_FORMAT "\n"
1015                          "innerAllTheSame:   " INT64_FORMAT "\n"
1016                          "leafPlaceholders:  " INT64_FORMAT "\n"
1017                          "innerPlaceholders: " INT64_FORMAT "\n"
1018                          "leafRedirects:     " INT64_FORMAT "\n"
1019                          "innerRedirects:    " INT64_FORMAT,
1020                          totalPages, deletedPages, innerPages, leafPages, emptyPages,
1021                          usedSpace / 1024.0,
1022                          usedInnerSpace / 1024.0,
1023                          usedLeafSpace / 1024.0,
1024                          (((double) bufferSize) * ((double) totalPages) - usedSpace) / 1024,
1025                          100.0 * (usedSpace / (((double) bufferSize) * ((double) totalPages))),
1026                          leafTuples, innerTuples, nAllTheSame,
1027                          nLeafPlaceholder, nInnerPlaceholder,
1028                          nLeafRedirect, nInnerRedirect);
1029
1030         PG_RETURN_TEXT_P(CStringGetTextDatum(res));
1031 #endif
1032 }
1033
1034 #if PG_VERSION_NUM >= 90200
1035
1036 typedef struct SPGistPrintStackElem {
1037         ItemPointerData         iptr;
1038         int16                           nlabel;
1039         int                                     level;
1040 } SPGistPrintStackElem;
1041
1042 typedef struct SPGistPrint {
1043         SpGistState     state;
1044         Relation        index;
1045         Datum           dvalues[7 /* see CreateTemplateTupleDesc call */];
1046         char            nulls[7 /* see CreateTemplateTupleDesc call */];
1047         List            *stack;
1048 } SPGistPrint;
1049
1050 static void
1051 pushSPGistPrint(FuncCallContext *funcctx, SPGistPrint *prst, ItemPointer ip, int level) {
1052         MemoryContext   oldcontext;
1053         SPGistPrintStackElem    *e;
1054
1055         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1056
1057         e = palloc(sizeof(*e));
1058         e->iptr = *ip;
1059         e->nlabel = 0;
1060         e->level = level;
1061         prst->stack = lcons(e, prst->stack);
1062
1063         MemoryContextSwitchTo(oldcontext);
1064 }
1065
1066 static void
1067 close_spgist_print(SPGistPrint *prst) {
1068         index_close(prst->index, AccessExclusiveLock);
1069 }
1070 #endif
1071
1072 PG_FUNCTION_INFO_V1(spgist_print);
1073 Datum spgist_print(PG_FUNCTION_ARGS);
1074 Datum
1075 spgist_print(PG_FUNCTION_ARGS)
1076 {
1077 #if PG_VERSION_NUM < 90200
1078         elog(NOTICE, "Function is not working under PgSQL < 9.2");
1079
1080         PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1081 #else
1082         FuncCallContext                 *funcctx;
1083         SPGistPrint                             *prst;
1084         SPGistPrintStackElem    *s;
1085         HeapTuple                               htuple;
1086         Datum                                   result;
1087         MemoryContext                   oldcontext;
1088
1089         if (SRF_IS_FIRSTCALL()) {
1090                 text                    *name=PG_GETARG_TEXT_P(0);
1091                 RangeVar                *relvar;
1092                 Relation                index;
1093                 ItemPointerData ipd;
1094                 TupleDesc               tupdesc;
1095
1096                 funcctx = SRF_FIRSTCALL_INIT();
1097                 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1098                 index = relation_openrv(relvar, AccessExclusiveLock);
1099
1100                 if (!IS_INDEX(index) || !IS_SPGIST(index))
1101                         elog(ERROR, "relation \"%s\" is not an SPGiST index",
1102                                  RelationGetRelationName(index));
1103
1104                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1105
1106                 prst = palloc(sizeof(*prst));
1107
1108                 prst->index = index;
1109                 initSpGistState(&prst->state, index);
1110
1111                 tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ +  2 /* tids */, false);
1112                 TupleDescInitEntry(tupdesc, 1, "tid", TIDOID, -1, 0);
1113                 TupleDescInitEntry(tupdesc, 2, "node", INT4OID, -1, 0);
1114                 TupleDescInitEntry(tupdesc, 3, "level", INT4OID, -1, 0);
1115                 TupleDescInitEntry(tupdesc, 4, "tid_pointer", TIDOID, -1, 0);
1116                 TupleDescInitEntry(tupdesc, 5, "prefix",
1117                                 (prst->state.attPrefixType.type == VOIDOID) ? INT4OID : prst->state.attPrefixType.type, -1, 0);
1118                 TupleDescInitEntry(tupdesc, 6, "label",
1119                                 (prst->state.attLabelType.type == VOIDOID) ? INT4OID : prst->state.attLabelType.type, -1, 0);
1120                 TupleDescInitEntry(tupdesc, 7, "leaf",
1121                                 (prst->state.attType.type == VOIDOID) ? INT4OID : prst->state.attType.type, -1, 0);
1122
1123                 funcctx->slot = TupleDescGetSlot(tupdesc);
1124                 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1125
1126                 funcctx->user_fctx = (void*)prst;
1127
1128                 MemoryContextSwitchTo(oldcontext);
1129
1130                 ItemPointerSet(&ipd, SPGIST_ROOT_BLKNO, FirstOffsetNumber);
1131                 prst->stack = NIL;
1132                 pushSPGistPrint(funcctx, prst, &ipd, 1);
1133
1134                 PG_FREE_IF_COPY(name,0);
1135         }
1136
1137         funcctx = SRF_PERCALL_SETUP();
1138         prst = (SPGistPrint*)(funcctx->user_fctx);
1139
1140 next:
1141         for(;;) {
1142                 if ( prst->stack == NIL ) {
1143                         close_spgist_print(prst);
1144                         SRF_RETURN_DONE(funcctx);
1145                 }
1146
1147                 CHECK_FOR_INTERRUPTS();
1148
1149                 s = (SPGistPrintStackElem*)linitial(prst->stack);
1150                 prst->stack = list_delete_first(prst->stack);
1151
1152                 if (ItemPointerIsValid(&s->iptr))
1153                         break;
1154                 free(s);
1155         }
1156
1157         do {
1158                 Buffer                          buffer;
1159                 Page                            page;
1160                 SpGistDeadTuple         dtuple;
1161                 ItemPointer                     tid;
1162
1163                 buffer = ReadBuffer(prst->index, ItemPointerGetBlockNumber(&s->iptr));
1164                 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1165
1166                 page = BufferGetPage(buffer);
1167                 if (ItemPointerGetOffsetNumber(&s->iptr) > PageGetMaxOffsetNumber(page)) {
1168                         UnlockReleaseBuffer(buffer);
1169                         pfree(s);
1170                         goto next;
1171                 }
1172
1173                 dtuple = (SpGistDeadTuple)PageGetItem(page, PageGetItemId(page, ItemPointerGetOffsetNumber(&s->iptr)));
1174
1175                 if (dtuple->tupstate != SPGIST_LIVE)  {
1176                         UnlockReleaseBuffer(buffer);
1177                         pfree(s);
1178                         goto next;
1179                 }
1180
1181                 if (SpGistPageIsLeaf(page)) {
1182                                 SpGistLeafTuple leafTuple = (SpGistLeafTuple)dtuple;
1183
1184                                 tid = palloc(sizeof(ItemPointerData));
1185                                 *tid = s->iptr;
1186                                 prst->dvalues[0] = PointerGetDatum(tid);
1187                                 prst->nulls[0] = ISNOTNULL;
1188                                 prst->nulls[1] = ISNULL;
1189                                 prst->dvalues[2]  = s->level;
1190                                 prst->nulls[2] = ISNOTNULL;
1191                                 prst->nulls[3] = ISNULL;
1192                                 prst->nulls[4] = ISNULL;
1193                                 prst->nulls[5] = ISNULL;
1194                                 prst->dvalues[6]  = datumCopy(SGLTDATUM(leafTuple, &prst->state),
1195                                                                                         prst->state.attType.attbyval, prst->state.attType.attlen);
1196                                 prst->nulls[6] = ISNOTNULL;
1197                 } else {
1198                         SpGistInnerTuple        innerTuple = (SpGistInnerTuple)dtuple;
1199                         int                             i;
1200                         SpGistNodeTuple         node;
1201
1202                         SGITITERATE(innerTuple, i, node) {
1203                                 if (ItemPointerIsValid(&node->t_tid)) {
1204                                         if (i >= s->nlabel)
1205                                                 break;
1206                                 }
1207                         }
1208
1209                         if (i >= innerTuple->nNodes) {
1210                                 UnlockReleaseBuffer(buffer);
1211                                 pfree(s);
1212                                 goto next;
1213                         }
1214
1215                         tid = palloc(sizeof(ItemPointerData));
1216                         *tid = s->iptr;
1217                         prst->dvalues[0] = PointerGetDatum(tid);
1218                         prst->nulls[0] = ISNOTNULL;
1219                         prst->dvalues[1] = Int32GetDatum(s->nlabel);
1220                         prst->nulls[1] = ISNOTNULL;
1221                         prst->dvalues[2]  = s->level;
1222                         prst->nulls[2] = ISNOTNULL;
1223                         tid = palloc(sizeof(ItemPointerData));
1224                         *tid = node->t_tid;
1225                         prst->dvalues[3] = PointerGetDatum(tid);
1226                         prst->nulls[3] = ISNOTNULL;
1227                         if (innerTuple->prefixSize > 0) {
1228                                 prst->dvalues[4]  = datumCopy(SGITDATUM(innerTuple, &prst->state),
1229                                                                                         prst->state.attPrefixType.attbyval, prst->state.attPrefixType.attlen);
1230                                 prst->nulls[4] = ISNOTNULL;
1231                         } else
1232                                 prst->nulls[4] = ISNULL;
1233                         if (!IndexTupleHasNulls(node)) {
1234                                 prst->dvalues[5]  = datumCopy(SGNTDATUM(node, &prst->state),
1235                                                                                         prst->state.attLabelType.attbyval, prst->state.attLabelType.attlen);
1236                                 prst->nulls[5] = ISNOTNULL;
1237                         } else
1238                                 prst->nulls[5] = ISNULL;
1239                         prst->nulls[6] = ISNULL;
1240
1241                         pushSPGistPrint(funcctx, prst, &node->t_tid, s->level + 1);
1242                         s->nlabel = i + 1;
1243                         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1244                         prst->stack = lcons(s, prst->stack);
1245                         MemoryContextSwitchTo(oldcontext);
1246                         s = NULL;
1247                 }
1248
1249                 UnlockReleaseBuffer(buffer);
1250                 if (s) pfree(s);
1251         } while(0);
1252
1253         htuple = heap_formtuple(funcctx->attinmeta->tupdesc, prst->dvalues, prst->nulls);
1254         result = TupleGetDatum(funcctx->slot, htuple);
1255
1256         SRF_RETURN_NEXT(funcctx, result);
1257 #endif
1258 }
1259
1260
1261 PG_FUNCTION_INFO_V1(gin_statpage);
1262 Datum gin_statpage(PG_FUNCTION_ARGS);
1263 Datum
1264 gin_statpage(PG_FUNCTION_ARGS)
1265 {
1266 #if PG_VERSION_NUM < 90400
1267         elog(NOTICE, "Function is not working under PgSQL < 9.4");
1268
1269         PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1270 #else
1271         text       *name = PG_GETARG_TEXT_P(0);
1272         RangeVar   *relvar;
1273         Relation        index;
1274         BlockNumber blkno;
1275         char            res[1024];
1276         uint32          totalPages,
1277                                 entryPages = 0,
1278                                 dataPages = 0,
1279                                 dataInnerPages = 0,
1280                                 dataLeafPages = 0,
1281                                 entryInnerPages = 0,
1282                                 entryLeafPages = 0
1283                                 ;
1284         uint64          dataInnerFreeSpace = 0,
1285                                 dataLeafFreeSpace = 0,
1286                                 dataInnerTuplesCount = 0,
1287                                 dataLeafIptrsCount = 0,
1288                                 entryInnerFreeSpace = 0,
1289                                 entryLeafFreeSpace = 0,
1290                                 entryInnerTuplesCount = 0,
1291                                 entryLeafTuplesCount = 0,
1292                                 entryPostingSize = 0,
1293                                 entryPostingCount = 0,
1294                                 entryAttrSize = 0
1295                                 ;
1296
1297         relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1298         index = relation_openrv(relvar, AccessExclusiveLock);
1299
1300         if (index->rd_rel->relkind != RELKIND_INDEX ||
1301                         index->rd_rel->relam != GIN_AM_OID)
1302                 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1303                          RelationGetRelationName(index));
1304
1305         totalPages = RelationGetNumberOfBlocks(index);
1306
1307         for (blkno = GIN_ROOT_BLKNO; blkno < totalPages; blkno++)
1308         {
1309                 Buffer          buffer;
1310                 Page            page;
1311                 PageHeader      header;
1312
1313                 buffer = ReadBuffer(index, blkno);
1314                 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1315
1316                 page = BufferGetPage(buffer);
1317                 header = (PageHeader)page;
1318
1319                 if (GinPageIsData(page))
1320                 {
1321                         dataPages++;
1322                         if (GinPageIsLeaf(page))
1323                         {
1324                                 ItemPointerData minItem;
1325                                 int nlist;
1326
1327                                 dataLeafPages++;
1328                                 dataLeafFreeSpace += header->pd_upper - header->pd_lower;
1329                                 ItemPointerSetMin(&minItem);
1330                                 pfree(GinDataLeafPageGetItems(page, &nlist, minItem));
1331                                 dataLeafIptrsCount += nlist;
1332                         }
1333                         else
1334                         {
1335                                 dataInnerPages++;
1336                                 dataInnerFreeSpace += header->pd_upper - header->pd_lower;
1337                                 dataInnerTuplesCount += GinPageGetOpaque(page)->maxoff;
1338                         }
1339                 }
1340                 else
1341                 {
1342                         IndexTuple itup;
1343                         OffsetNumber i, maxoff;
1344
1345                         maxoff = PageGetMaxOffsetNumber(page);
1346
1347                         entryPages++;
1348                         if (GinPageIsLeaf(page))
1349                         {
1350                                 entryLeafPages++;
1351                                 entryLeafFreeSpace += header->pd_upper - header->pd_lower;
1352                                 entryLeafTuplesCount += maxoff;
1353                         }
1354                         else
1355                         {
1356                                 entryInnerPages++;
1357                                 entryInnerFreeSpace += header->pd_upper - header->pd_lower;
1358                                 entryInnerTuplesCount += maxoff;
1359                         }
1360
1361                         for (i = 1; i <= maxoff; i++)
1362                         {
1363                                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
1364
1365                                 if (GinPageIsLeaf(page))
1366                                 {
1367                                         GinPostingList *list = (GinPostingList *)GinGetPosting(itup);
1368                                         entryPostingCount += GinGetNPosting(itup);
1369                                         entryPostingSize += SizeOfGinPostingList(list);
1370                                         entryAttrSize += GinGetPostingOffset(itup) - IndexInfoFindDataOffset((itup)->t_info);
1371                                 }
1372                                 else
1373                                 {
1374                                         entryAttrSize += IndexTupleSize(itup) - IndexInfoFindDataOffset((itup)->t_info);
1375                                 }
1376                         }
1377                 }
1378
1379                 UnlockReleaseBuffer(buffer);
1380         }
1381
1382         index_close(index, AccessExclusiveLock);
1383         totalPages--;
1384
1385         snprintf(res, sizeof(res),
1386                          "totalPages:            %u\n"
1387                          "dataPages:             %u\n"
1388                          "dataInnerPages:        %u\n"
1389                          "dataLeafPages:         %u\n"
1390                          "dataInnerFreeSpace:    " INT64_FORMAT "\n"
1391                          "dataLeafFreeSpace:     " INT64_FORMAT "\n"
1392                          "dataInnerTuplesCount:  " INT64_FORMAT "\n"
1393                          "dataLeafIptrsCount:    " INT64_FORMAT "\n"
1394                          "entryPages:            %u\n"
1395                          "entryInnerPages:       %u\n"
1396                          "entryLeafPages:        %u\n"
1397                          "entryInnerFreeSpace:   " INT64_FORMAT "\n"
1398                          "entryLeafFreeSpace:    " INT64_FORMAT "\n"
1399                          "entryInnerTuplesCount: " INT64_FORMAT "\n"
1400                          "entryLeafTuplesCount:  " INT64_FORMAT "\n"
1401                          "entryPostingSize:      " INT64_FORMAT "\n"
1402                          "entryPostingCount:     " INT64_FORMAT "\n"
1403                          "entryAttrSize:         " INT64_FORMAT "\n"
1404                          ,
1405                          totalPages,
1406                          dataPages,
1407                          dataInnerPages,
1408                          dataLeafPages,
1409                          dataInnerFreeSpace,
1410                          dataLeafFreeSpace,
1411                          dataInnerTuplesCount,
1412                          dataLeafIptrsCount,
1413                          entryPages,
1414                          entryInnerPages,
1415                          entryLeafPages,
1416                          entryInnerFreeSpace,
1417                          entryLeafFreeSpace,
1418                          entryInnerTuplesCount,
1419                          entryLeafTuplesCount,
1420                          entryPostingSize,
1421                          entryPostingCount,
1422                          entryAttrSize
1423                          );
1424
1425         PG_RETURN_TEXT_P(CStringGetTextDatum(res));
1426 #endif
1427 }
1428