diff -c -r -N src.orig/backend/access/gin/ginbtree.c src/backend/access/gin/ginbtree.c
*** src.orig/backend/access/gin/ginbtree.c	2010-05-27 14:45:43.000000000 +0400
--- src/backend/access/gin/ginbtree.c	2010-05-27 15:34:27.000000000 +0400
***************
*** 268,274 ****
  /*
   * Insert value (stored in GinBtree) to tree described by stack
   */
! void
  ginInsertValue(GinBtree btree, GinBtreeStack *stack)
  {
  	GinBtreeStack *parent = stack;
--- 268,274 ----
  /*
   * Insert value (stored in GinBtree) to tree described by stack
   */
! BlockNumber
  ginInsertValue(GinBtree btree, GinBtreeStack *stack)
  {
  	GinBtreeStack *parent = stack;
***************
*** 276,281 ****
--- 276,282 ----
  	Page		page,
  				rpage,
  				lpage;
+ 	BlockNumber newPages = 0;
  
  	/* remember root BlockNumber */
  	while (parent)
***************
*** 312,324 ****
  			END_CRIT_SECTION();
  
  			freeGinBtreeStack(stack->parent);
! 			return;
  		}
  		else
  		{
  			Buffer		rbuffer = GinNewBuffer(btree->index);
  			Page		newlpage;
  
  			/*
  			 * newlpage is a pointer to memory page, it doesn't associate with
  			 * buffer, stack->buffer should be untouched
--- 313,327 ----
  			END_CRIT_SECTION();
  
  			freeGinBtreeStack(stack->parent);
! 			return newPages;
  		}
  		else
  		{
  			Buffer		rbuffer = GinNewBuffer(btree->index);
  			Page		newlpage;
  
+ 			newPages++;
+ 
  			/*
  			 * newlpage is a pointer to memory page, it doesn't associate with
  			 * buffer, stack->buffer should be untouched
***************
*** 338,343 ****
--- 341,348 ----
  				 */
  				Buffer		lbuffer = GinNewBuffer(btree->index);
  
+ 				newPages++;
+ 
  				((ginxlogSplit *) (rdata->data))->isRootSplit = TRUE;
  				((ginxlogSplit *) (rdata->data))->rrlink = InvalidBlockNumber;
  
***************
*** 379,385 ****
  
  				END_CRIT_SECTION();
  
! 				return;
  			}
  			else
  			{
--- 384,390 ----
  
  				END_CRIT_SECTION();
  
! 				return newPages;
  			}
  			else
  			{
***************
*** 449,452 ****
--- 454,459 ----
  		pfree(stack);
  		stack = parent;
  	}
+ 
+ 	return newPages;
  }
diff -c -r -N src.orig/backend/access/gin/gindatapage.c src/backend/access/gin/gindatapage.c
*** src.orig/backend/access/gin/gindatapage.c	2010-05-27 14:45:43.000000000 +0400
--- src/backend/access/gin/gindatapage.c	2010-05-27 15:34:27.000000000 +0400
***************
*** 627,636 ****
  /*
   * Inserts array of item pointers, may execute several tree scan (very rare)
   */
! void
  insertItemPointer(GinPostingTreeScan *gdi, ItemPointerData *items, uint32 nitem)
  {
  	BlockNumber rootBlkno = gdi->stack->blkno;
  
  	gdi->btree.items = items;
  	gdi->btree.nitem = nitem;
--- 627,637 ----
  /*
   * Inserts array of item pointers, may execute several tree scan (very rare)
   */
! BlockNumber
  insertItemPointer(GinPostingTreeScan *gdi, ItemPointerData *items, uint32 nitem)
  {
  	BlockNumber rootBlkno = gdi->stack->blkno;
+ 	BlockNumber	nNewPages = 0;
  
  	gdi->btree.items = items;
  	gdi->btree.nitem = nitem;
***************
*** 653,662 ****
  			freeGinBtreeStack(gdi->stack);
  		}
  		else
! 			ginInsertValue(&(gdi->btree), gdi->stack);
  
  		gdi->stack = NULL;
  	}
  }
  
  Buffer
--- 654,665 ----
  			freeGinBtreeStack(gdi->stack);
  		}
  		else
! 			nNewPages += ginInsertValue(&(gdi->btree), gdi->stack);
  
  		gdi->stack = NULL;
  	}
+ 
+ 	return nNewPages;
  }
  
  Buffer
diff -c -r -N src.orig/backend/access/gin/ginfast.c src/backend/access/gin/ginfast.c
*** src.orig/backend/access/gin/ginfast.c	2010-05-27 14:45:43.000000000 +0400
--- src/backend/access/gin/ginfast.c	2010-05-27 15:34:27.000000000 +0400
***************
*** 274,279 ****
--- 274,280 ----
  		 * We should make sublist separately and append it to the tail
  		 */
  		GinMetaPageData sublist;
+ 		GinExtraData	*extra;
  
  		memset(&sublist, 0, sizeof(GinMetaPageData));
  		makeSublist(index, collector->tuples, collector->ntuples, &sublist);
