mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +03:00
Support for INCLUDE attributes in GiST indexes
Similarly to B-tree, GiST index access method gets support of INCLUDE attributes. These attributes aren't used for tree navigation and aren't present in non-leaf pages. But they are present in leaf pages and can be fetched during index-only scan. The point of having INCLUDE attributes in GiST indexes is slightly different from the point of having them in B-tree. The main point of INCLUDE attributes in B-tree is to define UNIQUE constraint over part of attributes enabled for index-only scan. In GiST the main point of INCLUDE attributes is to use index-only scan for attributes, whose data types don't have GiST opclasses. Discussion: https://postgr.es/m/73A1A452-AD5F-40D4-BD61-978622FF75C1%40yandex-team.ru Author: Andrey Borodin, with small changes by me Reviewed-by: Andreas Karlsson
This commit is contained in:
parent
a0b7626268
commit
f2e403803f
@ -1145,8 +1145,8 @@ CREATE UNIQUE INDEX tab_x_y ON tab(x) INCLUDE (y);
|
|||||||
likely to not need to access the heap. If the heap tuple must be visited
|
likely to not need to access the heap. If the heap tuple must be visited
|
||||||
anyway, it costs nothing more to get the column's value from there.
|
anyway, it costs nothing more to get the column's value from there.
|
||||||
Other restrictions are that expressions are not currently supported as
|
Other restrictions are that expressions are not currently supported as
|
||||||
included columns, and that only B-tree indexes currently support included
|
included columns, and that only B-tree and GiST indexes currently support
|
||||||
columns.
|
included columns.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -1165,7 +1165,7 @@ CREATE INDEX tab_x_y ON tab(x, y);
|
|||||||
enforce uniqueness on the key column(s). Also, explicitly marking
|
enforce uniqueness on the key column(s). Also, explicitly marking
|
||||||
non-searchable columns as <literal>INCLUDE</literal> columns makes the
|
non-searchable columns as <literal>INCLUDE</literal> columns makes the
|
||||||
index slightly smaller, because such columns need not be stored in upper
|
index slightly smaller, because such columns need not be stored in upper
|
||||||
B-tree levels.
|
tree levels.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -181,10 +181,10 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Currently, only the B-tree index access method supports this feature.
|
Currently, the B-tree and the GiST index access methods supports this
|
||||||
In B-tree indexes, the values of columns listed in the
|
feature. In B-tree and the GiST indexes, the values of columns listed
|
||||||
<literal>INCLUDE</literal> clause are included in leaf tuples which
|
in the <literal>INCLUDE</literal> clause are included in leaf tuples
|
||||||
correspond to heap tuples, but are not included in upper-level
|
which correspond to heap tuples, but are not included in upper-level
|
||||||
index entries used for tree navigation.
|
index entries used for tree navigation.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
@ -3674,6 +3674,12 @@ SELECT plainto_tsquery('supernovae stars');
|
|||||||
retrieved to see if the match is correct.
|
retrieved to see if the match is correct.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
A GiST index can be covering, i.e. use the <literal>INCLUDE</literal>
|
||||||
|
clause. Included columns can have data types without any GiST operator
|
||||||
|
class. Included attributes will be stored uncompressed.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Lossiness causes performance degradation due to unnecessary fetches of table
|
Lossiness causes performance degradation due to unnecessary fetches of table
|
||||||
records that turn out to be false matches. Since random access to table
|
records that turn out to be false matches. Since random access to table
|
||||||
|
@ -75,7 +75,7 @@ gisthandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->amclusterable = true;
|
amroutine->amclusterable = true;
|
||||||
amroutine->ampredlocks = true;
|
amroutine->ampredlocks = true;
|
||||||
amroutine->amcanparallel = false;
|
amroutine->amcanparallel = false;
|
||||||
amroutine->amcaninclude = false;
|
amroutine->amcaninclude = true;
|
||||||
amroutine->amkeytype = InvalidOid;
|
amroutine->amkeytype = InvalidOid;
|
||||||
|
|
||||||
amroutine->ambuild = gistbuild;
|
amroutine->ambuild = gistbuild;
|
||||||
@ -1382,8 +1382,10 @@ gistSplit(Relation r,
|
|||||||
IndexTupleSize(itup[0]), GiSTPageSize,
|
IndexTupleSize(itup[0]), GiSTPageSize,
|
||||||
RelationGetRelationName(r))));
|
RelationGetRelationName(r))));
|
||||||
|
|
||||||
memset(v.spl_lisnull, true, sizeof(bool) * giststate->tupdesc->natts);
|
memset(v.spl_lisnull, true,
|
||||||
memset(v.spl_risnull, true, sizeof(bool) * giststate->tupdesc->natts);
|
sizeof(bool) * giststate->nonLeafTupdesc->natts);
|
||||||
|
memset(v.spl_risnull, true,
|
||||||
|
sizeof(bool) * giststate->nonLeafTupdesc->natts);
|
||||||
gistSplitByKey(r, page, itup, len, giststate, &v, 0);
|
gistSplitByKey(r, page, itup, len, giststate, &v, 0);
|
||||||
|
|
||||||
/* form left and right vector */
|
/* form left and right vector */
|
||||||
@ -1461,9 +1463,23 @@ initGISTstate(Relation index)
|
|||||||
|
|
||||||
giststate->scanCxt = scanCxt;
|
giststate->scanCxt = scanCxt;
|
||||||
giststate->tempCxt = scanCxt; /* caller must change this if needed */
|
giststate->tempCxt = scanCxt; /* caller must change this if needed */
|
||||||
giststate->tupdesc = index->rd_att;
|
giststate->leafTupdesc = index->rd_att;
|
||||||
|
|
||||||
for (i = 0; i < index->rd_att->natts; i++)
|
/*
|
||||||
|
* The truncated tupdesc for non-leaf index tuples, which doesn't contain
|
||||||
|
* the INCLUDE attributes.
|
||||||
|
*
|
||||||
|
* It is used to form tuples during tuple adjustement and page split.
|
||||||
|
* B-tree creates shortened tuple descriptor for every truncated tuple,
|
||||||
|
* because it is doing this less often: it does not have to form truncated
|
||||||
|
* tuples during page split. Also, B-tree is not adjusting tuples on
|
||||||
|
* internal pages the way GiST does.
|
||||||
|
*/
|
||||||
|
giststate->nonLeafTupdesc = CreateTupleDescCopyConstr(index->rd_att);
|
||||||
|
giststate->nonLeafTupdesc->natts =
|
||||||
|
IndexRelationGetNumberOfKeyAttributes(index);
|
||||||
|
|
||||||
|
for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(index); i++)
|
||||||
{
|
{
|
||||||
fmgr_info_copy(&(giststate->consistentFn[i]),
|
fmgr_info_copy(&(giststate->consistentFn[i]),
|
||||||
index_getprocinfo(index, i + 1, GIST_CONSISTENT_PROC),
|
index_getprocinfo(index, i + 1, GIST_CONSISTENT_PROC),
|
||||||
@ -1531,6 +1547,21 @@ initGISTstate(Relation index)
|
|||||||
giststate->supportCollation[i] = DEFAULT_COLLATION_OID;
|
giststate->supportCollation[i] = DEFAULT_COLLATION_OID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* No opclass information for INCLUDE attributes */
|
||||||
|
for (; i < index->rd_att->natts; i++)
|
||||||
|
{
|
||||||
|
giststate->consistentFn[i].fn_oid = InvalidOid;
|
||||||
|
giststate->unionFn[i].fn_oid = InvalidOid;
|
||||||
|
giststate->compressFn[i].fn_oid = InvalidOid;
|
||||||
|
giststate->decompressFn[i].fn_oid = InvalidOid;
|
||||||
|
giststate->penaltyFn[i].fn_oid = InvalidOid;
|
||||||
|
giststate->picksplitFn[i].fn_oid = InvalidOid;
|
||||||
|
giststate->equalFn[i].fn_oid = InvalidOid;
|
||||||
|
giststate->distanceFn[i].fn_oid = InvalidOid;
|
||||||
|
giststate->fetchFn[i].fn_oid = InvalidOid;
|
||||||
|
giststate->supportCollation[i] = InvalidOid;
|
||||||
|
}
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldCxt);
|
MemoryContextSwitchTo(oldCxt);
|
||||||
|
|
||||||
return giststate;
|
return giststate;
|
||||||
|
@ -164,7 +164,7 @@ gistindex_keytest(IndexScanDesc scan,
|
|||||||
|
|
||||||
datum = index_getattr(tuple,
|
datum = index_getattr(tuple,
|
||||||
key->sk_attno,
|
key->sk_attno,
|
||||||
giststate->tupdesc,
|
giststate->leafTupdesc,
|
||||||
&isNull);
|
&isNull);
|
||||||
|
|
||||||
if (key->sk_flags & SK_ISNULL)
|
if (key->sk_flags & SK_ISNULL)
|
||||||
@ -244,7 +244,7 @@ gistindex_keytest(IndexScanDesc scan,
|
|||||||
|
|
||||||
datum = index_getattr(tuple,
|
datum = index_getattr(tuple,
|
||||||
key->sk_attno,
|
key->sk_attno,
|
||||||
giststate->tupdesc,
|
giststate->leafTupdesc,
|
||||||
&isNull);
|
&isNull);
|
||||||
|
|
||||||
if ((key->sk_flags & SK_ISNULL) || isNull)
|
if ((key->sk_flags & SK_ISNULL) || isNull)
|
||||||
@ -769,11 +769,13 @@ gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
|
|||||||
*
|
*
|
||||||
* Opclasses that implement a fetch function support index-only scans.
|
* Opclasses that implement a fetch function support index-only scans.
|
||||||
* Opclasses without compression functions also support index-only scans.
|
* Opclasses without compression functions also support index-only scans.
|
||||||
|
* Included attributes always can be fetched for index-only scans.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
gistcanreturn(Relation index, int attno)
|
gistcanreturn(Relation index, int attno)
|
||||||
{
|
{
|
||||||
if (OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)) ||
|
if (attno > IndexRelationGetNumberOfKeyAttributes(index) ||
|
||||||
|
OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)) ||
|
||||||
!OidIsValid(index_getprocid(index, attno, GIST_COMPRESS_PROC)))
|
!OidIsValid(index_getprocid(index, attno, GIST_COMPRESS_PROC)))
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
|
@ -158,6 +158,7 @@ gistrescan(IndexScanDesc scan, ScanKey key, int nkeys,
|
|||||||
if (scan->xs_want_itup && !scan->xs_hitupdesc)
|
if (scan->xs_want_itup && !scan->xs_hitupdesc)
|
||||||
{
|
{
|
||||||
int natts;
|
int natts;
|
||||||
|
int nkeyatts;
|
||||||
int attno;
|
int attno;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -167,13 +168,23 @@ gistrescan(IndexScanDesc scan, ScanKey key, int nkeys,
|
|||||||
* types.
|
* types.
|
||||||
*/
|
*/
|
||||||
natts = RelationGetNumberOfAttributes(scan->indexRelation);
|
natts = RelationGetNumberOfAttributes(scan->indexRelation);
|
||||||
|
nkeyatts = IndexRelationGetNumberOfKeyAttributes(scan->indexRelation);
|
||||||
so->giststate->fetchTupdesc = CreateTemplateTupleDesc(natts);
|
so->giststate->fetchTupdesc = CreateTemplateTupleDesc(natts);
|
||||||
for (attno = 1; attno <= natts; attno++)
|
for (attno = 1; attno <= nkeyatts; attno++)
|
||||||
{
|
{
|
||||||
TupleDescInitEntry(so->giststate->fetchTupdesc, attno, NULL,
|
TupleDescInitEntry(so->giststate->fetchTupdesc, attno, NULL,
|
||||||
scan->indexRelation->rd_opcintype[attno - 1],
|
scan->indexRelation->rd_opcintype[attno - 1],
|
||||||
-1, 0);
|
-1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (; attno <= natts; attno++)
|
||||||
|
{
|
||||||
|
/* taking opcintype from giststate->tupdesc */
|
||||||
|
TupleDescInitEntry(so->giststate->fetchTupdesc, attno, NULL,
|
||||||
|
TupleDescAttr(so->giststate->leafTupdesc,
|
||||||
|
attno - 1)->atttypid,
|
||||||
|
-1, 0);
|
||||||
|
}
|
||||||
scan->xs_hitupdesc = so->giststate->fetchTupdesc;
|
scan->xs_hitupdesc = so->giststate->fetchTupdesc;
|
||||||
|
|
||||||
/* Also create a memory context that will hold the returned tuples */
|
/* Also create a memory context that will hold the returned tuples */
|
||||||
|
@ -207,7 +207,7 @@ placeOne(Relation r, GISTSTATE *giststate, GistSplitVector *v,
|
|||||||
gistDeCompressAtt(giststate, r, itup, NULL, (OffsetNumber) 0,
|
gistDeCompressAtt(giststate, r, itup, NULL, (OffsetNumber) 0,
|
||||||
identry, isnull);
|
identry, isnull);
|
||||||
|
|
||||||
for (; attno < giststate->tupdesc->natts; attno++)
|
for (; attno < giststate->nonLeafTupdesc->natts; attno++)
|
||||||
{
|
{
|
||||||
float lpenalty,
|
float lpenalty,
|
||||||
rpenalty;
|
rpenalty;
|
||||||
@ -485,7 +485,7 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVec
|
|||||||
*/
|
*/
|
||||||
v->spl_dontcare = NULL;
|
v->spl_dontcare = NULL;
|
||||||
|
|
||||||
if (attno + 1 < giststate->tupdesc->natts)
|
if (attno + 1 < giststate->nonLeafTupdesc->natts)
|
||||||
{
|
{
|
||||||
int NumDontCare;
|
int NumDontCare;
|
||||||
|
|
||||||
@ -639,7 +639,7 @@ gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len,
|
|||||||
Datum datum;
|
Datum datum;
|
||||||
bool IsNull;
|
bool IsNull;
|
||||||
|
|
||||||
datum = index_getattr(itup[i - 1], attno + 1, giststate->tupdesc,
|
datum = index_getattr(itup[i - 1], attno + 1, giststate->leafTupdesc,
|
||||||
&IsNull);
|
&IsNull);
|
||||||
gistdentryinit(giststate, attno, &(entryvec->vector[i]),
|
gistdentryinit(giststate, attno, &(entryvec->vector[i]),
|
||||||
datum, r, page, i,
|
datum, r, page, i,
|
||||||
@ -657,7 +657,7 @@ gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len,
|
|||||||
*/
|
*/
|
||||||
v->spl_risnull[attno] = v->spl_lisnull[attno] = true;
|
v->spl_risnull[attno] = v->spl_lisnull[attno] = true;
|
||||||
|
|
||||||
if (attno + 1 < giststate->tupdesc->natts)
|
if (attno + 1 < giststate->nonLeafTupdesc->natts)
|
||||||
gistSplitByKey(r, page, itup, len, giststate, v, attno + 1);
|
gistSplitByKey(r, page, itup, len, giststate, v, attno + 1);
|
||||||
else
|
else
|
||||||
gistSplitHalf(&v->splitVector, len);
|
gistSplitHalf(&v->splitVector, len);
|
||||||
@ -683,7 +683,7 @@ gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len,
|
|||||||
v->splitVector.spl_left[v->splitVector.spl_nleft++] = i;
|
v->splitVector.spl_left[v->splitVector.spl_nleft++] = i;
|
||||||
|
|
||||||
/* Compute union keys, unless outer recursion level will handle it */
|
/* Compute union keys, unless outer recursion level will handle it */
|
||||||
if (attno == 0 && giststate->tupdesc->natts == 1)
|
if (attno == 0 && giststate->nonLeafTupdesc->natts == 1)
|
||||||
{
|
{
|
||||||
v->spl_dontcare = NULL;
|
v->spl_dontcare = NULL;
|
||||||
gistunionsubkey(giststate, itup, v);
|
gistunionsubkey(giststate, itup, v);
|
||||||
@ -700,7 +700,7 @@ gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len,
|
|||||||
* Splitting on attno column is not optimal, so consider
|
* Splitting on attno column is not optimal, so consider
|
||||||
* redistributing don't-care tuples according to the next column
|
* redistributing don't-care tuples according to the next column
|
||||||
*/
|
*/
|
||||||
Assert(attno + 1 < giststate->tupdesc->natts);
|
Assert(attno + 1 < giststate->nonLeafTupdesc->natts);
|
||||||
|
|
||||||
if (v->spl_dontcare == NULL)
|
if (v->spl_dontcare == NULL)
|
||||||
{
|
{
|
||||||
@ -771,7 +771,7 @@ gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len,
|
|||||||
* that PickSplit (or the special cases above) produced correct union
|
* that PickSplit (or the special cases above) produced correct union
|
||||||
* datums.
|
* datums.
|
||||||
*/
|
*/
|
||||||
if (attno == 0 && giststate->tupdesc->natts > 1)
|
if (attno == 0 && giststate->nonLeafTupdesc->natts > 1)
|
||||||
{
|
{
|
||||||
v->spl_dontcare = NULL;
|
v->spl_dontcare = NULL;
|
||||||
gistunionsubkey(giststate, itup, v);
|
gistunionsubkey(giststate, itup, v);
|
||||||
|
@ -160,7 +160,7 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
|
|||||||
|
|
||||||
evec = (GistEntryVector *) palloc((len + 2) * sizeof(GISTENTRY) + GEVHDRSZ);
|
evec = (GistEntryVector *) palloc((len + 2) * sizeof(GISTENTRY) + GEVHDRSZ);
|
||||||
|
|
||||||
for (i = 0; i < giststate->tupdesc->natts; i++)
|
for (i = 0; i < giststate->nonLeafTupdesc->natts; i++)
|
||||||
{
|
{
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
@ -171,7 +171,8 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
|
|||||||
Datum datum;
|
Datum datum;
|
||||||
bool IsNull;
|
bool IsNull;
|
||||||
|
|
||||||
datum = index_getattr(itvec[j], i + 1, giststate->tupdesc, &IsNull);
|
datum = index_getattr(itvec[j], i + 1, giststate->leafTupdesc,
|
||||||
|
&IsNull);
|
||||||
if (IsNull)
|
if (IsNull)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -296,11 +297,11 @@ gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p,
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < r->rd_att->natts; i++)
|
for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
|
||||||
{
|
{
|
||||||
Datum datum;
|
Datum datum;
|
||||||
|
|
||||||
datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
|
datum = index_getattr(tuple, i + 1, giststate->leafTupdesc, &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]);
|
||||||
@ -329,7 +330,7 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis
|
|||||||
gistDeCompressAtt(giststate, r, addtup, NULL,
|
gistDeCompressAtt(giststate, r, addtup, NULL,
|
||||||
(OffsetNumber) 0, addentries, addisnull);
|
(OffsetNumber) 0, addentries, addisnull);
|
||||||
|
|
||||||
for (i = 0; i < r->rd_att->natts; i++)
|
for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
|
||||||
{
|
{
|
||||||
gistMakeUnionKey(giststate, i,
|
gistMakeUnionKey(giststate, i,
|
||||||
oldentries + i, oldisnull[i],
|
oldentries + i, oldisnull[i],
|
||||||
@ -442,14 +443,15 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
|
|||||||
zero_penalty = true;
|
zero_penalty = true;
|
||||||
|
|
||||||
/* Loop over index attributes. */
|
/* Loop over index attributes. */
|
||||||
for (j = 0; j < r->rd_att->natts; j++)
|
for (j = 0; j < IndexRelationGetNumberOfKeyAttributes(r); j++)
|
||||||
{
|
{
|
||||||
Datum datum;
|
Datum datum;
|
||||||
float usize;
|
float usize;
|
||||||
bool IsNull;
|
bool IsNull;
|
||||||
|
|
||||||
/* Compute penalty for this column. */
|
/* Compute penalty for this column. */
|
||||||
datum = index_getattr(itup, j + 1, giststate->tupdesc, &IsNull);
|
datum = index_getattr(itup, j + 1, giststate->leafTupdesc,
|
||||||
|
&IsNull);
|
||||||
gistdentryinit(giststate, j, &entry, datum, r, p, i,
|
gistdentryinit(giststate, j, &entry, datum, r, p, i,
|
||||||
false, IsNull);
|
false, IsNull);
|
||||||
usize = gistpenalty(giststate, j, &entry, IsNull,
|
usize = gistpenalty(giststate, j, &entry, IsNull,
|
||||||
@ -470,7 +472,7 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
|
|||||||
result = i;
|
result = i;
|
||||||
best_penalty[j] = usize;
|
best_penalty[j] = usize;
|
||||||
|
|
||||||
if (j < r->rd_att->natts - 1)
|
if (j < IndexRelationGetNumberOfKeyAttributes(r) - 1)
|
||||||
best_penalty[j + 1] = -1;
|
best_penalty[j + 1] = -1;
|
||||||
|
|
||||||
/* we have new best, so reset keep-it decision */
|
/* we have new best, so reset keep-it decision */
|
||||||
@ -500,7 +502,7 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
|
|||||||
* If we looped past the last column, and did not update "result",
|
* If we looped past the last column, and did not update "result",
|
||||||
* then this tuple is exactly as good as the prior best tuple.
|
* then this tuple is exactly as good as the prior best tuple.
|
||||||
*/
|
*/
|
||||||
if (j == r->rd_att->natts && result != i)
|
if (j == IndexRelationGetNumberOfKeyAttributes(r) && result != i)
|
||||||
{
|
{
|
||||||
if (keep_current_best == -1)
|
if (keep_current_best == -1)
|
||||||
{
|
{
|
||||||
@ -579,7 +581,7 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
|
|||||||
/*
|
/*
|
||||||
* Call the compress method on each attribute.
|
* Call the compress method on each attribute.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < r->rd_att->natts; i++)
|
for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
|
||||||
{
|
{
|
||||||
if (isnull[i])
|
if (isnull[i])
|
||||||
compatt[i] = (Datum) 0;
|
compatt[i] = (Datum) 0;
|
||||||
@ -602,7 +604,23 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res = index_form_tuple(giststate->tupdesc, compatt, isnull);
|
if (isleaf)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Emplace each included attribute if any.
|
||||||
|
*/
|
||||||
|
for (; i < r->rd_att->natts; i++)
|
||||||
|
{
|
||||||
|
if (isnull[i])
|
||||||
|
compatt[i] = (Datum) 0;
|
||||||
|
else
|
||||||
|
compatt[i] = attdata[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res = index_form_tuple(isleaf ? giststate->leafTupdesc :
|
||||||
|
giststate->nonLeafTupdesc,
|
||||||
|
compatt, isnull);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The offset number on tuples on internal pages is unused. For historical
|
* The offset number on tuples on internal pages is unused. For historical
|
||||||
@ -644,11 +662,11 @@ gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple)
|
|||||||
bool isnull[INDEX_MAX_KEYS];
|
bool isnull[INDEX_MAX_KEYS];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < r->rd_att->natts; i++)
|
for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
|
||||||
{
|
{
|
||||||
Datum datum;
|
Datum datum;
|
||||||
|
|
||||||
datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
|
datum = index_getattr(tuple, i + 1, giststate->leafTupdesc, &isnull[i]);
|
||||||
|
|
||||||
if (giststate->fetchFn[i].fn_oid != InvalidOid)
|
if (giststate->fetchFn[i].fn_oid != InvalidOid)
|
||||||
{
|
{
|
||||||
@ -679,6 +697,15 @@ gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple)
|
|||||||
fetchatt[i] = (Datum) 0;
|
fetchatt[i] = (Datum) 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get each included attribute.
|
||||||
|
*/
|
||||||
|
for (; i < r->rd_att->natts; i++)
|
||||||
|
{
|
||||||
|
fetchatt[i] = index_getattr(tuple, i + 1, giststate->leafTupdesc,
|
||||||
|
&isnull[i]);
|
||||||
|
}
|
||||||
MemoryContextSwitchTo(oldcxt);
|
MemoryContextSwitchTo(oldcxt);
|
||||||
|
|
||||||
return heap_form_tuple(giststate->fetchTupdesc, fetchatt, isnull);
|
return heap_form_tuple(giststate->fetchTupdesc, fetchatt, isnull);
|
||||||
|
@ -78,7 +78,9 @@ typedef struct GISTSTATE
|
|||||||
MemoryContext scanCxt; /* context for scan-lifespan data */
|
MemoryContext scanCxt; /* context for scan-lifespan data */
|
||||||
MemoryContext tempCxt; /* short-term context for calling functions */
|
MemoryContext tempCxt; /* short-term context for calling functions */
|
||||||
|
|
||||||
TupleDesc tupdesc; /* index's tuple descriptor */
|
TupleDesc leafTupdesc; /* index's tuple descriptor */
|
||||||
|
TupleDesc nonLeafTupdesc; /* truncated tuple descriptor for non-leaf
|
||||||
|
* pages */
|
||||||
TupleDesc fetchTupdesc; /* tuple descriptor for tuples returned in an
|
TupleDesc fetchTupdesc; /* tuple descriptor for tuples returned in an
|
||||||
* index-only scan */
|
* index-only scan */
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ select prop,
|
|||||||
can_unique | f | |
|
can_unique | f | |
|
||||||
can_multi_col | t | |
|
can_multi_col | t | |
|
||||||
can_exclude | t | |
|
can_exclude | t | |
|
||||||
can_include | f | |
|
can_include | t | |
|
||||||
bogus | | |
|
bogus | | |
|
||||||
(19 rows)
|
(19 rows)
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ select amname, prop, pg_indexam_has_property(a.oid, prop) as p
|
|||||||
gist | can_unique | f
|
gist | can_unique | f
|
||||||
gist | can_multi_col | t
|
gist | can_multi_col | t
|
||||||
gist | can_exclude | t
|
gist | can_exclude | t
|
||||||
gist | can_include | f
|
gist | can_include | t
|
||||||
gist | bogus |
|
gist | bogus |
|
||||||
hash | can_order | f
|
hash | can_order | f
|
||||||
hash | can_unique | f
|
hash | can_unique | f
|
||||||
|
@ -330,22 +330,20 @@ SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname;
|
|||||||
|
|
||||||
DROP TABLE tbl;
|
DROP TABLE tbl;
|
||||||
/*
|
/*
|
||||||
* 7. Check various AMs. All but btree must fail.
|
* 7. Check various AMs. All but btree and gist must fail.
|
||||||
*/
|
*/
|
||||||
CREATE TABLE tbl (c1 int,c2 int, c3 box, c4 box);
|
CREATE TABLE tbl (c1 int,c2 int, c3 box, c4 box);
|
||||||
CREATE INDEX on tbl USING brin(c1, c2) INCLUDE (c3, c4);
|
CREATE INDEX on tbl USING brin(c1, c2) INCLUDE (c3, c4);
|
||||||
ERROR: access method "brin" does not support included columns
|
ERROR: access method "brin" does not support included columns
|
||||||
CREATE INDEX on tbl USING gist(c3) INCLUDE (c4);
|
CREATE INDEX on tbl USING gist(c3) INCLUDE (c1, c4);
|
||||||
ERROR: access method "gist" does not support included columns
|
|
||||||
CREATE INDEX on tbl USING spgist(c3) INCLUDE (c4);
|
CREATE INDEX on tbl USING spgist(c3) INCLUDE (c4);
|
||||||
ERROR: access method "spgist" does not support included columns
|
ERROR: access method "spgist" does not support included columns
|
||||||
CREATE INDEX on tbl USING gin(c1, c2) INCLUDE (c3, c4);
|
CREATE INDEX on tbl USING gin(c1, c2) INCLUDE (c3, c4);
|
||||||
ERROR: access method "gin" does not support included columns
|
ERROR: access method "gin" does not support included columns
|
||||||
CREATE INDEX on tbl USING hash(c1, c2) INCLUDE (c3, c4);
|
CREATE INDEX on tbl USING hash(c1, c2) INCLUDE (c3, c4);
|
||||||
ERROR: access method "hash" does not support included columns
|
ERROR: access method "hash" does not support included columns
|
||||||
CREATE INDEX on tbl USING rtree(c1, c2) INCLUDE (c3, c4);
|
CREATE INDEX on tbl USING rtree(c3) INCLUDE (c1, c4);
|
||||||
NOTICE: substituting access method "gist" for obsolete method "rtree"
|
NOTICE: substituting access method "gist" for obsolete method "rtree"
|
||||||
ERROR: access method "gist" does not support included columns
|
|
||||||
CREATE INDEX on tbl USING btree(c1, c2) INCLUDE (c3, c4);
|
CREATE INDEX on tbl USING btree(c1, c2) INCLUDE (c3, c4);
|
||||||
DROP TABLE tbl;
|
DROP TABLE tbl;
|
||||||
/*
|
/*
|
||||||
|
166
src/test/regress/expected/index_including_gist.out
Normal file
166
src/test/regress/expected/index_including_gist.out
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* 1.1. test CREATE INDEX with buffered build
|
||||||
|
*/
|
||||||
|
-- Regular index with included columns
|
||||||
|
CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box);
|
||||||
|
-- size is chosen to exceed page size and trigger actual truncation
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,8000) AS x;
|
||||||
|
CREATE INDEX tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c2,c3);
|
||||||
|
SELECT pg_get_indexdef(i.indexrelid)
|
||||||
|
FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid
|
||||||
|
WHERE i.indrelid = 'tbl_gist'::regclass ORDER BY c.relname;
|
||||||
|
pg_get_indexdef
|
||||||
|
-----------------------------------------------------------------------------------
|
||||||
|
CREATE INDEX tbl_gist_idx ON public.tbl_gist USING gist (c4) INCLUDE (c1, c2, c3)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM tbl_gist where c4 <@ box(point(1,1),point(10,10));
|
||||||
|
c1 | c2 | c3 | c4
|
||||||
|
----+----+----+-------------
|
||||||
|
1 | 2 | 3 | (2,3),(1,2)
|
||||||
|
2 | 4 | 6 | (4,5),(2,3)
|
||||||
|
3 | 6 | 9 | (6,7),(3,4)
|
||||||
|
4 | 8 | 12 | (8,9),(4,5)
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SET enable_bitmapscan TO off;
|
||||||
|
EXPLAIN (costs off) SELECT * FROM tbl_gist where c4 <@ box(point(1,1),point(10,10));
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------
|
||||||
|
Index Only Scan using tbl_gist_idx on tbl_gist
|
||||||
|
Index Cond: (c4 <@ '(10,10),(1,1)'::box)
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SET enable_bitmapscan TO default;
|
||||||
|
DROP TABLE tbl_gist;
|
||||||
|
/*
|
||||||
|
* 1.2. test CREATE INDEX with inserts
|
||||||
|
*/
|
||||||
|
-- Regular index with included columns
|
||||||
|
CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box);
|
||||||
|
-- size is chosen to exceed page size and trigger actual truncation
|
||||||
|
CREATE INDEX tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c2,c3);
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,8000) AS x;
|
||||||
|
SELECT pg_get_indexdef(i.indexrelid)
|
||||||
|
FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid
|
||||||
|
WHERE i.indrelid = 'tbl_gist'::regclass ORDER BY c.relname;
|
||||||
|
pg_get_indexdef
|
||||||
|
-----------------------------------------------------------------------------------
|
||||||
|
CREATE INDEX tbl_gist_idx ON public.tbl_gist USING gist (c4) INCLUDE (c1, c2, c3)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM tbl_gist where c4 <@ box(point(1,1),point(10,10));
|
||||||
|
c1 | c2 | c3 | c4
|
||||||
|
----+----+----+-------------
|
||||||
|
1 | 2 | 3 | (2,3),(1,2)
|
||||||
|
2 | 4 | 6 | (4,5),(2,3)
|
||||||
|
3 | 6 | 9 | (6,7),(3,4)
|
||||||
|
4 | 8 | 12 | (8,9),(4,5)
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SET enable_bitmapscan TO off;
|
||||||
|
EXPLAIN (costs off) SELECT * FROM tbl_gist where c4 <@ box(point(1,1),point(10,10));
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------
|
||||||
|
Index Only Scan using tbl_gist_idx on tbl_gist
|
||||||
|
Index Cond: (c4 <@ '(10,10),(1,1)'::box)
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SET enable_bitmapscan TO default;
|
||||||
|
DROP TABLE tbl_gist;
|
||||||
|
/*
|
||||||
|
* 2. CREATE INDEX CONCURRENTLY
|
||||||
|
*/
|
||||||
|
CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box);
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,10) AS x;
|
||||||
|
CREATE INDEX CONCURRENTLY tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c2,c3);
|
||||||
|
SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname;
|
||||||
|
indexdef
|
||||||
|
-----------------------------------------------------------------------------------
|
||||||
|
CREATE INDEX tbl_gist_idx ON public.tbl_gist USING gist (c4) INCLUDE (c1, c2, c3)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE tbl_gist;
|
||||||
|
/*
|
||||||
|
* 3. REINDEX
|
||||||
|
*/
|
||||||
|
CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box);
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,10) AS x;
|
||||||
|
CREATE INDEX tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c3);
|
||||||
|
SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname;
|
||||||
|
indexdef
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
CREATE INDEX tbl_gist_idx ON public.tbl_gist USING gist (c4) INCLUDE (c1, c3)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
REINDEX INDEX tbl_gist_idx;
|
||||||
|
SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname;
|
||||||
|
indexdef
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
CREATE INDEX tbl_gist_idx ON public.tbl_gist USING gist (c4) INCLUDE (c1, c3)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER TABLE tbl_gist DROP COLUMN c1;
|
||||||
|
SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname;
|
||||||
|
indexdef
|
||||||
|
----------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
DROP TABLE tbl_gist;
|
||||||
|
/*
|
||||||
|
* 4. Update, delete values in indexed table.
|
||||||
|
*/
|
||||||
|
CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box);
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,10) AS x;
|
||||||
|
CREATE INDEX tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c3);
|
||||||
|
UPDATE tbl_gist SET c1 = 100 WHERE c1 = 2;
|
||||||
|
UPDATE tbl_gist SET c1 = 1 WHERE c1 = 3;
|
||||||
|
DELETE FROM tbl_gist WHERE c1 = 5 OR c3 = 12;
|
||||||
|
DROP TABLE tbl_gist;
|
||||||
|
/*
|
||||||
|
* 5. Alter column type.
|
||||||
|
*/
|
||||||
|
CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box);
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,10) AS x;
|
||||||
|
CREATE INDEX tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c3);
|
||||||
|
ALTER TABLE tbl_gist ALTER c1 TYPE bigint;
|
||||||
|
ALTER TABLE tbl_gist ALTER c3 TYPE bigint;
|
||||||
|
\d tbl_gist
|
||||||
|
Table "public.tbl_gist"
|
||||||
|
Column | Type | Collation | Nullable | Default
|
||||||
|
--------+---------+-----------+----------+---------
|
||||||
|
c1 | bigint | | |
|
||||||
|
c2 | integer | | |
|
||||||
|
c3 | bigint | | |
|
||||||
|
c4 | box | | |
|
||||||
|
Indexes:
|
||||||
|
"tbl_gist_idx" gist (c4) INCLUDE (c1, c3)
|
||||||
|
|
||||||
|
DROP TABLE tbl_gist;
|
||||||
|
/*
|
||||||
|
* 6. EXCLUDE constraint.
|
||||||
|
*/
|
||||||
|
CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box, EXCLUDE USING gist (c4 WITH &&) INCLUDE (c1, c2, c3));
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,10) AS x;
|
||||||
|
ERROR: conflicting key value violates exclusion constraint "tbl_gist_c4_c1_c2_c3_excl"
|
||||||
|
DETAIL: Key (c4)=((4,5),(2,3)) conflicts with existing key (c4)=((2,3),(1,2)).
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(3*x,2*x),point(3*x+1,2*x+1)) FROM generate_series(1,10) AS x;
|
||||||
|
EXPLAIN (costs off) SELECT * FROM tbl_gist where c4 <@ box(point(1,1),point(10,10));
|
||||||
|
QUERY PLAN
|
||||||
|
-------------------------------------------------------------
|
||||||
|
Index Only Scan using tbl_gist_c4_c1_c2_c3_excl on tbl_gist
|
||||||
|
Index Cond: (c4 <@ '(10,10),(1,1)'::box)
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
\d tbl_gist
|
||||||
|
Table "public.tbl_gist"
|
||||||
|
Column | Type | Collation | Nullable | Default
|
||||||
|
--------+---------+-----------+----------+---------
|
||||||
|
c1 | integer | | |
|
||||||
|
c2 | integer | | |
|
||||||
|
c3 | integer | | |
|
||||||
|
c4 | box | | |
|
||||||
|
Indexes:
|
||||||
|
"tbl_gist_c4_c1_c2_c3_excl" EXCLUDE USING gist (c4 WITH &&) INCLUDE (c1, c2, c3)
|
||||||
|
|
||||||
|
DROP TABLE tbl_gist;
|
@ -55,7 +55,7 @@ test: copy copyselect copydml
|
|||||||
# ----------
|
# ----------
|
||||||
test: create_misc create_operator create_procedure
|
test: create_misc create_operator create_procedure
|
||||||
# These depend on the above two
|
# These depend on the above two
|
||||||
test: create_index create_view index_including
|
test: create_index create_view index_including index_including_gist
|
||||||
|
|
||||||
# ----------
|
# ----------
|
||||||
# Another group of parallel tests
|
# Another group of parallel tests
|
||||||
|
@ -63,6 +63,7 @@ test: create_operator
|
|||||||
test: create_procedure
|
test: create_procedure
|
||||||
test: create_index
|
test: create_index
|
||||||
test: index_including
|
test: index_including
|
||||||
|
test: index_including_gist
|
||||||
test: create_view
|
test: create_view
|
||||||
test: create_aggregate
|
test: create_aggregate
|
||||||
test: create_function_3
|
test: create_function_3
|
||||||
|
@ -176,15 +176,15 @@ SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname;
|
|||||||
DROP TABLE tbl;
|
DROP TABLE tbl;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 7. Check various AMs. All but btree must fail.
|
* 7. Check various AMs. All but btree and gist must fail.
|
||||||
*/
|
*/
|
||||||
CREATE TABLE tbl (c1 int,c2 int, c3 box, c4 box);
|
CREATE TABLE tbl (c1 int,c2 int, c3 box, c4 box);
|
||||||
CREATE INDEX on tbl USING brin(c1, c2) INCLUDE (c3, c4);
|
CREATE INDEX on tbl USING brin(c1, c2) INCLUDE (c3, c4);
|
||||||
CREATE INDEX on tbl USING gist(c3) INCLUDE (c4);
|
CREATE INDEX on tbl USING gist(c3) INCLUDE (c1, c4);
|
||||||
CREATE INDEX on tbl USING spgist(c3) INCLUDE (c4);
|
CREATE INDEX on tbl USING spgist(c3) INCLUDE (c4);
|
||||||
CREATE INDEX on tbl USING gin(c1, c2) INCLUDE (c3, c4);
|
CREATE INDEX on tbl USING gin(c1, c2) INCLUDE (c3, c4);
|
||||||
CREATE INDEX on tbl USING hash(c1, c2) INCLUDE (c3, c4);
|
CREATE INDEX on tbl USING hash(c1, c2) INCLUDE (c3, c4);
|
||||||
CREATE INDEX on tbl USING rtree(c1, c2) INCLUDE (c3, c4);
|
CREATE INDEX on tbl USING rtree(c3) INCLUDE (c1, c4);
|
||||||
CREATE INDEX on tbl USING btree(c1, c2) INCLUDE (c3, c4);
|
CREATE INDEX on tbl USING btree(c1, c2) INCLUDE (c3, c4);
|
||||||
DROP TABLE tbl;
|
DROP TABLE tbl;
|
||||||
|
|
||||||
|
90
src/test/regress/sql/index_including_gist.sql
Normal file
90
src/test/regress/sql/index_including_gist.sql
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* 1.1. test CREATE INDEX with buffered build
|
||||||
|
*/
|
||||||
|
|
||||||
|
-- Regular index with included columns
|
||||||
|
CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box);
|
||||||
|
-- size is chosen to exceed page size and trigger actual truncation
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,8000) AS x;
|
||||||
|
CREATE INDEX tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c2,c3);
|
||||||
|
SELECT pg_get_indexdef(i.indexrelid)
|
||||||
|
FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid
|
||||||
|
WHERE i.indrelid = 'tbl_gist'::regclass ORDER BY c.relname;
|
||||||
|
SELECT * FROM tbl_gist where c4 <@ box(point(1,1),point(10,10));
|
||||||
|
SET enable_bitmapscan TO off;
|
||||||
|
EXPLAIN (costs off) SELECT * FROM tbl_gist where c4 <@ box(point(1,1),point(10,10));
|
||||||
|
SET enable_bitmapscan TO default;
|
||||||
|
DROP TABLE tbl_gist;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 1.2. test CREATE INDEX with inserts
|
||||||
|
*/
|
||||||
|
|
||||||
|
-- Regular index with included columns
|
||||||
|
CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box);
|
||||||
|
-- size is chosen to exceed page size and trigger actual truncation
|
||||||
|
CREATE INDEX tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c2,c3);
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,8000) AS x;
|
||||||
|
SELECT pg_get_indexdef(i.indexrelid)
|
||||||
|
FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid
|
||||||
|
WHERE i.indrelid = 'tbl_gist'::regclass ORDER BY c.relname;
|
||||||
|
SELECT * FROM tbl_gist where c4 <@ box(point(1,1),point(10,10));
|
||||||
|
SET enable_bitmapscan TO off;
|
||||||
|
EXPLAIN (costs off) SELECT * FROM tbl_gist where c4 <@ box(point(1,1),point(10,10));
|
||||||
|
SET enable_bitmapscan TO default;
|
||||||
|
DROP TABLE tbl_gist;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2. CREATE INDEX CONCURRENTLY
|
||||||
|
*/
|
||||||
|
CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box);
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,10) AS x;
|
||||||
|
CREATE INDEX CONCURRENTLY tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c2,c3);
|
||||||
|
SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname;
|
||||||
|
DROP TABLE tbl_gist;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 3. REINDEX
|
||||||
|
*/
|
||||||
|
CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box);
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,10) AS x;
|
||||||
|
CREATE INDEX tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c3);
|
||||||
|
SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname;
|
||||||
|
REINDEX INDEX tbl_gist_idx;
|
||||||
|
SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname;
|
||||||
|
ALTER TABLE tbl_gist DROP COLUMN c1;
|
||||||
|
SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname;
|
||||||
|
DROP TABLE tbl_gist;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 4. Update, delete values in indexed table.
|
||||||
|
*/
|
||||||
|
CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box);
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,10) AS x;
|
||||||
|
CREATE INDEX tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c3);
|
||||||
|
UPDATE tbl_gist SET c1 = 100 WHERE c1 = 2;
|
||||||
|
UPDATE tbl_gist SET c1 = 1 WHERE c1 = 3;
|
||||||
|
DELETE FROM tbl_gist WHERE c1 = 5 OR c3 = 12;
|
||||||
|
DROP TABLE tbl_gist;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 5. Alter column type.
|
||||||
|
*/
|
||||||
|
CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box);
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,10) AS x;
|
||||||
|
CREATE INDEX tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c3);
|
||||||
|
ALTER TABLE tbl_gist ALTER c1 TYPE bigint;
|
||||||
|
ALTER TABLE tbl_gist ALTER c3 TYPE bigint;
|
||||||
|
\d tbl_gist
|
||||||
|
DROP TABLE tbl_gist;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 6. EXCLUDE constraint.
|
||||||
|
*/
|
||||||
|
CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box, EXCLUDE USING gist (c4 WITH &&) INCLUDE (c1, c2, c3));
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,10) AS x;
|
||||||
|
INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(3*x,2*x),point(3*x+1,2*x+1)) FROM generate_series(1,10) AS x;
|
||||||
|
EXPLAIN (costs off) SELECT * FROM tbl_gist where c4 <@ box(point(1,1),point(10,10));
|
||||||
|
\d tbl_gist
|
||||||
|
DROP TABLE tbl_gist;
|
Loading…
x
Reference in New Issue
Block a user