diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml new file mode 100644 index 31ce279..c354411 *** 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_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,804 **** 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 minus infinity are used internally to handle cases such as nulls, so it is not recommended that distance functions return these values.) --- 800,821 ---- The arguments to the distance function are identical to ! the arguments of the consistent function. ! ! ! ! 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. For ! leaf nodes, the returned distance must be accurate, if the ! distance function returns *recheck == false for the tuple. ! Otherwise the same approximation is allowed, and the executor will ! re-order ambiguous cases after recalculating the actual distance. ! ! ! ! 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 new file mode 100644 index 717cb85..801e969 *** a/src/backend/access/gist/gistget.c --- b/src/backend/access/gist/gistget.c *************** gistindex_keytest(IndexScanDesc scan, *** 176,181 **** --- 176,182 ---- else { Datum dist; + bool recheck; GISTENTRY de; gistdentryinit(giststate, key->sk_attno - 1, &de, *************** gistindex_keytest(IndexScanDesc scan, *** 192,207 **** * 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); } --- 193,210 ---- * always be zero, but might as well pass it for possible future * use.) * ! * Note that Distance functions don't get a recheck argument. ! * Distance is rechecked whenever the quals are. */ ! dist = FunctionCall5Coll(&key->sk_func, key->sk_collation, PointerGetDatum(&de), key->sk_argument, Int32GetDatum(key->sk_strategy), ! ObjectIdGetDatum(key->sk_subtype), ! PointerGetDatum(&recheck)); ! ! *recheck_p |= recheck; *distance_p = DatumGetFloat8(dist); } *************** getNextNearest(IndexScanDesc scan) *** 411,416 **** --- 414,420 ---- { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; bool res = false; + int i; do { *************** getNextNearest(IndexScanDesc scan) *** 424,429 **** --- 428,438 ---- /* found a heap item at currently minimal distance */ scan->xs_ctup.t_self = item->data.heap.heapPtr; scan->xs_recheck = item->data.heap.recheck; + for (i = 0; i < scan->numberOfOrderBys; i++) + { + scan->xs_distances[i] = Float8GetDatum(item->distances[i]); + scan->xs_distance_nulls[i] = false; + } res = true; } else diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c new file mode 100644 index 9fab6c8..c1232d5 *** a/src/backend/access/gist/gistproc.c --- b/src/backend/access/gist/gistproc.c *************** gist_point_distance(PG_FUNCTION_ARGS) *** 1441,1443 **** --- 1441,1478 ---- PG_RETURN_FLOAT8(distance); } + + /* + * The inexact GiST distance method for geometric types that store bounding + * boxes. + * + * 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_bbox_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 cc8d818..066238e *** a/src/backend/access/gist/gistscan.c --- b/src/backend/access/gist/gistscan.c *************** gistbeginscan(PG_FUNCTION_ARGS) *** 85,90 **** --- 85,95 ---- /* workspaces with size dependent on numberOfOrderBys: */ so->distances = palloc(sizeof(double) * scan->numberOfOrderBys); so->qual_ok = true; /* in case there are zero keys */ + if (scan->numberOfOrderBys > 0) + { + scan->xs_distances = palloc(sizeof(Datum) * scan->numberOfOrderBys); + scan->xs_distance_nulls = palloc(sizeof(bool) * scan->numberOfOrderBys); + } scan->opaque = so; diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c new file mode 100644 index 48fa919..d46cb3d *** a/src/backend/executor/nodeIndexscan.c --- b/src/backend/executor/nodeIndexscan.c *************** *** 28,41 **** --- 28,113 ---- #include "access/relscan.h" #include "executor/execdebug.h" #include "executor/nodeIndexscan.h" + #include "lib/pairingheap.h" #include "optimizer/clauses.h" #include "utils/array.h" + #include "utils/datum.h" #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/rel.h" + /* + * When an ordering operator is used, tuples fetched from the index that + * need to be reordered are queued in a pairing heap, as ReorderTuples. + */ + typedef struct + { + pairingheap_node ph_node; + HeapTuple htup; + Datum *distances; + bool *distance_nulls; + } ReorderTuple; + + static int + cmp_distances(const Datum *adist, const bool *anulls, + const Datum *bdist, const bool *bnulls, + IndexScanState *node) + { + int i; + int result; + + for (i = 0; i < node->iss_NumOrderByKeys; i++) + { + SortSupport ssup = &node->iss_SortSupport[i]; + + /* Handle nulls. We only support NULLS LAST */ + if (anulls[i] && !bnulls[i]) + return 1; + else if (!anulls[i] && bnulls[i]) + return -1; + else if (anulls[i] && bnulls[i]) + return 0; + + result = ssup->comparator(adist[i], bdist[i], ssup); + if (result != 0) + return result; + } + + return 0; + } + + static int + reorderbuffer_cmp(const pairingheap_node *a, const pairingheap_node *b, void *arg) + { + ReorderTuple *rta = (ReorderTuple *) a; + ReorderTuple *rtb = (ReorderTuple *) b; + IndexScanState *node = (IndexScanState *) arg; + + return -cmp_distances(rta->distances, rta->distance_nulls, + rtb->distances, rtb->distance_nulls, + node); + } + + static void + copyDistances(IndexScanState *node, const Datum *src_datums, const bool *src_nulls, + Datum *dst_datums, bool *dst_nulls) + { + int i; + + for (i = 0; i < node->iss_NumOrderByKeys; i++) + { + if (!src_nulls[i]) + dst_datums[i] = datumCopy(src_datums[i], + node->iss_DistanceTypByVals[i], + node->iss_DistanceTypLens[i]); + else + dst_datums[i] = (Datum) 0; + dst_nulls[i] = src_nulls[i]; + } + } static TupleTableSlot *IndexNext(IndexScanState *node); + static void RecheckOrderBys(IndexScanState *node, TupleTableSlot *slot); /* ---------------------------------------------------------------- *************** IndexNext(IndexScanState *node) *** 54,59 **** --- 126,133 ---- IndexScanDesc scandesc; HeapTuple tuple; TupleTableSlot *slot; + MemoryContext oldContext; + ReorderTuple *reordertuple; /* * extract necessary information from index scan node *************** IndexNext(IndexScanState *node) *** 72,82 **** econtext = node->ss.ps.ps_ExprContext; slot = node->ss.ss_ScanTupleSlot; ! /* ! * ok, now that we have what we need, fetch the next tuple. ! */ ! while ((tuple = index_getnext(scandesc, direction)) != NULL) { /* * Store the scanned tuple in the scan tuple slot of the scan state. * Note: we pass 'false' because tuples returned by amgetnext are --- 146,205 ---- econtext = node->ss.ps.ps_ExprContext; slot = node->ss.ss_ScanTupleSlot; ! for (;;) { + /* Check the reorder queue first */ + if (node->iss_ReorderQueue) + { + if (pairingheap_is_empty(node->iss_ReorderQueue)) + { + if (node->iss_ReachedEnd) + break; + } + else + { + reordertuple = (ReorderTuple *) pairingheap_first(node->iss_ReorderQueue); + + /* Check if we can return this tuple */ + if (node->iss_ReachedEnd || + cmp_distances(reordertuple->distances, + reordertuple->distance_nulls, + scandesc->xs_distances, + scandesc->xs_distance_nulls, + node) < 0) + { + (void) pairingheap_remove_first(node->iss_ReorderQueue); + + tuple = reordertuple->htup; + pfree(reordertuple); + + /* + * Store the buffered tuple in the scan tuple slot of the + * scan state. + */ + ExecStoreTuple(tuple, slot, InvalidBuffer, true); + return slot; + } + } + } + + /* Fetch next tuple from the index */ + tuple = index_getnext(scandesc, direction); + + if (!tuple) + { + /* + * No more tuples from the index. If we have a reorder queue, + * we still need to drain all the remaining tuples in the queue + * before we're done. + */ + node->iss_ReachedEnd = true; + if (node->iss_ReorderQueue) + continue; + else + break; + } + /* * Store the scanned tuple in the scan tuple slot of the scan state. * Note: we pass 'false' because tuples returned by amgetnext are *************** IndexNext(IndexScanState *node) *** 103,108 **** --- 226,296 ---- } } + /* + * Re-check the ordering. + */ + if (node->iss_ReorderQueue) + { + /* + * The index returned the distance, as calculated by the indexam, + * in scandesc->xs_distances. If the index was lossy, we have to + * recheck the ordering expression too. Otherwise we take the + * indexam's values as is. + */ + if (scandesc->xs_recheck) + RecheckOrderBys(node, slot); + else + copyDistances(node, + scandesc->xs_distances, + scandesc->xs_distance_nulls, + node->iss_Distances, + node->iss_DistanceNulls); + + /* + * Can we return this tuple immediately, or does it need to be + * pushed to the reorder queue? If this tuple's distance was + * inaccurate, we can't return it yet, because the next tuple + * from the index might need to come before this one. Also, + * we can't return it yet if there are any smaller tuples in the + * queue already. + */ + if (!pairingheap_is_empty(node->iss_ReorderQueue)) + reordertuple = (ReorderTuple *) pairingheap_first(node->iss_ReorderQueue); + else + reordertuple = NULL; + + if ((cmp_distances(node->iss_Distances, + node->iss_DistanceNulls, + scandesc->xs_distances, + scandesc->xs_distance_nulls, + node) > 0) || + (reordertuple && cmp_distances(node->iss_Distances, + node->iss_DistanceNulls, + reordertuple->distances, + reordertuple->distance_nulls, + node) > 0)) + { + /* Need to put this to the queue */ + oldContext = MemoryContextSwitchTo(estate->es_query_cxt); + reordertuple = (ReorderTuple *) palloc(sizeof(ReorderTuple)); + reordertuple->htup = heap_copytuple(tuple); + reordertuple->distances = (Datum *) palloc(sizeof(Datum) * scandesc->numberOfOrderBys); + reordertuple->distance_nulls = (bool *) palloc(sizeof(bool) * scandesc->numberOfOrderBys); + copyDistances(node, + node->iss_Distances, + node->iss_DistanceNulls, + reordertuple->distances, + reordertuple->distance_nulls); + + pairingheap_add(node->iss_ReorderQueue, &reordertuple->ph_node); + + MemoryContextSwitchTo(oldContext); + + continue; + } + } + + /* Ok, got a tuple to return */ return slot; } *************** IndexNext(IndexScanState *node) *** 114,119 **** --- 302,342 ---- } /* + * Calculate the expressions in the ORDER BY clause, based on the heap tuple. + */ + static void + RecheckOrderBys(IndexScanState *node, TupleTableSlot *slot) + { + IndexScanDesc scandesc; + ExprContext *econtext; + int i; + ListCell *l; + MemoryContext oldContext; + + scandesc = node->iss_ScanDesc; + econtext = node->ss.ps.ps_ExprContext; + econtext->ecxt_scantuple = slot; + ResetExprContext(econtext); + + oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + i = 0; + foreach(l, node->indexorderbyorig) + { + ExprState *orderby = (ExprState *) lfirst(l); + + Assert(i < scandesc->numberOfOrderBys); + + node->iss_Distances[i] = ExecEvalExpr(orderby, + econtext, + &node->iss_DistanceNulls[i], + NULL); + } + + MemoryContextSwitchTo(oldContext); + } + + /* * IndexRecheck -- access method routine to recheck a tuple in EvalPlanQual */ static bool *************** ExecInitIndexScan(IndexScan *node, EStat *** 465,470 **** --- 688,694 ---- IndexScanState *indexstate; Relation currentRelation; bool relistarget; + int i; /* * create state structure *************** ExecInitIndexScan(IndexScan *node, EStat *** 501,506 **** --- 725,733 ---- indexstate->indexqualorig = (List *) ExecInitExpr((Expr *) node->indexqualorig, (PlanState *) indexstate); + indexstate->indexorderbyorig = (List *) + ExecInitExpr((Expr *) node->indexorderbyorig, + (PlanState *) indexstate); /* * tuple table initialization *************** ExecInitIndexScan(IndexScan *node, EStat *** 581,586 **** --- 808,859 ---- NULL, /* no ArrayKeys */ NULL); + /* Initialize sort support, if we need to re-check ORDER BY exprs */ + if (indexstate->iss_NumOrderByKeys > 0) + { + int numOrderByKeys = indexstate->iss_NumOrderByKeys; + + /* + * Prepare sort support, and look up the distance type for each + * ORDER BY expression. + */ + indexstate->iss_SortSupport = + palloc0(numOrderByKeys * sizeof(SortSupportData)); + indexstate->iss_DistanceTypByVals = + palloc(numOrderByKeys * sizeof(bool)); + indexstate->iss_DistanceTypLens = + palloc(numOrderByKeys * sizeof(int16)); + for (i = 0; i < indexstate->iss_NumOrderByKeys; i++) + { + Oid distanceType; + Oid opfamily; + int16 strategy; + + PrepareSortSupportFromOrderingOp(node->indexsortops[i], + &indexstate->iss_SortSupport[i]); + + if (!get_ordering_op_properties(node->indexsortops[i], + &opfamily, &distanceType, &strategy)) + { + elog(LOG, "operator %u is not a valid ordering operator", + node->indexsortops[i]); + } + get_typlenbyval(distanceType, + &indexstate->iss_DistanceTypLens[i], + &indexstate->iss_DistanceTypByVals[i]); + } + + /* allocate arrays to hold the re-calculated distances */ + indexstate->iss_Distances = + palloc(indexstate->iss_NumOrderByKeys * sizeof(Datum)); + indexstate->iss_DistanceNulls = + palloc(indexstate->iss_NumOrderByKeys * sizeof(bool)); + + /* and initialize the reorder queue */ + indexstate->iss_ReorderQueue = pairingheap_allocate(reorderbuffer_cmp, + indexstate); + } + /* * If we have runtime keys, we need an ExprContext to evaluate them. The * node's standard context won't do because we want to reset that context diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c new file mode 100644 index 655be81..bb71638 *** a/src/backend/optimizer/plan/createplan.c --- b/src/backend/optimizer/plan/createplan.c *************** *** 22,27 **** --- 22,28 ---- #include "access/skey.h" #include "access/sysattr.h" #include "catalog/pg_class.h" + #include "catalog/pg_operator.h" #include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" *************** static void copy_plan_costsize(Plan *des *** 102,108 **** static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid); static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid, Oid indexid, List *indexqual, List *indexqualorig, ! List *indexorderby, List *indexorderbyorig, ScanDirection indexscandir); static IndexOnlyScan *make_indexonlyscan(List *qptlist, List *qpqual, Index scanrelid, Oid indexid, --- 103,109 ---- static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid); static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid, Oid indexid, List *indexqual, List *indexqualorig, ! List *indexorderby, List *indexorderbyorig, Oid *sortOperators, ScanDirection indexscandir); static IndexOnlyScan *make_indexonlyscan(List *qptlist, List *qpqual, Index scanrelid, Oid indexid, *************** static Plan *prepare_sort_from_pathkeys( *** 168,174 **** Oid **p_collations, bool **p_nullsFirst); static EquivalenceMember *find_ec_member_for_tle(EquivalenceClass *ec, ! TargetEntry *tle, Relids relids); static Material *make_material(Plan *lefttree); --- 169,175 ---- Oid **p_collations, bool **p_nullsFirst); static EquivalenceMember *find_ec_member_for_tle(EquivalenceClass *ec, ! Expr *tlexpr, Relids relids); static Material *make_material(Plan *lefttree); *************** create_indexscan_plan(PlannerInfo *root, *** 1158,1163 **** --- 1159,1165 ---- List *stripped_indexquals; List *fixed_indexquals; List *fixed_indexorderbys; + Oid *sortOperators = NULL; ListCell *l; /* it should be a base rel... */ *************** create_indexscan_plan(PlannerInfo *root, *** 1266,1271 **** --- 1268,1303 ---- replace_nestloop_params(root, (Node *) indexorderbys); } + if (best_path->path.pathkeys && indexorderbys) + { + int numOrderBys = list_length(indexorderbys); + int i; + ListCell *pathkeyCell, *exprCell; + PathKey *pathkey; + Expr *expr; + + sortOperators = (Oid *) palloc(numOrderBys * sizeof(Oid)); + + /* + * Get ordering operator for each pathkey. + */ + i = 0; + forboth (pathkeyCell, best_path->path.pathkeys, exprCell, indexorderbys) + { + EquivalenceMember *em; + pathkey = (PathKey *)lfirst(pathkeyCell); + expr = (Expr *)lfirst(exprCell); + + em = find_ec_member_for_tle(pathkey->pk_eclass, expr, NULL); + + sortOperators[i] = get_opfamily_member(pathkey->pk_opfamily, + em->em_datatype, + em->em_datatype, + pathkey->pk_strategy); + i++; + } + } + /* Finally ready to build the plan node */ if (indexonly) scan_plan = (Scan *) make_indexonlyscan(tlist, *************** create_indexscan_plan(PlannerInfo *root, *** 1285,1290 **** --- 1317,1323 ---- stripped_indexquals, fixed_indexorderbys, indexorderbys, + sortOperators, best_path->indexscandir); copy_path_costsize(&scan_plan->plan, &best_path->path); *************** make_indexscan(List *qptlist, *** 3327,3332 **** --- 3360,3366 ---- List *indexqualorig, List *indexorderby, List *indexorderbyorig, + Oid *sortOperators, ScanDirection indexscandir) { IndexScan *node = makeNode(IndexScan); *************** make_indexscan(List *qptlist, *** 3344,3349 **** --- 3378,3384 ---- node->indexorderby = indexorderby; node->indexorderbyorig = indexorderbyorig; node->indexorderdir = indexscandir; + node->indexsortops = sortOperators; return node; } *************** prepare_sort_from_pathkeys(PlannerInfo * *** 3967,3973 **** tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]); if (tle) { ! em = find_ec_member_for_tle(ec, tle, relids); if (em) { /* found expr at right place in tlist */ --- 4002,4008 ---- tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]); if (tle) { ! em = find_ec_member_for_tle(ec, tle->expr, relids); if (em) { /* found expr at right place in tlist */ *************** prepare_sort_from_pathkeys(PlannerInfo * *** 3998,4004 **** foreach(j, tlist) { tle = (TargetEntry *) lfirst(j); ! em = find_ec_member_for_tle(ec, tle, relids); if (em) { /* found expr already in tlist */ --- 4033,4039 ---- foreach(j, tlist) { tle = (TargetEntry *) lfirst(j); ! em = find_ec_member_for_tle(ec, tle->expr, relids); if (em) { /* found expr already in tlist */ *************** prepare_sort_from_pathkeys(PlannerInfo * *** 4126,4139 **** */ static EquivalenceMember * find_ec_member_for_tle(EquivalenceClass *ec, ! TargetEntry *tle, Relids relids) { - Expr *tlexpr; ListCell *lc; /* We ignore binary-compatible relabeling on both ends */ - tlexpr = tle->expr; while (tlexpr && IsA(tlexpr, RelabelType)) tlexpr = ((RelabelType *) tlexpr)->arg; --- 4161,4172 ---- */ static EquivalenceMember * find_ec_member_for_tle(EquivalenceClass *ec, ! Expr *tlexpr, Relids relids) { ListCell *lc; /* We ignore binary-compatible relabeling on both ends */ while (tlexpr && IsA(tlexpr, RelabelType)) tlexpr = ((RelabelType *) tlexpr)->arg; diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c new file mode 100644 index 6b6510e..bf8a5da *** a/src/backend/utils/adt/geo_ops.c --- b/src/backend/utils/adt/geo_ops.c *************** dist_ppoly(PG_FUNCTION_ARGS) *** 2657,2662 **** --- 2657,2674 ---- PG_RETURN_FLOAT8(result); } + Datum + dist_polyp(PG_FUNCTION_ARGS) + { + POLYGON *poly = PG_GETARG_POLYGON_P(0); + Point *point = PG_GETARG_POINT_P(1); + float8 result; + + result = dist_ppoly_internal(point, poly); + + PG_RETURN_FLOAT8(result); + } + static double dist_ppoly_internal(Point *pt, POLYGON *poly) { *************** dist_pc(PG_FUNCTION_ARGS) *** 5073,5078 **** --- 5085,5105 ---- 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); + } /* circle_center - returns the center point of the circle. */ 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/relscan.h b/src/include/access/relscan.h new file mode 100644 index 9bb6362..e1f2031 *** a/src/include/access/relscan.h --- b/src/include/access/relscan.h *************** typedef struct IndexScanDescData *** 91,96 **** --- 91,105 ---- /* NB: if xs_cbuf is not InvalidBuffer, we hold a pin on that buffer */ bool xs_recheck; /* T means scan keys must be rechecked */ + /* + * If fetching with an ordering operator, the "distance" of the last + * returned heap tuple according to the index. If xs_recheck is true, + * this needs to be rechecked just like the scan keys, and the value + * returned here is a lower-bound on the actual distance. + */ + Datum *xs_distances; + bool *xs_distance_nulls; + /* state data for traversing HOT chains in index_getnext */ bool xs_continue_hot; /* T if must keep walking HOT chain */ } IndexScanDescData; 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..6e0df88 *** 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 9edfdb8..553cd24 *** 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 = 2179 ( gist_point_con *** 4084,4089 **** --- 4086,4093 ---- 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"); + DATA(insert OID = 3589 ( gist_bbox_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_bbox_distance _null_ _null_ _null_ )); + DESCR("GiST support"); /* GIN */ DATA(insert OID = 2731 ( gingetbitmap PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 20 "2281 2281" _null_ _null_ _null_ _null_ gingetbitmap _null_ _null_ _null_ )); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h new file mode 100644 index 41288ed..cc05fae *** a/src/include/nodes/execnodes.h --- b/src/include/nodes/execnodes.h *************** *** 17,22 **** --- 17,23 ---- #include "access/genam.h" #include "access/heapam.h" #include "executor/instrument.h" + #include "lib/pairingheap.h" #include "nodes/params.h" #include "nodes/plannodes.h" #include "utils/reltrigger.h" *************** typedef struct *** 1237,1242 **** --- 1238,1244 ---- * IndexScanState information * * indexqualorig execution state for indexqualorig expressions + * indexorderbyorig execution state for indexorderbyorig expressions * ScanKeys Skey structures for index quals * NumScanKeys number of ScanKeys * OrderByKeys Skey structures for index ordering operators *************** typedef struct *** 1247,1258 **** --- 1249,1268 ---- * RuntimeContext expr context for evaling runtime Skeys * RelationDesc index relation descriptor * ScanDesc index scan descriptor + * + * ReorderQueue queue of re-check tuples that need reordering + * Distances re-checked distances of last fetched tuple + * SortSupport for re-ordering ORDER BY exprs + * ReachedEnd have we fetched all tuples from index already? + * DistanceTypByVals is the datatype of order by expression pass-by-value? + * DistanceTypLens typlens of the datatypes of order by expressions * ---------------- */ typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ List *indexqualorig; + List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; ScanKey iss_OrderByKeys; *************** typedef struct IndexScanState *** 1263,1268 **** --- 1273,1287 ---- ExprContext *iss_RuntimeContext; Relation iss_RelationDesc; IndexScanDesc iss_ScanDesc; + + /* These are needed for re-checking ORDER BY expr ordering */ + pairingheap *iss_ReorderQueue; + Datum *iss_Distances; + bool *iss_DistanceNulls; + SortSupport iss_SortSupport; + bool *iss_DistanceTypByVals; + int16 *iss_DistanceTypLens; + bool iss_ReachedEnd; } IndexScanState; /* ---------------- diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h new file mode 100644 index 316c9ce..e171333 *** a/src/include/nodes/plannodes.h --- b/src/include/nodes/plannodes.h *************** typedef Scan SeqScan; *** 302,308 **** * index column order. Only the expressions are provided, not the auxiliary * sort-order information from the ORDER BY SortGroupClauses; it's assumed * that the sort ordering is fully determinable from the top-level operators. ! * indexorderbyorig is unused at run time, but is needed for EXPLAIN. * (Note these fields are used for amcanorderbyop cases, not amcanorder cases.) * * indexorderdir specifies the scan ordering, for indexscans on amcanorder --- 302,312 ---- * index column order. Only the expressions are provided, not the auxiliary * sort-order information from the ORDER BY SortGroupClauses; it's assumed * that the sort ordering is fully determinable from the top-level operators. ! * indexorderbyorig is used at run time to recheck the ordering, if the index ! * does not calculate an accurate ordering. It is also needed for EXPLAIN. ! * ! * indexsortops is an array of operators used to sort the ORDER BY expressions, ! * used together with indexorderbyorig to recheck ordering at run time. * (Note these fields are used for amcanorderbyop cases, not amcanorder cases.) * * indexorderdir specifies the scan ordering, for indexscans on amcanorder *************** typedef struct IndexScan *** 316,322 **** List *indexqual; /* list of index quals (usually OpExprs) */ List *indexqualorig; /* the same in original form */ List *indexorderby; /* list of index ORDER BY exprs */ ! List *indexorderbyorig; /* the same in original form */ ScanDirection indexorderdir; /* forward or backward or don't care */ } IndexScan; --- 320,327 ---- List *indexqual; /* list of index quals (usually OpExprs) */ List *indexqualorig; /* the same in original form */ List *indexorderby; /* list of index ORDER BY exprs */ ! List *indexorderbyorig; /* the same in original form */ ! Oid *indexsortops; /* OIDs of operators to sort ORDER BY exprs */ ScanDirection indexorderdir; /* forward or backward or don't care */ } IndexScan; diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h new file mode 100644 index 0b6d3c3..9f92968 *** 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_bbox_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;