***************
*** 287,297 ****
  		if (metadata->head == InvalidBlockNumber)
  		{
  			/*
! 			 * Main list is empty, so just copy sublist into main list
  			 */
  			START_CRIT_SECTION();
  
! 			memcpy(metadata, &sublist, sizeof(GinMetaPageData));
  		}
  		else
  		{
--- 288,305 ----
  		if (metadata->head == InvalidBlockNumber)
  		{
  			/*
! 			 * Main list is empty
  			 */
  			START_CRIT_SECTION();
  
! 			metadata->head = sublist.head;
! 			metadata->tail = sublist.tail;
! 			metadata->tailFreeSize = sublist.tailFreeSize;
! 			metadata->nPendingPages = sublist.nPendingPages;
! 			metadata->nPendingHeapTuples = sublist.nPendingHeapTuples;
! 
! 			if ( (extra = (GinExtraData*)index->rd_amcache) != NULL )
! 				extra->nPendingPages = metadata->nPendingPages;
  		}
  		else
  		{
***************
*** 318,323 ****
--- 326,334 ----
  
  			metadata->nPendingPages += sublist.nPendingPages;
  			metadata->nPendingHeapTuples += sublist.nPendingHeapTuples;
+ 
+ 			if ( (extra = (GinExtraData*)index->rd_amcache) != NULL )
+ 				extra->nPendingPages = metadata->nPendingPages;
  		}
  	}
  	else
***************
*** 486,496 ****
   */
  static bool
  shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
! 		  IndexBulkDeleteResult *stats)
  {
  	Page		metapage;
  	GinMetaPageData *metadata;
  	BlockNumber blknoToDelete;
  
  	metapage = BufferGetPage(metabuffer);
  	metadata = GinPageGetMeta(metapage);
--- 497,508 ----
   */
  static bool
  shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
! 		  IndexBulkDeleteResult *stats, GinEntryInsertResult *insStat)
  {
  	Page		metapage;
  	GinMetaPageData *metadata;
  	BlockNumber blknoToDelete;
+ 	GinExtraData	*extra;
  
  	metapage = BufferGetPage(metabuffer);
  	metadata = GinPageGetMeta(metapage);
***************
*** 555,560 ****
--- 567,582 ----
  			metadata->nPendingHeapTuples = 0;
  		}
  
+ 		if (insStat)
+ 		{
+ 			ginUpdateStatInPlace(index, metadata, insStat, true);
+ 			insStat = NULL; /* update only once */
+ 		}
+ 		else if ( (extra = (GinExtraData*)index->rd_amcache) != NULL )
+ 		{
+ 			extra->nPendingPages = metadata->nPendingPages;
+ 		}
+ 
  		MarkBufferDirty(metabuffer);
  
  		for (i = 0; i < data.ndeleted; i++)
***************
*** 772,777 ****
--- 794,803 ----
  			Datum		entry;
  			OffsetNumber maxoff,
  						attnum;
+ 			GinEntryInsertResult	insStat;
+ 
+ 
+ 			memset(&insStat, 0, sizeof(insStat));
  
  			/*
  			 * Unlock current page to increase performance. Changes of page
***************
*** 788,794 ****
  			 */
  			while ((list = ginGetEntry(&accum, &attnum, &entry, &nlist)) != NULL)
  			{
! 				ginEntryInsert(index, ginstate, attnum, entry, list, nlist, FALSE);
  				if (vac_delay)
  					vacuum_delay_point();
  			}
--- 814,820 ----
  			 */
  			while ((list = ginGetEntry(&accum, &attnum, &entry, &nlist)) != NULL)
  			{
! 				ginEntryInsert(index, ginstate, attnum, entry, list, nlist, FALSE, &insStat);
  				if (vac_delay)
  					vacuum_delay_point();
  			}
***************
*** 821,827 ****
  				processPendingPage(&accum, &datums, page, maxoff + 1);
  
  				while ((list = ginGetEntry(&accum, &attnum, &entry, &nlist)) != NULL)
! 					ginEntryInsert(index, ginstate, attnum, entry, list, nlist, FALSE);
  			}
  
  			/*
--- 847,853 ----
  				processPendingPage(&accum, &datums, page, maxoff + 1);
  
  				while ((list = ginGetEntry(&accum, &attnum, &entry, &nlist)) != NULL)
! 					ginEntryInsert(index, ginstate, attnum, entry, list, nlist, FALSE, &insStat);
  			}
  
  			/*
***************
*** 835,841 ****
  			 * remove readed pages from pending list, at this point all
  			 * content of readed pages is in regular structure
  			 */
