diff -c -r -N src.orig/backend/access/gist/gistget.c src/backend/access/gist/gistget.c
*** src.orig/backend/access/gist/gistget.c	2009-11-26 11:58:25.000000000 +0300
--- src/backend/access/gist/gistget.c	2009-11-26 12:03:14.000000000 +0300
***************
*** 17,516 ****
  #include "access/gist_private.h"
  #include "access/relscan.h"
  #include "executor/execdebug.h"
  #include "miscadmin.h"
  #include "pgstat.h"
  #include "storage/bufmgr.h"
  #include "utils/memutils.h"
  
! 
! static OffsetNumber gistfindnext(IndexScanDesc scan, OffsetNumber n);
! static int64 gistnext(IndexScanDesc scan, TIDBitmap *tbm);
! static bool gistindex_keytest(IndexTuple tuple, IndexScanDesc scan,
! 				  OffsetNumber offset);
! 
! static void
! killtuple(Relation r, GISTScanOpaque so, ItemPointer iptr)
  {
! 	Page		p;
! 	OffsetNumber offset;
  
! 	LockBuffer(so->curbuf, GIST_SHARE);
! 	gistcheckpage(r, so->curbuf);
! 	p = (Page) BufferGetPage(so->curbuf);
  
! 	if (XLByteEQ(so->stack->lsn, PageGetLSN(p)))
! 	{
! 		/* page unchanged, so all is simple */
! 		offset = ItemPointerGetOffsetNumber(iptr);
! 		ItemIdMarkDead(PageGetItemId(p, offset));
! 		SetBufferCommitInfoNeedsSave(so->curbuf);
! 	}
! 	else
  	{
! 		OffsetNumber maxoff = PageGetMaxOffsetNumber(p);
  
! 		for (offset = FirstOffsetNumber; offset <= maxoff; offset = OffsetNumberNext(offset))
  		{
! 			IndexTuple	ituple = (IndexTuple) PageGetItem(p, PageGetItemId(p, offset));
  
! 			if (ItemPointerEquals(&(ituple->t_tid), iptr))
  			{
! 				/* found */
! 				ItemIdMarkDead(PageGetItemId(p, offset));
! 				SetBufferCommitInfoNeedsSave(so->curbuf);
! 				break;
  			}
  		}
! 	}
! 
! 	LockBuffer(so->curbuf, GIST_UNLOCK);
! }
  
! /*
!  * gistgettuple() -- Get the next tuple in the scan
!  */
! Datum
! gistgettuple(PG_FUNCTION_ARGS)
! {
! 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
! 	ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
! 	GISTScanOpaque so;
! 	bool		res;
  
! 	so = (GISTScanOpaque) scan->opaque;
  
! 	if (dir != ForwardScanDirection)
! 		elog(ERROR, "GiST doesn't support other scan directions than forward");
  
! 	/*
! 	 * If we have produced an index tuple in the past and the executor has
! 	 * informed us we need to mark it as "killed", do so now.
! 	 */
! 	if (scan->kill_prior_tuple && ItemPointerIsValid(&(so->curpos)))
! 		killtuple(scan->indexRelation, so, &(so->curpos));
  
! 	/*
! 	 * Get the next tuple that matches the search key.
! 	 */
! 	res = (gistnext(scan, NULL) > 0);
  
! 	PG_RETURN_BOOL(res);
! }
  
! Datum
! gistgetbitmap(PG_FUNCTION_ARGS)
! {
! 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
! 	TIDBitmap  *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
! 	int64		ntids;
  
! 	ntids = gistnext(scan, tbm);
  
! 	PG_RETURN_INT64(ntids);
  }
  
  /*
!  * Fetch tuple(s) that match the search key; this can be invoked
!  * either to fetch the first such tuple or subsequent matching tuples.
!  *
!  * This function is used by both gistgettuple and gistgetbitmap. When
!  * invoked from gistgettuple, tbm is null and the next matching tuple
!  * is returned in scan->xs_ctup.t_self.  When invoked from getbitmap,
!  * tbm is non-null and all matching tuples are added to tbm before
!  * returning.  In both cases, the function result is the number of
!  * returned tuples.
!  *
!  * If scan specifies to skip killed tuples, continue looping until we find a
!  * non-killed tuple that matches the search key.
   */
! static int64
! gistnext(IndexScanDesc scan, TIDBitmap *tbm)
  {
! 	Page		p;
! 	OffsetNumber n;
! 	GISTScanOpaque so;
! 	GISTSearchStack *stk;
! 	IndexTuple	it;
! 	GISTPageOpaque opaque;
! 	int64		ntids = 0;
! 
! 	so = (GISTScanOpaque) scan->opaque;
! 
! 	if (so->qual_ok == false)
! 		return 0;
! 
! 	if (so->curbuf == InvalidBuffer)
  	{
! 		if (ItemPointerIsValid(&so->curpos) == false)
! 		{
! 			/* Being asked to fetch the first entry, so start at the root */
! 			Assert(so->curbuf == InvalidBuffer);
! 			Assert(so->stack == NULL);
  
! 			so->curbuf = ReadBuffer(scan->indexRelation, GIST_ROOT_BLKNO);
  
! 			stk = so->stack = (GISTSearchStack *) palloc0(sizeof(GISTSearchStack));
  
! 			stk->next = NULL;
! 			stk->block = GIST_ROOT_BLKNO;
  
! 			pgstat_count_index_scan(scan->indexRelation);
! 		}
! 		else
  		{
! 			/* scan is finished */
! 			return 0;
  		}
  	}
  
  	/*
! 	 * check stored pointers from last visit
  	 */
! 	if (so->nPageData > 0)
  	{
  		/*
! 		 * gistgetmulti never should go here
  		 */
! 		Assert(tbm == NULL);
! 
! 		if (so->curPageData < so->nPageData)
! 		{
! 			scan->xs_ctup.t_self = so->pageData[so->curPageData].heapPtr;
! 			scan->xs_recheck = so->pageData[so->curPageData].recheck;
! 
! 			ItemPointerSet(&so->curpos,
! 						   BufferGetBlockNumber(so->curbuf),
! 						   so->pageData[so->curPageData].pageOffset);
! 
! 			so->curPageData++;
! 
! 			return 1;
! 		}
! 		else
  		{
! 			/*
! 			 * Go to the next page
! 			 */
! 			stk = so->stack->next;
! 			pfree(so->stack);
! 			so->stack = stk;
! 
! 			/* If we're out of stack entries, we're done */
! 			if (so->stack == NULL)
! 			{
! 				ReleaseBuffer(so->curbuf);
! 				so->curbuf = InvalidBuffer;
! 				return 0;
  			}
! 
! 			so->curbuf = ReleaseAndReadBuffer(so->curbuf,
! 											  scan->indexRelation,
! 											  stk->block);
! 		}
! 	}
! 
! 	for (;;)
! 	{
! 		CHECK_FOR_INTERRUPTS();
! 
! 		/* First of all, we need lock buffer */
! 		Assert(so->curbuf != InvalidBuffer);
! 		LockBuffer(so->curbuf, GIST_SHARE);
! 		gistcheckpage(scan->indexRelation, so->curbuf);
! 		p = BufferGetPage(so->curbuf);
! 		opaque = GistPageGetOpaque(p);
! 
! 		/* remember lsn to identify page changed for tuple's killing */
! 		so->stack->lsn = PageGetLSN(p);
! 
! 		/* check page split, occured since visit to parent */
! 		if (!XLogRecPtrIsInvalid(so->stack->parentlsn) &&
! 			XLByteLT(so->stack->parentlsn, opaque->nsn) &&
! 			opaque->rightlink != InvalidBlockNumber /* sanity check */ &&
! 			(so->stack->next == NULL || so->stack->next->block != opaque->rightlink)	/* check if already
! 				added */ )
! 		{
! 			/* detect page split, follow right link to add pages */
! 
! 			stk = (GISTSearchStack *) palloc(sizeof(GISTSearchStack));
! 			stk->next = so->stack->next;
! 			stk->block = opaque->rightlink;
! 			stk->parentlsn = so->stack->parentlsn;
! 			memset(&(stk->lsn), 0, sizeof(GistNSN));
! 			so->stack->next = stk;
! 		}
! 
! 		/* if page is empty, then just skip it */
! 		if (PageIsEmpty(p))
! 		{
! 			LockBuffer(so->curbuf, GIST_UNLOCK);
! 			stk = so->stack->next;
! 			pfree(so->stack);
! 			so->stack = stk;
! 
! 			if (so->stack == NULL)
! 			{
! 				ReleaseBuffer(so->curbuf);
! 				so->curbuf = InvalidBuffer;
! 				return ntids;
  			}
! 
! 			so->curbuf = ReleaseAndReadBuffer(so->curbuf, scan->indexRelation,
! 											  stk->block);
! 			continue;
! 		}
! 
! 		n = FirstOffsetNumber;
! 
! 		/* wonderful, we can look at page */
! 		so->nPageData = so->curPageData = 0;
! 
! 		for (;;)
! 		{
! 			n = gistfindnext(scan, n);
! 
! 			if (!OffsetNumberIsValid(n))
  			{
! 				/*
! 				 * If we was called from gistgettuple and current buffer
! 				 * contains something matched then make a recursive call - it
! 				 * will return ItemPointer from so->pageData. But we save
! 				 * buffer pinned to support tuple's killing
  				 */
! 				if (!tbm && so->nPageData > 0)
! 				{
! 					LockBuffer(so->curbuf, GIST_UNLOCK);
! 					return gistnext(scan, NULL);
! 				}
  
! 				/*
! 				 * We ran out of matching index entries on the current page,
! 				 * so pop the top stack entry and use it to continue the
! 				 * search.
! 				 */
! 				LockBuffer(so->curbuf, GIST_UNLOCK);
! 				stk = so->stack->next;
! 				pfree(so->stack);
! 				so->stack = stk;
  
! 				/* If we're out of stack entries, we're done */
  
! 				if (so->stack == NULL)
  				{
! 					ReleaseBuffer(so->curbuf);
! 					so->curbuf = InvalidBuffer;
! 					return ntids;
  				}
  
! 				so->curbuf = ReleaseAndReadBuffer(so->curbuf,
! 												  scan->indexRelation,
! 												  stk->block);
! 				/* XXX	go up */
! 				break;
! 			}
  
! 			if (GistPageIsLeaf(p))
! 			{
! 				/*
! 				 * We've found a matching index entry in a leaf page, so
! 				 * return success. Note that we keep "curbuf" pinned so that
! 				 * we can efficiently resume the index scan later.
! 				 */
  
! 				if (!(scan->ignore_killed_tuples &&
! 					  ItemIdIsDead(PageGetItemId(p, n))))
  				{
! 					it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
! 					ntids++;
! 					if (tbm != NULL)
! 						tbm_add_tuples(tbm, &it->t_tid, 1, scan->xs_recheck);
! 					else
! 					{
! 						so->pageData[so->nPageData].heapPtr = it->t_tid;
! 						so->pageData[so->nPageData].pageOffset = n;
! 						so->pageData[so->nPageData].recheck = scan->xs_recheck;
! 						so->nPageData++;
! 					}
  				}
  			}
! 			else
! 			{
! 				/*
! 				 * We've found an entry in an internal node whose key is
! 				 * consistent with the search key, so push it to stack
! 				 */
! 				stk = (GISTSearchStack *) palloc(sizeof(GISTSearchStack));
  
! 				it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
! 				stk->block = ItemPointerGetBlockNumber(&(it->t_tid));
! 				memset(&(stk->lsn), 0, sizeof(GistNSN));
! 				stk->parentlsn = so->stack->lsn;
  
! 				stk->next = so->stack->next;
! 				so->stack->next = stk;
! 			}
  
! 			n = OffsetNumberNext(n);
  		}
  	}
  
! 	return ntids;
  }
  
  /*
!  * gistindex_keytest() -- does this index tuple satisfy the scan key(s)?
!  *
!  * On success return for a leaf tuple, scan->xs_recheck is set to indicate
!  * whether recheck is needed.  We recheck if any of the consistent() functions
!  * request it.
!  *
!  * We must decompress the key in the IndexTuple before passing it to the
!  * sk_func (and we have previously overwritten the sk_func to use the
!  * user-defined Consistent method, so we actually are invoking that).
!  *
!  * Note that this function is always invoked in a short-lived memory context,
!  * so we don't need to worry about cleaning up allocated memory, either here
!  * or in the implementation of any Consistent methods.
   */
  static bool
! gistindex_keytest(IndexTuple tuple,
! 				  IndexScanDesc scan,
! 				  OffsetNumber offset)
  {
! 	int			keySize = scan->numberOfKeys;
! 	ScanKey		key = scan->keyData;
! 	Relation	r = scan->indexRelation;
! 	GISTScanOpaque so;
! 	Page		p;
! 	GISTSTATE  *giststate;
! 
! 	so = (GISTScanOpaque) scan->opaque;
! 	giststate = so->giststate;
! 	p = BufferGetPage(so->curbuf);
! 
! 	scan->xs_recheck = false;
! 
! 	/*
! 	 * Tuple doesn't restore after crash recovery because of incomplete insert
! 	 */
! 	if (!GistPageIsLeaf(p) && GistTupleIsInvalid(tuple))
! 		return true;
  
! 	while (keySize > 0)
  	{
! 		Datum		datum;
! 		bool		isNull;
! 		Datum		test;
! 		bool		recheck;
! 		GISTENTRY	de;
  
! 		datum = index_getattr(tuple,
! 							  key->sk_attno,
! 							  giststate->tupdesc,
! 							  &isNull);
  
! 		if (key->sk_flags & SK_ISNULL)
  		{
! 			/*
! 			 * On non-leaf page we can't conclude that child hasn't NULL
! 			 * values because of assumption in GiST: uinon (VAL, NULL) is VAL
! 			 * But if on non-leaf page key IS  NULL then all childs has NULL.
! 			 */
! 
! 			Assert(key->sk_flags & SK_SEARCHNULL);
! 
! 			if (GistPageIsLeaf(p) && !isNull)
! 				return false;
  		}
! 		else if (isNull)
  		{
! 			return false;
  		}
! 		else
  		{
! 			gistdentryinit(giststate, key->sk_attno - 1, &de,
! 						   datum, r, p, offset,
! 						   FALSE, isNull);
  
! 			/*
! 			 * Call the Consistent function to evaluate the test.  The
! 			 * arguments are the index datum (as a GISTENTRY*), the comparison
! 			 * datum, the comparison operator's strategy number and subtype
! 			 * from pg_amop, and the recheck flag.
! 			 *
! 			 * (Presently there's no need to pass the subtype since it'll
! 			 * always be zero, but might as well pass it for possible future
! 			 * use.)
! 			 *
! 			 * We initialize the recheck flag to true (the safest assumption)
! 			 * in case the Consistent function forgets to set it.
! 			 */
! 			recheck = true;
  
! 			test = FunctionCall5(&key->sk_func,
! 								 PointerGetDatum(&de),
! 								 key->sk_argument,
! 								 Int32GetDatum(key->sk_strategy),
! 								 ObjectIdGetDatum(key->sk_subtype),
! 								 PointerGetDatum(&recheck));
! 
! 			if (!DatumGetBool(test))
! 				return false;
! 			scan->xs_recheck |= recheck;
! 		}
  
! 		keySize--;
! 		key++;
  	}
  
! 	return true;
  }
  
! /*
!  * Return the offset of the first index entry that is consistent with
!  * the search key after offset 'n' in the current page. If there are
!  * no more consistent entries, return InvalidOffsetNumber.
!  * On success, scan->xs_recheck is set correctly, too.
!  * Page should be locked....
!  */
! static OffsetNumber
! gistfindnext(IndexScanDesc scan, OffsetNumber n)
  {
! 	OffsetNumber maxoff;
! 	IndexTuple	it;
! 	GISTScanOpaque so;
! 	MemoryContext oldcxt;
! 	Page		p;
! 
! 	so = (GISTScanOpaque) scan->opaque;
! 	p = BufferGetPage(so->curbuf);
! 	maxoff = PageGetMaxOffsetNumber(p);
  
! 	/*
! 	 * Make sure we're in a short-lived memory context when we invoke a
! 	 * user-supplied GiST method in gistindex_keytest(), so we don't leak
! 	 * memory
! 	 */
! 	oldcxt = MemoryContextSwitchTo(so->tempCxt);
  
! 	while (n >= FirstOffsetNumber && n <= maxoff)
  	{
! 		it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
! 		if (gistindex_keytest(it, scan, n))
  			break;
  
! 		n = OffsetNumberNext(n);
! 	}
  
! 	MemoryContextSwitchTo(oldcxt);
! 	MemoryContextReset(so->tempCxt);
  
! 	/*
! 	 * If we found a matching entry, return its offset; otherwise return
! 	 * InvalidOffsetNumber to inform the caller to go to the next page.
! 	 */
! 	if (n >= FirstOffsetNumber && n <= maxoff)
! 		return n;
! 	else
! 		return InvalidOffsetNumber;
  }
--- 17,463 ----
  #include "access/gist_private.h"
  #include "access/relscan.h"
  #include "executor/execdebug.h"
+ #include "catalog/pg_type.h"
  #include "miscadmin.h"
  #include "pgstat.h"
  #include "storage/bufmgr.h"
  #include "utils/memutils.h"
+ #include "utils/builtins.h"
  
