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