! 			if (shiftList(index, metabuffer, blkno, stats))
  			{
  				/* another cleanup process is running concurrently */
  				LockBuffer(metabuffer, GIN_UNLOCK);
--- 861,867 ----
  			 * remove readed pages from pending list, at this point all
  			 * content of readed pages is in regular structure
  			 */
! 			if (shiftList(index, metabuffer, blkno, stats, &insStat))
  			{
  				/* another cleanup process is running concurrently */
  				LockBuffer(metabuffer, GIN_UNLOCK);
diff -c -r -N src.orig/backend/access/gin/gininsert.c src/backend/access/gin/gininsert.c
*** src.orig/backend/access/gin/gininsert.c	2010-05-27 14:45:43.000000000 +0400
--- src/backend/access/gin/gininsert.c	2010-05-27 15:34:27.000000000 +0400
***************
*** 26,32 ****
  typedef struct
  {
  	GinState	ginstate;
! 	double		indtuples;
  	MemoryContext tmpCtx;
  	MemoryContext funcCtx;
  	BuildAccumulator accum;
--- 26,33 ----
  typedef struct
  {
  	GinState	ginstate;
! 	int64					indtuples;
! 	GinEntryInsertResult	insertStat;
  	MemoryContext tmpCtx;
  	MemoryContext funcCtx;
  	BuildAccumulator accum;
***************
*** 98,104 ****
   */
  static IndexTuple
  addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack,
! 		  IndexTuple old, ItemPointerData *items, uint32 nitem, bool isBuild)
  {
  	Datum		key = gin_index_getattr(ginstate, old);
  	OffsetNumber attnum = gintuple_get_attrnum(ginstate, old);
--- 99,105 ----
   */
  static IndexTuple
  addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack,
! 		  IndexTuple old, ItemPointerData *items, uint32 nitem, bool isBuild, GinEntryInsertResult *insStat)
  {
  	Datum		key = gin_index_getattr(ginstate, old);
  	OffsetNumber attnum = gintuple_get_attrnum(ginstate, old);
***************
*** 130,136 ****
  		gdi = prepareScanPostingTree(index, postingRoot, FALSE);
  		gdi->btree.isBuild = isBuild;
  
! 		insertItemPointer(gdi, items, nitem);
  
  		pfree(gdi);
  	}
--- 131,137 ----
  		gdi = prepareScanPostingTree(index, postingRoot, FALSE);
  		gdi->btree.isBuild = isBuild;
  
! 		insStat->numNewDataPages += 1 /* root */ + insertItemPointer(gdi, items, nitem);
  
  		pfree(gdi);
  	}
***************
*** 145,151 ****
  ginEntryInsert(Relation index, GinState *ginstate,
  			   OffsetNumber attnum, Datum value,
  			   ItemPointerData *items, uint32 nitem,
! 			   bool isBuild)
  {
  	GinBtreeData btree;
  	GinBtreeStack *stack;
--- 146,152 ----
  ginEntryInsert(Relation index, GinState *ginstate,
  			   OffsetNumber attnum, Datum value,
  			   ItemPointerData *items, uint32 nitem,
! 			   bool isBuild, GinEntryInsertResult *insStat)
  {
  	GinBtreeData btree;
  	GinBtreeStack *stack;
***************
*** 175,186 ****
  			/* insert into posting tree */
  			gdi = prepareScanPostingTree(index, rootPostingTree, FALSE);
  			gdi->btree.isBuild = isBuild;
! 			insertItemPointer(gdi, items, nitem);
  
  			return;
  		}
  
! 		itup = addItemPointersToTuple(index, ginstate, stack, itup, items, nitem, isBuild);
  
  		btree.isDelete = TRUE;
  	}
--- 176,188 ----
  			/* insert into posting tree */
  			gdi = prepareScanPostingTree(index, rootPostingTree, FALSE);
  			gdi->btree.isBuild = isBuild;
! 			insStat->numNewDataPages += insertItemPointer(gdi, items, nitem);
  
  			return;
  		}
  
! 		itup = addItemPointersToTuple(index, ginstate, stack, itup, 
! 										items, nitem, isBuild, insStat);
  
  		btree.isDelete = TRUE;
  	}
***************
*** 194,206 ****
  			/* Add the rest, making a posting tree if necessary */
  			IndexTuple	previtup = itup;
  
! 			itup = addItemPointersToTuple(index, ginstate, stack, previtup, items + 1, nitem - 1, isBuild);
  			pfree(previtup);
  		}
  	}
  
  	btree.entry = itup;
! 	ginInsertValue(&btree, stack);
  	pfree(itup);
  }
  
--- 196,211 ----
  			/* Add the rest, making a posting tree if necessary */
  			IndexTuple	previtup = itup;
  
! 			itup = addItemPointersToTuple(index, ginstate, stack, previtup, 
! 											items + 1, nitem - 1, isBuild, insStat);
  			pfree(previtup);
  		}
+ 
+ 		insStat->numEntryInserted += 1;
  	}
  
  	btree.entry = itup;
! 	insStat->numNewEntryPages += ginInsertValue(&btree, stack);
  	pfree(itup);
  }
  
***************
*** 258,264 ****
  		{
  			/* there could be many entries, so be willing to abort here */
  			CHECK_FOR_INTERRUPTS();
! 			ginEntryInsert(index, &buildstate->ginstate, attnum, entry, list, nlist, TRUE);
  		}
  
  		MemoryContextReset(buildstate->tmpCtx);
--- 263,269 ----
  		{
  			/* there could be many entries, so be willing to abort here */
  			CHECK_FOR_INTERRUPTS();
! 			ginEntryInsert(index, &buildstate->ginstate, attnum, entry, list, nlist, TRUE, &buildstate->insertStat);
  		}
  
  		MemoryContextReset(buildstate->tmpCtx);
***************
*** 331,336 ****
--- 336,344 ----
  
  	/* build the index */
  	buildstate.indtuples = 0;
+ 	buildstate.insertStat.numEntryInserted = 0; 
+ 	buildstate.insertStat.numNewEntryPages = 1; /* root */
+ 	buildstate.insertStat.numNewDataPages = 0;
  
  	/*
  	 * create a temporary memory context that is reset once for each tuple
***************
*** 364,376 ****
  	{
  		/* there could be many entries, so be willing to abort here */
  		CHECK_FOR_INTERRUPTS();
! 		ginEntryInsert(index, &buildstate.ginstate, attnum, entry, list, nlist, TRUE);
  	}
  	MemoryContextSwitchTo(oldCtx);
  
  	MemoryContextDelete(buildstate.tmpCtx);
  
  	/*
  	 * Return statistics
  	 */
  	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
--- 372,389 ----
  	{
  		/* there could be many entries, so be willing to abort here */
  		CHECK_FOR_INTERRUPTS();
! 		ginEntryInsert(index, &buildstate.ginstate, attnum, entry, list, nlist, TRUE, &buildstate.insertStat);
  	}
  	MemoryContextSwitchTo(oldCtx);
  
  	MemoryContextDelete(buildstate.tmpCtx);
  
  	/*
+ 	 * Update local stats
+ 	 */
+ 	ginUpdateStat(index, &buildstate.insertStat, false);
+ 
+ 	/*
  	 * Return statistics
  	 */
  	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
***************
*** 385,391 ****
   * Inserts value during normal insertion
   */
  static uint32