! /*
!  * check item tuple by consistent fucntions 
!  */
! static double* 
! processIndexTuple(IndexScanDesc scan, Page page, OffsetNumber i, IndexTuple it, bool *recheck)
  {
! 	GISTScanOpaque		so = (GISTScanOpaque) scan->opaque;
! 	MemoryContext		oldcxt;
! 	int					keySize = scan->numberOfKeys;
! 	ScanKey				key = scan->keyData;
! 
! 	*recheck = false;
! 	memset(so->distances, 0, sizeof(double) * scan->numberOfKeys);
! 
! 	/* 
! 	 * tuple doesn't restore after crash, put it in the begining.
! 	 * Underlying tuple will be ordered by right way
! 	 */
! 	if (!GistPageIsLeaf(page) && GistTupleIsInvalid(it))
! 		return so->distances;
! 
! 	/*
! 	 * Count distance, user defined functions are called in short-lived memory context
! 	 */
  
! 	oldcxt = MemoryContextSwitchTo(so->tempCxt);
  
! 	while (keySize > 0)
  	{
! 		double		keyDistance = 0.0;
! 		Datum		datum;
! 		bool		isNull;
! 		GISTENTRY	de;
  
! 		datum = index_getattr(it,
! 							  key->sk_attno,
! 							  so->giststate->tupdesc,
! 							  &isNull);
! 
! 		if (key->sk_flags & SK_ISNULL)
  		{
! 			/*
! 			 * On non-leaf page we can't conclude that child hasn't NULL
! 			 * values because of assumption in GiST: union (VAL, NULL) is VAL
! 			 * But if on non-leaf page key IS  NULL then all childs has NULL.
! 			 */
! 			Assert(key->sk_flags & SK_SEARCHNULL);
  
! 			if (so->operatorRetType[ key - scan->keyData ] == BOOLOID)
  			{
! 				/* ordinary operator */
! 				if (GistPageIsLeaf(page) && !isNull)
! 					keyDistance = -1.0;
! 			}
! 			else if (GistPageIsLeaf(page))
! 			{
! 				/* knn-operator, NULL are last */
! 				keyDistance = (isNull) ? 0.0 : get_float8_infinity();
  			}
  		}
! 		else if (isNull)
! 		{
! 			if (so->operatorRetType[ key - scan->keyData ] == BOOLOID)
! 			{
! 				/* ordinary operator */
! 				keyDistance = -1.0;
! 			}
! 			else
! 			{
! 				/* knn-operator, NULL are last */
! 				keyDistance = get_float8_infinity();
! 			}
! 		}
! 		else
! 		{
! 			bool	keyRecheck = true;
! 			Datum	consistentResult;
  
! 			gistdentryinit(so->giststate, key->sk_attno - 1, &de,
! 							datum, scan->indexRelation, page, i,
! 							FALSE, isNull);
! 
! 			/*--------------
! 			 *  Return value for user-defined KNNGiST-style consistent method:
! 			 *  - negative value means that tuple doesn't match by query
! 			 *    (it's the same as false for GiST's consistent)
! 			 *  - 0.0 means one of:
! 			 *    - just a zero distance (exact match)
! 			 *    - just a match (for filtering clauses like a <@ or @> for point)
! 			 *    KNNGist doesn't distinguish filtering clauses and ordering clauses in this 
! 			 *    case, so it believes in user-defined methods
! 			 *  - positive means distance. In this case keyRecheck should be false!
! 			 *    because it's impossible to make right order with values which
! 			 *    shoould be rechecked.
! 			 */
  
! 			consistentResult = FunctionCall5(&key->sk_func,
! 										PointerGetDatum(&de),
! 										key->sk_argument,
! 										Int32GetDatum(key->sk_strategy),
! 										ObjectIdGetDatum(key->sk_subtype),
! 										PointerGetDatum(&keyRecheck));
  
! 			if ( so->consistentRetType[ key->sk_attno - 1 ] == FLOAT8OID )
! 			{
! 				/* KNNGiST - style consistentFn */
! 				keyDistance = DatumGetFloat8(consistentResult);
  
! 				if (keyDistance > 0.0 && keyRecheck == true)
! 					elog(ERROR, "Could not recheck result for distance ordering");
! 			}
! 			else
! 			{
! 				/* GiST - style consistentFn */
! 				if ( DatumGetBool(consistentResult) == false )
! 					keyDistance = -1.0;
! 			}
  
! 			*recheck |= keyRecheck;
! 		}
  
! 		/*
! 		 * negative distance points that tuple isn't matched by query
! 		 */
! 		if (keyDistance < 0)
! 		{
! 			so->distances[0] = -1.0;
! 			break;
! 		}
  
! 		if (is_infinite(keyDistance) != 0)
! 			so->distances[ key - scan->keyData ] = get_float8_infinity();
! 		else if (is_infinite(so->distances[ key->sk_attno - 1 ]) == 0)
! 			so->distances[ key - scan->keyData ] += keyDistance;
  
! 		keySize--;
! 		key++;
! 	}
  
! 	MemoryContextSwitchTo(oldcxt);
! 	MemoryContextReset(so->tempCxt);
! 	
! 	return (so->distances[0] >= 0.0) ? so->distances : NULL;
  }
  
  /*
!  * Put all items in page to stack in ordered manner, if it's 
!  * needed follow right link and process it too
   */
! static void
! processPage(IndexScanDesc scan, DataPointer *data, double *myDistances, TIDBitmap *tbm, int64 *ntids)
  {
! 	GISTScanOpaque		so = (GISTScanOpaque) scan->opaque;
! 	Buffer				buffer;
! 	Page				page;
! 	GISTPageOpaque		opaque;
! 	OffsetNumber		i;
! 	StackElem		   *elem = NULL;
! 
! 	Assert(data->blkno != InvalidBlockNumber);
! 
! 	buffer = ReadBuffer(scan->indexRelation, data->blkno);
! 	LockBuffer(buffer, GIST_SHARE);
! 	gistcheckpage(scan->indexRelation, buffer);
! 	page = BufferGetPage(buffer);
! 	opaque = GistPageGetOpaque(page);
! 
! 	/* check page split, occured since visit to parent */
! 	if ( !XLogRecPtrIsInvalid(data->data.parentlsn) &&
! 		 XLByteLT(data->data.parentlsn, opaque->nsn) &&
! 		 opaque->rightlink != InvalidBlockNumber /* sanity check */ )
  	{
! 		/* detect page split, follow right link to add pages */
! 		DataPointer	   *pointer;
! 		MemoryContext	oldcxt;
  
! 		Assert(myDistances != NULL); 
! 		oldcxt = MemoryContextSwitchTo(so->stackCxt);
  
! 		pointer = palloc(sizeof(DataPointer));
! 		pointer->next = NULL;
! 		pointer->blkno = opaque->rightlink;
! 		pointer->data.parentlsn = data->data.parentlsn;
  
! 		if (elem == NULL)
! 			elem = palloc(SEHDRSZ + sizeof(double) * scan->numberOfKeys);
  
! 		elem->head = elem->tail = pointer;
! 		memcpy( elem->distances, myDistances, sizeof(double) * scan->numberOfKeys);
! 
! 		if ( rb_insert(so->stack, elem) == NULL )
  		{
! 			elem = NULL; /* elem now in the tree, in opposite case it was appended to
! 						  * one of the previuos value */
  		}
+ 		MemoryContextSwitchTo(oldcxt);
  	}
  
+ 	so->nPageData = so->curPageData = 0;
+ 
  	/*
! 	 * check all tuples on page
  	 */
! 	for(i=FirstOffsetNumber; i<=PageGetMaxOffsetNumber(page); i=OffsetNumberNext(i)) 
  	{
+ 		IndexTuple 	it =	(IndexTuple) PageGetItem(page, PageGetItemId(page, i));
+ 		bool		recheck;
+ 		double	   *distances = processIndexTuple(scan, page, i, it, &recheck);
+ 
  		/*
! 		 * non-null pointer contains non-negative distances
  		 */
! 		if (distances)
  		{
! 			if (tbm && GistPageIsLeaf(page))
! 			{
! 				tbm_add_tuples(tbm, &it->t_tid, 1, recheck);
! 				(*ntids)++;
  			}
! 			else if (so->isKNNsearch == false && GistPageIsLeaf(page))
! 			{
! 				so->pageData[ so->nPageData ].heapPtr = it->t_tid;
! 				so->pageData[ so->nPageData ].recheck = recheck;
! 				so->nPageData++;
  			}
! 			else
  			{
! 				/* 
! 				 * We do a KNNsearch or we need to put inner page to the stack
  				 */
! 				DataPointer	   *pointer;
! 				MemoryContext	oldcxt;
  
! 				oldcxt = MemoryContextSwitchTo(so->stackCxt);
  
! 				pointer = palloc(sizeof(DataPointer));
! 				pointer->next = NULL;
  
! 				if (GistPageIsLeaf(page))
  				{
! 					pointer->blkno = InvalidBlockNumber;
! 					pointer->data.heap.ptr = it->t_tid;
! 					pointer->data.heap.recheck = recheck;
! 				}
! 				else
! 				{
! 					pointer->blkno = ItemPointerGetBlockNumber(&it->t_tid);
! 					/* lsn of current page is a lsn of parent page for child */ 
! 					pointer->data.parentlsn = PageGetLSN(page);
  				}
  
! 				if (elem == NULL)
! 					elem = palloc(SEHDRSZ + sizeof(double) * scan->numberOfKeys);
  
! 				elem->head = elem->tail = pointer;
! 				memcpy( elem->distances, distances, sizeof(double) * scan->numberOfKeys);
  
! 				if ( rb_insert(so->stack, elem) == NULL )
  				{
! 					elem = NULL; /* elem now in the tree, in opposite case it was appended to
! 								  * one of the previuos value */
  				}
+ 				MemoryContextSwitchTo(oldcxt);
  			}
! 		}
! 	}
  
! 	if (elem)
! 		pfree(elem);
! 	UnlockReleaseBuffer(buffer);
! }
  
! /*
!  * returns next pointer from stack
!  */
! static DataPointer*
! getNextDataPointer(GISTScanOpaque so)
! {
! 	DataPointer		*pointer = NULL;
  
! 	for(;;) 
! 	{
! 		if (so->currentPointer == NULL)
! 		{
! 			so->currentPointer = rb_leftmost(so->stack);
! 
! 			if (so->currentPointer == NULL)
! 				break;
  		}
+ 
+ 		pointer = so->currentPointer->head;
+ 		if (pointer == NULL)
+ 		{
+ 			pfree(so->currentPointer);
+ 			so->currentPointer = NULL;
+ 			continue;
+ 		}
+ 
+ 		so->currentPointer->head = pointer->next;
+ 		break;
  	}
  
! 	return pointer; 
  }
  
  /*
!  * returns next nearest heap pointer in for KNNsearch
   */
  static bool
! getNextNearest(IndexScanDesc scan, ItemPointerData *iptr, bool *recheck)
  {
! 	GISTScanOpaque		so = (GISTScanOpaque) scan->opaque;
! 	bool				res = false;
  
! 	do
  	{
! 		DataPointer		*pointer = getNextDataPointer(so);
  
! 		if (!pointer)
! 			break;
  
! 		if (pointer->blkno == InvalidBlockNumber)
  		{
! 			/* found a child on currently minimal distance */
! 			*iptr = pointer->data.heap.ptr;
! 			*recheck = pointer->data.heap.recheck;
! 			res = true;
  		}
! 		else
  		{
! 			/* childs in pointer are not closer than current distance */
! 			processPage(scan, pointer, so->currentPointer->distances, NULL, NULL);
  		}
! 
! 		pfree(pointer);
! 	} while(res == false);
! 
! 	return res;
! }
! 
! /*
!  * gistgettuple() -- Get the next tuple in the scan
!  */
! Datum
! gistgettuple(PG_FUNCTION_ARGS)
! {
! 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
! 	GISTScanOpaque		so = (GISTScanOpaque) scan->opaque;
! 
! 	if (so->qual_ok == false)
! 		PG_RETURN_BOOL(false);
! 
! 	if (so->firstCall == true)
! 	{
! 		DataPointer		fakePtr;
! 
! 		/* Being asked to fetch the first entry, so start at the root */
! 
! 		so->curPageData = so->nPageData = 0;
! 		so->firstCall = false;
! 		so->currentPointer = NULL;
! 
! 		fakePtr.blkno = GIST_ROOT_BLKNO;
! 		memset(&fakePtr.data.parentlsn, 0, sizeof(GistNSN));
! 		processPage(scan, &fakePtr, NULL, NULL, NULL);
! 	}
! 
! 	if (so->isKNNsearch)
! 	{
! 		PG_RETURN_BOOL(getNextNearest(scan, &scan->xs_ctup.t_self, &scan->xs_recheck));
! 	}
! 	else
! 	{
! 		for(;;)
  		{
! 			if ( so->curPageData < so->nPageData )
! 			{
! 				scan->xs_ctup.t_self = so->pageData[ so->curPageData ].heapPtr;
! 				scan->xs_recheck = so->pageData[ so->curPageData ].recheck;
! 				so->curPageData++;
! 				PG_RETURN_BOOL(true);
! 			}
  
! 			do
! 			{
! 				DataPointer		*pointer = getNextDataPointer(so);
  
! 				if (!pointer)
! 					PG_RETURN_BOOL(false);
  
! 				/* processPage puts on stack only inner pages */
! 				Assert(pointer->blkno != InvalidBlockNumber);
! 				processPage(scan, pointer, so->currentPointer->distances, NULL, NULL);
! 
! 				pfree(pointer);
! 			} while( so->nPageData == 0 );
! 		}
  	}
  
! 	PG_RETURN_BOOL(false); /* keep compiler quiet */
  }
  
! Datum
! gistgetbitmap(PG_FUNCTION_ARGS)
  {
! 	IndexScanDesc	 	scan = (IndexScanDesc) PG_GETARG_POINTER(0);
! 	TIDBitmap		   *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
! 	GISTScanOpaque		so = (GISTScanOpaque) scan->opaque;
! 	int64				ntids = 0;
! 	DataPointer			fakePtr;
  
! 	if (so->qual_ok == false)
! 		PG_RETURN_INT64(0);
! 
! 	fakePtr.blkno = GIST_ROOT_BLKNO;
! 	memset(&fakePtr.data.parentlsn, 0, sizeof(GistNSN));
! 	processPage(scan, &fakePtr, NULL, tbm, &ntids);
  
! 	so->currentPointer = NULL;
! 
! 	for(;;)
  	{
! 		DataPointer		*pointer = getNextDataPointer(so);
! 
! 		if (!pointer)
  			break;
  
! 		/* processPage puts on stack only inner pages */
! 		Assert(pointer->blkno != InvalidBlockNumber);
! 		processPage(scan, pointer, so->currentPointer->distances, tbm, &ntids);
  
! 		pfree(pointer);
! 	}
  
! 	PG_RETURN_INT64(ntids);
  }
+ 
diff -c -r -N src.orig/backend/access/gist/gistproc.c src/backend/access/gist/gistproc.c
*** src.orig/backend/access/gist/gistproc.c	2009-11-26 11:58:25.000000000 +0300
--- src/backend/access/gist/gistproc.c	2009-11-26 12:03:14.000000000 +0300
***************
*** 873,880 ****
  	PG_RETURN_BOOL(result);
  }
  
- 
- 
  /*
   * Point
   */
--- 873,878 ----
***************
*** 899,904 ****
--- 897,966 ----
  	PG_RETURN_POINTER(entry);
  }
  
+ #define	point_point_distance(p1,p2)	DatumGetFloat8(DirectFunctionCall2(point_distance, PointPGetDatum(p1), PointPGetDatum(p2))) 
+ static double
+ computeDistance(bool isLeaf, BOX *box, Point *point)
+ {
+ 	double			result;
+ 
+ 	if ( isLeaf ) 
+ 	{
+ 		/* point to point distance */
+ 		result = point_point_distance(point, &box->low);
+ 	}
+ 	else if (point->x <= box->high.x && point->x >= box->low.x &&
+ 			 point->y <= box->high.y && point->y >= box->low.y)
+ 	{
+ 		/* point inside the box */
+ 		result = 0.0;
+ 	}
+ 	else if (point->x <= box->high.x && point->x >= box->low.x)
+ 	{
+ 		/* point is over or below box */
+ 		Assert( box->low.y <= box->high.y );
+ 		if (point->y > box->high.y)
+ 			result = point->y - box->high.y;
+ 		else if (point->y < box->low.y)
+ 			result = box->low.y - point->y;
+ 		else 
+ 			elog(ERROR, "Should not be here 1");
+ 	}
+ 	else if (point->y <= box->high.y && point->y >= box->low.y)
+ 	{
+ 		/* point is lefter or rigther box */
+ 		Assert( box->low.x <= box->high.x );
+ 		if (point->x > box->high.x)
+ 			result = point->x - box->high.x;
+ 		else if (point->x < box->low.x)
+ 			result = box->low.x - point->x;
+ 		else
+ 			elog(ERROR, "Should not be here 2");
+ 	}
+ 	else
+ 	{
+ 		/* closest will be a vertix */
+ 		Point	p;
+ 		double	subresult;
+ 
+ 		result = point_point_distance(point, &box->low);
+ 
+ 		subresult = point_point_distance(point, &box->high);
+ 		if (result > subresult) result = subresult;
+ 
+ 		p.x = box->low.x;
+ 		p.y = box->high.y;
+ 		subresult = point_point_distance(point, &p);
+ 		if (result > subresult) result = subresult;
+ 
+ 		p.x = box->high.x;
+ 		p.y = box->low.y;
+ 		subresult = point_point_distance(point, &p);
+ 		if (result > subresult) result = subresult;
+ 	}
+ 
+ 	return result;
+ }
+ 
  static bool
  gist_point_consistent_internal(StrategyNumber strategy, bool isLeaf, BOX *key, Point *query)
  {
***************
*** 944,962 ****
  
  Datum
  gist_point_consistent(PG_FUNCTION_ARGS) {
! 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
! 	StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
! 	bool		result;
! 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
  	StrategyNumber	strategyGroup = strategy / StrategyNumberOffsetRange;
  
  	switch(strategyGroup)
  	{
  		case	PointStrategyNumberGroup:
! 			result = gist_point_consistent_internal(strategy % StrategyNumberOffsetRange,
! 													GIST_LEAF(entry),
! 													DatumGetBoxP(entry->key),
! 													PG_GETARG_POINT_P(1));
  			*recheck = false;
  			break;
  		case	BoxStrategyNumberGroup:
--- 1006,1036 ----
  
  Datum
  gist_point_consistent(PG_FUNCTION_ARGS) {
! 	GISTENTRY  *	entry = (GISTENTRY *) PG_GETARG_POINTER(0);
! 	StrategyNumber 	strategy = (StrategyNumber) PG_GETARG_UINT16(2);
! 	bool	   	   *recheck = (bool *) PG_GETARG_POINTER(4);
  	StrategyNumber	strategyGroup = strategy / StrategyNumberOffsetRange;
+ 	double			distance;
+ 	bool			result;
  
  	switch(strategyGroup)
  	{
  		case	PointStrategyNumberGroup:
! 			if (strategy % StrategyNumberOffsetRange == KNNSearchStrategyNumber)
! 			{
! 				distance = computeDistance(GIST_LEAF(entry), 
! 											DatumGetBoxP(entry->key), 
! 											PG_GETARG_POINT_P(1));
! 				*recheck = false; /* distance computation should be exact! */  
! 			}
! 			else
! 			{
! 				result = gist_point_consistent_internal(strategy % StrategyNumberOffsetRange,
! 														GIST_LEAF(entry),
! 														DatumGetBoxP(entry->key),
! 														PG_GETARG_POINT_P(1));
! 				distance = (result) ? 0.0 : -1.0;
! 			}
  			*recheck = false;
  			break;
  		case	BoxStrategyNumberGroup:
***************
*** 966,971 ****
--- 1040,1046 ----
  											PG_GETARG_DATUM(1),
  											Int16GetDatum(RTOverlapStrategyNumber),
  											0, PointerGetDatum(recheck)));
+ 			distance = (result) ? 0.0 : -1.0;
  			break;
  		case	PolygonStrategyNumberGroup:
  			do {
***************
*** 994,999 ****
--- 1069,1075 ----
  					*recheck = false;
  				}
  			} while (0);
+ 			distance = (result) ? 0.0 : -1.0;
  			break;
  		case	CircleStrategyNumberGroup:
  			do {
***************
*** 1022,1032 ****
  					*recheck = false;
  				}
  			} while(0);
  			break;
  		default:
  			elog(ERROR, "unknown strategy number: %d", strategy);
  	}
  
! 	PG_RETURN_BOOL(result);
  }
  
--- 1098,1109 ----
  					*recheck = false;
  				}
  			} while(0);
+ 			distance = (result) ? 0.0 : -1.0;
  			break;
  		default:
  			elog(ERROR, "unknown strategy number: %d", strategy);
  	}
  
! 	PG_RETURN_FLOAT8(distance);
  }
  
diff -c -r -N src.orig/backend/access/gist/gistscan.c src/backend/access/gist/gistscan.c
*** src.orig/backend/access/gist/gistscan.c	2009-11-26 11:58:25.000000000 +0300
--- src/backend/access/gist/gistscan.c	2009-11-26 12:03:14.000000000 +0300
***************
*** 18,27 ****
  #include "access/gist_private.h"
  #include "access/gistscan.h"
  #include "access/relscan.h"
  #include "storage/bufmgr.h"
  #include "utils/memutils.h"
  
! static void gistfreestack(GISTSearchStack *s);
  
  Datum
  gistbeginscan(PG_FUNCTION_ARGS)
--- 18,68 ----
  #include "access/gist_private.h"
  #include "access/gistscan.h"
  #include "access/relscan.h"
+ #include "catalog/pg_type.h"
  #include "storage/bufmgr.h"
