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