menthion Daria as one of aouthors
[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/builtins.h"
35 #include "utils/lsyscache.h"
36 #include "utils/datum.h"
37 #include "utils/fmgroids.h"
38 #include <fmgr.h>
39 #include <funcapi.h>
40 #include <access/heapam.h>
41 #include <catalog/pg_type.h>
42 #include <access/relscan.h>
43 #if PG_VERSION_NUM >= 120000
44 #include <access/nbtree.h>
45 #include <access/brin.h>
46 #include <access/brin_revmap.h>
47 #include <access/brin_page.h>
48 #include <access/brin_tuple.h>
49 #endif
50
51
52 #define PAGESIZE        (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData)))
53
54 #ifndef PG_NARGS
55 #define PG_NARGS() (fcinfo->nargs)
56 #endif
57
58 #if PG_VERSION_NUM >= 90600
59 #define ISNULL          true
60 #define ISNOTNULL       false
61 #define heap_formtuple  heap_form_tuple
62 #else
63 #define ISNULL          'n'
64 #define ISNOTNULL       ' '
65 #endif
66
67 static char
68 *t2c(text* in) {
69         char *out=palloc(VARSIZE(in));
70
71         memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ);
72         out[ VARSIZE(in)-VARHDRSZ ] ='\0';
73         return out;
74 }
75
76 typedef struct {
77         int maxlevel;
78         text    *txt;
79         char    *ptr;
80         int             len;
81 } IdxInfo;
82
83 static Relation checkOpenedRelation(Relation r, Oid PgAmOid);
84
85 #ifdef PG_MODULE_MAGIC
86 /* >= 8.2 */
87
88 PG_MODULE_MAGIC;
89
90 static Relation
91 gist_index_open(RangeVar *relvar) {
92 #if PG_VERSION_NUM < 90200
93         Oid relOid = RangeVarGetRelid(relvar, false);
94 #else
95         Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
96 #endif
97         return checkOpenedRelation(
98                                 index_open(relOid, AccessExclusiveLock), GIST_AM_OID);
99 }
100
101 #define gist_index_close(r)     index_close((r), AccessExclusiveLock)
102
103 static Relation
104 gin_index_open(RangeVar *relvar) {
105 #if PG_VERSION_NUM < 90200
106         Oid relOid = RangeVarGetRelid(relvar, false);
107 #else
108         Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
109 #endif
110         return checkOpenedRelation(
111                                 index_open(relOid, AccessShareLock), GIN_AM_OID);
112 }
113
114 #define gin_index_close(r) index_close((r), AccessShareLock)
115
116 #if PG_VERSION_NUM >= 120000
117 static Relation
118 btree_index_open(RangeVar *relvar) {
119         Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
120         return checkOpenedRelation(
121                                 index_open(relOid, AccessExclusiveLock), BTREE_AM_OID);
122 }
123
124 #define btree_index_close(r)    index_close((r), AccessExclusiveLock)
125
126 static Relation
127 brin_index_open(RangeVar *relvar)
128 {
129         Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
130         return checkOpenedRelation(
131                                 index_open(relOid, AccessExclusiveLock), BRIN_AM_OID);
132 }
133
134 #define brin_index_close(r)     index_close((r), AccessExclusiveLock)
135 #endif
136
137 #else /* <8.2 */
138
139 static Relation
140 gist_index_open(RangeVar *relvar) {
141         Relation rel = index_openrv(relvar);
142
143         LockRelation(rel, AccessExclusiveLock);
144         return checkOpenedRelation(rel, GIST_AM_OID);
145 }
146
147 static void
148 gist_index_close(Relation rel) {
149         UnlockRelation(rel, AccessExclusiveLock);
150         index_close(rel);
151 }
152
153 static Relation
154 gin_index_open(RangeVar *relvar) {
155         Relation rel = index_openrv(relvar);
156
157         LockRelation(rel, AccessShareLock);
158         return checkOpenedRelation(rel, GIN_AM_OID);
159 }
160
161 static void
162 gin_index_close(Relation rel) {
163         UnlockRelation(rel, AccessShareLock);
164         index_close(rel);
165 }
166
167 static Relation
168 btree_index_open(RangeVar *relvar) {
169         Relation rel = index_openrv(relvar);
170
171         LockRelation(rel, AccessExclusiveLock);
172         return checkOpenedRelation(rel, BTREE_AM_OID);
173 }
174
175 static void
176 btree_index_close(Relation rel) {
177         UnlockRelation(rel, AccessExclusiveLock);
178         index_close(rel);
179 }
180
181 #endif
182
183 #if PG_VERSION_NUM >= 80300
184 #define stringToQualifiedNameList(x,y)  stringToQualifiedNameList(x)
185 #endif
186
187 #if PG_VERSION_NUM < 80300
188 #define SET_VARSIZE(p,l)        VARATT_SIZEP(p)=(l)
189 #endif
190
191 static Relation
192 checkOpenedRelation(Relation r, Oid PgAmOid) {
193         if ( r->rd_index == NULL )
194                 elog(ERROR, "Relation %s.%s is not an index",
195                                         get_namespace_name(RelationGetNamespace(r)),
196                                         RelationGetRelationName(r)
197                         );
198
199         if ( r->rd_rel->relam != PgAmOid )
200                 elog(ERROR, "Index %s.%s has wrong type",
201                                         get_namespace_name(RelationGetNamespace(r)),
202                                         RelationGetRelationName(r)
203                         );
204
205         return r;
206 }
207
208 static void
209 gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo *info) {
210         Buffer          buffer;
211         Page            page;
212         IndexTuple      which;
213         ItemId          iid;
214         OffsetNumber i,
215                                 maxoff;
216         BlockNumber cblk;
217         char       *pred;
218
219         pred = (char *) palloc(sizeof(char) * level * 4 + 1);
220         MemSet(pred, ' ', level*4);
221         pred[level*4] = '\0';
222
223         buffer = ReadBuffer(r, blk);
224         page = (Page) BufferGetPage(buffer);
225
226         maxoff = PageGetMaxOffsetNumber(page);
227
228
229         while ( (info->ptr-((char*)info->txt)) + level*4 + 128 >= info->len ) {
230                 int dist=info->ptr-((char*)info->txt);
231                 info->len *= 2;
232                 info->txt=(text*)repalloc(info->txt, info->len);
233                 info->ptr = ((char*)info->txt)+dist;
234         }
235
236         sprintf(info->ptr, "%s%d(l:%d) blk: %u numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n",
237                 pred,
238                 coff,
239                 level,
240                 blk,
241                 (int) maxoff,
242                 (int) PageGetFreeSpace(page),
243                 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE),
244                 GistPageGetOpaque(page)->rightlink,
245                 ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" );
246         info->ptr=strchr(info->ptr,'\0');
247
248         if (!GistPageIsLeaf(page) && ( info->maxlevel<0 || level<info->maxlevel ) )
249                 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
250                         iid = PageGetItemId(page, i);
251                         which = (IndexTuple) PageGetItem(page, iid);
252                         cblk = ItemPointerGetBlockNumber(&(which->t_tid));
253                         gist_dumptree(r, level + 1, cblk, i, info);
254                 }
255         ReleaseBuffer(buffer);
256         pfree(pred);
257 }
258
259 PG_FUNCTION_INFO_V1(gist_tree);
260 Datum   gist_tree(PG_FUNCTION_ARGS);
261 Datum
262 gist_tree(PG_FUNCTION_ARGS) {
263         text    *name=PG_GETARG_TEXT_P(0);
264         char *relname=t2c(name);
265         RangeVar   *relvar;
266         Relation                index;
267         List       *relname_list;
268         IdxInfo info;
269
270         relname_list = stringToQualifiedNameList(relname, "gist_tree");
271         relvar = makeRangeVarFromNameList(relname_list);
272         index = gist_index_open(relvar);
273         PG_FREE_IF_COPY(name,0);
274
275         info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1;
276         info.len=1024;
277         info.txt=(text*)palloc( info.len );
278         info.ptr=((char*)info.txt)+VARHDRSZ;
279
280         gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0, &info);
281
282         gist_index_close(index);
283         pfree(relname);
284
285         SET_VARSIZE(info.txt, info.ptr-((char*)info.txt));
286         PG_RETURN_POINTER(info.txt);
287 }
288
289 typedef struct {
290         int             level;
291         int             numpages;
292         int             numleafpages;
293         int             numtuple;
294         int             numinvalidtuple;
295         int             numleaftuple;
296         uint64  tuplesize;
297         uint64  leaftuplesize;
298         uint64  totalsize;
299 } IdxStat;
300
301 static void
302 gist_stattree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxStat *info) {
303         Buffer          buffer;
304         Page            page;
305         IndexTuple      which;
306         ItemId          iid;
307         OffsetNumber i,
308                                 maxoff;
309         BlockNumber cblk;
310         char       *pred;
311
312         pred = (char *) palloc(sizeof(char) * level * 4 + 1);
313         MemSet(pred, ' ', level*4);
314         pred[level*4] = '\0';
315
316         buffer = ReadBuffer(r, blk);
317         page = (Page) BufferGetPage(buffer);
318
319         maxoff = PageGetMaxOffsetNumber(page);
320
321         info->numpages++;
322         info->tuplesize+=PAGESIZE-PageGetFreeSpace(page);
323         info->totalsize+=BLCKSZ;
324         info->numtuple+=maxoff;
325         if ( info->level < level )
326                 info->level = level;
327
328         if (GistPageIsLeaf(page)) {
329                 info->numleafpages++;
330                 info->leaftuplesize+=PAGESIZE-PageGetFreeSpace(page);
331                 info->numleaftuple+=maxoff;
332         } else {
333                 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
334                         iid = PageGetItemId(page, i);
335                         which = (IndexTuple) PageGetItem(page, iid);
336                         if ( GistTupleIsInvalid(which) )
337                                 info->numinvalidtuple++;
338                         cblk = ItemPointerGetBlockNumber(&(which->t_tid));
339                                 gist_stattree(r, level + 1, cblk, i, info);
340                 }
341         }
342
343         ReleaseBuffer(buffer);
344         pfree(pred);
345 }
346
347 PG_FUNCTION_INFO_V1(gist_stat);
348 Datum   gist_stat(PG_FUNCTION_ARGS);
349 Datum
350 gist_stat(PG_FUNCTION_ARGS) {
351         text    *name=PG_GETARG_TEXT_P(0);
352         char *relname=t2c(name);
353         RangeVar   *relvar;
354         Relation                index;
355         List       *relname_list;
356         IdxStat info;
357         text *out=(text*)palloc(1024);
358         char *ptr=((char*)out)+VARHDRSZ;
359
360
361         relname_list = stringToQualifiedNameList(relname, "gist_tree");
362         relvar = makeRangeVarFromNameList(relname_list);
363         index = gist_index_open(relvar);
364         PG_FREE_IF_COPY(name,0);
365
366         memset(&info, 0, sizeof(IdxStat));
367
368         gist_stattree(index, 0, GIST_ROOT_BLKNO, 0, &info);
369
370         gist_index_close(index);
371         pfree(relname);
372
373         sprintf(ptr,
374                 "Number of levels:                %d\n"
375                 "Number of pages:                  %d\n"
376                 "Number of leaf pages:    %d\n"
377                 "Number of tuples:                %d\n"
378                 "Number of invalid tuples:  %d\n"
379                 "Number of leaf tuples:  %d\n"
380                 "Total size of tuples:    "INT64_FORMAT" bytes\n"
381                 "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
382                 "Total size of index:      "INT64_FORMAT" bytes\n",
383                 info.level+1,
384                 info.numpages,
385                 info.numleafpages,
386                 info.numtuple,
387                 info.numinvalidtuple,
388                 info.numleaftuple,
389                 info.tuplesize,
390                 info.leaftuplesize,
391                 info.totalsize);
392
393         ptr=strchr(ptr,'\0');
394
395         SET_VARSIZE(out, ptr-((char*)out));
396         PG_RETURN_POINTER(out);
397 }
398
399 typedef struct GPItem {
400         Buffer  buffer;
401         Page    page;
402         OffsetNumber    offset;
403         int     level;
404         struct GPItem *next;
405 } GPItem;
406
407 typedef struct {
408         List    *relname_list;
409         RangeVar   *relvar;
410         Relation                index;
411         Datum   *dvalues;
412 #if PG_VERSION_NUM >= 90600
413         bool    *nulls;
414 #else
415         char    *nulls;
416 #endif
417         GPItem  *item;
418 } TypeStorage;
419
420 static GPItem*
421 openGPPage( FuncCallContext *funcctx, BlockNumber blk ) {
422         GPItem  *nitem;
423         MemoryContext    oldcontext;
424         Relation index = ( (TypeStorage*)(funcctx->user_fctx) )->index;
425
426         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
427         nitem = (GPItem*)palloc( sizeof(GPItem) );
428         memset(nitem,0,sizeof(GPItem));
429
430         nitem->buffer = ReadBuffer(index, blk);
431         nitem->page = (Page) BufferGetPage(nitem->buffer);
432         nitem->offset=FirstOffsetNumber;
433         nitem->next = ( (TypeStorage*)(funcctx->user_fctx) )->item;
434         nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1;
435         ( (TypeStorage*)(funcctx->user_fctx) )->item = nitem;
436
437         MemoryContextSwitchTo(oldcontext);
438         return nitem;
439 }
440
441 static GPItem*
442 closeGPPage( FuncCallContext *funcctx ) {
443         GPItem  *oitem = ( (TypeStorage*)(funcctx->user_fctx) )->item;
444
445         ( (TypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
446
447         ReleaseBuffer(oitem->buffer);
448         pfree( oitem );
449         return ( (TypeStorage*)(funcctx->user_fctx) )->item;
450 }
451
452 #if PG_VERSION_NUM >= 110000
453 #define TS_GET_TYPEVAL(s, i, v) (s)->index->rd_att->attrs[(i)].v
454 #else
455 #define TS_GET_TYPEVAL(s, i, v) (s)->index->rd_att->attrs[(i)]->v
456 #endif
457
458 static void
459 setup_firstcall(FuncCallContext  *funcctx, text *name) {
460         MemoryContext    oldcontext;
461         TypeStorage      *st;
462         char *relname=t2c(name);
463         TupleDesc                       tupdesc;
464         char                    attname[NAMEDATALEN];
465         int i;
466
467         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
468
469         st=(TypeStorage*)palloc( sizeof(TypeStorage) );
470         memset(st,0,sizeof(TypeStorage));
471         st->relname_list = stringToQualifiedNameList(relname, "gist_tree");
472         st->relvar = makeRangeVarFromNameList(st->relname_list);
473         st->index = gist_index_open(st->relvar);
474         funcctx->user_fctx = (void*)st;
475
476 #if PG_VERSION_NUM >= 120000
477         tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2);
478 #else
479         tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ +  2 /* tids */ + 1, false);
480 #endif
481         TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
482         TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
483         for (i = 0; i < st->index->rd_att->natts; i++) {
484                 sprintf(attname, "z%d", i+2);
485                 TupleDescInitEntry(
486                         tupdesc,
487                         i+3,
488                         attname,
489                         TS_GET_TYPEVAL(st, i, atttypid),
490                         TS_GET_TYPEVAL(st, i, atttypmod),
491                         TS_GET_TYPEVAL(st, i, attndims)
492                 );
493         }
494
495         st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
496         st->nulls = palloc((tupdesc->natts+2) * sizeof(*st->nulls));
497
498 #if PG_VERSION_NUM >= 120000
499         funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
500 #else
501         funcctx->slot = TupleDescGetSlot(tupdesc);
502 #endif
503
504         MemoryContextSwitchTo(oldcontext);
505         pfree(relname);
506
507         st->item=openGPPage(funcctx, GIST_ROOT_BLKNO);
508 }
509
510 static void
511 close_call( FuncCallContext  *funcctx ) {
512         TypeStorage *st = (TypeStorage*)(funcctx->user_fctx);
513
514         while(st->item && closeGPPage(funcctx));
515
516         pfree(st->dvalues);
517         pfree(st->nulls);
518
519         gist_index_close(st->index);
520 }
521
522 PG_FUNCTION_INFO_V1(gist_print);
523 Datum   gist_print(PG_FUNCTION_ARGS);
524 Datum
525 gist_print(PG_FUNCTION_ARGS) {
526         FuncCallContext  *funcctx;
527         TypeStorage      *st;
528         Datum result=(Datum)0;
529         ItemId            iid;
530         IndexTuple        ituple;
531         HeapTuple          htuple;
532         int i;
533         bool isnull;
534
535         if (SRF_IS_FIRSTCALL()) {
536                 text    *name=PG_GETARG_TEXT_P(0);
537                 funcctx = SRF_FIRSTCALL_INIT();
538                 setup_firstcall(funcctx, name);
539                 PG_FREE_IF_COPY(name,0);
540         }
541
542         funcctx = SRF_PERCALL_SETUP();
543         st = (TypeStorage*)(funcctx->user_fctx);
544
545         if ( !st->item ) {
546                 close_call(funcctx);
547                 SRF_RETURN_DONE(funcctx);
548         }
549
550         while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) ) {
551                 if ( ! closeGPPage(funcctx) ) {
552                         close_call(funcctx);
553                         SRF_RETURN_DONE(funcctx);
554                 }
555         }
556
557         iid = PageGetItemId( st->item->page, st->item->offset );
558         ituple = (IndexTuple) PageGetItem(st->item->page, iid);
559
560         st->dvalues[0] = Int32GetDatum( st->item->level );
561         st->nulls[0] = ISNOTNULL;
562         st->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true );
563         st->nulls[1] = ISNOTNULL;
564         for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++)
565         {
566                 if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) )
567                 {
568                         st->dvalues[i] = (Datum)0;
569                         st->nulls[i] = ISNULL;
570                 } else {
571                         st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull);
572                         st->nulls[i] = ( isnull ) ? ISNULL : ISNOTNULL;
573                 }
574         }
575
576         htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
577 #if PG_VERSION_NUM >= 120000
578         result = HeapTupleGetDatum(htuple);
579 #else
580         result = TupleGetDatum(funcctx->slot, htuple);
581 #endif
582         st->item->offset = OffsetNumberNext(st->item->offset);
583         if ( !GistPageIsLeaf(st->item->page) )
584                 openGPPage(funcctx, ItemPointerGetBlockNumber(&(ituple->t_tid)) );
585
586         SRF_RETURN_NEXT(funcctx, result);
587 }
588
589 typedef struct GinStatState {
590         Relation                index;
591         GinState                ginstate;
592         OffsetNumber    attnum;
593
594         Buffer                  buffer;
595         OffsetNumber    offset;
596         Datum                   curval;
597 #if PG_VERSION_NUM >= 90100
598         GinNullCategory category;
599 #endif
600         Datum                   dvalues[2];
601 #if PG_VERSION_NUM >= 110000
602         bool                    nulls[2];
603 #else
604         char                    nulls[2];
605 #endif
606 } GinStatState;
607
608 static bool
609 moveRightIfItNeeded( GinStatState *st )
610 {
611         Page page = BufferGetPage(st->buffer);
612
613         if ( st->offset > PageGetMaxOffsetNumber(page) ) {
614                 /*
615                 * We scaned the whole page, so we should take right page
616                 */
617                 BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
618
619                 if ( GinPageRightMost(page) )
620                         return false;  /* no more page */
621
622                 LockBuffer(st->buffer, GIN_UNLOCK);
623                 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
624                 LockBuffer(st->buffer, GIN_SHARE);
625                 st->offset = FirstOffsetNumber;
626         }
627
628         return true;
629 }
630
631 /*
632  * Refinds a previois position, at returns it has correctly
633  * set offset and buffer is locked
634  */
635 static bool
636 refindPosition(GinStatState *st)
637 {
638         Page    page;
639
640         /* find left if needed (it causes only for first search) */
641         for (;;) {
642                 IndexTuple  itup;
643                 BlockNumber blkno;
644
645                 LockBuffer(st->buffer, GIN_SHARE);
646
647                 page = BufferGetPage(st->buffer);
648                 if (GinPageIsLeaf(page))
649                         break;
650
651                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
652                 blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid);
653
654                 LockBuffer(st->buffer,GIN_UNLOCK);
655                 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
656         }
657
658         if (st->offset == InvalidOffsetNumber) {
659                 return (PageGetMaxOffsetNumber(page) >= FirstOffsetNumber ) ? true : false; /* first one */
660         }
661
662         for(;;) {
663                 int                             cmp;
664 #if PG_VERSION_NUM >= 90100
665                 GinNullCategory category;
666 #elif PG_VERSION_NUM < 80400
667                 bool                    isnull = false;
668 #endif
669                 Datum                   datum;
670                 IndexTuple              itup;
671
672                 if (moveRightIfItNeeded(st)==false)
673                         return false;
674
675                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
676 #if PG_VERSION_NUM >= 90100
677                 datum = gintuple_get_key(&st->ginstate, itup, &category);
678                 cmp = ginCompareAttEntries(&st->ginstate,
679                                                                         st->attnum + 1, st->curval, st->category,
680                                                                         gintuple_get_attrnum(&st->ginstate, itup), datum, category);
681 #elif PG_VERSION_NUM >= 80400
682                 datum = gin_index_getattr(&st->ginstate, itup);
683
684                 cmp = compareAttEntries(&st->ginstate,
685                                                                         st->attnum + 1, st->curval,
686                                                                         gintuple_get_attrnum(&st->ginstate, itup), datum);
687 #else
688                 datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull);
689
690                 cmp = DatumGetInt32(
691                                 FunctionCall2(
692                                                 &st->ginstate.compareFn,
693                                                 st->curval,
694                                                 datum
695                                         ));
696 #endif
697                 if ( cmp == 0 )
698                 {
699                         if ( st->curval && !TS_GET_TYPEVAL(st, st->attnum, attbyval) )
700                                 pfree( (void*) st->curval );
701                         return true;
702                 }
703
704                 st->offset++;
705         }
706
707         return false;
708 }
709
710 static void
711 gin_setup_firstcall(FuncCallContext  *funcctx, text *name, int attnum) {
712         MemoryContext    oldcontext;
713         GinStatState     *st;
714         char *relname=t2c(name);
715         TupleDesc                       tupdesc;
716
717         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
718
719         st=(GinStatState*)palloc( sizeof(GinStatState) );
720         memset(st,0,sizeof(GinStatState));
721         st->index = gin_index_open(
722                  makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_stat")));
723         initGinState( &st->ginstate, st->index );
724
725 #if PG_VERSION_NUM >= 80400
726         if (attnum < 0 || attnum >= st->index->rd_att->natts)
727                 elog(ERROR,"Wrong column's number");
728         st->attnum = attnum;
729 #else
730         st->attnum = 0;
731 #endif
732
733         funcctx->user_fctx = (void*)st;
734
735 #if PG_VERSION_NUM >= 120000
736         tupdesc = CreateTemplateTupleDesc(2);
737 #else
738         tupdesc = CreateTemplateTupleDesc(2, false);
739 #endif
740         TupleDescInitEntry(tupdesc, 1, "value",
741                         TS_GET_TYPEVAL(st, st->attnum, atttypid),
742                         TS_GET_TYPEVAL(st, st->attnum, atttypmod),
743                         TS_GET_TYPEVAL(st, st->attnum, attndims));
744         TupleDescInitEntry(tupdesc, 2, "nrow", INT4OID, -1, 0);
745
746         memset( st->nulls, ISNOTNULL, 2*sizeof(*st->nulls) );
747
748 #if PG_VERSION_NUM >= 120000
749         funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
750 #else
751         funcctx->slot = TupleDescGetSlot(tupdesc);
752 #endif
753
754         MemoryContextSwitchTo(oldcontext);
755         pfree(relname);
756
757         st->offset = InvalidOffsetNumber;
758         st->buffer = ReadBuffer( st->index, GIN_ROOT_BLKNO );
759 }
760
761 static void
762 processTuple( FuncCallContext  *funcctx,  GinStatState *st, IndexTuple itup ) {
763         MemoryContext           oldcontext;
764 #if PG_VERSION_NUM >= 90100
765         Datum                           key;
766 #elif PG_VERSION_NUM < 80400
767         bool                            isnull;
768 #endif
769
770         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
771
772 #if PG_VERSION_NUM >= 90100
773         key = gintuple_get_key(&st->ginstate, itup, &st->category);
774
775         if (st->category != GIN_CAT_NORM_KEY)
776                 st->curval = (Datum)0;
777         else
778 #endif
779         st->curval = datumCopy(
780 #if PG_VERSION_NUM >= 90100
781                                         key,
782 #elif PG_VERSION_NUM >= 80400
783                                         gin_index_getattr(&st->ginstate, itup),
784 #else
785                                         index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull),
786 #endif
787                                         TS_GET_TYPEVAL(st, st->attnum, attbyval),
788                                         TS_GET_TYPEVAL(st, st->attnum, attlen));
789         MemoryContextSwitchTo(oldcontext);
790
791         st->dvalues[0] = st->curval;
792 #if PG_VERSION_NUM >= 90100
793         /* do no distiguish various null category */
794         st->nulls[0] = (st->category == GIN_CAT_NORM_KEY) ? ISNOTNULL : ISNULL;
795 #endif
796
797         if ( GinIsPostingTree(itup) ) {
798                 BlockNumber     rootblkno = GinGetPostingTree(itup);
799 #if PG_VERSION_NUM >= 90400
800                 GinBtreeData    btree;
801                 GinBtreeStack   *stack;
802                 ItemPointerData minItem;
803                 int                             nlist;
804                 ItemPointer             list;
805 #else
806                 GinPostingTreeScan *gdi;
807                 Buffer                  entrybuffer;
808 #endif
809                 Page            page;
810                 uint32          predictNumber;
811
812                 LockBuffer(st->buffer, GIN_UNLOCK);
813 #if PG_VERSION_NUM >= 90400
814                 stack = ginScanBeginPostingTree(&btree, st->index, rootblkno
815 #if PG_VERSION_NUM >= 90600
816                                                                                 , NULL
817 #endif
818                                                                                 );
819                 page = BufferGetPage(stack->buffer);
820                 ItemPointerSetMin(&minItem);
821                 list = GinDataLeafPageGetItems(page, &nlist, minItem);
822                 pfree(list);
823                 predictNumber = stack->predictNumber;
824                 st->dvalues[1] = Int32GetDatum( predictNumber * nlist );
825 #elif PG_VERSION_NUM >= 90100
826                 gdi = ginPrepareScanPostingTree(st->index, rootblkno, TRUE);
827                 entrybuffer = ginScanBeginPostingTree(gdi);
828                 page = BufferGetPage(entrybuffer);
829                 predictNumber = gdi->stack->predictNumber;
830                 st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
831 #else
832                 gdi = prepareScanPostingTree(st->index, rootblkno, TRUE);
833                 entrybuffer = scanBeginPostingTree(gdi);
834                 page = BufferGetPage(entrybuffer);
835                 predictNumber = gdi->stack->predictNumber;
836                 st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
837 #endif
838
839 #if PG_VERSION_NUM < 90400
840                 LockBuffer(entrybuffer, GIN_UNLOCK);
841                 freeGinBtreeStack(gdi->stack);
842                 pfree(gdi);
843 #else
844                 LockBuffer(stack->buffer, GIN_UNLOCK);
845                 freeGinBtreeStack(stack);
846 #endif
847         } else {
848                 st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) );
849                 LockBuffer(st->buffer, GIN_UNLOCK);
850         }
851 }
852
853 PG_FUNCTION_INFO_V1(gin_stat);
854 Datum   gin_stat(PG_FUNCTION_ARGS);
855 Datum
856 gin_stat(PG_FUNCTION_ARGS) {
857         FuncCallContext  *funcctx;
858         GinStatState     *st;
859         Datum result=(Datum)0;
860         IndexTuple        ituple;
861         HeapTuple          htuple;
862         Page page;
863
864         if (SRF_IS_FIRSTCALL()) {
865                 text    *name=PG_GETARG_TEXT_P(0);
866                 funcctx = SRF_FIRSTCALL_INIT();
867                 gin_setup_firstcall(funcctx, name, (PG_NARGS()==2) ? PG_GETARG_INT32(1) : 0 );
868                 PG_FREE_IF_COPY(name,0);
869         }
870
871         funcctx = SRF_PERCALL_SETUP();
872         st = (GinStatState*)(funcctx->user_fctx);
873
874         if ( refindPosition(st) == false ) {
875                 UnlockReleaseBuffer( st->buffer );
876                 gin_index_close(st->index);
877
878                 SRF_RETURN_DONE(funcctx);
879         }
880
881         for(;;) {
882                 st->offset++;
883
884                 if (moveRightIfItNeeded(st)==false) {
885                         UnlockReleaseBuffer( st->buffer );
886                         gin_index_close(st->index);
887
888                         SRF_RETURN_DONE(funcctx);
889                 }
890
891                 page = BufferGetPage(st->buffer);
892                 ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
893
894 #if PG_VERSION_NUM >= 80400
895                 if (st->attnum + 1 == gintuple_get_attrnum(&st->ginstate, ituple))
896 #endif
897                         break;
898         }
899
900         processTuple( funcctx,  st, ituple );
901
902         htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
903 #if PG_VERSION_NUM >= 120000
904         result = HeapTupleGetDatum(htuple);
905 #else
906         result = TupleGetDatum(funcctx->slot, htuple);
907 #endif
908         
909         SRF_RETURN_NEXT(funcctx, result);
910 }
911
912 PG_FUNCTION_INFO_V1(gin_count_estimate);
913 Datum gin_count_estimate(PG_FUNCTION_ARGS);
914 #if PG_VERSION_NUM >= 80300
915 Datum
916 gin_count_estimate(PG_FUNCTION_ARGS) {
917         text                    *name=PG_GETARG_TEXT_P(0);
918         Relation                index;
919         IndexScanDesc   scan;
920         int64                   count = 0;
921         char                    *relname=t2c(name);
922         ScanKeyData             key;
923 #if PG_VERSION_NUM >= 80400
924         TIDBitmap               *bitmap = tbm_create(work_mem * 1024L
925 #if PG_VERSION_NUM >= 100000
926                                                                                  , NULL
927 #endif
928                                                                                  );
929 #else
930 #define MAXTIDS         1024
931         ItemPointerData tids[MAXTIDS];
932         int32                   returned_tids;
933         bool                    more;
934 #endif
935
936         index = gin_index_open(
937                  makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_count_estimate")));
938
939         if ( index->rd_opcintype[0] != TSVECTOROID ) {
940                 gin_index_close(index);
941                 elog(ERROR, "Column type is not a tsvector");
942         }
943
944         key.sk_flags = 0;
945         key.sk_attno = 1;
946         key.sk_strategy = TSearchStrategyNumber;
947         key.sk_subtype  = 0;
948         key.sk_argument = PG_GETARG_DATUM(1);
949
950         fmgr_info( F_TS_MATCH_VQ , &key.sk_func );
951
952 #if PG_VERSION_NUM >= 90100
953 #if PG_VERSION_NUM >= 90400
954         scan = index_beginscan_bitmap(index, SnapshotSelf, 1);
955 #else
956         scan = index_beginscan_bitmap(index, SnapshotNow, 1);
957 #endif
958         index_rescan(scan, &key, 1, NULL, 0);
959
960         count = index_getbitmap(scan, bitmap);
961         tbm_free(bitmap);
962 #elif PG_VERSION_NUM >= 80400
963         scan = index_beginscan_bitmap(index, SnapshotNow, 1, &key);
964
965         count = index_getbitmap(scan, bitmap);
966         tbm_free(bitmap);
967 #else
968         scan = index_beginscan_multi(index, SnapshotNow, 1, &key);
969
970         do {
971                 more = index_getmulti(scan, tids, MAXTIDS, &returned_tids);
972                 count += returned_tids;
973         } while(more);
974 #endif
975
976         index_endscan( scan );
977         gin_index_close(index);
978
979         PG_RETURN_INT64(count);
980 }
981 #else
982 Datum
983 gin_count_estimate(PG_FUNCTION_ARGS) {
984         elog(NOTICE, "Function is not working under PgSQL < 8.3");
985
986         PG_RETURN_INT64(0);
987 }
988 #endif
989
990 PG_FUNCTION_INFO_V1(spgist_stat);
991 Datum spgist_stat(PG_FUNCTION_ARGS);
992 Datum
993 spgist_stat(PG_FUNCTION_ARGS)
994 {
995 #if PG_VERSION_NUM < 90200
996         elog(NOTICE, "Function is not working under PgSQL < 9.2");
997
998         PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
999 #else
1000         text       *name = PG_GETARG_TEXT_P(0);
1001         RangeVar   *relvar;
1002         Relation        index;
1003         BlockNumber blkno;
1004         BlockNumber totalPages = 0,
1005                                 innerPages = 0,
1006                                 leafPages = 0,
1007                                 emptyPages = 0,
1008                                 deletedPages = 0;
1009         double    usedSpace = 0.0,
1010                           usedLeafSpace = 0.0,
1011                           usedInnerSpace = 0.0;
1012         char            res[1024];
1013         int              bufferSize = -1;
1014         int64      innerTuples = 0,
1015                                 leafTuples = 0,
1016                                 nAllTheSame = 0,
1017                                 nLeafPlaceholder = 0,
1018                                 nInnerPlaceholder = 0,
1019                                 nLeafRedirect = 0,
1020                                 nInnerRedirect = 0;
1021
1022 #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
1023 #define IS_SPGIST(r) ((r)->rd_rel->relam == SPGIST_AM_OID)
1024
1025         relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1026         index = relation_openrv(relvar, AccessExclusiveLock);
1027
1028         if (!IS_INDEX(index) || !IS_SPGIST(index))
1029                 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1030                          RelationGetRelationName(index));
1031
1032         totalPages = RelationGetNumberOfBlocks(index);
1033
1034         for (blkno = SPGIST_ROOT_BLKNO; blkno < totalPages; blkno++)
1035         {
1036                 Buffer    buffer;
1037                 Page            page;
1038                 int              pageFree;
1039
1040                 buffer = ReadBuffer(index, blkno);
1041                 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1042
1043                 page = BufferGetPage(buffer);
1044
1045                 if (PageIsNew(page) || SpGistPageIsDeleted(page))
1046                 {
1047                         deletedPages++;
1048                         UnlockReleaseBuffer(buffer);
1049                         continue;
1050                 }
1051
1052                 if (SpGistPageIsLeaf(page))
1053                 {
1054                         leafPages++;
1055                         leafTuples += PageGetMaxOffsetNumber(page);
1056                         nLeafPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
1057                         nLeafRedirect += SpGistPageGetOpaque(page)->nRedirection;
1058                 }
1059                 else
1060                 {
1061                         int      i,
1062                                         max;
1063
1064                         innerPages++;
1065                         max = PageGetMaxOffsetNumber(page);
1066                         innerTuples += max;
1067                         nInnerPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
1068                         nInnerRedirect += SpGistPageGetOpaque(page)->nRedirection;
1069                         for (i = FirstOffsetNumber; i <= max; i++)
1070                         {
1071                                 SpGistInnerTuple it;
1072
1073                                 it = (SpGistInnerTuple) PageGetItem(page,
1074                                                                                                         PageGetItemId(page, i));
1075                                 if (it->allTheSame)
1076                                         nAllTheSame++;
1077                         }
1078                 }
1079
1080                 if (bufferSize < 0)
1081                         bufferSize = BufferGetPageSize(buffer)
1082                                 - MAXALIGN(sizeof(SpGistPageOpaqueData))
1083                                 - SizeOfPageHeaderData;
1084
1085                 pageFree = PageGetExactFreeSpace(page);
1086
1087                 usedSpace += bufferSize - pageFree;
1088                 if (SpGistPageIsLeaf(page))
1089                         usedLeafSpace += bufferSize - pageFree;
1090                 else
1091                         usedInnerSpace += bufferSize - pageFree;
1092
1093                 if (pageFree == bufferSize)
1094                         emptyPages++;
1095
1096                 UnlockReleaseBuffer(buffer);
1097         }
1098
1099         index_close(index, AccessExclusiveLock);
1100
1101         totalPages--;                      /* discount metapage */
1102
1103         snprintf(res, sizeof(res),
1104                          "totalPages:           %u\n"
1105                          "deletedPages:   %u\n"
1106                          "innerPages:           %u\n"
1107                          "leafPages:             %u\n"
1108                          "emptyPages:           %u\n"
1109                          "usedSpace:             %.2f kbytes\n"
1110                          "usedInnerSpace:       %.2f kbytes\n"
1111                          "usedLeafSpace:         %.2f kbytes\n"
1112                          "freeSpace:             %.2f kbytes\n"
1113                          "fillRatio:             %.2f%%\n"
1114                          "leafTuples:           " INT64_FORMAT "\n"
1115                          "innerTuples:     " INT64_FORMAT "\n"
1116                          "innerAllTheSame:   " INT64_FORMAT "\n"
1117                          "leafPlaceholders:  " INT64_FORMAT "\n"
1118                          "innerPlaceholders: " INT64_FORMAT "\n"
1119                          "leafRedirects:         " INT64_FORMAT "\n"
1120                          "innerRedirects:       " INT64_FORMAT,
1121                          totalPages, deletedPages, innerPages, leafPages, emptyPages,
1122                          usedSpace / 1024.0,
1123                          usedInnerSpace / 1024.0,
1124                          usedLeafSpace / 1024.0,
1125                          (((double) bufferSize) * ((double) totalPages) - usedSpace) / 1024,
1126                          100.0 * (usedSpace / (((double) bufferSize) * ((double) totalPages))),
1127                          leafTuples, innerTuples, nAllTheSame,
1128                          nLeafPlaceholder, nInnerPlaceholder,
1129                          nLeafRedirect, nInnerRedirect);
1130
1131         PG_RETURN_TEXT_P(CStringGetTextDatum(res));
1132 #endif
1133 }
1134
1135 #if PG_VERSION_NUM >= 90200
1136
1137 typedef struct SPGistPrintStackElem {
1138         ItemPointerData         iptr;
1139         int16                           nlabel;
1140         int                                     level;
1141 } SPGistPrintStackElem;
1142
1143 typedef struct SPGistPrint {
1144         SpGistState     state;
1145         Relation        index;
1146         Datum           dvalues[8 /* see CreateTemplateTupleDesc call */];
1147 #if PG_VERSION_NUM >= 110000
1148         bool                    nulls[8];
1149 #else
1150         char                    nulls[8];
1151 #endif
1152         List            *stack;
1153 } SPGistPrint;
1154
1155 static void
1156 pushSPGistPrint(FuncCallContext *funcctx, SPGistPrint *prst, ItemPointer ip, int level) {
1157         MemoryContext   oldcontext;
1158         SPGistPrintStackElem    *e;
1159
1160         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1161
1162         e = palloc(sizeof(*e));
1163         e->iptr = *ip;
1164         e->nlabel = 0;
1165         e->level = level;
1166         prst->stack = lcons(e, prst->stack);
1167
1168         MemoryContextSwitchTo(oldcontext);
1169 }
1170
1171 static void
1172 close_spgist_print(SPGistPrint *prst) {
1173         index_close(prst->index, AccessExclusiveLock);
1174 }
1175 #endif
1176
1177 PG_FUNCTION_INFO_V1(spgist_print);
1178 Datum spgist_print(PG_FUNCTION_ARGS);
1179 Datum
1180 spgist_print(PG_FUNCTION_ARGS)
1181 {
1182 #if PG_VERSION_NUM < 90200
1183         elog(NOTICE, "Function is not working under PgSQL < 9.2");
1184
1185         PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1186 #else
1187         FuncCallContext                 *funcctx;
1188         SPGistPrint                             *prst;
1189         SPGistPrintStackElem    *s;
1190         HeapTuple                               htuple;
1191         Datum                                   result;
1192         MemoryContext                   oldcontext;
1193
1194         if (SRF_IS_FIRSTCALL()) {
1195                 text                    *name=PG_GETARG_TEXT_P(0);
1196                 RangeVar                *relvar;
1197                 Relation                index;
1198                 ItemPointerData ipd;
1199                 TupleDesc               tupdesc;
1200
1201                 funcctx = SRF_FIRSTCALL_INIT();
1202                 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1203                 index = relation_openrv(relvar, AccessExclusiveLock);
1204
1205                 if (!IS_INDEX(index) || !IS_SPGIST(index))
1206                         elog(ERROR, "relation \"%s\" is not an SPGiST index",
1207                                  RelationGetRelationName(index));
1208
1209                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1210
1211                 prst = palloc(sizeof(*prst));
1212
1213                 prst->index = index;
1214                 initSpGistState(&prst->state, index);
1215
1216 #if PG_VERSION_NUM >= 120000
1217                 tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ +  2 /* tids */ + 1);
1218 #else
1219                 tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ +  2 /* tids */ + 1, false);
1220 #endif
1221                 TupleDescInitEntry(tupdesc, 1, "tid", TIDOID, -1, 0);
1222                 TupleDescInitEntry(tupdesc, 2, "allthesame", BOOLOID, -1, 0);
1223                 TupleDescInitEntry(tupdesc, 3, "node", INT4OID, -1, 0);
1224                 TupleDescInitEntry(tupdesc, 4, "level", INT4OID, -1, 0);
1225                 TupleDescInitEntry(tupdesc, 5, "tid_pointer", TIDOID, -1, 0);
1226                 TupleDescInitEntry(tupdesc, 6, "prefix",
1227                                 (prst->state.attPrefixType.type == VOIDOID) ? INT4OID : prst->state.attPrefixType.type, -1, 0);
1228                 TupleDescInitEntry(tupdesc, 7, "label",
1229                                 (prst->state.attLabelType.type == VOIDOID) ? INT4OID : prst->state.attLabelType.type, -1, 0);
1230                 TupleDescInitEntry(tupdesc, 8, "leaf",
1231                                 (prst->state.attType.type == VOIDOID) ? INT4OID : prst->state.attType.type, -1, 0);
1232
1233 #if PG_VERSION_NUM >= 120000
1234                 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1235 #else
1236                 funcctx->slot = TupleDescGetSlot(tupdesc);
1237 #endif
1238
1239                 funcctx->user_fctx = (void*)prst;
1240
1241                 MemoryContextSwitchTo(oldcontext);
1242
1243                 ItemPointerSet(&ipd, SPGIST_ROOT_BLKNO, FirstOffsetNumber);
1244                 prst->stack = NIL;
1245                 pushSPGistPrint(funcctx, prst, &ipd, 1);
1246
1247                 PG_FREE_IF_COPY(name,0);
1248         }
1249
1250         funcctx = SRF_PERCALL_SETUP();
1251         prst = (SPGistPrint*)(funcctx->user_fctx);
1252
1253 next:
1254         for(;;) {
1255                 if ( prst->stack == NIL ) {
1256                         close_spgist_print(prst);
1257                         SRF_RETURN_DONE(funcctx);
1258                 }
1259
1260                 CHECK_FOR_INTERRUPTS();
1261
1262                 s = (SPGistPrintStackElem*)linitial(prst->stack);
1263                 prst->stack = list_delete_first(prst->stack);
1264
1265                 if (ItemPointerIsValid(&s->iptr))
1266                         break;
1267                 free(s);
1268         }
1269
1270         do {
1271                 Buffer                          buffer;
1272                 Page                            page;
1273                 SpGistDeadTuple         dtuple;
1274                 ItemPointer                     tid;
1275
1276                 buffer = ReadBuffer(prst->index, ItemPointerGetBlockNumber(&s->iptr));
1277                 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1278
1279                 page = BufferGetPage(buffer);
1280                 if (ItemPointerGetOffsetNumber(&s->iptr) > PageGetMaxOffsetNumber(page)) {
1281                         UnlockReleaseBuffer(buffer);
1282                         pfree(s);
1283                         goto next;
1284                 }
1285
1286                 dtuple = (SpGistDeadTuple)PageGetItem(page, PageGetItemId(page, ItemPointerGetOffsetNumber(&s->iptr)));
1287
1288                 if (dtuple->tupstate != SPGIST_LIVE)  {
1289                         UnlockReleaseBuffer(buffer);
1290                         pfree(s);
1291                         goto next;
1292                 }
1293
1294                 if (SpGistPageIsLeaf(page)) {
1295                                 SpGistLeafTuple leafTuple = (SpGistLeafTuple)dtuple;
1296
1297                                 tid = palloc(sizeof(ItemPointerData));
1298                                 *tid = s->iptr;
1299                                 prst->dvalues[0] = PointerGetDatum(tid);
1300                                 prst->nulls[0] = ISNOTNULL;
1301                                 prst->nulls[1] = ISNULL;
1302                                 prst->nulls[2] = ISNULL;
1303                                 prst->dvalues[3]  = s->level;
1304                                 prst->nulls[3] = ISNOTNULL;
1305                                 prst->nulls[4] = ISNULL;
1306                                 prst->nulls[5] = ISNULL;
1307                                 prst->nulls[6] = ISNULL;
1308                                 prst->dvalues[7]  = datumCopy(SGLTDATUM(leafTuple, &prst->state),
1309                                                                                         prst->state.attType.attbyval, prst->state.attType.attlen);
1310                                 prst->nulls[7] = ISNOTNULL;
1311                 } else {
1312                         SpGistInnerTuple        innerTuple = (SpGistInnerTuple)dtuple;
1313                         int                                     i;
1314                         SpGistNodeTuple         node;
1315
1316                         SGITITERATE(innerTuple, i, node) {
1317                                 if (ItemPointerIsValid(&node->t_tid)) {
1318                                         if (i >= s->nlabel)
1319                                                 break;
1320                                 }
1321                         }
1322
1323                         if (i >= innerTuple->nNodes) {
1324                                 UnlockReleaseBuffer(buffer);
1325                                 pfree(s);
1326                                 goto next;
1327                         }
1328
1329                         tid = palloc(sizeof(ItemPointerData));
1330                         *tid = s->iptr;
1331                         prst->dvalues[0] = PointerGetDatum(tid);
1332                         prst->nulls[0] = ISNOTNULL;
1333                         prst->dvalues[1] = innerTuple->allTheSame;
1334                         prst->nulls[1] = ISNOTNULL;
1335                         prst->dvalues[2] = Int32GetDatum(s->nlabel);
1336                         prst->nulls[2] = ISNOTNULL;
1337                         prst->dvalues[3]  = s->level;
1338                         prst->nulls[3] = ISNOTNULL;
1339                         tid = palloc(sizeof(ItemPointerData));
1340                         *tid = node->t_tid;
1341                         prst->dvalues[4] = PointerGetDatum(tid);
1342                         prst->nulls[5] = ISNOTNULL;
1343                         if (innerTuple->prefixSize > 0) {
1344                                 prst->dvalues[5]  = datumCopy(SGITDATUM(innerTuple, &prst->state),
1345                                                                                         prst->state.attPrefixType.attbyval, prst->state.attPrefixType.attlen);
1346                                 prst->nulls[5] = ISNOTNULL;
1347                         } else
1348                                 prst->nulls[5] = ISNULL;
1349                         if (!IndexTupleHasNulls(node)) {
1350                                 prst->dvalues[6]  = datumCopy(SGNTDATUM(node, &prst->state),
1351                                                                                         prst->state.attLabelType.attbyval, prst->state.attLabelType.attlen);
1352                                 prst->nulls[6] = ISNOTNULL;
1353                         } else
1354                                 prst->nulls[6] = ISNULL;
1355                         prst->nulls[7] = ISNULL;
1356
1357                         pushSPGistPrint(funcctx, prst, &node->t_tid, s->level + 1);
1358                         s->nlabel = i + 1;
1359                         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1360                         prst->stack = lcons(s, prst->stack);
1361                         MemoryContextSwitchTo(oldcontext);
1362                         s = NULL;
1363                 }
1364
1365                 UnlockReleaseBuffer(buffer);
1366                 if (s) pfree(s);
1367         } while(0);
1368
1369         htuple = heap_formtuple(funcctx->attinmeta->tupdesc, prst->dvalues, prst->nulls);
1370 #if PG_VERSION_NUM >= 120000
1371         result = HeapTupleGetDatum(htuple);
1372 #else
1373         result = TupleGetDatum(funcctx->slot, htuple);
1374 #endif
1375
1376         SRF_RETURN_NEXT(funcctx, result);
1377 #endif
1378 }
1379
1380 PG_FUNCTION_INFO_V1(gin_statpage);
1381 Datum gin_statpage(PG_FUNCTION_ARGS);
1382 Datum
1383 gin_statpage(PG_FUNCTION_ARGS)
1384 {
1385 #if PG_VERSION_NUM < 90400
1386         elog(NOTICE, "Function is not working under PgSQL < 9.4");
1387
1388         PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1389 #else
1390         text       *name = PG_GETARG_TEXT_P(0);
1391         RangeVar   *relvar;
1392         Relation        index;
1393         BlockNumber blkno;
1394         char            res[1024];
1395         uint32          totalPages,
1396 #if PG_VERSION_NUM >= 100000
1397                                 deletedPages = 0,
1398                                 emptyDataPages = 0,
1399 #endif
1400                                 entryPages = 0,
1401                                 dataPages = 0,
1402                                 dataInnerPages = 0,
1403                                 dataLeafPages = 0,
1404                                 entryInnerPages = 0,
1405                                 entryLeafPages = 0
1406                                 ;
1407         uint64          dataInnerFreeSpace = 0,
1408                                 dataLeafFreeSpace = 0,
1409                                 dataInnerTuplesCount = 0,
1410                                 dataLeafIptrsCount = 0,
1411                                 entryInnerFreeSpace = 0,
1412                                 entryLeafFreeSpace = 0,
1413                                 entryInnerTuplesCount = 0,
1414                                 entryLeafTuplesCount = 0,
1415                                 entryPostingSize = 0,
1416                                 entryPostingCount = 0,
1417                                 entryAttrSize = 0
1418                                 ;
1419
1420         relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1421         index = relation_openrv(relvar, AccessExclusiveLock);
1422
1423         if (index->rd_rel->relkind != RELKIND_INDEX ||
1424                         index->rd_rel->relam != GIN_AM_OID)
1425                 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1426                          RelationGetRelationName(index));
1427
1428         totalPages = RelationGetNumberOfBlocks(index);
1429
1430         for (blkno = GIN_ROOT_BLKNO; blkno < totalPages; blkno++)
1431         {
1432                 Buffer          buffer;
1433                 Page            page;
1434                 PageHeader      header;
1435
1436                 buffer = ReadBuffer(index, blkno);
1437                 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1438
1439                 page = BufferGetPage(buffer);
1440                 header = (PageHeader)page;
1441
1442 #if PG_VERSION_NUM >= 100000
1443                 if (GinPageIsDeleted(page))
1444                 {
1445                         deletedPages++;
1446                 }
1447                 else
1448 #endif
1449                 if (GinPageIsData(page))
1450                 {
1451                         dataPages++;
1452                         if (GinPageIsLeaf(page))
1453                         {
1454                                 ItemPointerData minItem, *ptr;
1455                                 int nlist;
1456
1457
1458                                 dataLeafPages++;
1459                                 dataLeafFreeSpace += header->pd_upper - header->pd_lower;
1460                                 ItemPointerSetMin(&minItem);
1461
1462                                 ptr = GinDataLeafPageGetItems(page, &nlist, minItem);
1463
1464                                 if (ptr)
1465                                 {
1466                                         pfree(ptr);
1467                                         dataLeafIptrsCount += nlist;
1468                                 }
1469                                 else
1470                                         emptyDataPages++;
1471                         }
1472                         else
1473                         {
1474                                 dataInnerPages++;
1475                                 dataInnerFreeSpace += header->pd_upper - header->pd_lower;
1476                                 dataInnerTuplesCount += GinPageGetOpaque(page)->maxoff;
1477                         }
1478                 }
1479                 else
1480                 {
1481                         IndexTuple itup;
1482                         OffsetNumber i, maxoff;
1483
1484                         maxoff = PageGetMaxOffsetNumber(page);
1485
1486                         entryPages++;
1487                         if (GinPageIsLeaf(page))
1488                         {
1489                                 entryLeafPages++;
1490                                 entryLeafFreeSpace += header->pd_upper - header->pd_lower;
1491                                 entryLeafTuplesCount += maxoff;
1492                         }
1493                         else
1494                         {
1495                                 entryInnerPages++;
1496                                 entryInnerFreeSpace += header->pd_upper - header->pd_lower;
1497                                 entryInnerTuplesCount += maxoff;
1498                         }
1499
1500                         for (i = 1; i <= maxoff; i++)
1501                         {
1502                                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
1503
1504                                 if (GinPageIsLeaf(page))
1505                                 {
1506                                         GinPostingList *list = (GinPostingList *)GinGetPosting(itup);
1507                                         entryPostingCount += GinGetNPosting(itup);
1508                                         entryPostingSize += SizeOfGinPostingList(list);
1509                                         entryAttrSize += GinGetPostingOffset(itup) - IndexInfoFindDataOffset((itup)->t_info);
1510                                 }
1511                                 else
1512                                 {
1513                                         entryAttrSize += IndexTupleSize(itup) - IndexInfoFindDataOffset((itup)->t_info);
1514                                 }
1515                         }
1516                 }
1517
1518                 UnlockReleaseBuffer(buffer);
1519         }
1520
1521         index_close(index, AccessExclusiveLock);
1522         totalPages--;
1523
1524         snprintf(res, sizeof(res),
1525                          "totalPages:                   %u\n"
1526 #if PG_VERSION_NUM >= 100000
1527                          "deletedPages:           %u\n"
1528                          "emptyDataPages:               %u\n"
1529 #endif
1530                          "dataPages:                     %u\n"
1531                          "dataInnerPages:               %u\n"
1532                          "dataLeafPages:                 %u\n"
1533                          "dataInnerFreeSpace:   " INT64_FORMAT "\n"
1534                          "dataLeafFreeSpace:     " INT64_FORMAT "\n"
1535                          "dataInnerTuplesCount:  " INT64_FORMAT "\n"
1536                          "dataLeafIptrsCount:   " INT64_FORMAT "\n"
1537                          "entryPages:                   %u\n"
1538                          "entryInnerPages:         %u\n"
1539                          "entryLeafPages:               %u\n"
1540                          "entryInnerFreeSpace:   " INT64_FORMAT "\n"
1541                          "entryLeafFreeSpace:   " INT64_FORMAT "\n"
1542                          "entryInnerTuplesCount: " INT64_FORMAT "\n"
1543                          "entryLeafTuplesCount:  " INT64_FORMAT "\n"
1544                          "entryPostingSize:       " INT64_FORMAT "\n"
1545                          "entryPostingCount:     " INT64_FORMAT "\n"
1546                          "entryAttrSize:                 " INT64_FORMAT "\n"
1547                          ,
1548                          totalPages,
1549 #if PG_VERSION_NUM >= 100000
1550                          deletedPages,
1551                          emptyDataPages,
1552 #endif
1553                          dataPages,
1554                          dataInnerPages,
1555                          dataLeafPages,
1556                          dataInnerFreeSpace,
1557                          dataLeafFreeSpace,
1558                          dataInnerTuplesCount,
1559                          dataLeafIptrsCount,
1560                          entryPages,
1561                          entryInnerPages,
1562                          entryLeafPages,
1563                          entryInnerFreeSpace,
1564                          entryLeafFreeSpace,
1565                          entryInnerTuplesCount,
1566                          entryLeafTuplesCount,
1567                          entryPostingSize,
1568                          entryPostingCount,
1569                          entryAttrSize
1570                          );
1571
1572         PG_RETURN_TEXT_P(CStringGetTextDatum(res));
1573 #endif
1574 }
1575
1576 #if PG_VERSION_NUM >= 120000
1577 typedef enum {stat, print} TreeCond;
1578 typedef struct
1579 {
1580         IdxInfo idxInfo;
1581         IdxStat idxStat;
1582 }BtreeIdxInfo;
1583
1584 /*
1585  * Depth-first search for btree
1586  * using for statistic data collection
1587  * and printing index values by level
1588  */
1589 static void
1590 btree_deep_search(Relation rel, int level,
1591                 BlockNumber blk,  BtreeIdxInfo *btreeIdxInfo, TreeCond cond)
1592 {
1593         Page                    page;
1594         IndexTuple              itup;
1595         ItemId                  iid;
1596         OffsetNumber    i,
1597                                         maxoff;
1598         BlockNumber             cblk;
1599         BTPageOpaque    opaque;
1600         Buffer buffer =  _bt_getbuf(rel, blk, BT_READ);
1601
1602         page = (Page) BufferGetPage(buffer);
1603         opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1604         maxoff = PageGetMaxOffsetNumber(page);
1605
1606         switch (cond)
1607         {
1608                 case stat:
1609                 {
1610                         btreeIdxInfo->idxStat.numpages++;
1611                         btreeIdxInfo->idxStat.tuplesize+=BTMaxItemSize(page)-PageGetFreeSpace(page);
1612                         btreeIdxInfo->idxStat.totalsize+=BLCKSZ;
1613                         btreeIdxInfo->idxStat.numtuple+=maxoff;
1614
1615                         if (level > btreeIdxInfo->idxStat.level)
1616                                 btreeIdxInfo->idxStat.level = level;
1617
1618                         if (P_ISLEAF(opaque))
1619                         {
1620                                 btreeIdxInfo->idxStat.numleafpages++;
1621                                 btreeIdxInfo->idxStat.leaftuplesize+=BTMaxItemSize(page)-
1622                                                 PageGetFreeSpace(page);
1623                                 btreeIdxInfo->idxStat.numleaftuple+=maxoff;
1624                         }
1625                         break;
1626                 }
1627                 case print:
1628                 {
1629                         while ( (btreeIdxInfo->idxInfo.ptr-((char*)btreeIdxInfo->idxInfo.txt))
1630                                         + level*4 + 128 >= btreeIdxInfo->idxInfo.len )
1631                         {
1632                                         int dist=btreeIdxInfo->idxInfo.ptr-((char*)btreeIdxInfo->idxInfo.txt);
1633                                         btreeIdxInfo->idxInfo.len *= 2;
1634                                         btreeIdxInfo->idxInfo.txt=(text*)repalloc(btreeIdxInfo->idxInfo.txt,
1635                                                         btreeIdxInfo->idxInfo.len);
1636                                         btreeIdxInfo->idxInfo.ptr = ((char*)btreeIdxInfo->idxInfo.txt)+dist;
1637                         }
1638
1639                         sprintf(btreeIdxInfo->idxInfo.ptr, "lvl: %d, blk: %d, numTuples: %d\n",
1640                                                         level,
1641                                                         blk,
1642                                                         (int)maxoff);
1643
1644                         btreeIdxInfo->idxInfo.ptr=strchr(btreeIdxInfo->idxInfo.ptr,'\0');
1645                         break;
1646                 }
1647         }
1648
1649         if (!P_ISLEAF(opaque) && ((level < btreeIdxInfo->idxInfo.maxlevel)
1650                         ||(btreeIdxInfo->idxInfo.maxlevel<0)))
1651         {
1652                 for (i = P_FIRSTDATAKEY(opaque); i <= maxoff; i = OffsetNumberNext(i))
1653                 {
1654                         iid = PageGetItemId(page, i);
1655
1656                         if (!ItemIdIsValid(iid))
1657                                 btreeIdxInfo->idxStat.numinvalidtuple++;
1658
1659                         itup = (IndexTuple) PageGetItem(page, iid);
1660                         cblk = BTreeInnerTupleGetDownLink(itup);
1661
1662                         btree_deep_search(rel, level + 1, cblk, btreeIdxInfo, cond);
1663                 }
1664         }
1665         UnlockReleaseBuffer(buffer);
1666 }
1667
1668 /*
1669  * Print some statistic about btree index
1670  * This function shows information for live pages only
1671  * and do not shows information about deleting pages
1672  *
1673  * SELECT btree_stat(INDEXNAME);
1674  */
1675 PG_FUNCTION_INFO_V1(btree_stat);
1676 Datum btree_stat(PG_FUNCTION_ARGS);
1677 Datum
1678 btree_stat(PG_FUNCTION_ARGS)
1679 {
1680         text            *name=PG_GETARG_TEXT_PP(0);
1681         RangeVar        *relvar;
1682         Relation        index;
1683         List            *relname_list;
1684         BtreeIdxInfo btreeIdxInfo;
1685
1686         Buffer          metabuf;
1687         Page            metapg;
1688         BTMetaPageData *metad;
1689         BlockNumber rootBlk;
1690
1691         text *out=(text*)palloc(1024);
1692         char *ptr=((char*)out)+VARHDRSZ;
1693         relname_list = textToQualifiedNameList(name);
1694         relvar = makeRangeVarFromNameList(relname_list);
1695         index = btree_index_open(relvar);
1696
1697         memset(&btreeIdxInfo.idxStat, 0, sizeof(IdxStat));
1698
1699         /* Start dts from root */
1700         metabuf = _bt_getbuf(index, BTREE_METAPAGE, BT_READ);
1701         metapg = BufferGetPage(metabuf);
1702         metad = BTPageGetMeta(metapg);
1703         rootBlk = metad->btm_root;
1704         UnlockReleaseBuffer(metabuf);
1705
1706         btree_deep_search(index, 0, rootBlk, &btreeIdxInfo,stat);
1707
1708         btree_index_close(index);
1709
1710         sprintf(ptr,
1711                 "Number of levels:                %d\n"
1712                 "Number of pages:                  %d\n"
1713                 "Number of leaf pages:    %d\n"
1714                 "Number of tuples:                %d\n"
1715                 "Number of invalid tuples:  %d\n"
1716                 "Number of leaf tuples:  %d\n"
1717                 "Total size of tuples:    "INT64_FORMAT" bytes\n"
1718                 "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
1719                 "Total size of index:      "INT64_FORMAT" bytes\n",
1720                 btreeIdxInfo.idxStat.level+1,
1721                 btreeIdxInfo.idxStat.numpages,
1722                 btreeIdxInfo.idxStat.numleafpages,
1723                 btreeIdxInfo.idxStat.numtuple,
1724                 btreeIdxInfo.idxStat.numinvalidtuple,
1725                 btreeIdxInfo.idxStat.numleaftuple,
1726                 btreeIdxInfo.idxStat.tuplesize,
1727                 btreeIdxInfo.idxStat.leaftuplesize,
1728                 btreeIdxInfo.idxStat.totalsize);
1729
1730         ptr=strchr(ptr,'\0');
1731
1732         SET_VARSIZE(out, ptr-((char*)out));
1733         PG_RETURN_POINTER(out);
1734 }
1735
1736 typedef struct BtPItem
1737 {
1738         Buffer             buffer;
1739         Page               page;
1740         OffsetNumber   offset;
1741         int                        level;
1742         struct BtPItem *next;
1743 } BtPItem;
1744
1745 typedef struct
1746 {
1747         List     *relname_list;
1748         RangeVar *relvar;
1749         Relation index;
1750         Datum    *dvalues;
1751         bool     *nulls;
1752         BtPItem  *item;
1753 } BtTypeStorage;
1754
1755 /*
1756  * Open page in btree
1757  * Returns nitem as a pointer to stack for btree levels stored.
1758  * We process tuples from buffer in the top of nitem.
1759  * After the complete processing of a top buffer
1760  * we return to buffer on one level upper and go to next btree leaf.
1761  */
1762 static BtPItem*
1763 openBtPPage( FuncCallContext *funcctx, BlockNumber blk )
1764 {
1765         BtPItem           *nitem;
1766         MemoryContext oldcontext;
1767
1768         Relation index = ( (BtTypeStorage*)(funcctx->user_fctx) )->index;
1769         BTPageOpaque opaque;
1770         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1771         nitem = (BtPItem*)palloc( sizeof(BtPItem) );
1772         memset(nitem,0,sizeof(BtPItem));
1773
1774         nitem->buffer = _bt_getbuf(index, blk, BT_READ);
1775         Assert(BufferIsValid(nitem->buffer));
1776         nitem->page = (Page) BufferGetPage(nitem->buffer);
1777         opaque = (BTPageOpaque)PageGetSpecialPointer(nitem->page);
1778         nitem->offset=P_FIRSTDATAKEY(opaque);
1779         nitem->next = ( (BtTypeStorage*)(funcctx->user_fctx) )->item;
1780         nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1;
1781         ( (BtTypeStorage*)(funcctx->user_fctx) )->item = nitem;
1782
1783         MemoryContextSwitchTo(oldcontext);
1784
1785         return nitem;
1786 }
1787
1788 static BtPItem*
1789 closeBtPPage( FuncCallContext *funcctx )
1790 {
1791         BtPItem  *oitem = ( (BtTypeStorage*)(funcctx->user_fctx) )->item;
1792
1793         ( (BtTypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
1794
1795         UnlockReleaseBuffer(oitem->buffer);
1796         pfree( oitem );
1797         return ( (BtTypeStorage*)(funcctx->user_fctx) )->item;
1798 }
1799
1800 static void
1801 btree_close_call( FuncCallContext  *funcctx )
1802 {
1803         BtTypeStorage *st = (BtTypeStorage*)(funcctx->user_fctx);
1804
1805         while(st->item && closeBtPPage(funcctx));
1806
1807         pfree(st->dvalues);
1808         pfree(st->nulls);
1809
1810         btree_index_close(st->index);
1811 }
1812
1813 /*
1814  * Settings for first call of btree_print
1815  * Sets the current memory context
1816  */
1817 static void
1818 btree_setup_firstcall(FuncCallContext  *funcctx, text *name)
1819 {
1820         MemoryContext oldcontext;
1821         BtTypeStorage *st;
1822         TupleDesc        tupdesc;
1823         char              attname[NAMEDATALEN];
1824         int                       i;
1825         BlockNumber   blk;
1826         Buffer            buffer;
1827
1828         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1829
1830         st=(BtTypeStorage*)palloc( sizeof(BtTypeStorage) );
1831         memset(st,0,sizeof(BtTypeStorage));
1832         st->relname_list = textToQualifiedNameList(name);
1833         st->relvar = makeRangeVarFromNameList(st->relname_list);
1834         st->index = btree_index_open(st->relvar);
1835         st->item = NULL;
1836         funcctx->user_fctx = (void*)st;
1837
1838         tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2);
1839         TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
1840         TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
1841         for (i = 0; i < st->index->rd_att->natts; i++)
1842         {
1843                 sprintf(attname, "z%d", i+2);
1844                 TupleDescInitEntry(
1845                         tupdesc,
1846                         i+3,
1847                         attname,
1848                         TS_GET_TYPEVAL(st, i, atttypid),
1849                         TS_GET_TYPEVAL(st, i, atttypmod),
1850                         TS_GET_TYPEVAL(st, i, attndims) );
1851                 BlessTupleDesc(tupdesc);
1852         }
1853         BlessTupleDesc(tupdesc);
1854
1855
1856         st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
1857         st->nulls = palloc((tupdesc->natts+2) * sizeof(*st->nulls));
1858
1859         funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1860
1861         buffer = _bt_gettrueroot(st->index);
1862         blk = BufferGetBlockNumber(buffer);
1863         UnlockReleaseBuffer(buffer);
1864
1865         MemoryContextSwitchTo(oldcontext);
1866
1867         st->item=openBtPPage(funcctx, blk);
1868 }
1869
1870 /*
1871  * Show index elements for btree from root to MAXLEVEL
1872  * SELECT btree_tree(INDEXNAME[, MAXLEVEL]);
1873  */
1874 PG_FUNCTION_INFO_V1(btree_tree);
1875 Datum btree_tree(PG_FUNCTION_ARGS);
1876 Datum
1877 btree_tree(PG_FUNCTION_ARGS)
1878 {
1879         text                    *name=PG_GETARG_TEXT_PP(0);
1880         RangeVar                *relvar;
1881         Relation                index;
1882         List                    *relname_list;
1883         BtreeIdxInfo btreeIdxInfo;
1884         Buffer                  metabuf;
1885         Page                    metapg;
1886         BTMetaPageData  *metad;
1887         BlockNumber             rootBlk;
1888
1889         /*
1890          *  If we use MAXLEVEL is not used in SELECT btree_tree(INDEXNAME),
1891          *  info.maxlevel set to -1
1892          *  If MAXLEVEL is used in btree_tree call, set info.maxlevel = MAXLEVEL
1893          */
1894         btreeIdxInfo.idxInfo.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1;
1895         btreeIdxInfo.idxInfo.len=1024;
1896         btreeIdxInfo.idxInfo.txt=(text*)palloc( btreeIdxInfo.idxInfo.len );
1897         btreeIdxInfo.idxInfo.ptr=((char*)btreeIdxInfo.idxInfo.txt)+VARHDRSZ;
1898
1899         relname_list = textToQualifiedNameList(name);
1900         relvar = makeRangeVarFromNameList(relname_list);
1901         index = btree_index_open(relvar);
1902
1903         /* Start dts from root block */
1904         metabuf = _bt_getbuf(index, BTREE_METAPAGE, BT_READ);
1905         metapg = BufferGetPage(metabuf);
1906         metad = BTPageGetMeta(metapg);
1907         rootBlk = metad->btm_root;
1908         UnlockReleaseBuffer(metabuf);
1909
1910         btree_deep_search(index, 0, rootBlk, &btreeIdxInfo, print);
1911
1912         btree_index_close(index);
1913
1914         btreeIdxInfo.idxInfo.ptr=strchr(btreeIdxInfo.idxInfo.ptr,'\0');
1915
1916         SET_VARSIZE(btreeIdxInfo.idxInfo.txt,
1917                         btreeIdxInfo.idxInfo.ptr-((char*)btreeIdxInfo.idxInfo.txt));
1918         PG_RETURN_POINTER(btreeIdxInfo.idxInfo.txt);
1919 }
1920
1921 /*
1922  * Print objects stored in btree tuples
1923  * works only if objects in index have textual representation
1924  * select * from btree_print(INDEXNAME)
1925  *              as t(level int, valid bool, a box) where level =1;
1926  */
1927 PG_FUNCTION_INFO_V1(btree_print);
1928 Datum btree_print(PG_FUNCTION_ARGS);
1929 Datum
1930 btree_print(PG_FUNCTION_ARGS)
1931 {
1932         FuncCallContext *funcctx;
1933         BtTypeStorage   *st;
1934         ItemId            iid;
1935         IndexTuple        ituple;
1936         HeapTuple          htuple;
1937         int                             i;
1938         bool                    isnull;
1939         BTPageOpaque opaque;
1940         Datum result=(Datum)0;
1941
1942         if (SRF_IS_FIRSTCALL())
1943         {
1944                 text    *name=PG_GETARG_TEXT_PP(0);
1945                 funcctx = SRF_FIRSTCALL_INIT();
1946                 btree_setup_firstcall(funcctx, name);
1947                 PG_FREE_IF_COPY(name,0);
1948         }
1949
1950         funcctx = SRF_PERCALL_SETUP();
1951         st = (BtTypeStorage*)(funcctx->user_fctx);
1952
1953         /*
1954          * Looking through the stack of btree pages.
1955          * If the item == NULL, then the search is over.
1956          * For each btree page we look through all the tuples.
1957          * If the offset is larger than the btree page size, the search is over.
1958          */
1959
1960         if ( !st->item )
1961         {
1962                 btree_close_call(funcctx);
1963                 SRF_RETURN_DONE(funcctx);
1964         }
1965
1966         /* View all tuples on the page */
1967         while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) )
1968         {
1969                 if ( ! closeBtPPage(funcctx) ) {
1970                         btree_close_call(funcctx);
1971                         SRF_RETURN_DONE(funcctx);
1972                 }
1973         }
1974
1975         iid = PageGetItemId( st->item->page, st->item->offset );
1976         ituple = (IndexTuple) PageGetItem(st->item->page, iid);
1977
1978         st->dvalues[0] = Int32GetDatum( st->item->level );
1979         st->nulls[0] = ISNOTNULL;
1980         opaque = (BTPageOpaque)PageGetSpecialPointer(st->item->page);
1981         st->dvalues[1] = BoolGetDatum(P_ISLEAF(opaque) && !ItemPointerIsValid(&(ituple->t_tid)) ? false : true);
1982         st->nulls[1] = ISNOTNULL;
1983         for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++)
1984         {
1985                 if (!P_ISLEAF(opaque) && !ItemPointerIsValid(&(ituple->t_tid)))
1986                 {
1987                         st->dvalues[i] = (Datum)0;
1988                         st->nulls[i] = ISNULL;
1989                 }
1990                 else
1991                 {
1992                         st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull);
1993                         st->nulls[i] = ( isnull ) ? ISNULL : ISNOTNULL;
1994                 }
1995         }
1996
1997         htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
1998         result = TupleGetDatum(funcctx->slot, htuple);
1999         result = HeapTupleGetDatum(htuple);
2000         st->item->offset = OffsetNumberNext(st->item->offset);
2001
2002         if (!P_ISLEAF(opaque))
2003         {
2004                 BlockNumber blk = BTreeInnerTupleGetDownLink(ituple);
2005                 openBtPPage(funcctx, blk );
2006         }
2007
2008         SRF_RETURN_NEXT(funcctx, result);
2009 }
2010
2011 /*
2012  * Print some statistic about brin index
2013  * SELECT brin_stat(INDEXNAME);
2014  */
2015 PG_FUNCTION_INFO_V1(brin_stat);
2016 Datum brin_stat(PG_FUNCTION_ARGS);
2017 Datum
2018 brin_stat(PG_FUNCTION_ARGS)
2019 {
2020         text            *name=PG_GETARG_TEXT_PP(0);
2021         RangeVar        *relvar;
2022         Relation        index;
2023         List            *relname_list;
2024
2025         BlockNumber startBlk;
2026         BlockNumber heapNumBlocks;
2027         BlockNumber pagesPerRange;
2028         BrinRevmap *revmap;
2029         Relation heapRel;
2030
2031         Buffer buf;
2032
2033         uint32 numRevmapPages=0;
2034         uint32 numTuples = 0;
2035         uint32 numEmptyPages = 0;
2036         uint32 numRegularPages = 0;
2037         uint64 freeSpace = 0;
2038         uint64 usedSpace = 0;
2039
2040         text *out=(text*)palloc(1024);
2041         char *ptr=((char*)out)+VARHDRSZ;
2042
2043         relname_list = textToQualifiedNameList(name);
2044         relvar = makeRangeVarFromNameList(relname_list);
2045         index = brin_index_open(relvar);
2046
2047         revmap = brinRevmapInitialize(index, &pagesPerRange, NULL);
2048         heapRel = table_open(IndexGetRelation(RelationGetRelid(index), false),
2049                                                                  AccessShareLock);
2050         /* Determine range of pages to process */
2051         heapNumBlocks = RelationGetNumberOfBlocks(heapRel); // total pages
2052
2053         table_close(heapRel, AccessShareLock);
2054
2055         /* The index is up to date, no update required */
2056         startBlk = 0;
2057
2058         buf = InvalidBuffer;
2059         /* Scan the revmap */
2060         for (; startBlk < heapNumBlocks; startBlk += pagesPerRange)
2061         {
2062                 BrinTuple  *tup;
2063                 OffsetNumber off;
2064                 Page    page;
2065
2066                 numRevmapPages++;
2067                 /* Get on-disk tuple */
2068                 tup = brinGetTupleForHeapBlock(revmap, startBlk, &buf, &off, NULL,
2069                                    BUFFER_LOCK_SHARE, NULL);
2070
2071                 if (tup)
2072                 {
2073                         BrinDesc* bdesc;
2074
2075                         numTuples++;
2076                         page = BufferGetPage(buf);
2077
2078                         if (BRIN_IS_REGULAR_PAGE(page))
2079                                 numRegularPages++;
2080                         freeSpace += PageGetFreeSpace(page);
2081                         bdesc = brin_build_desc(index);
2082                         usedSpace += MAXALIGN(sizeof(BrinMemTuple) +
2083                                         sizeof(BrinValues) * bdesc->bd_tupdesc->natts);
2084                 }
2085                 else
2086                         /* If the revmap page points to void */
2087                         numEmptyPages++;
2088
2089                 UnlockReleaseBuffer(buf);
2090         }
2091
2092         brinRevmapTerminate(revmap);
2093
2094         sprintf(ptr,
2095                 "Number of revmap pages:        %d\n"
2096                 "Number of empty revmap pages:  %d\n"
2097                 "Number of regular pages:       %d\n"
2098                 "Number of tuples:              %d\n"
2099                 "Used space             "INT64_FORMAT" bytes\n"
2100                 "Free space             "INT64_FORMAT" bytes\n",
2101                 numRevmapPages,
2102                 numEmptyPages,
2103                 numRegularPages,
2104                 numTuples,
2105                 usedSpace,
2106                 freeSpace
2107                 );
2108
2109         ptr=strchr(ptr,'\0');
2110
2111         brin_index_close(index);
2112         SET_VARSIZE(out, ptr-((char*)out));
2113         PG_RETURN_POINTER(out);
2114 }
2115
2116 /*
2117  * Print numbers of heap blocks from revmap
2118  * and numbers of end blocks from ranges
2119  */
2120 PG_FUNCTION_INFO_V1(brin_print);
2121 Datum brin_print(PG_FUNCTION_ARGS);
2122 Datum
2123 brin_print(PG_FUNCTION_ARGS)
2124 {
2125         text    *name=PG_GETARG_TEXT_PP(0);
2126         RangeVar                *relvar;
2127         Relation                index;
2128         List                    *relname_list;
2129
2130         BlockNumber heapBlk;
2131         BlockNumber numBlocks;
2132         BlockNumber pagesPerRange;
2133         BrinRevmap *revmap;
2134         Relation heapRel;
2135         int len = 1024;
2136
2137         text *out=(text*)palloc(len);
2138         char *ptr=((char*)out)+VARHDRSZ;
2139
2140         relname_list = textToQualifiedNameList(name);
2141         relvar = makeRangeVarFromNameList(relname_list);
2142         index = brin_index_open(relvar);
2143
2144         heapRel = table_open(IndexGetRelation(RelationGetRelid(index), false),
2145                                                                          AccessShareLock);
2146
2147         /* Determine range of pages to process */
2148         numBlocks = RelationGetNumberOfBlocks(heapRel);
2149         table_close(heapRel, AccessShareLock);
2150         revmap = brinRevmapInitialize(index, &pagesPerRange, NULL);
2151
2152         for(heapBlk = 0; heapBlk < numBlocks; heapBlk += pagesPerRange)
2153         {
2154                 BlockNumber rangeEndBlk;
2155                 BlockNumber rangeBlk;
2156
2157                 while ( (ptr-((char*)out)) + heapBlk*4 + 128 >= len )
2158                 {
2159                         int dist=ptr-((char*)out);
2160                         len *= 2;
2161                         out=(text*)repalloc(out, len);
2162                         ptr = ((char*)out)+dist;
2163                         Assert(0);
2164                 }
2165
2166                 /* Compute rage end */
2167                 if (heapBlk + pagesPerRange > numBlocks)
2168                         rangeEndBlk = Min(RelationGetNumberOfBlocks(heapRel) - heapBlk,
2169                                                                   pagesPerRange);
2170                 else
2171                         /* Easy case: range is known to be complete */
2172                         rangeEndBlk = pagesPerRange;
2173                 sprintf(ptr, "Start block: %d; end block: %d \n", heapBlk, rangeEndBlk);
2174
2175                 for (rangeBlk = 0; rangeBlk < rangeEndBlk; rangeBlk++)
2176                 {
2177                                 Page page;
2178                                 /* Read buffer for open table */
2179                                 Buffer buf = ReadBuffer(heapRel, rangeBlk);
2180                                 page = BufferGetPage(buf);
2181                                 sprintf(ptr, "Start block: %d; end block: %d; offset: %d, free: %d\n",
2182                                                 heapBlk,
2183                                                 rangeEndBlk,
2184                                                 (int) PageGetMaxOffsetNumber(page),
2185                                                 (int) PageGetFreeSpace(page));
2186
2187                                 ReleaseBuffer(buf);
2188                 }
2189
2190                 ptr=strchr(ptr,'\0');
2191         }
2192
2193         brinRevmapTerminate(revmap);
2194
2195         brin_index_close(index);
2196         ptr=strchr(ptr,'\0');
2197         SET_VARSIZE(out, ptr-((char*)out));
2198         PG_RETURN_POINTER(out);
2199 }
2200 #endif