! ginHeapTupleInsert(Relation index, GinState *ginstate, OffsetNumber attnum, Datum value, ItemPointer item)
  {
  	Datum	   *entries;
  	int32		i,
--- 398,405 ----
   * Inserts value during normal insertion
   */
  static uint32
! ginHeapTupleInsert(Relation index, GinState *ginstate, GinEntryInsertResult *insStat, 
! 					OffsetNumber attnum, Datum value, ItemPointer item)
  {
  	Datum	   *entries;
  	int32		i,
***************
*** 398,404 ****
  		return 0;
  
  	for (i = 0; i < nentries; i++)
! 		ginEntryInsert(index, ginstate, attnum, entries[i], item, 1, FALSE);
  
  	return nentries;
  }
--- 412,418 ----
  		return 0;
  
  	for (i = 0; i < nentries; i++)
! 		ginEntryInsert(index, ginstate, attnum, entries[i], item, 1, FALSE, insStat);
  
  	return nentries;
  }
***************
*** 444,454 ****
  	}
  	else
  	{
  		for (i = 0; i < ginstate.origTupdesc->natts; i++)
  			if (!isnull[i])
! 				ginHeapTupleInsert(index, &ginstate,
  								 (OffsetNumber) (i + 1), values[i], ht_ctid);
  
  	}
  
  	MemoryContextSwitchTo(oldCtx);
--- 458,476 ----
  	}
  	else
  	{
+ 		GinEntryInsertResult		res;
+ 
+ 		res.numEntryInserted = 0;
+ 		res.numNewEntryPages = 0;
+ 		res.numNewDataPages = 0;
+ 
  		for (i = 0; i < ginstate.origTupdesc->natts; i++)
  			if (!isnull[i])
! 				ginHeapTupleInsert(index, &ginstate, &res,
  								 (OffsetNumber) (i + 1), values[i], ht_ctid);
  
+ 		if (res.numEntryInserted + res.numNewEntryPages + res.numNewDataPages > 0)
+ 			ginUpdateStat(index, &res, true); 
  	}
  
  	MemoryContextSwitchTo(oldCtx);
diff -c -r -N src.orig/backend/access/gin/ginutil.c src/backend/access/gin/ginutil.c
*** src.orig/backend/access/gin/ginutil.c	2010-05-27 14:45:43.000000000 +0400
--- src/backend/access/gin/ginutil.c	2010-05-27 15:34:27.000000000 +0400
***************
*** 13,18 ****
--- 13,19 ----
   */
  
  #include "postgres.h"
+ #include "miscadmin.h"
  #include "access/genam.h"
  #include "access/gin.h"
  #include "access/reloptions.h"
***************
*** 227,232 ****
--- 228,236 ----
  	metadata->tailFreeSize = 0;
  	metadata->nPendingPages = 0;
  	metadata->nPendingHeapTuples = 0;
+ 	metadata->nEntryPages = 0;
+ 	metadata->nDataPages = 0;
+ 	metadata->nEntries = 0;
  }
  
  int
***************
*** 354,356 ****
--- 358,461 ----
  
  	PG_RETURN_BYTEA_P(rdopts);
  }
+ 
+ void
+ ginFillExtra(Relation index) 
+ {
+ 	Buffer			metabuffer;
+ 	Page			metapage;
+ 	GinMetaPageData	*metadata;
+ 	GinExtraData	*extra = (GinExtraData*)index->rd_amcache;
+ 
+ 	if (extra != NULL)
+ 		return;
+ 
+ 	extra = MemoryContextAlloc(index->rd_indexcxt, sizeof(*extra));
+ 
+ 	metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
+ 	LockBuffer(metabuffer, GIN_SHARE);
+ 	metapage = BufferGetPage(metabuffer);
+ 	metadata = GinPageGetMeta(metapage);
+ 
+ 	extra->nPendingPages = metadata->nPendingPages;
+ 	extra->nEntryPages = metadata->nEntryPages;
+ 	extra->nDataPages = metadata->nDataPages;
+ 	extra->nEntries = metadata->nEntries;
+ 
+ 	UnlockReleaseBuffer(metabuffer);
+ 
+ 	index->rd_amcache = extra;
+ }
+ 
+ void
+ ginUpdateStatInPlace(Relation index, GinMetaPageData *metadata, GinEntryInsertResult *r, bool isAdd)
+ {
+ 	GinExtraData	*extra = (GinExtraData*)index->rd_amcache;
+ 
+ 	if (extra == NULL)
+ 		extra = MemoryContextAlloc(index->rd_indexcxt, sizeof(*extra));
+ 
+ 	if (isAdd)
+ 	{
+ 		metadata->nEntryPages += r->numNewEntryPages;
+ 		metadata->nDataPages += r->numNewDataPages;
+ 		metadata->nEntries += r->numEntryInserted;
+ 	}
+ 	else
+ 	{
+ 		metadata->nEntryPages = r->numNewEntryPages;
+ 		metadata->nDataPages = r->numNewDataPages;
+ 		metadata->nEntries = r->numEntryInserted;
+ 	}
+ 
+ 	extra->nPendingPages = metadata->nPendingPages;
+ 	extra->nEntryPages = metadata->nEntryPages;
+ 	extra->nDataPages = metadata->nDataPages;
+ 	extra->nEntries = metadata->nEntries;
+ 
+ 	index->rd_amcache = extra;
+ }
+ 
+ void
+ ginUpdateStat(Relation index, GinEntryInsertResult *r, bool isAdd) 
+ {
+ 	Buffer			metabuffer;
+ 	Page			metapage;
+ 	GinMetaPageData	*metadata;
+ 
+ 	metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
+ 	LockBuffer(metabuffer, GIN_EXCLUSIVE);
+ 	metapage = BufferGetPage(metabuffer);
+ 	metadata = GinPageGetMeta(metapage);
+ 
+ 	START_CRIT_SECTION();
+ 
+ 	ginUpdateStatInPlace(index, metadata, r, isAdd);
+ 
+ 	MarkBufferDirty(metabuffer);
+ 
+ 	if (!index->rd_istemp)
+ 	{
+ 		XLogRecPtr  		recptr;
+ 		ginxlogUpdateMeta	data;
+ 		XLogRecData			rdata;
+ 
+ 		data.node = index->rd_node;
+ 		data.ntuples = 0;
+ 		data.newRightlink = data.prevTail = InvalidBlockNumber;
+ 		memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));
+ 
+ 		rdata.buffer = InvalidBuffer;
+ 		rdata.data = (char *) &data;
+ 		rdata.len = sizeof(ginxlogUpdateMeta);
+ 		rdata.next = NULL;
+ 
+ 		recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_UPDATE_META_PAGE, &rdata);
+ 		PageSetLSN(metapage, recptr);
+ 		PageSetTLI(metapage, ThisTimeLineID);
+ 	}
+ 
+ 	UnlockReleaseBuffer(metabuffer);
+ 
+ 	END_CRIT_SECTION();
+ }
diff -c -r -N src.orig/backend/access/gin/ginvacuum.c src/backend/access/gin/ginvacuum.c
*** src.orig/backend/access/gin/ginvacuum.c	2010-05-27 14:45:43.000000000 +0400
--- src/backend/access/gin/ginvacuum.c	2010-05-27 15:34:27.000000000 +0400
***************
*** 707,715 ****
  	BlockNumber npages,
  				blkno;
  	BlockNumber totFreePages;
