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