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);