diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml new file mode 100644 index 31ce279..b265e9c *** 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) *** 760,766 **** 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; --- 768,774 ---- 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) *** 779,784 **** --- 787,793 ---- 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) *** 791,801 **** 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 --- 800,815 ---- 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 717cb85..f2dc301 *** 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 "lib/pairingheap.h" *************** gistindex_keytest(IndexScanDesc scan, *** 56,62 **** GISTSTATE *giststate = so->giststate; ScanKey key = scan->keyData; int keySize = scan->numberOfKeys; ! double *distance_p; Relation r = scan->indexRelation; *recheck_p = false; --- 57,63 ---- 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, *** 73,79 **** 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; } --- 74,83 ---- 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, *** 171,177 **** if ((key->sk_flags & SK_ISNULL) || isNull) { /* Assume distance computes as null and sorts to the end */ ! *distance_p = get_float8_infinity(); } else { --- 175,182 ---- 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, *** 192,209 **** * 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++; --- 197,216 ---- * 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, *** 235,241 **** * sibling will be processed next. */ static void ! gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, TIDBitmap *tbm, int64 *ntids) { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; --- 242,248 ---- * 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 *** 280,286 **** /* Insert it into the queue using same distances as for this page */ memcpy(item->distances, myDistances, ! sizeof(double) * scan->numberOfOrderBys); pairingheap_add(so->queue, &item->phNode); --- 287,293 ---- /* Insert it into the queue using same distances as for this page */ memcpy(item->distances, myDistances, ! sizeof(GISTSearchTreeItemDistance) * scan->numberOfOrderBys); pairingheap_add(so->queue, &item->phNode); *************** gistScanPage(IndexScanDesc scan, GISTSea *** 368,374 **** /* Insert it into the queue using new distance data */ memcpy(item->distances, so->distances, ! sizeof(double) * scan->numberOfOrderBys); pairingheap_add(so->queue, &item->phNode); --- 375,381 ---- /* Insert it into the queue using new distance data */ memcpy(item->distances, so->distances, ! sizeof(GISTSearchTreeItemDistance) * scan->numberOfOrderBys); pairingheap_add(so->queue, &item->phNode); *************** gistScanPage(IndexScanDesc scan, GISTSea *** 380,406 **** } /* * Extract next item (in order) from search queue * * Returns a GISTSearchItem or NULL. Caller must pfree item when done with it. */ static GISTSearchItem * ! getNextGISTSearchItem(GISTScanOpaque so) { GISTSearchItem *item; if (!pairingheap_is_empty(so->queue)) { ! item = (GISTSearchItem *) pairingheap_remove_first(so->queue); } else { ! /* Done when both heaps are empty */ ! item = NULL; } - - /* Return item; caller is responsible to pfree it */ - return item; } /* --- 387,490 ---- } /* + * Do this tree item distance values needs recheck? + */ + static bool + searchTreeItemNeedDistanceRecheck(IndexScanDesc scan, GISTSearchItem *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, GISTSearchItem *item) + { + GISTScanOpaque so = (GISTScanOpaque) scan->opaque; + 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 */ + for (i = 0; i < scan->numberOfOrderBys; i++) + { + if (item->distances[i].recheck) + { + /* Re-calculate lossy distance */ + ScanKey key = scan->orderByData + i; + float8 newDistance; + + item->distances[i].recheck = false; + if (isnull[key->sk_attno - 1]) + { + item->distances[i].value = -get_float8_infinity(); + continue; + } + + newDistance = DatumGetFloat8( + FunctionCall2Coll(&so->orderByRechecks[i], + key->sk_collation, + values[key->sk_attno - 1], + key->sk_argument)); + + item->distances[i].value = newDistance; + + } + } + + pairingheap_add(so->queue, item); + } + + /* * Extract next item (in order) from search queue * * Returns a GISTSearchItem or NULL. Caller must pfree item when done with it. */ static GISTSearchItem * ! getNextGISTSearchItem(IndexScanDesc scan) { + GISTScanOpaque so = (GISTScanOpaque) scan->opaque; GISTSearchItem *item; if (!pairingheap_is_empty(so->queue)) { ! for (;;) ! { ! item = (GISTSearchItem *) pairingheap_remove_first(so->queue); ! ! /* Recheck distance from heap tuple if needed */ ! if (GISTSearchItemIsHeap(*item) && ! searchTreeItemNeedDistanceRecheck(scan, item)) ! { ! searchTreeItemDistanceRecheck(scan, item); ! continue; ! } ! return item; ! } } else { ! return NULL; } } /* *************** getNextNearest(IndexScanDesc scan) *** 414,420 **** do { ! GISTSearchItem *item = getNextGISTSearchItem(so); if (!item) break; --- 498,504 ---- do { ! GISTSearchItem *item = getNextGISTSearchItem(scan); if (!item) break; *************** gistgettuple(PG_FUNCTION_ARGS) *** 493,499 **** /* find and process the next index page */ do { ! GISTSearchItem *item = getNextGISTSearchItem(so); if (!item) PG_RETURN_BOOL(false); --- 577,583 ---- /* find and process the next index page */ do { ! GISTSearchItem *item = getNextGISTSearchItem(scan); if (!item) PG_RETURN_BOOL(false); *************** gistgetbitmap(PG_FUNCTION_ARGS) *** 544,550 **** */ for (;;) { ! GISTSearchItem *item = getNextGISTSearchItem(so); if (!item) break; --- 628,634 ---- */ 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 9fab6c8..c2692c3 *** a/src/backend/access/gist/gistproc.c --- b/src/backend/access/gist/gistproc.c *************** gist_poly_consistent(PG_FUNCTION_ARGS) *** 1089,1094 **** --- 1089,1095 ---- PG_RETURN_BOOL(result); } + /************************************************** * Circle ops **************************************************/ *************** gist_point_distance(PG_FUNCTION_ARGS) *** 1441,1443 **** --- 1442,1478 ---- 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 991858f..fab7237 *** 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" *************** pairingheap_GISTSearchItem_cmp(const pai *** 30,53 **** const GISTSearchItem *sa = (const GISTSearchItem *) a; const GISTSearchItem *sb = (const GISTSearchItem *) 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; ! } ! /* Heap items go before inner pages, to ensure a depth-first search */ ! if (GISTSearchItemIsHeap(*sa) && !GISTSearchItemIsHeap(*sb)) ! return 1; ! if (!GISTSearchItemIsHeap(*sa) && GISTSearchItemIsHeap(*sb)) ! return -1; ! return 0; ! } /* * Index AM API functions for scanning GiST indexes --- 33,65 ---- const GISTSearchItem *sa = (const GISTSearchItem *) a; const GISTSearchItem *sb = (const GISTSearchItem *) 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; ! /* Heap items go before inner pages, to ensure a depth-first search */ ! if (GISTSearchItemIsHeap(*sa) && !GISTSearchItemIsHeap(*sb)) ! return 1; ! if (!GISTSearchItemIsHeap(*sa) && GISTSearchItemIsHeap(*sb)) ! return -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_b.recheck ? 1 : -1; + } + + return recheckCmp; + } /* * Index AM API functions for scanning GiST indexes *************** gistbeginscan(PG_FUNCTION_ARGS) *** 83,91 **** so->queueCxt = giststate->scanCxt; /* see gistrescan */ /* workspaces with size dependent on numberOfOrderBys: */ ! so->distances = palloc(sizeof(double) * scan->numberOfOrderBys); so->qual_ok = true; /* in case there are zero keys */ scan->opaque = so; MemoryContextSwitchTo(oldCxt); --- 95,111 ---- so->queueCxt = giststate->scanCxt; /* see gistrescan */ /* workspaces with size dependent on 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) *** 238,243 **** --- 258,267 ---- 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 e6e4d28..90cf088 *** a/src/backend/access/index/genam.c --- b/src/backend/access/index/genam.c *************** RelationGetIndexScan(Relation indexRelat *** 124,129 **** --- 124,132 ---- 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 00c1d69..9c57311 *** 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 6cb6be5..1b2a511 *** a/src/backend/utils/adt/geo_ops.c --- b/src/backend/utils/adt/geo_ops.c *************** static Point *interpt_sl(LSEG *lseg, LIN *** 70,79 **** 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 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); - static double dist_ppoly_internal(Point *pt, POLYGON *poly); /* --- 70,79 ---- 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 double 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) *** 2623,2628 **** --- 2623,2660 ---- } /* + * 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_ppoly_internal(Point *pt, POLYGON * *** 2701,2706 **** --- 2733,2747 ---- return result; } + /* + * 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))); + } /*--------------------------------------------------------------------- * interpt_ *************** pt_contained_circle(PG_FUNCTION_ARGS) *** 5057,5079 **** } - /* 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 --- 5098,5103 ---- diff --git a/src/include/access/genam.h b/src/include/access/genam.h new file mode 100644 index d1d6247..359d488 *** 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 ce83042..27bd81c *** a/src/include/access/gist_private.h --- b/src/include/access/gist_private.h *************** *** 17,24 **** --- 17,26 ---- #include "access/gist.h" #include "access/itup.h" #include "access/xlogreader.h" + #include "executor/tuptable.h" #include "fmgr.h" #include "lib/pairingheap.h" + #include "nodes/execnodes.h" #include "storage/bufmgr.h" #include "storage/buffile.h" #include "utils/hsearch.h" *************** typedef struct GISTSearchHeapItem *** 120,125 **** --- 122,136 ---- bool recheck; /* T if quals must be rechecked */ } GISTSearchHeapItem; + /* + * KNN distance item: distance which can be rechecked from heap tuple. + */ + typedef struct GISTSearchTreeItemDistance + { + double value; + bool recheck; + } GISTSearchTreeItemDistance; + /* Unvisited item, either index page or heap tuple */ typedef struct GISTSearchItem { *************** typedef struct GISTSearchItem *** 131,143 **** /* we must store parentlsn to detect whether a split occurred */ GISTSearchHeapItem heap; /* heap info, if heap tuple */ } data; ! double distances[FLEXIBLE_ARRAY_MEMBER]; /* numberOfOrderBys ! * entries */ } GISTSearchItem; #define GISTSearchItemIsHeap(item) ((item).blkno == InvalidBlockNumber) ! #define SizeOfGISTSearchItem(n_distances) (offsetof(GISTSearchItem, distances) + sizeof(double) * (n_distances)) /* * GISTScanOpaqueData: private state for a scan of a GiST index --- 142,154 ---- /* we must store parentlsn to detect whether a split occurred */ GISTSearchHeapItem heap; /* heap info, if heap tuple */ } data; ! GISTSearchTreeItemDistance distances[FLEXIBLE_ARRAY_MEMBER]; /* array with numberOfOrderBys entries */ } GISTSearchItem; #define GISTSearchItemIsHeap(item) ((item).blkno == InvalidBlockNumber) ! #define GSTIHDRSZ offsetof(GISTSearchTreeItem, distances) ! #define SizeOfGISTSearchItem(n_distances) (offsetof(GISTSearchItem, distances) + sizeof(GISTSearchTreeItemDistance) * (n_distances)) /* * GISTScanOpaqueData: private state for a scan of a GiST index *************** typedef struct GISTScanOpaqueData *** 151,162 **** bool firstCall; /* true until first gistgettuple call */ /* pre-allocated workspace arrays */ ! 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; --- 162,176 ---- bool firstCall; /* true until first gistgettuple call */ /* pre-allocated workspace arrays */ ! 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 9bb6362..b1be157 *** 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 5aab896..4a6fa7f *** 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 49d3d13..43f77ed *** 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 af991d3..b375215 *** 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 = 3276 ( "<->" 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 = 3276 ( "<->" 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 3276 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 4268b99..7cbcc70 *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** DATA(insert OID = 727 ( dist_sl PGN *** 845,850 **** --- 845,852 ---- 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 = 3275 ( 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 *** 4076,4081 **** --- 4078,4085 ---- 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 8da6c6c..4632f64 *** a/src/include/utils/geo_decls.h --- b/src/include/utils/geo_decls.h *************** extern Datum circle_diameter(PG_FUNCTION *** 392,399 **** --- 392,401 ---- 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 *** 417,422 **** --- 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_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 5603817..cb18986 *** a/src/test/regress/expected/create_index.out --- b/src/test/regress/expected/create_index.out *************** SELECT count(*) FROM radix_text_tbl WHER *** 372,377 **** --- 372,407 ---- 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 *** 1152,1157 **** --- 1182,1235 ---- 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 f779fa0..5df9008 *** a/src/test/regress/sql/create_index.sql --- b/src/test/regress/sql/create_index.sql *************** SELECT count(*) FROM radix_text_tbl WHER *** 224,229 **** --- 224,233 ---- 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) *** 437,442 **** --- 441,454 ---- 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;