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 #include "utils/builtins.h"
14 #include "utils/lsyscache.h"
15 #include "utils/datum.h"
18 #include <access/heapam.h>
19 #include <catalog/pg_type.h>
21 #define PAGESIZE (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData)))
24 #define PG_NARGS() (fcinfo->nargs)
29 char *out=palloc( VARSIZE(in) );
30 memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ);
31 out[ VARSIZE(in)-VARHDRSZ ] ='\0';
42 static Relation checkOpenedRelation(Relation r, Oid PgAmOid);
44 #ifdef PG_MODULE_MAGIC
50 gist_index_open(RangeVar *relvar) {
51 Oid relOid = RangeVarGetRelid(relvar, false);
52 return checkOpenedRelation(
53 index_open(relOid, AccessExclusiveLock), GIST_AM_OID);
56 #define gist_index_close(r) index_close((r), AccessExclusiveLock)
59 gin_index_open(RangeVar *relvar) {
60 Oid relOid = RangeVarGetRelid(relvar, false);
61 return checkOpenedRelation(
62 index_open(relOid, AccessShareLock), GIN_AM_OID);
65 #define gin_index_close(r) index_close((r), AccessShareLock)
70 gist_index_open(RangeVar *relvar) {
71 Relation rel = index_openrv(relvar);
73 LockRelation(rel, AccessExclusiveLock);
74 return checkOpenedRelation(rel, GIST_AM_OID);
78 gist_index_close(Relation rel) {
79 UnlockRelation(rel, AccessExclusiveLock);
84 gin_index_open(RangeVar *relvar) {
85 Relation rel = index_openrv(relvar);
87 LockRelation(rel, AccessShareLock);
88 return checkOpenedRelation(rel, GIN_AM_OID);
92 gin_index_close(Relation rel) {
93 UnlockRelation(rel, AccessShareLock);
99 #if PG_VERSION_NUM >= 80300
100 #define stringToQualifiedNameList(x,y) stringToQualifiedNameList(x)
103 #if PG_VERSION_NUM < 80300
104 #define SET_VARSIZE(p,l) VARATT_SIZEP(p)=(l)
108 checkOpenedRelation(Relation r, Oid PgAmOid) {
109 if ( r->rd_am == NULL )
110 elog(ERROR, "Relation %s.%s is not an index",
111 get_namespace_name(RelationGetNamespace(r)),
112 RelationGetRelationName(r)
115 if ( r->rd_rel->relam != PgAmOid )
116 elog(ERROR, "Index %s.%s has wrong type",
117 get_namespace_name(RelationGetNamespace(r)),
118 RelationGetRelationName(r)
125 gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo *info) {
135 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
136 MemSet(pred, ' ', level*4);
137 pred[level*4] = '\0';
139 buffer = ReadBuffer(r, blk);
140 page = (Page) BufferGetPage(buffer);
142 maxoff = PageGetMaxOffsetNumber(page);
145 while ( (info->ptr-((char*)info->txt)) + level*4 + 128 >= info->len ) {
146 int dist=info->ptr-((char*)info->txt);
148 info->txt=(text*)repalloc(info->txt, info->len);
149 info->ptr = ((char*)info->txt)+dist;
152 sprintf(info->ptr, "%s%d(l:%d) blk: %d numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n",
158 PageGetFreeSpace(page),
159 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE),
160 GistPageGetOpaque(page)->rightlink,
161 ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" );
162 info->ptr=strchr(info->ptr,'\0');
164 if (!GistPageIsLeaf(page) && ( info->maxlevel<0 || level<info->maxlevel ) )
165 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
166 iid = PageGetItemId(page, i);
167 which = (IndexTuple) PageGetItem(page, iid);
168 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
169 gist_dumptree(r, level + 1, cblk, i, info);
171 ReleaseBuffer(buffer);
175 PG_FUNCTION_INFO_V1(gist_tree);
176 Datum gist_tree(PG_FUNCTION_ARGS);
178 gist_tree(PG_FUNCTION_ARGS) {
179 text *name=PG_GETARG_TEXT_P(0);
180 char *relname=t2c(name);
186 relname_list = stringToQualifiedNameList(relname, "gist_tree");
187 relvar = makeRangeVarFromNameList(relname_list);
188 index = gist_index_open(relvar);
189 PG_FREE_IF_COPY(name,0);
191 info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1;
193 info.txt=(text*)palloc( info.len );
194 info.ptr=((char*)info.txt)+VARHDRSZ;
196 gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0, &info);
198 gist_index_close(index);
201 SET_VARSIZE(info.txt, info.ptr-((char*)info.txt));
202 PG_RETURN_POINTER(info.txt);
213 uint64 leaftuplesize;
218 gist_stattree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxStat *info) {
228 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
229 MemSet(pred, ' ', level*4);
230 pred[level*4] = '\0';
232 buffer = ReadBuffer(r, blk);
233 page = (Page) BufferGetPage(buffer);
235 maxoff = PageGetMaxOffsetNumber(page);
238 info->tuplesize+=PAGESIZE-PageGetFreeSpace(page);
239 info->totalsize+=BLCKSZ;
240 info->numtuple+=maxoff;
241 if ( info->level < level )
244 if (GistPageIsLeaf(page)) {
245 info->numleafpages++;
246 info->leaftuplesize+=PAGESIZE-PageGetFreeSpace(page);
247 info->numleaftuple+=maxoff;
249 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
250 iid = PageGetItemId(page, i);
251 which = (IndexTuple) PageGetItem(page, iid);
252 if ( GistTupleIsInvalid(which) )
253 info->numinvalidtuple++;
254 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
255 gist_stattree(r, level + 1, cblk, i, info);
259 ReleaseBuffer(buffer);
263 PG_FUNCTION_INFO_V1(gist_stat);
264 Datum gist_stat(PG_FUNCTION_ARGS);
266 gist_stat(PG_FUNCTION_ARGS) {
267 text *name=PG_GETARG_TEXT_P(0);
268 char *relname=t2c(name);
273 text *out=(text*)palloc(1024);
274 char *ptr=((char*)out)+VARHDRSZ;
277 relname_list = stringToQualifiedNameList(relname, "gist_tree");
278 relvar = makeRangeVarFromNameList(relname_list);
279 index = gist_index_open(relvar);
280 PG_FREE_IF_COPY(name,0);
282 memset(&info, 0, sizeof(IdxStat));
284 gist_stattree(index, 0, GIST_ROOT_BLKNO, 0, &info);
286 gist_index_close(index);
290 "Number of levels: %d\n"
291 "Number of pages: %d\n"
292 "Number of leaf pages: %d\n"
293 "Number of tuples: %d\n"
294 "Number of invalid tuples: %d\n"
295 "Number of leaf tuples: %d\n"
296 "Total size of tuples: "INT64_FORMAT" bytes\n"
297 "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
298 "Total size of index: "INT64_FORMAT" bytes\n",
303 info.numinvalidtuple,
309 ptr=strchr(ptr,'\0');
311 SET_VARSIZE(out, ptr-((char*)out));
312 PG_RETURN_POINTER(out);
315 typedef struct GPItem {
333 openGPPage( FuncCallContext *funcctx, BlockNumber blk ) {
335 MemoryContext oldcontext;
336 Relation index = ( (TypeStorage*)(funcctx->user_fctx) )->index;
338 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
339 nitem = (GPItem*)palloc( sizeof(GPItem) );
340 memset(nitem,0,sizeof(GPItem));
342 nitem->buffer = ReadBuffer(index, blk);
343 nitem->page = (Page) BufferGetPage(nitem->buffer);
344 nitem->offset=FirstOffsetNumber;
345 nitem->next = ( (TypeStorage*)(funcctx->user_fctx) )->item;
346 nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1;
347 ( (TypeStorage*)(funcctx->user_fctx) )->item = nitem;
349 MemoryContextSwitchTo(oldcontext);
354 closeGPPage( FuncCallContext *funcctx ) {
355 GPItem *oitem = ( (TypeStorage*)(funcctx->user_fctx) )->item;
357 ( (TypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
359 ReleaseBuffer(oitem->buffer);
361 return ( (TypeStorage*)(funcctx->user_fctx) )->item;
365 setup_firstcall(FuncCallContext *funcctx, text *name) {
366 MemoryContext oldcontext;
368 char *relname=t2c(name);
370 char attname[NAMEDATALEN];
373 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
375 st=(TypeStorage*)palloc( sizeof(TypeStorage) );
376 memset(st,0,sizeof(TypeStorage));
377 st->relname_list = stringToQualifiedNameList(relname, "gist_tree");
378 st->relvar = makeRangeVarFromNameList(st->relname_list);
379 st->index = gist_index_open(st->relvar);
380 funcctx->user_fctx = (void*)st;
382 tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2, false);
383 TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
384 TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
385 for (i = 0; i < st->index->rd_att->natts; i++) {
386 sprintf(attname, "z%d", i+2);
391 st->index->rd_att->attrs[i]->atttypid,
392 st->index->rd_att->attrs[i]->atttypmod,
393 st->index->rd_att->attrs[i]->attndims
397 st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
398 st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(char));
400 funcctx->slot = TupleDescGetSlot(tupdesc);
401 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
403 MemoryContextSwitchTo(oldcontext);
406 st->item=openGPPage(funcctx, GIST_ROOT_BLKNO);
410 close_call( FuncCallContext *funcctx ) {
411 TypeStorage *st = (TypeStorage*)(funcctx->user_fctx);
413 while( st->item && closeGPPage(funcctx) );
415 pfree( st->dvalues );
418 gist_index_close(st->index);
421 PG_FUNCTION_INFO_V1(gist_print);
422 Datum gist_print(PG_FUNCTION_ARGS);
424 gist_print(PG_FUNCTION_ARGS) {
425 FuncCallContext *funcctx;
427 Datum result=(Datum)0;
434 if (SRF_IS_FIRSTCALL()) {
435 text *name=PG_GETARG_TEXT_P(0);
436 funcctx = SRF_FIRSTCALL_INIT();
437 setup_firstcall(funcctx, name);
438 PG_FREE_IF_COPY(name,0);
441 funcctx = SRF_PERCALL_SETUP();
442 st = (TypeStorage*)(funcctx->user_fctx);
446 SRF_RETURN_DONE(funcctx);
449 while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) ) {
450 if ( ! closeGPPage(funcctx) ) {
452 SRF_RETURN_DONE(funcctx);
456 iid = PageGetItemId( st->item->page, st->item->offset );
457 ituple = (IndexTuple) PageGetItem(st->item->page, iid);
459 st->dvalues[0] = Int32GetDatum( st->item->level );
461 st->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true );
463 for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++) {
464 if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) ) {
465 st->dvalues[i] = (Datum)0;
468 st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull);
469 st->nulls[i] = ( isnull ) ? 'n' : ' ';
473 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
474 result = TupleGetDatum(funcctx->slot, htuple);
475 st->item->offset = OffsetNumberNext(st->item->offset);
476 if ( !GistPageIsLeaf(st->item->page) )
477 openGPPage(funcctx, ItemPointerGetBlockNumber(&(ituple->t_tid)) );
479 SRF_RETURN_NEXT(funcctx, result);
482 typedef struct GinStatState {
494 moveRightIfItNeeded( GinStatState *st )
496 Page page = BufferGetPage(st->buffer);
498 if ( st->offset > PageGetMaxOffsetNumber(page) ) {
500 * We scaned the whole page, so we should take right page
502 BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
504 if ( GinPageRightMost(page) )
505 return false; /* no more page */
507 LockBuffer(st->buffer, GIN_UNLOCK);
508 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
509 LockBuffer(st->buffer, GIN_SHARE);
510 st->offset = FirstOffsetNumber;
517 * Refinds a previois position, at returns it has correctly
518 * set offset and buffer is locked
521 refindPosition(GinStatState *st)
525 /* find left if needed (it causes only for first search) */
530 LockBuffer(st->buffer, GIN_SHARE);
532 page = BufferGetPage(st->buffer);
533 if (GinPageIsLeaf(page))
536 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
537 blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid);
539 LockBuffer(st->buffer,GIN_UNLOCK);
540 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
543 if (st->offset == InvalidOffsetNumber) {
544 return (PageGetMaxOffsetNumber(page) >= FirstOffsetNumber ) ? true : false; /* first one */
553 if (moveRightIfItNeeded(st)==false)
556 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
557 datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull);
560 &st->ginstate.compareFn,
566 if ( !st->ginstate.tupdesc->attrs[0]->attbyval )
567 pfree( (void*) st->curval );
578 gin_setup_firstcall(FuncCallContext *funcctx, text *name) {
579 MemoryContext oldcontext;
581 char *relname=t2c(name);
584 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
586 st=(GinStatState*)palloc( sizeof(GinStatState) );
587 memset(st,0,sizeof(GinStatState));
588 st->index = gin_index_open(
589 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gist_tree")));
590 initGinState( &st->ginstate, st->index );
592 funcctx->user_fctx = (void*)st;
594 tupdesc = CreateTemplateTupleDesc(2, false);
595 TupleDescInitEntry(tupdesc, 1, "value",
596 st->index->rd_att->attrs[0]->atttypid,
597 st->index->rd_att->attrs[0]->atttypmod,
598 st->index->rd_att->attrs[0]->attndims);
599 TupleDescInitEntry(tupdesc, 2, "nrow", INT4OID, -1, 0);
601 memset( st->nulls, ' ', 2*sizeof(char) );
603 funcctx->slot = TupleDescGetSlot(tupdesc);
604 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
606 MemoryContextSwitchTo(oldcontext);
609 st->offset = InvalidOffsetNumber;
610 st->buffer = ReadBuffer( st->index, GIN_ROOT_BLKNO );
614 processTuple( FuncCallContext *funcctx, GinStatState *st, IndexTuple itup ) {
615 MemoryContext oldcontext;
619 datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull);
620 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
621 st->curval = datumCopy(
622 index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull),
623 st->ginstate.tupdesc->attrs[0]->attbyval,
624 st->ginstate.tupdesc->attrs[0]->attlen );
625 MemoryContextSwitchTo(oldcontext);
627 st->dvalues[0] = st->curval;
629 if ( GinIsPostingTree(itup) ) {
630 BlockNumber rootblkno = GinGetPostingTree(itup);
631 GinPostingTreeScan *gdi;
635 LockBuffer(st->buffer, GIN_UNLOCK);
636 gdi = prepareScanPostingTree(st->index, rootblkno, TRUE);
637 entrybuffer = scanBeginPostingTree(gdi);
639 page = BufferGetPage(entrybuffer);
640 st->dvalues[1] = Int32GetDatum( gdi->stack->predictNumber * GinPageGetOpaque(page)->maxoff );
642 LockBuffer(entrybuffer, GIN_UNLOCK);
643 freeGinBtreeStack(gdi->stack);
646 st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) );
647 LockBuffer(st->buffer, GIN_UNLOCK);
651 PG_FUNCTION_INFO_V1(gin_stat);
652 Datum gin_stat(PG_FUNCTION_ARGS);
654 gin_stat(PG_FUNCTION_ARGS) {
655 FuncCallContext *funcctx;
657 Datum result=(Datum)0;
662 if (SRF_IS_FIRSTCALL()) {
663 text *name=PG_GETARG_TEXT_P(0);
664 funcctx = SRF_FIRSTCALL_INIT();
665 gin_setup_firstcall(funcctx, name);
666 PG_FREE_IF_COPY(name,0);
669 funcctx = SRF_PERCALL_SETUP();
670 st = (GinStatState*)(funcctx->user_fctx);
672 if ( refindPosition(st) == false ) {
673 UnlockReleaseBuffer( st->buffer );
674 gin_index_close(st->index);
676 SRF_RETURN_DONE(funcctx);
681 if (moveRightIfItNeeded(st)==false) {
682 UnlockReleaseBuffer( st->buffer );
683 gin_index_close(st->index);
685 SRF_RETURN_DONE(funcctx);
688 page = BufferGetPage(st->buffer);
689 ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
691 processTuple( funcctx, st, ituple );
693 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
694 result = TupleGetDatum(funcctx->slot, htuple);
696 SRF_RETURN_NEXT(funcctx, result);