partial-knn-1.patch

application/octet-stream

Filename: partial-knn-1.patch
Type: application/octet-stream
Part: 1
Message: PoC: Partial sort

Patch

Format: unified
File+
src/backend/access/gist/gistget.c 0 0
src/backend/access/gist/gistproc.c 0 0
src/backend/access/gist/gistscan.c 0 0
src/backend/utils/adt/geo_ops.c 0 0
src/include/access/gist_private.h 0 0
src/include/catalog/pg_amop.h 0 0
src/include/catalog/pg_amproc.h 0 0
src/include/catalog/pg_operator.h 0 0
src/include/catalog/pg_proc.h 0 0
src/include/utils/geo_decls.h 0 0
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
new file mode 100644
index e97ab8f..6ad5677
*** a/src/backend/access/gist/gistget.c
--- b/src/backend/access/gist/gistget.c
***************
*** 16,21 ****
--- 16,22 ----
  
  #include "access/gist_private.h"
  #include "access/relscan.h"
+ #include "catalog/index.h"
  #include "miscadmin.h"
  #include "pgstat.h"
  #include "utils/builtins.h"
*************** gistindex_keytest(IndexScanDesc scan,
*** 55,61 ****
  	GISTSTATE  *giststate = so->giststate;
  	ScanKey		key = scan->keyData;
  	int			keySize = scan->numberOfKeys;
! 	double	   *distance_p;
  	Relation	r = scan->indexRelation;
  
  	*recheck_p = false;
--- 56,62 ----
  	GISTSTATE  *giststate = so->giststate;
  	ScanKey		key = scan->keyData;
  	int			keySize = scan->numberOfKeys;
! 	GISTSearchTreeItemDistance *distance_p;
  	Relation	r = scan->indexRelation;
  
  	*recheck_p = false;
*************** gistindex_keytest(IndexScanDesc scan,
*** 72,78 ****
  		if (GistPageIsLeaf(page))		/* shouldn't happen */
  			elog(ERROR, "invalid GiST tuple found on leaf page");
  		for (i = 0; i < scan->numberOfOrderBys; i++)
! 			so->distances[i] = -get_float8_infinity();
  		return true;
  	}
  
--- 73,82 ----
  		if (GistPageIsLeaf(page))		/* shouldn't happen */
  			elog(ERROR, "invalid GiST tuple found on leaf page");
  		for (i = 0; i < scan->numberOfOrderBys; i++)
! 		{
! 			so->distances[i].value = -get_float8_infinity();
! 			so->distances[i].recheck = false;
! 		}
  		return true;
  	}
  