+ #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  
! static int
! StackElemComparator(const void *a, const void *b, void *arg)
! {
! 	StackElem      *sa = (StackElem*)a,
! 				   *sb = (StackElem*)b;
! 	IndexScanDesc	scan = (IndexScanDesc)arg;
! 	int i;
! 
! 	for(i=0; i<scan->numberOfKeys; i++)
! 	{
! 		if (sa->distances[i] != sb->distances[i])
! 			return (sa->distances[i] > sb->distances[i]) ? 1 : -1;
! 	}
! 
! 	return 0;
! }
! 
! static void*
! StackElemAppendator(void *current, void *new, void *arg)
! {
! 	StackElem   *scurrent = (StackElem*)current,
! 	*snew = (StackElem*)new;
! 
! 	/*
! 	 * We would like to keep heap pointer in the begining of list
! 	 * to speedup knn-search
! 	 */
! 	if (snew->head->blkno == InvalidBlockNumber)
! 	{
! 		snew->head->next = scurrent->head;
! 		scurrent->head = snew->head;
! 	}
! 	else
! 	{
! 		scurrent->tail->next = snew->head;
! 		scurrent->tail = snew->head;
! 	}
! 
! 	return current;
! }
  
  Datum
  gistbeginscan(PG_FUNCTION_ARGS)
***************
*** 39,82 ****
  Datum
  gistrescan(PG_FUNCTION_ARGS)
  {
! 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
! 	ScanKey		key = (ScanKey) PG_GETARG_POINTER(1);
! 	GISTScanOpaque so;
! 	int			i;
  
  	so = (GISTScanOpaque) scan->opaque;
  	if (so != NULL)
  	{
  		/* rescan an existing indexscan --- reset state */
! 		gistfreestack(so->stack);
! 		so->stack = NULL;
! 		/* drop pins on buffers -- no locks held */
! 		if (BufferIsValid(so->curbuf))
! 		{
! 			ReleaseBuffer(so->curbuf);
! 			so->curbuf = InvalidBuffer;
! 		}
  	}
  	else
  	{
  		/* initialize opaque data */
! 		so = (GISTScanOpaque) palloc(sizeof(GISTScanOpaqueData));
! 		so->stack = NULL;
  		so->tempCxt = createTempGistContext();
! 		so->curbuf = InvalidBuffer;
  		so->giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
  		initGISTstate(so->giststate, scan->indexRelation);
  
  		scan->opaque = so;
  	}
  
! 	/*
! 	 * Clear all the pointers.
! 	 */
! 	ItemPointerSetInvalid(&so->curpos);
! 	so->nPageData = so->curPageData = 0;
  
  	so->qual_ok = true;
  
  	/* Update scan key, if a new one is given */
  	if (key && scan->numberOfKeys > 0)
--- 80,127 ----
  Datum
  gistrescan(PG_FUNCTION_ARGS)
  {
! 	IndexScanDesc 	scan = (IndexScanDesc) PG_GETARG_POINTER(0);
! 	ScanKey			key = (ScanKey) PG_GETARG_POINTER(1);
! 	GISTScanOpaque 	so;
! 	int				i;
! 	MemoryContext	oldCxt;
  
  	so = (GISTScanOpaque) scan->opaque;
  	if (so != NULL)
  	{
  		/* rescan an existing indexscan --- reset state */
! 		MemoryContextReset(so->stackCxt);
! 		so->currentPointer = NULL;
  	}
  	else
  	{
  		/* initialize opaque data */
! 		so = (GISTScanOpaque) palloc0(sizeof(GISTScanOpaqueData));
  		so->tempCxt = createTempGistContext();
! 		so->stackCxt = AllocSetContextCreate(CurrentMemoryContext,
! 												"GiST stack context",
! 												ALLOCSET_DEFAULT_MINSIZE,
! 												ALLOCSET_DEFAULT_INITSIZE,
! 												ALLOCSET_DEFAULT_MAXSIZE);
  		so->giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
  		initGISTstate(so->giststate, scan->indexRelation);
+ 		so->operatorRetType = palloc(sizeof(Oid) * (scan->numberOfKeys + 1));
+ 		so->distances = palloc(sizeof(double) * (scan->numberOfKeys + 1));
  
  		scan->opaque = so;
  	}
  
! 	oldCxt = MemoryContextSwitchTo(so->stackCxt);
! 	so->stack = rb_create(
! 						StackElemComparator,
! 						StackElemAppendator,
! 						NULL, 
! 						scan);
! 	MemoryContextSwitchTo(oldCxt);
  
+ 	so->firstCall = true;
  	so->qual_ok = true;
+ 	so->isKNNsearch = false;
  
  	/* Update scan key, if a new one is given */
  	if (key && scan->numberOfKeys > 0)
***************
*** 96,103 ****
--- 141,159 ----
  		 */
  		for (i = 0; i < scan->numberOfKeys; i++)
  		{
+ 			/* cache retype of operator to distinguish KNN-search and plain search */
+ 			if (scan->keyData[i].sk_flags & SK_ISNULL)
+ 				so->operatorRetType[ i ] = BOOLOID; /* is NULL clause */
+ 			else
+ 				so->operatorRetType[ i ] = get_func_rettype(scan->keyData[i].sk_func.fn_oid);
+ 			so->isKNNsearch |= (so->operatorRetType[ i ] != BOOLOID);
+ 
  			scan->keyData[i].sk_func = so->giststate->consistentFn[scan->keyData[i].sk_attno - 1];
  
+ 			/* cache rettype to have ability to distinguish GiST-style consistentFn */
+ 			so->consistentRetType[scan->keyData[i].sk_attno - 1] =
+ 										get_func_rettype(scan->keyData[i].sk_func.fn_oid);
+ 
  			if (scan->keyData[i].sk_flags & SK_ISNULL)
  			{
  				if ((scan->keyData[i].sk_flags & SK_SEARCHNULL) == 0)
***************
*** 133,159 ****
  
  	if (so != NULL)
  	{
! 		gistfreestack(so->stack);
  		if (so->giststate != NULL)
  			freeGISTstate(so->giststate);
! 		/* drop pins on buffers -- we aren't holding any locks */
! 		if (BufferIsValid(so->curbuf))
! 			ReleaseBuffer(so->curbuf);
! 		MemoryContextDelete(so->tempCxt);
  		pfree(scan->opaque);
  	}
  
  	PG_RETURN_VOID();
  }
  
- static void
- gistfreestack(GISTSearchStack *s)
- {
- 	while (s != NULL)
- 	{
- 		GISTSearchStack *p = s->next;
- 
- 		pfree(s);
- 		s = p;
- 	}
- }
--- 189,203 ----
  
  	if (so != NULL)
  	{
! 		MemoryContextDelete(so->tempCxt);
! 		MemoryContextDelete(so->stackCxt);
  		if (so->giststate != NULL)
  			freeGISTstate(so->giststate);
! 		pfree(so->operatorRetType);
! 		pfree(so->distances);
  		pfree(scan->opaque);
  	}
  
  	PG_RETURN_VOID();
  }
  
diff -c -r -N src.orig/backend/commands/opclasscmds.c src/backend/commands/opclasscmds.c
*** src.orig/backend/commands/opclasscmds.c	2009-11-26 11:58:25.000000000 +0300
--- src/backend/commands/opclasscmds.c	2009-11-26 12:03:14.000000000 +0300
***************
*** 1073,1088 ****
  	opform = (Form_pg_operator) GETSTRUCT(optup);
  
  	/*
! 	 * Opfamily operators must be binary ops returning boolean.
  	 */
  	if (opform->oprkind != 'b')
  		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
  				 errmsg("index operators must be binary")));
  	if (opform->oprresult != BOOLOID)
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
! 				 errmsg("index operators must return boolean")));
  
  	/*
  	 * If lefttype/righttype isn't specified, use the operator's input types
--- 1073,1106 ----
  	opform = (Form_pg_operator) GETSTRUCT(optup);
  
  	/*
! 	 * Opfamily operators must be binary ops returning boolean or 
! 	 * btree indexable type (for pg_am->amcanorderbyop indexes).
  	 */
  	if (opform->oprkind != 'b')
  		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
  				 errmsg("index operators must be binary")));
  	if (opform->oprresult != BOOLOID)
! 	{
! 		Form_pg_am pg_am;
! 		HeapTuple  amtup;
! 
! 		amtup = SearchSysCache(AMOID, ObjectIdGetDatum(amoid), 0, 0, 0);
! 		Assert(HeapTupleIsValid(amtup));
! 		pg_am = (Form_pg_am) GETSTRUCT(amtup);
! 
! 		if (!pg_am->amcanorderbyop)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
! 					 errmsg("index operators must return boolean")));
! 
! 		ReleaseSysCache(amtup);
! 
! 		if (GetDefaultOpClass(opform->oprresult, BTREE_AM_OID) == InvalidOid)	
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
! 					 errmsg("index operators must return boolean or type with default btree operator class")));
! 	}
  
  	/*
  	 * If lefttype/righttype isn't specified, use the operator's input types
diff -c -r -N src.orig/backend/optimizer/path/indxpath.c src/backend/optimizer/path/indxpath.c
*** src.orig/backend/optimizer/path/indxpath.c	2009-11-26 11:58:25.000000000 +0300
--- src/backend/optimizer/path/indxpath.c	2009-11-26 13:14:49.000000000 +0300
***************
*** 103,108 ****
--- 103,110 ----
  						   IndexOptInfo *index);
  static bool match_special_index_operator(Expr *clause, Oid opfamily,
  							 bool indexkey_on_left);
+ static void match_pathkey_to_index(IndexOptInfo *index, List *pathkeys, 
+ 	                               /* out: */ List **matched_pathkeys, List **addrestrictclauses);
  static Expr *expand_boolean_index_clause(Node *clause, int indexcol,
  							IndexOptInfo *index);
  static List *expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily);
***************
*** 174,185 ****
  	/*
  	 * Find all the index paths that are directly usable for this relation
  	 * (ie, are valid without considering OR or JOIN clauses).
- 	 */
- 	indexpaths = find_usable_indexes(root, rel,
- 									 rel->baserestrictinfo, NIL,
- 									 true, NULL, SAOP_FORBID, ST_ANYSCAN);
- 
- 	/*
  	 * Submit all the ones that can form plain IndexScan plans to add_path. (A
  	 * plain IndexPath always represents a plain IndexScan plan; however some
  	 * of the indexes might support only bitmap scans, and those we mustn't
--- 176,181 ----
***************
*** 189,201 ****
  	 * selectivity; we should discard anything that was generated solely for
  	 * ordering purposes.
  	 */