- 	BlockNumber lastBlock = GIN_ROOT_BLKNO,
- 				lastFilledBlock = GIN_ROOT_BLKNO;
  	GinState	ginstate;
  
  	/*
  	 * In an autovacuum analyze, we want to clean up pending insertions.
--- 707,714 ----
  	BlockNumber npages,
  				blkno;
  	BlockNumber totFreePages;
  	GinState	ginstate;
+ 	GinEntryInsertResult	idxStat;
  
  	/*
  	 * In an autovacuum analyze, we want to clean up pending insertions.
***************
*** 736,741 ****
--- 735,742 ----
  		ginInsertCleanup(index, &ginstate, true, stats);
  	}
  
+ 	memset(&idxStat, 0, sizeof(idxStat));
+ 
  	/*
  	 * XXX we always report the heap tuple count as the number of index
  	 * entries.  This is bogus if the index is partial, but it's real hard to
***************
*** 757,763 ****
  
  	totFreePages = 0;
  
! 	for (blkno = GIN_ROOT_BLKNO + 1; blkno < npages; blkno++)
  	{
  		Buffer		buffer;
  		Page		page;
--- 758,764 ----
  
  	totFreePages = 0;
  
! 	for (blkno = GIN_ROOT_BLKNO; blkno < npages; blkno++)
  	{
  		Buffer		buffer;
  		Page		page;
***************
*** 771,785 ****
  
  		if (GinPageIsDeleted(page))
  		{
  			RecordFreeIndexPage(index, blkno);
  			totFreePages++;
  		}
! 		else
! 			lastFilledBlock = blkno;
  
  		UnlockReleaseBuffer(buffer);
  	}
! 	lastBlock = npages - 1;
  
  	/* Finally, vacuum the FSM */
  	IndexFreeSpaceMapVacuum(info->index);
--- 772,797 ----
  
  		if (GinPageIsDeleted(page))
  		{
+ 			Assert(blkno != GIN_ROOT_BLKNO);
  			RecordFreeIndexPage(index, blkno);
  			totFreePages++;
  		}
! 		else if (GinPageIsData(page))
! 		{
! 			idxStat.numNewDataPages++;
! 		}
! 		else if (!GinPageIsList(page))
! 		{
! 			idxStat.numNewEntryPages++;
! 
! 			if ( GinPageIsLeaf(page) )
! 				idxStat.numEntryInserted += PageGetMaxOffsetNumber(page); 
! 		}
  
  		UnlockReleaseBuffer(buffer);
  	}
! 
! 	ginUpdateStat(info->index, &idxStat, false);
  
  	/* Finally, vacuum the FSM */
  	IndexFreeSpaceMapVacuum(info->index);
diff -c -r -N src.orig/backend/utils/adt/selfuncs.c src/backend/utils/adt/selfuncs.c
*** src.orig/backend/utils/adt/selfuncs.c	2010-05-27 14:45:47.000000000 +0400
--- src/backend/utils/adt/selfuncs.c	2010-05-27 15:34:27.000000000 +0400
***************
*** 92,97 ****
--- 92,98 ----
  #include <math.h>
  
  #include "access/sysattr.h"
+ #include "access/gin.h"
  #include "catalog/index.h"
  #include "catalog/pg_opfamily.h"
  #include "catalog/pg_statistic.h"
***************
*** 6235,6240 ****
--- 6236,6252 ----
  	PG_RETURN_VOID();
  }
  
+ static int
+ match_index_to_operand_wo_col(Node *op, IndexOptInfo *index) {
+ 	int	i;
+ 
+ 	for(i=0; i<index->ncolumns; i++)
+ 		if (match_index_to_operand(op, i, index))
+ 			return i;
+ 
+ 	return -1;
+ }
+ 
  Datum
  gincostestimate(PG_FUNCTION_ARGS)
  {
***************
*** 6246,6255 ****
  	Cost	   *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
  	Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
  	double	   *indexCorrelation = (double *) PG_GETARG_POINTER(7);
  
! 	genericcostestimate(root, index, indexQuals, outer_rel, 0.0,
! 						indexStartupCost, indexTotalCost,
! 						indexSelectivity, indexCorrelation);
  
  	PG_RETURN_VOID();
  }
