Add support for index-only scans in GiST.
This adds a new GiST opclass method, 'fetch', which is used to reconstruct the original Datum from the value stored in the index. Also, the 'canreturn' index AM interface function gains a new 'attno' argument. That makes it possible to use index-only scans on a multi-column index where some of the opclasses support index-only scans but some do not. This patch adds support in the box and point opclasses. Other opclasses can added later as follow-on patches (btree_gist would be particularly interesting). Anastasia Lubennikova, with additional fixes and modifications by me.
This commit is contained in:
parent
8fa393a6d7
commit
d04c8ed904
|
@ -724,8 +724,8 @@
|
||||||
<entry><structfield>amcanreturn</structfield></entry>
|
<entry><structfield>amcanreturn</structfield></entry>
|
||||||
<entry><type>regproc</type></entry>
|
<entry><type>regproc</type></entry>
|
||||||
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
|
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
|
||||||
<entry>Function to check whether index supports index-only scans,
|
<entry>Function to check whether an index column supports index-only
|
||||||
or zero if none</entry>
|
scans. Can be zero if index-only scans are never supported.</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
|
|
|
@ -266,7 +266,7 @@ CREATE INDEX ON my_table USING gist (my_inet_column inet_ops);
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
There are seven methods that an index operator class for
|
There are seven methods that an index operator class for
|
||||||
<acronym>GiST</acronym> must provide, and an eighth that is optional.
|
<acronym>GiST</acronym> must provide, and two that are optional.
|
||||||
Correctness of the index is ensured
|
Correctness of the index is ensured
|
||||||
by proper implementation of the <function>same</>, <function>consistent</>
|
by proper implementation of the <function>same</>, <function>consistent</>
|
||||||
and <function>union</> methods, while efficiency (size and speed) of the
|
and <function>union</> methods, while efficiency (size and speed) of the
|
||||||
|
@ -282,7 +282,8 @@ CREATE INDEX ON my_table USING gist (my_inet_column inet_ops);
|
||||||
of the <command>CREATE OPERATOR CLASS</> command can be used.
|
of the <command>CREATE OPERATOR CLASS</> command can be used.
|
||||||
The optional eighth method is <function>distance</>, which is needed
|
The optional eighth method is <function>distance</>, which is needed
|
||||||
if the operator class wishes to support ordered scans (nearest-neighbor
|
if the operator class wishes to support ordered scans (nearest-neighbor
|
||||||
searches).
|
searches). The optional ninth method <function>fetch</> is needed if the
|
||||||
|
operator class wishes to support index-only scans.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<variablelist>
|
<variablelist>
|
||||||
|
@ -506,7 +507,7 @@ my_compress(PG_FUNCTION_ARGS)
|
||||||
<para>
|
<para>
|
||||||
The reverse of the <function>compress</function> method. Converts the
|
The reverse of the <function>compress</function> method. Converts the
|
||||||
index representation of the data item into a format that can be
|
index representation of the data item into a format that can be
|
||||||
manipulated by the database.
|
manipulated by the other GiST methods in the operator class.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -807,6 +808,72 @@ my_distance(PG_FUNCTION_ARGS)
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><function>fetch</></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Converts the compressed index representation of the data item into the
|
||||||
|
original data type, for index-only scans. The returned data must be an
|
||||||
|
exact, non-lossy copy of the originally indexed value.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <acronym>SQL</> declaration of the function must look like this:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
CREATE OR REPLACE FUNCTION my_fetch(internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT;
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
The argument is a pointer to a <structname>GISTENTRY</> struct. On
|
||||||
|
entry, its 'key' field contains a non-NULL leaf datum in its
|
||||||
|
compressed form. The return value is another <structname>GISTENTRY</>
|
||||||
|
struct, whose 'key' field contains the same datum in the original,
|
||||||
|
uncompressed form. If the opclass' compress function does nothing for
|
||||||
|
leaf entries, the fetch method can return the argument as is.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The matching code in the C module could then follow this skeleton:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
Datum my_fetch(PG_FUNCTION_ARGS);
|
||||||
|
PG_FUNCTION_INFO_V1(my_fetch);
|
||||||
|
|
||||||
|
Datum
|
||||||
|
my_fetch(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
||||||
|
input_data_type *in = DatumGetP(entry->key);
|
||||||
|
fetched_data_type *fetched_data;
|
||||||
|
GISTENTRY *retval;
|
||||||
|
|
||||||
|
retval = palloc(sizeof(GISTENTRY));
|
||||||
|
fetched_data = palloc(sizeof(fetched_data_type));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert 'fetched_data' into the a Datum of the original datatype.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* fill *retval from fetch_data. */
|
||||||
|
gistentryinit(*retval, PointerGetDatum(converted_datum),
|
||||||
|
entry->rel, entry->page, entry->offset, FALSE);
|
||||||
|
|
||||||
|
PG_RETURN_POINTER(retval);
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
If the compress method is lossy for leaf entries, the operator class
|
||||||
|
cannot support index-only scans, and must not define a 'fetch'
|
||||||
|
function.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
|
|
@ -274,14 +274,15 @@ amvacuumcleanup (IndexVacuumInfo *info,
|
||||||
<para>
|
<para>
|
||||||
<programlisting>
|
<programlisting>
|
||||||
bool
|
bool
|
||||||
amcanreturn (Relation indexRelation);
|
amcanreturn (Relation indexRelation, int attno);
|
||||||
</programlisting>
|
</programlisting>
|
||||||
Check whether the index can support <firstterm>index-only scans</> by
|
Check whether the index can support <firstterm>index-only scans</> on the
|
||||||
returning the indexed column values for an index entry in the form of an
|
given column, by returning the indexed column values for an index entry in
|
||||||
<structname>IndexTuple</structname>. Return TRUE if so, else FALSE. If the index AM can never
|
the form of an <structname>IndexTuple</structname>. The attribute number
|
||||||
support index-only scans (an example is hash, which stores only
|
is 1-based, i.e. the first columns attno is 1. Returns TRUE if supported,
|
||||||
the hash values not the original data), it is sufficient to set its
|
else FALSE. If the access method does not support index-only scans at all,
|
||||||
<structfield>amcanreturn</> field to zero in <structname>pg_am</>.
|
the <structfield>amcanreturn</> field in its <structname>pg_am</> row can
|
||||||
|
be set to zero.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
|
|
@ -1404,6 +1404,14 @@ initGISTstate(Relation index)
|
||||||
else
|
else
|
||||||
giststate->distanceFn[i].fn_oid = InvalidOid;
|
giststate->distanceFn[i].fn_oid = InvalidOid;
|
||||||
|
|
||||||
|
/* opclasses are not required to provide a Fetch method */
|
||||||
|
if (OidIsValid(index_getprocid(index, i + 1, GIST_FETCH_PROC)))
|
||||||
|
fmgr_info_copy(&(giststate->fetchFn[i]),
|
||||||
|
index_getprocinfo(index, i + 1, GIST_FETCH_PROC),
|
||||||
|
scanCxt);
|
||||||
|
else
|
||||||
|
giststate->fetchFn[i].fn_oid = InvalidOid;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the index column has a specified collation, we should honor that
|
* If the index column has a specified collation, we should honor that
|
||||||
* while doing comparisons. However, we may have a collatable storage
|
* while doing comparisons. However, we may have a collatable storage
|
||||||
|
|
|
@ -228,7 +228,9 @@ gistindex_keytest(IndexScanDesc scan,
|
||||||
* tuples should be reported directly into the bitmap. If they are NULL,
|
* tuples should be reported directly into the bitmap. If they are NULL,
|
||||||
* we're doing a plain or ordered indexscan. For a plain indexscan, heap
|
* we're doing a plain or ordered indexscan. For a plain indexscan, heap
|
||||||
* tuple TIDs are returned into so->pageData[]. For an ordered indexscan,
|
* tuple TIDs are returned into so->pageData[]. For an ordered indexscan,
|
||||||
* heap tuple TIDs are pushed into individual search queue items.
|
* heap tuple TIDs are pushed into individual search queue items. In an
|
||||||
|
* index-only scan, reconstructed index tuples are returned along with the
|
||||||
|
* TIDs.
|
||||||
*
|
*
|
||||||
* If we detect that the index page has split since we saw its downlink
|
* If we detect that the index page has split since we saw its downlink
|
||||||
* in the parent, we push its new right sibling onto the queue so the
|
* in the parent, we push its new right sibling onto the queue so the
|
||||||
|
@ -239,6 +241,8 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
|
||||||
TIDBitmap *tbm, int64 *ntids)
|
TIDBitmap *tbm, int64 *ntids)
|
||||||
{
|
{
|
||||||
GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
|
GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
|
||||||
|
GISTSTATE *giststate = so->giststate;
|
||||||
|
Relation r = scan->indexRelation;
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
Page page;
|
Page page;
|
||||||
GISTPageOpaque opaque;
|
GISTPageOpaque opaque;
|
||||||
|
@ -288,6 +292,8 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
|
||||||
}
|
}
|
||||||
|
|
||||||
so->nPageData = so->curPageData = 0;
|
so->nPageData = so->curPageData = 0;
|
||||||
|
if (so->pageDataCxt)
|
||||||
|
MemoryContextReset(so->pageDataCxt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* check all tuples on page
|
* check all tuples on page
|
||||||
|
@ -326,10 +332,21 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
|
||||||
else if (scan->numberOfOrderBys == 0 && GistPageIsLeaf(page))
|
else if (scan->numberOfOrderBys == 0 && GistPageIsLeaf(page))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Non-ordered scan, so report heap tuples in so->pageData[]
|
* Non-ordered scan, so report tuples in so->pageData[]
|
||||||
*/
|
*/
|
||||||
so->pageData[so->nPageData].heapPtr = it->t_tid;
|
so->pageData[so->nPageData].heapPtr = it->t_tid;
|
||||||
so->pageData[so->nPageData].recheck = recheck;
|
so->pageData[so->nPageData].recheck = recheck;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In an index-only scan, also fetch the data from the tuple.
|
||||||
|
*/
|
||||||
|
if (scan->xs_want_itup)
|
||||||
|
{
|
||||||
|
oldcxt = MemoryContextSwitchTo(so->pageDataCxt);
|
||||||
|
so->pageData[so->nPageData].ftup =
|
||||||
|
gistFetchTuple(giststate, r, it);
|
||||||
|
MemoryContextSwitchTo(oldcxt);
|
||||||
|
}
|
||||||
so->nPageData++;
|
so->nPageData++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -352,6 +369,12 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
|
||||||
item->blkno = InvalidBlockNumber;
|
item->blkno = InvalidBlockNumber;
|
||||||
item->data.heap.heapPtr = it->t_tid;
|
item->data.heap.heapPtr = it->t_tid;
|
||||||
item->data.heap.recheck = recheck;
|
item->data.heap.recheck = recheck;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In an index-only scan, also fetch the data from the tuple.
|
||||||
|
*/
|
||||||
|
if (scan->xs_want_itup)
|
||||||
|
item->data.heap.ftup = gistFetchTuple(giststate, r, it);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -412,6 +435,13 @@ getNextNearest(IndexScanDesc scan)
|
||||||
GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
|
GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
|
||||||
bool res = false;
|
bool res = false;
|
||||||
|
|
||||||
|
if (scan->xs_itup)
|
||||||
|
{
|
||||||
|
/* free previously returned tuple */
|
||||||
|
pfree(scan->xs_itup);
|
||||||
|
scan->xs_itup = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
GISTSearchItem *item = getNextGISTSearchItem(so);
|
GISTSearchItem *item = getNextGISTSearchItem(so);
|
||||||
|
@ -424,6 +454,10 @@ getNextNearest(IndexScanDesc scan)
|
||||||
/* found a heap item at currently minimal distance */
|
/* found a heap item at currently minimal distance */
|
||||||
scan->xs_ctup.t_self = item->data.heap.heapPtr;
|
scan->xs_ctup.t_self = item->data.heap.heapPtr;
|
||||||
scan->xs_recheck = item->data.heap.recheck;
|
scan->xs_recheck = item->data.heap.recheck;
|
||||||
|
|
||||||
|
/* in an index-only scan, also return the reconstructed tuple. */
|
||||||
|
if (scan->xs_want_itup)
|
||||||
|
scan->xs_itup = item->data.heap.ftup;
|
||||||
res = true;
|
res = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -465,6 +499,8 @@ gistgettuple(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
so->firstCall = false;
|
so->firstCall = false;
|
||||||
so->curPageData = so->nPageData = 0;
|
so->curPageData = so->nPageData = 0;
|
||||||
|
if (so->pageDataCxt)
|
||||||
|
MemoryContextReset(so->pageDataCxt);
|
||||||
|
|
||||||
fakeItem.blkno = GIST_ROOT_BLKNO;
|
fakeItem.blkno = GIST_ROOT_BLKNO;
|
||||||
memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
|
memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
|
||||||
|
@ -483,10 +519,17 @@ gistgettuple(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
if (so->curPageData < so->nPageData)
|
if (so->curPageData < so->nPageData)
|
||||||
{
|
{
|
||||||
|
|
||||||
/* continuing to return tuples from a leaf page */
|
/* continuing to return tuples from a leaf page */
|
||||||
scan->xs_ctup.t_self = so->pageData[so->curPageData].heapPtr;
|
scan->xs_ctup.t_self = so->pageData[so->curPageData].heapPtr;
|
||||||
scan->xs_recheck = so->pageData[so->curPageData].recheck;
|
scan->xs_recheck = so->pageData[so->curPageData].recheck;
|
||||||
|
|
||||||
|
/* in an index-only scan, also return the reconstructed tuple */
|
||||||
|
if (scan->xs_want_itup)
|
||||||
|
scan->xs_itup = so->pageData[so->curPageData].ftup;
|
||||||
|
|
||||||
so->curPageData++;
|
so->curPageData++;
|
||||||
|
|
||||||
PG_RETURN_BOOL(true);
|
PG_RETURN_BOOL(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,6 +576,8 @@ gistgetbitmap(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
/* Begin the scan by processing the root page */
|
/* Begin the scan by processing the root page */
|
||||||
so->curPageData = so->nPageData = 0;
|
so->curPageData = so->nPageData = 0;
|
||||||
|
if (so->pageDataCxt)
|
||||||
|
MemoryContextReset(so->pageDataCxt);
|
||||||
|
|
||||||
fakeItem.blkno = GIST_ROOT_BLKNO;
|
fakeItem.blkno = GIST_ROOT_BLKNO;
|
||||||
memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
|
memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
|
||||||
|
@ -558,3 +603,20 @@ gistgetbitmap(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
PG_RETURN_INT64(ntids);
|
PG_RETURN_INT64(ntids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Can we do index-only scans on the given index column?
|
||||||
|
*
|
||||||
|
* Opclasses that implement a fetch function support index-only scans.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
gistcanreturn(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Relation index = (Relation) PG_GETARG_POINTER(0);
|
||||||
|
int attno = PG_GETARG_INT32(1);
|
||||||
|
|
||||||
|
if (OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)))
|
||||||
|
PG_RETURN_BOOL(true);
|
||||||
|
else
|
||||||
|
PG_RETURN_BOOL(false);
|
||||||
|
}
|
||||||
|
|
|
@ -151,6 +151,16 @@ gist_box_decompress(PG_FUNCTION_ARGS)
|
||||||
PG_RETURN_POINTER(PG_GETARG_POINTER(0));
|
PG_RETURN_POINTER(PG_GETARG_POINTER(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GiST Fetch method for boxes
|
||||||
|
* do not do anything --- we just return the stored box as is.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
gist_box_fetch(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
PG_RETURN_POINTER(PG_GETARG_POINTER(0));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The GiST Penalty method for boxes (also used for points)
|
* The GiST Penalty method for boxes (also used for points)
|
||||||
*
|
*
|
||||||
|
@ -1186,6 +1196,33 @@ gist_point_compress(PG_FUNCTION_ARGS)
|
||||||
PG_RETURN_POINTER(entry);
|
PG_RETURN_POINTER(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GiST Fetch method for point
|
||||||
|
*
|
||||||
|
* Get point coordinates from its bounding box coordinates and form new
|
||||||
|
* gistentry.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
gist_point_fetch(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
||||||
|
BOX *in = DatumGetBoxP(entry->key);
|
||||||
|
Point *r;
|
||||||
|
GISTENTRY *retval;
|
||||||
|
|
||||||
|
retval = palloc(sizeof(GISTENTRY));
|
||||||
|
|
||||||
|
r = (Point *) palloc(sizeof(Point));
|
||||||
|
r->x = in->high.x;
|
||||||
|
r->y = in->high.y;
|
||||||
|
gistentryinit(*retval, PointerGetDatum(r),
|
||||||
|
entry->rel, entry->page,
|
||||||
|
entry->offset, FALSE);
|
||||||
|
|
||||||
|
PG_RETURN_POINTER(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#define point_point_distance(p1,p2) \
|
#define point_point_distance(p1,p2) \
|
||||||
DatumGetFloat8(DirectFunctionCall2(point_distance, \
|
DatumGetFloat8(DirectFunctionCall2(point_distance, \
|
||||||
PointPGetDatum(p1), PointPGetDatum(p2)))
|
PointPGetDatum(p1), PointPGetDatum(p2)))
|
||||||
|
|
|
@ -88,6 +88,13 @@ gistbeginscan(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
scan->opaque = so;
|
scan->opaque = so;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All fields required for index-only scans are null until gistrescan.
|
||||||
|
* However, we set up scan->xs_itupdesc whether we'll need it or not,
|
||||||
|
* since that's cheap.
|
||||||
|
*/
|
||||||
|
scan->xs_itupdesc = RelationGetDescr(r);
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldCxt);
|
MemoryContextSwitchTo(oldCxt);
|
||||||
|
|
||||||
PG_RETURN_POINTER(scan);
|
PG_RETURN_POINTER(scan);
|
||||||
|
@ -141,6 +148,17 @@ gistrescan(PG_FUNCTION_ARGS)
|
||||||
first_time = false;
|
first_time = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we're doing an index-only scan, also create a memory context to hold
|
||||||
|
* the returned tuples.
|
||||||
|
*/
|
||||||
|
if (scan->xs_want_itup && so->pageDataCxt == NULL)
|
||||||
|
so->pageDataCxt = AllocSetContextCreate(so->giststate->scanCxt,
|
||||||
|
"GiST page data context",
|
||||||
|
ALLOCSET_DEFAULT_MINSIZE,
|
||||||
|
ALLOCSET_DEFAULT_INITSIZE,
|
||||||
|
ALLOCSET_DEFAULT_MAXSIZE);
|
||||||
|
|
||||||
/* create new, empty RBTree for search queue */
|
/* create new, empty RBTree for search queue */
|
||||||
oldCxt = MemoryContextSwitchTo(so->queueCxt);
|
oldCxt = MemoryContextSwitchTo(so->queueCxt);
|
||||||
so->queue = pairingheap_allocate(pairingheap_GISTSearchItem_cmp, scan);
|
so->queue = pairingheap_allocate(pairingheap_GISTSearchItem_cmp, scan);
|
||||||
|
|
|
@ -294,8 +294,9 @@ gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p,
|
||||||
|
|
||||||
for (i = 0; i < r->rd_att->natts; i++)
|
for (i = 0; i < r->rd_att->natts; i++)
|
||||||
{
|
{
|
||||||
Datum datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
|
Datum datum;
|
||||||
|
|
||||||
|
datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
|
||||||
gistdentryinit(giststate, i, &attdata[i],
|
gistdentryinit(giststate, i, &attdata[i],
|
||||||
datum, r, p, o,
|
datum, r, p, o,
|
||||||
FALSE, isnull[i]);
|
FALSE, isnull[i]);
|
||||||
|
@ -598,6 +599,67 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* initialize a GiST entry with fetched value in key field
|
||||||
|
*/
|
||||||
|
static Datum
|
||||||
|
gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
|
||||||
|
{
|
||||||
|
GISTENTRY fentry;
|
||||||
|
GISTENTRY *fep;
|
||||||
|
|
||||||
|
gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false);
|
||||||
|
|
||||||
|
fep = (GISTENTRY *)
|
||||||
|
DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
|
||||||
|
giststate->supportCollation[nkey],
|
||||||
|
PointerGetDatum(&fentry)));
|
||||||
|
|
||||||
|
/* fetchFn set 'key', return it to the caller */
|
||||||
|
return fep->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch all keys in tuple.
|
||||||
|
* returns new IndexTuple that contains GISTENTRY with fetched data
|
||||||
|
*/
|
||||||
|
IndexTuple
|
||||||
|
gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple)
|
||||||
|
{
|
||||||
|
MemoryContext oldcxt = MemoryContextSwitchTo(giststate->tempCxt);
|
||||||
|
Datum fetchatt[INDEX_MAX_KEYS];
|
||||||
|
bool isnull[INDEX_MAX_KEYS];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < r->rd_att->natts; i++)
|
||||||
|
{
|
||||||
|
Datum datum;
|
||||||
|
|
||||||
|
datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
|
||||||
|
|
||||||
|
if (giststate->fetchFn[i].fn_oid != InvalidOid)
|
||||||
|
{
|
||||||
|
if (!isnull[i])
|
||||||
|
fetchatt[i] = gistFetchAtt(giststate, i, datum, r);
|
||||||
|
else
|
||||||
|
fetchatt[i] = (Datum) 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Index-only scans not supported for this column. Since the
|
||||||
|
* planner chose an index-only scan anyway, it is not interested
|
||||||
|
* in this column, and we can replace it with a NULL.
|
||||||
|
*/
|
||||||
|
isnull[i] = true;
|
||||||
|
fetchatt[i] = (Datum) 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MemoryContextSwitchTo(oldcxt);
|
||||||
|
|
||||||
|
return index_form_tuple(giststate->tupdesc, fetchatt, isnull);
|
||||||
|
}
|
||||||
|
|
||||||
float
|
float
|
||||||
gistpenalty(GISTSTATE *giststate, int attno,
|
gistpenalty(GISTSTATE *giststate, int attno,
|
||||||
GISTENTRY *orig, bool isNullOrig,
|
GISTENTRY *orig, bool isNullOrig,
|
||||||
|
|
|
@ -722,11 +722,14 @@ index_vacuum_cleanup(IndexVacuumInfo *info,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* index_can_return - does index support index-only scans?
|
* index_can_return
|
||||||
|
*
|
||||||
|
* Does the index access method support index-only scans for the given
|
||||||
|
* column?
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
index_can_return(Relation indexRelation)
|
index_can_return(Relation indexRelation, int attno)
|
||||||
{
|
{
|
||||||
FmgrInfo *procedure;
|
FmgrInfo *procedure;
|
||||||
|
|
||||||
|
@ -738,8 +741,9 @@ index_can_return(Relation indexRelation)
|
||||||
|
|
||||||
GET_REL_PROCEDURE(amcanreturn);
|
GET_REL_PROCEDURE(amcanreturn);
|
||||||
|
|
||||||
return DatumGetBool(FunctionCall1(procedure,
|
return DatumGetBool(FunctionCall2(procedure,
|
||||||
PointerGetDatum(indexRelation)));
|
PointerGetDatum(indexRelation),
|
||||||
|
Int32GetDatum(attno)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
|
|
@ -658,6 +658,7 @@ Datum
|
||||||
spgcanreturn(PG_FUNCTION_ARGS)
|
spgcanreturn(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Relation index = (Relation) PG_GETARG_POINTER(0);
|
Relation index = (Relation) PG_GETARG_POINTER(0);
|
||||||
|
/* int i = PG_GETARG_INT32(1); */
|
||||||
SpGistCache *cache;
|
SpGistCache *cache;
|
||||||
|
|
||||||
/* We can do it if the opclass config function says so */
|
/* We can do it if the opclass config function says so */
|
||||||
|
|
|
@ -1786,15 +1786,13 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
|
||||||
{
|
{
|
||||||
bool result;
|
bool result;
|
||||||
Bitmapset *attrs_used = NULL;
|
Bitmapset *attrs_used = NULL;
|
||||||
Bitmapset *index_attrs = NULL;
|
Bitmapset *index_canreturn_attrs = NULL;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Index-only scans must be enabled, and index must be capable of them */
|
/* Index-only scans must be enabled */
|
||||||
if (!enable_indexonlyscan)
|
if (!enable_indexonlyscan)
|
||||||
return false;
|
return false;
|
||||||
if (!index->canreturn)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that all needed attributes of the relation are available from the
|
* Check that all needed attributes of the relation are available from the
|
||||||
|
@ -1824,7 +1822,10 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
|
||||||
pull_varattnos((Node *) rinfo->clause, rel->relid, &attrs_used);
|
pull_varattnos((Node *) rinfo->clause, rel->relid, &attrs_used);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Construct a bitmapset of columns stored in the index. */
|
/*
|
||||||
|
* Construct a bitmapset of columns that the index can return back in an
|
||||||
|
* index-only scan.
|
||||||
|
*/
|
||||||
for (i = 0; i < index->ncolumns; i++)
|
for (i = 0; i < index->ncolumns; i++)
|
||||||
{
|
{
|
||||||
int attno = index->indexkeys[i];
|
int attno = index->indexkeys[i];
|
||||||
|
@ -1836,16 +1837,17 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
|
||||||
if (attno == 0)
|
if (attno == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
index_attrs =
|
if (index->canreturn[i])
|
||||||
bms_add_member(index_attrs,
|
index_canreturn_attrs =
|
||||||
|
bms_add_member(index_canreturn_attrs,
|
||||||
attno - FirstLowInvalidHeapAttributeNumber);
|
attno - FirstLowInvalidHeapAttributeNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do we have all the necessary attributes? */
|
/* Do we have all the necessary attributes? */
|
||||||
result = bms_is_subset(attrs_used, index_attrs);
|
result = bms_is_subset(attrs_used, index_canreturn_attrs);
|
||||||
|
|
||||||
bms_free(attrs_used);
|
bms_free(attrs_used);
|
||||||
bms_free(index_attrs);
|
bms_free(index_canreturn_attrs);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,6 +207,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
|
||||||
info->indexcollations = (Oid *) palloc(sizeof(Oid) * ncolumns);
|
info->indexcollations = (Oid *) palloc(sizeof(Oid) * ncolumns);
|
||||||
info->opfamily = (Oid *) palloc(sizeof(Oid) * ncolumns);
|
info->opfamily = (Oid *) palloc(sizeof(Oid) * ncolumns);
|
||||||
info->opcintype = (Oid *) palloc(sizeof(Oid) * ncolumns);
|
info->opcintype = (Oid *) palloc(sizeof(Oid) * ncolumns);
|
||||||
|
info->canreturn = (bool *) palloc(sizeof(bool) * ncolumns);
|
||||||
|
|
||||||
for (i = 0; i < ncolumns; i++)
|
for (i = 0; i < ncolumns; i++)
|
||||||
{
|
{
|
||||||
|
@ -214,11 +215,11 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
|
||||||
info->indexcollations[i] = indexRelation->rd_indcollation[i];
|
info->indexcollations[i] = indexRelation->rd_indcollation[i];
|
||||||
info->opfamily[i] = indexRelation->rd_opfamily[i];
|
info->opfamily[i] = indexRelation->rd_opfamily[i];
|
||||||
info->opcintype[i] = indexRelation->rd_opcintype[i];
|
info->opcintype[i] = indexRelation->rd_opcintype[i];
|
||||||
|
info->canreturn[i] = index_can_return(indexRelation, i + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
info->relam = indexRelation->rd_rel->relam;
|
info->relam = indexRelation->rd_rel->relam;
|
||||||
info->amcostestimate = indexRelation->rd_am->amcostestimate;
|
info->amcostestimate = indexRelation->rd_am->amcostestimate;
|
||||||
info->canreturn = index_can_return(indexRelation);
|
|
||||||
info->amcanorderbyop = indexRelation->rd_am->amcanorderbyop;
|
info->amcanorderbyop = indexRelation->rd_am->amcanorderbyop;
|
||||||
info->amoptionalkey = indexRelation->rd_am->amoptionalkey;
|
info->amoptionalkey = indexRelation->rd_am->amoptionalkey;
|
||||||
info->amsearcharray = indexRelation->rd_am->amsearcharray;
|
info->amsearcharray = indexRelation->rd_am->amsearcharray;
|
||||||
|
|
|
@ -156,7 +156,7 @@ extern IndexBulkDeleteResult *index_bulk_delete(IndexVacuumInfo *info,
|
||||||
void *callback_state);
|
void *callback_state);
|
||||||
extern IndexBulkDeleteResult *index_vacuum_cleanup(IndexVacuumInfo *info,
|
extern IndexBulkDeleteResult *index_vacuum_cleanup(IndexVacuumInfo *info,
|
||||||
IndexBulkDeleteResult *stats);
|
IndexBulkDeleteResult *stats);
|
||||||
extern bool index_can_return(Relation indexRelation);
|
extern bool index_can_return(Relation indexRelation, int attno);
|
||||||
extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum,
|
extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum,
|
||||||
uint16 procnum);
|
uint16 procnum);
|
||||||
extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
|
extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
|
||||||
|
|
|
@ -33,7 +33,8 @@
|
||||||
#define GIST_PICKSPLIT_PROC 6
|
#define GIST_PICKSPLIT_PROC 6
|
||||||
#define GIST_EQUAL_PROC 7
|
#define GIST_EQUAL_PROC 7
|
||||||
#define GIST_DISTANCE_PROC 8
|
#define GIST_DISTANCE_PROC 8
|
||||||
#define GISTNProcs 8
|
#define GIST_FETCH_PROC 9
|
||||||
|
#define GISTNProcs 9
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* strategy numbers for GiST opclasses that want to implement the old
|
* strategy numbers for GiST opclasses that want to implement the old
|
||||||
|
|
|
@ -87,6 +87,7 @@ typedef struct GISTSTATE
|
||||||
FmgrInfo picksplitFn[INDEX_MAX_KEYS];
|
FmgrInfo picksplitFn[INDEX_MAX_KEYS];
|
||||||
FmgrInfo equalFn[INDEX_MAX_KEYS];
|
FmgrInfo equalFn[INDEX_MAX_KEYS];
|
||||||
FmgrInfo distanceFn[INDEX_MAX_KEYS];
|
FmgrInfo distanceFn[INDEX_MAX_KEYS];
|
||||||
|
FmgrInfo fetchFn[INDEX_MAX_KEYS];
|
||||||
|
|
||||||
/* Collations to pass to the support functions */
|
/* Collations to pass to the support functions */
|
||||||
Oid supportCollation[INDEX_MAX_KEYS];
|
Oid supportCollation[INDEX_MAX_KEYS];
|
||||||
|
@ -118,6 +119,8 @@ typedef struct GISTSearchHeapItem
|
||||||
{
|
{
|
||||||
ItemPointerData heapPtr;
|
ItemPointerData heapPtr;
|
||||||
bool recheck; /* T if quals must be rechecked */
|
bool recheck; /* T if quals must be rechecked */
|
||||||
|
IndexTuple ftup; /* data fetched back from the index, used in
|
||||||
|
* index-only scans */
|
||||||
} GISTSearchHeapItem;
|
} GISTSearchHeapItem;
|
||||||
|
|
||||||
/* Unvisited item, either index page or heap tuple */
|
/* Unvisited item, either index page or heap tuple */
|
||||||
|
@ -157,6 +160,8 @@ typedef struct GISTScanOpaqueData
|
||||||
GISTSearchHeapItem pageData[BLCKSZ / sizeof(IndexTupleData)];
|
GISTSearchHeapItem pageData[BLCKSZ / sizeof(IndexTupleData)];
|
||||||
OffsetNumber nPageData; /* number of valid items in array */
|
OffsetNumber nPageData; /* number of valid items in array */
|
||||||
OffsetNumber curPageData; /* next item to return */
|
OffsetNumber curPageData; /* next item to return */
|
||||||
|
MemoryContext pageDataCxt; /* context holding the fetched tuples, for
|
||||||
|
index-only scans */
|
||||||
} GISTScanOpaqueData;
|
} GISTScanOpaqueData;
|
||||||
|
|
||||||
typedef GISTScanOpaqueData *GISTScanOpaque;
|
typedef GISTScanOpaqueData *GISTScanOpaque;
|
||||||
|
@ -409,6 +414,7 @@ typedef struct GiSTOptions
|
||||||
/* gist.c */
|
/* gist.c */
|
||||||
extern Datum gistbuildempty(PG_FUNCTION_ARGS);
|
extern Datum gistbuildempty(PG_FUNCTION_ARGS);
|
||||||
extern Datum gistinsert(PG_FUNCTION_ARGS);
|
extern Datum gistinsert(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum gistcanreturn(PG_FUNCTION_ARGS);
|
||||||
extern MemoryContext createTempGistContext(void);
|
extern MemoryContext createTempGistContext(void);
|
||||||
extern GISTSTATE *initGISTstate(Relation index);
|
extern GISTSTATE *initGISTstate(Relation index);
|
||||||
extern void freeGISTstate(GISTSTATE *giststate);
|
extern void freeGISTstate(GISTSTATE *giststate);
|
||||||
|
@ -504,7 +510,8 @@ extern void gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
|
||||||
extern bool gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b);
|
extern bool gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b);
|
||||||
extern void gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p,
|
extern void gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p,
|
||||||
OffsetNumber o, GISTENTRY *attdata, bool *isnull);
|
OffsetNumber o, GISTENTRY *attdata, bool *isnull);
|
||||||
|
extern IndexTuple gistFetchTuple(GISTSTATE *giststate, Relation r,
|
||||||
|
IndexTuple tuple);
|
||||||
extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
|
extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
|
||||||
GISTENTRY *entry1, bool isnull1,
|
GISTENTRY *entry1, bool isnull1,
|
||||||
GISTENTRY *entry2, bool isnull2,
|
GISTENTRY *entry2, bool isnull2,
|
||||||
|
|
|
@ -53,6 +53,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 201503191
|
#define CATALOG_VERSION_NO 201503261
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -123,7 +123,7 @@ DESCR("b-tree index access method");
|
||||||
DATA(insert OID = 405 ( hash 1 1 f f t f f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbuildempty hashbulkdelete hashvacuumcleanup - hashcostestimate hashoptions ));
|
DATA(insert OID = 405 ( hash 1 1 f f t f f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbuildempty hashbulkdelete hashvacuumcleanup - hashcostestimate hashoptions ));
|
||||||
DESCR("hash index access method");
|
DESCR("hash index access method");
|
||||||
#define HASH_AM_OID 405
|
#define HASH_AM_OID 405
|
||||||
DATA(insert OID = 783 ( gist 0 8 f t f f t t f t t t f 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbuildempty gistbulkdelete gistvacuumcleanup - gistcostestimate gistoptions ));
|
DATA(insert OID = 783 ( gist 0 9 f t f f t t f t t t f 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbuildempty gistbulkdelete gistvacuumcleanup gistcanreturn gistcostestimate gistoptions ));
|
||||||
DESCR("GiST index access method");
|
DESCR("GiST index access method");
|
||||||
#define GIST_AM_OID 783
|
#define GIST_AM_OID 783
|
||||||
DATA(insert OID = 2742 ( gin 0 6 f f f f t t f f t f f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbuildempty ginbulkdelete ginvacuumcleanup - gincostestimate ginoptions ));
|
DATA(insert OID = 2742 ( gin 0 6 f f f f t t f f t f f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbuildempty ginbulkdelete ginvacuumcleanup - gincostestimate ginoptions ));
|
||||||
|
|
|
@ -191,6 +191,7 @@ DATA(insert ( 1029 600 600 5 2581 ));
|
||||||
DATA(insert ( 1029 600 600 6 2582 ));
|
DATA(insert ( 1029 600 600 6 2582 ));
|
||||||
DATA(insert ( 1029 600 600 7 2584 ));
|
DATA(insert ( 1029 600 600 7 2584 ));
|
||||||
DATA(insert ( 1029 600 600 8 3064 ));
|
DATA(insert ( 1029 600 600 8 3064 ));
|
||||||
|
DATA(insert ( 1029 600 600 9 3282 ));
|
||||||
DATA(insert ( 2593 603 603 1 2578 ));
|
DATA(insert ( 2593 603 603 1 2578 ));
|
||||||
DATA(insert ( 2593 603 603 2 2583 ));
|
DATA(insert ( 2593 603 603 2 2583 ));
|
||||||
DATA(insert ( 2593 603 603 3 2579 ));
|
DATA(insert ( 2593 603 603 3 2579 ));
|
||||||
|
@ -198,6 +199,7 @@ DATA(insert ( 2593 603 603 4 2580 ));
|
||||||
DATA(insert ( 2593 603 603 5 2581 ));
|
DATA(insert ( 2593 603 603 5 2581 ));
|
||||||
DATA(insert ( 2593 603 603 6 2582 ));
|
DATA(insert ( 2593 603 603 6 2582 ));
|
||||||
DATA(insert ( 2593 603 603 7 2584 ));
|
DATA(insert ( 2593 603 603 7 2584 ));
|
||||||
|
DATA(insert ( 2593 603 603 9 3281 ));
|
||||||
DATA(insert ( 2594 604 604 1 2585 ));
|
DATA(insert ( 2594 604 604 1 2585 ));
|
||||||
DATA(insert ( 2594 604 604 2 2583 ));
|
DATA(insert ( 2594 604 604 2 2583 ));
|
||||||
DATA(insert ( 2594 604 604 3 2586 ));
|
DATA(insert ( 2594 604 604 3 2586 ));
|
||||||
|
|
|
@ -558,7 +558,7 @@ DATA(insert OID = 332 ( btbulkdelete PGNSP PGUID 12 1 0 0 0 f f f f t f v 4
|
||||||
DESCR("btree(internal)");
|
DESCR("btree(internal)");
|
||||||
DATA(insert OID = 972 ( btvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ btvacuumcleanup _null_ _null_ _null_ ));
|
DATA(insert OID = 972 ( btvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ btvacuumcleanup _null_ _null_ _null_ ));
|
||||||
DESCR("btree(internal)");
|
DESCR("btree(internal)");
|
||||||
DATA(insert OID = 276 ( btcanreturn PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 16 "2281" _null_ _null_ _null_ _null_ btcanreturn _null_ _null_ _null_ ));
|
DATA(insert OID = 276 ( btcanreturn PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "2281 23" _null_ _null_ _null_ _null_ btcanreturn _null_ _null_ _null_ ));
|
||||||
DESCR("btree(internal)");
|
DESCR("btree(internal)");
|
||||||
DATA(insert OID = 1268 ( btcostestimate PGNSP PGUID 12 1 0 0 0 f f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ btcostestimate _null_ _null_ _null_ ));
|
DATA(insert OID = 1268 ( btcostestimate PGNSP PGUID 12 1 0 0 0 f f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ btcostestimate _null_ _null_ _null_ ));
|
||||||
DESCR("btree(internal)");
|
DESCR("btree(internal)");
|
||||||
|
@ -987,6 +987,8 @@ DATA(insert OID = 776 ( gistbulkdelete PGNSP PGUID 12 1 0 0 0 f f f f t f v
|
||||||
DESCR("gist(internal)");
|
DESCR("gist(internal)");
|
||||||
DATA(insert OID = 2561 ( gistvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ gistvacuumcleanup _null_ _null_ _null_ ));
|
DATA(insert OID = 2561 ( gistvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ gistvacuumcleanup _null_ _null_ _null_ ));
|
||||||
DESCR("gist(internal)");
|
DESCR("gist(internal)");
|
||||||
|
DATA(insert OID = 3280 ( gistcanreturn PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "2281 23" _null_ _null_ _null_ _null_ gistcanreturn _null_ _null_ _null_ ));
|
||||||
|
DESCR("gist(internal)");
|
||||||
DATA(insert OID = 772 ( gistcostestimate PGNSP PGUID 12 1 0 0 0 f f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ gistcostestimate _null_ _null_ _null_ ));
|
DATA(insert OID = 772 ( gistcostestimate PGNSP PGUID 12 1 0 0 0 f f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ gistcostestimate _null_ _null_ _null_ ));
|
||||||
DESCR("gist(internal)");
|
DESCR("gist(internal)");
|
||||||
DATA(insert OID = 2787 ( gistoptions PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ gistoptions _null_ _null_ _null_ ));
|
DATA(insert OID = 2787 ( gistoptions PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ gistoptions _null_ _null_ _null_ ));
|
||||||
|
@ -4089,6 +4091,8 @@ DATA(insert OID = 2579 ( gist_box_compress PGNSP PGUID 12 1 0 0 0 f f f f t f
|
||||||
DESCR("GiST support");
|
DESCR("GiST support");
|
||||||
DATA(insert OID = 2580 ( gist_box_decompress PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_box_decompress _null_ _null_ _null_ ));
|
DATA(insert OID = 2580 ( gist_box_decompress PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_box_decompress _null_ _null_ _null_ ));
|
||||||
DESCR("GiST support");
|
DESCR("GiST support");
|
||||||
|
DATA(insert OID = 3281 ( gist_box_fetch PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_box_fetch _null_ _null_ _null_ ));
|
||||||
|
DESCR("GiST support");
|
||||||
DATA(insert OID = 2581 ( gist_box_penalty PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ gist_box_penalty _null_ _null_ _null_ ));
|
DATA(insert OID = 2581 ( gist_box_penalty PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ gist_box_penalty _null_ _null_ _null_ ));
|
||||||
DESCR("GiST support");
|
DESCR("GiST support");
|
||||||
DATA(insert OID = 2582 ( gist_box_picksplit PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ gist_box_picksplit _null_ _null_ _null_ ));
|
DATA(insert OID = 2582 ( gist_box_picksplit PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ gist_box_picksplit _null_ _null_ _null_ ));
|
||||||
|
@ -4107,6 +4111,8 @@ DATA(insert OID = 2592 ( gist_circle_compress PGNSP PGUID 12 1 0 0 0 f f f f t
|
||||||
DESCR("GiST support");
|
DESCR("GiST support");
|
||||||
DATA(insert OID = 1030 ( gist_point_compress PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_point_compress _null_ _null_ _null_ ));
|
DATA(insert OID = 1030 ( gist_point_compress PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_point_compress _null_ _null_ _null_ ));
|
||||||
DESCR("GiST support");
|
DESCR("GiST support");
|
||||||
|
DATA(insert OID = 3282 ( gist_point_fetch PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_point_fetch _null_ _null_ _null_ ));
|
||||||
|
DESCR("GiST support");
|
||||||
DATA(insert OID = 2179 ( gist_point_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 5 0 16 "2281 600 23 26 2281" _null_ _null_ _null_ _null_ gist_point_consistent _null_ _null_ _null_ ));
|
DATA(insert OID = 2179 ( gist_point_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 5 0 16 "2281 600 23 26 2281" _null_ _null_ _null_ _null_ gist_point_consistent _null_ _null_ _null_ ));
|
||||||
DESCR("GiST support");
|
DESCR("GiST support");
|
||||||
DATA(insert OID = 3064 ( gist_point_distance PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 701 "2281 600 23 26" _null_ _null_ _null_ _null_ gist_point_distance _null_ _null_ _null_ ));
|
DATA(insert OID = 3064 ( gist_point_distance PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 701 "2281 600 23 26" _null_ _null_ _null_ _null_ gist_point_distance _null_ _null_ _null_ ));
|
||||||
|
@ -5039,7 +5045,7 @@ DATA(insert OID = 4011 ( spgbulkdelete PGNSP PGUID 12 1 0 0 0 f f f f t f v
|
||||||
DESCR("spgist(internal)");
|
DESCR("spgist(internal)");
|
||||||
DATA(insert OID = 4012 ( spgvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ spgvacuumcleanup _null_ _null_ _null_ ));
|
DATA(insert OID = 4012 ( spgvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ spgvacuumcleanup _null_ _null_ _null_ ));
|
||||||
DESCR("spgist(internal)");
|
DESCR("spgist(internal)");
|
||||||
DATA(insert OID = 4032 ( spgcanreturn PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 16 "2281" _null_ _null_ _null_ _null_ spgcanreturn _null_ _null_ _null_ ));
|
DATA(insert OID = 4032 ( spgcanreturn PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "2281 23" _null_ _null_ _null_ _null_ spgcanreturn _null_ _null_ _null_ ));
|
||||||
DESCR("spgist(internal)");
|
DESCR("spgist(internal)");
|
||||||
DATA(insert OID = 4013 ( spgcostestimate PGNSP PGUID 12 1 0 0 0 f f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ spgcostestimate _null_ _null_ _null_ ));
|
DATA(insert OID = 4013 ( spgcostestimate PGNSP PGUID 12 1 0 0 0 f f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ spgcostestimate _null_ _null_ _null_ ));
|
||||||
DESCR("spgist(internal)");
|
DESCR("spgist(internal)");
|
||||||
|
|
|
@ -520,6 +520,8 @@ typedef struct IndexOptInfo
|
||||||
Oid *sortopfamily; /* OIDs of btree opfamilies, if orderable */
|
Oid *sortopfamily; /* OIDs of btree opfamilies, if orderable */
|
||||||
bool *reverse_sort; /* is sort order descending? */
|
bool *reverse_sort; /* is sort order descending? */
|
||||||
bool *nulls_first; /* do NULLs come first in the sort order? */
|
bool *nulls_first; /* do NULLs come first in the sort order? */
|
||||||
|
bool *canreturn; /* which index cols can be returned in an
|
||||||
|
index-only scan? */
|
||||||
Oid relam; /* OID of the access method (in pg_am) */
|
Oid relam; /* OID of the access method (in pg_am) */
|
||||||
|
|
||||||
RegProcedure amcostestimate; /* OID of the access method's cost fcn */
|
RegProcedure amcostestimate; /* OID of the access method's cost fcn */
|
||||||
|
@ -533,7 +535,6 @@ typedef struct IndexOptInfo
|
||||||
bool unique; /* true if a unique index */
|
bool unique; /* true if a unique index */
|
||||||
bool immediate; /* is uniqueness enforced immediately? */
|
bool immediate; /* is uniqueness enforced immediately? */
|
||||||
bool hypothetical; /* true if index doesn't really exist */
|
bool hypothetical; /* true if index doesn't really exist */
|
||||||
bool canreturn; /* can index return IndexTuples? */
|
|
||||||
bool amcanorderbyop; /* does AM support order by operator result? */
|
bool amcanorderbyop; /* does AM support order by operator result? */
|
||||||
bool amoptionalkey; /* can query omit key for the first column? */
|
bool amoptionalkey; /* can query omit key for the first column? */
|
||||||
bool amsearcharray; /* can AM handle ScalarArrayOpExpr quals? */
|
bool amsearcharray; /* can AM handle ScalarArrayOpExpr quals? */
|
||||||
|
|
|
@ -410,6 +410,7 @@ extern Datum gist_box_picksplit(PG_FUNCTION_ARGS);
|
||||||
extern Datum gist_box_consistent(PG_FUNCTION_ARGS);
|
extern Datum gist_box_consistent(PG_FUNCTION_ARGS);
|
||||||
extern Datum gist_box_penalty(PG_FUNCTION_ARGS);
|
extern Datum gist_box_penalty(PG_FUNCTION_ARGS);
|
||||||
extern Datum gist_box_same(PG_FUNCTION_ARGS);
|
extern Datum gist_box_same(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum gist_box_fetch(PG_FUNCTION_ARGS);
|
||||||
extern Datum gist_poly_compress(PG_FUNCTION_ARGS);
|
extern Datum gist_poly_compress(PG_FUNCTION_ARGS);
|
||||||
extern Datum gist_poly_consistent(PG_FUNCTION_ARGS);
|
extern Datum gist_poly_consistent(PG_FUNCTION_ARGS);
|
||||||
extern Datum gist_circle_compress(PG_FUNCTION_ARGS);
|
extern Datum gist_circle_compress(PG_FUNCTION_ARGS);
|
||||||
|
@ -417,6 +418,8 @@ extern Datum gist_circle_consistent(PG_FUNCTION_ARGS);
|
||||||
extern Datum gist_point_compress(PG_FUNCTION_ARGS);
|
extern Datum gist_point_compress(PG_FUNCTION_ARGS);
|
||||||
extern Datum gist_point_consistent(PG_FUNCTION_ARGS);
|
extern Datum gist_point_consistent(PG_FUNCTION_ARGS);
|
||||||
extern Datum gist_point_distance(PG_FUNCTION_ARGS);
|
extern Datum gist_point_distance(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum gist_point_fetch(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
|
||||||
/* geo_selfuncs.c */
|
/* geo_selfuncs.c */
|
||||||
extern Datum areasel(PG_FUNCTION_ARGS);
|
extern Datum areasel(PG_FUNCTION_ARGS);
|
||||||
|
|
|
@ -384,7 +384,7 @@ SELECT * FROM fast_emp4000
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
Sort
|
Sort
|
||||||
Sort Key: ((home_base[0])[0])
|
Sort Key: ((home_base[0])[0])
|
||||||
-> Index Scan using grect2ind on fast_emp4000
|
-> Index Only Scan using grect2ind on fast_emp4000
|
||||||
Index Cond: (home_base @ '(2000,1000),(200,200)'::box)
|
Index Cond: (home_base @ '(2000,1000),(200,200)'::box)
|
||||||
(4 rows)
|
(4 rows)
|
||||||
|
|
||||||
|
@ -402,7 +402,7 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using grect2ind on fast_emp4000
|
-> Index Only Scan using grect2ind on fast_emp4000
|
||||||
Index Cond: (home_base && '(1000,1000),(0,0)'::box)
|
Index Cond: (home_base && '(1000,1000),(0,0)'::box)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -415,9 +415,9 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
|
SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
--------------------------------------------------
|
-------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using grect2ind on fast_emp4000
|
-> Index Only Scan using grect2ind on fast_emp4000
|
||||||
Index Cond: (home_base IS NULL)
|
Index Cond: (home_base IS NULL)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -501,7 +501,7 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ box '(0,0,100,100)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using gpointind on point_tbl
|
-> Index Only Scan using gpointind on point_tbl
|
||||||
Index Cond: (f1 <@ '(100,100),(0,0)'::box)
|
Index Cond: (f1 <@ '(100,100),(0,0)'::box)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -516,8 +516,8 @@ SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using gpointind on point_tbl
|
-> Index Only Scan using gpointind on point_tbl
|
||||||
Index Cond: ('(100,100),(0,0)'::box @> f1)
|
Index Cond: (f1 <@ '(100,100),(0,0)'::box)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1;
|
SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1;
|
||||||
|
@ -531,7 +531,7 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ polygon '(0,0),(0,100),(100,100),(50,
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using gpointind on point_tbl
|
-> Index Only Scan using gpointind on point_tbl
|
||||||
Index Cond: (f1 <@ '((0,0),(0,100),(100,100),(50,50),(100,0),(0,0))'::polygon)
|
Index Cond: (f1 <@ '((0,0),(0,100),(100,100),(50,50),(100,0),(0,0))'::polygon)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -546,7 +546,7 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using gpointind on point_tbl
|
-> Index Only Scan using gpointind on point_tbl
|
||||||
Index Cond: (f1 <@ '<(50,50),50>'::circle)
|
Index Cond: (f1 <@ '<(50,50),50>'::circle)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -559,9 +559,9 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)';
|
SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------------
|
------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using gpointind on point_tbl p
|
-> Index Only Scan using gpointind on point_tbl p
|
||||||
Index Cond: (f1 << '(0,0)'::point)
|
Index Cond: (f1 << '(0,0)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -574,9 +574,9 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)';
|
SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------------
|
------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using gpointind on point_tbl p
|
-> Index Only Scan using gpointind on point_tbl p
|
||||||
Index Cond: (f1 >> '(0,0)'::point)
|
Index Cond: (f1 >> '(0,0)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -589,9 +589,9 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)';
|
SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------------
|
------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using gpointind on point_tbl p
|
-> Index Only Scan using gpointind on point_tbl p
|
||||||
Index Cond: (f1 <^ '(0,0)'::point)
|
Index Cond: (f1 <^ '(0,0)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -604,9 +604,9 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)';
|
SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------------
|
------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using gpointind on point_tbl p
|
-> Index Only Scan using gpointind on point_tbl p
|
||||||
Index Cond: (f1 >^ '(0,0)'::point)
|
Index Cond: (f1 >^ '(0,0)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -619,9 +619,9 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
|
SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------------
|
------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using gpointind on point_tbl p
|
-> Index Only Scan using gpointind on point_tbl p
|
||||||
Index Cond: (f1 ~= '(-5,-12)'::point)
|
Index Cond: (f1 ~= '(-5,-12)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -634,8 +634,8 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
|
SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------
|
----------------------------------------------
|
||||||
Index Scan using gpointind on point_tbl
|
Index Only Scan using gpointind on point_tbl
|
||||||
Order By: (f1 <-> '(0,1)'::point)
|
Order By: (f1 <-> '(0,1)'::point)
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
|
@ -654,8 +654,8 @@ SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT * FROM point_tbl WHERE f1 IS NULL;
|
SELECT * FROM point_tbl WHERE f1 IS NULL;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------
|
----------------------------------------------
|
||||||
Index Scan using gpointind on point_tbl
|
Index Only Scan using gpointind on point_tbl
|
||||||
Index Cond: (f1 IS NULL)
|
Index Cond: (f1 IS NULL)
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
|
@ -668,8 +668,8 @@ SELECT * FROM point_tbl WHERE f1 IS NULL;
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
|
SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------
|
----------------------------------------------
|
||||||
Index Scan using gpointind on point_tbl
|
Index Only Scan using gpointind on point_tbl
|
||||||
Index Cond: (f1 IS NOT NULL)
|
Index Cond: (f1 IS NOT NULL)
|
||||||
Order By: (f1 <-> '(0,1)'::point)
|
Order By: (f1 <-> '(0,1)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
@ -689,7 +689,7 @@ EXPLAIN (COSTS OFF)
|
||||||
SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
|
SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
Index Scan using gpointind on point_tbl
|
Index Only Scan using gpointind on point_tbl
|
||||||
Index Cond: (f1 <@ '(10,10),(-10,-10)'::box)
|
Index Cond: (f1 <@ '(10,10),(-10,-10)'::box)
|
||||||
Order By: (f1 <-> '(0,1)'::point)
|
Order By: (f1 <-> '(0,1)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
|
@ -17,3 +17,149 @@ delete from gist_point_tbl where id % 2 = 1;
|
||||||
-- would exercise it)
|
-- would exercise it)
|
||||||
delete from gist_point_tbl where id < 10000;
|
delete from gist_point_tbl where id < 10000;
|
||||||
vacuum gist_point_tbl;
|
vacuum gist_point_tbl;
|
||||||
|
--
|
||||||
|
-- Test Index-only plans on GiST indexes
|
||||||
|
--
|
||||||
|
create table gist_tbl (b box, p point, c circle);
|
||||||
|
insert into gist_tbl
|
||||||
|
select box(point(0.05*i, 0.05*i), point(0.05*i, 0.05*i)),
|
||||||
|
point(0.05*i, 0.05*i),
|
||||||
|
circle(point(0.05*i, 0.05*i), 1.0)
|
||||||
|
from generate_series(0,10000) as i;
|
||||||
|
vacuum analyze;
|
||||||
|
set enable_seqscan=off;
|
||||||
|
set enable_bitmapscan=off;
|
||||||
|
set enable_indexonlyscan=on;
|
||||||
|
-- Test index-only scan with point opclass
|
||||||
|
create index gist_tbl_point_index on gist_tbl using gist (p);
|
||||||
|
-- check that the planner chooses an index-only scan
|
||||||
|
explain (costs off)
|
||||||
|
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5));
|
||||||
|
QUERY PLAN
|
||||||
|
--------------------------------------------------------
|
||||||
|
Index Only Scan using gist_tbl_point_index on gist_tbl
|
||||||
|
Index Cond: (p <@ '(0.5,0.5),(0,0)'::box)
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- execute the same
|
||||||
|
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5));
|
||||||
|
p
|
||||||
|
-------------
|
||||||
|
(0,0)
|
||||||
|
(0.05,0.05)
|
||||||
|
(0.1,0.1)
|
||||||
|
(0.15,0.15)
|
||||||
|
(0.2,0.2)
|
||||||
|
(0.25,0.25)
|
||||||
|
(0.3,0.3)
|
||||||
|
(0.35,0.35)
|
||||||
|
(0.4,0.4)
|
||||||
|
(0.45,0.45)
|
||||||
|
(0.5,0.5)
|
||||||
|
(11 rows)
|
||||||
|
|
||||||
|
-- Also test an index-only knn-search
|
||||||
|
explain (costs off)
|
||||||
|
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5))
|
||||||
|
order by p <-> point(0.2, 0.2);
|
||||||
|
QUERY PLAN
|
||||||
|
--------------------------------------------------------
|
||||||
|
Index Only Scan using gist_tbl_point_index on gist_tbl
|
||||||
|
Index Cond: (p <@ '(0.5,0.5),(0,0)'::box)
|
||||||
|
Order By: (p <-> '(0.2,0.2)'::point)
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5))
|
||||||
|
order by p <-> point(0.2, 0.2);
|
||||||
|
p
|
||||||
|
-------------
|
||||||
|
(0.2,0.2)
|
||||||
|
(0.25,0.25)
|
||||||
|
(0.15,0.15)
|
||||||
|
(0.3,0.3)
|
||||||
|
(0.1,0.1)
|
||||||
|
(0.35,0.35)
|
||||||
|
(0.05,0.05)
|
||||||
|
(0,0)
|
||||||
|
(0.4,0.4)
|
||||||
|
(0.45,0.45)
|
||||||
|
(0.5,0.5)
|
||||||
|
(11 rows)
|
||||||
|
|
||||||
|
drop index gist_tbl_point_index;
|
||||||
|
-- Test index-only scan with box opclass
|
||||||
|
create index gist_tbl_box_index on gist_tbl using gist (b);
|
||||||
|
-- check that the planner chooses an index-only scan
|
||||||
|
explain (costs off)
|
||||||
|
select b from gist_tbl where b <@ box(point(5,5), point(6,6));
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------------
|
||||||
|
Index Only Scan using gist_tbl_box_index on gist_tbl
|
||||||
|
Index Cond: (b <@ '(6,6),(5,5)'::box)
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- execute the same
|
||||||
|
select b from gist_tbl where b <@ box(point(5,5), point(6,6));
|
||||||
|
b
|
||||||
|
-------------------------
|
||||||
|
(5,5),(5,5)
|
||||||
|
(5.05,5.05),(5.05,5.05)
|
||||||
|
(5.1,5.1),(5.1,5.1)
|
||||||
|
(5.15,5.15),(5.15,5.15)
|
||||||
|
(5.2,5.2),(5.2,5.2)
|
||||||
|
(5.25,5.25),(5.25,5.25)
|
||||||
|
(5.3,5.3),(5.3,5.3)
|
||||||
|
(5.35,5.35),(5.35,5.35)
|
||||||
|
(5.4,5.4),(5.4,5.4)
|
||||||
|
(5.45,5.45),(5.45,5.45)
|
||||||
|
(5.5,5.5),(5.5,5.5)
|
||||||
|
(5.55,5.55),(5.55,5.55)
|
||||||
|
(5.6,5.6),(5.6,5.6)
|
||||||
|
(5.65,5.65),(5.65,5.65)
|
||||||
|
(5.7,5.7),(5.7,5.7)
|
||||||
|
(5.75,5.75),(5.75,5.75)
|
||||||
|
(5.8,5.8),(5.8,5.8)
|
||||||
|
(5.85,5.85),(5.85,5.85)
|
||||||
|
(5.9,5.9),(5.9,5.9)
|
||||||
|
(5.95,5.95),(5.95,5.95)
|
||||||
|
(6,6),(6,6)
|
||||||
|
(21 rows)
|
||||||
|
|
||||||
|
drop index gist_tbl_box_index;
|
||||||
|
-- Test that an index-only scan is not chosen, when the query involves the
|
||||||
|
-- circle column (the circle opclass does not support index-only scans).
|
||||||
|
create index gist_tbl_multi_index on gist_tbl using gist (p, c);
|
||||||
|
explain (costs off)
|
||||||
|
select p, c from gist_tbl
|
||||||
|
where p <@ box(point(5,5), point(6, 6));
|
||||||
|
QUERY PLAN
|
||||||
|
---------------------------------------------------
|
||||||
|
Index Scan using gist_tbl_multi_index on gist_tbl
|
||||||
|
Index Cond: (p <@ '(6,6),(5,5)'::box)
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- execute the same
|
||||||
|
select b, p from gist_tbl
|
||||||
|
where b <@ box(point(4.5, 4.5), point(5.5, 5.5))
|
||||||
|
and p <@ box(point(5,5), point(6, 6));
|
||||||
|
b | p
|
||||||
|
-------------------------+-------------
|
||||||
|
(5,5),(5,5) | (5,5)
|
||||||
|
(5.05,5.05),(5.05,5.05) | (5.05,5.05)
|
||||||
|
(5.1,5.1),(5.1,5.1) | (5.1,5.1)
|
||||||
|
(5.15,5.15),(5.15,5.15) | (5.15,5.15)
|
||||||
|
(5.2,5.2),(5.2,5.2) | (5.2,5.2)
|
||||||
|
(5.25,5.25),(5.25,5.25) | (5.25,5.25)
|
||||||
|
(5.3,5.3),(5.3,5.3) | (5.3,5.3)
|
||||||
|
(5.35,5.35),(5.35,5.35) | (5.35,5.35)
|
||||||
|
(5.4,5.4),(5.4,5.4) | (5.4,5.4)
|
||||||
|
(5.45,5.45),(5.45,5.45) | (5.45,5.45)
|
||||||
|
(5.5,5.5),(5.5,5.5) | (5.5,5.5)
|
||||||
|
(11 rows)
|
||||||
|
|
||||||
|
drop index gist_tbl_multi_index;
|
||||||
|
-- Clean up
|
||||||
|
reset enable_seqscan;
|
||||||
|
reset enable_bitmapscan;
|
||||||
|
reset enable_indexonlyscan;
|
||||||
|
drop table gist_tbl;
|
||||||
|
|
|
@ -23,3 +23,76 @@ delete from gist_point_tbl where id % 2 = 1;
|
||||||
delete from gist_point_tbl where id < 10000;
|
delete from gist_point_tbl where id < 10000;
|
||||||
|
|
||||||
vacuum gist_point_tbl;
|
vacuum gist_point_tbl;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Test Index-only plans on GiST indexes
|
||||||
|
--
|
||||||
|
|
||||||
|
create table gist_tbl (b box, p point, c circle);
|
||||||
|
|
||||||
|
insert into gist_tbl
|
||||||
|
select box(point(0.05*i, 0.05*i), point(0.05*i, 0.05*i)),
|
||||||
|
point(0.05*i, 0.05*i),
|
||||||
|
circle(point(0.05*i, 0.05*i), 1.0)
|
||||||
|
from generate_series(0,10000) as i;
|
||||||
|
|
||||||
|
vacuum analyze;
|
||||||
|
|
||||||
|
set enable_seqscan=off;
|
||||||
|
set enable_bitmapscan=off;
|
||||||
|
set enable_indexonlyscan=on;
|
||||||
|
|
||||||
|
-- Test index-only scan with point opclass
|
||||||
|
create index gist_tbl_point_index on gist_tbl using gist (p);
|
||||||
|
|
||||||
|
-- check that the planner chooses an index-only scan
|
||||||
|
explain (costs off)
|
||||||
|
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5));
|
||||||
|
|
||||||
|
-- execute the same
|
||||||
|
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5));
|
||||||
|
|
||||||
|
-- Also test an index-only knn-search
|
||||||
|
explain (costs off)
|
||||||
|
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5))
|
||||||
|
order by p <-> point(0.2, 0.2);
|
||||||
|
|
||||||
|
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5))
|
||||||
|
order by p <-> point(0.2, 0.2);
|
||||||
|
|
||||||
|
drop index gist_tbl_point_index;
|
||||||
|
|
||||||
|
-- Test index-only scan with box opclass
|
||||||
|
create index gist_tbl_box_index on gist_tbl using gist (b);
|
||||||
|
|
||||||
|
-- check that the planner chooses an index-only scan
|
||||||
|
explain (costs off)
|
||||||
|
select b from gist_tbl where b <@ box(point(5,5), point(6,6));
|
||||||
|
|
||||||
|
-- execute the same
|
||||||
|
select b from gist_tbl where b <@ box(point(5,5), point(6,6));
|
||||||
|
|
||||||
|
drop index gist_tbl_box_index;
|
||||||
|
|
||||||
|
-- Test that an index-only scan is not chosen, when the query involves the
|
||||||
|
-- circle column (the circle opclass does not support index-only scans).
|
||||||
|
create index gist_tbl_multi_index on gist_tbl using gist (p, c);
|
||||||
|
|
||||||
|
explain (costs off)
|
||||||
|
select p, c from gist_tbl
|
||||||
|
where p <@ box(point(5,5), point(6, 6));
|
||||||
|
|
||||||
|
-- execute the same
|
||||||
|
select b, p from gist_tbl
|
||||||
|
where b <@ box(point(4.5, 4.5), point(5.5, 5.5))
|
||||||
|
and p <@ box(point(5,5), point(6, 6));
|
||||||
|
|
||||||
|
drop index gist_tbl_multi_index;
|
||||||
|
|
||||||
|
-- Clean up
|
||||||
|
reset enable_seqscan;
|
||||||
|
reset enable_bitmapscan;
|
||||||
|
reset enable_indexonlyscan;
|
||||||
|
|
||||||
|
drop table gist_tbl;
|
||||||
|
|
Loading…
Reference in New Issue