! 	bitindexpaths = NIL;
  	foreach(l, indexpaths)
  	{
  		IndexPath  *ipath = (IndexPath *) lfirst(l);
  
! 		if (ipath->indexinfo->amhasgettuple)
! 			add_path(rel, (Path *) ipath);
  
  		if (ipath->indexinfo->amhasgetbitmap &&
  			ipath->indexselectivity < 1.0 &&
--- 185,206 ----
  	 * selectivity; we should discard anything that was generated solely for
  	 * ordering purposes.
  	 */
! 	indexpaths = find_usable_indexes(root, rel,
! 									 rel->baserestrictinfo, NIL,
! 									 true, NULL, SAOP_FORBID, ST_INDEXSCAN);
  	foreach(l, indexpaths)
  	{
  		IndexPath  *ipath = (IndexPath *) lfirst(l);
+ 		add_path(rel, (Path *) ipath);
+ 	}
  
! 	indexpaths = find_usable_indexes(root, rel,
! 									 rel->baserestrictinfo, NIL,
! 									 true, NULL, SAOP_FORBID, ST_BITMAPSCAN);
! 	bitindexpaths = NIL;
! 	foreach(l, indexpaths)
! 	{
! 		IndexPath  *ipath = (IndexPath *) lfirst(l);
  
  		if (ipath->indexinfo->amhasgetbitmap &&
  			ipath->indexselectivity < 1.0 &&
***************
*** 394,401 ****
--- 399,419 ----
  			useful_pathkeys = truncate_useless_pathkeys(root, rel,
  														index_pathkeys);
  		}
+ 		else if (index->amcanorderbyop && possibly_useful_pathkeys &&
+ 				 istoplevel && outer_rel == NULL && scantype == ST_INDEXSCAN) 
+ 		{
+ 			/*
+ 			 * restrictclauses could be extended to support ordering by operation,
+ 			 * but it's not desirable for bitmap scan
+ 			 */
+ 			useful_pathkeys = NIL;
+ 			match_pathkey_to_index(index, root->query_pathkeys,
+ 									&useful_pathkeys, &restrictclauses);
+ 		}
  		else
+ 		{
  			useful_pathkeys = NIL;
+ 		}
  
  		/*
  		 * 3. Generate an indexscan path if there are relevant restriction
***************
*** 441,447 ****
  	return result;
  }
  
- 
  /*
   * find_saop_paths
   *		Find all the potential indexpaths that make use of ScalarArrayOpExpr
--- 459,464 ----
***************
*** 1605,1610 ****
--- 1622,1718 ----
  }
  
  /*
+  * Choose first relevant pathkeys for indexes which supports
+  * order by operation
+  */
+ static void
+ match_pathkey_to_index(IndexOptInfo *index, List *pathkeys, 
+ 	/* out: */ List **matched_pathkeys, List **restrictclauses)
+ {
+ 	ListCell	*l1, *l2, *l3;
+ 
+ 	if (!index->amcanorderbyop)
+ 		return;
+ 
+ 	foreach(l1, pathkeys)
+ 	{
+ 		PathKey *pathkey = lfirst(l1);
+ 		bool	 found = false;
+ 
+ 		/*
+ 		 * Currently, only descending and nulls last ordering is supported
+ 		 */
+ 		if (!(pathkey->pk_strategy == BTLessStrategyNumber && pathkey->pk_nulls_first == false))
+ 			return;
+ 
+ 		foreach(l2, pathkey->pk_eclass->ec_members)
+ 		{
+ 			EquivalenceMember	*member = lfirst(l2);
+ 			int					 indexcol = 0;
+ 			Oid			   		*families = index->opfamily;
+ 			RestrictInfo		*rinfo = make_simple_restrictinfo(member->em_expr); 
+ 
+ 			do
+ 			{
+ 				Oid			curFamily = families[0];
+ 
+ 				if (list_length(*restrictclauses) < indexcol && !index->amoptionalkey)
+ 					break;
+ 
+ 				if (match_clause_to_indexcol(index,
+ 											 indexcol,
+ 											 curFamily,
+ 											 rinfo,
+ 											 NULL,
+ 											 SAOP_FORBID))
+ 				{
+ 					*matched_pathkeys = lappend(*matched_pathkeys, pathkey);
+ 
+ 					if (list_length(*restrictclauses) <= indexcol)
+ 					{
+ 						/*
+ 						 * Insert new rinfo in last group
+ 						 */
+ 						while(list_length(*restrictclauses) < indexcol)
+ 							*restrictclauses = lappend(*restrictclauses, NIL);
+ 						*restrictclauses = lappend(*restrictclauses, lappend(NIL, rinfo));
+ 					}
+ 					else
+ 					{
+ 						int i = 0;
+ 
+ 						/*
+ 						 * Insert new rinfo in group in the middle
+ 						 */
+ 						foreach(l3, *restrictclauses)
+ 						{
+ 							if (i==indexcol)
+ 							{
+ 								lfirst(l3) = lappend(lfirst(l3), rinfo);
+ 								break;
+ 							}
+ 							i++;
+ 						}
+ 					}
+ 
+ 					found = true;
+ 					break;
+ 				}
+ 
+ 				indexcol++;
+ 				families++;
+ 			} while (!DoneMatchingIndexKeys(families));
+ 
+ 			if (found) /* not need to check other members */
+ 				break;
+ 		}
+ 
+ 		if (!found) /* go away if we don't have index match */
+ 			return;
+ 	}
+ }
+ 
+ /*
   * eclass_matches_any_index
   *	  Workhorse for indexable_outerrelids: see if an EquivalenceClass member
   *	  can be matched to any index column of the given rel.
***************
*** 1677,1683 ****
  	List	   *clause_list;
  	List	   *indexpaths;
  	List	   *bitindexpaths;
- 	List	   *allindexpaths;
  	ListCell   *l;
  	InnerIndexscanInfo *info;
  	MemoryContext oldcontext;
--- 1785,1790 ----
***************
*** 1773,1799 ****
  	 * Find all the index paths that are usable for this join, except for
  	 * stuff involving OR and ScalarArrayOpExpr clauses.
  	 */
! 	allindexpaths = find_usable_indexes(root, rel,
! 										clause_list, NIL,
! 										false, outer_rel,
! 										SAOP_FORBID,
! 										ST_ANYSCAN);
! 
! 	/*
! 	 * Include the ones that are usable as plain indexscans in indexpaths, and
! 	 * include the ones that are usable as bitmap scans in bitindexpaths.
! 	 */
! 	indexpaths = bitindexpaths = NIL;
! 	foreach(l, allindexpaths)
! 	{
! 		IndexPath  *ipath = (IndexPath *) lfirst(l);
! 
! 		if (ipath->indexinfo->amhasgettuple)
! 			indexpaths = lappend(indexpaths, ipath);
! 
! 		if (ipath->indexinfo->amhasgetbitmap)
! 			bitindexpaths = lappend(bitindexpaths, ipath);
! 	}
  
  	/*
  	 * Generate BitmapOrPaths for any suitable OR-clauses present in the
--- 1880,1895 ----
  	 * Find all the index paths that are usable for this join, except for
  	 * stuff involving OR and ScalarArrayOpExpr clauses.
  	 */
! 	indexpaths = find_usable_indexes(root, rel,
! 									 clause_list, NIL,
! 									 false, outer_rel,
! 									 SAOP_FORBID,
! 									 ST_INDEXSCAN);
! 	bitindexpaths = find_usable_indexes(root, rel,
! 									 	clause_list, NIL,
! 									 	false, outer_rel,
! 									 	SAOP_FORBID,
! 									 	ST_BITMAPSCAN);
  
  	/*
  	 * Generate BitmapOrPaths for any suitable OR-clauses present in the
diff -c -r -N src.orig/backend/optimizer/util/plancat.c src/backend/optimizer/util/plancat.c
*** src.orig/backend/optimizer/util/plancat.c	2009-11-26 11:58:26.000000000 +0300
--- src/backend/optimizer/util/plancat.c	2009-11-26 12:03:14.000000000 +0300
***************
*** 212,217 ****
--- 212,218 ----
  			info->amsearchnulls = indexRelation->rd_am->amsearchnulls;
  			info->amhasgettuple = OidIsValid(indexRelation->rd_am->amgettuple);
  			info->amhasgetbitmap = OidIsValid(indexRelation->rd_am->amgetbitmap);
+ 			info->amcanorderbyop = indexRelation->rd_am->amcanorderbyop;
  
  			/*
  			 * Fetch the ordering operators associated with the index, if any.
diff -c -r -N src.orig/backend/optimizer/util/predtest.c src/backend/optimizer/util/predtest.c
*** src.orig/backend/optimizer/util/predtest.c	2009-11-26 11:58:26.000000000 +0300
--- src/backend/optimizer/util/predtest.c	2009-11-26 12:03:14.000000000 +0300
***************
*** 1047,1053 ****
  		{
  			if (is_opclause(clause) &&
  				list_member_strip(((OpExpr *) clause)->args, nonnullarg) &&
! 				op_strict(((OpExpr *) clause)->opno))
  				return true;
  			if (is_funcclause(clause) &&
  				list_member_strip(((FuncExpr *) clause)->args, nonnullarg) &&
--- 1047,1054 ----
  		{
  			if (is_opclause(clause) &&
  				list_member_strip(((OpExpr *) clause)->args, nonnullarg) &&
! 				op_strict(((OpExpr *) clause)->opno) &&
! 				exprType(clause) == BOOLOID /* index with canorderbyop can add non-bool op */)
  				return true;
  			if (is_funcclause(clause) &&
  				list_member_strip(((FuncExpr *) clause)->args, nonnullarg) &&
***************
*** 1108,1114 ****
  		/* Any strict op/func on foo refutes foo IS NULL */
  		if (is_opclause(clause) &&
  			list_member_strip(((OpExpr *) clause)->args, isnullarg) &&
! 			op_strict(((OpExpr *) clause)->opno))
  			return true;
  		if (is_funcclause(clause) &&
  			list_member_strip(((FuncExpr *) clause)->args, isnullarg) &&
--- 1109,1116 ----
  		/* Any strict op/func on foo refutes foo IS NULL */
  		if (is_opclause(clause) &&
  			list_member_strip(((OpExpr *) clause)->args, isnullarg) &&
! 			op_strict(((OpExpr *) clause)->opno) &&
! 			exprType(clause) == BOOLOID /* index with canorderbyop can add non-bool op */ )
  			return true;
  		if (is_funcclause(clause) &&
  			list_member_strip(((FuncExpr *) clause)->args, isnullarg) &&
diff -c -r -N src.orig/include/access/gist.h src/include/access/gist.h
*** src.orig/include/access/gist.h	2009-11-26 11:58:33.000000000 +0300
--- src/include/access/gist.h	2009-11-26 12:03:14.000000000 +0300
***************
*** 54,59 ****
--- 54,64 ----
  #define RTOldContainedByStrategyNumber	14		/* for old spelling of <@ */
  
  /*
+  * Strategy number for knn-search
+  */
+ #define KNNSearchStrategyNumber 		15
+ 
+ /*
   * Page opaque data in a GiST index page.
   */
  #define F_LEAF				(1 << 0)
diff -c -r -N src.orig/include/access/gist_private.h src/include/access/gist_private.h
*** src.orig/include/access/gist_private.h	2009-11-26 11:58:33.000000000 +0300
--- src/include/access/gist_private.h	2009-11-26 12:03:14.000000000 +0300
***************
*** 17,49 ****
  #include "access/gist.h"
  #include "access/itup.h"
  #include "storage/bufmgr.h"
  
  #define GIST_UNLOCK BUFFER_LOCK_UNLOCK
  #define GIST_SHARE	BUFFER_LOCK_SHARE
  #define GIST_EXCLUSIVE	BUFFER_LOCK_EXCLUSIVE
  
- 
  /*
!  * XXX old comment!!!
!  * When we descend a tree, we keep a stack of parent pointers. This
!  * allows us to follow a chain of internal node points until we reach
!  * a leaf node, and then back up the stack to re-examine the internal
!  * nodes.
!  *
!  * 'parent' is the previous stack entry -- i.e. the node we arrived
!  * from. 'block' is the node's block number. 'offset' is the offset in
!  * the node's page that we stopped at (i.e. we followed the child
!  * pointer located at the specified offset).
   */
! typedef struct GISTSearchStack
! {
! 	struct GISTSearchStack *next;
! 	BlockNumber block;
! 	/* to identify page changed */
! 	GistNSN		lsn;
! 	/* to recognize split occured */
! 	GistNSN		parentlsn;
! } GISTSearchStack;
  
  typedef struct GISTSTATE
  {
--- 17,51 ----
  #include "access/gist.h"
  #include "access/itup.h"
  #include "storage/bufmgr.h"
+ #include "utils/rbtree.h"
  
  #define GIST_UNLOCK BUFFER_LOCK_UNLOCK
  #define GIST_SHARE	BUFFER_LOCK_SHARE
  #define GIST_EXCLUSIVE	BUFFER_LOCK_EXCLUSIVE
  
  /*
!  * Elements of stack of search tree traversal
   */
! typedef struct DataPointer {
! 	BlockNumber     blkno; /* if blkno is InvalidBlockNumber then data.heap should be used */
! 	struct DataPointer  *next;
! 	union {
! 		/* to recognize split occured */
! 		GistNSN         parentlsn;
! 		struct {
! 			ItemPointerData     ptr;
! 			bool                recheck;
! 		} heap;
! 	} data;
! } DataPointer;
! 
! typedef struct StackElem {
! 	DataPointer *head;
! 	DataPointer *tail;
! 	double       distances[0]; /*distances per key, variable size */ 
! } StackElem;
! 
! #define SEHDRSZ	offsetof(StackElem, distances)
  
  typedef struct GISTSTATE
  {
***************
*** 61,68 ****
  typedef struct ItemResult
  {
  	ItemPointerData heapPtr;
! 	OffsetNumber pageOffset;	/* offset in index page */
! 	bool		recheck;
  } ItemResult;
  
  /*
--- 63,69 ----
  typedef struct ItemResult
  {
  	ItemPointerData heapPtr;
! 	bool			recheck;
  } ItemResult;
  
  /*
***************
*** 71,87 ****
   */
  typedef struct GISTScanOpaqueData
  {
! 	GISTSearchStack *stack;
! 	GISTSearchStack *markstk;
! 	bool		qual_ok;		/* false if qual can never be satisfied */
! 	GISTSTATE  *giststate;
! 	MemoryContext tempCxt;
! 	Buffer		curbuf;
! 	ItemPointerData curpos;
! 
! 	ItemResult	pageData[BLCKSZ / sizeof(IndexTupleData)];
! 	OffsetNumber nPageData;
! 	OffsetNumber curPageData;
  } GISTScanOpaqueData;
  
  typedef GISTScanOpaqueData *GISTScanOpaque;
--- 72,94 ----
   */
  typedef struct GISTScanOpaqueData
  {
!     RBTree          *stack;
! 	GISTSTATE       *giststate;
! 	MemoryContext   tempCxt;
! 	MemoryContext   stackCxt;
! 
! 	Oid             consistentRetType[INDEX_MAX_KEYS];
! 	Oid            *operatorRetType;
! 	double		   *distances;
! 	bool            qual_ok;
! 	bool            firstCall;
! 	bool			isKNNsearch;
! 
! 	StackElem      *currentPointer;
! 
! 	ItemResult  	pageData[BLCKSZ / sizeof(IndexTupleData)];
! 	OffsetNumber 	nPageData;
! 	OffsetNumber 	curPageData;
  } GISTScanOpaqueData;
  
  typedef GISTScanOpaqueData *GISTScanOpaque;
diff -c -r -N src.orig/include/catalog/pg_am.h src/include/catalog/pg_am.h
*** src.orig/include/catalog/pg_am.h	2009-11-26 11:58:32.000000000 +0300
--- src/include/catalog/pg_am.h	2009-11-26 12:03:14.000000000 +0300
***************
*** 49,54 ****
--- 49,55 ----
  	bool		amsearchnulls;	/* can AM search for NULL index entries? */
  	bool		amstorage;		/* can storage type differ from column type? */
  	bool		amclusterable;	/* does AM support cluster command? */
+ 	bool		amcanorderbyop;	/* does AM support ordering operations? */
  	Oid			amkeytype;		/* type of data in index, or InvalidOid */
  	regproc		aminsert;		/* "insert this tuple" function */
  	regproc		ambeginscan;	/* "start new scan" function */
***************
*** 109,124 ****
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree	5 1 t t t t t t t f t 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoptions ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash	1 1 f t f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist	0 7 f f f t t t t t t 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin	0 5 f f f t t f f t f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
  
--- 110,125 ----
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree	5 1 t t t t t t t f t f 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoptions ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash	1 1 f t f f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist	0 7 f f f t t t t t t t 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin	0 5 f f f t t f f t f f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
  
diff -c -r -N src.orig/include/catalog/pg_amop.h src/include/catalog/pg_amop.h
*** src.orig/include/catalog/pg_amop.h	2009-11-26 11:58:32.000000000 +0300
--- src/include/catalog/pg_amop.h	2009-11-26 12:03:14.000000000 +0300
***************
*** 590,603 ****
  DATA(insert (	1689   600 600 5  508 783 ));
  DATA(insert (	1689   600 600 10 509 783 ));
  DATA(insert (	1689   600 600 6  510 783 ));
! DATA(insert (	1689   603 600 27  433 783 ));
! DATA(insert (	1689   600 603 28  511 783 ));
  DATA(insert (	1689   604 600 47 757 783 ));
  DATA(insert (	1689   600 604 48 756 783 ));
  DATA(insert (	1689   718 600 67 759 783 ));
  DATA(insert (	1689   600 718 68 758 783 ));
  
- 
  /*
   *	gist poly_ops (supports polygons)
   */
--- 590,603 ----
  DATA(insert (	1689   600 600 5  508 783 ));
  DATA(insert (	1689   600 600 10 509 783 ));
  DATA(insert (	1689   600 600 6  510 783 ));
! DATA(insert (	1689   600 600 15 517 783 )); 
! DATA(insert (	1689   603 600 27 433 783 ));
! DATA(insert (	1689   600 603 28 511 783 ));
  DATA(insert (	1689   604 600 47 757 783 ));
  DATA(insert (	1689   600 604 48 756 783 ));
  DATA(insert (	1689   718 600 67 759 783 ));
  DATA(insert (	1689   600 718 68 758 783 ));
  
  /*
   *	gist poly_ops (supports polygons)
   */
diff -c -r -N src.orig/include/catalog/pg_proc.h src/include/catalog/pg_proc.h
*** src.orig/include/catalog/pg_proc.h	2009-11-26 11:58:32.000000000 +0300
--- src/include/catalog/pg_proc.h	2009-11-26 12:03:14.000000000 +0300
***************
*** 4204,4210 ****
  DESCR("GiST support");
  DATA(insert OID = 2187 (  gist_point_compress	PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_point_compress _null_ _null_ _null_ ));
  DESCR("GiST support");
! DATA(insert OID = 2179 (  gist_point_consistent	PGNSP PGUID 12 1 0 0 f f f t f i 5 0 16 "2281 603 23 26 2281" _null_ _null_ _null_ _null_	gist_point_consistent _null_ _null_ _null_ ));
  DESCR("GiST support");
  
  /* GIN */
--- 4204,4210 ----
  DESCR("GiST support");
  DATA(insert OID = 2187 (  gist_point_compress	PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_point_compress _null_ _null_ _null_ ));
  DESCR("GiST support");
! DATA(insert OID = 2179 (  gist_point_consistent	PGNSP PGUID 12 1 0 0 f f f t f i 5 0 701 "2281 603 23 26 2281" _null_ _null_ _null_ _null_	gist_point_consistent _null_ _null_ _null_ ));
  DESCR("GiST support");
  
  /* GIN */
diff -c -r -N src.orig/include/nodes/relation.h src/include/nodes/relation.h
*** src.orig/include/nodes/relation.h	2009-11-26 11:58:32.000000000 +0300
--- src/include/nodes/relation.h	2009-11-26 12:03:14.000000000 +0300
***************
*** 452,457 ****
--- 452,458 ----
  	bool		amsearchnulls;	/* can AM search for NULL index entries? */
  	bool		amhasgettuple;	/* does AM have amgettuple interface? */
  	bool		amhasgetbitmap; /* does AM have amgetbitmap interface? */
+ 	bool		amcanorderbyop; /* does AM support ordering operations? */
  } IndexOptInfo;
  
  
diff -c -r -N src.orig/test/regress/expected/create_index.out src/test/regress/expected/create_index.out
*** src.orig/test/regress/expected/create_index.out	2009-11-26 11:58:35.000000000 +0300
--- src/test/regress/expected/create_index.out	2009-11-26 12:03:14.000000000 +0300
***************
*** 51,56 ****
--- 51,57 ----
  CREATE INDEX grect2ind ON fast_emp4000 USING gist (home_base);
  CREATE INDEX gpolygonind ON polygon_tbl USING gist (f1);
  CREATE INDEX gcircleind ON circle_tbl USING gist (f1);
+ INSERT INTO POINT_TBL(f1) VALUES (NULL);
  CREATE INDEX gpointind ON point_tbl USING gist (f1);
  CREATE TEMP TABLE gpolygon_tbl AS
      SELECT polygon(home_base) AS f1 FROM slow_emp4000;
***************
*** 167,172 ****
--- 168,211 ----
       1
  (1 row)
  
+ SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
+      f1     
+ ------------
+  (0,0)
+  (-3,4)
+  (-10,0)
+  (10,10)
+  (-5,-12)
+  (5.1,34.5)
+  
+ (7 rows)
+ 
+ SELECT * FROM point_tbl WHERE f1 IS NULL;
+  f1 
+ ----
+  
+ (1 row)
+ 
+ SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
+      f1     
+ ------------
+  (0,0)
+  (-3,4)
+  (-10,0)
+  (10,10)
+  (-5,-12)
+  (5.1,34.5)
+ (6 rows)
+ 
+ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
+    f1    
+ ---------
+  (0,0)
+  (-3,4)
+  (-10,0)
+  (10,10)
+ (4 rows)
+ 
  SET enable_seqscan = OFF;
  SET enable_indexscan = ON;
  SET enable_bitmapscan = ON;
***************
*** 435,440 ****
--- 474,574 ----
       1
  (1 row)
  
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
+                QUERY PLAN                
+ -----------------------------------------
+  Index Scan using gpointind on point_tbl
+    Index Cond: (f1 <-> '(0,1)'::point)
+ (2 rows)
+ 
+ SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
+      f1     
+ ------------
+  (0,0)
+  (-3,4)
+  (-10,0)
+  (10,10)
+  (-5,-12)
+  (5.1,34.5)
+  
+ (7 rows)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM point_tbl WHERE f1 IS NULL;
+                QUERY PLAN                
+ -----------------------------------------
+  Index Scan using gpointind on point_tbl
+    Index Cond: (f1 IS NULL)
+ (2 rows)
+ 
+ SELECT * FROM point_tbl WHERE f1 IS NULL;
+  f1 
+ ----
+  
+ (1 row)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
+                QUERY PLAN                
+ -----------------------------------------
+  Index Scan using gpointind on point_tbl
+    Index Cond: (f1 <-> '(0,1)'::point)
+    Filter: (f1 IS NOT NULL)
+ (3 rows)
+ 
+ SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
+      f1     
+ ------------
+  (0,0)
+  (-3,4)
+  (-10,0)
+  (10,10)
+  (-5,-12)
+  (5.1,34.5)
+ (6 rows)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
+                                   QUERY PLAN                                  
+ ------------------------------------------------------------------------------
+  Index Scan using gpointind on point_tbl
+    Index Cond: ((f1 <@ '(10,10),(-10,-10)'::box) AND (f1 <-> '(0,1)'::point))
+ (2 rows)
+ 
+ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
+    f1    
+ ---------
+  (0,0)
+  (-3,4)
+  (-10,0)
+  (10,10)
+ (4 rows)
+ 
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = OFF;
+ SET enable_bitmapscan = ON;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
+                          QUERY PLAN                         
+ ------------------------------------------------------------
+  Sort
+    Sort Key: ((f1 <-> '(0,1)'::point))
+    ->  Bitmap Heap Scan on point_tbl
+          Recheck Cond: (f1 <@ '(10,10),(-10,-10)'::box)
+          ->  Bitmap Index Scan on gpointind
+                Index Cond: (f1 <@ '(10,10),(-10,-10)'::box)
+ (6 rows)
+ 
+ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
+    f1    
+ ---------
+  (0,0)
+  (-3,4)
+  (-10,0)
+  (10,10)
+ (4 rows)
+ 
  RESET enable_seqscan;
  RESET enable_indexscan;
  RESET enable_bitmapscan;
diff -c -r -N src.orig/test/regress/expected/opr_sanity.out src/test/regress/expected/opr_sanity.out
*** src.orig/test/regress/expected/opr_sanity.out	2009-11-26 11:58:35.000000000 +0300
--- src/test/regress/expected/opr_sanity.out	2009-11-26 12:03:14.000000000 +0300
***************
*** 816,824 ****
  -- Check that amopopr points at a reasonable-looking operator, ie a binary
  -- operator yielding boolean.
  SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname
! FROM pg_amop AS p1, pg_operator AS p2
  WHERE p1.amopopr = p2.oid AND
!     (p2.oprkind != 'b' OR p2.oprresult != 'bool'::regtype);
   amopfamily | amopopr | oid | oprname 
  ------------+---------+-----+---------
  (0 rows)
--- 816,836 ----
  -- Check that amopopr points at a reasonable-looking operator, ie a binary
  -- operator yielding boolean.
  SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname
! FROM pg_amop AS p1, pg_operator AS p2, pg_am am
  WHERE p1.amopopr = p2.oid AND
! 	  am.oid = p1.amopmethod AND
! 	  ( 
!     	(p2.oprkind != 'b' OR NOT (
! 			p2.oprresult = 'bool'::regtype OR (am.amname='gist' AND 
! 			EXISTS( 
! 				SELECT * FROM pg_opclass oc, pg_am am 
! 				WHERE oc.opcintype = p2.oprresult AND 
! 					oc.opcdefault='t' AND
! 					am.amname = 'btree' AND
! 					oc.opcmethod = am.oid 
! 				)
! 		)))
! 	  );
   amopfamily | amopopr | oid | oprname 
  ------------+---------+-----+---------
  (0 rows)
***************
*** 859,864 ****
--- 871,877 ----
          783 |           12 | |&>
          783 |           13 | ~
          783 |           14 | @
+         783 |           15 | <->
          783 |           27 | @>
          783 |           28 | <@
          783 |           47 | @>
***************
*** 871,877 ****
         2742 |            2 | @@@
         2742 |            3 | <@
         2742 |            4 | =
! (39 rows)
  
  -- Check that all operators linked to by opclass entries have selectivity
  -- estimators.  This is not absolutely required, but it seems a reasonable
--- 884,890 ----
         2742 |            2 | @@@
         2742 |            3 | <@
         2742 |            4 | =
! (40 rows)
  
  -- Check that all operators linked to by opclass entries have selectivity
  -- estimators.  This is not absolutely required, but it seems a reasonable
***************
*** 882,888 ****
      (p2.oprrest = 0 OR p2.oprjoin = 0);
   amopfamily | amopopr | oid | oprname 
  ------------+---------+-----+---------
! (0 rows)
  
  -- Check that each opclass in an opfamily has associated operators, that is
  -- ones whose oprleft matches opcintype (possibly by coercion).
--- 895,902 ----
      (p2.oprrest = 0 OR p2.oprjoin = 0);
   amopfamily | amopopr | oid | oprname 
  ------------+---------+-----+---------
!        1689 |     517 | 517 | <->
! (1 row)
  
  -- Check that each opclass in an opfamily has associated operators, that is
  -- ones whose oprleft matches opcintype (possibly by coercion).
diff -c -r -N src.orig/test/regress/sql/create_index.sql src/test/regress/sql/create_index.sql
*** src.orig/test/regress/sql/create_index.sql	2009-11-26 11:58:35.000000000 +0300
--- src/test/regress/sql/create_index.sql	2009-11-26 12:03:14.000000000 +0300
***************
*** 76,81 ****
--- 76,82 ----
  
  CREATE INDEX gcircleind ON circle_tbl USING gist (f1);
  
+ INSERT INTO POINT_TBL(f1) VALUES (NULL);
  CREATE INDEX gpointind ON point_tbl USING gist (f1);
  
  CREATE TEMP TABLE gpolygon_tbl AS
***************
*** 130,135 ****
--- 131,144 ----
  
  SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
  
+ SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
+ 
+ SELECT * FROM point_tbl WHERE f1 IS NULL;
+ 
+ SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
+ 
+ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
+ 
  SET enable_seqscan = OFF;
  SET enable_indexscan = ON;
  SET enable_bitmapscan = ON;
***************
*** 206,211 ****
--- 215,244 ----
  SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
  SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
  
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
+ SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM point_tbl WHERE f1 IS NULL;
+ SELECT * FROM point_tbl WHERE f1 IS NULL;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
+ SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
+ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
+ 
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = OFF;
+ SET enable_bitmapscan = ON;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
+ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
+ 
  RESET enable_seqscan;
  RESET enable_indexscan;
  RESET enable_bitmapscan;
diff -c -r -N src.orig/test/regress/sql/opr_sanity.sql src/test/regress/sql/opr_sanity.sql
*** src.orig/test/regress/sql/opr_sanity.sql	2009-11-26 11:58:35.000000000 +0300
--- src/test/regress/sql/opr_sanity.sql	2009-11-26 12:03:14.000000000 +0300
***************
*** 667,675 ****
  -- operator yielding boolean.
  
  SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname
! FROM pg_amop AS p1, pg_operator AS p2
  WHERE p1.amopopr = p2.oid AND
!     (p2.oprkind != 'b' OR p2.oprresult != 'bool'::regtype);
  
  -- Make a list of all the distinct operator names being used in particular
  -- strategy slots.  This is a bit hokey, since the list might need to change
--- 667,687 ----
  -- operator yielding boolean.
  
  SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname
! FROM pg_amop AS p1, pg_operator AS p2, pg_am am
  WHERE p1.amopopr = p2.oid AND
! 	  am.oid = p1.amopmethod AND
! 	  ( 
!     	(p2.oprkind != 'b' OR NOT (
! 			p2.oprresult = 'bool'::regtype OR (am.amname='gist' AND 
! 			EXISTS( 
! 				SELECT * FROM pg_opclass oc, pg_am am 
! 				WHERE oc.opcintype = p2.oprresult AND 
! 					oc.opcdefault='t' AND
! 					am.amname = 'btree' AND
! 					oc.opcmethod = am.oid 
! 				)
! 		)))
! 	  );
  
  -- Make a list of all the distinct operator names being used in particular
  -- strategy slots.  This is a bit hokey, since the list might need to change
diff -c -r -N contrib.orig/btree_gist/btree_cash.c contrib/btree_gist/btree_cash.c
*** contrib.orig/btree_gist/btree_cash.c	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_cash.c	2009-11-26 16:55:13.000000000 +0300
***************
*** 66,71 ****
--- 66,76 ----
  
  }
  
+ static float8 
+ gbt_cash_dist(const void *a, const void *b)
+ {
+ 	return (float8)Abs(*((Cash *) a) - *((Cash *) b));
+ }
  
  static const gbtree_ninfo tinfo =
  {
***************
*** 76,84 ****
  	gbt_casheq,
  	gbt_cashle,
  	gbt_cashlt,
! 	gbt_cashkey_cmp
  };
  
  
  /**************************************************
   * Cash ops
--- 81,100 ----
  	gbt_casheq,
  	gbt_cashle,
  	gbt_cashlt,
! 	gbt_cashkey_cmp,
! 	gbt_cash_dist
  };
  
+ PG_FUNCTION_INFO_V1(cash_dist);
+ Datum		cash_dist(PG_FUNCTION_ARGS);
+ Datum
+ cash_dist(PG_FUNCTION_ARGS)
+ {
+ 	Cash		a = PG_GETARG_CASH(0);
+ 	Cash		b = PG_GETARG_CASH(1);
+ 
+ 	PG_RETURN_CASH( Abs(a - b) );
+ }
  
  /**************************************************
   * Cash ops
***************
*** 113,119 ****
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_BOOL(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
--- 129,135 ----
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_FLOAT8(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
diff -c -r -N contrib.orig/btree_gist/btree_date.c contrib/btree_gist/btree_date.c
*** contrib.orig/btree_gist/btree_date.c	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_date.c	2009-11-26 16:13:01.000000000 +0300
***************
*** 80,85 ****
--- 80,93 ----
  	return 0;
  }
  
+ static float8
+ gdb_date_dist(const void *a, const void *b)
+ {
+ 	int4 res = DatumGetInt32(
+ 			DirectFunctionCall2(date_mi, DateADTGetDatum(*((DateADT *) a)), DateADTGetDatum(*((DateADT *) b)))
+ 		);
+ 	return (float8)Abs(res);
+ }
  
  static const gbtree_ninfo tinfo =
  {
***************
*** 90,98 ****
  	gbt_dateeq,
  	gbt_datele,
  	gbt_datelt,
! 	gbt_datekey_cmp
  };
  
  
  /**************************************************
   * date ops
--- 98,119 ----
  	gbt_dateeq,
  	gbt_datele,
  	gbt_datelt,
! 	gbt_datekey_cmp,
! 	gdb_date_dist
  };
  
+ PG_FUNCTION_INFO_V1(date_dist);
+ Datum       date_dist(PG_FUNCTION_ARGS);
+ Datum
+ date_dist(PG_FUNCTION_ARGS)
+ {
+ 	int4 res = DatumGetInt32(
+ 			DirectFunctionCall2(date_mi, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1))
+ 		);
+ 
+ 	PG_RETURN_INT32( Abs(res)  );
+ }
+ 
  
  /**************************************************
   * date ops
***************
*** 129,135 ****
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_BOOL(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
--- 150,156 ----
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_FLOAT8(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
diff -c -r -N contrib.orig/btree_gist/btree_float4.c contrib/btree_gist/btree_float4.c
*** contrib.orig/btree_gist/btree_float4.c	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_float4.c	2009-11-26 16:13:57.000000000 +0300
***************
*** 65,70 ****
--- 65,75 ----
  
  }
  
+ static float8
+ gbt_float4_dist(const void *a, const void *b)
+ {
+ 	return (float8)Abs(*((float4 *) a) - *((float4 *) b));
+ }
  
  static const gbtree_ninfo tinfo =
  {
***************
*** 75,83 ****
  	gbt_float4eq,
  	gbt_float4le,
  	gbt_float4lt,
! 	gbt_float4key_cmp
  };
  
  
  /**************************************************
   * float4 ops
--- 80,99 ----
  	gbt_float4eq,
  	gbt_float4le,
  	gbt_float4lt,
! 	gbt_float4key_cmp,
! 	gbt_float4_dist
  };
  
+ PG_FUNCTION_INFO_V1(float4_dist);
+ Datum       float4_dist(PG_FUNCTION_ARGS);
+ Datum
+ float4_dist(PG_FUNCTION_ARGS)
+ {
+     float4        a = PG_GETARG_FLOAT4(0);
+ 	float4        b = PG_GETARG_FLOAT4(1);
+ 
+ 	PG_RETURN_FLOAT4( Abs(a - b) );
+ }
  
  /**************************************************
   * float4 ops
***************
*** 112,118 ****
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_BOOL(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
--- 128,134 ----
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_FLOAT8(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
diff -c -r -N contrib.orig/btree_gist/btree_float8.c contrib/btree_gist/btree_float8.c
*** contrib.orig/btree_gist/btree_float8.c	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_float8.c	2009-11-26 16:14:50.000000000 +0300
***************
*** 66,71 ****
--- 66,76 ----
  
  }
  
+ static float8
+ gbt_float8_dist(const void *a, const void *b)
+ {
+ 	return Abs(*((float8 *) a) - *((float8 *) b));
+ }
  
  static const gbtree_ninfo tinfo =
  {
***************
*** 76,84 ****
  	gbt_float8eq,
  	gbt_float8le,
  	gbt_float8lt,
! 	gbt_float8key_cmp
  };
  
  
  /**************************************************
   * float8 ops
--- 81,100 ----
  	gbt_float8eq,
  	gbt_float8le,
  	gbt_float8lt,
! 	gbt_float8key_cmp,
! 	gbt_float8_dist
  };
  
+ PG_FUNCTION_INFO_V1(float8_dist);
+ Datum       float8_dist(PG_FUNCTION_ARGS);
+ Datum
+ float8_dist(PG_FUNCTION_ARGS)
+ {
+     float8        a = PG_GETARG_FLOAT8(0);
+ 	float8        b = PG_GETARG_FLOAT8(1);
+ 
+ 	PG_RETURN_FLOAT8( Abs(a - b) );
+ }
  
  /**************************************************
   * float8 ops
***************
*** 113,119 ****
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_BOOL(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
--- 129,135 ----
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_FLOAT8(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
diff -c -r -N contrib.orig/btree_gist/btree_gist.sql.in contrib/btree_gist/btree_gist.sql.in
*** contrib.orig/btree_gist/btree_gist.sql.in	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_gist.sql.in	2009-11-26 17:10:39.000000000 +0300
***************
*** 84,90 ****
--- 84,233 ----
  	STORAGE = EXTENDED
  );
  
+ --distance operation
+ CREATE OR REPLACE FUNCTION cash_dist(money, money)
+ RETURNS money
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE OPERATOR <-> (
+ 	LEFTARG = money,
+ 	RIGHTARG = money,
+ 	PROCEDURE = cash_dist,
+ 	COMMUTATOR = '<->'
+ );
+ 
+ CREATE OR REPLACE FUNCTION date_dist(date, date)
+ RETURNS int4
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
  
+ CREATE OPERATOR <-> (
+ 	LEFTARG = date,
+ 	RIGHTARG = date,
+ 	PROCEDURE = date_dist,
+ 	COMMUTATOR = '<->'
+ );
+ 
+ CREATE OR REPLACE FUNCTION float4_dist(float4, float4)
+ RETURNS float4
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE OPERATOR <-> (
+ 	LEFTARG = float4,
+ 	RIGHTARG = float4,
+ 	PROCEDURE = float4_dist,
+ 	COMMUTATOR = '<->'
+ );
+ 
+ CREATE OR REPLACE FUNCTION float8_dist(float8, float8)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE OPERATOR <-> (
+ 	LEFTARG = float8,
+ 	RIGHTARG = float8,
+ 	PROCEDURE = float8_dist,
+ 	COMMUTATOR = '<->'
+ );
+ 
+ CREATE OR REPLACE FUNCTION int2_dist(int2, int2)
+ RETURNS int2
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE OPERATOR <-> (
+ 	LEFTARG = int2,
+ 	RIGHTARG = int2,
+ 	PROCEDURE = int2_dist,
+ 	COMMUTATOR = '<->'
+ );
+ 
+ CREATE OR REPLACE FUNCTION int4_dist(int4, int4)
+ RETURNS int4
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE OPERATOR <-> (
+ 	LEFTARG = int4,
+ 	RIGHTARG = int4,
+ 	PROCEDURE = int4_dist,
+ 	COMMUTATOR = '<->'
+ );
+ 
+ CREATE OR REPLACE FUNCTION int8_dist(int8, int8)
+ RETURNS int8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE OPERATOR <-> (
+ 	LEFTARG = int8,
+ 	RIGHTARG = int8,
+ 	PROCEDURE = int8_dist,
+ 	COMMUTATOR = '<->'
+ );
+ 
+ CREATE OR REPLACE FUNCTION interval_dist(interval, interval)
+ RETURNS interval
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE OPERATOR <-> (
+ 	LEFTARG = interval,
+ 	RIGHTARG = interval,
+ 	PROCEDURE = interval_dist,
+ 	COMMUTATOR = '<->'
+ );
+ 
+ CREATE OR REPLACE FUNCTION oid_dist(oid, oid)
+ RETURNS oid
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE OPERATOR <-> (
+ 	LEFTARG = oid,
+ 	RIGHTARG = oid,
+ 	PROCEDURE = oid_dist,
+ 	COMMUTATOR = '<->'
+ );
+ 
+ CREATE OR REPLACE FUNCTION time_dist(time, time)
+ RETURNS interval
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE OPERATOR <-> (
+ 	LEFTARG = time,
+ 	RIGHTARG = time,
+ 	PROCEDURE = time_dist,
+ 	COMMUTATOR = '<->'
+ );
+ 
+ CREATE OR REPLACE FUNCTION ts_dist(timestamp, timestamp)
+ RETURNS interval
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE OPERATOR <-> (
+ 	LEFTARG = timestamp,
+ 	RIGHTARG = timestamp,
+ 	PROCEDURE = ts_dist,
+ 	COMMUTATOR = '<->'
+ );
+ 
+ CREATE OR REPLACE FUNCTION tstz_dist(timestamptz, timestamptz)
+ RETURNS interval
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE OPERATOR <-> (
+ 	LEFTARG = timestamptz,
+ 	RIGHTARG = timestamptz,
+ 	PROCEDURE = tstz_dist,
+ 	COMMUTATOR = '<->'
+ );
  
  --
  --
***************
*** 95,101 ****
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
--- 238,244 ----
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
***************
*** 143,148 ****
--- 286,292 ----
  	OPERATOR	3	=  ,
  	OPERATOR	4	>= ,
  	OPERATOR	5	>  ,
+ 	OPERATOR	15	<->,
  	FUNCTION	1	gbt_oid_consistent (internal, oid, int2, oid, internal),
  	FUNCTION	2	gbt_oid_union (bytea, internal),
  	FUNCTION	3	gbt_oid_compress (internal),
***************
*** 162,168 ****
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
--- 306,312 ----
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
***************
*** 200,205 ****
--- 344,350 ----
  	OPERATOR	3	=  ,
  	OPERATOR	4	>= ,
  	OPERATOR	5	>  ,
+ 	OPERATOR	15	<->,
  	FUNCTION	1	gbt_int2_consistent (internal, int2, int2, oid, internal),
  	FUNCTION	2	gbt_int2_union (bytea, internal),
  	FUNCTION	3	gbt_int2_compress (internal),
***************
*** 218,224 ****
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
--- 363,369 ----
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
***************
*** 256,261 ****
--- 401,407 ----
  	OPERATOR	3	=  ,
  	OPERATOR	4	>= ,
  	OPERATOR	5	>  ,
+ 	OPERATOR	15	<->,
  	FUNCTION	1	gbt_int4_consistent (internal, int4, int2, oid, internal),
  	FUNCTION	2	gbt_int4_union (bytea, internal),
  	FUNCTION	3	gbt_int4_compress (internal),
***************
*** 274,280 ****
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
--- 420,426 ----
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
***************
*** 312,317 ****
--- 458,464 ----
  	OPERATOR	3	=  ,
  	OPERATOR	4	>= ,
  	OPERATOR	5	>  ,
+ 	OPERATOR	15	<->,
  	FUNCTION	1	gbt_int8_consistent (internal, int8, int2, oid, internal),
  	FUNCTION	2	gbt_int8_union (bytea, internal),
  	FUNCTION	3	gbt_int8_compress (internal),
***************
*** 331,337 ****
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
--- 478,484 ----
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
***************
*** 369,374 ****
--- 516,522 ----
  	OPERATOR	3	=  ,
  	OPERATOR	4	>= ,
  	OPERATOR	5	>  ,
+ 	OPERATOR	15	<->,
  	FUNCTION	1	gbt_float4_consistent (internal, float4, int2, oid, internal),
  	FUNCTION	2	gbt_float4_union (bytea, internal),
  	FUNCTION	3	gbt_float4_compress (internal),
***************
*** 390,396 ****
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
--- 538,544 ----
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
***************
*** 428,433 ****
--- 576,582 ----
  	OPERATOR	3	=  ,
  	OPERATOR	4	>= ,
  	OPERATOR	5	>  ,
+ 	OPERATOR	15	<->,
  	FUNCTION	1	gbt_float8_consistent (internal, float8, int2, oid, internal),
  	FUNCTION	2	gbt_float8_union (bytea, internal),
  	FUNCTION	3	gbt_float8_compress (internal),
***************
*** 447,458 ****
  --
  
  CREATE OR REPLACE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
  CREATE OR REPLACE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
        
--- 596,607 ----
  --
  
  CREATE OR REPLACE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
  CREATE OR REPLACE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
        
***************
*** 495,500 ****
--- 644,650 ----
  	OPERATOR	3	=  ,
  	OPERATOR	4	>= ,
  	OPERATOR	5	>  ,
+ 	OPERATOR	15	<->,
  	FUNCTION	1	gbt_ts_consistent (internal, timestamp, int2, oid, internal),
  	FUNCTION	2	gbt_ts_union (bytea, internal),
  	FUNCTION	3	gbt_ts_compress (internal),
***************
*** 514,519 ****
--- 664,670 ----
  	OPERATOR	3	=  ,
  	OPERATOR	4	>= ,
  	OPERATOR	5	>  ,
+ 	OPERATOR	15	<->,
  	FUNCTION	1	gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
  	FUNCTION	2	gbt_ts_union (bytea, internal),
  	FUNCTION	3	gbt_tstz_compress (internal),
***************
*** 533,544 ****
  --
  
  CREATE OR REPLACE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
  CREATE OR REPLACE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
--- 684,695 ----
  --
  
  CREATE OR REPLACE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
  CREATE OR REPLACE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
***************
*** 581,586 ****
--- 732,738 ----
  	OPERATOR	3	=  ,
  	OPERATOR	4	>= ,
  	OPERATOR	5	>  ,
+ 	OPERATOR	15	<->,
  	FUNCTION	1	gbt_time_consistent (internal, time, int2, oid, internal),
  	FUNCTION	2	gbt_time_union (bytea, internal),
  	FUNCTION	3	gbt_time_compress (internal),
***************
*** 617,623 ****
  --
  
  CREATE OR REPLACE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
--- 769,775 ----
  --
  
  CREATE OR REPLACE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
***************
*** 655,660 ****
--- 807,813 ----
  	OPERATOR	3	=  ,
  	OPERATOR	4	>= ,
  	OPERATOR	5	>  ,
+ 	OPERATOR	15	<->,
  	FUNCTION	1	gbt_date_consistent (internal, date, int2, oid, internal),
  	FUNCTION	2	gbt_date_union (bytea, internal),
  	FUNCTION	3	gbt_date_compress (internal),
***************
*** 674,680 ****
  --
  
  CREATE OR REPLACE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
--- 827,833 ----
  --
  
  CREATE OR REPLACE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
***************
*** 717,722 ****
--- 870,876 ----
  	OPERATOR	3	= ,
  	OPERATOR	4	>= ,
  	OPERATOR	5	> ,
+ 	OPERATOR	15	<->,
  	FUNCTION	1	gbt_intv_consistent (internal, interval, int2, oid, internal),
  	FUNCTION	2	gbt_intv_union (bytea, internal),
  	FUNCTION	3	gbt_intv_compress (internal),
***************
*** 735,741 ****
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
--- 889,895 ----
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
***************
*** 773,778 ****
--- 927,933 ----
  	OPERATOR	3	= ,
  	OPERATOR	4	>= ,
  	OPERATOR	5	> ,
+ 	OPERATOR	15	<->,
  	FUNCTION	1	gbt_cash_consistent (internal, money, int2, oid, internal),
  	FUNCTION	2	gbt_cash_union (bytea, internal),
  	FUNCTION	3	gbt_cash_compress (internal),
***************
*** 791,797 ****
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
--- 946,952 ----
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
***************
*** 1124,1130 ****
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
--- 1279,1285 ----
  --
  -- define the GiST support methods
  CREATE OR REPLACE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
  
diff -c -r -N contrib.orig/btree_gist/btree_inet.c contrib/btree_gist/btree_inet.c
*** contrib.orig/btree_gist/btree_inet.c	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_inet.c	2009-11-26 15:47:47.000000000 +0300
***************
*** 79,85 ****
  	gbt_ineteq,
  	gbt_inetle,
  	gbt_inetlt,
! 	gbt_inetkey_cmp
  };
  
  
--- 79,86 ----
  	gbt_ineteq,
  	gbt_inetle,
  	gbt_inetlt,
! 	gbt_inetkey_cmp,
! 	NULL
  };
  
  
***************
*** 130,136 ****
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_BOOL(gbt_num_consistent(&key, (void *) &query,
  									  &strategy, GIST_LEAF(entry), &tinfo));
  }
  
--- 131,137 ----
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_FLOAT8(gbt_num_consistent(&key, (void *) &query,
  									  &strategy, GIST_LEAF(entry), &tinfo));
  }
  
diff -c -r -N contrib.orig/btree_gist/btree_int2.c contrib/btree_gist/btree_int2.c
*** contrib.orig/btree_gist/btree_int2.c	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_int2.c	2009-11-26 16:16:15.000000000 +0300
***************
*** 65,70 ****
--- 65,75 ----
  
  }
  
+ static float8
+ gbt_int2_dist(const void *a, const void *b)
+ {
+ 	return (float8)Abs(*((int16 *) a) - *((int16 *) b));
+ }
  
  static const gbtree_ninfo tinfo =
  {
***************
*** 75,86 ****
  	gbt_int2eq,
  	gbt_int2le,
  	gbt_int2lt,
! 	gbt_int2key_cmp
  };
  
  
  
! 
  
  
  /**************************************************
--- 80,100 ----
  	gbt_int2eq,
  	gbt_int2le,
  	gbt_int2lt,
! 	gbt_int2key_cmp,
! 	gbt_int2_dist
  };
  
  
+ PG_FUNCTION_INFO_V1(int2_dist);
+ Datum       int2_dist(PG_FUNCTION_ARGS);
+ Datum
+ int2_dist(PG_FUNCTION_ARGS)
+ {
+     int2        a = PG_GETARG_INT16(0);
+ 	int2        b = PG_GETARG_INT16(1);
  
! 	PG_RETURN_INT16( Abs(a - b) );
! }
  
  
  /**************************************************
***************
*** 116,122 ****
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_BOOL(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
--- 130,136 ----
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_FLOAT8(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
diff -c -r -N contrib.orig/btree_gist/btree_int4.c contrib/btree_gist/btree_int4.c
*** contrib.orig/btree_gist/btree_int4.c	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_int4.c	2009-11-26 16:17:42.000000000 +0300
***************
*** 66,71 ****
--- 66,76 ----
  
  }
  
+ static float8
+ gbt_int4_dist(const void *a, const void *b)
+ {
+ 	return (float8)Abs(*((int32 *) a) - *((int32 *) b));
+ }
  
  static const gbtree_ninfo tinfo =
  {
***************
*** 76,84 ****
  	gbt_int4eq,
  	gbt_int4le,
  	gbt_int4lt,
! 	gbt_int4key_cmp
  };
  
  
  /**************************************************
   * int32 ops
--- 81,100 ----
  	gbt_int4eq,
  	gbt_int4le,
  	gbt_int4lt,
! 	gbt_int4key_cmp,
! 	gbt_int4_dist
  };
  
+ PG_FUNCTION_INFO_V1(int4_dist);
+ Datum       int4_dist(PG_FUNCTION_ARGS);
+ Datum
+ int4_dist(PG_FUNCTION_ARGS)
+ {
+     int4        a = PG_GETARG_INT32(0);
+ 	int4        b = PG_GETARG_INT32(1);
+ 
+ 	PG_RETURN_INT32( Abs(a - b) );
+ }
  
  /**************************************************
   * int32 ops
***************
*** 113,119 ****
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_BOOL(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
--- 129,135 ----
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_FLOAT8(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
diff -c -r -N contrib.orig/btree_gist/btree_int8.c contrib/btree_gist/btree_int8.c
*** contrib.orig/btree_gist/btree_int8.c	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_int8.c	2009-11-26 17:52:03.000000000 +0300
***************
*** 66,71 ****
--- 66,76 ----
  
  }
  
+ static float8
+ gbt_int8_dist(const void *a, const void *b)
+ {
+ 	return (float8)Abs(*((int64 *) a) - *((int64 *) b));
+ }
  
  static const gbtree_ninfo tinfo =
  {
***************
*** 76,84 ****
  	gbt_int8eq,
  	gbt_int8le,
  	gbt_int8lt,
! 	gbt_int8key_cmp
  };
  
  
  /**************************************************
   * int64 ops
--- 81,100 ----
  	gbt_int8eq,
  	gbt_int8le,
  	gbt_int8lt,
! 	gbt_int8key_cmp,
! 	gbt_int8_dist
  };
  
+ PG_FUNCTION_INFO_V1(int8_dist);
+ Datum       int8_dist(PG_FUNCTION_ARGS);
+ Datum
+ int8_dist(PG_FUNCTION_ARGS)
+ {
+     int64       a = PG_GETARG_INT64(0);
+ 	int64       b = PG_GETARG_INT64(1);
+ 
+ 	PG_RETURN_INT64( Abs(a - b) );
+ }
  
  /**************************************************
   * int64 ops
***************
*** 113,119 ****
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_BOOL(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
--- 129,135 ----
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_FLOAT8(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
diff -c -r -N contrib.orig/btree_gist/btree_interval.c contrib/btree_gist/btree_interval.c
*** contrib.orig/btree_gist/btree_interval.c	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_interval.c	2009-11-26 16:47:21.000000000 +0300
***************
*** 80,85 ****
--- 80,91 ----
  	return INTERVAL_TO_SEC(i);
  }
  
+ static float8
+ gbt_intv_dist(const void *a, const void *b)
+ {
+ 	return (float8)Abs(intr2num((Interval*)a) - intr2num((Interval*)b));
+ }
+ 
  /*
   * INTERVALSIZE should be the actual size-on-disk of an Interval, as shown
   * in pg_type.	This might be less than sizeof(Interval) if the compiler
***************
*** 96,104 ****
  	gbt_intveq,
  	gbt_intvle,
  	gbt_intvlt,
! 	gbt_intvkey_cmp
  };
  
  
  /**************************************************
   * interval ops
--- 102,133 ----
  	gbt_intveq,
  	gbt_intvle,
  	gbt_intvlt,
! 	gbt_intvkey_cmp,
! 	gbt_intv_dist
  };
  
+ Interval *
+ abs_interval(Interval *a)
+ {
+ 	static Interval zero = {0, 0, 0};
+ 
+ 	if (DatumGetBool(DirectFunctionCall2(interval_lt, IntervalPGetDatum(a), IntervalPGetDatum(&zero))))
+ 	{
+ 		a = DatumGetIntervalP(DirectFunctionCall1(interval_um, IntervalPGetDatum(a)));
+ 	}
+ 
+ 	return a;
+ }
+ 
+ PG_FUNCTION_INFO_V1(interval_dist);
+ Datum       interval_dist(PG_FUNCTION_ARGS);
+ Datum
+ interval_dist(PG_FUNCTION_ARGS)
+ {
+ 	Interval*		res = DatumGetIntervalP(DirectFunctionCall2(interval_mi,PG_GETARG_DATUM(0), PG_GETARG_DATUM(1))); 
+ 
+ 	PG_RETURN_INTERVAL_P( abs_interval(res) );
+ }
  
  /**************************************************
   * interval ops
***************
*** 181,187 ****
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_BOOL(
  				   gbt_num_consistent(&key, (void *) query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
--- 210,216 ----
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_FLOAT8(
  				   gbt_num_consistent(&key, (void *) query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
diff -c -r -N contrib.orig/btree_gist/btree_macaddr.c contrib/btree_gist/btree_macaddr.c
*** contrib.orig/btree_gist/btree_macaddr.c	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_macaddr.c	2009-11-26 15:48:19.000000000 +0300
***************
*** 82,88 ****
  	gbt_macadeq,
  	gbt_macadle,
  	gbt_macadlt,
! 	gbt_macadkey_cmp
  };
  
  
--- 82,89 ----
  	gbt_macadeq,
  	gbt_macadle,
  	gbt_macadlt,
! 	gbt_macadkey_cmp,
! 	NULL
  };
  
  
***************
*** 134,140 ****
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_BOOL(
  				   gbt_num_consistent(&key, (void *) query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
--- 135,141 ----
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_FLOAT8(
  				   gbt_num_consistent(&key, (void *) query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
diff -c -r -N contrib.orig/btree_gist/btree_oid.c contrib/btree_gist/btree_oid.c
*** contrib.orig/btree_gist/btree_oid.c	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_oid.c	2009-11-26 16:49:22.000000000 +0300
***************
*** 66,71 ****
--- 66,81 ----
  
  }
  
+ static float8
+ gbt_oid_dist(const void *a, const void *b)
+ {
+ 	if (*((Oid *) a) < *((Oid *) b))
+ 		return (float8)( *((Oid *) b) - *((Oid *) a) );
+ 	else if (*((Oid *) a) > *((Oid *) b))
+ 		return (float8)( *((Oid *) a) - *((Oid *) b) );
+ 	else
+ 		return 0.0;
+ }
  
  static const gbtree_ninfo tinfo =
  {
***************
*** 76,84 ****
  	gbt_oideq,
  	gbt_oidle,
  	gbt_oidlt,
! 	gbt_oidkey_cmp
  };
  
  
  /**************************************************
   * Oid ops
--- 86,112 ----
  	gbt_oideq,
  	gbt_oidle,
  	gbt_oidlt,
! 	gbt_oidkey_cmp,
! 	gbt_oid_dist
  };
  
+ PG_FUNCTION_INFO_V1(oid_dist);
+ Datum       oid_dist(PG_FUNCTION_ARGS);
+ Datum
+ oid_dist(PG_FUNCTION_ARGS)
+ {
+     Oid        	a = PG_GETARG_OID(0);
+ 	Oid        	b = PG_GETARG_OID(1);
+ 	Oid 		res;
+ 
+ 	if (a > b)
+ 		res = a - b;
+ 	else if (a < b)
+ 		res = b - a;
+ 	else
+ 		res = 0;
+ 	PG_RETURN_OID( res );
+ }
  
  /**************************************************
   * Oid ops
***************
*** 113,119 ****
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_BOOL(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
--- 141,147 ----
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_FLOAT8(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
diff -c -r -N contrib.orig/btree_gist/btree_time.c contrib/btree_gist/btree_time.c
*** contrib.orig/btree_gist/btree_time.c	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_time.c	2009-11-26 16:52:37.000000000 +0300
***************
*** 108,113 ****
--- 108,126 ----
  	return 0;
  }
  
+ static float8
+ gbt_time_dist(const void *a, const void *b)
+ {
+ 	const TimeADT *aa = (const TimeADT *) a;
+ 	const TimeADT *bb = (const TimeADT *) b;
+ 	Interval	  *i;
+ 
+ 	i = DatumGetIntervalP(DirectFunctionCall2(time_mi_time,
+ 											TimeADTGetDatumFast(*aa),
+ 											TimeADTGetDatumFast(*bb)));
+ 	return (float8)Abs(INTERVAL_TO_SEC(i));
+ }
+ 
  
  static const gbtree_ninfo tinfo =
  {
***************
*** 118,126 ****
  	gbt_timeeq,
  	gbt_timele,
  	gbt_timelt,
! 	gbt_timekey_cmp
  };
  
  
  /**************************************************
   * time ops
--- 131,148 ----
  	gbt_timeeq,
  	gbt_timele,
  	gbt_timelt,
! 	gbt_timekey_cmp,
! 	gbt_time_dist
  };
  
+ PG_FUNCTION_INFO_V1(time_dist);
+ Datum       time_dist(PG_FUNCTION_ARGS);
+ Datum
+ time_dist(PG_FUNCTION_ARGS)
+ {
+ 	Interval	  *r = DatumGetIntervalP(DirectFunctionCall2(time_mi_time,PG_GETARG_DATUM(0),PG_GETARG_DATUM(1)));
+ 	PG_RETURN_INTERVAL_P( abs_interval(r) );
+ }
  
  /**************************************************
   * time ops
***************
*** 187,193 ****
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_BOOL(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
--- 209,215 ----
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_FLOAT8(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
***************
*** 217,223 ****
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_BOOL(
  				   gbt_num_consistent(&key, (void *) &qqq, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
--- 239,245 ----
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_FLOAT8(
  				   gbt_num_consistent(&key, (void *) &qqq, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
diff -c -r -N contrib.orig/btree_gist/btree_ts.c contrib/btree_gist/btree_ts.c
*** contrib.orig/btree_gist/btree_ts.c	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_ts.c	2009-11-26 18:05:46.000000000 +0300
***************
*** 4,9 ****
--- 4,10 ----
  #include "btree_gist.h"
  #include "btree_utils_num.h"
  #include "utils/datetime.h"
+ #include "utils/builtins.h"
  
  typedef struct
  {
***************
*** 106,111 ****
--- 107,127 ----
  	return 0;
  }
  
+ static float8
+ gbt_ts_dist(const void *a, const void *b)
+ {
+     const Timestamp *aa = (const Timestamp *) a;
+ 	const Timestamp *bb = (const Timestamp *) b;
+ 	Interval      *i;
+ 
+ 	if (TIMESTAMP_NOT_FINITE(*aa) || TIMESTAMP_NOT_FINITE(*bb))
+ 		return get_float8_infinity();
+ 
+ 	i = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ 											  TimestampGetDatumFast(*aa),
+ 											  TimestampGetDatumFast(*bb)));
+ 	return (float8)Abs(INTERVAL_TO_SEC(i));
+ }
  
  static const gbtree_ninfo tinfo =
  {
***************
*** 116,125 ****
  	gbt_tseq,
  	gbt_tsle,
  	gbt_tslt,
! 	gbt_tskey_cmp
  };
  
  
  /**************************************************
   * timestamp ops
   **************************************************/
--- 132,197 ----
  	gbt_tseq,
  	gbt_tsle,
  	gbt_tslt,
! 	gbt_tskey_cmp,
! 	gbt_ts_dist
  };
  
  
+ PG_FUNCTION_INFO_V1(ts_dist);
+ Datum       ts_dist(PG_FUNCTION_ARGS);
+ Datum
+ ts_dist(PG_FUNCTION_ARGS)
+ {
+ 	Timestamp	a = PG_GETARG_TIMESTAMP(0); 
+ 	Timestamp	b = PG_GETARG_TIMESTAMP(1); 
+     Interval      *r;
+ 
+ 	if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ 	{
+ 		Interval *p = palloc(sizeof(Interval));
+ 
+ 		p->day = INT_MAX;
+ 		p->month = INT_MAX;
+ #ifdef HAVE_INT64_TIMESTAMP
+ 		p->time = INT64CONST(0x7FFFFFFFFFFFFFFF);	
+ #else
+ 		p->time = DBL_MAX;
+ #endif
+ 		PG_RETURN_INTERVAL_P(p);
+ 	}
+ 	else
+ 
+ 	r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,PG_GETARG_DATUM(0),PG_GETARG_DATUM(1)));
+ 	PG_RETURN_INTERVAL_P( abs_interval(r) );
+ }
+ 
+ PG_FUNCTION_INFO_V1(tstz_dist);
+ Datum       tstz_dist(PG_FUNCTION_ARGS);
+ Datum
+ tstz_dist(PG_FUNCTION_ARGS)
+ {
+ 	TimestampTz	a = PG_GETARG_TIMESTAMPTZ(0); 
+ 	TimestampTz	b = PG_GETARG_TIMESTAMPTZ(1); 
+     Interval      *r;
+ 
+ 	if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ 	{
+ 		Interval *p = palloc(sizeof(Interval));
+ 
+ 		p->day = INT_MAX;
+ 		p->month = INT_MAX;
+ #ifdef HAVE_INT64_TIMESTAMP
+ 		p->time = INT64CONST(0x7FFFFFFFFFFFFFFF);	
+ #else
+ 		p->time = DBL_MAX;
+ #endif
+ 		PG_RETURN_INTERVAL_P(p);
+ 	}
+ 
+ 	r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,PG_GETARG_DATUM(0),PG_GETARG_DATUM(1)));
+ 	PG_RETURN_INTERVAL_P( abs_interval(r) );
+ }
+ 
  /**************************************************
   * timestamp ops
   **************************************************/