--- 6258,6547 ----
  	Cost	   *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
  	Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
  	double	   *indexCorrelation = (double *) PG_GETARG_POINTER(7);
+ 	ListCell	   *l;
+ 	int32		   nfullscan = 0;
+ 	List		   *selectivityQuals;
+ 	double		   numPages = index->pages,
+ 				   numTuples = index->tuples;
+ 	double		   numEntryPages,
+ 				   numDataPages,
+ 				   numPendingPages,
+ 				   numEntries;
+ 	double		   partialEntriesInQuals = 0.0;
+ 	double		   searchEntriesInQuals = 0.0;
+ 	double		   pagesFetched,
+ 				   pagesFetchedBySel;
+ 	double		   qual_op_cost,
+ 				   qual_arg_cost,
+ 				   spc_random_page_cost,
+ 				   num_scans;
+ 	QualCost	   index_qual_cost;
+ 	Relation	   indexRel;
+ 	GinExtraData   *extra;
+ 
+ 	/*
+ 	 * Obtain statistic information from the meta page
+ 	 */
+ 	indexRel = index_open(index->indexoid, AccessShareLock);
+ 	ginFillExtra(indexRel);
+ 	extra = (GinExtraData*)indexRel->rd_amcache;
+ 	numEntryPages = extra->nEntryPages;
+ 	numDataPages = extra->nDataPages;
+ 	numPendingPages = extra->nPendingPages;
+ 	numEntries = extra->nEntries;
+ 	index_close(indexRel, AccessShareLock);
+ 
+ #if 0
+ elog(NOTICE, "GIN stats: nEntryPages: %f nDataPages: %f nPendingPages :%f nEntries: %f",
+ 				numEntryPages, numDataPages, numPendingPages, numEntries);
+ #endif
+ 	/*
+ 	 * validation of stat's info
+ 	 */
+ 	if (numPages == 0.0)
+ 		numPages = 1.0;
+ 	if (numEntries == 0.0)
+ 		numEntries = numTuples;
+ 	if (numEntryPages == 0.0 || numEntryPages > numPages)
+ 		numEntryPages = numPages;
+ 	
+ 	/*
+ 	 * Copy-paste from genericcostesimate
+ 	 */
+ 	if (index->indpred != NIL)
+ 	{
+ 		List	   *predExtraQuals = NIL;
  
! 		foreach(l, index->indpred)
! 		{
! 			Node	   *predQual = (Node *) lfirst(l);
! 			List	   *oneQual = list_make1(predQual);
! 
! 			if (!predicate_implied_by(oneQual, indexQuals))
! 				predExtraQuals = list_concat(predExtraQuals, oneQual);
! 		}
! 		/* list_concat avoids modifying the passed-in indexQuals list */
! 		selectivityQuals = list_concat(predExtraQuals, indexQuals);
! 	}
! 	else
! 		selectivityQuals = indexQuals;
! 
! 	*indexSelectivity = clauselist_selectivity(root, selectivityQuals,
! 												index->rel->relid,
! 												JOIN_INNER,
! 												NULL);
! 
! 	/* fetch estimated page cost for schema containing index */
!     get_tablespace_page_costs(index->reltablespace,
! 							  &spc_random_page_cost,
! 							  NULL);
! 
! 	*indexStartupCost = *indexTotalCost = *indexCorrelation = 0.0;
! 
! 	/*
! 	 * Disc access costs is fully different from other indexes
! 	 */
! 	foreach(l, indexQuals) {
! 		RestrictInfo   	*rinfo = (RestrictInfo *) lfirst(l);
! 		Expr       	   	*clause;
! 		Node           	*leftop,
! 					   	*rightop,
! 					  	*operand;
! 		Oid				extractProcOid;
! 		Oid				clause_op;
! 		int				strategy_op;
! 		Oid				lefttype,
! 						righttype;
! 		int32			nentries;
! 		bool			*partial_matches = NULL;
! 		Pointer			*extra_data = NULL;
! 		int				indexcol;
! 
! 		Assert(IsA(rinfo, RestrictInfo));
! 		clause = rinfo->clause;
! 		Assert( IsA(clause, OpExpr) );
! 		leftop = get_leftop(clause);
! 		rightop = get_rightop(clause);
! 		clause_op = ((OpExpr *) clause)->opno;
! 
! 		if ( (indexcol = match_index_to_operand_wo_col(leftop, index)) >= 0 )
! 		{
! 			operand = rightop;
! 		}
! 		else if ( (indexcol = match_index_to_operand_wo_col(rightop, index)) >= 0 )
! 		{
! 			operand = leftop;
! 			clause_op = get_commutator(clause_op);
! 		} 
! 		else
! 		{
! 			elog(ERROR, "Could not match index to operand");
! 			operand = NULL; /* keep compiler quiet */
! 		}
! 
! 		if ( IsA(operand, RelabelType) )
! 			operand = (Node *) ((RelabelType *) operand)->arg;
! 
! 		/*
! 		 * It's impossible to call extractQuery method for not yet
! 		 * known operand (taken from table, for example). In this
! 		 * case we can't do anything useful...
! 		 */
! 		if ( !IsA(operand, Const) ) 
! 		{
! 			searchEntriesInQuals++;
! 		 	continue;
! 		 }
! 
! 		if (!op_in_opfamily(clause_op, index->opfamily[indexcol])) 
! 		{
! 			/* interesting: commutate operator is not indexable */
! 			searchEntriesInQuals++;
! 			continue;
! 		}
! 
! 		/*
! 		 * lefttype is a type of index column, righttype is a
! 		 * type of operand (query)
! 		 */
! 		get_op_opfamily_properties( clause_op, index->opfamily[indexcol],
! 									 &strategy_op, &lefttype, &righttype);
! 
! 		/* 
! 		 * GIN (as GiST) always has lefttype == righttype in pg_amproc
! 		 * and they are equal to type Oid on which index was created/designed
! 		 */
! 		extractProcOid = get_opfamily_proc( index->opfamily[indexcol],
! 											lefttype, lefttype, 
! 											GIN_EXTRACTQUERY_PROC );
! 
! 		if ( !OidIsValid(extractProcOid) )
! 			continue; /* should not be */
! 
! 		OidFunctionCall5( extractProcOid,
! 							((Const*)operand)->constvalue,
! 							PointerGetDatum(&nentries),
! 							UInt16GetDatum(strategy_op),
! 							PointerGetDatum(&partial_matches),
! 							PointerGetDatum(&extra_data));
! 
! 		if ( nentries == 0 )
! 		{
! 			nfullscan++;
! 		}
! 		else if ( nentries < 0 )
! 		{
! 			/*
! 			 * GIN_EXTRACTQUERY_PROC guarantees that nothing will be found
! 			 */
! 			*indexStartupCost = 0;
! 			*indexTotalCost = 0;
! 			*indexSelectivity = 0;
! 			*indexCorrelation = 0;
! 			PG_RETURN_VOID();
! 		}
! 		else
! 		{
! 			int		i;
! 
! 			for(i=0; i<nentries; i++)
! 			{
! 				/*
! 				 * For partial match we havn't any information to
! 				 * estimate number of matched entries in index, so,
! 				 * we just estimate it to 100
! 				 */
! 				if (partial_matches[i])
! 					partialEntriesInQuals += 100;
! 
! 				searchEntriesInQuals++;
! 			}
! 		}
! 	}
! 
! 	if ( nfullscan == list_length(indexQuals) )
! 		searchEntriesInQuals = numEntries;
! 
! 	if (outer_rel != NULL && outer_rel->rows > 1)
! 		num_scans = outer_rel->rows;
! 	else
! 		num_scans = 1;
! 
! #if 0
! elog(NOTICE,"searchEntriesInQuals: %f partialEntriesInQuals: %f numPages: %f numEntryPages: %f numTuples: %f numEntries: %f",
! 				searchEntriesInQuals,
! 				partialEntriesInQuals,
! 				numPages,
! 				numEntryPages,
! 				numTuples,
! 				numEntries);
! #endif
! 
! 
! 	/*
! 	 * cost to begin scan, first of all, pay attention to
! 	 * pending list.
! 	 */
! 	pagesFetched = numPendingPages;
! 
! 	/*
! 	 * estimate number of entry page reading, we need to do searchEntriesInQuals searches.
! 	 * Use a power function as it shout be, but tuples on leaf pages usually
! 	 * is much greater. 
! 	 */
! 	pagesFetched += ceil( searchEntriesInQuals * rint(pow( numEntryPages, 0.15 )) );
! 
! #if 0
! elog(NOTICE, "pagesFetched entry %f", pagesFetched);
! #endif
! 
! 	/* calculate cache effects */
! 	if (num_scans>1 || searchEntriesInQuals>1)
! 		pagesFetched =  index_pages_fetched(pagesFetched, 
! 											(BlockNumber)numEntryPages, 
! 											numEntryPages, root);
! 	*indexStartupCost = pagesFetched * spc_random_page_cost;
! 
! 	/* calc partial match: we don't need a search but an index scan */
! 	*indexStartupCost += partialEntriesInQuals * numEntryPages / numEntries;
! 
! 	/* cost to scan data pages for each matched entry */
! 	pagesFetched = ceil( (searchEntriesInQuals + partialEntriesInQuals) * 
! 						 numDataPages / numEntries );
! 
! 	pagesFetchedBySel = ceil( *indexSelectivity * (numTuples / ( BLCKSZ / SizeOfIptrData)) );
! #if 0
! elog(NOTICE, "pagesFetched data %f (by selectivity %f)", pagesFetched, pagesFetchedBySel);
! #endif
! 
! 	if ( pagesFetchedBySel > pagesFetched ) {
! 		/*
! 		 * At least one of entries is very frequent and, unfortunately,
! 		 * we couldn't get statistic about entries (only tsvector has
! 		 * has such statistics). So, we obviously have too small estimation of
! 		 * pages fetched form data tree. Re-estimate it from known 
! 		 * capacity of data pages
! 		 */
! 
! 		pagesFetched = pagesFetchedBySel;
! 	}
! 
! 	if (num_scans>1)
! 		pagesFetched =  index_pages_fetched(pagesFetched, 
! 											(BlockNumber)(numPages - numEntryPages), 
! 											(numPages - numEntryPages), root);
! 	*indexTotalCost = *indexStartupCost + pagesFetched * spc_random_page_cost; 
! 
! 	cost_qual_eval(&index_qual_cost, indexQuals, root);
! 	qual_op_cost = cpu_operator_cost * list_length(indexQuals);
! 	qual_arg_cost = index_qual_cost.startup +
! 					index_qual_cost.per_tuple - qual_op_cost;
! 	if (qual_arg_cost < 0)      /* just in case... */
! 		qual_arg_cost = 0;
! 
! 	*indexStartupCost += qual_arg_cost;
! 	*indexTotalCost += qual_arg_cost;
! 	*indexTotalCost += ( numTuples * *indexSelectivity ) * (cpu_index_tuple_cost + qual_op_cost);
  
  	PG_RETURN_VOID();
  }
