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