Add tests, make complile for different versions (8.3+)
[gevel.git] / gevel.c
1 #include "postgres.h"
2
3 #include "access/genam.h"
4 #include "access/gist.h"
5 #include "access/gist_private.h"
6 #include "access/gistscan.h"
7 #include "access/heapam.h"
8 #include "catalog/index.h"
9 #include "miscadmin.h"
10 #include "storage/lmgr.h"
11 #include "catalog/namespace.h"
12 #include "utils/builtins.h"
13 #include <fmgr.h>
14 #include <funcapi.h>
15 #include <access/heapam.h>
16 #include <catalog/pg_type.h>
17
18 #define PAGESIZE        (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData)))
19
20 #ifndef PG_NARGS
21 #define PG_NARGS() (fcinfo->nargs)
22 #endif
23
24 static char
25 *t2c(text* in) {
26         char *out=palloc( VARSIZE(in) );
27         memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ);
28         out[ VARSIZE(in)-VARHDRSZ ] ='\0';
29         return out;
30 }
31
32 typedef struct {
33         int maxlevel;
34         text    *txt;
35         char    *ptr;
36         int     len;    
37 } IdxInfo;
38
39
40 #ifdef PG_MODULE_MAGIC
41 /* >= 8.2 */ 
42
43 PG_MODULE_MAGIC;
44
45 static Relation
46 gist_index_open(RangeVar *relvar) {
47         Oid relOid = RangeVarGetRelid(relvar, false);
48         return index_open(relOid, AccessExclusiveLock);
49 }
50
51 #define gist_index_close(r)     index_close((r), AccessExclusiveLock)
52
53 #else /* <8.2 */
54
55 static Relation
56 gist_index_open(RangeVar *relvar) {
57         Relation rel = index_openrv(relvar);
58
59         LockRelation(rel, AccessExclusiveLock);
60         return rel;
61 }
62
63 static void
64 gist_index_close(Relation rel) {
65         UnlockRelation(rel, AccessExclusiveLock);
66         index_close(rel);
67 }
68
69 #endif
70
71 #if PG_VERSION_NUM >= 80300
72 #define stringToQualifiedNameList(x,y)  stringToQualifiedNameList(x)
73 #endif
74
75 #if PG_VERSION_NUM < 80300
76 #define SET_VARSIZE(p,l)        VARATT_SIZEP(p)=(l)
77 #endif
78
79 static void
80 gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo *info) {
81         Buffer          buffer;
82         Page            page;
83         IndexTuple      which;
84         ItemId          iid;
85         OffsetNumber i,
86                                 maxoff;
87         BlockNumber cblk;
88         char       *pred;
89
90         pred = (char *) palloc(sizeof(char) * level * 4 + 1);
91         MemSet(pred, ' ', level*4);
92         pred[level*4] = '\0';
93
94         buffer = ReadBuffer(r, blk);
95         page = (Page) BufferGetPage(buffer);
96
97         maxoff = PageGetMaxOffsetNumber(page);
98
99
100         while ( (info->ptr-((char*)info->txt)) + level*4 + 128 >= info->len ) {
101                 int dist=info->ptr-((char*)info->txt);
102                 info->len *= 2;
103                 info->txt=(text*)repalloc(info->txt, info->len);
104                 info->ptr = ((char*)info->txt)+dist;
105         }
106
107         sprintf(info->ptr, "%s%d(l:%d) blk: %d numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n", 
108                 pred,
109                 coff, 
110                 level, 
111                 (int) blk,
112                 (int) maxoff, 
113                 PageGetFreeSpace(page),  
114                 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE),
115                 GistPageGetOpaque(page)->rightlink,
116                 ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" );
117         info->ptr=strchr(info->ptr,'\0');
118
119         if (!GistPageIsLeaf(page) && ( info->maxlevel<0 || level<info->maxlevel ) )
120                 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
121                         iid = PageGetItemId(page, i);
122                         which = (IndexTuple) PageGetItem(page, iid);
123                         cblk = ItemPointerGetBlockNumber(&(which->t_tid));
124                         gist_dumptree(r, level + 1, cblk, i, info);
125                 }
126         ReleaseBuffer(buffer);
127         pfree(pred);
128 }
129
130 PG_FUNCTION_INFO_V1(gist_tree);
131 Datum   gist_tree(PG_FUNCTION_ARGS);
132 Datum
133 gist_tree(PG_FUNCTION_ARGS) {
134         text    *name=PG_GETARG_TEXT_P(0);
135         char *relname=t2c(name);
136         RangeVar   *relvar;
137         Relation        index;
138         List       *relname_list;
139         IdxInfo info;
140
141         relname_list = stringToQualifiedNameList(relname, "gist_tree");
142         relvar = makeRangeVarFromNameList(relname_list);
143         index = gist_index_open(relvar);
144         PG_FREE_IF_COPY(name,0);
145
146         info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1; 
147         info.len=1024;
148         info.txt=(text*)palloc( info.len );
149         info.ptr=((char*)info.txt)+VARHDRSZ;
150
151         gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0, &info);
152
153         gist_index_close(index);
154         pfree(relname);
155
156         SET_VARSIZE(info.txt, info.ptr-((char*)info.txt));
157         PG_RETURN_POINTER(info.txt);
158 }
159
160 typedef struct {
161         int     level;
162         int     numpages;
163         int     numleafpages;
164         int     numtuple;
165         int     numinvalidtuple;
166         int     numleaftuple;
167         uint64  tuplesize;
168         uint64  leaftuplesize;
169         uint64  totalsize;
170 } IdxStat;
171
172 static void
173 gist_stattree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxStat *info) {
174         Buffer          buffer;
175         Page            page;
176         IndexTuple      which;
177         ItemId          iid;
178         OffsetNumber i,
179                                 maxoff;
180         BlockNumber cblk;
181         char       *pred;
182
183         pred = (char *) palloc(sizeof(char) * level * 4 + 1);
184         MemSet(pred, ' ', level*4);
185         pred[level*4] = '\0';
186
187         buffer = ReadBuffer(r, blk);
188         page = (Page) BufferGetPage(buffer);
189
190         maxoff = PageGetMaxOffsetNumber(page);
191
192         info->numpages++;
193         info->tuplesize+=PAGESIZE-PageGetFreeSpace(page);
194         info->totalsize+=BLCKSZ;
195         info->numtuple+=maxoff;
196         if ( info->level < level )
197                 info->level = level;
198
199         if (GistPageIsLeaf(page)) {
200                 info->numleafpages++;
201                 info->leaftuplesize+=PAGESIZE-PageGetFreeSpace(page);
202                 info->numleaftuple+=maxoff;
203         } else {
204                 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
205                         iid = PageGetItemId(page, i);
206                         which = (IndexTuple) PageGetItem(page, iid);
207                         if ( GistTupleIsInvalid(which) )
208                                 info->numinvalidtuple++;
209                         cblk = ItemPointerGetBlockNumber(&(which->t_tid));
210                                 gist_stattree(r, level + 1, cblk, i, info);
211                 }
212         }
213
214         ReleaseBuffer(buffer);
215         pfree(pred);
216 }
217
218 PG_FUNCTION_INFO_V1(gist_stat);
219 Datum   gist_stat(PG_FUNCTION_ARGS);
220 Datum
221 gist_stat(PG_FUNCTION_ARGS) {
222         text    *name=PG_GETARG_TEXT_P(0);
223         char *relname=t2c(name);
224         RangeVar   *relvar;
225         Relation        index;
226         List       *relname_list;
227         IdxStat info;
228         text *out=(text*)palloc(1024);
229         char *ptr=((char*)out)+VARHDRSZ;
230
231
232         relname_list = stringToQualifiedNameList(relname, "gist_tree");
233         relvar = makeRangeVarFromNameList(relname_list);
234         index = gist_index_open(relvar);
235         PG_FREE_IF_COPY(name,0);
236
237         memset(&info, 0, sizeof(IdxStat));
238
239         gist_stattree(index, 0, GIST_ROOT_BLKNO, 0, &info);
240
241         gist_index_close(index);
242         pfree(relname);
243
244         sprintf(ptr, 
245                 "Number of levels:          %d\n"
246                 "Number of pages:           %d\n"
247                 "Number of leaf pages:      %d\n"
248                 "Number of tuples:          %d\n"
249                 "Number of invalid tuples:  %d\n"
250                 "Number of leaf tuples:     %d\n"
251                 "Total size of tuples:      "INT64_FORMAT" bytes\n"
252                 "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
253                 "Total size of index:       "INT64_FORMAT" bytes\n",
254                 info.level+1,
255                 info.numpages,
256                 info.numleafpages,
257                 info.numtuple,
258                 info.numinvalidtuple,
259                 info.numleaftuple,
260                 info.tuplesize,
261                 info.leaftuplesize,
262                 info.totalsize);
263
264         ptr=strchr(ptr,'\0');
265                  
266         SET_VARSIZE(out, ptr-((char*)out));
267         PG_RETURN_POINTER(out);
268 }
269
270 typedef struct GPItem {
271         Buffer  buffer;
272         Page    page;
273         OffsetNumber    offset;
274         int     level;
275         struct GPItem *next;
276 } GPItem;
277
278 typedef struct {
279         List    *relname_list;
280         RangeVar   *relvar;
281         Relation        index;
282         Datum   *dvalues;
283         char    *nulls;
284         GPItem  *item;
285 } TypeStorage;
286
287 static GPItem*
288 openGPPage( FuncCallContext *funcctx, BlockNumber blk ) {
289         GPItem  *nitem;
290         MemoryContext     oldcontext;
291         Relation index = ( (TypeStorage*)(funcctx->user_fctx) )->index;
292         
293         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
294         nitem = (GPItem*)palloc( sizeof(GPItem) );
295         memset(nitem,0,sizeof(GPItem));
296
297         nitem->buffer = ReadBuffer(index, blk);
298         nitem->page = (Page) BufferGetPage(nitem->buffer);
299         nitem->offset=FirstOffsetNumber;
300         nitem->next = ( (TypeStorage*)(funcctx->user_fctx) )->item;
301         nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1; 
302         ( (TypeStorage*)(funcctx->user_fctx) )->item = nitem;
303
304         MemoryContextSwitchTo(oldcontext);
305         return nitem;
306
307
308 static GPItem*
309 closeGPPage( FuncCallContext *funcctx ) {
310         GPItem  *oitem = ( (TypeStorage*)(funcctx->user_fctx) )->item;
311
312         ( (TypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
313         
314         ReleaseBuffer(oitem->buffer);
315         pfree( oitem );
316         return ( (TypeStorage*)(funcctx->user_fctx) )->item; 
317 }
318
319 static void
320 setup_firstcall(FuncCallContext  *funcctx, text *name) {
321         MemoryContext     oldcontext;
322         TypeStorage     *st;
323         char *relname=t2c(name);
324         TupleDesc            tupdesc;
325         char            attname[NAMEDATALEN];
326         int i;
327
328         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
329
330         st=(TypeStorage*)palloc( sizeof(TypeStorage) );
331         memset(st,0,sizeof(TypeStorage));
332         st->relname_list = stringToQualifiedNameList(relname, "gist_tree");
333         st->relvar = makeRangeVarFromNameList(st->relname_list);
334         st->index = gist_index_open(st->relvar);
335         funcctx->user_fctx = (void*)st;
336
337         tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2, false);
338         TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
339         TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
340         for (i = 0; i < st->index->rd_att->natts; i++) {
341                 sprintf(attname, "z%d", i+2);
342                 TupleDescInitEntry(
343                         tupdesc,
344                         i+3,
345                         attname,
346                         st->index->rd_att->attrs[i]->atttypid,
347                         st->index->rd_att->attrs[i]->atttypmod,
348                         st->index->rd_att->attrs[i]->attndims
349                 );
350         }
351
352         st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
353         st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(char));
354
355         funcctx->slot = TupleDescGetSlot(tupdesc);
356         funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
357
358         MemoryContextSwitchTo(oldcontext);
359         pfree(relname);
360
361         st->item=openGPPage(funcctx, GIST_ROOT_BLKNO);
362
363
364 static void 
365 close_call( FuncCallContext  *funcctx ) {
366         TypeStorage *st = (TypeStorage*)(funcctx->user_fctx);
367         
368         while( st->item && closeGPPage(funcctx) );
369         
370         pfree( st->dvalues );
371         pfree( st->nulls );
372
373         gist_index_close(st->index);
374 }
375
376 PG_FUNCTION_INFO_V1(gist_print);
377 Datum   gist_print(PG_FUNCTION_ARGS);
378 Datum
379 gist_print(PG_FUNCTION_ARGS) {
380         FuncCallContext  *funcctx;
381         TypeStorage     *st;
382         Datum result=(Datum)0;
383         ItemId          iid;
384         IndexTuple      ituple;
385         HeapTuple       htuple;
386         int i;
387         bool isnull;
388
389         if (SRF_IS_FIRSTCALL()) {
390                 text    *name=PG_GETARG_TEXT_P(0);
391                 funcctx = SRF_FIRSTCALL_INIT();
392                 setup_firstcall(funcctx, name);
393                 PG_FREE_IF_COPY(name,0);
394         }
395
396         funcctx = SRF_PERCALL_SETUP();  
397         st = (TypeStorage*)(funcctx->user_fctx);
398
399         if ( !st->item ) {
400                 close_call(funcctx);
401                 SRF_RETURN_DONE(funcctx);
402         }
403
404         while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) ) {
405                 if ( ! closeGPPage(funcctx) ) {
406                         close_call(funcctx);
407                         SRF_RETURN_DONE(funcctx);
408                 } 
409         }
410
411         iid = PageGetItemId( st->item->page, st->item->offset );
412         ituple = (IndexTuple) PageGetItem(st->item->page, iid);
413
414         st->dvalues[0] = Int32GetDatum( st->item->level );
415         st->nulls[0] = ' ';
416         st->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true );
417         st->nulls[1] = ' ';
418         for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++) {
419                 if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) ) {
420                         st->dvalues[i] = (Datum)0;
421                         st->nulls[i] = 'n';
422                 } else {
423                         st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull); 
424                         st->nulls[i] = ( isnull ) ? 'n' : ' ';
425                 }
426         }
427
428         htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
429         result = TupleGetDatum(funcctx->slot, htuple);
430         st->item->offset = OffsetNumberNext(st->item->offset);
431         if ( !GistPageIsLeaf(st->item->page) )
432                 openGPPage(funcctx, ItemPointerGetBlockNumber(&(ituple->t_tid)) );
433
434         SRF_RETURN_NEXT(funcctx, result);
435 }
436