diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml index e7d1ff9d83f4f9b469480dc6dfc22303a2d51c4a..1291f8dd0c8cdb89ea8105f990922b4875ae1acf 100644 --- a/doc/src/sgml/gist.sgml +++ b/doc/src/sgml/gist.sgml @@ -105,6 +105,7 @@ <literal>~=</> </entry> <entry> + <literal><-></> </entry> </row> <row> @@ -163,6 +164,7 @@ <literal>~=</> </entry> <entry> + <literal><-></> </entry> </row> <row> @@ -206,6 +208,12 @@ </tgroup> </table> + <para> + Currently, ordering by the distance operator <literal><-></> + is supported only with <literal>point</> by the operator classes + of the geometric types. + </para> + <para> For historical reasons, the <literal>inet_ops</> operator class is not the default class for types <type>inet</> and <type>cidr</>. @@ -780,6 +788,7 @@ my_distance(PG_FUNCTION_ARGS) 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; @@ -792,14 +801,24 @@ my_distance(PG_FUNCTION_ARGS) </programlisting> The arguments to the <function>distance</> function are identical to - the arguments of the <function>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 <type>float8</> value. (Infinity and + the arguments of the <function>consistent</> function. + </para> + + <para> + Some approximation is allowed when determining the distance, as long as + the result is never greater than the entry's actual distance. Thus, for + example, distance to a bounding box is usually sufficient in geometric + applications. For an internal tree node, the distance returned must not + be greater than the distance to any of the child nodes. If the returned + distance is not accurate, the function must set *recheck to false. (This + is not necessary for internal tree nodes; for them, the calculation is + always assumed to be inaccurate). The executor will calculate the + accurate distance after fetching the tuple from the heap, and reorder + the tuples if necessary. + </para> + + <para> + The result value can be any finite <type>float8</> value. (Infinity and minus infinity are used internally to handle cases such as nulls, so it is not recommended that <function>distance</> functions return these values.) diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c index e4c00c2c9f5adf8b7e4e65024443b51766afccde..1f791a4f8eeff47591dc0cbf480d97ae45bd6f5f 100644 --- a/src/backend/access/gist/gistget.c +++ b/src/backend/access/gist/gistget.c @@ -30,10 +30,10 @@ * The index tuple might represent either a heap tuple or a lower index page, * depending on whether the containing page is a leaf page or not. * - * On success return for a heap tuple, *recheck_p is set to indicate - * whether recheck is needed. We recheck if any of the consistent() functions - * request it. recheck is not interesting when examining a non-leaf entry, - * since we must visit the lower index page if there's any doubt. + * On success return for a heap tuple, *recheck_p is set to indicate whether + * recheck is needed. We recheck if any of the consistent() or distance() + * functions request it. recheck is not interesting when examining a non-leaf + * entry, since we must visit the lower index page if there's any doubt. * * If we are doing an ordered scan, so->distances[] is filled with distance * data from the distance() functions before returning success. @@ -176,6 +176,7 @@ gistindex_keytest(IndexScanDesc scan, else { Datum dist; + bool recheck; GISTENTRY de; gistdentryinit(giststate, key->sk_attno - 1, &de, @@ -192,16 +193,21 @@ gistindex_keytest(IndexScanDesc scan, * always be zero, but might as well pass it for possible future * use.) * - * Note that Distance functions don't get a recheck argument. We - * can't tolerate lossy distance calculations on leaf tuples; - * there is no opportunity to re-sort the tuples afterwards. + * Distance functions get a recheck argument as well. In this + * case the returned distance is the lower bound of distance + * and needs to be rechecked. We return single recheck flag + * which means that both quals and distances are to be + * rechecked. */ - dist = FunctionCall4Coll(&key->sk_func, + dist = FunctionCall5Coll(&key->sk_func, key->sk_collation, PointerGetDatum(&de), key->sk_argument, Int32GetDatum(key->sk_strategy), - ObjectIdGetDatum(key->sk_subtype)); + ObjectIdGetDatum(key->sk_subtype), + PointerGetDatum(&recheck)); + + *recheck_p |= recheck; *distance_p = DatumGetFloat8(dist); } @@ -434,6 +440,7 @@ getNextNearest(IndexScanDesc scan) { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; bool res = false; + int i; if (scan->xs_itup) { @@ -454,6 +461,11 @@ getNextNearest(IndexScanDesc scan) /* 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_orderbyvals[i] = Float8GetDatum(item->distances[i]); + scan->xs_orderbynulls[i] = false; + } /* in an index-only scan, also return the reconstructed tuple. */ if (scan->xs_want_itup) diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index 9d21e3fb947eea13ff7a23b4a3f535679a849d05..9667e397ce470764f36d5353f2428a0bdc173ab2 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -1478,3 +1478,40 @@ gist_point_distance(PG_FUNCTION_ARGS) 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 a 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; + + /* Bounding box distance is always inexact. */ + *recheck = true; + + switch (strategyGroup) + { + case PointStrategyNumberGroup: + distance = computeDistance(false, + DatumGetBoxP(entry->key), + PG_GETARG_POINT_P(1)); + break; + default: + elog(ERROR, "unknown strategy number: %d", strategy); + distance = 0.0; /* keep compiler quiet */ + } + + PG_RETURN_FLOAT8(distance); +} diff --git a/src/backend/access/gist/gistscan.c b/src/backend/access/gist/gistscan.c index 6f6539823026052b2690469c373ee564898fa2a8..099849a606b1e32aeea4f16393eb5868186ead99 100644 --- a/src/backend/access/gist/gistscan.c +++ b/src/backend/access/gist/gistscan.c @@ -85,6 +85,11 @@ gistbeginscan(PG_FUNCTION_ARGS) /* 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_orderbyvals = palloc(sizeof(Datum) * scan->numberOfOrderBys); + scan->xs_orderbynulls = palloc(sizeof(bool) * scan->numberOfOrderBys); + } scan->opaque = so; diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 48fa91981ff2a10820e6a764035d20ebb4d857bd..2164da0c84788ff1455404f452ed874328cea2b7 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -16,6 +16,7 @@ * INTERFACE ROUTINES * ExecIndexScan scans a relation using an index * IndexNext retrieve next tuple using index + * IndexNextWithReorder same, but recheck ORDER BY expressions * ExecInitIndexScan creates and initializes state info. * ExecReScanIndexScan rescans the indexed relation. * ExecEndIndexScan releases all storage. @@ -28,14 +29,38 @@ #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 *orderbyvals; + bool *orderbynulls; +} ReorderTuple; static TupleTableSlot *IndexNext(IndexScanState *node); +static TupleTableSlot *IndexNextWithReorder(IndexScanState *node); +static void EvalOrderByExpressions(IndexScanState *node, ExprContext *econtext); +static bool IndexRecheck(IndexScanState *node, TupleTableSlot *slot); +static int cmp_orderbyvals(const Datum *adist, const bool *anulls, + const Datum *bdist, const bool *bnulls, + IndexScanState *node); +static int reorderqueue_cmp(const pairingheap_node *a, + const pairingheap_node *b, void *arg); +static void reorderqueue_push(IndexScanState *node, HeapTuple tuple, + Datum *orderbyvals, bool *orderbynulls); +static HeapTuple reorderqueue_pop(IndexScanState *node); /* ---------------------------------------------------------------- @@ -106,6 +131,169 @@ IndexNext(IndexScanState *node) return slot; } + /* + * if we get here it means the index scan failed so we are at the end of + * the scan.. + */ + node->iss_ReachedEnd = true; + return ExecClearTuple(slot); +} + +/* ---------------------------------------------------------------- + * IndexNextWithReorder + * + * Like IndexNext, but his version can also re-check any + * ORDER BY expressions, and reorder the tuples as necessary. + * ---------------------------------------------------------------- + */ +static TupleTableSlot * +IndexNextWithReorder(IndexScanState *node) +{ + ExprContext *econtext; + IndexScanDesc scandesc; + HeapTuple tuple; + TupleTableSlot *slot; + ReorderTuple *topmost = NULL; + bool was_exact; + Datum *lastfetched_vals; + bool *lastfetched_nulls; + int cmp; + + /* only forward scan is supported with reordering. */ + Assert(!ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indexorderdir)); + Assert(ScanDirectionIsForward(node->ss.ps.state->es_direction)); + scandesc = node->iss_ScanDesc; + econtext = node->ss.ps.ps_ExprContext; + slot = node->ss.ss_ScanTupleSlot; + + for (;;) + { + /* + * Check the reorder queue first. If the topmost tuple in the queue + * has an ORDER BY value smaller than (or equal to) the value last + * returned by the index, we can return it now. + */ + if (!pairingheap_is_empty(node->iss_ReorderQueue)) + { + topmost = (ReorderTuple *) pairingheap_first(node->iss_ReorderQueue); + + if (node->iss_ReachedEnd || + cmp_orderbyvals(topmost->orderbyvals, + topmost->orderbynulls, + scandesc->xs_orderbyvals, + scandesc->xs_orderbynulls, + node) <= 0) + { + tuple = reorderqueue_pop(node); + + /* Pass 'true', as the tuple in the queue is a palloc'd copy */ + ExecStoreTuple(tuple, slot, InvalidBuffer, true); + return slot; + } + } + else if (node->iss_ReachedEnd) + { + /* Queue is empty, and no more tuples from index. We're done. */ + return ExecClearTuple(slot); + } + + /* + * Fetch next tuple from the index. + */ +next_indextuple: + tuple = index_getnext(scandesc, ForwardScanDirection); + if (!tuple) + { + /* + * No more tuples from the index. But we still need to drain any + * remaining tuples from the queue before we're done. + */ + node->iss_ReachedEnd = true; + continue; + } + + /* + * Store the scanned tuple in the scan tuple slot of the scan state. + * Note: we pass 'false' because tuples returned by amgetnext are + * pointers onto disk pages and must not be pfree()'d. + */ + ExecStoreTuple(tuple, /* tuple to store */ + slot, /* slot to store in */ + scandesc->xs_cbuf, /* buffer containing tuple */ + false); /* don't pfree */ + + /* + * If the index was lossy, we have to recheck the index quals and + * ORDER BY expressions using the fetched tuple. + */ + if (scandesc->xs_recheck) + { + econtext->ecxt_scantuple = slot; + ResetExprContext(econtext); + if (!ExecQual(node->indexqualorig, econtext, false)) + { + /* Fails recheck, so drop it and loop back for another */ + InstrCountFiltered2(node, 1); + goto next_indextuple; + } + + EvalOrderByExpressions(node, econtext); + + /* + * Was the ORDER BY value returned by the index accurate? The + * recheck flag means that the index can return inaccurate values, + * but then again, the value returned for any particular tuple + * could also be exactly correct. Compare the value returned by + * the index with the recalculated value. (If the value returned + * by the index happened to be exact right, we can often avoid + * pushing the tuple to the queue, just to pop it back out again.) + */ + cmp = cmp_orderbyvals(node->iss_OrderByValues, + node->iss_OrderByNulls, + scandesc->xs_orderbyvals, + scandesc->xs_orderbynulls, + node); + if (cmp < 0) + elog(ERROR, "index returned tuples in wrong order"); + else if (cmp == 0) + was_exact = true; + else + was_exact = false; + lastfetched_vals = node->iss_OrderByValues; + lastfetched_nulls = node->iss_OrderByNulls; + } + else + { + was_exact = true; + lastfetched_vals = scandesc->xs_orderbyvals; + lastfetched_nulls = scandesc->xs_orderbynulls; + } + + /* + * Can we return this tuple immediately, or does it need to be pushed + * to the reorder queue? If the ORDER BY expression values returned + * by the index were 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 (!was_exact || (topmost && cmp_orderbyvals(lastfetched_vals, + lastfetched_nulls, + topmost->orderbyvals, + topmost->orderbynulls, + node) > 0)) + { + /* Put this tuple to the queue */ + reorderqueue_push(node, tuple, lastfetched_vals, lastfetched_nulls); + continue; + } + else + { + /* Can return this tuple immediately. */ + return slot; + } + } + /* * if we get here it means the index scan failed so we are at the end of * the scan.. @@ -113,6 +301,33 @@ IndexNext(IndexScanState *node) return ExecClearTuple(slot); } +/* + * Calculate the expressions in the ORDER BY clause, based on the heap tuple. + */ +static void +EvalOrderByExpressions(IndexScanState *node, ExprContext *econtext) +{ + int i; + ListCell *l; + MemoryContext oldContext; + + oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + i = 0; + foreach(l, node->indexorderbyorig) + { + ExprState *orderby = (ExprState *) lfirst(l); + + node->iss_OrderByValues[i] = ExecEvalExpr(orderby, + econtext, + &node->iss_OrderByNulls[i], + NULL); + i++; + } + + MemoryContextSwitchTo(oldContext); +} + /* * IndexRecheck -- access method routine to recheck a tuple in EvalPlanQual */ @@ -134,6 +349,109 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) return ExecQual(node->indexqualorig, econtext, false); } + +/* + * Compare ORDER BY expression values. + */ +static int +cmp_orderbyvals(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; +} + +/* + * Pairing heap provides getting topmost (greatest) element while KNN provides + * ascending sort. That's why we inverse the sort order. + */ +static int +reorderqueue_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_orderbyvals(rta->orderbyvals, rta->orderbynulls, + rtb->orderbyvals, rtb->orderbynulls, + node); +} + +/* + * Helper function to push a tuple to the reorder queue. + */ +static void +reorderqueue_push(IndexScanState *node, HeapTuple tuple, + Datum *orderbyvals, bool *orderbynulls) +{ + IndexScanDesc scandesc = node->iss_ScanDesc; + EState *estate = node->ss.ps.state; + MemoryContext oldContext = MemoryContextSwitchTo(estate->es_query_cxt); + ReorderTuple *rt; + int i; + + rt = (ReorderTuple *) palloc(sizeof(ReorderTuple)); + rt->htup = heap_copytuple(tuple); + rt->orderbyvals = + (Datum *) palloc(sizeof(Datum) * scandesc->numberOfOrderBys); + rt->orderbynulls = + (bool *) palloc(sizeof(bool) * scandesc->numberOfOrderBys); + for (i = 0; i < node->iss_NumOrderByKeys; i++) + { + if (!orderbynulls[i]) + rt->orderbyvals[i] = datumCopy(orderbyvals[i], + node->iss_OrderByTypByVals[i], + node->iss_OrderByTypLens[i]); + else + rt->orderbyvals[i] = (Datum) 0; + rt->orderbynulls[i] = orderbynulls[i]; + } + pairingheap_add(node->iss_ReorderQueue, &rt->ph_node); + + MemoryContextSwitchTo(oldContext); +} + +/* + * Helper function to pop the next tuple from the reorder queue. + */ +static HeapTuple +reorderqueue_pop(IndexScanState *node) +{ + HeapTuple result; + ReorderTuple *topmost; + + topmost = (ReorderTuple *) pairingheap_remove_first(node->iss_ReorderQueue); + + result = topmost->htup; + pfree(topmost->orderbyvals); + pfree(topmost->orderbynulls); + pfree(topmost); + + return result; +} + + /* ---------------------------------------------------------------- * ExecIndexScan(node) * ---------------------------------------------------------------- @@ -147,9 +465,14 @@ ExecIndexScan(IndexScanState *node) if (node->iss_NumRuntimeKeys != 0 && !node->iss_RuntimeKeysReady) ExecReScan((PlanState *) node); - return ExecScan(&node->ss, - (ExecScanAccessMtd) IndexNext, - (ExecScanRecheckMtd) IndexRecheck); + if (node->iss_NumOrderByKeys > 0) + return ExecScan(&node->ss, + (ExecScanAccessMtd) IndexNextWithReorder, + (ExecScanRecheckMtd) IndexRecheck); + else + return ExecScan(&node->ss, + (ExecScanAccessMtd) IndexNext, + (ExecScanRecheckMtd) IndexRecheck); } /* ---------------------------------------------------------------- @@ -465,6 +788,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) IndexScanState *indexstate; Relation currentRelation; bool relistarget; + int i; /* * create state structure @@ -501,6 +825,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) indexstate->indexqualorig = (List *) ExecInitExpr((Expr *) node->indexqualorig, (PlanState *) indexstate); + indexstate->indexorderbyorig = (List *) + ExecInitExpr((Expr *) node->indexorderbyorig, + (PlanState *) indexstate); /* * tuple table initialization @@ -581,6 +908,52 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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_OrderByTypByVals = + palloc(numOrderByKeys * sizeof(bool)); + indexstate->iss_OrderByTypLens = + palloc(numOrderByKeys * sizeof(int16)); + for (i = 0; i < indexstate->iss_NumOrderByKeys; i++) + { + Oid orderbyType; + Oid opfamily; + int16 strategy; + + PrepareSortSupportFromOrderingOp(node->indexorderbyops[i], + &indexstate->iss_SortSupport[i]); + + if (!get_ordering_op_properties(node->indexorderbyops[i], + &opfamily, &orderbyType, &strategy)) + { + elog(LOG, "operator %u is not a valid ordering operator", + node->indexorderbyops[i]); + } + get_typlenbyval(orderbyType, + &indexstate->iss_OrderByTypLens[i], + &indexstate->iss_OrderByTypByVals[i]); + } + + /* allocate arrays to hold the re-calculated distances */ + indexstate->iss_OrderByValues = + palloc(indexstate->iss_NumOrderByKeys * sizeof(Datum)); + indexstate->iss_OrderByNulls = + palloc(indexstate->iss_NumOrderByKeys * sizeof(bool)); + + /* and initialize the reorder queue */ + indexstate->iss_ReorderQueue = pairingheap_allocate(reorderqueue_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 index c809237283198ad4d29e6ddce1acd057371cf6bb..783e34b4fbc228f19653b26d4213be88af43863e 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -22,6 +22,7 @@ #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" @@ -102,7 +103,7 @@ static void copy_plan_costsize(Plan *dest, Plan *src); 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, + List *indexorderby, List *indexorderbyorig, Oid *indexorderbyops, ScanDirection indexscandir); static IndexOnlyScan *make_indexonlyscan(List *qptlist, List *qpqual, Index scanrelid, Oid indexid, @@ -167,8 +168,8 @@ static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Oid **p_sortOperators, Oid **p_collations, bool **p_nullsFirst); -static EquivalenceMember *find_ec_member_for_tle(EquivalenceClass *ec, - TargetEntry *tle, +static EquivalenceMember *find_ec_member_for_expr(EquivalenceClass *ec, + Expr *tlexpr, Relids relids); static Material *make_material(Plan *lefttree); @@ -1158,6 +1159,7 @@ create_indexscan_plan(PlannerInfo *root, List *stripped_indexquals; List *fixed_indexquals; List *fixed_indexorderbys; + Oid *indexorderbyops = NULL; ListCell *l; /* it should be a base rel... */ @@ -1269,6 +1271,46 @@ create_indexscan_plan(PlannerInfo *root, replace_nestloop_params(root, (Node *) indexorderbys); } + /* + * If there are ORDER BY expressions, look up the sort operators for + * their datatypes. + */ + if (best_path->path.pathkeys && indexorderbys) + { + int numOrderBys = list_length(indexorderbys); + int i; + ListCell *pathkeyCell, + *exprCell; + PathKey *pathkey; + Expr *expr; + EquivalenceMember *em; + + indexorderbyops = (Oid *) palloc(numOrderBys * sizeof(Oid)); + + /* + * PathKey contains pointer to the equivalence class, but that's not + * enough because we need the expression's datatype to look up the + * sort operator in the operator family. We have to dig the + * equivalence member for the datatype. + */ + i = 0; + forboth (pathkeyCell, best_path->path.pathkeys, exprCell, indexorderbys) + { + pathkey = (PathKey *) lfirst(pathkeyCell); + expr = (Expr *) lfirst(exprCell); + + /* Find equivalence member for the order by expression */ + em = find_ec_member_for_expr(pathkey->pk_eclass, expr, NULL); + + /* Get sort operator from opfamily */ + indexorderbyops[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, @@ -1288,6 +1330,7 @@ create_indexscan_plan(PlannerInfo *root, stripped_indexquals, fixed_indexorderbys, indexorderbys, + indexorderbyops, best_path->indexscandir); copy_path_costsize(&scan_plan->plan, &best_path->path); @@ -3344,6 +3387,7 @@ make_indexscan(List *qptlist, List *indexqualorig, List *indexorderby, List *indexorderbyorig, + Oid *indexorderbyops, ScanDirection indexscandir) { IndexScan *node = makeNode(IndexScan); @@ -3360,6 +3404,7 @@ make_indexscan(List *qptlist, node->indexqualorig = indexqualorig; node->indexorderby = indexorderby; node->indexorderbyorig = indexorderbyorig; + node->indexorderbyops = indexorderbyops; node->indexorderdir = indexscandir; return node; @@ -3990,7 +4035,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]); if (tle) { - em = find_ec_member_for_tle(ec, tle, relids); + em = find_ec_member_for_expr(ec, tle->expr, relids); if (em) { /* found expr at right place in tlist */ @@ -4021,7 +4066,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, foreach(j, tlist) { tle = (TargetEntry *) lfirst(j); - em = find_ec_member_for_tle(ec, tle, relids); + em = find_ec_member_for_expr(ec, tle->expr, relids); if (em) { /* found expr already in tlist */ @@ -4142,23 +4187,21 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, } /* - * find_ec_member_for_tle - * Locate an EquivalenceClass member matching the given TLE, if any + * find_ec_member_for_expr + * Locate an EquivalenceClass member matching the given expression, if any * * Child EC members are ignored unless they match 'relids'. */ static EquivalenceMember * -find_ec_member_for_tle(EquivalenceClass *ec, - TargetEntry *tle, - Relids relids) +find_ec_member_for_expr(EquivalenceClass *ec, + Expr *expr, + 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; + while (expr && IsA(expr, RelabelType)) + expr = ((RelabelType *) expr)->arg; foreach(lc, ec->ec_members) { @@ -4184,7 +4227,7 @@ find_ec_member_for_tle(EquivalenceClass *ec, while (emexpr && IsA(emexpr, RelabelType)) emexpr = ((RelabelType *) emexpr)->arg; - if (equal(emexpr, tlexpr)) + if (equal(emexpr, expr)) return em; } diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c index 39a78552410b44c60da235ca28526576c9022eca..a3af08ad6b00e160e7a4528d6e1e8ad34f861082 100644 --- a/src/backend/utils/adt/geo_ops.c +++ b/src/backend/utils/adt/geo_ops.c @@ -2657,6 +2657,18 @@ dist_ppoly(PG_FUNCTION_ARGS) 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) { @@ -5112,6 +5124,21 @@ dist_pc(PG_FUNCTION_ARGS) 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 index d86590ac111e6064c06a85b4dbc7b9979b9192b2..f129c4b58f9f9bf4cd0731a4f0dc3b14c6cb8ac1 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -147,7 +147,10 @@ extern void index_restrpos(IndexScanDesc scan); 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 index 9bb63622fe889eb2adf49105147edacfdad3c352..865d36403a2113a463f27a6e714e521ddc4247e0 100644 --- a/src/include/access/relscan.h +++ b/src/include/access/relscan.h @@ -91,6 +91,15 @@ typedef struct IndexScanDescData /* NB: if xs_cbuf is not InvalidBuffer, we hold a pin on that buffer */ bool xs_recheck; /* T means scan keys must be rechecked */ + /* + * When fetching with an ordering operator, the values of the ORDER BY + * expressions of the last returned tuple, according to the index. If + * xs_recheck is true, these need to be rechecked just like the scan keys, + * and the values returned here are a lower-bound on the actual values. + */ + Datum *xs_orderbyvals; + bool *xs_orderbynulls; + /* 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/catversion.h b/src/include/catalog/catversion.h index 09fdc364e03ba18a4cad6c8ce86088e55dc185a3..b6a6da9a10a78d330de9f6be40fb558f3e4ed5f4 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201505141 +#define CATALOG_VERSION_NO 201505151 #endif diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index 5aab896786fe2a805038b9dda723d8d162350f8f..9b8294fd94c0f464bcfabcef54fab934778be80c 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -650,6 +650,7 @@ DATA(insert ( 2594 604 604 11 s 2577 783 0 )); DATA(insert ( 2594 604 604 12 s 2576 783 0 )); DATA(insert ( 2594 604 604 13 s 2861 783 0 )); DATA(insert ( 2594 604 604 14 s 2860 783 0 )); +DATA(insert ( 2594 604 600 15 o 3289 783 1970 )); /* * gist circle_ops @@ -669,6 +670,7 @@ DATA(insert ( 2595 718 718 11 s 1514 783 0 )); DATA(insert ( 2595 718 718 12 s 2590 783 0 )); DATA(insert ( 2595 718 718 13 s 2865 783 0 )); DATA(insert ( 2595 718 718 14 s 2864 783 0 )); +DATA(insert ( 2595 718 600 15 o 3291 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 index e3de3b57e79cb4539b735d28f9160f30d36a838e..3111d6f4ad17b484bf9982a0034606701a796600 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -208,6 +208,7 @@ DATA(insert ( 2594 604 604 4 2580 )); DATA(insert ( 2594 604 604 5 2581 )); DATA(insert ( 2594 604 604 6 2582 )); DATA(insert ( 2594 604 604 7 2584 )); +DATA(insert ( 2594 604 604 8 3288 )); DATA(insert ( 2595 718 718 1 2591 )); DATA(insert ( 2595 718 718 2 2583 )); DATA(insert ( 2595 718 718 3 2592 )); @@ -215,6 +216,7 @@ DATA(insert ( 2595 718 718 4 2580 )); DATA(insert ( 2595 718 718 5 2581 )); DATA(insert ( 2595 718 718 6 2582 )); DATA(insert ( 2595 718 718 7 2584 )); +DATA(insert ( 2595 718 718 8 3288 )); 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 index 34ebb50ea5f7cb361831ea00626ef299c004985c..6e260cb304743eaa922ff348a007f7c068f40efd 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -1015,9 +1015,13 @@ DATA(insert OID = 1520 ( "<->" PGNSP PGUID b f f 718 718 701 1520 0 ci DESCR("distance between"); DATA(insert OID = 1521 ( "#" PGNSP PGUID l f f 0 604 23 0 0 poly_npoints - - )); DESCR("number of points"); -DATA(insert OID = 1522 ( "<->" PGNSP PGUID b f f 600 718 701 0 0 dist_pc - - )); +DATA(insert OID = 1522 ( "<->" PGNSP PGUID b f f 600 718 701 3291 0 dist_pc - - )); DESCR("distance between"); -DATA(insert OID = 3276 ( "<->" PGNSP PGUID b f f 600 604 701 0 0 dist_ppoly - - )); +DATA(insert OID = 3291 ( "<->" 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 3289 0 dist_ppoly - - )); +DESCR("distance between"); +DATA(insert OID = 3289 ( "<->" 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 index d4bc60b7d66a22e1e0bdac3944485619e626d2a0..1c9edbc3b371e46cc1c3eafc02385969669e8db7 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -856,6 +856,8 @@ DATA(insert OID = 727 ( dist_sl PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 70 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_ _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_ _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_ _null_ dist_ppoly _null_ _null_ _null_ )); +DATA(insert OID = 3292 ( 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_ _null_ dist_polyp _null_ _null_ _null_ )); +DATA(insert OID = 3290 ( 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_ _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_ _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_ _null_ text_le _null_ _null_ _null_ )); @@ -4165,6 +4167,8 @@ DATA(insert OID = 2179 ( gist_point_consistent PGNSP PGUID 12 1 0 0 0 f f f f t 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_ _null_ gist_point_distance _null_ _null_ _null_ )); DESCR("GiST support"); +DATA(insert OID = 3288 ( 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_ _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_ _null_ gingetbitmap _null_ _null_ _null_ )); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 5ad2cc235883b973fb8b232feb96af1910842e53..fcfe1107f92cf3b9430b70d4f2ec4a5c0711e9b3 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -17,6 +17,7 @@ #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" @@ -1262,6 +1263,7 @@ typedef struct * 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 @@ -1272,12 +1274,21 @@ typedef struct * RuntimeContext expr context for evaling runtime Skeys * RelationDesc index relation descriptor * ScanDesc index scan descriptor + * + * ReorderQueue tuples that need reordering due to re-check + * ReachedEnd have we fetched all tuples from index already? + * OrderByValues values of ORDER BY exprs of last fetched tuple + * OrderByNulls null flags for OrderByValues + * SortSupport for reordering ORDER BY exprs + * OrderByTypByVals is the datatype of order by expression pass-by-value? + * OrderByTypLens 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; @@ -1288,6 +1299,15 @@ typedef struct IndexScanState ExprContext *iss_RuntimeContext; Relation iss_RelationDesc; IndexScanDesc iss_ScanDesc; + + /* These are needed for re-checking ORDER BY expr ordering */ + pairingheap *iss_ReorderQueue; + bool iss_ReachedEnd; + Datum *iss_OrderByValues; + bool *iss_OrderByNulls; + SortSupport iss_SortSupport; + bool *iss_OrderByTypByVals; + int16 *iss_OrderByTypLens; } IndexScanState; /* ---------------- diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 1494b336c22b9c1bbaeb942936ee790e40f8ef6c..65f71d81705ddc7b87587fe468697f4b3922d4fa 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -311,8 +311,13 @@ typedef Scan SeqScan; * 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.) + * indexorderbyorig is used at runtime to recheck the ordering, if the index + * cannot calculate an accurate ordering. It is also needed for EXPLAIN. + * + * indexorderbyops 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 * indexes (for other indexes it should be "don't care"). @@ -326,6 +331,7 @@ typedef struct IndexScan List *indexqualorig; /* the same in original form */ List *indexorderby; /* list of index ORDER BY exprs */ List *indexorderbyorig; /* the same in original form */ + Oid *indexorderbyops; /* 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 index 4377baa645ed26529c1170c9664bfb3b9eb85e40..2311d35dd72c903106871786d43d85ecb929afb4 100644 --- a/src/include/utils/geo_decls.h +++ b/src/include/utils/geo_decls.h @@ -394,8 +394,10 @@ extern Datum circle_diameter(PG_FUNCTION_ARGS); extern Datum circle_radius(PG_FUNCTION_ARGS); extern Datum circle_distance(PG_FUNCTION_ARGS); extern Datum dist_pc(PG_FUNCTION_ARGS); +extern Datum dist_cpoint(PG_FUNCTION_ARGS); extern Datum dist_cpoly(PG_FUNCTION_ARGS); extern Datum dist_ppoly(PG_FUNCTION_ARGS); +extern Datum dist_polyp(PG_FUNCTION_ARGS); extern Datum circle_center(PG_FUNCTION_ARGS); extern Datum cr_circle(PG_FUNCTION_ARGS); extern Datum box_circle(PG_FUNCTION_ARGS); @@ -420,6 +422,7 @@ extern Datum gist_circle_consistent(PG_FUNCTION_ARGS); extern Datum gist_point_compress(PG_FUNCTION_ARGS); extern Datum gist_point_consistent(PG_FUNCTION_ARGS); extern Datum gist_point_distance(PG_FUNCTION_ARGS); +extern Datum gist_bbox_distance(PG_FUNCTION_ARGS); extern Datum gist_point_fetch(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index a98b10520d5f212d29b19c88ca5b78119cd9ec5a..71aee7622568f45d6dfe6a5f001dcc3c93c45cd1 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -372,6 +372,36 @@ SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth 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; @@ -1152,6 +1182,54 @@ SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth 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 index 6333a30bf6d62c774268c4985a7c2ea45647658d..acbfbd51332e354dc67f06c4fe81f02825c89ceb 100644 --- a/src/test/regress/sql/create_index.sql +++ b/src/test/regress/sql/create_index.sql @@ -224,6 +224,10 @@ SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth 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; @@ -437,6 +441,14 @@ EXPLAIN (COSTS OFF) 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;