diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml new file mode 100644 index 0158b17..2cfe9e8 *** a/doc/src/sgml/gist.sgml --- b/doc/src/sgml/gist.sgml *************** *** 105,110 **** --- 105,111 ---- ~= + <-> *************** *** 163,168 **** --- 164,170 ---- ~= + <-> *************** *** 207,212 **** --- 209,220 ---- + 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, *************** my_same(PG_FUNCTION_ARGS) *** 766,772 **** The SQL declaration of the function must look like this: ! CREATE OR REPLACE FUNCTION my_distance(internal, data_type, smallint, oid) RETURNS float8 AS 'MODULE_PATHNAME' LANGUAGE C STRICT; --- 774,780 ---- The SQL declaration of the function must look like this: ! CREATE OR REPLACE FUNCTION my_distance(internal, data_type, smallint, oid, internal) RETURNS float8 AS 'MODULE_PATHNAME' LANGUAGE C STRICT; *************** my_distance(PG_FUNCTION_ARGS) *** 785,790 **** --- 793,799 ---- 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; *************** my_distance(PG_FUNCTION_ARGS) *** 797,807 **** 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 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 --- 806,821 ---- The arguments to the distance function are identical to ! 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 diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c new file mode 100644 index 7a8692b..e454ba2 *** 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, *** 191,208 **** * 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. */ ! 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++; --- 196,215 ---- * always be zero, but might as well pass it for possible future * use.) * ! * Distance function gets a recheck argument as well as consistent ! * function. Distance will be re-calculated from heap tuple when ! * needed. */ ! 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,473 ---- } /* + * 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; + Datum values[INDEX_MAX_KEYS]; + bool isnull[INDEX_MAX_KEYS]; + bool isNew; + int i; + + /* Get index values from heap */ + if (!index_get_heap_values(scan, &item->data.heap.heapPtr, values, isnull)) + { + /* + * Tuple not found: it has been deleted from heap. We don't have to + * reinsert it into RB-tree. + */ + pfree(item); + return; + } + + /* 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); + } + + /* * 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; --- 477,486 ---- * the distances value for the item. */ static GISTSearchItem * ! getNextGISTSearchItem(IndexScanDesc scan) { + GISTScanOpaque so = (GISTScanOpaque) scan->opaque; + for (;;) { GISTSearchItem *item; *************** getNextGISTSearchItem(GISTScanOpaque so) *** 418,423 **** --- 501,514 ---- 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; --- 532,538 ---- 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); --- 612,618 ---- /* 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; --- 664,670 ---- */ 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 db0bec6..fd3546a *** a/src/backend/access/gist/gistproc.c --- b/src/backend/access/gist/gistproc.c *************** gist_poly_consistent(PG_FUNCTION_ARGS) *** 1098,1103 **** --- 1098,1104 ---- PG_RETURN_BOOL(result); } + /************************************************** * Circle ops **************************************************/ *************** gist_point_distance(PG_FUNCTION_ARGS) *** 1459,1461 **** --- 1460,1496 ---- 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 new file mode 100644 index 8360b16..284e8dc *** 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 *** 31,46 **** 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; } ! return 0; } static void --- 34,59 ---- const GISTSearchTreeItem *sa = (const GISTSearchTreeItem *) a; const GISTSearchTreeItem *sb = (const GISTSearchTreeItem *) b; IndexScanDesc scan = (IndexScanDesc) arg; ! int i, recheckCmp = 0; /* Order according to distance comparison */ for (i = 0; i < scan->numberOfOrderBys; i++) { ! 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; ! ! /* ! * When all distance values are the same, items without recheck ! * can be immediately returned. So they are placed first. ! */ ! if (recheckCmp == 0 && distance_a.recheck != distance_b.recheck) ! recheckCmp = distance_a.recheck ? 1 : -1; } ! return recheckCmp; } static void *************** GISTSearchTreeItemAllocator(void *arg) *** 83,89 **** { IndexScanDesc scan = (IndexScanDesc) arg; ! return palloc(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys); } static void --- 96,102 ---- { 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); --- 140,158 ---- 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) + { + /* Functions for distance recheck from heap tuple */ + so->orderByRechecks = (FmgrInfo *)palloc(sizeof(FmgrInfo) + * scan->numberOfOrderBys); + } + scan->opaque = so; MemoryContextSwitchTo(oldCxt); *************** gistrescan(PG_FUNCTION_ARGS) *** 188,194 **** /* create new, empty RBTree for search queue */ oldCxt = MemoryContextSwitchTo(so->queueCxt); ! so->queue = rb_create(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys, GISTSearchTreeItemComparator, GISTSearchTreeItemCombiner, GISTSearchTreeItemAllocator, --- 210,217 ---- /* 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 **** --- 312,321 ---- 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 */ diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c new file mode 100644 index 850008b..18cd20b *** a/src/backend/access/index/genam.c --- b/src/backend/access/index/genam.c *************** RelationGetIndexScan(Relation indexRelat *** 120,125 **** --- 120,128 ---- scan->xs_ctup.t_data = NULL; scan->xs_cbuf = InvalidBuffer; scan->xs_continue_hot = false; + scan->indexInfo = NULL; + scan->estate = NULL; + scan->slot = NULL; return scan; } diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c new file mode 100644 index 53cf96f..5b526c6 *** a/src/backend/access/index/indexam.c --- b/src/backend/access/index/indexam.c *************** *** 69,74 **** --- 69,75 ---- #include "access/transam.h" #include "access/xlog.h" + #include "executor/executor.h" #include "catalog/index.h" #include "catalog/catalog.h" #include "pgstat.h" *************** index_beginscan(Relation heapRelation, *** 254,259 **** --- 255,265 ---- scan->heapRelation = heapRelation; scan->xs_snapshot = snapshot; + /* Prepare data structures for getting original indexed values from heap */ + scan->indexInfo = BuildIndexInfo(scan->indexRelation); + scan->estate = CreateExecutorState(); + scan->slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation)); + return scan; } *************** index_endscan(IndexScanDesc scan) *** 377,382 **** --- 383,393 ---- scan->xs_cbuf = InvalidBuffer; } + if (scan->slot) + ExecDropSingleTupleTableSlot(scan->slot); + if (scan->estate) + FreeExecutorState(scan->estate); + /* End the AM's scan */ FunctionCall1(procedure, PointerGetDatum(scan)); *************** index_fetch_heap(IndexScanDesc scan) *** 564,569 **** --- 575,623 ---- } /* ---------------- + * index_get_heap_values - get original indexed values from heap + * + * Fetches heap tuple of heapPtr and calculated original indexed values. + * Returns true on success. Returns false when heap tuple wasn't found. + * Useful for indexes with lossy representation of keys. + * ---------------- + */ + bool + index_get_heap_values(IndexScanDesc scan, ItemPointer heapPtr, + Datum values[INDEX_MAX_KEYS], bool isnull[INDEX_MAX_KEYS]) + { + Buffer buffer; + bool got_heap_tuple, all_dead; + HeapTupleData tup; + + /* Get tuple from heap */ + buffer = ReadBuffer(scan->heapRelation, + ItemPointerGetBlockNumber(heapPtr)); + LockBuffer(buffer, BUFFER_LOCK_SHARE); + got_heap_tuple = heap_hot_search_buffer(heapPtr, + scan->heapRelation, + buffer, + scan->xs_snapshot, + &tup, + &all_dead, + true); + if (!got_heap_tuple) + { + /* Tuple not found: it has been deleted from heap. */ + UnlockReleaseBuffer(buffer); + return false; + } + + /* Calculate index datums */ + ExecStoreTuple(heap_copytuple(&tup), scan->slot, InvalidBuffer, true); + FormIndexDatum(scan->indexInfo, scan->slot, scan->estate, values, isnull); + + UnlockReleaseBuffer(buffer); + + return true; + } + + /* ---------------- * index_getnext - get the next heap tuple from a scan * * The result is the next heap tuple satisfying the scan keys and the diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c new file mode 100644 index 402ea40..c9788e4 *** a/src/backend/utils/adt/geo_ops.c --- b/src/backend/utils/adt/geo_ops.c *************** static Point *interpt_sl(LSEG *lseg, LIN *** 70,75 **** --- 70,76 ---- 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); *************** dist_lb(PG_FUNCTION_ARGS) *** 2641,2646 **** --- 2642,2679 ---- } /* + * 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) *** 2699,2706 **** Datum dist_ppoly(PG_FUNCTION_ARGS) { ! Point *point = PG_GETARG_POINT_P(0); ! POLYGON *poly = PG_GETARG_POLYGON_P(1); float8 result; float8 distance; int i; --- 2732,2754 ---- Datum dist_ppoly(PG_FUNCTION_ARGS) { ! 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; *************** dist_ppoly(PG_FUNCTION_ARGS) *** 2711,2717 **** #ifdef GEODEBUG printf("dist_ppoly- point inside of polygon\n"); #endif ! PG_RETURN_FLOAT8(0.0); } /* initialize distance with segment between first and last points */ --- 2759,2765 ---- #ifdef GEODEBUG printf("dist_ppoly- point inside of polygon\n"); #endif ! return 0.0; } /* initialize distance with segment between first and last points */ *************** dist_ppoly(PG_FUNCTION_ARGS) *** 2739,2745 **** result = distance; } ! PG_RETURN_FLOAT8(result); } --- 2787,2793 ---- result = distance; } ! return result; } *************** pt_contained_circle(PG_FUNCTION_ARGS) *** 5098,5120 **** } - /* 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 --- 5146,5151 ---- diff --git a/src/include/access/genam.h b/src/include/access/genam.h new file mode 100644 index d99158f..170069e *** a/src/include/access/genam.h --- b/src/include/access/genam.h *************** extern void index_restrpos(IndexScanDesc *** 147,153 **** --- 147,156 ---- extern ItemPointer index_getnext_tid(IndexScanDesc scan, ScanDirection direction); extern HeapTuple index_fetch_heap(IndexScanDesc scan); + extern bool index_get_heap_values(IndexScanDesc scan, ItemPointer heapPtr, + Datum values[INDEX_MAX_KEYS], bool isnull[INDEX_MAX_KEYS]); extern HeapTuple index_getnext(IndexScanDesc scan, ScanDirection direction); + extern int64 index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap); extern IndexBulkDeleteResult *index_bulk_delete(IndexVacuumInfo *info, diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h new file mode 100644 index 03e9903..f7b7aeb *** 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 *** 135,140 **** --- 137,151 ---- #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 *** 144,150 **** 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) --- 155,161 ---- 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 *** 164,175 **** /* 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; --- 175,189 ---- /* 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 */ + + /* Data structures for performing recheck of lossy knn distance */ + FmgrInfo *orderByRechecks; /* functions for lossy knn distance recheck */ } GISTScanOpaqueData; typedef GISTScanOpaqueData *GISTScanOpaque; diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h new file mode 100644 index 8a57698..5acf228 *** a/src/include/access/relscan.h --- b/src/include/access/relscan.h *************** *** 19,24 **** --- 19,25 ---- #include "access/htup_details.h" #include "access/itup.h" #include "access/tupdesc.h" + #include "nodes/execnodes.h" typedef struct HeapScanDescData *************** typedef struct IndexScanDescData *** 93,98 **** --- 94,104 ---- /* state data for traversing HOT chains in index_getnext */ bool xs_continue_hot; /* T if must keep walking HOT chain */ + + /* Data structures for getting original indexed values from heap */ + IndexInfo *indexInfo; /* index info for index tuple calculation */ + TupleTableSlot *slot; /* heap tuple slot */ + EState *estate; /* executor state for index tuple calculation */ } IndexScanDescData; /* Struct for heap-or-index scans of system tables */ diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h new file mode 100644 index 3ef5a49..dd468f6 *** a/src/include/catalog/pg_amop.h --- b/src/include/catalog/pg_amop.h *************** DATA(insert ( 2594 604 604 11 s 2577 7 *** 650,655 **** --- 650,656 ---- 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 11 s 1514 7 *** 669,674 **** --- 670,676 ---- 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 diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h new file mode 100644 index a1de336..0f505ae *** a/src/include/catalog/pg_amproc.h --- b/src/include/catalog/pg_amproc.h *************** DATA(insert ( 2594 604 604 4 2580 )); *** 205,210 **** --- 205,211 ---- 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 )); *** 212,217 **** --- 213,219 ---- 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 )); diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h new file mode 100644 index 2275c2c..3cfcbea *** a/src/include/catalog/pg_operator.h --- b/src/include/catalog/pg_operator.h *************** DATA(insert OID = 1520 ( "<->" PGNSP *** 1014,1022 **** 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 - - )); DESCR("distance between"); ! DATA(insert OID = 3591 ( "<->" PGNSP PGUID b f f 600 604 701 0 0 dist_ppoly - - )); DESCR("distance between"); DATA(insert OID = 1523 ( "<->" PGNSP PGUID b f f 718 604 701 0 0 dist_cpoly - - )); DESCR("distance between"); --- 1014,1026 ---- 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 3586 0 dist_pc - - )); DESCR("distance between"); ! 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"); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h new file mode 100644 index 617a83d..a06f925 *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** DATA(insert OID = 727 ( dist_sl PGN *** 816,821 **** --- 816,823 ---- 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 = 2585 ( gist_poly_cons *** 4023,4028 **** --- 4025,4032 ---- 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_ )); diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h new file mode 100644 index 91610d8..64f63b2 *** a/src/include/utils/geo_decls.h --- b/src/include/utils/geo_decls.h *************** extern Datum circle_diameter(PG_FUNCTION *** 394,401 **** --- 394,403 ---- 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 gist_circle_consistent(PG_F *** 419,424 **** --- 421,427 ---- 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); diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out new file mode 100644 index a2bef7a..81645bc *** a/src/test/regress/expected/create_index.out --- b/src/test/regress/expected/create_index.out *************** SELECT count(*) FROM radix_text_tbl WHER *** 366,371 **** --- 366,401 ---- 48 (1 row) + SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; + f1 + ------------------------------------------------- + ((240,359),(240,455),(337,455),(337,359)) + ((662,163),(662,187),(759,187),(759,163)) + ((1000,0),(0,1000)) + ((0,1000),(1000,1000)) + ((1346,344),(1346,403),(1444,403),(1444,344)) + ((278,1409),(278,1457),(369,1457),(369,1409)) + ((907,1156),(907,1201),(948,1201),(948,1156)) + ((1517,971),(1517,1043),(1594,1043),(1594,971)) + ((175,1820),(175,1850),(259,1850),(259,1820)) + ((2424,81),(2424,160),(2424,160),(2424,81)) + (10 rows) + + SELECT * FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10; + f1 + ----------------------------------- + <(288.5,407),68.2367203197809> + <(710.5,175),49.9624859269432> + <(323.5,1433),51.4417145903983> + <(927.5,1178.5),30.4384625104489> + <(1395,373.5),57.1948424248201> + <(1555.5,1007),52.7091073724456> + <(217,1835),44.5982062419555> + <(489,2421.5),22.3886131772381> + <(2424,120.5),39.5> + <(751.5,2655),20.4022057631032> + (10 rows) + -- Now check the results from plain indexscan SET enable_seqscan = OFF; SET enable_indexscan = ON; *************** SELECT count(*) FROM radix_text_tbl WHER *** 1146,1151 **** --- 1176,1229 ---- 48 (1 row) + EXPLAIN (COSTS OFF) + SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; + QUERY PLAN + ----------------------------------------------------- + Limit + -> Index Scan using ggpolygonind on gpolygon_tbl + Order By: (f1 <-> '(0,0)'::point) + (3 rows) + + SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; + f1 + ------------------------------------------------- + ((240,359),(240,455),(337,455),(337,359)) + ((662,163),(662,187),(759,187),(759,163)) + ((1000,0),(0,1000)) + ((0,1000),(1000,1000)) + ((1346,344),(1346,403),(1444,403),(1444,344)) + ((278,1409),(278,1457),(369,1457),(369,1409)) + ((907,1156),(907,1201),(948,1201),(948,1156)) + ((1517,971),(1517,1043),(1594,1043),(1594,971)) + ((175,1820),(175,1850),(259,1850),(259,1820)) + ((2424,81),(2424,160),(2424,160),(2424,81)) + (10 rows) + + EXPLAIN (COSTS OFF) + SELECT * FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10; + QUERY PLAN + --------------------------------------------------- + Limit + -> Index Scan using ggcircleind on gcircle_tbl + Order By: (f1 <-> '(200,300)'::point) + (3 rows) + + SELECT * FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10; + f1 + ----------------------------------- + <(288.5,407),68.2367203197809> + <(710.5,175),49.9624859269432> + <(323.5,1433),51.4417145903983> + <(927.5,1178.5),30.4384625104489> + <(1395,373.5),57.1948424248201> + <(1555.5,1007),52.7091073724456> + <(217,1835),44.5982062419555> + <(489,2421.5),22.3886131772381> + <(2424,120.5),39.5> + <(751.5,2655),20.4022057631032> + (10 rows) + -- Now check the results from bitmap indexscan SET enable_seqscan = OFF; SET enable_indexscan = OFF; diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql new file mode 100644 index d4d24ef..d9bce16 *** a/src/test/regress/sql/create_index.sql --- b/src/test/regress/sql/create_index.sql *************** SELECT count(*) FROM radix_text_tbl WHER *** 220,225 **** --- 220,229 ---- SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; + SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; + + SELECT * FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10; + -- Now check the results from plain indexscan SET enable_seqscan = OFF; SET enable_indexscan = ON; *************** EXPLAIN (COSTS OFF) *** 433,438 **** --- 437,450 ---- SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; + EXPLAIN (COSTS OFF) + SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; + SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; + + EXPLAIN (COSTS OFF) + SELECT * FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10; + SELECT * FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10; + -- Now check the results from bitmap indexscan SET enable_seqscan = OFF; SET enable_indexscan = OFF;