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