3 #include "access/genam.h"
4 #include "access/gin.h"
5 #include "access/gist.h"
6 #include "access/gist_private.h"
7 #include "access/gistscan.h"
8 #include "access/heapam.h"
9 #include "catalog/index.h"
10 #include "miscadmin.h"
11 #include "storage/lmgr.h"
12 #include "catalog/namespace.h"
13 #if PG_VERSION_NUM >= 80300
14 #include <tsearch/ts_utils.h>
16 #include <utils/tqual.h>
17 #include "utils/builtins.h"
18 #include "utils/lsyscache.h"
19 #include "utils/datum.h"
20 #include "utils/fmgroids.h"
23 #include <access/heapam.h>
24 #include <catalog/pg_type.h>
25 #include <access/relscan.h>
28 #define PAGESIZE (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData)))
31 #define PG_NARGS() (fcinfo->nargs)
36 char *out=palloc( VARSIZE(in) );
37 memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ);
38 out[ VARSIZE(in)-VARHDRSZ ] ='\0';
49 static Relation checkOpenedRelation(Relation r, Oid PgAmOid);
51 #ifdef PG_MODULE_MAGIC
57 gist_index_open(RangeVar *relvar) {
58 Oid relOid = RangeVarGetRelid(relvar, false);
59 return checkOpenedRelation(
60 index_open(relOid, AccessExclusiveLock), GIST_AM_OID);
63 #define gist_index_close(r) index_close((r), AccessExclusiveLock)
66 gin_index_open(RangeVar *relvar) {
67 Oid relOid = RangeVarGetRelid(relvar, false);
68 return checkOpenedRelation(
69 index_open(relOid, AccessShareLock), GIN_AM_OID);
72 #define gin_index_close(r) index_close((r), AccessShareLock)
77 gist_index_open(RangeVar *relvar) {
78 Relation rel = index_openrv(relvar);
80 LockRelation(rel, AccessExclusiveLock);
81 return checkOpenedRelation(rel, GIST_AM_OID);
85 gist_index_close(Relation rel) {
86 UnlockRelation(rel, AccessExclusiveLock);
91 gin_index_open(RangeVar *relvar) {
92 Relation rel = index_openrv(relvar);
94 LockRelation(rel, AccessShareLock);
95 return checkOpenedRelation(rel, GIN_AM_OID);
99 gin_index_close(Relation rel) {
100 UnlockRelation(rel, AccessShareLock);
106 #if PG_VERSION_NUM >= 80300
107 #define stringToQualifiedNameList(x,y) stringToQualifiedNameList(x)
110 #if PG_VERSION_NUM < 80300
111 #define SET_VARSIZE(p,l) VARATT_SIZEP(p)=(l)
115 checkOpenedRelation(Relation r, Oid PgAmOid) {
116 if ( r->rd_am == NULL )
117 elog(ERROR, "Relation %s.%s is not an index",
118 get_namespace_name(RelationGetNamespace(r)),
119 RelationGetRelationName(r)
122 if ( r->rd_rel->relam != PgAmOid )
123 elog(ERROR, "Index %s.%s has wrong type",
124 get_namespace_name(RelationGetNamespace(r)),
125 RelationGetRelationName(r)
132 gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo *info) {
142 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
143 MemSet(pred, ' ', level*4);
144 pred[level*4] = '\0';
146 buffer = ReadBuffer(r, blk);
147 page = (Page) BufferGetPage(buffer);
149 maxoff = PageGetMaxOffsetNumber(page);
152 while ( (info->ptr-((char*)info->txt)) + level*4 + 128 >= info->len ) {
153 int dist=info->ptr-((char*)info->txt);
155 info->txt=(text*)repalloc(info->txt, info->len);
156 info->ptr = ((char*)info->txt)+dist;
159 sprintf(info->ptr, "%s%d(l:%d) blk: %d numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n",
165 PageGetFreeSpace(page),
166 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE),
167 GistPageGetOpaque(page)->rightlink,
168 ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" );
169 info->ptr=strchr(info->ptr,'\0');
171 if (!GistPageIsLeaf(page) && ( info->maxlevel<0 || level<info->maxlevel ) )
172 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
173 iid = PageGetItemId(page, i);
174 which = (IndexTuple) PageGetItem(page, iid);
175 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
176 gist_dumptree(r, level + 1, cblk, i, info);
178 ReleaseBuffer(buffer);
182 PG_FUNCTION_INFO_V1(gist_tree);
183 Datum gist_tree(PG_FUNCTION_ARGS);
185 gist_tree(PG_FUNCTION_ARGS) {
186 text *name=PG_GETARG_TEXT_P(0);
187 char *relname=t2c(name);
193 relname_list = stringToQualifiedNameList(relname, "gist_tree");
194 relvar = makeRangeVarFromNameList(relname_list);
195 index = gist_index_open(relvar);
196 PG_FREE_IF_COPY(name,0);
198 info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1;
200 info.txt=(text*)palloc( info.len );
201 info.ptr=((char*)info.txt)+VARHDRSZ;
203 gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0, &info);
205 gist_index_close(index);
208 SET_VARSIZE(info.txt, info.ptr-((char*)info.txt));
209 PG_RETURN_POINTER(info.txt);
220 uint64 leaftuplesize;
225 gist_stattree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxStat *info) {
235 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
236 MemSet(pred, ' ', level*4);
237 pred[level*4] = '\0';
239 buffer = ReadBuffer(r, blk);
240 page = (Page) BufferGetPage(buffer);
242 maxoff = PageGetMaxOffsetNumber(page);
245 info->tuplesize+=PAGESIZE-PageGetFreeSpace(page);
246 info->totalsize+=BLCKSZ;
247 info->numtuple+=maxoff;
248 if ( info->level < level )
251 if (GistPageIsLeaf(page)) {
252 info->numleafpages++;
253 info->leaftuplesize+=PAGESIZE-PageGetFreeSpace(page);
254 info->numleaftuple+=maxoff;
256 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
257 iid = PageGetItemId(page, i);
258 which = (IndexTuple) PageGetItem(page, iid);
259 if ( GistTupleIsInvalid(which) )
260 info->numinvalidtuple++;
261 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
262 gist_stattree(r, level + 1, cblk, i, info);
266 ReleaseBuffer(buffer);
270 PG_FUNCTION_INFO_V1(gist_stat);
271 Datum gist_stat(PG_FUNCTION_ARGS);
273 gist_stat(PG_FUNCTION_ARGS) {
274 text *name=PG_GETARG_TEXT_P(0);
275 char *relname=t2c(name);
280 text *out=(text*)palloc(1024);
281 char *ptr=((char*)out)+VARHDRSZ;
284 relname_list = stringToQualifiedNameList(relname, "gist_tree");
285 relvar = makeRangeVarFromNameList(relname_list);
286 index = gist_index_open(relvar);
287 PG_FREE_IF_COPY(name,0);
289 memset(&info, 0, sizeof(IdxStat));
291 gist_stattree(index, 0, GIST_ROOT_BLKNO, 0, &info);
293 gist_index_close(index);
297 "Number of levels: %d\n"
298 "Number of pages: %d\n"
299 "Number of leaf pages: %d\n"
300 "Number of tuples: %d\n"
301 "Number of invalid tuples: %d\n"
302 "Number of leaf tuples: %d\n"
303 "Total size of tuples: "INT64_FORMAT" bytes\n"
304 "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
305 "Total size of index: "INT64_FORMAT" bytes\n",
310 info.numinvalidtuple,
316 ptr=strchr(ptr,'\0');
318 SET_VARSIZE(out, ptr-((char*)out));
319 PG_RETURN_POINTER(out);
322 typedef struct GPItem {
340 openGPPage( FuncCallContext *funcctx, BlockNumber blk ) {
342 MemoryContext oldcontext;
343 Relation index = ( (TypeStorage*)(funcctx->user_fctx) )->index;
345 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
346 nitem = (GPItem*)palloc( sizeof(GPItem) );
347 memset(nitem,0,sizeof(GPItem));
349 nitem->buffer = ReadBuffer(index, blk);
350 nitem->page = (Page) BufferGetPage(nitem->buffer);
351 nitem->offset=FirstOffsetNumber;
352 nitem->next = ( (TypeStorage*)(funcctx->user_fctx) )->item;
353 nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1;
354 ( (TypeStorage*)(funcctx->user_fctx) )->item = nitem;
356 MemoryContextSwitchTo(oldcontext);
361 closeGPPage( FuncCallContext *funcctx ) {
362 GPItem *oitem = ( (TypeStorage*)(funcctx->user_fctx) )->item;
364 ( (TypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
366 ReleaseBuffer(oitem->buffer);
368 return ( (TypeStorage*)(funcctx->user_fctx) )->item;
372 setup_firstcall(FuncCallContext *funcctx, text *name) {
373 MemoryContext oldcontext;
375 char *relname=t2c(name);
377 char attname[NAMEDATALEN];
380 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
382 st=(TypeStorage*)palloc( sizeof(TypeStorage) );
383 memset(st,0,sizeof(TypeStorage));
384 st->relname_list = stringToQualifiedNameList(relname, "gist_tree");
385 st->relvar = makeRangeVarFromNameList(st->relname_list);
386 st->index = gist_index_open(st->relvar);
387 funcctx->user_fctx = (void*)st;
389 tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2, false);
390 TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
391 TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
392 for (i = 0; i < st->index->rd_att->natts; i++) {
393 sprintf(attname, "z%d", i+2);
398 st->index->rd_att->attrs[i]->atttypid,
399 st->index->rd_att->attrs[i]->atttypmod,
400 st->index->rd_att->attrs[i]->attndims
404 st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
405 st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(char));
407 funcctx->slot = TupleDescGetSlot(tupdesc);
408 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
410 MemoryContextSwitchTo(oldcontext);
413 st->item=openGPPage(funcctx, GIST_ROOT_BLKNO);
417 close_call( FuncCallContext *funcctx ) {
418 TypeStorage *st = (TypeStorage*)(funcctx->user_fctx);
420 while( st->item && closeGPPage(funcctx) );
422 pfree( st->dvalues );
425 gist_index_close(st->index);
428 PG_FUNCTION_INFO_V1(gist_print);
429 Datum gist_print(PG_FUNCTION_ARGS);
431 gist_print(PG_FUNCTION_ARGS) {
432 FuncCallContext *funcctx;
434 Datum result=(Datum)0;
441 if (SRF_IS_FIRSTCALL()) {
442 text *name=PG_GETARG_TEXT_P(0);
443 funcctx = SRF_FIRSTCALL_INIT();
444 setup_firstcall(funcctx, name);
445 PG_FREE_IF_COPY(name,0);
448 funcctx = SRF_PERCALL_SETUP();
449 st = (TypeStorage*)(funcctx->user_fctx);
453 SRF_RETURN_DONE(funcctx);
456 while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) ) {
457 if ( ! closeGPPage(funcctx) ) {
459 SRF_RETURN_DONE(funcctx);
463 iid = PageGetItemId( st->item->page, st->item->offset );
464 ituple = (IndexTuple) PageGetItem(st->item->page, iid);
466 st->dvalues[0] = Int32GetDatum( st->item->level );
468 st->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true );
470 for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++) {
471 if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) ) {
472 st->dvalues[i] = (Datum)0;
475 st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull);
476 st->nulls[i] = ( isnull ) ? 'n' : ' ';
480 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
481 result = TupleGetDatum(funcctx->slot, htuple);
482 st->item->offset = OffsetNumberNext(st->item->offset);
483 if ( !GistPageIsLeaf(st->item->page) )
484 openGPPage(funcctx, ItemPointerGetBlockNumber(&(ituple->t_tid)) );
486 SRF_RETURN_NEXT(funcctx, result);
489 typedef struct GinStatState {
502 moveRightIfItNeeded( GinStatState *st )
504 Page page = BufferGetPage(st->buffer);
506 if ( st->offset > PageGetMaxOffsetNumber(page) ) {
508 * We scaned the whole page, so we should take right page
510 BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
512 if ( GinPageRightMost(page) )
513 return false; /* no more page */
515 LockBuffer(st->buffer, GIN_UNLOCK);
516 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
517 LockBuffer(st->buffer, GIN_SHARE);
518 st->offset = FirstOffsetNumber;
525 * Refinds a previois position, at returns it has correctly
526 * set offset and buffer is locked
529 refindPosition(GinStatState *st)
533 /* find left if needed (it causes only for first search) */
538 LockBuffer(st->buffer, GIN_SHARE);
540 page = BufferGetPage(st->buffer);
541 if (GinPageIsLeaf(page))
544 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
545 blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid);
547 LockBuffer(st->buffer,GIN_UNLOCK);
548 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
551 if (st->offset == InvalidOffsetNumber) {
552 return (PageGetMaxOffsetNumber(page) >= FirstOffsetNumber ) ? true : false; /* first one */
557 #if PG_VERSION_NUM < 80400
563 if (moveRightIfItNeeded(st)==false)
566 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
567 #if PG_VERSION_NUM >= 80400
568 datum = gin_index_getattr(&st->ginstate, itup);
570 cmp = compareAttEntries(&st->ginstate,
571 st->attnum + 1, st->curval,
572 gintuple_get_attrnum(&st->ginstate, itup), datum);
574 datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull);
578 &st->ginstate.compareFn,
585 if ( !st->index->rd_att->attrs[st->attnum]->attbyval )
586 pfree( (void*) st->curval );
597 gin_setup_firstcall(FuncCallContext *funcctx, text *name, int attnum) {
598 MemoryContext oldcontext;
600 char *relname=t2c(name);
603 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
605 st=(GinStatState*)palloc( sizeof(GinStatState) );
606 memset(st,0,sizeof(GinStatState));
607 st->index = gin_index_open(
608 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_stat")));
609 initGinState( &st->ginstate, st->index );
611 #if PG_VERSION_NUM >= 80400
612 if (attnum < 0 || attnum >= st->index->rd_att->natts)
613 elog(ERROR,"Wrong column's number");
619 funcctx->user_fctx = (void*)st;
621 tupdesc = CreateTemplateTupleDesc(2, false);
622 TupleDescInitEntry(tupdesc, 1, "value",
623 st->index->rd_att->attrs[st->attnum]->atttypid,
624 st->index->rd_att->attrs[st->attnum]->atttypmod,
625 st->index->rd_att->attrs[st->attnum]->attndims);
626 TupleDescInitEntry(tupdesc, 2, "nrow", INT4OID, -1, 0);
628 memset( st->nulls, ' ', 2*sizeof(char) );
630 funcctx->slot = TupleDescGetSlot(tupdesc);
631 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
633 MemoryContextSwitchTo(oldcontext);
636 st->offset = InvalidOffsetNumber;
637 st->buffer = ReadBuffer( st->index, GIN_ROOT_BLKNO );
641 processTuple( FuncCallContext *funcctx, GinStatState *st, IndexTuple itup ) {
642 MemoryContext oldcontext;
643 #if PG_VERSION_NUM < 80400
647 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
648 st->curval = datumCopy(
649 #if PG_VERSION_NUM >= 80400
650 gin_index_getattr(&st->ginstate, itup),
652 index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull),
654 st->index->rd_att->attrs[st->attnum]->attbyval,
655 st->index->rd_att->attrs[st->attnum]->attlen );
656 MemoryContextSwitchTo(oldcontext);
658 st->dvalues[0] = st->curval;
660 if ( GinIsPostingTree(itup) ) {
661 BlockNumber rootblkno = GinGetPostingTree(itup);
662 GinPostingTreeScan *gdi;
666 LockBuffer(st->buffer, GIN_UNLOCK);
667 gdi = prepareScanPostingTree(st->index, rootblkno, TRUE);
668 entrybuffer = scanBeginPostingTree(gdi);
670 page = BufferGetPage(entrybuffer);
671 st->dvalues[1] = Int32GetDatum( gdi->stack->predictNumber * GinPageGetOpaque(page)->maxoff );
673 LockBuffer(entrybuffer, GIN_UNLOCK);
674 freeGinBtreeStack(gdi->stack);
677 st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) );
678 LockBuffer(st->buffer, GIN_UNLOCK);
682 PG_FUNCTION_INFO_V1(gin_stat);
683 Datum gin_stat(PG_FUNCTION_ARGS);
685 gin_stat(PG_FUNCTION_ARGS) {
686 FuncCallContext *funcctx;
688 Datum result=(Datum)0;
693 if (SRF_IS_FIRSTCALL()) {
694 text *name=PG_GETARG_TEXT_P(0);
695 funcctx = SRF_FIRSTCALL_INIT();
696 gin_setup_firstcall(funcctx, name, (PG_NARGS()==2) ? PG_GETARG_INT32(1) : 0 );
697 PG_FREE_IF_COPY(name,0);
700 funcctx = SRF_PERCALL_SETUP();
701 st = (GinStatState*)(funcctx->user_fctx);
703 if ( refindPosition(st) == false ) {
704 UnlockReleaseBuffer( st->buffer );
705 gin_index_close(st->index);
707 SRF_RETURN_DONE(funcctx);
713 if (moveRightIfItNeeded(st)==false) {
714 UnlockReleaseBuffer( st->buffer );
715 gin_index_close(st->index);
717 SRF_RETURN_DONE(funcctx);
720 page = BufferGetPage(st->buffer);
721 ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
723 #if PG_VERSION_NUM >= 80400
724 if (st->attnum + 1 == gintuple_get_attrnum(&st->ginstate, ituple))
729 processTuple( funcctx, st, ituple );
731 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
732 result = TupleGetDatum(funcctx->slot, htuple);
734 SRF_RETURN_NEXT(funcctx, result);
737 PG_FUNCTION_INFO_V1(gin_count_estimate);
738 Datum gin_count_estimate(PG_FUNCTION_ARGS);
739 #if PG_VERSION_NUM >= 80300
741 gin_count_estimate(PG_FUNCTION_ARGS) {
742 text *name=PG_GETARG_TEXT_P(0);
746 char *relname=t2c(name);
748 #if PG_VERSION_NUM >= 80400
749 TIDBitmap *bitmap = tbm_create(work_mem * 1024L);
752 ItemPointerData tids[MAXTIDS];
757 index = gin_index_open(
758 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_count_estimate")));
760 if ( index->rd_opcintype[0] != TSVECTOROID ) {
761 gin_index_close(index);
762 elog(ERROR, "Column type is not a tsvector");
767 key.sk_strategy = TSearchStrategyNumber;
769 key.sk_argument = PG_GETARG_DATUM(1);
771 fmgr_info( F_TS_MATCH_VQ , &key.sk_func );
773 #if PG_VERSION_NUM >= 80400
774 scan = index_beginscan_bitmap(index, SnapshotNow, 1, &key);
776 count = index_getbitmap(scan, bitmap);
779 scan = index_beginscan_multi(index, SnapshotNow, 1, &key);
782 more = index_getmulti(scan, tids, MAXTIDS, &returned_tids);
783 count += returned_tids;
787 index_endscan( scan );
788 gin_index_close(index);
790 PG_RETURN_INT64(count);
794 gin_count_estimate(PG_FUNCTION_ARGS) {
795 elog(NOTICE, "Function is not working under PgSQL < 8.3");