diff -c -r -N src.orig/include/access/gin.h src/include/access/gin.h
*** src.orig/include/access/gin.h	2010-05-27 14:45:51.000000000 +0400
--- src/include/access/gin.h	2010-05-27 15:34:27.000000000 +0400
***************
*** 79,84 ****
--- 79,91 ----
  	 */
  	BlockNumber nPendingPages;
  	int64		nPendingHeapTuples;
+ 
+ 	/*
+ 	 * Statistic for planner
+ 	 */
+ 	BlockNumber	nEntryPages;
+ 	BlockNumber	nDataPages;
+ 	int64		nEntries;
  } GinMetaPageData;
  
  #define GinPageGetMeta(p) \
***************
*** 94,99 ****
--- 101,108 ----
  #define GinPageSetNonLeaf(page)    ( GinPageGetOpaque(page)->flags &= ~GIN_LEAF )
  #define GinPageIsData(page)    ( GinPageGetOpaque(page)->flags & GIN_DATA )
  #define GinPageSetData(page)   ( GinPageGetOpaque(page)->flags |= GIN_DATA )
+ #define GinPageIsList(page)    ( GinPageGetOpaque(page)->flags & GIN_LIST )
+ #define GinPageSetList(page)   ( GinPageGetOpaque(page)->flags |= GIN_LIST )
  #define GinPageHasFullRow(page)    ( GinPageGetOpaque(page)->flags & GIN_LIST_FULLROW )
  #define GinPageSetFullRow(page)   ( GinPageGetOpaque(page)->flags |= GIN_LIST_FULLROW )
  
