Allow GiST distance function to return merely a lower-bound.
The distance function can now set *recheck = false, like index quals. The executor will then re-check the ORDER BY expressions, and use a queue to reorder the results on the fly. This makes it possible to do kNN-searches on polygons and circles, which don't store the exact value in the index, but just a bounding box. Alexander Korotkov and me
This commit is contained in:
parent
ecd222e770
commit
35fcb1b3d0
@ -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.)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,9 +135,199 @@ 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
|
||||
*/
|
||||
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201505141
|
||||
#define CATALOG_VERSION_NO 201505151
|
||||
|
||||
#endif
|
||||
|
@ -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
|
||||
|
@ -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 ));
|
||||
|
@ -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");
|
||||
|
@ -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_ ));
|
||||
|
@ -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;
|
||||
|
||||
/* ----------------
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user