diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml index 0158b17..2cfe9e8 100644 --- a/doc/src/sgml/gist.sgml +++ b/doc/src/sgml/gist.sgml @@ -98,20 +98,21 @@ <<| <@ @> @ |&> |>> ~ ~= + <-> inet_ops inet, cidr && >> >>= > @@ -156,20 +157,21 @@ <<| <@ @> @ |&> |>> ~ ~= + <-> range_ops any range type && &> &< >> @@ -200,20 +202,26 @@ @@ + Currently, ordering by the distance operator <-> + is supported only with point by the operator classes + of the geometric types. + + + For historical reasons, the inet_ops operator class is not the default class for types inet and cidr. To use it, mention the class name in CREATE INDEX, for example CREATE INDEX ON my_table USING gist (my_inet_column inet_ops); @@ -759,56 +767,62 @@ my_same(PG_FUNCTION_ARGS) so the results must be consistent with the operator's semantics. For a leaf index entry the result just represents the distance to the index entry; for an internal tree node, the result must be the smallest distance that any child entry could have. The SQL declaration of the function must look like this: -CREATE OR REPLACE FUNCTION my_distance(internal, data_type, smallint, oid) +CREATE OR REPLACE FUNCTION my_distance(internal, data_type, smallint, oid, internal) RETURNS float8 AS 'MODULE_PATHNAME' LANGUAGE C STRICT; And the matching code in the C module could then follow this skeleton: Datum my_distance(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(my_distance); Datum my_distance(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); data_type *query = PG_GETARG_DATA_TYPE_P(1); StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); /* Oid subtype = PG_GETARG_OID(3); */ + bool *recheck = (bool *) PG_GETARG_POINTER(4); data_type *key = DatumGetDataType(entry->key); double retval; /* * determine return value as a function of strategy, key and query. */ PG_RETURN_FLOAT8(retval); } The arguments to the distance function are identical to - the arguments of the consistent function, except that no - recheck flag is used. The distance to a leaf index entry must always - be determined exactly, since there is no way to re-order the tuples - once they are returned. Some approximation is allowed when determining - the distance to an internal tree node, so long as the result is never + the arguments of the consistent function. When + recheck = true then value of distance will + be rechecked from heap tuple before tuple is returned. If + recheck flag isn't set then it's true by default for + compatibility reasons. The recheck flag can be used only + when ordering operator returns float8 value comparable with + result of distance function. Result of distance function + should be never greater than result of ordering operator. + Same approximation is allowed when determining the distance to an + internal tree node, so long as the result is never greater than any child's actual distance. Thus, for example, distance to a bounding box is usually sufficient in geometric applications. The result value can be any finite float8 value. (Infinity and minus infinity are used internally to handle cases such as nulls, so it is not recommended that distance functions return these values.) diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c index 7a8692b..7be050d 100644 --- a/src/backend/access/gist/gistget.c +++ b/src/backend/access/gist/gistget.c @@ -9,20 +9,21 @@ * * IDENTIFICATION * src/backend/access/gist/gistget.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/gist_private.h" #include "access/relscan.h" +#include "catalog/index.h" #include "miscadmin.h" #include "pgstat.h" #include "utils/builtins.h" #include "utils/memutils.h" #include "utils/rel.h" /* * gistindex_keytest() -- does this index tuple satisfy the scan key(s)? * @@ -48,38 +49,41 @@ static bool gistindex_keytest(IndexScanDesc scan, IndexTuple tuple, Page page, OffsetNumber offset, bool *recheck_p) { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; GISTSTATE *giststate = so->giststate; ScanKey key = scan->keyData; int keySize = scan->numberOfKeys; - double *distance_p; + GISTSearchTreeItemDistance *distance_p; Relation r = scan->indexRelation; *recheck_p = false; /* * If it's a leftover invalid tuple from pre-9.1, treat it as a match with * minimum possible distances. This means we'll always follow it to the * referenced page. */ if (GistTupleIsInvalid(tuple)) { int i; 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(); + { + so->distances[i].value = -get_float8_infinity(); + so->distances[i].recheck = false; + } return true; } /* Check whether it matches according to the Consistent functions */ while (keySize > 0) { Datum datum; bool isNull; datum = index_getattr(tuple, @@ -163,53 +167,56 @@ gistindex_keytest(IndexScanDesc scan, bool isNull; datum = index_getattr(tuple, key->sk_attno, giststate->tupdesc, &isNull); if ((key->sk_flags & SK_ISNULL) || isNull) { /* Assume distance computes as null and sorts to the end */ - *distance_p = get_float8_infinity(); + distance_p->value = get_float8_infinity(); + distance_p->recheck = false; } else { Datum dist; GISTENTRY de; gistdentryinit(giststate, key->sk_attno - 1, &de, datum, r, page, offset, FALSE, isNull); /* * Call the Distance function to evaluate the distance. The * arguments are the index datum (as a GISTENTRY*), the comparison * datum, and the ordering operator's strategy number and subtype * from pg_amop. * * (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.) * - * Note that Distance functions don't get a recheck argument. We - * can't tolerate lossy distance calculations on leaf tuples; - * there is no opportunity to re-sort the tuples afterwards. + * Distance function gets a recheck argument as well as consistent + * function. Distance will be re-calculated from heap tuple when + * needed. */ - dist = FunctionCall4Coll(&key->sk_func, + 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)); + ObjectIdGetDatum(key->sk_subtype), + PointerGetDatum(&distance_p->recheck)); - *distance_p = DatumGetFloat8(dist); + distance_p->value = DatumGetFloat8(dist); } key++; distance_p++; keySize--; } return true; } @@ -227,21 +234,21 @@ gistindex_keytest(IndexScanDesc scan, * tuples should be reported directly into the bitmap. If they are NULL, * we're doing a plain or ordered indexscan. For a plain indexscan, heap * tuple TIDs are returned into so->pageData[]. For an ordered indexscan, * heap tuple TIDs are pushed into individual search queue items. * * If we detect that the index page has split since we saw its downlink * in the parent, we push its new right sibling onto the queue so the * sibling will be processed next. */ static void -gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, +gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, GISTSearchTreeItemDistance *myDistances, TIDBitmap *tbm, int64 *ntids) { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; Buffer buffer; Page page; GISTPageOpaque opaque; OffsetNumber maxoff; OffsetNumber i; GISTSearchTreeItem *tmpItem = so->tmpTreeItem; bool isNew; @@ -277,21 +284,21 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, /* Create new GISTSearchItem for the right sibling index page */ item = palloc(sizeof(GISTSearchItem)); item->next = NULL; item->blkno = opaque->rightlink; item->data.parentlsn = pageItem->data.parentlsn; /* Insert it into the queue using same distances as for this page */ tmpItem->head = item; tmpItem->lastHeap = NULL; memcpy(tmpItem->distances, myDistances, - sizeof(double) * scan->numberOfOrderBys); + sizeof(GISTSearchTreeItemDistance) * scan->numberOfOrderBys); (void) rb_insert(so->queue, (RBNode *) tmpItem, &isNew); MemoryContextSwitchTo(oldcxt); } so->nPageData = so->curPageData = 0; /* * check all tuples on page @@ -368,63 +375,166 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, * only have a shared lock, so we need to get the LSN * atomically. */ item->data.parentlsn = BufferGetLSNAtomic(buffer); } /* Insert it into the queue using new distance data */ tmpItem->head = item; tmpItem->lastHeap = GISTSearchItemIsHeap(*item) ? item : NULL; memcpy(tmpItem->distances, so->distances, - sizeof(double) * scan->numberOfOrderBys); + sizeof(GISTSearchTreeItemDistance) * scan->numberOfOrderBys); (void) rb_insert(so->queue, (RBNode *) tmpItem, &isNew); MemoryContextSwitchTo(oldcxt); } } UnlockReleaseBuffer(buffer); } /* + * 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; + + /* Get tuple from heap */ + 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) + { + /* + * Tuple not found: it has been deleted from heap. We don't have to + * reinsert it into RB-tree. + */ + UnlockReleaseBuffer(buffer); + pfree(item); + return; + } + + /* Calculate index datums */ + ExecStoreTuple(&tup, so->slot, InvalidBuffer, false); + FormIndexDatum(so->indexInfo, so->slot, so->estate, values, isnull); + + /* Prepare new tree item and reinsert it */ + memcpy(tmpItem, treeItem, GSTIHDRSZ + sizeof(GISTSearchTreeItemDistance) * + scan->numberOfOrderBys); + tmpItem->head = item; + tmpItem->lastHeap = item; + item->next = NULL; + for (i = 0; i < scan->numberOfOrderBys; i++) + { + if (tmpItem->distances[i].recheck) + { + /* Re-calculate lossy distance */ + 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. * * NOTE: on successful return, so->curTreeItem is the GISTSearchTreeItem that * contained the result item. Callers can use so->curTreeItem->distances as * the distances value for the item. */ static GISTSearchItem * -getNextGISTSearchItem(GISTScanOpaque so) +getNextGISTSearchItem(IndexScanDesc scan) { + GISTScanOpaque so = (GISTScanOpaque) scan->opaque; + for (;;) { GISTSearchItem *item; /* Update curTreeItem if we don't have one */ if (so->curTreeItem == NULL) { so->curTreeItem = (GISTSearchTreeItem *) rb_leftmost(so->queue); /* Done when tree is empty */ if (so->curTreeItem == NULL) break; } item = so->curTreeItem->head; if (item != NULL) { /* Delink item from chain */ 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; } /* curTreeItem is exhausted, so remove it from rbtree */ rb_delete(so->queue, (RBNode *) so->curTreeItem); so->curTreeItem = NULL; } return NULL; @@ -434,21 +544,21 @@ getNextGISTSearchItem(GISTScanOpaque so) * Fetch next heap tuple in an ordered search */ static bool getNextNearest(IndexScanDesc scan) { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; bool res = false; do { - GISTSearchItem *item = getNextGISTSearchItem(so); + GISTSearchItem *item = getNextGISTSearchItem(scan); if (!item) break; if (GISTSearchItemIsHeap(*item)) { /* found a heap item at currently minimal distance */ scan->xs_ctup.t_self = item->data.heap.heapPtr; scan->xs_recheck = item->data.heap.recheck; res = true; @@ -514,21 +624,21 @@ gistgettuple(PG_FUNCTION_ARGS) /* continuing to return tuples from a leaf page */ scan->xs_ctup.t_self = so->pageData[so->curPageData].heapPtr; scan->xs_recheck = so->pageData[so->curPageData].recheck; so->curPageData++; PG_RETURN_BOOL(true); } /* find and process the next index page */ do { - GISTSearchItem *item = getNextGISTSearchItem(so); + GISTSearchItem *item = getNextGISTSearchItem(scan); if (!item) PG_RETURN_BOOL(false); CHECK_FOR_INTERRUPTS(); /* * While scanning a leaf page, ItemPointers of matching heap * tuples are stored in so->pageData. If there are any on * this page, we fall out of the inner "do" and loop around to @@ -566,21 +676,21 @@ gistgetbitmap(PG_FUNCTION_ARGS) fakeItem.blkno = GIST_ROOT_BLKNO; memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN)); gistScanPage(scan, &fakeItem, NULL, tbm, &ntids); /* * While scanning a leaf page, ItemPointers of matching heap tuples will * be stored directly into tbm, so we don't need to deal with them here. */ for (;;) { - GISTSearchItem *item = getNextGISTSearchItem(so); + GISTSearchItem *item = getNextGISTSearchItem(scan); if (!item) break; CHECK_FOR_INTERRUPTS(); gistScanPage(scan, item, so->curTreeItem->distances, tbm, &ntids); pfree(item); } diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index db0bec6..fd3546a 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -1091,20 +1091,21 @@ gist_poly_consistent(PG_FUNCTION_ARGS) */ result = rtree_internal_consistent(DatumGetBoxP(entry->key), &(query->boundbox), strategy); /* Avoid memory leak if supplied poly is toasted */ PG_FREE_IF_COPY(query, 1); PG_RETURN_BOOL(result); } + /************************************************** * Circle ops **************************************************/ /* * GiST compress for circles: represent a circle by its bounding box */ Datum gist_circle_compress(PG_FUNCTION_ARGS) { @@ -1452,10 +1453,44 @@ gist_point_distance(PG_FUNCTION_ARGS) PG_GETARG_POINT_P(1)); break; default: elog(ERROR, "unrecognized strategy number: %d", strategy); distance = 0.0; /* keep compiler quiet */ break; } PG_RETURN_FLOAT8(distance); } + +/* + * The inexact GiST distance method for geometric types + * + * Compute lossy distance from point to index entries. The result is inexact + * because index entries are bounding boxes, not the exact shapes of the + * indexed geometric types. We use distance from point to MBR of index entry. + * This is correct lower bound estimate of distance from point to indexed + * geometric type. + */ +Datum +gist_inexact_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 = computeDistance(false, + 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 index 8360b16..9bb8294 100644 --- a/src/backend/access/gist/gistscan.c +++ b/src/backend/access/gist/gistscan.c @@ -10,41 +10,54 @@ * IDENTIFICATION * src/backend/access/gist/gistscan.c * *------------------------------------------------------------------------- */ #include "postgres.h" #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" /* * RBTree support functions for the GISTSearchTreeItem queue */ static int GISTSearchTreeItemComparator(const RBNode *a, const RBNode *b, void *arg) { const GISTSearchTreeItem *sa = (const GISTSearchTreeItem *) a; const GISTSearchTreeItem *sb = (const GISTSearchTreeItem *) b; IndexScanDesc scan = (IndexScanDesc) arg; int i; /* 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; + const GISTSearchTreeItemDistance distance_a = sa->distances[i]; + const GISTSearchTreeItemDistance distance_b = sb->distances[i]; + + if (distance_a.value != distance_b.value) + return (distance_a.value > distance_b.value) ? 1 : -1; + + /* + * Items without recheck can be immediately returned. So they are + * placed first. + */ + if (distance_a.recheck != distance_b.recheck) + return distance_a.recheck ? 1 : -1; } return 0; } static void GISTSearchTreeItemCombiner(RBNode *existing, const RBNode *newrb, void *arg) { GISTSearchTreeItem *scurrent = (GISTSearchTreeItem *) existing; const GISTSearchTreeItem *snew = (const GISTSearchTreeItem *) newrb; @@ -76,21 +89,21 @@ GISTSearchTreeItemCombiner(RBNode *existing, const RBNode *newrb, void *arg) newitem->next = scurrent->lastHeap->next; scurrent->lastHeap->next = newitem; } } static RBNode * GISTSearchTreeItemAllocator(void *arg) { IndexScanDesc scan = (IndexScanDesc) arg; - return palloc(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys); + return palloc(GSTIHDRSZ + sizeof(GISTSearchTreeItemDistance) * scan->numberOfOrderBys); } static void GISTSearchTreeItemDeleter(RBNode *rb, void *arg) { pfree(rb); } /* @@ -120,24 +133,36 @@ gistbeginscan(PG_FUNCTION_ARGS) oldCxt = MemoryContextSwitchTo(giststate->scanCxt); /* initialize opaque data */ so = (GISTScanOpaque) palloc0(sizeof(GISTScanOpaqueData)); so->giststate = giststate; giststate->tempCxt = createTempGistContext(); so->queue = NULL; 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->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) + { + /* Prepare data structures for distance recheck from heap tuple */ + + so->orderByRechecks = (FmgrInfo *)palloc(sizeof(FmgrInfo) + * scan->numberOfOrderBys); + so->indexInfo = BuildIndexInfo(scan->indexRelation); + so->estate = CreateExecutorState(); + } + scan->opaque = so; MemoryContextSwitchTo(oldCxt); PG_RETURN_POINTER(scan); } Datum gistrescan(PG_FUNCTION_ARGS) { @@ -179,23 +204,30 @@ gistrescan(PG_FUNCTION_ARGS) ALLOCSET_DEFAULT_MAXSIZE); first_time = false; } else { /* third or later time through */ MemoryContextReset(so->queueCxt); first_time = false; } + if (scan->numberOfOrderBys > 0 && !so->slot) + { + /* Prepare heap tuple slot for distance recheck */ + so->slot = MakeSingleTupleTableSlot(RelationGetDescr(scan->heapRelation)); + } + /* create new, empty RBTree for search queue */ oldCxt = MemoryContextSwitchTo(so->queueCxt); - so->queue = rb_create(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys, + so->queue = rb_create(GSTIHDRSZ + sizeof(GISTSearchTreeItemDistance) * + scan->numberOfOrderBys, GISTSearchTreeItemComparator, GISTSearchTreeItemCombiner, GISTSearchTreeItemAllocator, GISTSearchTreeItemDeleter, scan); MemoryContextSwitchTo(oldCxt); so->curTreeItem = NULL; so->firstCall = true; @@ -282,20 +314,24 @@ gistrescan(PG_FUNCTION_ARGS) { ScanKey skey = scan->orderByData + i; FmgrInfo *finfo = &(so->giststate->distanceFn[skey->sk_attno - 1]); /* Check we actually have a distance function ... */ if (!OidIsValid(finfo->fn_oid)) elog(ERROR, "missing support function %d for attribute %d of index \"%s\"", GIST_DISTANCE_PROC, skey->sk_attno, RelationGetRelationName(scan->indexRelation)); + /* Copy original sk_func for distance recheck from heap tuple */ + 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 */ if (!first_time) skey->sk_func.fn_extra = fn_extras[i]; } if (!first_time) pfree(fn_extras); } @@ -316,18 +352,21 @@ gistrestrpos(PG_FUNCTION_ARGS) elog(ERROR, "GiST does not support mark/restore"); PG_RETURN_VOID(); } Datum gistendscan(PG_FUNCTION_ARGS) { 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. */ freeGISTstate(so->giststate); PG_RETURN_VOID(); } diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c index 402ea40..c9788e4 100644 --- a/src/backend/utils/adt/geo_ops.c +++ b/src/backend/utils/adt/geo_ops.c @@ -63,20 +63,21 @@ static int pair_encode(float8 x, float8 y, char *str); static int pair_count(char *s, char delim); static int path_decode(int opentype, int npts, char *str, int *isopen, char **ss, Point *p); static char *path_encode(enum path_delim path_delim, int npts, Point *pt); static void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2); static double box_ar(BOX *box); static void box_cn(Point *center, BOX *box); static Point *interpt_sl(LSEG *lseg, LINE *line); static bool has_interpt_sl(LSEG *lseg, LINE *line); static double dist_pl_internal(Point *pt, LINE *line); static double dist_ps_internal(Point *pt, LSEG *lseg); +static float8 dist_ppoly_internal(Point *point, POLYGON *poly); static Point *line_interpt_internal(LINE *l1, LINE *l2); static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start); static Point *lseg_interpt_internal(LSEG *l1, LSEG *l2); /* * Delimiters for input and output strings. * LDELIM, RDELIM, and DELIM are left, right, and separator delimiters, respectively. * LDELIM_EP, RDELIM_EP are left and right delimiters for paths with endpoints. */ @@ -2634,20 +2635,52 @@ dist_lb(PG_FUNCTION_ARGS) /* need to think about this one for a while */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function \"dist_lb\" not implemented"))); PG_RETURN_NULL(); } /* + * Distance from a point to a circle + */ +Datum +dist_pc(PG_FUNCTION_ARGS) +{ + Point *point = PG_GETARG_POINT_P(0); + CIRCLE *circle = PG_GETARG_CIRCLE_P(1); + float8 result; + + result = point_dt(point, &circle->center) - circle->radius; + if (result < 0) + result = 0; + PG_RETURN_FLOAT8(result); +} + +/* + * Distance from a circle to a point + */ +Datum +dist_cpoint(PG_FUNCTION_ARGS) +{ + CIRCLE *circle = PG_GETARG_CIRCLE_P(0); + Point *point = PG_GETARG_POINT_P(1); + float8 result; + + result = point_dt(point, &circle->center) - circle->radius; + if (result < 0) + result = 0; + PG_RETURN_FLOAT8(result); +} + +/* * Distance from a circle to a polygon */ Datum dist_cpoly(PG_FUNCTION_ARGS) { CIRCLE *circle = PG_GETARG_CIRCLE_P(0); POLYGON *poly = PG_GETARG_POLYGON_P(1); float8 result; float8 d; int i; @@ -2692,33 +2725,48 @@ dist_cpoly(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(result); } /* * Distance from a point to a polygon */ Datum dist_ppoly(PG_FUNCTION_ARGS) { - Point *point = PG_GETARG_POINT_P(0); - POLYGON *poly = PG_GETARG_POLYGON_P(1); + PG_RETURN_FLOAT8(dist_ppoly_internal(PG_GETARG_POINT_P(0), + PG_GETARG_POLYGON_P(1))); +} + +/* + * Distance from a polygon to a point + */ +Datum +dist_polyp(PG_FUNCTION_ARGS) +{ + PG_RETURN_FLOAT8(dist_ppoly_internal(PG_GETARG_POINT_P(1), + PG_GETARG_POLYGON_P(0))); +} + +static float8 +dist_ppoly_internal(Point *point, POLYGON *poly) +{ float8 result; float8 distance; int i; LSEG seg; if (point_inside(point, poly->npts, poly->p) != 0) { #ifdef GEODEBUG printf("dist_ppoly- point inside of polygon\n"); #endif - PG_RETURN_FLOAT8(0.0); + return 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_ppoly- segment 0/n distance is %f\n", result); @@ -2732,21 +2780,21 @@ dist_ppoly(PG_FUNCTION_ARGS) seg.p[1].x = poly->p[i + 1].x; seg.p[1].y = poly->p[i + 1].y; distance = dist_ps_internal(point, &seg); #ifdef GEODEBUG printf("dist_ppoly- segment %d distance is %f\n", i + 1, distance); #endif if (distance < result) result = distance; } - PG_RETURN_FLOAT8(result); + return result; } /*--------------------------------------------------------------------- * interpt_ * Intersection point of objects. * We choose to ignore the "point" of intersection between * lines and boxes, since there are typically two. *-------------------------------------------------------------------*/ @@ -5091,37 +5139,20 @@ pt_contained_circle(PG_FUNCTION_ARGS) { Point *point = PG_GETARG_POINT_P(0); CIRCLE *circle = PG_GETARG_CIRCLE_P(1); double d; d = point_dt(&circle->center, point); PG_RETURN_BOOL(d <= circle->radius); } -/* dist_pc - returns the distance between - * a point and a circle. - */ -Datum -dist_pc(PG_FUNCTION_ARGS) -{ - Point *point = PG_GETARG_POINT_P(0); - CIRCLE *circle = PG_GETARG_CIRCLE_P(1); - float8 result; - - result = point_dt(point, &circle->center) - circle->radius; - if (result < 0) - result = 0; - PG_RETURN_FLOAT8(result); -} - - /* circle_center - returns the center point of the circle. */ Datum circle_center(PG_FUNCTION_ARGS) { CIRCLE *circle = PG_GETARG_CIRCLE_P(0); Point *result; result = (Point *) palloc(sizeof(Point)); result->x = circle->center.x; diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index 03e9903..6f98583 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -9,21 +9,23 @@ * * src/include/access/gist_private.h * *------------------------------------------------------------------------- */ #ifndef GIST_PRIVATE_H #define GIST_PRIVATE_H #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" #include "utils/hsearch.h" /* * Maximum number of "halves" a page can be split into in one operation. * Typically a split produces 2 halves, but can be more if keys have very * different lengths, or when inserting multiple keys in one operation (as * when inserting downlinks to an internal node). There is no theoretical @@ -128,55 +130,70 @@ typedef struct GISTSearchItem { GistNSN parentlsn; /* parent page's LSN, if index page */ /* we must store parentlsn to detect whether a split occurred */ GISTSearchHeapItem heap; /* heap info, if heap tuple */ } data; } GISTSearchItem; #define GISTSearchItemIsHeap(item) ((item).blkno == InvalidBlockNumber) /* + * KNN distance item: distance which can be rechecked from heap tuple. + */ +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 * to the last heap item in the chain, or is NULL if there are none. */ typedef struct GISTSearchTreeItem { 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 */ + GISTSearchTreeItemDistance distances[1]; /* array with numberOfOrderBys entries */ } GISTSearchTreeItem; #define GSTIHDRSZ offsetof(GISTSearchTreeItem, distances) /* * GISTScanOpaqueData: private state for a scan of a GiST index */ typedef struct GISTScanOpaqueData { GISTSTATE *giststate; /* index information, see above */ RBTree *queue; /* queue of unvisited items */ MemoryContext queueCxt; /* context holding the queue */ bool qual_ok; /* false if qual can never be satisfied */ bool firstCall; /* true until first gistgettuple call */ GISTSearchTreeItem *curTreeItem; /* current queue item, if any */ /* pre-allocated workspace arrays */ GISTSearchTreeItem *tmpTreeItem; /* workspace to pass to rb_insert */ - double *distances; /* output area for gistindex_keytest */ + 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 */ + + /* Data structures for performing recheck of lossy knn distance */ + FmgrInfo *orderByRechecks; /* functions for lossy knn distance recheck */ + IndexInfo *indexInfo; /* index info for index tuple calculation */ + TupleTableSlot *slot; /* heap tuple slot */ + EState *estate; /* executor state for index tuple calculation */ } GISTScanOpaqueData; typedef GISTScanOpaqueData *GISTScanOpaque; /* XLog stuff */ #define XLOG_GIST_PAGE_UPDATE 0x00 /* #define XLOG_GIST_NEW_ROOT 0x20 */ /* not used anymore */ #define XLOG_GIST_PAGE_SPLIT 0x30 diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index 3ef5a49..dd468f6 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -643,39 +643,41 @@ DATA(insert ( 2594 604 604 4 s 487 783 0 )); DATA(insert ( 2594 604 604 5 s 488 783 0 )); DATA(insert ( 2594 604 604 6 s 491 783 0 )); DATA(insert ( 2594 604 604 7 s 490 783 0 )); DATA(insert ( 2594 604 604 8 s 489 783 0 )); DATA(insert ( 2594 604 604 9 s 2575 783 0 )); DATA(insert ( 2594 604 604 10 s 2574 783 0 )); DATA(insert ( 2594 604 604 11 s 2577 783 0 )); 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 3588 783 1970 )); /* * gist circle_ops */ DATA(insert ( 2595 718 718 1 s 1506 783 0 )); DATA(insert ( 2595 718 718 2 s 1507 783 0 )); DATA(insert ( 2595 718 718 3 s 1513 783 0 )); DATA(insert ( 2595 718 718 4 s 1508 783 0 )); DATA(insert ( 2595 718 718 5 s 1509 783 0 )); DATA(insert ( 2595 718 718 6 s 1512 783 0 )); DATA(insert ( 2595 718 718 7 s 1511 783 0 )); DATA(insert ( 2595 718 718 8 s 1510 783 0 )); DATA(insert ( 2595 718 718 9 s 2589 783 0 )); DATA(insert ( 2595 718 718 10 s 1515 783 0 )); DATA(insert ( 2595 718 718 11 s 1514 783 0 )); DATA(insert ( 2595 718 718 12 s 2590 783 0 )); DATA(insert ( 2595 718 718 13 s 2865 783 0 )); DATA(insert ( 2595 718 718 14 s 2864 783 0 )); +DATA(insert ( 2595 718 600 15 o 3586 783 1970 )); /* * gin array_ops (these anyarray operators are used with all the opclasses * of the family) */ DATA(insert ( 2745 2277 2277 1 s 2750 2742 0 )); DATA(insert ( 2745 2277 2277 2 s 2751 2742 0 )); DATA(insert ( 2745 2277 2277 3 s 2752 2742 0 )); DATA(insert ( 2745 2277 2277 4 s 1070 2742 0 )); diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index 10a47df..1149923 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -197,27 +197,29 @@ DATA(insert ( 2593 603 603 4 2580 )); DATA(insert ( 2593 603 603 5 2581 )); DATA(insert ( 2593 603 603 6 2582 )); DATA(insert ( 2593 603 603 7 2584 )); DATA(insert ( 2594 604 604 1 2585 )); DATA(insert ( 2594 604 604 2 2583 )); DATA(insert ( 2594 604 604 3 2586 )); DATA(insert ( 2594 604 604 4 2580 )); 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 3589 )); DATA(insert ( 2595 718 718 1 2591 )); DATA(insert ( 2595 718 718 2 2583 )); DATA(insert ( 2595 718 718 3 2592 )); DATA(insert ( 2595 718 718 4 2580 )); DATA(insert ( 2595 718 718 5 2581 )); DATA(insert ( 2595 718 718 6 2582 )); DATA(insert ( 2595 718 718 7 2584 )); +DATA(insert ( 2595 718 718 8 3589 )); DATA(insert ( 3655 3614 3614 1 3654 )); DATA(insert ( 3655 3614 3614 2 3651 )); DATA(insert ( 3655 3614 3614 3 3648 )); DATA(insert ( 3655 3614 3614 4 3649 )); DATA(insert ( 3655 3614 3614 5 3653 )); DATA(insert ( 3655 3614 3614 6 3650 )); DATA(insert ( 3655 3614 3614 7 3652 )); DATA(insert ( 3702 3615 3615 1 3701 )); DATA(insert ( 3702 3615 3615 2 3698 )); DATA(insert ( 3702 3615 3615 3 3695 )); diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index c31b8a8..b633665 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -1007,23 +1007,27 @@ DATA(insert OID = 1517 ( "-" PGNSP PGUID b f f 718 600 718 0 0 circle_ DESCR("subtract"); DATA(insert OID = 1518 ( "*" PGNSP PGUID b f f 718 600 718 0 0 circle_mul_pt - - )); DESCR("multiply"); DATA(insert OID = 1519 ( "/" PGNSP PGUID b f f 718 600 718 0 0 circle_div_pt - - )); DESCR("divide"); DATA(insert OID = 1520 ( "<->" PGNSP PGUID b f f 718 718 701 1520 0 circle_distance - - )); DESCR("distance between"); DATA(insert OID = 1521 ( "#" PGNSP PGUID l f f 0 604 23 0 0 poly_npoints - - )); DESCR("number of points"); -DATA(insert OID = 1522 ( "<->" PGNSP PGUID b f f 600 718 701 0 0 dist_pc - - )); +DATA(insert OID = 1522 ( "<->" PGNSP PGUID b f f 600 718 701 3586 0 dist_pc - - )); DESCR("distance between"); -DATA(insert OID = 3591 ( "<->" PGNSP PGUID b f f 600 604 701 0 0 dist_ppoly - - )); +DATA(insert OID = 3586 ( "<->" PGNSP PGUID b f f 718 600 701 1522 0 dist_cpoint - - )); +DESCR("distance between"); +DATA(insert OID = 3591 ( "<->" PGNSP PGUID b f f 600 604 701 3588 0 dist_ppoly - - )); +DESCR("distance between"); +DATA(insert OID = 3588 ( "<->" PGNSP PGUID b f f 604 600 701 3591 0 dist_polyp - - )); DESCR("distance between"); DATA(insert OID = 1523 ( "<->" PGNSP PGUID b f f 718 604 701 0 0 dist_cpoly - - )); DESCR("distance between"); /* additional geometric operators - thomas 1997-07-09 */ DATA(insert OID = 1524 ( "<->" PGNSP PGUID b f f 628 603 701 0 0 dist_lb - - )); DESCR("distance between"); DATA(insert OID = 1525 ( "?#" PGNSP PGUID b f f 601 601 16 1525 0 lseg_intersect - - )); DESCR("intersect"); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 95f0b74..1b7664e 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -807,20 +807,22 @@ DATA(insert OID = 749 ( overlay PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 17 DESCR("substitute portion of string"); DATA(insert OID = 752 ( overlay PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 17 "17 17 23" _null_ _null_ _null_ _null_ byteaoverlay_no_len _null_ _null_ _null_ )); DESCR("substitute portion of string"); DATA(insert OID = 725 ( dist_pl PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "600 628" _null_ _null_ _null_ _null_ dist_pl _null_ _null_ _null_ )); DATA(insert OID = 726 ( dist_lb PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "628 603" _null_ _null_ _null_ _null_ dist_lb _null_ _null_ _null_ )); 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 = 3590 ( dist_ppoly PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "600 604" _null_ _null_ _null_ _null_ dist_ppoly _null_ _null_ _null_ )); +DATA(insert OID = 3587 ( 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 = 3585 ( dist_cpoint PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "718 600" _null_ _null_ _null_ _null_ dist_cpoint _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 = 742 ( text_gt PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_gt _null_ _null_ _null_ )); DATA(insert OID = 743 ( text_ge PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_ge _null_ _null_ _null_ )); DATA(insert OID = 745 ( current_user PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 19 "" _null_ _null_ _null_ _null_ current_user _null_ _null_ _null_ )); DESCR("current user name"); DATA(insert OID = 746 ( session_user PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 19 "" _null_ _null_ _null_ _null_ session_user _null_ _null_ _null_ )); DESCR("session user name"); @@ -4002,20 +4004,22 @@ DESCR("GiST support"); DATA(insert OID = 2582 ( gist_box_picksplit PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ gist_box_picksplit _null_ _null_ _null_ )); DESCR("GiST support"); DATA(insert OID = 2583 ( gist_box_union PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 603 "2281 2281" _null_ _null_ _null_ _null_ gist_box_union _null_ _null_ _null_ )); DESCR("GiST support"); DATA(insert OID = 2584 ( gist_box_same PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 2281 "603 603 2281" _null_ _null_ _null_ _null_ gist_box_same _null_ _null_ _null_ )); DESCR("GiST support"); DATA(insert OID = 2585 ( gist_poly_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 5 0 16 "2281 604 23 26 2281" _null_ _null_ _null_ _null_ gist_poly_consistent _null_ _null_ _null_ )); 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 = 3589 ( gist_inexact_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_inexact_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_ )); DESCR("GiST support"); DATA(insert OID = 1030 ( gist_point_compress PGNSP PGUID 12 1 0 0 0 f 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 0 f f f f t f i 5 0 16 "2281 600 23 26 2281" _null_ _null_ _null_ _null_ gist_point_consistent _null_ _null_ _null_ )); DESCR("GiST support"); DATA(insert OID = 3064 ( gist_point_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_point_distance _null_ _null_ _null_ )); DESCR("GiST support"); diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h index 91610d8..64f63b2 100644 --- a/src/include/utils/geo_decls.h +++ b/src/include/utils/geo_decls.h @@ -387,22 +387,24 @@ extern Datum circle_ge(PG_FUNCTION_ARGS); extern Datum circle_contain_pt(PG_FUNCTION_ARGS); extern Datum pt_contained_circle(PG_FUNCTION_ARGS); extern Datum circle_add_pt(PG_FUNCTION_ARGS); extern Datum circle_sub_pt(PG_FUNCTION_ARGS); extern Datum circle_mul_pt(PG_FUNCTION_ARGS); extern Datum circle_div_pt(PG_FUNCTION_ARGS); extern Datum circle_diameter(PG_FUNCTION_ARGS); extern Datum circle_radius(PG_FUNCTION_ARGS); extern Datum circle_distance(PG_FUNCTION_ARGS); extern Datum dist_pc(PG_FUNCTION_ARGS); +extern Datum dist_cpoint(PG_FUNCTION_ARGS); extern Datum dist_cpoly(PG_FUNCTION_ARGS); extern Datum dist_ppoly(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 circle_box(PG_FUNCTION_ARGS); extern Datum poly_circle(PG_FUNCTION_ARGS); extern Datum circle_poly(PG_FUNCTION_ARGS); extern Datum circle_area(PG_FUNCTION_ARGS); /* support routines for the GiST access method (access/gist/gistproc.c) */ extern Datum gist_box_compress(PG_FUNCTION_ARGS); @@ -412,20 +414,21 @@ extern Datum gist_box_picksplit(PG_FUNCTION_ARGS); extern Datum gist_box_consistent(PG_FUNCTION_ARGS); extern Datum gist_box_penalty(PG_FUNCTION_ARGS); extern Datum gist_box_same(PG_FUNCTION_ARGS); extern Datum gist_poly_compress(PG_FUNCTION_ARGS); extern Datum gist_poly_consistent(PG_FUNCTION_ARGS); extern Datum gist_circle_compress(PG_FUNCTION_ARGS); extern Datum gist_circle_consistent(PG_FUNCTION_ARGS); 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_inexact_distance(PG_FUNCTION_ARGS); /* geo_selfuncs.c */ extern Datum areasel(PG_FUNCTION_ARGS); extern Datum areajoinsel(PG_FUNCTION_ARGS); extern Datum positionsel(PG_FUNCTION_ARGS); extern Datum positionjoinsel(PG_FUNCTION_ARGS); extern Datum contsel(PG_FUNCTION_ARGS); extern Datum contjoinsel(PG_FUNCTION_ARGS); #endif /* GEO_DECLS_H */