***************
*** 353,365 ****
  extern Datum gin_index_getattr(GinState *ginstate, IndexTuple tuple);
  extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
  
  /* gininsert.c */
  extern Datum ginbuild(PG_FUNCTION_ARGS);
  extern Datum gininsert(PG_FUNCTION_ARGS);
  extern void ginEntryInsert(Relation index, GinState *ginstate,
  			   OffsetNumber attnum, Datum value,
  			   ItemPointerData *items, uint32 nitem,
! 			   bool isBuild);
  
  /* ginxlog.c */
  extern void gin_redo(XLogRecPtr lsn, XLogRecord *record);
--- 362,399 ----
  extern Datum gin_index_getattr(GinState *ginstate, IndexTuple tuple);
  extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
  
+ /*
+  * GinExtraData is stored in rm_amcache field
+  */
+ typedef struct GinExtraData {
+ 	/*
+ 	 * Statistic for planner
+ 	 */
+ 	BlockNumber nPendingPages;
+ 	BlockNumber	nEntryPages;
+ 	BlockNumber	nDataPages;
+ 	int64		nEntries;
+ } GinExtraData;
+ 
+ void ginFillExtra(Relation index);
+ 
+ typedef struct GinEntryInsertResult {
+ 	int64			numEntryInserted;
+ 	BlockNumber		numNewEntryPages;
+ 	BlockNumber		numNewDataPages;
+ } GinEntryInsertResult;
+ 
+ void ginUpdateStatInPlace(Relation index, GinMetaPageData *metadata, 
+ 							GinEntryInsertResult *r, bool isAdd);
+ void ginUpdateStat(Relation index, GinEntryInsertResult *insStat, bool isAdd);
+ 
  /* gininsert.c */
  extern Datum ginbuild(PG_FUNCTION_ARGS);
  extern Datum gininsert(PG_FUNCTION_ARGS);
  extern void ginEntryInsert(Relation index, GinState *ginstate,
  			   OffsetNumber attnum, Datum value,
  			   ItemPointerData *items, uint32 nitem,
! 			   bool isBuild, GinEntryInsertResult *insStat);
  
  /* ginxlog.c */
  extern void gin_redo(XLogRecPtr lsn, XLogRecord *record);
***************
*** 423,429 ****
  extern GinBtreeStack *ginPrepareFindLeafPage(GinBtree btree, BlockNumber blkno);
  extern GinBtreeStack *ginFindLeafPage(GinBtree btree, GinBtreeStack *stack);
  extern void freeGinBtreeStack(GinBtreeStack *stack);
! extern void ginInsertValue(GinBtree btree, GinBtreeStack *stack);
  extern void findParents(GinBtree btree, GinBtreeStack *stack, BlockNumber rootBlkno);
  
  /* ginentrypage.c */
--- 457,463 ----
  extern GinBtreeStack *ginPrepareFindLeafPage(GinBtree btree, BlockNumber blkno);
  extern GinBtreeStack *ginFindLeafPage(GinBtree btree, GinBtreeStack *stack);
  extern void freeGinBtreeStack(GinBtreeStack *stack);
! extern BlockNumber ginInsertValue(GinBtree btree, GinBtreeStack *stack);
  extern void findParents(GinBtree btree, GinBtreeStack *stack, BlockNumber rootBlkno);
  
  /* ginentrypage.c */
***************
*** 453,459 ****
  
  extern GinPostingTreeScan *prepareScanPostingTree(Relation index,
  					   BlockNumber rootBlkno, bool searchMode);
! extern void insertItemPointer(GinPostingTreeScan *gdi,
  				  ItemPointerData *items, uint32 nitem);
  extern Buffer scanBeginPostingTree(GinPostingTreeScan *gdi);
  extern void dataFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf);
--- 487,493 ----
  
  extern GinPostingTreeScan *prepareScanPostingTree(Relation index,
  					   BlockNumber rootBlkno, bool searchMode);
! extern BlockNumber insertItemPointer(GinPostingTreeScan *gdi,
  				  ItemPointerData *items, uint32 nitem);
  extern Buffer scanBeginPostingTree(GinPostingTreeScan *gdi);
  extern void dataFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf);