***************
*** 204,210 ****
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_BOOL(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
--- 276,282 ----
  	key.lower = (GBT_NUMKEY *) &kkk->lower;
  	key.upper = (GBT_NUMKEY *) &kkk->upper;
  
! 	PG_RETURN_FLOAT8(
  				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
***************
*** 229,235 ****
  	key.upper = (GBT_NUMKEY *) &kkk[MAXALIGN(tinfo.size)];
  	qqq = tstz_to_ts_gmt(query);
  
! 	PG_RETURN_BOOL(
  				   gbt_num_consistent(&key, (void *) &qqq, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
--- 301,307 ----
  	key.upper = (GBT_NUMKEY *) &kkk[MAXALIGN(tinfo.size)];
  	qqq = tstz_to_ts_gmt(query);
  
! 	PG_RETURN_FLOAT8(
  				   gbt_num_consistent(&key, (void *) &qqq, &strategy, GIST_LEAF(entry), &tinfo)
  		);
  }
diff -c -r -N contrib.orig/btree_gist/btree_utils_num.c contrib/btree_gist/btree_utils_num.c
*** contrib.orig/btree_gist/btree_utils_num.c	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_utils_num.c	2009-11-26 16:00:01.000000000 +0300
***************
*** 187,193 ****
  ** The GiST consistent method
  */
  
! bool
  gbt_num_consistent(
  				   const GBT_NUMKEY_R *key,
  				   const void *query,
--- 187,193 ----
  ** The GiST consistent method
  */
  
! float8
  gbt_num_consistent(
  				   const GBT_NUMKEY_R *key,
  				   const void *query,
***************
*** 197,235 ****
  )
  {
  
! 	bool		retval = FALSE;
  
  	switch (*strategy)
  	{
  		case BTLessEqualStrategyNumber:
! 			retval = (*tinfo->f_ge) (query, key->lower);
  			break;
  		case BTLessStrategyNumber:
  			if (is_leaf)
! 				retval = (*tinfo->f_gt) (query, key->lower);
  			else
! 				retval = (*tinfo->f_ge) (query, key->lower);
  			break;
  		case BTEqualStrategyNumber:
  			if (is_leaf)
! 				retval = (*tinfo->f_eq) (query, key->lower);
  			else
! 				retval = (*tinfo->f_le) (key->lower, query) && (*tinfo->f_le) (query, key->upper);
  			break;
  		case BTGreaterStrategyNumber:
  			if (is_leaf)
! 				retval = (*tinfo->f_lt) (query, key->upper);
  			else
! 				retval = (*tinfo->f_le) (query, key->upper);
  			break;
  		case BTGreaterEqualStrategyNumber:
! 			retval = (*tinfo->f_le) (query, key->upper);
  			break;
  		default:
! 			retval = FALSE;
  	}
  
! 	return (retval);
  }
  
  
--- 197,245 ----
  )
  {
  
! 	float8		retval = -1.0;
  
  	switch (*strategy)
  	{
  		case BTLessEqualStrategyNumber:
! 			retval = (*tinfo->f_ge) (query, key->lower) ? 0.0 : -1.0;
  			break;
  		case BTLessStrategyNumber:
  			if (is_leaf)
! 				retval = (*tinfo->f_gt) (query, key->lower) ? 0.0 : -1.0;
  			else
! 				retval = (*tinfo->f_ge) (query, key->lower) ? 0.0 : -1.0;
  			break;
  		case BTEqualStrategyNumber:
  			if (is_leaf)
! 				retval = (*tinfo->f_eq) (query, key->lower) ? 0.0 : -1.0;
  			else
! 				retval = ((*tinfo->f_le) (key->lower, query) && (*tinfo->f_le) (query, key->upper)) ? 0.0 : -1.0;
  			break;
  		case BTGreaterStrategyNumber:
  			if (is_leaf)
! 				retval = (*tinfo->f_lt) (query, key->upper) ? 0.0 : -1.0;
  			else
! 				retval = (*tinfo->f_le) (query, key->upper) ? 0.0 : -1.0;
  			break;
  		case BTGreaterEqualStrategyNumber:
! 			retval = (*tinfo->f_le) (query, key->upper) ? 0.0 : -1.0;
! 			break;
! 		case KNNSearchStrategyNumber:
! 			if (tinfo->f_dist == NULL)
! 				elog(ERROR,"KNNsearch is not supported");
! 			if ( tinfo->f_le(query, key->lower) )
! 				retval = tinfo->f_dist(query, key->lower);
! 			else if ( tinfo->f_ge(query, key->upper) )
! 				retval = tinfo->f_dist(query, key->upper);
! 			else
! 				retval = 0.0;
  			break;
  		default:
! 			retval = -1.0;
  	}
  
! 	return retval;
  }
  
  
diff -c -r -N contrib.orig/btree_gist/btree_utils_num.h contrib/btree_gist/btree_utils_num.h
*** contrib.orig/btree_gist/btree_utils_num.h	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/btree_utils_num.h	2009-11-26 16:54:44.000000000 +0300
***************
*** 46,51 ****
--- 46,52 ----
  	bool		(*f_le) (const void *, const void *);	/* less equal */
  	bool		(*f_lt) (const void *, const void *);	/* less then */
  	int			(*f_cmp) (const void *, const void *);	/* key compare function */
+ 	float8		(*f_dist) (const void *, const void *);	/* key distance function */
  } gbtree_ninfo;
  
  
***************
*** 91,98 ****
  	 (ivp)->month * (30.0 * SECS_PER_DAY))
  #endif
  
  
! extern bool gbt_num_consistent(const GBT_NUMKEY_R *key, const void *query,
  				   const StrategyNumber *strategy, bool is_leaf,
  				   const gbtree_ninfo *tinfo);
  
--- 92,100 ----
  	 (ivp)->month * (30.0 * SECS_PER_DAY))
  #endif
  