*************** gistindex_keytest(IndexScanDesc scan,
*** 170,176 ****
  		if ((key->sk_flags & SK_ISNULL) || isNull)
  		{
  			/* Assume distance computes as null and sorts to the end */
! 			*distance_p = get_float8_infinity();
  		}
  		else
  		{
--- 174,181 ----
  		if ((key->sk_flags & SK_ISNULL) || isNull)
  		{
  			/* Assume distance computes as null and sorts to the end */
! 			distance_p->value = get_float8_infinity();
! 			distance_p->recheck = false;
  		}
  		else
  		{
*************** gistindex_keytest(IndexScanDesc scan,
*** 195,208 ****
  			 * can't tolerate lossy distance calculations on leaf tuples;
  			 * there is no opportunity to re-sort the tuples afterwards.
  			 */
! 			dist = FunctionCall4Coll(&key->sk_func,
  									 key->sk_collation,
  									 PointerGetDatum(&de),
  									 key->sk_argument,
  									 Int32GetDatum(key->sk_strategy),
! 									 ObjectIdGetDatum(key->sk_subtype));
  
! 			*distance_p = DatumGetFloat8(dist);
  		}
  
  		key++;
--- 200,215 ----
  			 * can't tolerate lossy distance calculations on leaf tuples;
  			 * there is no opportunity to re-sort the tuples afterwards.
  			 */
! 			distance_p->recheck = false;
! 			dist = FunctionCall5Coll(&key->sk_func,
  									 key->sk_collation,
  									 PointerGetDatum(&de),
  									 key->sk_argument,
  									 Int32GetDatum(key->sk_strategy),
! 									 ObjectIdGetDatum(key->sk_subtype),
! 									 PointerGetDatum(&distance_p->recheck));
  
! 			distance_p->value = DatumGetFloat8(dist);
  		}
  
  		key++;
*************** gistindex_keytest(IndexScanDesc scan,
*** 234,240 ****
   * sibling will be processed next.
   */
  static void
! gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
  			 TIDBitmap *tbm, int64 *ntids)
  {
  	GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
--- 241,247 ----
   * sibling will be processed next.
   */
  static void
! gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, GISTSearchTreeItemDistance *myDistances,
  			 TIDBitmap *tbm, int64 *ntids)
  {
  	GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
*************** gistScanPage(IndexScanDesc scan, GISTSea
*** 284,290 ****
  		tmpItem->head = item;
  		tmpItem->lastHeap = NULL;
  		memcpy(tmpItem->distances, myDistances,
! 			   sizeof(double) * scan->numberOfOrderBys);
  
  		(void) rb_insert(so->queue, (RBNode *) tmpItem, &isNew);
  
--- 291,297 ----
  		tmpItem->head = item;
  		tmpItem->lastHeap = NULL;
  		memcpy(tmpItem->distances, myDistances,
! 			   sizeof(GISTSearchTreeItemDistance) * scan->numberOfOrderBys);
  
  		(void) rb_insert(so->queue, (RBNode *) tmpItem, &isNew);
  
*************** gistScanPage(IndexScanDesc scan, GISTSea
*** 375,381 ****
  			tmpItem->head = item;
  			tmpItem->lastHeap = GISTSearchItemIsHeap(*item) ? item : NULL;
  			memcpy(tmpItem->distances, so->distances,
! 				   sizeof(double) * scan->numberOfOrderBys);
  
  			(void) rb_insert(so->queue, (RBNode *) tmpItem, &isNew);
  
--- 382,388 ----
  			tmpItem->head = item;
  			tmpItem->lastHeap = GISTSearchItemIsHeap(*item) ? item : NULL;
  			memcpy(tmpItem->distances, so->distances,
! 				   sizeof(GISTSearchTreeItemDistance) * scan->numberOfOrderBys);
  
  			(void) rb_insert(so->queue, (RBNode *) tmpItem, &isNew);
  
*************** gistScanPage(IndexScanDesc scan, GISTSea
*** 387,392 ****
--- 394,485 ----
  }
  
  /*
+  * Do this tree item distance values needs recheck?
+  */
+ static bool
+ searchTreeItemNeedDistanceRecheck(IndexScanDesc scan, GISTSearchTreeItem *item)
+ {
+ 	int i;
+ 	for (i = 0; i < scan->numberOfOrderBys; i++)
+ 	{
+ 		if (item->distances[i].recheck)
+ 			return true;
+ 	}
+ 	return false;
+ }
+ 
+ /*
+  * Recheck distance values of item from heap and reinsert it into RB-tree.
+  */
+ static void
+ searchTreeItemDistanceRecheck(IndexScanDesc scan, GISTSearchTreeItem *treeItem,
+ 		GISTSearchItem *item)
+ {
+ 	GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
+ 	GISTSearchTreeItem *tmpItem = so->tmpTreeItem;
+ 	Buffer	buffer;
+ 	bool	got_heap_tuple, all_dead;
+ 	HeapTupleData tup;
+ 	Datum	values[INDEX_MAX_KEYS];
+ 	bool	isnull[INDEX_MAX_KEYS];
+ 	bool	isNew;
+ 	int		i;
+ 
+ 	buffer = ReadBuffer(scan->heapRelation,
+ 			ItemPointerGetBlockNumber(&item->data.heap.heapPtr));
+ 	LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 	got_heap_tuple = heap_hot_search_buffer(&item->data.heap.heapPtr,
+ 											scan->heapRelation,
+ 											buffer,
+ 											scan->xs_snapshot,
+ 											&tup,
+ 											&all_dead,
+ 											true);
+ 	if (!got_heap_tuple)
+ 	{
+ 		UnlockReleaseBuffer(buffer);
+ 		pfree(item);
+ 		return;
+ 	}
+ 
+ 	memcpy(tmpItem, treeItem,  GSTIHDRSZ +
+ 			sizeof(GISTSearchTreeItemDistance) * scan->numberOfOrderBys);
+ 	tmpItem->head = item;
+ 	tmpItem->lastHeap = item;
+ 	item->next = NULL;
+ 
+ 	ExecStoreTuple(&tup, so->slot, InvalidBuffer, false);
+ 	FormIndexDatum(so->indexInfo, so->slot, so->estate, values, isnull);
+ 
+ 	for (i = 0; i < scan->numberOfOrderBys; i++)
+ 	{
+ 		if (tmpItem->distances[i].recheck)
+ 		{
+ 			ScanKey	key = scan->orderByData + i;
+ 			float8 newDistance;
+ 
+ 			tmpItem->distances[i].recheck = false;
+ 			if (isnull[key->sk_attno - 1])
+ 			{
+ 				tmpItem->distances[i].value = -get_float8_infinity();
+ 				continue;
+ 			}
+ 
+ 			newDistance = DatumGetFloat8(
+ 				FunctionCall2Coll(&so->orderByRechecks[i],
+ 					 key->sk_collation,
+ 					 values[key->sk_attno - 1],
+ 					 key->sk_argument));
+ 
+ 			tmpItem->distances[i].value = newDistance;
+ 
+ 		}
+ 	}
+ 	(void) rb_insert(so->queue, (RBNode *) tmpItem, &isNew);
+ 	UnlockReleaseBuffer(buffer);
+ }
+ 
+ /*
   * Extract next item (in order) from search queue
   *
   * Returns a GISTSearchItem or NULL.  Caller must pfree item when done with it.
*************** gistScanPage(IndexScanDesc scan, GISTSea
*** 396,403 ****
   * the distances value for the item.
   */
  static GISTSearchItem *
! getNextGISTSearchItem(GISTScanOpaque so)
  {
  	for (;;)
  	{
  		GISTSearchItem *item;
--- 489,498 ----
   * the distances value for the item.
   */
  static GISTSearchItem *
! getNextGISTSearchItem(IndexScanDesc scan)
  {
+ 	GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
+ 
  	for (;;)
  	{
  		GISTSearchItem *item;
*************** getNextGISTSearchItem(GISTScanOpaque so)
*** 418,423 ****
--- 513,526 ----
  			so->curTreeItem->head = item->next;
  			if (item == so->curTreeItem->lastHeap)
  				so->curTreeItem->lastHeap = NULL;
+ 
+ 			/* Recheck distance from heap tuple if needed */
+ 			if (GISTSearchItemIsHeap(*item) &&
+ 				searchTreeItemNeedDistanceRecheck(scan, so->curTreeItem))
+ 			{
+ 				searchTreeItemDistanceRecheck(scan, so->curTreeItem, item);
+ 				continue;
+ 			}
  			/* Return item; caller is responsible to pfree it */
  			return item;
  		}
*************** getNextNearest(IndexScanDesc scan)
*** 441,447 ****
  
  	do
  	{
! 		GISTSearchItem *item = getNextGISTSearchItem(so);
  
  		if (!item)
  			break;
--- 544,550 ----
  
  	do
  	{
! 		GISTSearchItem *item = getNextGISTSearchItem(scan);
  
  		if (!item)
  			break;
*************** gistgettuple(PG_FUNCTION_ARGS)
*** 521,527 ****
  			/* find and process the next index page */
  			do
  			{
! 				GISTSearchItem *item = getNextGISTSearchItem(so);
  
  				if (!item)
  					PG_RETURN_BOOL(false);
--- 624,630 ----
  			/* find and process the next index page */
  			do
  			{
! 				GISTSearchItem *item = getNextGISTSearchItem(scan);
  
  				if (!item)
  					PG_RETURN_BOOL(false);
*************** gistgetbitmap(PG_FUNCTION_ARGS)
*** 573,579 ****
  	 */
  	for (;;)
  	{
! 		GISTSearchItem *item = getNextGISTSearchItem(so);
  
  		if (!item)
  			break;
--- 676,682 ----
  	 */
  	for (;;)
  	{
! 		GISTSearchItem *item = getNextGISTSearchItem(scan);
  
  		if (!item)
  			break;
diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c
new file mode 100644
index 3a45781..afe447f
*** a/src/backend/access/gist/gistproc.c
--- b/src/backend/access/gist/gistproc.c
*************** gist_poly_consistent(PG_FUNCTION_ARGS)
*** 1094,1099 ****
--- 1094,1100 ----
  	PG_RETURN_BOOL(result);
  }
  
+ 
  /**************************************************
   * Circle ops
   **************************************************/
*************** computeDistance(bool isLeaf, BOX *box, P
*** 1270,1275 ****
--- 1271,1337 ----
  	return result;
  }
  
+ static double
+ computeDistanceMBR(BOX *box, Point *point)
+ {
+ 	double		result = 0.0;
+ 
+ 	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, "inconsistent point values");
+ 	}
+ 	else if (point->y <= box->high.y && point->y >= box->low.y)
+ 	{
+ 		/* point is to left or right of 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, "inconsistent point values");
+ 	}
+ 	else
+ 	{
+ 		/* closest point will be a vertex */
+ 		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)
*************** gist_point_distance(PG_FUNCTION_ARGS)
*** 1451,1453 ****
--- 1513,1540 ----
  
  	PG_RETURN_FLOAT8(distance);
  }
+ 
+ Datum
+ gist_poly_distance(PG_FUNCTION_ARGS)
+ {
+ 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ 	StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+ 	bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ 	double		distance;
+ 	StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
+ 
+ 	*recheck = true;
+ 
+ 	switch (strategyGroup)
+ 	{
+ 		case PointStrategyNumberGroup:
+ 			distance = computeDistanceMBR(DatumGetBoxP(entry->key),
+ 									   PG_GETARG_POINT_P(1));
+ 			break;
+ 		default:
+ 			elog(ERROR, "unknown strategy number: %d", strategy);
+ 			distance = 0.0;		/* keep compiler quiet */
+ 	}
+ 
+ 	PG_RETURN_FLOAT8(distance);
+ }
diff --git a/src/backend/access/gist/gistscan.c b/src/backend/access/gist/gistscan.c
new file mode 100644
index b5553ff..61e5597
*** a/src/backend/access/gist/gistscan.c
--- b/src/backend/access/gist/gistscan.c
***************
*** 17,22 ****
--- 17,25 ----
  #include "access/gist_private.h"
  #include "access/gistscan.h"
  #include "access/relscan.h"
+ #include "catalog/index.h"
+ #include "executor/executor.h"
+ #include "executor/tuptable.h"
  #include "utils/memutils.h"
  #include "utils/rel.h"
  
*************** GISTSearchTreeItemComparator(const RBNod
*** 36,43 ****
  	/* Order according to distance comparison */
  	for (i = 0; i < scan->numberOfOrderBys; i++)
  	{
! 		if (sa->distances[i] != sb->distances[i])
! 			return (sa->distances[i] > sb->distances[i]) ? 1 : -1;
  	}
  
  	return 0;
--- 39,53 ----
  	/* Order according to distance comparison */
  	for (i = 0; i < scan->numberOfOrderBys; i++)
  	{
! 		if (sa->distances[i].value != sb->distances[i].value)
! 			return (sa->distances[i].value > sb->distances[i].value) ? 1 : -1;
! 
! 		/*
! 		 * Items without recheck can be immediately returned. So they are
! 		 * placed first.
! 		 */
! 		if (sa->distances[i].recheck != sb->distances[i].recheck)
! 			return sa->distances[i].recheck ? 1 : -1;
  	}
  
  	return 0;
*************** GISTSearchTreeItemAllocator(void *arg)
*** 83,89 ****
  {
  	IndexScanDesc scan = (IndexScanDesc) arg;
  
! 	return palloc(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys);
  }
  
  static void
--- 93,99 ----
  {
  	IndexScanDesc scan = (IndexScanDesc) arg;
  
! 	return palloc(GSTIHDRSZ + sizeof(GISTSearchTreeItemDistance) * scan->numberOfOrderBys);
  }
  
  static void
*************** gistbeginscan(PG_FUNCTION_ARGS)
*** 127,136 ****
  	so->queueCxt = giststate->scanCxt;	/* see gistrescan */
  
  	/* workspaces with size dependent on numberOfOrderBys: */
! 	so->tmpTreeItem = palloc(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys);
! 	so->distances = palloc(sizeof(double) * scan->numberOfOrderBys);
  	so->qual_ok = true;			/* in case there are zero keys */
  
  	scan->opaque = so;
  
  	MemoryContextSwitchTo(oldCxt);
--- 137,153 ----
  	so->queueCxt = giststate->scanCxt;	/* see gistrescan */
  
  	/* workspaces with size dependent on numberOfOrderBys: */
! 	so->tmpTreeItem = palloc(GSTIHDRSZ + sizeof(GISTSearchTreeItemDistance) * scan->numberOfOrderBys);
! 	so->distances = palloc(sizeof(GISTSearchTreeItemDistance) * scan->numberOfOrderBys);
  	so->qual_ok = true;			/* in case there are zero keys */
  
+ 	if (scan->numberOfOrderBys > 0)
+ 	{
+ 		so->orderByRechecks = (FmgrInfo *)palloc(sizeof(FmgrInfo) * scan->numberOfOrderBys);
+ 		so->indexInfo = BuildIndexInfo(scan->indexRelation);
+ 		so->estate = CreateExecutorState();
+ 	}
+ 
  	scan->opaque = so;
  
  	MemoryContextSwitchTo(oldCxt);
*************** gistrescan(PG_FUNCTION_ARGS)
*** 186,194 ****
  		first_time = false;
  	}
  
  	/* create new, empty RBTree for search queue */
  	oldCxt = MemoryContextSwitchTo(so->queueCxt);
! 	so->queue = rb_create(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys,
  						  GISTSearchTreeItemComparator,
  						  GISTSearchTreeItemCombiner,
  						  GISTSearchTreeItemAllocator,
--- 203,216 ----
  		first_time = false;
  	}
  
+ 	if (scan->numberOfOrderBys > 0 && !so->slot)
+ 	{
+ 		so->slot = MakeSingleTupleTableSlot(RelationGetDescr(scan->heapRelation));
+ 	}
+ 
  	/* create new, empty RBTree for search queue */
  	oldCxt = MemoryContextSwitchTo(so->queueCxt);
! 	so->queue = rb_create(GSTIHDRSZ + sizeof(GISTSearchTreeItemDistance) * scan->numberOfOrderBys,
  						  GISTSearchTreeItemComparator,
  						  GISTSearchTreeItemCombiner,
  						  GISTSearchTreeItemAllocator,
*************** gistrescan(PG_FUNCTION_ARGS)
*** 289,294 ****
--- 311,319 ----
  					 GIST_DISTANCE_PROC, skey->sk_attno,
  					 RelationGetRelationName(scan->indexRelation));
  
+ 			fmgr_info_copy(&so->orderByRechecks[i], &(skey->sk_func),
+ 													so->giststate->scanCxt);
+ 
  			fmgr_info_copy(&(skey->sk_func), finfo, so->giststate->scanCxt);
  
  			/* Restore prior fn_extra pointers, if not first time */
*************** gistendscan(PG_FUNCTION_ARGS)
*** 323,328 ****
--- 348,356 ----
  	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
  	GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
  
+ 	if (so->slot)
+ 		ExecDropSingleTupleTableSlot(so->slot);
+ 
  	/*
  	 * freeGISTstate is enough to clean up everything made by gistbeginscan,
  	 * as well as the queueCxt if there is a separate context for it.
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
new file mode 100644
index 41178a6..16b60fe
*** a/src/backend/utils/adt/geo_ops.c
--- b/src/backend/utils/adt/geo_ops.c
*************** dist_cpoly(PG_FUNCTION_ARGS)
*** 2664,2669 ****
--- 2664,2715 ----
  	PG_RETURN_FLOAT8(result);
  }
  
+ Datum
+ dist_polyp(PG_FUNCTION_ARGS)
+ {
+ 	POLYGON    *poly = PG_GETARG_POLYGON_P(0);
+ 	Point	   *point = PG_GETARG_POINT_P(1);
+ 	float8		result;
+ 	float8		d;
+ 	int			i;
+ 	LSEG		seg;
+ 
+ 	if (point_inside(point, poly->npts, poly->p) != 0)
+ 	{
+ #ifdef GEODEBUG
+ 		printf("dist_polyp- point inside of polygon\n");
+ #endif
+ 		PG_RETURN_FLOAT8(0.0);
+ 	}
+ 
+ 	/* initialize distance with segment between first and last points */
+ 	seg.p[0].x = poly->p[0].x;
+ 	seg.p[0].y = poly->p[0].y;
+ 	seg.p[1].x = poly->p[poly->npts - 1].x;
+ 	seg.p[1].y = poly->p[poly->npts - 1].y;
+ 	result = dist_ps_internal(point, &seg);
+ #ifdef GEODEBUG
+ 	printf("dist_polyp- segment 0/n distance is %f\n", result);
+ #endif
+ 
+ 	/* check distances for other segments */
+ 	for (i = 0; (i < poly->npts - 1); i++)
+ 	{
+ 		seg.p[0].x = poly->p[i].x;
+ 		seg.p[0].y = poly->p[i].y;
+ 		seg.p[1].x = poly->p[i + 1].x;
+ 		seg.p[1].y = poly->p[i + 1].y;
+ 		d = dist_ps_internal(point, &seg);
+ #ifdef GEODEBUG
+ 		printf("dist_polyp- segment %d distance is %f\n", (i + 1), d);
+ #endif
+ 		if (d < result)
+ 			result = d;
+ 	}
+ 
+ 	PG_RETURN_FLOAT8(result);
+ }
+ 
  
  /*---------------------------------------------------------------------
   *		interpt_
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
new file mode 100644
index cae6dbc..b2572df
*** a/src/include/access/gist_private.h
--- b/src/include/access/gist_private.h
***************
*** 16,22 ****
--- 16,24 ----
  
  #include "access/gist.h"
  #include "access/itup.h"
+ #include "executor/tuptable.h"
  #include "fmgr.h"
+ #include "nodes/execnodes.h"
  #include "storage/bufmgr.h"
  #include "storage/buffile.h"
  #include "utils/rbtree.h"
*************** typedef struct GISTSearchItem
*** 119,124 ****
--- 121,132 ----
  
  #define GISTSearchItemIsHeap(item)	((item).blkno == InvalidBlockNumber)
  
+ typedef struct GISTSearchTreeItemDistance
+ {
+ 	double	value;
+ 	bool	recheck;
+ } GISTSearchTreeItemDistance;
+ 
  /*
   * Within a GISTSearchTreeItem's chain, heap items always appear before
   * index-page items, since we want to visit heap items first.  lastHeap points
*************** typedef struct GISTSearchTreeItem
*** 129,135 ****
  	RBNode		rbnode;			/* this is an RBTree item */
  	GISTSearchItem *head;		/* first chain member */
  	GISTSearchItem *lastHeap;	/* last heap-tuple member, if any */
! 	double		distances[1];	/* array with numberOfOrderBys entries */
  } GISTSearchTreeItem;
  
  #define GSTIHDRSZ offsetof(GISTSearchTreeItem, distances)
--- 137,143 ----
  	RBNode		rbnode;			/* this is an RBTree item */
  	GISTSearchItem *head;		/* first chain member */
  	GISTSearchItem *lastHeap;	/* last heap-tuple member, if any */
! 	GISTSearchTreeItemDistance	distances[1];	/* array with numberOfOrderBys entries */
  } GISTSearchTreeItem;
  
  #define GSTIHDRSZ offsetof(GISTSearchTreeItem, distances)
*************** typedef struct GISTScanOpaqueData
*** 149,160 ****
  
  	/* pre-allocated workspace arrays */
  	GISTSearchTreeItem *tmpTreeItem;	/* workspace to pass to rb_insert */
! 	double	   *distances;		/* output area for gistindex_keytest */
  
  	/* In a non-ordered search, returnable heap items are stored here: */
  	GISTSearchHeapItem pageData[BLCKSZ / sizeof(IndexTupleData)];
  	OffsetNumber nPageData;		/* number of valid items in array */
  	OffsetNumber curPageData;	/* next item to return */
  } GISTScanOpaqueData;
  
  typedef GISTScanOpaqueData *GISTScanOpaque;
--- 157,172 ----
  
  	/* pre-allocated workspace arrays */
  	GISTSearchTreeItem *tmpTreeItem;	/* workspace to pass to rb_insert */
! 	GISTSearchTreeItemDistance	*distances;		/* output area for gistindex_keytest */
  
  	/* In a non-ordered search, returnable heap items are stored here: */
  	GISTSearchHeapItem pageData[BLCKSZ / sizeof(IndexTupleData)];
  	OffsetNumber nPageData;		/* number of valid items in array */
  	OffsetNumber curPageData;	/* next item to return */
+ 	FmgrInfo	*orderByRechecks;
+ 	IndexInfo *indexInfo;
+ 	TupleTableSlot *slot;
+ 	EState *estate;
  } GISTScanOpaqueData;
  
  typedef GISTScanOpaqueData *GISTScanOpaque;
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
new file mode 100644
index c8a548c..e7a79c6
*** a/src/include/catalog/pg_amop.h
--- b/src/include/catalog/pg_amop.h
*************** DATA(insert (	2594   604 604 11 s 2577 7
*** 638,643 ****
--- 638,644 ----
  DATA(insert (	2594   604 604 12 s 2576 783 0 ));
  DATA(insert (	2594   604 604 13 s 2861 783 0 ));
  DATA(insert (	2594   604 604 14 s 2860 783 0 ));
+ DATA(insert (	2594   604 600 15 o 3569 783 1970 ));
  
  /*
   *	gist circle_ops
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
new file mode 100644
index 53a3a7a..29c7c09
*** a/src/include/catalog/pg_amproc.h
--- b/src/include/catalog/pg_amproc.h
*************** DATA(insert (	2594   604 604 4 2580 ));
*** 188,193 ****
--- 188,194 ----
  DATA(insert (	2594   604 604 5 2581 ));
  DATA(insert (	2594   604 604 6 2582 ));
  DATA(insert (	2594   604 604 7 2584 ));
+ DATA(insert (	2594   604 604 8 3567 ));
  DATA(insert (	2595   718 718 1 2591 ));
  DATA(insert (	2595   718 718 2 2583 ));
  DATA(insert (	2595   718 718 3 2592 ));
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
new file mode 100644
index 78efaa5..32ac483
*** a/src/include/catalog/pg_operator.h
--- b/src/include/catalog/pg_operator.h
*************** DATA(insert OID = 709 (  "<->"	   PGNSP 
*** 591,596 ****
--- 591,598 ----
  DESCR("distance between");
  DATA(insert OID = 712 (  "<->"	   PGNSP PGUID b f f 604 604 701 712	 0 poly_distance - - ));
  DESCR("distance between");
+ DATA(insert OID = 3569 (  "<->"	   PGNSP PGUID b f f 604 600 701 0 		 0 dist_polyp - - ));
+ DESCR("distance between");
  
  DATA(insert OID = 713 (  "<>"	   PGNSP PGUID b f f 600 600	16 713 510 point_ne neqsel neqjoinsel ));
  DESCR("not equal");
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
new file mode 100644
index 0117500..85d077b
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DATA(insert OID = 726 (  dist_lb		   PGN
*** 809,814 ****
--- 809,815 ----
  DATA(insert OID = 727 (  dist_sl		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "601 628" _null_ _null_ _null_ _null_	dist_sl _null_ _null_ _null_ ));
  DATA(insert OID = 728 (  dist_cpoly		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "718 604" _null_ _null_ _null_ _null_	dist_cpoly _null_ _null_ _null_ ));
  DATA(insert OID = 729 (  poly_distance	   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "604 604" _null_ _null_ _null_ _null_	poly_distance _null_ _null_ _null_ ));
+ DATA(insert OID = 3568 (  dist_polyp	   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "604 600" _null_ _null_ _null_ _null_	dist_polyp _null_ _null_ _null_ ));
  
  DATA(insert OID = 740 (  text_lt		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_lt _null_ _null_ _null_ ));
  DATA(insert OID = 741 (  text_le		   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_le _null_ _null_ _null_ ));
*************** DATA(insert OID = 2585 (  gist_poly_cons
*** 3937,3942 ****
--- 3938,3945 ----
  DESCR("GiST support");
  DATA(insert OID = 2586 (  gist_poly_compress	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_poly_compress _null_ _null_ _null_ ));
  DESCR("GiST support");
+ DATA(insert OID = 3567 (  gist_poly_distance	PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 701 "2281 600 23 26" _null_ _null_ _null_ _null_	gist_poly_distance _null_ _null_ _null_ ));
+ DESCR("GiST support");
  DATA(insert OID = 2591 (  gist_circle_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 5 0 16 "2281 718 23 26 2281" _null_ _null_ _null_ _null_	gist_circle_consistent _null_ _null_ _null_ ));
  DESCR("GiST support");
  DATA(insert OID = 2592 (  gist_circle_compress	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_circle_compress _null_ _null_ _null_ ));
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
new file mode 100644
index 1e648c0..b8a04cb
*** a/src/include/utils/geo_decls.h
--- b/src/include/utils/geo_decls.h
*************** extern Datum circle_radius(PG_FUNCTION_A
*** 395,400 ****
--- 395,401 ----
  extern Datum circle_distance(PG_FUNCTION_ARGS);
  extern Datum dist_pc(PG_FUNCTION_ARGS);
  extern Datum dist_cpoly(PG_FUNCTION_ARGS);
+ extern Datum dist_polyp(PG_FUNCTION_ARGS);
  extern Datum circle_center(PG_FUNCTION_ARGS);
  extern Datum cr_circle(PG_FUNCTION_ARGS);
  extern Datum box_circle(PG_FUNCTION_ARGS);
*************** extern Datum gist_circle_consistent(PG_F
*** 418,423 ****
--- 419,425 ----
  extern Datum gist_point_compress(PG_FUNCTION_ARGS);
  extern Datum gist_point_consistent(PG_FUNCTION_ARGS);
  extern Datum gist_point_distance(PG_FUNCTION_ARGS);
+ extern Datum gist_poly_distance(PG_FUNCTION_ARGS);
  
  /* geo_selfuncs.c */
  extern Datum areasel(PG_FUNCTION_ARGS);