9.2devel support
[gevel.git] / gevel.c
1 #include "postgres.h"
2
3 #include "access/genam.h"
4 #include "access/gin.h"
5 #if PG_VERSION_NUM >= 90100
6 #include "access/gin_private.h"
7 #endif
8 #include "access/gist.h"
9 #include "access/gist_private.h"
10 #include "access/gistscan.h"
11 #include "access/heapam.h"
12 #include "catalog/index.h"
13 #include "miscadmin.h"
14 #include "storage/lmgr.h"
15 #include "catalog/namespace.h"
16 #if PG_VERSION_NUM >= 80300
17 #include <tsearch/ts_utils.h>
18 #endif
19 #include <utils/tqual.h>
20 #include "utils/builtins.h"
21 #include "utils/lsyscache.h"
22 #include "utils/datum.h"
23 #include "utils/fmgroids.h"
24 #include <fmgr.h>
25 #include <funcapi.h>
26 #include <access/heapam.h>
27 #include <catalog/pg_type.h>
28 #include <access/relscan.h>
29
30
31 #define PAGESIZE        (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData)))
32
33 #ifndef PG_NARGS
34 #define PG_NARGS() (fcinfo->nargs)
35 #endif
36
37 static char
38 *t2c(text* in) {
39         char *out=palloc( VARSIZE(in) );
40         memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ);
41         out[ VARSIZE(in)-VARHDRSZ ] ='\0';
42         return out;
43 }
44
45 typedef struct {
46         int maxlevel;
47         text    *txt;
48         char    *ptr;
49         int     len;    
50 } IdxInfo;
51
52 static Relation checkOpenedRelation(Relation r, Oid PgAmOid);
53
54 #ifdef PG_MODULE_MAGIC
55 /* >= 8.2 */ 
56
57 PG_MODULE_MAGIC;
58
59 static Relation
60 gist_index_open(RangeVar *relvar) {
61 #if PG_VERSION_NUM < 90100 
62         Oid relOid = RangeVarGetRelid(relvar, false);
63 #else
64         Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
65 #endif
66         return checkOpenedRelation(
67                                 index_open(relOid, AccessExclusiveLock), GIST_AM_OID);
68 }
69
70 #define gist_index_close(r)     index_close((r), AccessExclusiveLock)
71
72 static Relation
73 gin_index_open(RangeVar *relvar) {
74 #if PG_VERSION_NUM < 90100 
75         Oid relOid = RangeVarGetRelid(relvar, false);
76 #else
77         Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
78 #endif
79         return checkOpenedRelation(
80                                 index_open(relOid, AccessShareLock), GIN_AM_OID);
81 }
82
83 #define gin_index_close(r) index_close((r), AccessShareLock)
84
85 #else /* <8.2 */
86
87 static Relation
88 gist_index_open(RangeVar *relvar) {
89         Relation rel = index_openrv(relvar);
90
91         LockRelation(rel, AccessExclusiveLock);
92         return checkOpenedRelation(rel, GIST_AM_OID);
93 }
94
95 static void
96 gist_index_close(Relation rel) {
97         UnlockRelation(rel, AccessExclusiveLock);
98         index_close(rel);
99 }
100
101 static Relation
102 gin_index_open(RangeVar *relvar) {
103         Relation rel = index_openrv(relvar);
104
105         LockRelation(rel, AccessShareLock);
106         return checkOpenedRelation(rel, GIN_AM_OID);
107 }
108
109 static void
110 gin_index_close(Relation rel) {
111         UnlockRelation(rel, AccessShareLock);
112         index_close(rel);
113 }
114
115 #endif
116
117 #if PG_VERSION_NUM >= 80300
118 #define stringToQualifiedNameList(x,y)  stringToQualifiedNameList(x)
119 #endif
120
121 #if PG_VERSION_NUM < 80300
122 #define SET_VARSIZE(p,l)        VARATT_SIZEP(p)=(l)
123 #endif
124
125 static Relation 
126 checkOpenedRelation(Relation r, Oid PgAmOid) {
127         if ( r->rd_am == NULL )
128                 elog(ERROR, "Relation %s.%s is not an index",
129                                         get_namespace_name(RelationGetNamespace(r)),
130                                         RelationGetRelationName(r)
131                         );
132
133         if ( r->rd_rel->relam != PgAmOid )
134                 elog(ERROR, "Index %s.%s has wrong type",
135                                         get_namespace_name(RelationGetNamespace(r)),
136                                         RelationGetRelationName(r)
137                         );
138         
139         return r;
140 }
141
142 static void
143 gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo *info) {
144         Buffer          buffer;
145         Page            page;
146         IndexTuple      which;
147         ItemId          iid;
148         OffsetNumber i,
149                                 maxoff;
150         BlockNumber cblk;
151         char       *pred;
152
153         pred = (char *) palloc(sizeof(char) * level * 4 + 1);
154         MemSet(pred, ' ', level*4);
155         pred[level*4] = '\0';
156
157         buffer = ReadBuffer(r, blk);
158         page = (Page) BufferGetPage(buffer);
159
160         maxoff = PageGetMaxOffsetNumber(page);
161
162
163         while ( (info->ptr-((char*)info->txt)) + level*4 + 128 >= info->len ) {
164                 int dist=info->ptr-((char*)info->txt);
165                 info->len *= 2;
166                 info->txt=(text*)repalloc(info->txt, info->len);
167                 info->ptr = ((char*)info->txt)+dist;
168         }
169
170         sprintf(info->ptr, "%s%d(l:%d) blk: %u numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n", 
171                 pred,
172                 coff, 
173                 level, 
174                 blk,
175                 (int) maxoff, 
176                 (int) PageGetFreeSpace(page),  
177                 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE),
178                 GistPageGetOpaque(page)->rightlink,
179                 ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" );
180         info->ptr=strchr(info->ptr,'\0');
181
182         if (!GistPageIsLeaf(page) && ( info->maxlevel<0 || level<info->maxlevel ) )
183                 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
184                         iid = PageGetItemId(page, i);
185                         which = (IndexTuple) PageGetItem(page, iid);
186                         cblk = ItemPointerGetBlockNumber(&(which->t_tid));
187                         gist_dumptree(r, level + 1, cblk, i, info);
188                 }
189         ReleaseBuffer(buffer);
190         pfree(pred);
191 }
192
193 PG_FUNCTION_INFO_V1(gist_tree);
194 Datum   gist_tree(PG_FUNCTION_ARGS);
195 Datum
196 gist_tree(PG_FUNCTION_ARGS) {
197         text    *name=PG_GETARG_TEXT_P(0);
198         char *relname=t2c(name);
199         RangeVar   *relvar;
200         Relation        index;
201         List       *relname_list;
202         IdxInfo info;
203
204         relname_list = stringToQualifiedNameList(relname, "gist_tree");
205         relvar = makeRangeVarFromNameList(relname_list);
206         index = gist_index_open(relvar);
207         PG_FREE_IF_COPY(name,0);
208
209         info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1; 
210         info.len=1024;
211         info.txt=(text*)palloc( info.len );
212         info.ptr=((char*)info.txt)+VARHDRSZ;
213
214         gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0, &info);
215
216         gist_index_close(index);
217         pfree(relname);
218
219         SET_VARSIZE(info.txt, info.ptr-((char*)info.txt));
220         PG_RETURN_POINTER(info.txt);
221 }
222
223 typedef struct {
224         int     level;
225         int     numpages;
226         int     numleafpages;
227         int     numtuple;
228         int     numinvalidtuple;
229         int     numleaftuple;
230         uint64  tuplesize;
231         uint64  leaftuplesize;
232         uint64  totalsize;
233 } IdxStat;
234
235 static void
236 gist_stattree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxStat *info) {
237         Buffer          buffer;
238         Page            page;
239         IndexTuple      which;
240         ItemId          iid;
241         OffsetNumber i,
242                                 maxoff;
243         BlockNumber cblk;
244         char       *pred;
245
246         pred = (char *) palloc(sizeof(char) * level * 4 + 1);
247         MemSet(pred, ' ', level*4);
248         pred[level*4] = '\0';
249
250         buffer = ReadBuffer(r, blk);
251         page = (Page) BufferGetPage(buffer);
252
253         maxoff = PageGetMaxOffsetNumber(page);
254
255         info->numpages++;
256         info->tuplesize+=PAGESIZE-PageGetFreeSpace(page);
257         info->totalsize+=BLCKSZ;
258         info->numtuple+=maxoff;
259         if ( info->level < level )
260                 info->level = level;
261
262         if (GistPageIsLeaf(page)) {
263                 info->numleafpages++;
264                 info->leaftuplesize+=PAGESIZE-PageGetFreeSpace(page);
265                 info->numleaftuple+=maxoff;
266         } else {
267                 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
268                         iid = PageGetItemId(page, i);
269                         which = (IndexTuple) PageGetItem(page, iid);
270                         if ( GistTupleIsInvalid(which) )
271                                 info->numinvalidtuple++;
272                         cblk = ItemPointerGetBlockNumber(&(which->t_tid));
273                                 gist_stattree(r, level + 1, cblk, i, info);
274                 }
275         }
276
277         ReleaseBuffer(buffer);
278         pfree(pred);
279 }
280
281 PG_FUNCTION_INFO_V1(gist_stat);
282 Datum   gist_stat(PG_FUNCTION_ARGS);
283 Datum
284 gist_stat(PG_FUNCTION_ARGS) {
285         text    *name=PG_GETARG_TEXT_P(0);
286         char *relname=t2c(name);
287         RangeVar   *relvar;
288         Relation        index;
289         List       *relname_list;
290         IdxStat info;
291         text *out=(text*)palloc(1024);
292         char *ptr=((char*)out)+VARHDRSZ;
293
294
295         relname_list = stringToQualifiedNameList(relname, "gist_tree");
296         relvar = makeRangeVarFromNameList(relname_list);
297         index = gist_index_open(relvar);
298         PG_FREE_IF_COPY(name,0);
299
300         memset(&info, 0, sizeof(IdxStat));
301
302         gist_stattree(index, 0, GIST_ROOT_BLKNO, 0, &info);
303
304         gist_index_close(index);
305         pfree(relname);
306
307         sprintf(ptr, 
308                 "Number of levels:          %d\n"
309                 "Number of pages:           %d\n"
310                 "Number of leaf pages:      %d\n"
311                 "Number of tuples:          %d\n"
312                 "Number of invalid tuples:  %d\n"
313                 "Number of leaf tuples:     %d\n"
314                 "Total size of tuples:      "INT64_FORMAT" bytes\n"
315                 "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
316                 "Total size of index:       "INT64_FORMAT" bytes\n",
317                 info.level+1,
318                 info.numpages,
319                 info.numleafpages,
320                 info.numtuple,
321                 info.numinvalidtuple,
322                 info.numleaftuple,
323                 info.tuplesize,
324                 info.leaftuplesize,
325                 info.totalsize);
326
327         ptr=strchr(ptr,'\0');
328                  
329         SET_VARSIZE(out, ptr-((char*)out));
330         PG_RETURN_POINTER(out);
331 }
332
333 typedef struct GPItem {
334         Buffer  buffer;
335         Page    page;
336         OffsetNumber    offset;
337         int     level;
338         struct GPItem *next;
339 } GPItem;
340
341 typedef struct {
342         List    *relname_list;
343         RangeVar   *relvar;
344         Relation        index;
345         Datum   *dvalues;
346         char    *nulls;
347         GPItem  *item;
348 } TypeStorage;
349
350 static GPItem*
351 openGPPage( FuncCallContext *funcctx, BlockNumber blk ) {
352         GPItem  *nitem;
353         MemoryContext     oldcontext;
354         Relation index = ( (TypeStorage*)(funcctx->user_fctx) )->index;
355         
356         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
357         nitem = (GPItem*)palloc( sizeof(GPItem) );
358         memset(nitem,0,sizeof(GPItem));
359
360         nitem->buffer = ReadBuffer(index, blk);
361         nitem->page = (Page) BufferGetPage(nitem->buffer);
362         nitem->offset=FirstOffsetNumber;
363         nitem->next = ( (TypeStorage*)(funcctx->user_fctx) )->item;
364         nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1; 
365         ( (TypeStorage*)(funcctx->user_fctx) )->item = nitem;
366
367         MemoryContextSwitchTo(oldcontext);
368         return nitem;
369
370
371 static GPItem*
372 closeGPPage( FuncCallContext *funcctx ) {
373         GPItem  *oitem = ( (TypeStorage*)(funcctx->user_fctx) )->item;
374
375         ( (TypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
376         
377         ReleaseBuffer(oitem->buffer);
378         pfree( oitem );
379         return ( (TypeStorage*)(funcctx->user_fctx) )->item; 
380 }
381
382 static void
383 setup_firstcall(FuncCallContext  *funcctx, text *name) {
384         MemoryContext     oldcontext;
385         TypeStorage     *st;
386         char *relname=t2c(name);
387         TupleDesc            tupdesc;
388         char            attname[NAMEDATALEN];
389         int i;
390
391         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
392
393         st=(TypeStorage*)palloc( sizeof(TypeStorage) );
394         memset(st,0,sizeof(TypeStorage));
395         st->relname_list = stringToQualifiedNameList(relname, "gist_tree");
396         st->relvar = makeRangeVarFromNameList(st->relname_list);
397         st->index = gist_index_open(st->relvar);
398         funcctx->user_fctx = (void*)st;
399
400         tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2, false);
401         TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
402         TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
403         for (i = 0; i < st->index->rd_att->natts; i++) {
404                 sprintf(attname, "z%d", i+2);
405                 TupleDescInitEntry(
406                         tupdesc,
407                         i+3,
408                         attname,
409                         st->index->rd_att->attrs[i]->atttypid,
410                         st->index->rd_att->attrs[i]->atttypmod,
411                         st->index->rd_att->attrs[i]->attndims
412                 );
413         }
414
415         st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
416         st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(char));
417
418         funcctx->slot = TupleDescGetSlot(tupdesc);
419         funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
420
421         MemoryContextSwitchTo(oldcontext);
422         pfree(relname);
423
424         st->item=openGPPage(funcctx, GIST_ROOT_BLKNO);
425
426
427 static void 
428 close_call( FuncCallContext  *funcctx ) {
429         TypeStorage *st = (TypeStorage*)(funcctx->user_fctx);
430         
431         while( st->item && closeGPPage(funcctx) );
432         
433         pfree( st->dvalues );
434         pfree( st->nulls );
435
436         gist_index_close(st->index);
437 }
438
439 PG_FUNCTION_INFO_V1(gist_print);
440 Datum   gist_print(PG_FUNCTION_ARGS);
441 Datum
442 gist_print(PG_FUNCTION_ARGS) {
443         FuncCallContext  *funcctx;
444         TypeStorage     *st;
445         Datum result=(Datum)0;
446         ItemId          iid;
447         IndexTuple      ituple;
448         HeapTuple       htuple;
449         int i;
450         bool isnull;
451
452         if (SRF_IS_FIRSTCALL()) {
453                 text    *name=PG_GETARG_TEXT_P(0);
454                 funcctx = SRF_FIRSTCALL_INIT();
455                 setup_firstcall(funcctx, name);
456                 PG_FREE_IF_COPY(name,0);
457         }
458
459         funcctx = SRF_PERCALL_SETUP();  
460         st = (TypeStorage*)(funcctx->user_fctx);
461
462         if ( !st->item ) {
463                 close_call(funcctx);
464                 SRF_RETURN_DONE(funcctx);
465         }
466
467         while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) ) {
468                 if ( ! closeGPPage(funcctx) ) {
469                         close_call(funcctx);
470                         SRF_RETURN_DONE(funcctx);
471                 } 
472         }
473
474         iid = PageGetItemId( st->item->page, st->item->offset );
475         ituple = (IndexTuple) PageGetItem(st->item->page, iid);
476
477         st->dvalues[0] = Int32GetDatum( st->item->level );
478         st->nulls[0] = ' ';
479         st->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true );
480         st->nulls[1] = ' ';
481         for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++) {
482                 if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) ) {
483                         st->dvalues[i] = (Datum)0;
484                         st->nulls[i] = 'n';
485                 } else {
486                         st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull); 
487                         st->nulls[i] = ( isnull ) ? 'n' : ' ';
488                 }
489         }
490
491         htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
492         result = TupleGetDatum(funcctx->slot, htuple);
493         st->item->offset = OffsetNumberNext(st->item->offset);
494         if ( !GistPageIsLeaf(st->item->page) )
495                 openGPPage(funcctx, ItemPointerGetBlockNumber(&(ituple->t_tid)) );
496
497         SRF_RETURN_NEXT(funcctx, result);
498 }
499
500 typedef struct GinStatState {
501         Relation                index;
502         GinState                ginstate;
503         OffsetNumber    attnum;
504
505         Buffer                  buffer;
506         OffsetNumber    offset;
507         Datum                   curval;
508 #if PG_VERSION_NUM >= 90100
509         GinNullCategory category;
510 #endif
511         Datum                   dvalues[2];
512         char                    nulls[2];
513 } GinStatState;
514
515 static bool
516 moveRightIfItNeeded( GinStatState *st )
517 {
518         Page page = BufferGetPage(st->buffer);
519
520         if ( st->offset > PageGetMaxOffsetNumber(page) ) {
521                 /*
522                 * We scaned the whole page, so we should take right page
523                 */
524                 BlockNumber blkno = GinPageGetOpaque(page)->rightlink;               
525
526                 if ( GinPageRightMost(page) )
527                         return false;  /* no more page */
528
529                 LockBuffer(st->buffer, GIN_UNLOCK);
530                 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
531                 LockBuffer(st->buffer, GIN_SHARE);
532                 st->offset = FirstOffsetNumber;
533         }
534
535         return true;
536 }
537
538 /*
539  * Refinds a previois position, at returns it has correctly 
540  * set offset and buffer is locked
541  */
542 static bool
543 refindPosition(GinStatState *st)
544 {
545         Page    page;        
546
547         /* find left if needed (it causes only for first search) */
548         for (;;) {
549                 IndexTuple  itup;
550                 BlockNumber blkno;
551
552                 LockBuffer(st->buffer, GIN_SHARE);
553
554                 page = BufferGetPage(st->buffer);
555                 if (GinPageIsLeaf(page))
556                         break;
557
558                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
559                 blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid);
560
561                 LockBuffer(st->buffer,GIN_UNLOCK);
562                 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
563         }
564
565         if (st->offset == InvalidOffsetNumber) {
566                 return (PageGetMaxOffsetNumber(page) >= FirstOffsetNumber ) ? true : false; /* first one */
567         }
568
569         for(;;) {
570                 int                     cmp;
571 #if PG_VERSION_NUM >= 90100
572                 GinNullCategory category;
573 #elif PG_VERSION_NUM < 80400
574                 bool                    isnull = false;
575 #endif
576                 Datum                   datum;
577                 IndexTuple              itup;
578
579                 if (moveRightIfItNeeded(st)==false)
580                         return false;
581
582                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
583 #if PG_VERSION_NUM >= 90100
584                 datum = gintuple_get_key(&st->ginstate, itup, &category); 
585                 cmp = ginCompareAttEntries(&st->ginstate,
586                                                                         st->attnum + 1, st->curval, st->category,
587                                                                         gintuple_get_attrnum(&st->ginstate, itup), datum, category);
588 #elif PG_VERSION_NUM >= 80400
589                 datum = gin_index_getattr(&st->ginstate, itup);
590
591                 cmp = compareAttEntries(&st->ginstate,
592                                                                         st->attnum + 1, st->curval,
593                                                                         gintuple_get_attrnum(&st->ginstate, itup), datum);
594 #else
595                 datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull);
596
597                 cmp = DatumGetInt32(
598                                 FunctionCall2(
599                                                 &st->ginstate.compareFn,
600                                                 st->curval,
601                                                 datum
602                                         ));
603 #endif
604                 if ( cmp == 0 )
605                 {
606                         if ( !st->index->rd_att->attrs[st->attnum]->attbyval )
607                                 pfree( (void*) st->curval );
608                         return true;
609                 }
610
611                 st->offset++;
612         }
613
614         return false;
615 }
616
617 static void
618 gin_setup_firstcall(FuncCallContext  *funcctx, text *name, int attnum) {
619         MemoryContext     oldcontext;
620         GinStatState     *st;
621         char *relname=t2c(name);
622         TupleDesc            tupdesc;
623
624         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
625
626         st=(GinStatState*)palloc( sizeof(GinStatState) );
627         memset(st,0,sizeof(GinStatState));
628         st->index = gin_index_open(
629                  makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_stat")));
630         initGinState( &st->ginstate, st->index );
631
632 #if PG_VERSION_NUM >= 80400
633         if (attnum < 0 || attnum >= st->index->rd_att->natts)
634                 elog(ERROR,"Wrong column's number");
635         st->attnum = attnum;
636 #else
637         st->attnum = 0;
638 #endif
639
640         funcctx->user_fctx = (void*)st;
641
642         tupdesc = CreateTemplateTupleDesc(2, false);
643         TupleDescInitEntry(tupdesc, 1, "value", 
644                         st->index->rd_att->attrs[st->attnum]->atttypid, 
645                         st->index->rd_att->attrs[st->attnum]->atttypmod,
646                         st->index->rd_att->attrs[st->attnum]->attndims);
647         TupleDescInitEntry(tupdesc, 2, "nrow", INT4OID, -1, 0);
648
649         memset( st->nulls, ' ', 2*sizeof(char) );
650
651         funcctx->slot = TupleDescGetSlot(tupdesc);
652         funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
653
654         MemoryContextSwitchTo(oldcontext);
655         pfree(relname);
656
657         st->offset = InvalidOffsetNumber;
658         st->buffer = ReadBuffer( st->index, GIN_ROOT_BLKNO );
659 }
660
661 static void
662 processTuple( FuncCallContext  *funcctx,  GinStatState *st, IndexTuple itup ) {
663         MemoryContext           oldcontext;
664 #if PG_VERSION_NUM < 80400
665         bool                            isnull;
666 #endif
667
668         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
669         st->curval = datumCopy(
670 #if PG_VERSION_NUM >= 90100
671                                         gintuple_get_key(&st->ginstate, itup, &st->category),
672 #elif PG_VERSION_NUM >= 80400
673                                         gin_index_getattr(&st->ginstate, itup),
674 #else
675                                         index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull),
676 #endif
677                                         st->index->rd_att->attrs[st->attnum]->attbyval,
678                                         st->index->rd_att->attrs[st->attnum]->attlen );
679         MemoryContextSwitchTo(oldcontext);
680
681         st->dvalues[0] = st->curval;
682 #if PG_VERSION_NUM >= 90100
683         /* do no distiguish various null category */
684         st->nulls[0] = (st->category == GIN_CAT_NORM_KEY) ? ' ' : 'n';
685 #endif
686                 
687         if ( GinIsPostingTree(itup) ) {
688                 BlockNumber     rootblkno = GinGetPostingTree(itup);
689                 GinPostingTreeScan *gdi;
690                 Buffer          entrybuffer;              
691                 Page        page;
692
693                 LockBuffer(st->buffer, GIN_UNLOCK);
694 #if PG_VERSION_NUM >= 90100
695                 gdi = ginPrepareScanPostingTree(st->index, rootblkno, TRUE);
696                 entrybuffer = ginScanBeginPostingTree(gdi);
697 #else
698                 gdi = prepareScanPostingTree(st->index, rootblkno, TRUE);
699                 entrybuffer = scanBeginPostingTree(gdi);
700 #endif
701
702                 page = BufferGetPage(entrybuffer);
703                 st->dvalues[1] = Int32GetDatum( gdi->stack->predictNumber * GinPageGetOpaque(page)->maxoff );
704
705                 LockBuffer(entrybuffer, GIN_UNLOCK);
706                 freeGinBtreeStack(gdi->stack);
707                 pfree(gdi);
708         } else {
709                 st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) );
710                 LockBuffer(st->buffer, GIN_UNLOCK);
711         }
712 }
713
714 PG_FUNCTION_INFO_V1(gin_stat);
715 Datum   gin_stat(PG_FUNCTION_ARGS);
716 Datum
717 gin_stat(PG_FUNCTION_ARGS) {
718         FuncCallContext  *funcctx;
719         GinStatState     *st;
720         Datum result=(Datum)0;
721         IndexTuple      ituple;
722         HeapTuple       htuple;
723         Page page;
724
725         if (SRF_IS_FIRSTCALL()) {
726                 text    *name=PG_GETARG_TEXT_P(0);
727                 funcctx = SRF_FIRSTCALL_INIT();
728                 gin_setup_firstcall(funcctx, name, (PG_NARGS()==2) ? PG_GETARG_INT32(1) : 0 );
729                 PG_FREE_IF_COPY(name,0);
730         }
731
732         funcctx = SRF_PERCALL_SETUP();  
733         st = (GinStatState*)(funcctx->user_fctx);
734
735         if ( refindPosition(st) == false ) {
736                 UnlockReleaseBuffer( st->buffer );
737                 gin_index_close(st->index);
738
739                 SRF_RETURN_DONE(funcctx);
740         }
741
742         for(;;) {
743                 st->offset++;
744         
745                 if (moveRightIfItNeeded(st)==false) { 
746                         UnlockReleaseBuffer( st->buffer );
747                         gin_index_close(st->index);
748
749                         SRF_RETURN_DONE(funcctx);
750                 }
751
752                 page = BufferGetPage(st->buffer);
753                 ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset)); 
754
755 #if PG_VERSION_NUM >= 80400
756                 if (st->attnum + 1 == gintuple_get_attrnum(&st->ginstate, ituple))
757 #endif
758                         break;
759         }
760
761         processTuple( funcctx,  st, ituple );
762         
763         htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
764         result = TupleGetDatum(funcctx->slot, htuple);
765
766         SRF_RETURN_NEXT(funcctx, result);
767 }
768
769 PG_FUNCTION_INFO_V1(gin_count_estimate);
770 Datum gin_count_estimate(PG_FUNCTION_ARGS);
771 #if PG_VERSION_NUM >= 80300
772 Datum
773 gin_count_estimate(PG_FUNCTION_ARGS) {
774         text                    *name=PG_GETARG_TEXT_P(0);
775         Relation                index;
776         IndexScanDesc   scan;
777         int64                   count = 0;
778         char                    *relname=t2c(name);
779         ScanKeyData             key;
780 #if PG_VERSION_NUM >= 80400
781         TIDBitmap               *bitmap = tbm_create(work_mem * 1024L);
782 #else
783 #define MAXTIDS         1024
784         ItemPointerData tids[MAXTIDS];
785         int32                   returned_tids;
786         bool                    more;
787 #endif
788
789         index = gin_index_open(
790                  makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_count_estimate")));
791
792         if ( index->rd_opcintype[0] != TSVECTOROID ) {
793                 gin_index_close(index);
794                 elog(ERROR, "Column type is not a tsvector");
795         }
796
797         key.sk_flags    = 0;
798         key.sk_attno    = 1;
799         key.sk_strategy = TSearchStrategyNumber;
800         key.sk_subtype  = 0;
801         key.sk_argument = PG_GETARG_DATUM(1);
802
803         fmgr_info( F_TS_MATCH_VQ , &key.sk_func );
804
805 #if PG_VERSION_NUM >= 90100
806         scan = index_beginscan_bitmap(index, SnapshotNow, 1);
807         index_rescan(scan, &key, 1, NULL, 0);
808
809         count = index_getbitmap(scan, bitmap);
810         tbm_free(bitmap);
811 #elif PG_VERSION_NUM >= 80400
812         scan = index_beginscan_bitmap(index, SnapshotNow, 1, &key);
813
814         count = index_getbitmap(scan, bitmap);
815         tbm_free(bitmap);
816 #else
817         scan = index_beginscan_multi(index, SnapshotNow, 1, &key);
818
819         do {
820                 more = index_getmulti(scan, tids, MAXTIDS, &returned_tids);
821                 count += returned_tids;
822         } while(more);
823 #endif
824
825         index_endscan( scan );
826         gin_index_close(index);
827
828         PG_RETURN_INT64(count);
829 }
830 #else
831 Datum
832 gin_count_estimate(PG_FUNCTION_ARGS) {
833         elog(NOTICE, "Function is not working under PgSQL < 8.3");
834
835         PG_RETURN_INT64(0);
836 }
837 #endif