3 #include "access/genam.h"
4 #include "access/gin.h"
5 #if PG_VERSION_NUM >= 90100
6 #include "access/gin_private.h"
8 #include "access/gist.h"
9 #include "access/gist_private.h"
10 #include "access/gistscan.h"
11 #include "access/heapam.h"
12 #include "catalog/index.h"
13 #include "miscadmin.h"
14 #include "storage/lmgr.h"
15 #include "catalog/namespace.h"
16 #if PG_VERSION_NUM >= 80300
17 #include <tsearch/ts_utils.h>
19 #include <utils/tqual.h>
20 #include "utils/builtins.h"
21 #include "utils/lsyscache.h"
22 #include "utils/datum.h"
23 #include "utils/fmgroids.h"
26 #include <access/heapam.h>
27 #include <catalog/pg_type.h>
28 #include <access/relscan.h>
31 #define PAGESIZE (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData)))
34 #define PG_NARGS() (fcinfo->nargs)
39 char *out=palloc( VARSIZE(in) );
40 memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ);
41 out[ VARSIZE(in)-VARHDRSZ ] ='\0';
52 static Relation checkOpenedRelation(Relation r, Oid PgAmOid);
54 #ifdef PG_MODULE_MAGIC
60 gist_index_open(RangeVar *relvar) {
61 #if PG_VERSION_NUM < 90100
62 Oid relOid = RangeVarGetRelid(relvar, false);
64 Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
66 return checkOpenedRelation(
67 index_open(relOid, AccessExclusiveLock), GIST_AM_OID);
70 #define gist_index_close(r) index_close((r), AccessExclusiveLock)
73 gin_index_open(RangeVar *relvar) {
74 #if PG_VERSION_NUM < 90100
75 Oid relOid = RangeVarGetRelid(relvar, false);
77 Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
79 return checkOpenedRelation(
80 index_open(relOid, AccessShareLock), GIN_AM_OID);
83 #define gin_index_close(r) index_close((r), AccessShareLock)
88 gist_index_open(RangeVar *relvar) {
89 Relation rel = index_openrv(relvar);
91 LockRelation(rel, AccessExclusiveLock);
92 return checkOpenedRelation(rel, GIST_AM_OID);
96 gist_index_close(Relation rel) {
97 UnlockRelation(rel, AccessExclusiveLock);
102 gin_index_open(RangeVar *relvar) {
103 Relation rel = index_openrv(relvar);
105 LockRelation(rel, AccessShareLock);
106 return checkOpenedRelation(rel, GIN_AM_OID);
110 gin_index_close(Relation rel) {
111 UnlockRelation(rel, AccessShareLock);
117 #if PG_VERSION_NUM >= 80300
118 #define stringToQualifiedNameList(x,y) stringToQualifiedNameList(x)
121 #if PG_VERSION_NUM < 80300
122 #define SET_VARSIZE(p,l) VARATT_SIZEP(p)=(l)
126 checkOpenedRelation(Relation r, Oid PgAmOid) {
127 if ( r->rd_am == NULL )
128 elog(ERROR, "Relation %s.%s is not an index",
129 get_namespace_name(RelationGetNamespace(r)),
130 RelationGetRelationName(r)
133 if ( r->rd_rel->relam != PgAmOid )
134 elog(ERROR, "Index %s.%s has wrong type",
135 get_namespace_name(RelationGetNamespace(r)),
136 RelationGetRelationName(r)
143 gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo *info) {
153 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
154 MemSet(pred, ' ', level*4);
155 pred[level*4] = '\0';
157 buffer = ReadBuffer(r, blk);
158 page = (Page) BufferGetPage(buffer);
160 maxoff = PageGetMaxOffsetNumber(page);
163 while ( (info->ptr-((char*)info->txt)) + level*4 + 128 >= info->len ) {
164 int dist=info->ptr-((char*)info->txt);
166 info->txt=(text*)repalloc(info->txt, info->len);
167 info->ptr = ((char*)info->txt)+dist;
170 sprintf(info->ptr, "%s%d(l:%d) blk: %u numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n",
176 (int) PageGetFreeSpace(page),
177 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE),
178 GistPageGetOpaque(page)->rightlink,
179 ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" );
180 info->ptr=strchr(info->ptr,'\0');
182 if (!GistPageIsLeaf(page) && ( info->maxlevel<0 || level<info->maxlevel ) )
183 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
184 iid = PageGetItemId(page, i);
185 which = (IndexTuple) PageGetItem(page, iid);
186 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
187 gist_dumptree(r, level + 1, cblk, i, info);
189 ReleaseBuffer(buffer);
193 PG_FUNCTION_INFO_V1(gist_tree);
194 Datum gist_tree(PG_FUNCTION_ARGS);
196 gist_tree(PG_FUNCTION_ARGS) {
197 text *name=PG_GETARG_TEXT_P(0);
198 char *relname=t2c(name);
204 relname_list = stringToQualifiedNameList(relname, "gist_tree");
205 relvar = makeRangeVarFromNameList(relname_list);
206 index = gist_index_open(relvar);
207 PG_FREE_IF_COPY(name,0);
209 info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1;
211 info.txt=(text*)palloc( info.len );
212 info.ptr=((char*)info.txt)+VARHDRSZ;
214 gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0, &info);
216 gist_index_close(index);
219 SET_VARSIZE(info.txt, info.ptr-((char*)info.txt));
220 PG_RETURN_POINTER(info.txt);
231 uint64 leaftuplesize;
236 gist_stattree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxStat *info) {
246 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
247 MemSet(pred, ' ', level*4);
248 pred[level*4] = '\0';
250 buffer = ReadBuffer(r, blk);
251 page = (Page) BufferGetPage(buffer);
253 maxoff = PageGetMaxOffsetNumber(page);
256 info->tuplesize+=PAGESIZE-PageGetFreeSpace(page);
257 info->totalsize+=BLCKSZ;
258 info->numtuple+=maxoff;
259 if ( info->level < level )
262 if (GistPageIsLeaf(page)) {
263 info->numleafpages++;
264 info->leaftuplesize+=PAGESIZE-PageGetFreeSpace(page);
265 info->numleaftuple+=maxoff;
267 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
268 iid = PageGetItemId(page, i);
269 which = (IndexTuple) PageGetItem(page, iid);
270 if ( GistTupleIsInvalid(which) )
271 info->numinvalidtuple++;
272 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
273 gist_stattree(r, level + 1, cblk, i, info);
277 ReleaseBuffer(buffer);
281 PG_FUNCTION_INFO_V1(gist_stat);
282 Datum gist_stat(PG_FUNCTION_ARGS);
284 gist_stat(PG_FUNCTION_ARGS) {
285 text *name=PG_GETARG_TEXT_P(0);
286 char *relname=t2c(name);
291 text *out=(text*)palloc(1024);
292 char *ptr=((char*)out)+VARHDRSZ;
295 relname_list = stringToQualifiedNameList(relname, "gist_tree");
296 relvar = makeRangeVarFromNameList(relname_list);
297 index = gist_index_open(relvar);
298 PG_FREE_IF_COPY(name,0);
300 memset(&info, 0, sizeof(IdxStat));
302 gist_stattree(index, 0, GIST_ROOT_BLKNO, 0, &info);
304 gist_index_close(index);
308 "Number of levels: %d\n"
309 "Number of pages: %d\n"
310 "Number of leaf pages: %d\n"
311 "Number of tuples: %d\n"
312 "Number of invalid tuples: %d\n"
313 "Number of leaf tuples: %d\n"
314 "Total size of tuples: "INT64_FORMAT" bytes\n"
315 "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
316 "Total size of index: "INT64_FORMAT" bytes\n",
321 info.numinvalidtuple,
327 ptr=strchr(ptr,'\0');
329 SET_VARSIZE(out, ptr-((char*)out));
330 PG_RETURN_POINTER(out);
333 typedef struct GPItem {
351 openGPPage( FuncCallContext *funcctx, BlockNumber blk ) {
353 MemoryContext oldcontext;
354 Relation index = ( (TypeStorage*)(funcctx->user_fctx) )->index;
356 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
357 nitem = (GPItem*)palloc( sizeof(GPItem) );
358 memset(nitem,0,sizeof(GPItem));
360 nitem->buffer = ReadBuffer(index, blk);
361 nitem->page = (Page) BufferGetPage(nitem->buffer);
362 nitem->offset=FirstOffsetNumber;
363 nitem->next = ( (TypeStorage*)(funcctx->user_fctx) )->item;
364 nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1;
365 ( (TypeStorage*)(funcctx->user_fctx) )->item = nitem;
367 MemoryContextSwitchTo(oldcontext);
372 closeGPPage( FuncCallContext *funcctx ) {
373 GPItem *oitem = ( (TypeStorage*)(funcctx->user_fctx) )->item;
375 ( (TypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
377 ReleaseBuffer(oitem->buffer);
379 return ( (TypeStorage*)(funcctx->user_fctx) )->item;
383 setup_firstcall(FuncCallContext *funcctx, text *name) {
384 MemoryContext oldcontext;
386 char *relname=t2c(name);
388 char attname[NAMEDATALEN];
391 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
393 st=(TypeStorage*)palloc( sizeof(TypeStorage) );
394 memset(st,0,sizeof(TypeStorage));
395 st->relname_list = stringToQualifiedNameList(relname, "gist_tree");
396 st->relvar = makeRangeVarFromNameList(st->relname_list);
397 st->index = gist_index_open(st->relvar);
398 funcctx->user_fctx = (void*)st;
400 tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2, false);
401 TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
402 TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
403 for (i = 0; i < st->index->rd_att->natts; i++) {
404 sprintf(attname, "z%d", i+2);
409 st->index->rd_att->attrs[i]->atttypid,
410 st->index->rd_att->attrs[i]->atttypmod,
411 st->index->rd_att->attrs[i]->attndims
415 st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
416 st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(char));
418 funcctx->slot = TupleDescGetSlot(tupdesc);
419 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
421 MemoryContextSwitchTo(oldcontext);
424 st->item=openGPPage(funcctx, GIST_ROOT_BLKNO);
428 close_call( FuncCallContext *funcctx ) {
429 TypeStorage *st = (TypeStorage*)(funcctx->user_fctx);
431 while( st->item && closeGPPage(funcctx) );
433 pfree( st->dvalues );
436 gist_index_close(st->index);
439 PG_FUNCTION_INFO_V1(gist_print);
440 Datum gist_print(PG_FUNCTION_ARGS);
442 gist_print(PG_FUNCTION_ARGS) {
443 FuncCallContext *funcctx;
445 Datum result=(Datum)0;
452 if (SRF_IS_FIRSTCALL()) {
453 text *name=PG_GETARG_TEXT_P(0);
454 funcctx = SRF_FIRSTCALL_INIT();
455 setup_firstcall(funcctx, name);
456 PG_FREE_IF_COPY(name,0);
459 funcctx = SRF_PERCALL_SETUP();
460 st = (TypeStorage*)(funcctx->user_fctx);
464 SRF_RETURN_DONE(funcctx);
467 while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) ) {
468 if ( ! closeGPPage(funcctx) ) {
470 SRF_RETURN_DONE(funcctx);
474 iid = PageGetItemId( st->item->page, st->item->offset );
475 ituple = (IndexTuple) PageGetItem(st->item->page, iid);
477 st->dvalues[0] = Int32GetDatum( st->item->level );
479 st->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true );
481 for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++) {
482 if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) ) {
483 st->dvalues[i] = (Datum)0;
486 st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull);
487 st->nulls[i] = ( isnull ) ? 'n' : ' ';
491 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
492 result = TupleGetDatum(funcctx->slot, htuple);
493 st->item->offset = OffsetNumberNext(st->item->offset);
494 if ( !GistPageIsLeaf(st->item->page) )
495 openGPPage(funcctx, ItemPointerGetBlockNumber(&(ituple->t_tid)) );
497 SRF_RETURN_NEXT(funcctx, result);
500 typedef struct GinStatState {
508 #if PG_VERSION_NUM >= 90100
509 GinNullCategory category;
516 moveRightIfItNeeded( GinStatState *st )
518 Page page = BufferGetPage(st->buffer);
520 if ( st->offset > PageGetMaxOffsetNumber(page) ) {
522 * We scaned the whole page, so we should take right page
524 BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
526 if ( GinPageRightMost(page) )
527 return false; /* no more page */
529 LockBuffer(st->buffer, GIN_UNLOCK);
530 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
531 LockBuffer(st->buffer, GIN_SHARE);
532 st->offset = FirstOffsetNumber;
539 * Refinds a previois position, at returns it has correctly
540 * set offset and buffer is locked
543 refindPosition(GinStatState *st)
547 /* find left if needed (it causes only for first search) */
552 LockBuffer(st->buffer, GIN_SHARE);
554 page = BufferGetPage(st->buffer);
555 if (GinPageIsLeaf(page))
558 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
559 blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid);
561 LockBuffer(st->buffer,GIN_UNLOCK);
562 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
565 if (st->offset == InvalidOffsetNumber) {
566 return (PageGetMaxOffsetNumber(page) >= FirstOffsetNumber ) ? true : false; /* first one */
571 #if PG_VERSION_NUM >= 90100
572 GinNullCategory category;
573 #elif PG_VERSION_NUM < 80400
579 if (moveRightIfItNeeded(st)==false)
582 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
583 #if PG_VERSION_NUM >= 90100
584 datum = gintuple_get_key(&st->ginstate, itup, &category);
585 cmp = ginCompareAttEntries(&st->ginstate,
586 st->attnum + 1, st->curval, st->category,
587 gintuple_get_attrnum(&st->ginstate, itup), datum, category);
588 #elif PG_VERSION_NUM >= 80400
589 datum = gin_index_getattr(&st->ginstate, itup);
591 cmp = compareAttEntries(&st->ginstate,
592 st->attnum + 1, st->curval,
593 gintuple_get_attrnum(&st->ginstate, itup), datum);
595 datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull);
599 &st->ginstate.compareFn,
606 if ( !st->index->rd_att->attrs[st->attnum]->attbyval )
607 pfree( (void*) st->curval );
618 gin_setup_firstcall(FuncCallContext *funcctx, text *name, int attnum) {
619 MemoryContext oldcontext;
621 char *relname=t2c(name);
624 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
626 st=(GinStatState*)palloc( sizeof(GinStatState) );
627 memset(st,0,sizeof(GinStatState));
628 st->index = gin_index_open(
629 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_stat")));
630 initGinState( &st->ginstate, st->index );
632 #if PG_VERSION_NUM >= 80400
633 if (attnum < 0 || attnum >= st->index->rd_att->natts)
634 elog(ERROR,"Wrong column's number");
640 funcctx->user_fctx = (void*)st;
642 tupdesc = CreateTemplateTupleDesc(2, false);
643 TupleDescInitEntry(tupdesc, 1, "value",
644 st->index->rd_att->attrs[st->attnum]->atttypid,
645 st->index->rd_att->attrs[st->attnum]->atttypmod,
646 st->index->rd_att->attrs[st->attnum]->attndims);
647 TupleDescInitEntry(tupdesc, 2, "nrow", INT4OID, -1, 0);
649 memset( st->nulls, ' ', 2*sizeof(char) );
651 funcctx->slot = TupleDescGetSlot(tupdesc);
652 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
654 MemoryContextSwitchTo(oldcontext);
657 st->offset = InvalidOffsetNumber;
658 st->buffer = ReadBuffer( st->index, GIN_ROOT_BLKNO );
662 processTuple( FuncCallContext *funcctx, GinStatState *st, IndexTuple itup ) {
663 MemoryContext oldcontext;
664 #if PG_VERSION_NUM < 80400
668 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
669 st->curval = datumCopy(
670 #if PG_VERSION_NUM >= 90100
671 gintuple_get_key(&st->ginstate, itup, &st->category),
672 #elif PG_VERSION_NUM >= 80400
673 gin_index_getattr(&st->ginstate, itup),
675 index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull),
677 st->index->rd_att->attrs[st->attnum]->attbyval,
678 st->index->rd_att->attrs[st->attnum]->attlen );
679 MemoryContextSwitchTo(oldcontext);
681 st->dvalues[0] = st->curval;
682 #if PG_VERSION_NUM >= 90100
683 /* do no distiguish various null category */
684 st->nulls[0] = (st->category == GIN_CAT_NORM_KEY) ? ' ' : 'n';
687 if ( GinIsPostingTree(itup) ) {
688 BlockNumber rootblkno = GinGetPostingTree(itup);
689 GinPostingTreeScan *gdi;
693 LockBuffer(st->buffer, GIN_UNLOCK);
694 #if PG_VERSION_NUM >= 90100
695 gdi = ginPrepareScanPostingTree(st->index, rootblkno, TRUE);
696 entrybuffer = ginScanBeginPostingTree(gdi);
698 gdi = prepareScanPostingTree(st->index, rootblkno, TRUE);
699 entrybuffer = scanBeginPostingTree(gdi);
702 page = BufferGetPage(entrybuffer);
703 st->dvalues[1] = Int32GetDatum( gdi->stack->predictNumber * GinPageGetOpaque(page)->maxoff );
705 LockBuffer(entrybuffer, GIN_UNLOCK);
706 freeGinBtreeStack(gdi->stack);
709 st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) );
710 LockBuffer(st->buffer, GIN_UNLOCK);
714 PG_FUNCTION_INFO_V1(gin_stat);
715 Datum gin_stat(PG_FUNCTION_ARGS);
717 gin_stat(PG_FUNCTION_ARGS) {
718 FuncCallContext *funcctx;
720 Datum result=(Datum)0;
725 if (SRF_IS_FIRSTCALL()) {
726 text *name=PG_GETARG_TEXT_P(0);
727 funcctx = SRF_FIRSTCALL_INIT();
728 gin_setup_firstcall(funcctx, name, (PG_NARGS()==2) ? PG_GETARG_INT32(1) : 0 );
729 PG_FREE_IF_COPY(name,0);
732 funcctx = SRF_PERCALL_SETUP();
733 st = (GinStatState*)(funcctx->user_fctx);
735 if ( refindPosition(st) == false ) {
736 UnlockReleaseBuffer( st->buffer );
737 gin_index_close(st->index);
739 SRF_RETURN_DONE(funcctx);
745 if (moveRightIfItNeeded(st)==false) {
746 UnlockReleaseBuffer( st->buffer );
747 gin_index_close(st->index);
749 SRF_RETURN_DONE(funcctx);
752 page = BufferGetPage(st->buffer);
753 ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
755 #if PG_VERSION_NUM >= 80400
756 if (st->attnum + 1 == gintuple_get_attrnum(&st->ginstate, ituple))
761 processTuple( funcctx, st, ituple );
763 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
764 result = TupleGetDatum(funcctx->slot, htuple);
766 SRF_RETURN_NEXT(funcctx, result);
769 PG_FUNCTION_INFO_V1(gin_count_estimate);
770 Datum gin_count_estimate(PG_FUNCTION_ARGS);
771 #if PG_VERSION_NUM >= 80300
773 gin_count_estimate(PG_FUNCTION_ARGS) {
774 text *name=PG_GETARG_TEXT_P(0);
778 char *relname=t2c(name);
780 #if PG_VERSION_NUM >= 80400
781 TIDBitmap *bitmap = tbm_create(work_mem * 1024L);
784 ItemPointerData tids[MAXTIDS];
789 index = gin_index_open(
790 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_count_estimate")));
792 if ( index->rd_opcintype[0] != TSVECTOROID ) {
793 gin_index_close(index);
794 elog(ERROR, "Column type is not a tsvector");
799 key.sk_strategy = TSearchStrategyNumber;
801 key.sk_argument = PG_GETARG_DATUM(1);
803 fmgr_info( F_TS_MATCH_VQ , &key.sk_func );
805 #if PG_VERSION_NUM >= 90100
806 scan = index_beginscan_bitmap(index, SnapshotNow, 1);
807 index_rescan(scan, &key, 1, NULL, 0);
809 count = index_getbitmap(scan, bitmap);
811 #elif PG_VERSION_NUM >= 80400
812 scan = index_beginscan_bitmap(index, SnapshotNow, 1, &key);
814 count = index_getbitmap(scan, bitmap);
817 scan = index_beginscan_multi(index, SnapshotNow, 1, &key);
820 more = index_getmulti(scan, tids, MAXTIDS, &returned_tids);
821 count += returned_tids;
825 index_endscan( scan );
826 gin_index_close(index);
828 PG_RETURN_INT64(count);
832 gin_count_estimate(PG_FUNCTION_ARGS) {
833 elog(NOTICE, "Function is not working under PgSQL < 8.3");