+ extern Interval * abs_interval(Interval *a);
  
! extern float8 gbt_num_consistent(const GBT_NUMKEY_R *key, const void *query,
  				   const StrategyNumber *strategy, bool is_leaf,
  				   const gbtree_ninfo *tinfo);
  
diff -c -r -N contrib.orig/btree_gist/expected/cash.out contrib/btree_gist/expected/cash.out
*** contrib.orig/btree_gist/expected/cash.out	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/expected/cash.out	2009-11-26 18:06:13.000000000 +0300
***************
*** 32,37 ****
--- 32,45 ----
     253
  (1 row)
  
+ SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3;
+      a      | ?column? 
+ ------------+----------
+  $21,472.79 |    $0.00
+  $21,469.25 |    $3.54
+  $21,915.01 |  $442.22
+ (3 rows)
+ 
  CREATE INDEX moneyidx ON moneytmp USING gist ( a );
  SET enable_seqscan=off;
  SELECT count(*) FROM moneytmp WHERE a <  '22649.64'::money;
***************
*** 64,66 ****
--- 72,91 ----
     253
  (1 row)
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3;
+                    QUERY PLAN                    
+ -------------------------------------------------
+  Limit
+    ->  Index Scan using moneyidx on moneytmp
+          Index Cond: (a <-> '$21,472.79'::money)
+ (3 rows)
+ 
+ SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3;
+      a      | ?column? 
+ ------------+----------
+  $21,472.79 |    $0.00
+  $21,469.25 |    $3.54
+  $21,915.01 |  $442.22
+ (3 rows)
+ 
diff -c -r -N contrib.orig/btree_gist/expected/date.out contrib/btree_gist/expected/date.out
*** contrib.orig/btree_gist/expected/date.out	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/expected/date.out	2009-11-26 18:06:13.000000000 +0300
***************
*** 32,37 ****
--- 32,45 ----
     313
  (1 row)
  
+ SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3;
+      a      | ?column? 
+ ------------+----------
+  02-13-2001 |        0
+  02-11-2001 |        2
+  03-24-2001 |       39
+ (3 rows)
+ 
  CREATE INDEX dateidx ON datetmp USING gist ( a );
  SET enable_seqscan=off;
  SELECT count(*) FROM datetmp WHERE a <  '2001-02-13'::date;
***************
*** 64,66 ****
--- 72,91 ----
     313
  (1 row)
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3;
+                    QUERY PLAN                   
+ ------------------------------------------------
+  Limit
+    ->  Index Scan using dateidx on datetmp
+          Index Cond: (a <-> '02-13-2001'::date)
+ (3 rows)
+ 
+ SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3;
+      a      | ?column? 
+ ------------+----------
+  02-13-2001 |        0
+  02-11-2001 |        2
+  03-24-2001 |       39
+ (3 rows)
+ 
diff -c -r -N contrib.orig/btree_gist/expected/float4.out contrib/btree_gist/expected/float4.out
*** contrib.orig/btree_gist/expected/float4.out	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/expected/float4.out	2009-11-26 18:06:13.000000000 +0300
***************
*** 32,37 ****
--- 32,45 ----
     302
  (1 row)
  
+ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3;
+     a     | ?column? 
+ ----------+----------
+      -179 |        0
+  -189.024 |  10.0239
+  -158.177 |  20.8226
+ (3 rows)
+ 
  CREATE INDEX float4idx ON float4tmp USING gist ( a );
  SET enable_seqscan=off;
  SELECT count(*) FROM float4tmp WHERE a <  -179.0::float4;
***************
*** 64,66 ****
--- 72,91 ----
     302
  (1 row)
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3;
+                   QUERY PLAN                   
+ -----------------------------------------------
+  Limit
+    ->  Index Scan using float4idx on float4tmp
+          Index Cond: (a <-> (-179)::real)
+ (3 rows)
+ 
+ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3;
+     a     | ?column? 
+ ----------+----------
+      -179 |        0
+  -189.024 |  10.0239
+  -158.177 |  20.8226
+ (3 rows)
+ 
diff -c -r -N contrib.orig/btree_gist/expected/float8.out contrib/btree_gist/expected/float8.out
*** contrib.orig/btree_gist/expected/float8.out	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/expected/float8.out	2009-11-26 18:06:13.000000000 +0300
***************
*** 32,37 ****
--- 32,45 ----
     306
  (1 row)
  
+ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3;
+       a       |  ?column?  
+ --------------+------------
+         -1890 |          0
+  -2003.634512 | 113.634512
+   -1769.73634 |  120.26366
+ (3 rows)
+ 
  CREATE INDEX float8idx ON float8tmp USING gist ( a );
  SET enable_seqscan=off;
  SELECT count(*) FROM float8tmp WHERE a <  -1890.0::float8;
***************
*** 64,66 ****
--- 72,91 ----
     306
  (1 row)
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3;
+                       QUERY PLAN                       
+ -------------------------------------------------------
+  Limit
+    ->  Index Scan using float8idx on float8tmp
+          Index Cond: (a <-> (-1890)::double precision)
+ (3 rows)
+ 
+ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3;
+       a       |  ?column?  
+ --------------+------------
+         -1890 |          0
+  -2003.634512 | 113.634512
+   -1769.73634 |  120.26366
+ (3 rows)
+ 
diff -c -r -N contrib.orig/btree_gist/expected/int2.out contrib/btree_gist/expected/int2.out
*** contrib.orig/btree_gist/expected/int2.out	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/expected/int2.out	2009-11-26 18:06:13.000000000 +0300
***************
*** 32,37 ****
--- 32,45 ----
     248
  (1 row)
  
+ SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3;
+   a  | ?column? 
+ -----+----------
+  237 |        0
+  232 |        5
+  228 |        9
+ (3 rows)
+ 
  CREATE INDEX int2idx ON int2tmp USING gist ( a );
  SET enable_seqscan=off;
  SELECT count(*) FROM int2tmp WHERE a <  237::int2;
***************
*** 64,66 ****
--- 72,91 ----
     248
  (1 row)
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3;
+                 QUERY PLAN                 
+ -------------------------------------------
+  Limit
+    ->  Index Scan using int2idx on int2tmp
+          Index Cond: (a <-> 237::smallint)
+ (3 rows)
+ 
+ SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3;
+   a  | ?column? 
+ -----+----------
+  237 |        0
+  232 |        5
+  228 |        9
+ (3 rows)
+ 
diff -c -r -N contrib.orig/btree_gist/expected/int4.out contrib/btree_gist/expected/int4.out
*** contrib.orig/btree_gist/expected/int4.out	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/expected/int4.out	2009-11-26 18:06:13.000000000 +0300
***************
*** 32,37 ****
--- 32,45 ----
     248
  (1 row)
  
+ SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3;
+   a  | ?column? 
+ -----+----------
+  237 |        0
+  232 |        5
+  228 |        9
+ (3 rows)
+ 
  CREATE INDEX int4idx ON int4tmp USING gist ( a );
  SET enable_seqscan=off;
  SELECT count(*) FROM int4tmp WHERE a <  237::int4;
***************
*** 64,66 ****
--- 72,91 ----
     248
  (1 row)
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3;
+                 QUERY PLAN                 
+ -------------------------------------------
+  Limit
+    ->  Index Scan using int4idx on int4tmp
+          Index Cond: (a <-> 237)
+ (3 rows)
+ 
+ SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3;
+   a  | ?column? 
+ -----+----------
+  237 |        0
+  232 |        5
+  228 |        9
+ (3 rows)
+ 
diff -c -r -N contrib.orig/btree_gist/expected/int8.out contrib/btree_gist/expected/int8.out
*** contrib.orig/btree_gist/expected/int8.out	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/expected/int8.out	2009-11-26 18:06:13.000000000 +0300
***************
*** 32,37 ****
--- 32,45 ----
     270
  (1 row)
  
+ SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' LIMIT 3;
+         a        |    ?column?    
+ -----------------+----------------
+  464571291354841 |              0
+  457257666629329 |  7313624725512
+  478227196042750 | 13655904687909
+ (3 rows)
+ 
  CREATE INDEX int8idx ON int8tmp USING gist ( a );
  SET enable_seqscan=off;
  SELECT count(*) FROM int8tmp WHERE a <  464571291354841::int8;
***************
*** 64,66 ****
--- 72,91 ----
     270
  (1 row)
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' LIMIT 3;
+                      QUERY PLAN                      
+ -----------------------------------------------------
+  Limit
+    ->  Index Scan using int8idx on int8tmp
+          Index Cond: (a <-> 464571291354841::bigint)
+ (3 rows)
+ 
+ SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' LIMIT 3;
+         a        |    ?column?    
+ -----------------+----------------
+  464571291354841 |              0
+  457257666629329 |  7313624725512
+  478227196042750 | 13655904687909
+ (3 rows)
+ 
diff -c -r -N contrib.orig/btree_gist/expected/interval.out contrib/btree_gist/expected/interval.out
*** contrib.orig/btree_gist/expected/interval.out	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/expected/interval.out	2009-11-26 18:06:13.000000000 +0300
***************
*** 32,37 ****
--- 32,45 ----
     270
  (1 row)
  
+ SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
+                   a                  |               ?column?               
+ -------------------------------------+--------------------------------------
+  @ 199 days 21 hours 21 mins 23 secs | @ 0
+  @ 183 days 6 hours 52 mins 48 secs  | @ 16 days 14 hours 28 mins 35 secs
+  @ 220 days 19 hours 5 mins 42 secs  | @ 21 days -2 hours -15 mins -41 secs
+ (3 rows)
+ 
  CREATE INDEX intervalidx ON intervaltmp USING gist ( a );
  SET enable_seqscan=off;
  SELECT count(*) FROM intervaltmp WHERE a <  '199 days 21:21:23'::interval;
***************
*** 64,66 ****
--- 72,91 ----
     270
  (1 row)
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
+                                  QUERY PLAN                                  
+ -----------------------------------------------------------------------------
+  Limit
+    ->  Index Scan using intervalidx on intervaltmp
+          Index Cond: (a <-> '@ 199 days 21 hours 21 mins 23 secs'::interval)
+ (3 rows)
+ 
+ SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
+                   a                  |               ?column?               
+ -------------------------------------+--------------------------------------
+  @ 199 days 21 hours 21 mins 23 secs | @ 0
+  @ 183 days 6 hours 52 mins 48 secs  | @ 16 days 14 hours 28 mins 35 secs
+  @ 220 days 19 hours 5 mins 42 secs  | @ 21 days -2 hours -15 mins -41 secs
+ (3 rows)
+ 
diff -c -r -N contrib.orig/btree_gist/expected/time.out contrib/btree_gist/expected/time.out
*** contrib.orig/btree_gist/expected/time.out	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/expected/time.out	2009-11-26 18:06:13.000000000 +0300
***************
*** 32,37 ****
--- 32,45 ----
     292
  (1 row)
  
+ SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3;
+     a     |    ?column?     
+ ----------+-----------------
+  10:57:11 | @ 0
+  10:57:10 | @ 1 sec
+  10:55:32 | @ 1 min 39 secs
+ (3 rows)
+ 
  CREATE INDEX timeidx ON timetmp USING gist ( a );
  SET enable_seqscan=off;
  SELECT count(*) FROM timetmp WHERE a <  '10:57:11'::time;
***************
*** 64,66 ****
--- 72,91 ----
     292
  (1 row)
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3;
+                            QUERY PLAN                           
+ ----------------------------------------------------------------
+  Limit
+    ->  Index Scan using timeidx on timetmp
+          Index Cond: (a <-> '10:57:11'::time without time zone)
+ (3 rows)
+ 
+ SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3;
+     a     |    ?column?     
+ ----------+-----------------
+  10:57:11 | @ 0
+  10:57:10 | @ 1 sec
+  10:55:32 | @ 1 min 39 secs
+ (3 rows)
+ 
diff -c -r -N contrib.orig/btree_gist/expected/timestamp.out contrib/btree_gist/expected/timestamp.out
*** contrib.orig/btree_gist/expected/timestamp.out	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/expected/timestamp.out	2009-11-26 18:06:13.000000000 +0300
***************
*** 32,37 ****
--- 32,45 ----
     289
  (1 row)
  
+ SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10-26 08:55:08' LIMIT 3;
+             a             |              ?column?              
+ --------------------------+------------------------------------
+  Tue Oct 26 08:55:08 2004 | @ 0
+  Sun Oct 31 06:35:03 2004 | @ 4 days 21 hours 39 mins 55 secs
+  Mon Nov 29 20:12:43 2004 | @ 34 days 11 hours 17 mins 35 secs
+ (3 rows)
+ 
  CREATE INDEX timestampidx ON timestamptmp USING gist ( a );
  SET enable_seqscan=off;
  SELECT count(*) FROM timestamptmp WHERE a <  '2004-10-26 08:55:08'::timestamp;
***************
*** 64,66 ****
--- 72,91 ----
     289
  (1 row)
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10-26 08:55:08' LIMIT 3;
+                                      QUERY PLAN                                      
+ -------------------------------------------------------------------------------------
+  Limit
+    ->  Index Scan using timestampidx on timestamptmp
+          Index Cond: (a <-> 'Tue Oct 26 08:55:08 2004'::timestamp without time zone)
+ (3 rows)
+ 
+ SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10-26 08:55:08' LIMIT 3;
+             a             |              ?column?              
+ --------------------------+------------------------------------
+  Tue Oct 26 08:55:08 2004 | @ 0
+  Sun Oct 31 06:35:03 2004 | @ 4 days 21 hours 39 mins 55 secs
+  Mon Nov 29 20:12:43 2004 | @ 34 days 11 hours 17 mins 35 secs
+ (3 rows)
+ 
diff -c -r -N contrib.orig/btree_gist/expected/timestamptz.out contrib/btree_gist/expected/timestamptz.out
*** contrib.orig/btree_gist/expected/timestamptz.out	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/expected/timestamptz.out	2009-11-26 18:06:13.000000000 +0300
***************
*** 92,97 ****
--- 92,105 ----
     157
  (1 row)
  
+ SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> '2018-12-18 10:59:54 GMT+2' LIMIT 3;
+               a               |             ?column?              
+ ------------------------------+-----------------------------------
+  Tue Dec 18 05:59:54 2018 PST | @ 1 hour
+  Thu Jan 10 03:01:34 2019 PST | @ 22 days 22 hours 1 min 40 secs
+  Thu Jan 24 12:28:12 2019 PST | @ 37 days 7 hours 28 mins 18 secs
+ (3 rows)
+ 
  CREATE INDEX timestamptzidx ON timestamptztmp USING gist ( a );
  SET enable_seqscan=off;
  SELECT count(*) FROM timestamptztmp WHERE a <  '2018-12-18 10:59:54 GMT+3'::timestamptz;
***************
*** 184,186 ****
--- 192,211 ----
     157
  (1 row)
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> '2018-12-18 10:59:54 GMT+2' LIMIT 3;
+                                       QUERY PLAN                                      
+ --------------------------------------------------------------------------------------
+  Limit
+    ->  Index Scan using timestamptzidx on timestamptztmp
+          Index Cond: (a <-> 'Tue Dec 18 04:59:54 2018 PST'::timestamp with time zone)
+ (3 rows)
+ 
+ SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> '2018-12-18 10:59:54 GMT+2' LIMIT 3;
+               a               |             ?column?              
+ ------------------------------+-----------------------------------
+  Tue Dec 18 05:59:54 2018 PST | @ 1 hour
+  Thu Jan 10 03:01:34 2019 PST | @ 22 days 22 hours 1 min 40 secs
+  Thu Jan 24 12:28:12 2019 PST | @ 37 days 7 hours 28 mins 18 secs
+ (3 rows)
+ 
diff -c -r -N contrib.orig/btree_gist/sql/cash.sql contrib/btree_gist/sql/cash.sql
*** contrib.orig/btree_gist/sql/cash.sql	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/sql/cash.sql	2009-11-26 17:15:30.000000000 +0300
***************
*** 16,21 ****
--- 16,23 ----
  
  SELECT count(*) FROM moneytmp WHERE a >  '22649.64';
  
+ SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3;
+ 
  CREATE INDEX moneyidx ON moneytmp USING gist ( a );
  
  SET enable_seqscan=off;
***************
*** 29,31 ****
--- 31,37 ----
  SELECT count(*) FROM moneytmp WHERE a >= '22649.64'::money;
  
  SELECT count(*) FROM moneytmp WHERE a >  '22649.64'::money;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3;
+ SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3;
diff -c -r -N contrib.orig/btree_gist/sql/date.sql contrib/btree_gist/sql/date.sql
*** contrib.orig/btree_gist/sql/date.sql	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/sql/date.sql	2009-11-26 17:16:18.000000000 +0300
***************
*** 16,21 ****
--- 16,23 ----
  
  SELECT count(*) FROM datetmp WHERE a >  '2001-02-13';
  
+ SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3;
+ 
  CREATE INDEX dateidx ON datetmp USING gist ( a );
  
  SET enable_seqscan=off;
***************
*** 30,32 ****
--- 32,37 ----
  
  SELECT count(*) FROM datetmp WHERE a >  '2001-02-13'::date;
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3;
+ SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3;
diff -c -r -N contrib.orig/btree_gist/sql/float4.sql contrib/btree_gist/sql/float4.sql
*** contrib.orig/btree_gist/sql/float4.sql	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/sql/float4.sql	2009-11-26 17:17:06.000000000 +0300
***************
*** 16,21 ****
--- 16,23 ----
  
  SELECT count(*) FROM float4tmp WHERE a >  -179.0;
  
+ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3;
+ 
  CREATE INDEX float4idx ON float4tmp USING gist ( a );
  
  SET enable_seqscan=off;
***************
*** 29,31 ****
--- 31,37 ----
  SELECT count(*) FROM float4tmp WHERE a >= -179.0::float4;
  
  SELECT count(*) FROM float4tmp WHERE a >  -179.0::float4;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3;
+ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3;
diff -c -r -N contrib.orig/btree_gist/sql/float8.sql contrib/btree_gist/sql/float8.sql
*** contrib.orig/btree_gist/sql/float8.sql	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/sql/float8.sql	2009-11-26 17:36:40.000000000 +0300
***************
*** 16,21 ****
--- 16,23 ----
  
  SELECT count(*) FROM float8tmp WHERE a >  -1890.0;
  
+ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3;
+ 
  CREATE INDEX float8idx ON float8tmp USING gist ( a );
  
  SET enable_seqscan=off;
***************
*** 29,31 ****
--- 31,37 ----
  SELECT count(*) FROM float8tmp WHERE a >= -1890.0::float8;
  
  SELECT count(*) FROM float8tmp WHERE a >  -1890.0::float8;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3;
+ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3;
diff -c -r -N contrib.orig/btree_gist/sql/int2.sql contrib/btree_gist/sql/int2.sql
*** contrib.orig/btree_gist/sql/int2.sql	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/sql/int2.sql	2009-11-26 17:36:53.000000000 +0300
***************
*** 16,21 ****
--- 16,23 ----
  
  SELECT count(*) FROM int2tmp WHERE a >  237;
  
+ SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3;
+ 
  CREATE INDEX int2idx ON int2tmp USING gist ( a );
  
  SET enable_seqscan=off;
***************
*** 30,32 ****
--- 32,37 ----
  
  SELECT count(*) FROM int2tmp WHERE a >  237::int2;
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3;
+ SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3;
diff -c -r -N contrib.orig/btree_gist/sql/int4.sql contrib/btree_gist/sql/int4.sql
*** contrib.orig/btree_gist/sql/int4.sql	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/sql/int4.sql	2009-11-26 17:37:04.000000000 +0300
***************
*** 16,21 ****
--- 16,23 ----
  
  SELECT count(*) FROM int4tmp WHERE a >  237;
  
+ SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3;
+ 
  CREATE INDEX int4idx ON int4tmp USING gist ( a );
  
  SET enable_seqscan=off;
***************
*** 30,32 ****
--- 32,37 ----
  
  SELECT count(*) FROM int4tmp WHERE a >  237::int4;
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3;
+ SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3;
diff -c -r -N contrib.orig/btree_gist/sql/int8.sql contrib/btree_gist/sql/int8.sql
*** contrib.orig/btree_gist/sql/int8.sql	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/sql/int8.sql	2009-11-26 17:37:14.000000000 +0300
***************
*** 16,21 ****
--- 16,23 ----
  
  SELECT count(*) FROM int8tmp WHERE a >  464571291354841;
  
+ SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' LIMIT 3;
+ 
  CREATE INDEX int8idx ON int8tmp USING gist ( a );
  
  SET enable_seqscan=off;
***************
*** 30,32 ****
--- 32,37 ----
  
  SELECT count(*) FROM int8tmp WHERE a >  464571291354841::int8;
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' LIMIT 3;
+ SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' LIMIT 3;
diff -c -r -N contrib.orig/btree_gist/sql/interval.sql contrib/btree_gist/sql/interval.sql
*** contrib.orig/btree_gist/sql/interval.sql	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/sql/interval.sql	2009-11-26 17:37:23.000000000 +0300
***************
*** 16,21 ****
--- 16,23 ----
  
  SELECT count(*) FROM intervaltmp WHERE a >  '199 days 21:21:23';
  
+ SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
+ 
  CREATE INDEX intervalidx ON intervaltmp USING gist ( a );
  
  SET enable_seqscan=off;
***************
*** 30,32 ****
--- 32,37 ----
  
  SELECT count(*) FROM intervaltmp WHERE a >  '199 days 21:21:23'::interval;
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
+ SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3;
diff -c -r -N contrib.orig/btree_gist/sql/time.sql contrib/btree_gist/sql/time.sql
*** contrib.orig/btree_gist/sql/time.sql	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/sql/time.sql	2009-11-26 17:37:36.000000000 +0300
***************
*** 16,21 ****
--- 16,23 ----
  
  SELECT count(*) FROM timetmp WHERE a >  '10:57:11';
  
+ SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3;
+ 
  CREATE INDEX timeidx ON timetmp USING gist ( a );
  
  SET enable_seqscan=off;
***************
*** 30,32 ****
--- 32,37 ----
  
  SELECT count(*) FROM timetmp WHERE a >  '10:57:11'::time;
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3;
+ SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3;
diff -c -r -N contrib.orig/btree_gist/sql/timestamp.sql contrib/btree_gist/sql/timestamp.sql
*** contrib.orig/btree_gist/sql/timestamp.sql	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/sql/timestamp.sql	2009-11-26 17:37:48.000000000 +0300
***************
*** 16,21 ****
--- 16,23 ----
  
  SELECT count(*) FROM timestamptmp WHERE a >  '2004-10-26 08:55:08';
  
+ SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10-26 08:55:08' LIMIT 3;
+ 
  CREATE INDEX timestampidx ON timestamptmp USING gist ( a );
  
  SET enable_seqscan=off;
***************
*** 30,32 ****
--- 32,37 ----
  
  SELECT count(*) FROM timestamptmp WHERE a >  '2004-10-26 08:55:08'::timestamp;
  
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10-26 08:55:08' LIMIT 3;
+ SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10-26 08:55:08' LIMIT 3;
diff -c -r -N contrib.orig/btree_gist/sql/timestamptz.sql contrib/btree_gist/sql/timestamptz.sql
*** contrib.orig/btree_gist/sql/timestamptz.sql	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/sql/timestamptz.sql	2009-11-26 17:38:01.000000000 +0300
***************
*** 37,43 ****
  
  SELECT count(*) FROM timestamptztmp WHERE a >  '2018-12-18 10:59:54 GMT+4';
  
! 
  
  CREATE INDEX timestamptzidx ON timestamptztmp USING gist ( a );
  
--- 37,43 ----
  
  SELECT count(*) FROM timestamptztmp WHERE a >  '2018-12-18 10:59:54 GMT+4';
  
! SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> '2018-12-18 10:59:54 GMT+2' LIMIT 3;
  
  CREATE INDEX timestamptzidx ON timestamptztmp USING gist ( a );
  
***************
*** 74,76 ****
--- 74,80 ----
  SELECT count(*) FROM timestamptztmp WHERE a >= '2018-12-18 10:59:54 GMT+4'::timestamptz;
  
  SELECT count(*) FROM timestamptztmp WHERE a >  '2018-12-18 10:59:54 GMT+4'::timestamptz;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> '2018-12-18 10:59:54 GMT+2' LIMIT 3;
+ SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> '2018-12-18 10:59:54 GMT+2' LIMIT 3;
diff -c -r -N contrib.orig/btree_gist/uninstall_btree_gist.sql contrib/btree_gist/uninstall_btree_gist.sql
*** contrib.orig/btree_gist/uninstall_btree_gist.sql	2009-11-26 11:58:36.000000000 +0300
--- contrib/btree_gist/uninstall_btree_gist.sql	2009-11-26 18:12:56.000000000 +0300
***************
*** 278,280 ****
--- 278,329 ----
  DROP TYPE gbtreekey8 CASCADE;
  
  DROP TYPE gbtreekey4 CASCADE;
+ 
+ DROP OPERATOR <->(money, money);
+ 
+ DROP OPERATOR <->(date, date);
+ 
+ DROP OPERATOR <->(float4, float4);
+ 
+ DROP OPERATOR <->(float8, float8);
+ 
+ DROP OPERATOR <->(int2, int2);
+ 
+ DROP OPERATOR <->(int4, int4);
+ 
+ DROP OPERATOR <->(int8, int8);
+ 
+ DROP OPERATOR <->(interval, interval);
+ 
+ DROP OPERATOR <->(oid, oid);
+ 
+ DROP OPERATOR <->(time, time);
+ 
+ DROP OPERATOR <->(timestamp, timestamp);
+ 
+ DROP OPERATOR <->(timestamptz, timestamptz);
+ 
+ DROP FUNCTION cash_dist(money, money);
+ 
+ DROP FUNCTION date_dist(date, date);
+ 
+ DROP FUNCTION float4_dist(float4, float4);
+ 
+ DROP FUNCTION float8_dist(float8, float8);
+ 
+ DROP FUNCTION int2_dist(int2, int2);
+ 
+ DROP FUNCTION int4_dist(int4, int4);
+ 
+ DROP FUNCTION int8_dist(int8, int8);
+ 
+ DROP FUNCTION interval_dist(interval, interval);
+ 
+ DROP FUNCTION oid_dist(oid, oid);
+ 
+ DROP FUNCTION time_dist(time, time);
+ 
+ DROP FUNCTION ts_dist(timestamp, timestamp);
+ 
+ DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+ 
diff -c -r -N contrib.orig/pg_trgm/expected/pg_trgm.out contrib/pg_trgm/expected/pg_trgm.out
*** contrib.orig/pg_trgm/expected/pg_trgm.out	2009-11-26 11:58:37.000000000 +0300
--- contrib/pg_trgm/expected/pg_trgm.out	2009-11-26 14:01:27.000000000 +0300
***************
*** 1187,1192 ****
--- 1187,1199 ----
   qwertyu0988 | 0.333333
  (1 row)
  
+ select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
+  ?column? |      t      
+ ----------+-------------
+  0.411765 | qwertyu0988
+       0.5 | qwertyu0987
+ (2 rows)
+ 
  create index trgm_idx on test_trgm using gist (t gist_trgm_ops);
  set enable_seqscan=off;
  select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t;
***************
*** 2315,2320 ****
--- 2322,2343 ----
   qwertyu0988 | 0.333333
  (1 row)
  
+ explain (costs off)
+ select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
+                      QUERY PLAN                      
+ -----------------------------------------------------
+  Limit
+    ->  Index Scan using trgm_idx on test_trgm
+          Index Cond: (t <-> 'q0987wertyu0988'::text)
+ (3 rows)
+ 
+ select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
+  ?column? |      t      
+ ----------+-------------
+  0.411765 | qwertyu0988
+       0.5 | qwertyu0987
+ (2 rows)
+ 
  drop index trgm_idx;
  create index trgm_idx on test_trgm using gin (t gin_trgm_ops);
  set enable_seqscan=off;
diff -c -r -N contrib.orig/pg_trgm/pg_trgm.sql.in contrib/pg_trgm/pg_trgm.sql.in
*** contrib.orig/pg_trgm/pg_trgm.sql.in	2009-11-26 11:58:37.000000000 +0300
--- contrib/pg_trgm/pg_trgm.sql.in	2009-11-26 12:03:14.000000000 +0300
***************
*** 37,42 ****
--- 37,54 ----
          JOIN = contjoinsel
  );
  
+ CREATE OR REPLACE FUNCTION similarity_dist(text,text)
+ RETURNS float4
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C STRICT STABLE;
+ 
+ CREATE OPERATOR <-> (
+         LEFTARG = text,
+         RIGHTARG = text,
+         PROCEDURE = similarity_dist,
+         COMMUTATOR = '<->'
+ );
+ 
  -- gist key
  CREATE OR REPLACE FUNCTION gtrgm_in(cstring)
  RETURNS gtrgm
***************
*** 56,62 ****
  
  -- support functions for gist
  CREATE OR REPLACE FUNCTION gtrgm_consistent(internal,text,int,oid,internal)
! RETURNS bool
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
   
--- 68,74 ----
  
  -- support functions for gist
  CREATE OR REPLACE FUNCTION gtrgm_consistent(internal,text,int,oid,internal)
! RETURNS float8
  AS 'MODULE_PATHNAME'
  LANGUAGE C IMMUTABLE STRICT;
   
***************
*** 95,100 ****
--- 107,113 ----
  FOR TYPE text USING gist
  AS
          OPERATOR        1       % (text, text),
+         OPERATOR        2       <-> (text, text),
          FUNCTION        1       gtrgm_consistent (internal, text, int, oid, internal),
          FUNCTION        2       gtrgm_union (bytea, internal),
          FUNCTION        3       gtrgm_compress (internal),
diff -c -r -N contrib.orig/pg_trgm/sql/pg_trgm.sql contrib/pg_trgm/sql/pg_trgm.sql
*** contrib.orig/pg_trgm/sql/pg_trgm.sql	2009-11-26 11:58:37.000000000 +0300
--- contrib/pg_trgm/sql/pg_trgm.sql	2009-11-26 13:59:31.000000000 +0300
***************
*** 26,31 ****
--- 26,32 ----
  select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t;
  select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t;
  select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t;
+ select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
  
  create index trgm_idx on test_trgm using gist (t gist_trgm_ops);
  set enable_seqscan=off;
***************
*** 33,38 ****
--- 34,42 ----
  select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t;
  select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t;
  select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t;
+ explain (costs off)
+ select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
+ select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
  
  drop index trgm_idx;
  create index trgm_idx on test_trgm using gin (t gin_trgm_ops);
diff -c -r -N contrib.orig/pg_trgm/trgm_gist.c contrib/pg_trgm/trgm_gist.c
*** contrib.orig/pg_trgm/trgm_gist.c	2009-11-26 11:58:37.000000000 +0300
--- contrib/pg_trgm/trgm_gist.c	2009-11-26 14:02:02.000000000 +0300
***************
*** 5,10 ****
--- 5,11 ----
  
  #include "access/gist.h"
  #include "access/itup.h"
+ #include "access/skey.h"
  #include "access/tuptoaster.h"
  #include "storage/bufpage.h"
  #include "utils/array.h"
***************
*** 159,176 ****
  	}
  }
  
  Datum
  gtrgm_consistent(PG_FUNCTION_ARGS)
  {
  	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  	text	   *query = PG_GETARG_TEXT_P(1);
! 
! 	/* StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); */
  	/* Oid		subtype = PG_GETARG_OID(3); */
  	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
  	TRGM	   *key = (TRGM *) DatumGetPointer(entry->key);
  	TRGM	   *qtrg;
! 	bool		res = false;
  	char	   *cache = (char *) fcinfo->flinfo->fn_extra;
  
  	/* All cases served by this function are exact */
--- 160,197 ----
  	}
  }
  
+ #define	SimilarityStrategyNumber	1
+ #define	DistanceStrategyNumber		2
+ 
+ static int4
+ cnt_sml_sign_common(TRGM *qtrg, BITVECP sign)
+ {
+ 	int4		count = 0;
+ 	int4		k,
+ 				len = ARRNELEM(qtrg);
+ 	trgm	   *ptr = GETARR(qtrg);
+ 	int4		tmp = 0;
+ 
+ 	for (k = 0; k < len; k++)
+ 	{
+ 		CPTRGM(((char *) &tmp), ptr + k);
+ 		count += GETBIT(sign, HASHVAL(tmp));
+ 	}
+ 
+ 	return count;
+ }
+ 
  Datum
  gtrgm_consistent(PG_FUNCTION_ARGS)
  {
  	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  	text	   *query = PG_GETARG_TEXT_P(1);
! 	StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
  	/* Oid		subtype = PG_GETARG_OID(3); */
  	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
  	TRGM	   *key = (TRGM *) DatumGetPointer(entry->key);
  	TRGM	   *qtrg;
! 	float8		res;
  	char	   *cache = (char *) fcinfo->flinfo->fn_extra;
  
  	/* All cases served by this function are exact */
***************
*** 193,231 ****
  
  	qtrg = (TRGM *) (cache + MAXALIGN(VARSIZE(query)));
  
! 	if (GIST_LEAF(entry))
! 	{							/* all leafs contains orig trgm */
! 		float4		tmpsml = cnt_sml(key, qtrg);
  
! 		/* strange bug at freebsd 5.2.1 and gcc 3.3.3 */
! 		res = (*(int *) &tmpsml == *(int *) &trgm_limit || tmpsml > trgm_limit) ? true : false;
! 	}
! 	else if (ISALLTRUE(key))
! 	{							/* non-leaf contains signature */
! 		res = true;
! 	}
! 	else
! 	{							/* non-leaf contains signature */
! 		int4		count = 0;
! 		int4		k,
! 					len = ARRNELEM(qtrg);
! 		trgm	   *ptr = GETARR(qtrg);
! 		BITVECP		sign = GETSIGN(key);
! 		int4		tmp = 0;
  
! 		for (k = 0; k < len; k++)
! 		{
! 			CPTRGM(((char *) &tmp), ptr + k);
! 			count += GETBIT(sign, HASHVAL(tmp));
! 		}
! #ifdef DIVUNION
! 		res = (len == count) ? true : ((((((float4) count) / ((float4) (len - count)))) >= trgm_limit) ? true : false);
! #else
! 		res = (len == 0) ? false : ((((((float4) count) / ((float4) len))) >= trgm_limit) ? true : false);
! #endif
  	}
  
! 	PG_RETURN_BOOL(res);
  }
  
  static int4
--- 214,266 ----
  
  	qtrg = (TRGM *) (cache + MAXALIGN(VARSIZE(query)));
  
! 	switch(strategy)
! 	{
! 		case SimilarityStrategyNumber:
! 			if (GIST_LEAF(entry))
! 			{							/* all leafs contains orig trgm */
! 				float4      tmpsml = cnt_sml(key, qtrg);
  
! 				/* strange bug at freebsd 5.2.1 and gcc 3.3.3 */
! 				res = (*(int *) &tmpsml == *(int *) &trgm_limit || tmpsml > trgm_limit) ? 0.0 : -1.0;
! 			}
! 			else if (ISALLTRUE(key))
! 			{							/* non-leaf contains signature */
! 				res = 0.0; /*true!*/
! 			}
! 			else
! 			{							/* non-leaf contains signature */
! 				int4 count = cnt_sml_sign_common(qtrg, GETSIGN(key));
! 				int4 len = ARRNELEM(qtrg);
! 
! 				if (len == 0)
! 					res = -1.0;
! 				else
! 					res = (((((float8) count) / ((float8) len))) >= trgm_limit) ? 0.0 : -1.0;
! 			}
! 			break;
! 		case DistanceStrategyNumber:
! 			if (GIST_LEAF(entry))
! 			{							/* all leafs contains orig trgm */
! 				res = 1.0 - cnt_sml(key, qtrg);
! 			}
! 			else if (ISALLTRUE(key))
! 			{							/* all leafs contains orig trgm */
! 				res = 0.0; 
! 			}
! 			else
! 			{							/* non-leaf contains signature */
! 				int4 count = cnt_sml_sign_common(qtrg, GETSIGN(key));
! 				int4 len = ARRNELEM(qtrg);
  
! 				res = (len == 0) ? -1.0 : 1.0 - ((float8) count) / ((float8) len);
! 			}
! 			break;
! 		default:
! 			elog(ERROR, "Unknown strategy number: %d", strategy);
  	}
  
! 	PG_RETURN_FLOAT8(res);
  }
  
  static int4
diff -c -r -N contrib.orig/pg_trgm/trgm_op.c contrib/pg_trgm/trgm_op.c
*** contrib.orig/pg_trgm/trgm_op.c	2009-11-26 11:58:37.000000000 +0300
--- contrib/pg_trgm/trgm_op.c	2009-11-26 13:20:13.000000000 +0300
***************
*** 359,364 ****
--- 359,377 ----
  	PG_RETURN_FLOAT4(res);
  }
  
+ PG_FUNCTION_INFO_V1(similarity_dist);
+ Datum		similarity_dist(PG_FUNCTION_ARGS);
+ Datum
+ similarity_dist(PG_FUNCTION_ARGS)
+ {
+ 	float4		res = DatumGetFloat4(DirectFunctionCall2(
+ 														 similarity,
+ 														 PG_GETARG_DATUM(0),
+ 														 PG_GETARG_DATUM(1)
+ 														 ));
+ 	PG_RETURN_FLOAT4( 1.0 - res );
+ }
+ 
  PG_FUNCTION_INFO_V1(similarity_op);
  Datum		similarity_op(PG_FUNCTION_ARGS);
  Datum
diff -c -r -N contrib.orig/pg_trgm/uninstall_pg_trgm.sql contrib/pg_trgm/uninstall_pg_trgm.sql
*** contrib.orig/pg_trgm/uninstall_pg_trgm.sql	2009-11-26 11:58:37.000000000 +0300
--- contrib/pg_trgm/uninstall_pg_trgm.sql	2009-11-26 17:08:32.000000000 +0300
***************
*** 33,38 ****
--- 33,42 ----
  
  DROP FUNCTION similarity_op(text,text);
  
+ DROP OPERATOR <-> (text, text);
+ 
+ DROP FUNCTION similarity_dist(text,text);
+ 
  DROP FUNCTION similarity(text,text);
  
  DROP FUNCTION show_trgm(text);
