diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/access/gist/gistget.c | 30 | ||||
-rw-r--r-- | src/backend/access/gist/gistproc.c | 37 | ||||
-rw-r--r-- | src/backend/access/gist/gistscan.c | 5 | ||||
-rw-r--r-- | src/backend/executor/nodeIndexscan.c | 379 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 73 | ||||
-rw-r--r-- | src/backend/utils/adt/geo_ops.c | 27 |
6 files changed, 524 insertions, 27 deletions
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c index e4c00c2c9f5..1f791a4f8ee 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 9d21e3fb947..9667e397ce4 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 6f653982302..099849a606b 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 48fa91981ff..2164da0c847 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); /* ---------------------------------------------------------------- @@ -110,10 +135,200 @@ IndexNext(IndexScanState *node) * 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.. + */ 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 */ static bool @@ -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 c8092372831..783e34b4fbc 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 39a78552410..a3af08ad6b0 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. */ |