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